Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-nbconvert for 
openSUSE:Factory checked in at 2023-06-12 15:26:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-nbconvert (Old)
 and      /work/SRC/openSUSE:Factory/.python-nbconvert.new.15902 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-nbconvert"

Mon Jun 12 15:26:34 2023 rev:23 rq:1092410 version:7.4.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-nbconvert/python-nbconvert.changes        
2023-05-02 16:23:24.131367330 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-nbconvert.new.15902/python-nbconvert.changes 
    2023-06-12 15:27:36.995465594 +0200
@@ -1,0 +2,10 @@
+Sun Jun 11 20:00:15 UTC 2023 - Ben Greiner <[email protected]>
+
+- Update to 7.4.0
+  * Add ExtractAttachmentsPreprocessor #1978 (@tuncbkose)
+  * Moved ensure_dir_exists to FilesWriter #1987 (@tuncbkose)
+  * Tweak exporter default_config merging behavior #1981
+    (@tuncbkose)
+  * Revert unintended effects of #1966 #1974 (@tuncbkose)
+
+-------------------------------------------------------------------

Old:
----
  nbconvert-7.3.1.tar.gz

New:
----
  nbconvert-7.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-nbconvert.spec ++++++
--- /var/tmp/diff_new_pack.hpnzHT/_old  2023-06-12 15:27:37.687469687 +0200
+++ /var/tmp/diff_new_pack.hpnzHT/_new  2023-06-12 15:27:37.691469711 +0200
@@ -29,8 +29,12 @@
 %else
 %bcond_with libalternatives
 %endif
+# avoid rewriting
+%define python3dist python3dist
+# 7.4.0 gets abbreviated by pythondistdeps
+%define shortversion 7.4
 Name:           python-nbconvert%{psuffix}
-Version:        7.3.1
+Version:        7.4.0
 Release:        0
 Summary:        Conversion of Jupyter Notebooks
 License:        BSD-3-Clause AND MIT
@@ -51,7 +55,6 @@
 Requires:       python-defusedxml
 Requires:       python-jupyter-core >= 4.7
 Requires:       python-jupyterlab-pygments
-Requires:       python-lxml
 Requires:       python-nbclient >= 0.5
 Requires:       python-nbformat >= 5.1
 Requires:       python-packaging
@@ -94,7 +97,7 @@
 Summary:        Conversion of Jupyter Notebooks
 Requires:       jupyter-ipykernel
 Requires:       jupyter-jupyter-core
-Requires:       python3-nbconvert = %{version}
+Requires:       %python3dist(nbconvert) = %{shortversion}
 Conflicts:      python3-jupyter_nbconvert < 5.5.0
 
 %description -n jupyter-nbconvert
@@ -111,9 +114,6 @@
 Requires:       tex(adjustbox.sty)
 Requires:       tex(eurosym.sty)
 Requires:       tex(ulem.sty)
-Provides:       %{python_module jupyter_nbconvert-latex = %{version}}
-Provides:       %{python_module nbconvert-latex = %{version}}
-Obsoletes:      %{python_module jupyter_nbconvert-latex < %{version}}
 
 %description -n jupyter-nbconvert-latex
 The jupyter nbconvert package converts notebooks to various other formats
@@ -149,8 +149,6 @@
 export PYTHONDONTWRITEBYTECODE=1
 # requires modules not installed: 
https://github.com/jupyter/nbconvert/issues/1846
 donttest="test_convert_full_qualified_name or test_post_processor"
-# https://github.com/jupyter/nbconvert/pull/1985
-donttest+=" or test_errors_print_traceback"
 %{python_expand # installed package in :test flavor
 $python -m ipykernel.kernelspec --user
 pytest-%{$python_bin_suffix} -v -m 'not network' -k "not ($donttest)" --pyargs 
nbconvert

++++++ nbconvert-7.3.1.tar.gz -> nbconvert-7.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/.github/workflows/tests.yml 
new/nbconvert-7.4.0/.github/workflows/tests.yml
--- old/nbconvert-7.3.1/.github/workflows/tests.yml     2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/.github/workflows/tests.yml     2020-02-02 
01:00:00.000000000 +0100
@@ -54,10 +54,15 @@
         run: |
           hatch run cov:test
 
-      - name: Code coverage
-        run: |
-          pip install codecov coverage[toml]
-          codecov
+      - uses: jupyterlab/maintainer-tools/.github/actions/upload-coverage@v1
+
+  coverage:
+    runs-on: ubuntu-latest
+    needs:
+      - run-tests
+    steps:
+      - uses: actions/checkout@v3
+      - uses: jupyterlab/maintainer-tools/.github/actions/report-coverage@v1
 
   test_lint:
     name: Test Lint
@@ -145,7 +150,7 @@
   tests_check: # This job does nothing and is only used for the branch 
protection
     if: always()
     needs:
-      - run-tests
+      - coverage
       - test_lint
       - test_minimum_versions
       - test_prereleases
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/.pre-commit-config.yaml 
new/nbconvert-7.4.0/.pre-commit-config.yaml
--- old/nbconvert-7.3.1/.pre-commit-config.yaml 2020-02-02 01:00:00.000000000 
+0100
+++ new/nbconvert-7.4.0/.pre-commit-config.yaml 2020-02-02 01:00:00.000000000 
+0100
@@ -36,7 +36,7 @@
       - id: black
 
   - repo: https://github.com/charliermarsh/ruff-pre-commit
-    rev: v0.0.260
+    rev: v0.0.263
     hooks:
       - id: ruff
         args: ["--fix"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/.readthedocs.yaml 
new/nbconvert-7.4.0/.readthedocs.yaml
--- old/nbconvert-7.3.1/.readthedocs.yaml       2020-02-02 01:00:00.000000000 
+0100
+++ new/nbconvert-7.4.0/.readthedocs.yaml       2020-02-02 01:00:00.000000000 
+0100
@@ -10,13 +10,14 @@
 
 formats: all
 
-build:
-  image: latest
-
 python:
-  version: 3.8
   install:
     - method: pip
       path: .
       extra_requirements:
         - docs
+
+build:
+  os: ubuntu-22.04
+  tools:
+     python: "3.11"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/CHANGELOG.md 
new/nbconvert-7.4.0/CHANGELOG.md
--- old/nbconvert-7.3.1/CHANGELOG.md    2020-02-02 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/CHANGELOG.md    2020-02-02 01:00:00.000000000 +0100
@@ -2,6 +2,34 @@
 
 <!-- <START NEW CHANGELOG ENTRY> -->
 
+## 7.4.0
+
+([Full 
Changelog](https://github.com/jupyter/nbconvert/compare/v7.3.1...32fcf7b26462f5d51d577f8beda9d49cd3a0f441))
+
+### Enhancements made
+
+- Add ExtractAttachmentsPreprocessor 
[#1978](https://github.com/jupyter/nbconvert/pull/1978) 
([@tuncbkose](https://github.com/tuncbkose))
+
+### Bugs fixed
+
+- Moved ensure_dir_exists to FilesWriter 
[#1987](https://github.com/jupyter/nbconvert/pull/1987) 
([@tuncbkose](https://github.com/tuncbkose))
+- Tweak exporter default_config merging behavior 
[#1981](https://github.com/jupyter/nbconvert/pull/1981) 
([@tuncbkose](https://github.com/tuncbkose))
+- Revert unintended effects of #1966 
[#1974](https://github.com/jupyter/nbconvert/pull/1974) 
([@tuncbkose](https://github.com/tuncbkose))
+
+### Maintenance and upkeep improvements
+
+- Fix test_errors_print_traceback test 
[#1985](https://github.com/jupyter/nbconvert/pull/1985) 
([@blink1073](https://github.com/blink1073))
+- Ensure toml support in coverage reporting 
[#1984](https://github.com/jupyter/nbconvert/pull/1984) 
([@blink1073](https://github.com/blink1073))
+- Use local coverage [#1976](https://github.com/jupyter/nbconvert/pull/1976) 
([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this 
release](https://github.com/jupyter/nbconvert/graphs/contributors?from=2023-04-10&to=2023-05-08&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Ablink1073+updated%3A2023-04-10..2023-05-08&type=Issues)
 | 
[@krassowski](https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Akrassowski+updated%3A2023-04-10..2023-05-08&type=Issues)
 | 
[@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Apre-commit-ci+updated%3A2023-04-10..2023-05-08&type=Issues)
 | 
[@tuncbkose](https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Atuncbkose+updated%3A2023-04-10..2023-05-08&type=Issues)
+
+<!-- <END NEW CHANGELOG ENTRY> -->
+
 ## 7.3.1
 
 ([Full 
Changelog](https://github.com/jupyter/nbconvert/compare/v7.3.0...3860152ecea3d9833540eebe279ff603b3d47cea))
@@ -18,8 +46,6 @@
 
 
[@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Apre-commit-ci+updated%3A2023-04-03..2023-04-10&type=Issues)
 | 
[@tuncbkose](https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Atuncbkose+updated%3A2023-04-03..2023-04-10&type=Issues)
 
-<!-- <END NEW CHANGELOG ENTRY> -->
-
 ## 7.3.0
 
 ([Full 
Changelog](https://github.com/jupyter/nbconvert/compare/v7.2.10...056dc4ecc8f9f3e9249f0dbddf1221c65228b961))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/PKG-INFO new/nbconvert-7.4.0/PKG-INFO
--- old/nbconvert-7.3.1/PKG-INFO        2020-02-02 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/PKG-INFO        2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: nbconvert
-Version: 7.3.1
+Version: 7.4.0
 Summary: Converting Jupyter Notebooks
 Project-URL: Homepage, https://jupyter.org
 Author-email: Jupyter Development Team <[email protected]>
@@ -92,7 +92,6 @@
 
 [![Build 
Status](https://github.com/jupyter/nbconvert/actions/workflows/tests.yml/badge.svg?query=branch%3Amain++)](https://github.com/jupyter/nbconvert/actions/workflows/tests.yml/badge.svg?query=branch%3Amain++)
 [![Documentation 
Status](https://readthedocs.org/projects/nbconvert/badge/?version=latest)](https://nbconvert.readthedocs.io/en/latest/?badge=latest)
-[![codecov.io](https://codecov.io/gh/jupyter/nbconvert/coverage.svg?branch=main)](https://codecov.io/gh/gh/nbconvert?branch=main)
 
 The **nbconvert** tool, `jupyter nbconvert`, converts notebooks to various 
other
 formats via [Jinja] templates. The nbconvert tool allows you to convert an
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/README.md 
new/nbconvert-7.4.0/README.md
--- old/nbconvert-7.3.1/README.md       2020-02-02 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/README.md       2020-02-02 01:00:00.000000000 +0100
@@ -4,7 +4,6 @@
 
 [![Build 
Status](https://github.com/jupyter/nbconvert/actions/workflows/tests.yml/badge.svg?query=branch%3Amain++)](https://github.com/jupyter/nbconvert/actions/workflows/tests.yml/badge.svg?query=branch%3Amain++)
 [![Documentation 
Status](https://readthedocs.org/projects/nbconvert/badge/?version=latest)](https://nbconvert.readthedocs.io/en/latest/?badge=latest)
-[![codecov.io](https://codecov.io/gh/jupyter/nbconvert/coverage.svg?branch=main)](https://codecov.io/gh/gh/nbconvert?branch=main)
 
 The **nbconvert** tool, `jupyter nbconvert`, converts notebooks to various 
other
 formats via [Jinja] templates. The nbconvert tool allows you to convert an
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/codecov.yml 
new/nbconvert-7.4.0/codecov.yml
--- old/nbconvert-7.3.1/codecov.yml     2020-02-02 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/codecov.yml     1970-01-01 01:00:00.000000000 +0100
@@ -1,9 +0,0 @@
-coverage:
-  status:
-    project:
-      default:
-        target: auto
-        threshold: 1
-    patch:
-      default:
-        target: 0%
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/_version.py 
new/nbconvert-7.4.0/nbconvert/_version.py
--- old/nbconvert-7.3.1/nbconvert/_version.py   2020-02-02 01:00:00.000000000 
+0100
+++ new/nbconvert-7.4.0/nbconvert/_version.py   2020-02-02 01:00:00.000000000 
+0100
@@ -3,7 +3,7 @@
 from typing import List
 
 # Version string must appear intact for versioning
-__version__ = "7.3.1"
+__version__ = "7.4.0"
 
 # Build up version_info tuple for backwards compatibility
 pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/asciidoc.py 
new/nbconvert-7.4.0/nbconvert/exporters/asciidoc.py
--- old/nbconvert-7.3.1/nbconvert/exporters/asciidoc.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/asciidoc.py 2020-02-02 
01:00:00.000000000 +0100
@@ -48,5 +48,8 @@
                 "HighlightMagicsPreprocessor": {"enabled": True},
             }
         )
-        c.merge(super().default_config)
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
         return c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/exporter.py 
new/nbconvert-7.4.0/nbconvert/exporters/exporter.py
--- old/nbconvert-7.3.1/nbconvert/exporters/exporter.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/exporter.py 2020-02-02 
01:00:00.000000000 +0100
@@ -95,6 +95,7 @@
             "nbconvert.preprocessors.LatexPreprocessor",
             "nbconvert.preprocessors.HighlightMagicsPreprocessor",
             "nbconvert.preprocessors.ExtractOutputPreprocessor",
+            "nbconvert.preprocessors.ExtractAttachmentsPreprocessor",
             "nbconvert.preprocessors.ClearMetadataPreprocessor",
         ],
         help="""List of preprocessors available by default, by name, namespace,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/html.py 
new/nbconvert-7.4.0/nbconvert/exporters/html.py
--- old/nbconvert-7.3.1/nbconvert/exporters/html.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/html.py     2020-02-02 
01:00:00.000000000 +0100
@@ -187,7 +187,10 @@
                 "HighlightMagicsPreprocessor": {"enabled": True},
             }
         )
-        c.merge(super().default_config)
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
         return c
 
     @contextfilter
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/latex.py 
new/nbconvert-7.4.0/nbconvert/exporters/latex.py
--- old/nbconvert-7.3.1/nbconvert/exporters/latex.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/latex.py    2020-02-02 
01:00:00.000000000 +0100
@@ -54,6 +54,7 @@
                         "text/plain",
                     ]
                 },
+                "ExtractAttachmentsPreprocessor": {"enabled": True},
                 "ExtractOutputPreprocessor": {"enabled": True},
                 "SVG2PDFPreprocessor": {"enabled": True},
                 "LatexPreprocessor": {"enabled": True},
@@ -61,7 +62,10 @@
                 "HighlightMagicsPreprocessor": {"enabled": True},
             }
         )
-        c.merge(super().default_config)
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
         return c
 
     def from_notebook_node(self, nb, resources=None, **kw):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/markdown.py 
new/nbconvert-7.4.0/nbconvert/exporters/markdown.py
--- old/nbconvert-7.3.1/nbconvert/exporters/markdown.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/markdown.py 2020-02-02 
01:00:00.000000000 +0100
@@ -34,6 +34,7 @@
     def default_config(self):
         c = Config(
             {
+                "ExtractAttachmentsPreprocessor": {"enabled": True},
                 "ExtractOutputPreprocessor": {"enabled": True},
                 "NbConvertBase": {
                     "display_data_priority": [
@@ -49,5 +50,8 @@
                 "HighlightMagicsPreprocessor": {"enabled": True},
             }
         )
-        c.merge(super().default_config)
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
         return c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/pdf.py 
new/nbconvert-7.4.0/nbconvert/exporters/pdf.py
--- old/nbconvert-7.3.1/nbconvert/exporters/pdf.py      2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/pdf.py      2020-02-02 
01:00:00.000000000 +0100
@@ -140,7 +140,7 @@
                     stdout=stdout,
                     stderr=subprocess.STDOUT,
                     stdin=null,
-                    shell=shell,
+                    shell=shell,  # noqa
                     env=env,
                 )
                 out, _ = p.communicate()
@@ -210,8 +210,9 @@
         # convert output extension to pdf
         # the writer above required it to be tex
         resources["output_extension"] = ".pdf"
-        # clear figure outputs, extracted by latex export,
+        # clear figure outputs and attachments, extracted by latex export,
         # so we don't claim to be a multi-file export.
         resources.pop("outputs", None)
+        resources.pop("attachments", None)
 
         return pdf_data, resources
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/exporters/rst.py 
new/nbconvert-7.4.0/nbconvert/exporters/rst.py
--- old/nbconvert-7.3.1/nbconvert/exporters/rst.py      2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/rst.py      2020-02-02 
01:00:00.000000000 +0100
@@ -33,5 +33,8 @@
                 "HighlightMagicsPreprocessor": {"enabled": True},
             }
         )
-        c.merge(super().default_config)
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
         return c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nbconvert-7.3.1/nbconvert/exporters/templateexporter.py 
new/nbconvert-7.4.0/nbconvert/exporters/templateexporter.py
--- old/nbconvert-7.3.1/nbconvert/exporters/templateexporter.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/templateexporter.py 2020-02-02 
01:00:00.000000000 +0100
@@ -183,7 +183,10 @@
                 "TagRemovePreprocessor": {"enabled": True},
             }
         )
-        c.merge(super().default_config)
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
         return c
 
     template_name = Unicode(help="Name of the template to use").tag(
@@ -645,20 +648,16 @@
                         break
                 if not found_at_least_one:
                     paths = "\n\t".join(root_dirs)
-                    raise ValueError(
-                        "No template sub-directory with name %r found in the 
following paths:\n\t%s"
-                        % (base_template, paths)
-                    )
+                    msg = f"No template sub-directory with name 
{base_template!r} found in the following paths:\n\t{paths}"
+                    raise ValueError(msg)
             merged_conf = recursive_update(dict(conf), merged_conf)
             base_template = conf.get("base_template")
         conf = merged_conf
         mimetypes = [mimetype for mimetype, enabled in conf.get("mimetypes", 
{}).items() if enabled]
         if self.output_mimetype and self.output_mimetype not in mimetypes and 
mimetypes:
             supported_mimetypes = "\n\t".join(mimetypes)
-            raise ValueError(
-                "Unsupported mimetype %r for template %r, mimetypes supported 
are: \n\t%s"
-                % (self.output_mimetype, self.template_name, 
supported_mimetypes)
-            )
+            msg = f"Unsupported mimetype {self.output_mimetype!r} for template 
{self.template_name!r}, mimetypes supported are: \n\t{supported_mimetypes}"
+            raise ValueError(msg)
         return template_names
 
     def get_prefix_root_dirs(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nbconvert-7.3.1/nbconvert/exporters/tests/test_exporter.py 
new/nbconvert-7.4.0/nbconvert/exporters/tests/test_exporter.py
--- old/nbconvert-7.3.1/nbconvert/exporters/tests/test_exporter.py      
2020-02-02 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/exporters/tests/test_exporter.py      
2020-02-02 01:00:00.000000000 +0100
@@ -19,8 +19,8 @@
 from traitlets.config import Config
 
 from ...preprocessors.base import Preprocessor
+from .. import Exporter, TemplateExporter
 from ..base import get_export_names
-from ..exporter import Exporter
 from .base import ExportersTestsBase
 
 # -----------------------------------------------------------------------------
@@ -38,6 +38,21 @@
         return nb, resources
 
 
+class DummyExporter(TemplateExporter):
+    """
+    Dummy exporter to check that parent default_config gets overwritten 
properly
+    """
+
+    @property
+    def default_config(self):
+        c = Config({"TagRemovePreprocessor": {"enabled": False}})
+        if super().default_config:
+            c2 = super().default_config.copy()
+            c2.merge(c)
+            c = c2
+        return c
+
+
 class TestExporter(ExportersTestsBase):
     """Contains test functions for exporter.py"""
 
@@ -87,3 +102,12 @@
         del os.environ["NBCONVERT_DISABLE_CONFIG_EXPORTERS"]
         export_names = get_export_names(config=config)
         self.assertEqual(export_names, ["notebook"])
+
+    def test_default_config_merge(self):
+        """
+        Do default_configs merge properly?
+        Class config should overwrite parent config
+        """
+        e = DummyExporter()
+        self.assertFalse(e.default_config["TagRemovePreprocessor"]["enabled"])
+        self.assertTrue(e.default_config["RegexRemovePreprocessor"]["enabled"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/nbconvertapp.py 
new/nbconvert-7.4.0/nbconvert/nbconvertapp.py
--- old/nbconvert-7.3.1/nbconvert/nbconvertapp.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/nbconvertapp.py       2020-02-02 
01:00:00.000000000 +0100
@@ -218,7 +218,7 @@
     output_base = Unicode(
         "{notebook_name}",
         help="""Overwrite base name use for output files.
-            Supports pattern replacements '{notebook_name}' and 
'{notebook_filename}'.
+            Supports pattern replacements '{notebook_name}'.
             """,
     ).tag(config=True)
 
@@ -420,9 +420,7 @@
         """
         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
-        )
+        notebook_name = self.output_base.format(notebook_name=notebook_name)
 
         return notebook_name
 
@@ -513,7 +511,7 @@
             raise KeyError(msg)
 
         notebook_name = resources["unique_key"]
-        if self.use_output_suffix:
+        if self.use_output_suffix and self.output_base == "{notebook_name}":
             notebook_name += resources.get("output_suffix", "")
 
         write_results = self.writer.write(output, resources, 
notebook_name=notebook_name)
@@ -585,18 +583,6 @@
             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:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/preprocessors/__init__.py 
new/nbconvert-7.4.0/nbconvert/preprocessors/__init__.py
--- old/nbconvert-7.3.1/nbconvert/preprocessors/__init__.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/preprocessors/__init__.py     2020-02-02 
01:00:00.000000000 +0100
@@ -11,6 +11,7 @@
 from .convertfigures import ConvertFiguresPreprocessor
 from .csshtmlheader import CSSHTMLHeaderPreprocessor
 from .execute import ExecutePreprocessor
+from .extractattachments import ExtractAttachmentsPreprocessor
 from .extractoutput import ExtractOutputPreprocessor
 from .highlightmagics import HighlightMagicsPreprocessor
 from .latex import LatexPreprocessor
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nbconvert-7.3.1/nbconvert/preprocessors/extractattachments.py 
new/nbconvert-7.4.0/nbconvert/preprocessors/extractattachments.py
--- old/nbconvert-7.3.1/nbconvert/preprocessors/extractattachments.py   
1970-01-01 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/preprocessors/extractattachments.py   
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,108 @@
+"""
+Module that extracts attachments from notebooks into their own files
+"""
+
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import os
+from base64 import b64decode
+
+from traitlets import Bool, Unicode
+
+from .base import Preprocessor
+
+
+class ExtractAttachmentsPreprocessor(Preprocessor):
+    """
+    Extracts attachments from all (markdown and raw) cells in a notebook.
+    The extracted attachments are stored in a directory ('attachments' by 
default).
+    
https://nbformat.readthedocs.io/en/latest/format_description.html#cell-attachments
+    """
+
+    attachments_directory_template = Unicode(
+        "{notebook_name}_attachments",
+        help="Directory to place attachments if use_separate_dir is True",
+    ).tag(config=True)
+
+    use_separate_dir = Bool(
+        False,
+        help="Whether to use output_files_dir (which ExtractOutput also uses) 
or "
+        "create a separate directory for attachments",
+    ).tag(config=True)
+
+    def __init__(self, **kw):
+        """
+        Public constructor
+        """
+        super().__init__(**kw)
+        # directory path,
+        self.path_name = ""  # will be set in self.preprocess, needs resources
+        # Where extracted attachments are stored in resources
+        self.resources_item_key = (
+            "attachments"  # Here as a default, in case someone doesn't want 
to call preprocess
+        )
+
+    # Add condition and configurability here
+    def preprocess(self, nb, resources):
+        """
+        Determine some settings and apply preprocessor to notebook
+        """
+        if self.use_separate_dir:
+            self.path_name = self.attachments_directory_template.format(
+                notebook_name=resources["unique_key"]
+            )
+            # Initialize resources for attachments
+            resources["attachment_files_dir"] = self.path_name
+            resources["attachments"] = {}
+            self.resources_item_key = "attachments"
+        else:
+            # Use same resources as ExtractOutput
+            self.path_name = resources["output_files_dir"]
+            self.resources_item_key = "outputs"
+
+        # Make sure key exists
+        if not isinstance(resources[self.resources_item_key], dict):
+            resources[self.resources_item_key] = {}
+
+        nb, resources = super().preprocess(nb, resources)
+        return nb, resources
+
+    def preprocess_cell(self, cell, resources, index):
+        """
+        Extract attachments to individual files and
+        change references to them.
+        E.g.
+        '![image.png](attachment:021fdd80.png)'
+        becomes
+        '![image.png]({path_name}/021fdd80.png)'
+        Assumes self.path_name and self.resources_item_key is set properly 
(usually in preprocess).
+        """
+        if "attachments" in cell:
+            for fname in cell.attachments:
+                self.log.debug(f"Encountered attachment {fname}")
+
+                # Add file for writer
+
+                # Right now I don't know of a situation where there would be 
multiple
+                # mime types under same filename, and I can't index into it 
without the mimetype.
+                # So I only read the first one.
+                for mimetype in cell.attachments[fname]:
+                    # convert to bytes and decode
+                    data = cell.attachments[fname][mimetype].encode("utf-8")
+                    decoded = b64decode(data)
+                    break
+
+                # FilesWriter wants path to be in attachment filename here
+                new_filename = os.path.join(self.path_name, fname)
+                resources[self.resources_item_key][new_filename] = decoded
+
+                # Edit the reference to the attachment
+
+                # os.path.join on windows uses "\\" separator,
+                # but files like markdown still want "/"
+                if os.path.sep != "/":
+                    new_filename = new_filename.replace(os.path.sep, "/")
+                cell.source = cell.source.replace("attachment:" + fname, 
new_filename)
+
+        return cell, resources
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/preprocessors/svg2pdf.py 
new/nbconvert-7.4.0/nbconvert/preprocessors/svg2pdf.py
--- old/nbconvert-7.3.1/nbconvert/preprocessors/svg2pdf.py      2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/preprocessors/svg2pdf.py      2020-02-02 
01:00:00.000000000 +0100
@@ -54,7 +54,7 @@
     @default("inkscape_version")
     def _inkscape_version_default(self):
         p = subprocess.Popen(
-            [self.inkscape, "--version"], stdout=subprocess.PIPE, 
stderr=subprocess.PIPE
+            [self.inkscape, "--version"], stdout=subprocess.PIPE, 
stderr=subprocess.PIPE  # noqa
         )
         output, _ = p.communicate()
         if p.returncode != 0:
@@ -143,7 +143,7 @@
                 # For backwards compatibility with specifying strings
                 # Okay-ish, since the string is trusted
                 full_cmd = self.command.format(*template_vars)
-            subprocess.call(full_cmd, shell=isinstance(full_cmd, str))
+            subprocess.call(full_cmd, shell=isinstance(full_cmd, str))  # noqa
 
             # Read output from drive
             # return value expects a filename
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nbconvert-7.3.1/nbconvert/preprocessors/tests/base.py 
new/nbconvert-7.4.0/nbconvert/preprocessors/tests/base.py
--- old/nbconvert-7.3.1/nbconvert/preprocessors/tests/base.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/preprocessors/tests/base.py   2020-02-02 
01:00:00.000000000 +0100
@@ -3,6 +3,8 @@
 # Copyright (c) IPython Development Team.
 # Distributed under the terms of the Modified BSD License.
 
+from base64 import b64encode
+
 from nbformat import v4 as nbformat
 
 from ...exporters.exporter import ResourcesDict
@@ -12,7 +14,7 @@
 class PreprocessorTestsBase(TestsBase):
     """Contains test functions preprocessor tests"""
 
-    def build_notebook(self, with_json_outputs=False):
+    def build_notebook(self, with_json_outputs=False, with_attachment=False):
         """Build a notebook in memory for use with preprocessor tests"""
 
         outputs = [
@@ -42,6 +44,19 @@
             nbformat.new_markdown_cell(source="$ e $"),
         ]
 
+        if with_attachment:
+            data = b"test"
+            encoded_data = b64encode(data)
+            # this is conversion of bytes to string, not base64 decoding
+            attachments = {"image.png": {"image/png": encoded_data.decode()}}
+            cells.extend(
+                [
+                    nbformat.new_markdown_cell(
+                        source="![image.png](attachment:image.png)", 
attachments=attachments
+                    )
+                ]
+            )
+
         return nbformat.new_notebook(cells=cells)
 
     def build_resources(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nbconvert-7.3.1/nbconvert/preprocessors/tests/test_extractattachments.py 
new/nbconvert-7.4.0/nbconvert/preprocessors/tests/test_extractattachments.py
--- 
old/nbconvert-7.3.1/nbconvert/preprocessors/tests/test_extractattachments.py    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/nbconvert-7.4.0/nbconvert/preprocessors/tests/test_extractattachments.py    
    2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,87 @@
+"""Tests for the ExtractAttachments preprocessor"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import os
+from base64 import b64decode
+
+from ..extractattachments import ExtractAttachmentsPreprocessor
+from .base import PreprocessorTestsBase
+
+
+class TestExtractAttachments(PreprocessorTestsBase):
+    """Contains test functions for extractattachments.py"""
+
+    def build_preprocessor(self):
+        """Make an instance of a preprocessor"""
+        preprocessor = ExtractAttachmentsPreprocessor()
+        preprocessor.enabled = True
+        return preprocessor
+
+    def test_constructor(self):
+        """Can a ExtractAttachmentsPreprocessor be constructed?"""
+        self.build_preprocessor()
+
+    def test_attachment(self):
+        """Test the output of the ExtractAttachmentsPreprocessor"""
+        nb = self.build_notebook(with_attachment=True)
+        res = self.build_resources()
+        preprocessor = self.build_preprocessor()
+        nb, res = preprocessor(nb, res)
+
+        # Check if attachment was extracted.
+        attachments = nb.cells[-1].attachments
+        self.assertIn("image.png", attachments)
+        self.assertIn("image/png", attachments["image.png"])
+        data = attachments["image.png"]["image/png"]
+        # convert to bytes, b64 decode, convert to str
+        data = b64decode(data.encode("utf-8"))
+        self.assertEqual(data, b"test")
+
+        # Verify attachment
+        self.assertIn("image.png", res["outputs"])
+        self.assertEqual(res["outputs"]["image.png"], b"test")
+
+        # Verify cell source changed appropriately
+        src = nb.cells[-1].source
+        self.assertEqual(src, "![image.png](image.png)")
+
+    def test_attachment_with_directory(self):
+        """Test that cell source modifications are correct when files are put 
in a directory"""
+        nb = self.build_notebook(with_attachment=True)
+        res = self.build_resources()
+        output_dir = "outputs"
+        res["output_files_dir"] = output_dir
+        preprocessor = self.build_preprocessor()
+        nb, res = preprocessor(nb, res)
+
+        # Verify attachment
+        # This can have "\\" separator on Windows
+        file_path = os.path.join("outputs", "image.png")
+        self.assertIn(file_path, res["outputs"])
+
+        # Verify cell source changed appropriately
+        src = nb.cells[-1].source
+        # This shouldn't change on Windows
+        self.assertEqual(src, "![image.png](outputs/image.png)")
+
+    def test_use_separate_dir_config(self):
+        """Test that use_separate_dir and attachment_directory_template work 
properly"""
+        nb = self.build_notebook(with_attachment=True)
+        res = self.build_resources()
+        res["unique_key"] = "notebook1"  # add notebook name for the folder
+        preprocessor = self.build_preprocessor()
+        preprocessor.use_separate_dir = True
+        preprocessor.attachments_directory_template = "{notebook_name}_custom"
+        nb, res = preprocessor(nb, res)
+
+        # Verify attachment
+        # This can have "\\" separator on Windows
+        file_path = os.path.join("notebook1_custom", "image.png")
+        self.assertIn(file_path, res["attachments"])
+
+        # Verify cell source changed appropriately
+        src = nb.cells[-1].source
+        # This shouldn't change on Windows
+        self.assertEqual(src, "![image.png](notebook1_custom/image.png)")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/tests/base.py 
new/nbconvert-7.4.0/nbconvert/tests/base.py
--- old/nbconvert-7.3.1/nbconvert/tests/base.py 2020-02-02 01:00:00.000000000 
+0100
+++ new/nbconvert-7.4.0/nbconvert/tests/base.py 2020-02-02 01:00:00.000000000 
+0100
@@ -161,7 +161,7 @@
             if isinstance(parameters, (str,)):
                 parameters = shlex.split(parameters)
             cmd += parameters
-        p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
+        p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)  # noqa
         stdout, stderr = p.communicate(input=stdin)
         if not (p.returncode == 0 or ignore_return_code):
             raise OSError(stderr.decode("utf8", "replace"))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/tests/test_nbconvertapp.py 
new/nbconvert-7.4.0/nbconvert/tests/test_nbconvertapp.py
--- old/nbconvert-7.3.1/nbconvert/tests/test_nbconvertapp.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/tests/test_nbconvertapp.py    2020-02-02 
01:00:00.000000000 +0100
@@ -431,7 +431,8 @@
             )
             assert 'print("Some text before the error")' in error_output
             assert 'raise RuntimeError("This is a deliberate exception")' in 
error_output
-            assert "RuntimeError: This is a deliberate exception" in 
error_output
+            assert "RuntimeError" in error_output
+            assert "This is a deliberate exception" in error_output
 
     def test_fenced_code_blocks_markdown(self):
         """
@@ -653,16 +654,31 @@
             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")
+        nbname = notebook_names[0]
+        with self.create_temp_cwd([nbname + ".ipynb"]):
+            self.nbconvert(f"{nbname}.ipynb --output notebook_test_name --to 
markdown")
             assert os.path.isfile("notebook_test_name.md")
 
+            self.nbconvert(f"{nbname}.ipynb --to notebook")
+            assert os.path.isfile("notebook1.nbconvert.ipynb")
+
+            self.nbconvert(f"{nbname}.ipynb --to notebook --output out.ipynb")
+            assert os.path.isfile("out.ipynb")
+
         # 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")
+
+    def test_same_filename_different_dir(self):
+        """
+        Check if files with same name in different directories pose a problem
+        """
+        with self.create_temp_cwd() as temp_wd:
+            self.copy_files_to(["notebook1.ipynb"], temp_wd + "/dir1")
+            self.copy_files_to(["notebook1.ipynb"], temp_wd + "/dir2")
+            self.nbconvert("dir1/notebook1.ipynb dir2/notebook1.ipynb --to 
markdown")
+            assert os.path.isfile("dir1/notebook1.md")
+            assert os.path.isfile("dir2/notebook1.md")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/utils/io.py 
new/nbconvert-7.4.0/nbconvert/utils/io.py
--- old/nbconvert-7.3.1/nbconvert/utils/io.py   2020-02-02 01:00:00.000000000 
+0100
+++ new/nbconvert-7.4.0/nbconvert/utils/io.py   2020-02-02 01:00:00.000000000 
+0100
@@ -117,21 +117,3 @@
         # Either link isn't supported, or the filesystem doesn't support
         # linking, or 'src' and 'dst' are on different filesystems.
         shutil.copy(src, dst)
-
-
-def ensure_dir_exists(path, mode=0o755):
-    """ensure that a directory exists
-
-    If it doesn't exist, try to create it and protect against a race condition
-    if another process is doing the same.
-
-    The default permissions are 755, which differ from os.makedirs default of 
777.
-    """
-    if not os.path.exists(path):
-        try:
-            os.makedirs(path, mode=mode)
-        except OSError as e:
-            if e.errno != errno.EEXIST:
-                raise
-    elif not os.path.isdir(path):
-        raise OSError("%r exists but is not a directory" % path)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/utils/pandoc.py 
new/nbconvert-7.4.0/nbconvert/utils/pandoc.py
--- old/nbconvert-7.3.1/nbconvert/utils/pandoc.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/utils/pandoc.py       2020-02-02 
01:00:00.000000000 +0100
@@ -51,7 +51,7 @@
     check_pandoc_version()
 
     # we can safely continue
-    p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+    p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)  
# noqa
     out, _ = p.communicate(source.encode())
     out_str = TextIOWrapper(BytesIO(out), encoding, "replace").read()
     return out_str.rstrip("\n")
@@ -75,7 +75,7 @@
         if not shutil.which("pandoc"):
             raise PandocMissing()
 
-        out = subprocess.check_output(["pandoc", "-v"])
+        out = subprocess.check_output(["pandoc", "-v"])  # noqa
         out_lines = out.splitlines()
         version_pattern = re.compile(r"^\d+(\.\d+){1,}$")
         for tok in out_lines[0].decode("ascii", "replace").split():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/nbconvert/writers/files.py 
new/nbconvert-7.4.0/nbconvert/writers/files.py
--- old/nbconvert-7.3.1/nbconvert/writers/files.py      2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/writers/files.py      2020-02-02 
01:00:00.000000000 +0100
@@ -3,13 +3,14 @@
 # Copyright (c) IPython Development Team.
 # Distributed under the terms of the Modified BSD License.
 
+import errno
 import glob
 import os
 from pathlib import Path
 
 from traitlets import Unicode, observe
 
-from nbconvert.utils.io import ensure_dir_exists, link_or_copy
+from nbconvert.utils.io import link_or_copy
 
 from .base import WriterBase
 
@@ -38,18 +39,43 @@
     def _build_directory_changed(self, change):
         new = change["new"]
         if new:
-            ensure_dir_exists(new)
+            self._makedir(new)
 
     def __init__(self, **kw):
         """Initialize the writer."""
         super().__init__(**kw)
         self._build_directory_changed({"new": self.build_directory})
 
-    def _makedir(self, path):
-        """Make a directory if it doesn't already exist"""
-        if path:
+    def _makedir(self, path, mode=0o755):
+        """ensure that a directory exists
+
+        If it doesn't exist, try to create it and protect against a race 
condition
+        if another process is doing the same.
+
+        The default permissions are 755, which differ from os.makedirs default 
of 777.
+        """
+        if not os.path.exists(path):
             self.log.info("Making directory %s", path)
-            ensure_dir_exists(path)
+            try:
+                os.makedirs(path, mode=mode)
+            except OSError as e:
+                if e.errno != errno.EEXIST:
+                    raise
+        elif not os.path.isdir(path):
+            raise OSError("%r exists but is not a directory" % path)
+
+    def _write_items(self, items, build_dir):
+        """Write a dict containing filename->binary data"""
+        for filename, data in items:
+            # Determine where to write the file to
+            dest = os.path.join(build_dir, filename)
+            path = os.path.dirname(dest)
+            self._makedir(path)
+
+            # Write file
+            self.log.debug("Writing %i bytes to %s", len(data), dest)
+            with open(dest, "wb") as f:
+                f.write(data)
 
     def write(self, output, resources, notebook_name=None, **kw):
         """
@@ -73,7 +99,7 @@
         relpath = self.relpath or resource_path
         build_directory = self.build_directory or resource_path
 
-        # Write all of the extracted resources to the destination directory.
+        # Write the extracted outputs to the destination directory.
         # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY.  THE EXTRACT FIG
         # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
 
@@ -83,16 +109,17 @@
                 "Support files will be in %s",
                 os.path.join(resources.get("output_files_dir", ""), ""),
             )
-        for filename, data in items:
-            # Determine where to write the file to
-            dest = os.path.join(build_directory, filename)
-            path = os.path.dirname(dest)
-            self._makedir(path)
+            self._write_items(items, build_directory)
 
-            # Write file
-            self.log.debug("Writing %i bytes to support file %s", len(data), 
dest)
-            with open(dest, "wb") as f:
-                f.write(data)
+        # Write the extracted attachments
+        # if ExtractAttachmentsOutput specified a separate directory
+        attachs = resources.get("attachments", {}).items()
+        if attachs:
+            self.log.info(
+                "Attachments will be in %s",
+                os.path.join(resources.get("attachment_files_dir", ""), ""),
+            )
+            self._write_items(attachs, build_directory)
 
         # Copy referenced files to output directory
         if build_directory:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nbconvert-7.3.1/nbconvert/writers/tests/test_files.py 
new/nbconvert-7.4.0/nbconvert/writers/tests/test_files.py
--- old/nbconvert-7.3.1/nbconvert/writers/tests/test_files.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/nbconvert/writers/tests/test_files.py   2020-02-02 
01:00:00.000000000 +0100
@@ -19,7 +19,7 @@
 
         # Work in a temporary directory.
         with self.create_temp_cwd():
-            # Create the resoruces dictionary
+            # Create the resources dictionary
             res: dict = {}
 
             # Create files writer, test output
@@ -54,8 +54,11 @@
 
         # Work in a temporary directory.
         with self.create_temp_cwd():
-            # Create the resoruces dictionary
-            res = {"outputs": {os.path.join("z_files", "a"): b"b"}}
+            # Create the resources dictionary
+            res = {
+                "outputs": {os.path.join("z_files", "a"): b"b"},
+                "attachments": {os.path.join("z_attachments", "c"): b"d"},
+            }
 
             # Create files writer, test output
             writer = FilesWriter()
@@ -73,12 +76,18 @@
                 output = f.read()
                 self.assertEqual(output, "b")
 
+            attachment_file_dest = os.path.join("z_attachments", "c")
+            assert os.path.isfile(attachment_file_dest)
+            with open(attachment_file_dest) as f:
+                content = f.read()
+                self.assertEqual(content, "d")
+
     def test_build_dir(self):
         """Can FilesWriter write to a build dir correctly?"""
 
         # Work in a temporary directory.
         with self.create_temp_cwd():
-            # Create the resoruces dictionary
+            # Create the resources dictionary
             res = {"outputs": {os.path.join("z_files", "a"): b"b"}}
 
             # Create files writer, test output
@@ -122,7 +131,7 @@
             with open(os.path.join("sub", "c"), "w") as f:
                 f.write("d")
 
-            # Create the resoruces dictionary
+            # Create the resources dictionary
             res: dict = {}
 
             # Create files writer, test output
@@ -195,7 +204,7 @@
             with open(os.path.join("sub", "c"), "w") as f:
                 f.write("d")
 
-            # Create the resoruces dictionary
+            # Create the resources dictionary
             res: dict = {}
 
             # Create files writer, test output
@@ -229,7 +238,7 @@
             with open(os.path.join("sub", "c"), "w") as f:
                 f.write("d")
 
-            # Create the resoruces dictionary
+            # Create the resources dictionary
             res = {"metadata": {"path": "sub"}}
 
             # Create files writer, test output
@@ -262,7 +271,7 @@
             with open(os.path.join("sub", "c"), "w") as f:
                 f.write("d")
 
-            # Create the resoruces dictionary
+            # Create the resources dictionary
             res = {"metadata": {"path": "other_sub"}}
 
             # Create files writer, test output
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nbconvert-7.3.1/pyproject.toml 
new/nbconvert-7.4.0/pyproject.toml
--- old/nbconvert-7.3.1/pyproject.toml  2020-02-02 01:00:00.000000000 +0100
+++ new/nbconvert-7.4.0/pyproject.toml  2020-02-02 01:00:00.000000000 +0100
@@ -113,7 +113,7 @@
 
 [tool.hatch.envs.cov]
 features = ["all"]
-dependencies = ["coverage", "pytest-cov"]
+dependencies = ["coverage[toml]", "pytest-cov"]
 [tool.hatch.envs.cov.scripts]
 test = "python -m pytest -vv --cov nbconvert --cov-branch --cov-report 
term-missing:skip-covered {args}"
 nowarn = "test -W default {args}"
@@ -129,7 +129,7 @@
   "black[jupyter]==23.3.0",
   "mdformat>0.7",
   "mdformat-gfm>=0.3.5",
-  "ruff==0.0.260"
+  "ruff==0.0.263"
 ]
 detached = true
 [tool.hatch.envs.lint.scripts]
@@ -179,6 +179,10 @@
 "@(abc\\.)?abstractmethod",
 ]
 
+[tool.coverage.run]
+relative_files = true
+source = ["nbconvert"]
+
 [tool.mypy]
 check_untyped_defs = true
 disallow_incomplete_defs = true

Reply via email to