Skip to content

Commit

Permalink
New config option cm_config_log_level (#963)
Browse files Browse the repository at this point in the history
* New config option `cm_config_log_level`
* Log the config file when the config contents has changed
  • Loading branch information
mwouts authored Jul 3, 2022
1 parent 4507f81 commit 72afe5d
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 10 deletions.
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Jupytext ChangeLog

**Changed**
- Hidden configuration files like `.jupytext.toml` or `.jupytext.py` are now ignored by Jupytext's contents manager when `allow_hidden=False` (that option was introduced in `jupyter_server==2.0.0a1`) ([#964](https://github.com/mwouts/jupytext/issues/964)).
- The Jupytext configuration file has a new option `cm_config_log_level` that defaults to `info_if_changed`.
With that value, the contents manager will log a line regarding the configuration file used only when the
config file is not the same as the one previously used ([#959](https://github.com/mwouts/jupytext/issues/959))


1.13.8 (2022-04-04)
Expand Down
14 changes: 14 additions & 0 deletions jupytext/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ class JupytextConfiguration(Configurable):
config=True,
)

cm_config_log_level = Enum(
values=["warning", "info", "info_if_changed", "debug", "none"],
default_value="info_if_changed",
help="The log level for config file logs in the Jupytext contents manager",
config=True,
)

cell_markers = Unicode(
help='Start and end cell markers for the light format, comma separated. Use "{{{,}}}" to mark cells'
'as foldable regions in Vim, and "region,endregion" to mark cells as Vscode/PyCharm regions',
Expand Down Expand Up @@ -244,6 +251,13 @@ def default_formats(self, path):

return None

def __eq__(self, other):
for key in self.class_trait_names():
if getattr(self, key) != getattr(other, key):
return False

return True


def preferred_format(incomplete_format, preferred_formats):
"""Return the preferred format for the given extension"""
Expand Down
39 changes: 29 additions & 10 deletions jupytext/contentsmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,11 +526,12 @@ def get_config_file(self, directory):
parent_dir = self.get_parent_dir(directory)
return self.get_config_file(parent_dir)

def load_config_file(self, config_file, is_os_path=False):
def load_config_file(
self, config_file, *, prev_config_file, prev_config, is_os_path=False
):
"""Load the configuration file"""
if config_file is None:
return None
self.log.info("Loading Jupytext configuration file at %s", config_file)
if config_file.endswith(".py") and not is_os_path:
config_file = self._get_os_path(config_file)
is_os_path = True
Expand All @@ -543,7 +544,21 @@ def load_config_file(self, config_file, is_os_path=False):
except HTTPError:
pass

return load_jupytext_configuration_file(config_file, config_content)
config = load_jupytext_configuration_file(config_file, config_content)
if config is None:
return config

log_level = config.cm_config_log_level
if log_level == "info_if_changed":
if config_file != prev_config_file or config != prev_config:
log_level = "info"
else:
log_level = "none"
if log_level != "none":
getattr(self.log, log_level)(
"Loaded Jupytext configuration file at %s", config_file
)
return config

def get_config(self, path, use_cache=False):
"""Return the Jupytext configuration for the given path"""
Expand All @@ -556,10 +571,19 @@ def get_config(self, path, use_cache=False):
try:
config_file = self.get_config_file(parent_dir)
if config_file:
self.cached_config.config = self.load_config_file(config_file)
self.cached_config.config = self.load_config_file(
config_file,
prev_config_file=self.cached_config.config_file,
prev_config=self.cached_config.config,
)
else:
config_file = find_global_jupytext_configuration_file()
self.cached_config.config = self.load_config_file(config_file)
self.cached_config.config = self.load_config_file(
config_file,
prev_config_file=self.cached_config.config_file,
prev_config=self.cached_config.config,
is_os_path=True,
)
self.cached_config.config_file = config_file
self.cached_config.path = parent_dir
except JupytextConfigurationError as err:
Expand All @@ -572,11 +596,6 @@ def get_config(self, path, use_cache=False):
raise HTTPError(500, f"{err}")

if self.cached_config.config is not None:
self.log.debug(
"Configuration file for %s is %s",
path,
self.cached_config.config_file,
)
return self.cached_config.config
if isinstance(self.notebook_extensions, str):
self.notebook_extensions = self.notebook_extensions.split(",")
Expand Down
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ def cwd_tmpdir(tmpdir):
yield tmpdir


@pytest.fixture()
def cwd_tmp_path(tmp_path):
# Run the whole test from inside tmp_path
with tmp_path.cwd():
yield tmp_path


@pytest.fixture
def jupytext_repo_root():
"""The local path of this repo, to use in .pre-commit-config.yaml in tests"""
Expand Down
59 changes: 59 additions & 0 deletions tests/test_cm_config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import os
import sys
import unittest.mock as mock
Expand Down Expand Up @@ -220,3 +221,61 @@ def test_test_no_text_representation_metadata_in_ipynb_900(
assert "text_representation" in tmpdir.join("test.py").read()
# But not in the ipynb notebook
assert "text_representation" not in tmpdir.join("test.ipynb").read()


def test_cm_config_no_log(cwd_tmp_path, tmp_path, caplog):
cm = jupytext.TextFileContentsManager()
cm.root_dir = str(tmp_path)

config = 'cm_config_log_level="none"'
(tmp_path / "jupytext.toml").write_text(config)
(tmp_path / "nb1.py").write_text("# %%")
(tmp_path / "subfolder").mkdir()
(tmp_path / "subfolder" / "jupytext.toml").write_text(config)
(tmp_path / "subfolder" / "nb2.py").write_text("# %%")

caplog.set_level(logging.DEBUG)

cm.get("nb1.py", type="notebook", content=False)
cm.get("nb1.py", type="notebook", content=True)
cm.get("subfolder/nb2.py", type="notebook", content=False)
cm.get("subfolder/nb2.py", type="notebook", content=True)

assert "Jupytext configuration file" not in caplog.text


def test_cm_config_log_only_if_changed(cwd_tmp_path, tmp_path, caplog):
cm = jupytext.TextFileContentsManager()
cm.root_dir = str(tmp_path)

config = ""
(tmp_path / "jupytext.toml").write_text(config)
(tmp_path / "nb1.py").write_text("# %%")
(tmp_path / "subfolder").mkdir()
(tmp_path / "subfolder" / "jupytext.toml").write_text(config)
(tmp_path / "subfolder" / "nb2.py").write_text("# %%")

caplog.set_level(logging.INFO)

cm.get("nb1.py", type="notebook", content=False)
assert "Jupytext configuration file" in caplog.text
caplog.clear()

# Same notebook, same config => no log
cm.get("nb1.py", type="notebook", content=True)
assert "Jupytext configuration file" not in caplog.text

# Same notebook, config changed => log
(tmp_path / "jupytext.toml").write_text('formats="ipynb,py:percent"')
cm.get("nb1.py", type="notebook", content=True)
assert "Jupytext configuration file" in caplog.text
caplog.clear()

# Different folder, different config
cm.get("subfolder/nb2.py", type="notebook", content=False)
assert "Jupytext configuration file" in caplog.text
caplog.clear()

# Same config as previously => no log
cm.get("subfolder/nb2.py", type="notebook", content=False)
assert "Jupytext configuration file" not in caplog.text

0 comments on commit 72afe5d

Please sign in to comment.