Skip to content

Commit

Permalink
Allow pattern in output_base (#1967)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
JeppeKlitgaard and pre-commit-ci[bot] authored Mar 31, 2023
1 parent 6f77b0b commit d5404cd
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 25 deletions.
62 changes: 37 additions & 25 deletions nbconvert/nbconvertapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ def _classes_default(self):
)

output_base = Unicode(
"",
help="""overwrite base name use for output files.
can only be used when converting one notebook at a time.
"{notebook_name}",
help="""Overwrite base name use for output files.
Supports pattern replacements '{notebook_name}' and '{notebook_filename}'.
""",
).tag(config=True)

Expand Down Expand Up @@ -413,6 +413,19 @@ def start(self):
super().start()
self.convert_notebooks()

def _notebook_filename_to_name(self, notebook_filename):
"""
Returns the notebook name from the notebook filename by
applying `output_base` pattern and stripping extension
"""
basename = os.path.basename(notebook_filename)
notebook_name = basename[: basename.rfind(".")]
notebook_name = self.output_base.format(
notebook_name=notebook_name, notebook_filename=notebook_filename
)

return notebook_name

def init_single_notebook_resources(self, notebook_filename):
"""Step 1: Initialize resources
Expand All @@ -427,16 +440,7 @@ def init_single_notebook_resources(self, notebook_filename):
- output_files_dir: a directory where output files (not
including the notebook itself) should be saved
"""
basename = os.path.basename(notebook_filename)
notebook_name = basename[: basename.rfind(".")]
if self.output_base:
# strip duplicate extension from output_base, to avoid Basename.ext.ext
if getattr(self.exporter, "file_extension", False):
base, ext = os.path.splitext(self.output_base)
if ext == self.exporter.file_extension:
self.output_base = base
notebook_name = self.output_base

notebook_name = self._notebook_filename_to_name(notebook_filename)
self.log.debug("Notebook name is '%s'", notebook_name)

# first initialize the resources we want to use
Expand Down Expand Up @@ -509,7 +513,7 @@ def write_single_notebook(self, output, resources):
raise KeyError(msg)

notebook_name = resources["unique_key"]
if self.use_output_suffix and not self.output_base:
if self.use_output_suffix:
notebook_name += resources.get("output_suffix", "")

write_results = self.writer.write(output, resources, notebook_name=notebook_name)
Expand Down Expand Up @@ -557,17 +561,7 @@ def convert_single_notebook(self, notebook_filename, input_buffer=None):
self.postprocess_single_notebook(write_results)

def convert_notebooks(self):
"""Convert the notebooks in the self.notebook traitlet"""
# check that the output base isn't specified if there is more than
# one notebook to convert
if self.output_base != "" and len(self.notebooks) > 1:
self.log.error(
"""
UsageError: --output flag or `NbConvertApp.output_base` config option
cannot be used when converting multiple notebooks.
"""
)
self.exit(1)
"""Convert the notebooks in the self.notebooks traitlet"""

# no notebooks to convert!
if len(self.notebooks) == 0 and not self.from_stdin:
Expand All @@ -585,6 +579,24 @@ def convert_notebooks(self):
cls = get_exporter(self.export_format)
self.exporter = cls(config=self.config)

# strip duplicate extension from output_base, to avoid Basename.ext.ext
if getattr(self.exporter, "file_extension", False):
base, ext = os.path.splitext(self.output_base)
if ext == self.exporter.file_extension:
self.output_base = base

# Validate that output_base does not cause us to overwrite already generated
# files
notebook_names = [self._notebook_filename_to_name(fn) for fn in self.notebooks]
if len(notebook_names) != len(set(notebook_names)):
msg = (
"Conversion would override an already generated output. "
"This is probably due to --output or output_base configuration "
"leading to non-unique output names. "
f"Output notebook names were: {notebook_names}"
)
raise ValueError(msg)

# convert each notebook
if not self.from_stdin:
for notebook_filename in self.notebooks:
Expand Down
34 changes: 34 additions & 0 deletions nbconvert/tests/test_nbconvertapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,3 +632,37 @@ def test_execute_multiple_notebooks(self):
text = f.read()
assert '<script type="application/vnd.jupyter.widget-view+json">' in text
assert '<script type="application/vnd.jupyter.widget-state+json">' in text

def test_output_base(self):
"""
Check various configurations of output_base (--output)
"""
notebook_names = [
"notebook1",
"notebook2",
]
with self.create_temp_cwd([x + ".ipynb" for x in notebook_names]):
self.nbconvert("*.ipynb --output '{notebook_name}_test_addition.asd' --to markdown")

for nbn in notebook_names:
assert os.path.isfile(f"{nbn}_test_addition.asd.md")

with self.create_temp_cwd([x + ".ipynb" for x in notebook_names]):
self.nbconvert("*.ipynb --to markdown")

for nbn in notebook_names:
assert os.path.isfile(f"{nbn}.md")

with pytest.raises(OSError), self.create_temp_cwd([x + ".ipynb" for x in notebook_names]):
self.nbconvert("*.ipynb --output notebook_test_name --to markdown")

# Test single output with static output name
with self.create_temp_cwd([notebook_names[0] + ".ipynb"]):
self.nbconvert("*.ipynb --output notebook_test_name --to markdown")
assert os.path.isfile("notebook_test_name.md")

# Test double extension fix
with self.create_temp_cwd([notebook_names[0] + ".ipynb"]):
self.nbconvert("*.ipynb --output notebook_test_name.md --to markdown")
assert os.path.isfile("notebook_test_name.md")
assert not os.path.isfile("notebook_test_name.md.md")

0 comments on commit d5404cd

Please sign in to comment.