diff --git a/src/jupytext/labconfig.py b/src/jupytext/labconfig.py deleted file mode 100644 index 7c3d4fcf1..000000000 --- a/src/jupytext/labconfig.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -helper to inspect / initialize jupyterlab labconfig settings -that are required to open jupytext notebooks in jupyterlab by default -when these settings are not present, a double click on a jupytext -notebook will cause jupyterlab to open it in an editor, i.e. as a text file -""" - -import copy -import json -import logging - -# import pprint -from pathlib import Path - - -class LabConfig: - SETTINGS = Path.home() / ".jupyter" / "labconfig" / "default_setting_overrides.json" - - DOCTYPES = [ - "python", - "markdown", - "myst", - "r-markdown", - "quarto", - "julia", - "r", - ] - - def __init__(self, logger=None): - self.logger = logger or logging.getLogger(__name__) - self.config = {} - # the state before any changes - self._prior_config = {} - - def read(self): - """ - read the labconfig settings file - """ - try: - if self.SETTINGS.exists(): - with self.SETTINGS.open() as fid: - self.config = json.load(fid) - except OSError as exc: - self.logger.error(f"Could not read {self.SETTINGS}", exc) - return False - # store for further comparison - self._prior_config = copy.deepcopy(self.config) - return self - - def list_default_viewer(self): - """ - list the current labconfig settings - """ - self.logger.debug( - f"Current @jupyterlab/docmanager-extension:plugin in {self.SETTINGS}" - ) - docmanager = self.config.get("@jupyterlab/docmanager-extension:plugin", {}) - viewers = docmanager.get("defaultViewers", {}) - for key, value in viewers.items(): - print(f"{key}: {value}") - - def set_default_viewers(self, doctypes=None): - if not doctypes: - doctypes = self.DOCTYPES - for doctype in doctypes: - self.set_default_viewer(doctype) - return self - - def set_default_viewer(self, doctype): - if "@jupyterlab/docmanager-extension:plugin" not in self.config: - self.config["@jupyterlab/docmanager-extension:plugin"] = {} - if ( - "defaultViewers" - not in self.config["@jupyterlab/docmanager-extension:plugin"] - ): - self.config["@jupyterlab/docmanager-extension:plugin"][ - "defaultViewers" - ] = {} - viewers = self.config["@jupyterlab/docmanager-extension:plugin"][ - "defaultViewers" - ] - if doctype not in viewers: - viewers[doctype] = "Jupytext Notebook" - - def unset_default_viewers(self, doctypes=None): - if not doctypes: - doctypes = self.DOCTYPES - for doctype in doctypes: - self.unset_default_viewer(doctype) - return self - - def unset_default_viewer(self, doctype): - viewers = self.config.get("@jupyterlab/docmanager-extension:plugin", {}).get( - "defaultViewers", {} - ) - if doctype not in viewers: - return - del viewers[doctype] - - def write(self) -> bool: - """ - write the labconfig settings file - """ - # compare - avoid changing the file if nothing changed - if self.config == self._prior_config: - self.logger.info(f"Nothing to do for {self.SETTINGS}") - return True - - # save - try: - self.SETTINGS.parent.mkdir(parents=True, exist_ok=True) - with self.SETTINGS.open("w") as fid: - json.dump(self.config, fid, indent=2) - # useful in case of successive write's - self._prior_config = copy.deepcopy(self.config) - return True - except OSError as exc: - self.logger.error(f"Could not write {self.SETTINGS}", exc) - return False diff --git a/src/jupytext_config/__main__.py b/src/jupytext_config/__main__.py index 5220474ac..a5538fe60 100644 --- a/src/jupytext_config/__main__.py +++ b/src/jupytext_config/__main__.py @@ -7,7 +7,7 @@ import sys -from jupytext.jupytext_config import main +from .jupytext_config import main if __name__ == "__main__": sys.exit(main()) diff --git a/src/jupytext/jupytext_config.py b/src/jupytext_config/jupytext_config.py similarity index 73% rename from src/jupytext/jupytext_config.py rename to src/jupytext_config/jupytext_config.py index 25ca5464f..871395e33 100644 --- a/src/jupytext/jupytext_config.py +++ b/src/jupytext_config/jupytext_config.py @@ -6,6 +6,9 @@ import sys from argparse import ArgumentParser +from pathlib import Path + +import jupyter_core.paths as jupyter_core_paths from .labconfig import LabConfig @@ -23,20 +26,15 @@ def main(self, args): """ return 0 if all goes well """ - print( - f"{self.__class__.__name__}: redefine main() to implement this subcommand" - ) - return 1 + raise NotImplementedError() # pragma: no cover class ListDefaultViewer(SubCommand): def __init__(self): - super().__init__( - "list-default-viewer", "Display current settings in labconfig/" - ) + super().__init__("list-default-viewer", "Display current settings in labconfig") def main(self, args): - LabConfig().read().list_default_viewer() + LabConfig(settings_file=args.settings_file).read().list_default_viewer() return 0 def fill_parser(self, subparser): @@ -48,7 +46,9 @@ def __init__(self): super().__init__("set-default-viewer", "Set default viewers for JupyterLab") def main(self, args): - LabConfig().read().set_default_viewers(args.doctype).write() + LabConfig(settings_file=args.settings_file).read().set_default_viewers( + args.doctype + ).write() return 0 def fill_parser(self, subparser): @@ -65,7 +65,9 @@ def __init__(self): super().__init__("unset-default-viewer", "Unset default viewers for JupyterLab") def main(self, args): - LabConfig().read().unset_default_viewers(args.doctype).write() + LabConfig(settings_file=args.settings_file).read().unset_default_viewers( + args.doctype + ).write() return 0 def fill_parser(self, subparser): @@ -87,6 +89,12 @@ def fill_parser(self, subparser): def main(): parser = ArgumentParser() + parser.add_argument( + "--settings-file", + default=Path(jupyter_core_paths.jupyter_config_dir()) + / "labconfig" + / "default_setting_overrides.json", + ) subparsers = parser.add_subparsers(required=True) for subcommand in SUBCOMMANDS: subparser = subparsers.add_parser(subcommand.name, help=subcommand.help) diff --git a/src/jupytext_config/labconfig.py b/src/jupytext_config/labconfig.py new file mode 100644 index 000000000..05c3ac2da --- /dev/null +++ b/src/jupytext_config/labconfig.py @@ -0,0 +1,86 @@ +""" +helper to inspect / initialize jupyterlab labconfig settings +that are required to open jupytext notebooks in jupyterlab by default +when these settings are not present, a double click on a jupytext +notebook will cause jupyterlab to open it in an editor, i.e. as a text file +""" + +import json +import logging +from pathlib import Path + + +class LabConfig: + DOCTYPES = [ + "python", + "markdown", + "myst", + "r-markdown", + "quarto", + "julia", + "r", + ] + + def __init__(self, *, settings_file, logger=None): + self.settings_file = Path(settings_file) + self.logger = logger or logging.getLogger(__name__) + self.config = {} + + def read(self): + """ + read the labconfig settings file + """ + if self.settings_file.exists(): + with self.settings_file.open() as fid: + self.config = json.load(fid) + else: + self.logger.info(f"Could not read from {self.settings_file} (not found)") + + return self + + def get_viewers(self): + return self.config.setdefault( + "@jupyterlab/docmanager-extension:plugin", {} + ).setdefault("defaultViewers", {}) + + def list_default_viewer(self): + """ + list the current labconfig settings + """ + self.logger.debug( + f"Current @jupyterlab/docmanager-extension:plugin in {self.settings_file}" + ) + for key, value in self.get_viewers().items(): + print(f"{key}: {value}") + + def set_default_viewers(self, doctypes=None): + if not doctypes: + doctypes = self.DOCTYPES + for doctype in doctypes: + self.set_default_viewer(doctype) + return self + + def set_default_viewer(self, doctype): + viewers = self.get_viewers() + if doctype not in viewers: + viewers[doctype] = "Jupytext Notebook" + + def unset_default_viewers(self, doctypes=None): + if not doctypes: + doctypes = self.DOCTYPES + for doctype in doctypes: + self.unset_default_viewer(doctype) + return self + + def unset_default_viewer(self, doctype): + viewers = self.get_viewers() + if doctype in viewers: + del viewers[doctype] + + def write(self): + """ + write the labconfig settings file + """ + self.settings_file.parent.mkdir(parents=True, exist_ok=True) + with self.settings_file.open("w") as fid: + json.dump(self.config, fid, indent=2) diff --git a/tests/integration/jupytext_config/test_jupytext_config.py b/tests/integration/jupytext_config/test_jupytext_config.py new file mode 100644 index 000000000..9b54cc6ec --- /dev/null +++ b/tests/integration/jupytext_config/test_jupytext_config.py @@ -0,0 +1,30 @@ +from jupytext.cli import system + + +def test_jupytext_config_cli(tmp_path): + settings_file = tmp_path / "default_setting_overrides.json" + system("jupytext-config", "-h") + system( + "jupytext-config", + "--settings-file", + str(settings_file), + "set-default-viewer", + "python", + "markdown", + ) + assert "python" in (settings_file).read_text() + assert "markdown" in system( + "jupytext-config", "--settings-file", settings_file, "list-default-viewer" + ) + system( + "jupytext-config", + "--settings-file", + str(settings_file), + "unset-default-viewer", + "markdown", + ) + list_viewers = system( + "jupytext-config", "--settings-file", settings_file, "list-default-viewer" + ) + assert "python" in list_viewers + assert "markdown" not in list_viewers diff --git a/tests/unit/test_labconfig.py b/tests/unit/test_labconfig.py new file mode 100644 index 000000000..e96f9f326 --- /dev/null +++ b/tests/unit/test_labconfig.py @@ -0,0 +1,55 @@ +import json + +import pytest + +from jupytext_config.labconfig import LabConfig + + +@pytest.fixture() +def sample_viewer_config(): + return { + "@jupyterlab/docmanager-extension:plugin": { + "defaultViewers": { + "markdown": "Jupytext Notebook", + "myst": "Jupytext Notebook", + "r-markdown": "Jupytext Notebook", + "quarto": "Jupytext Notebook", + "julia": "Jupytext Notebook", + "python": "Jupytext Notebook", + "r": "Jupytext Notebook", + } + } + } + + +@pytest.fixture() +def sample_empty_viewer_config(): + return {"@jupyterlab/docmanager-extension:plugin": {"defaultViewers": {}}} + + +@pytest.fixture() +def settings_file(tmp_path): + return tmp_path / "default_setting_overrides.json" + + +def test_read_config(settings_file, sample_viewer_config): + (settings_file).write_text(json.dumps(sample_viewer_config)) + labconfig = LabConfig(settings_file=settings_file).read() + assert labconfig.config == sample_viewer_config + + +def test_set_unset_default_viewers( + settings_file, sample_viewer_config, sample_empty_viewer_config +): + labconfig = LabConfig(settings_file=settings_file) + labconfig.set_default_viewers() + assert labconfig.config == sample_viewer_config + labconfig.unset_default_viewers() + assert labconfig.config == sample_empty_viewer_config + + +def test_write_config(settings_file, sample_viewer_config): + labconfig = LabConfig(settings_file=settings_file) + labconfig.set_default_viewers() + labconfig.write() + assert json.loads(settings_file.read_text()) == sample_viewer_config