diff --git a/nbconvert/exporters/pdf.py b/nbconvert/exporters/pdf.py index 420031cf3..193045cb0 100644 --- a/nbconvert/exporters/pdf.py +++ b/nbconvert/exporters/pdf.py @@ -6,12 +6,13 @@ import subprocess import os import sys +from tempfile import TemporaryDirectory import shutil from traitlets import Integer, List, Bool, Instance, Unicode, default -from testpath.tempdir import TemporaryWorkingDirectory from typing import Optional from .latex import LatexExporter +from ..utils import _contextlib_chdir class LatexFailed(IOError): """Exception for failed latex run @@ -175,7 +176,7 @@ def from_notebook_node(self, nb, resources=None, **kw): self.texinputs = os.getcwd() self._captured_outputs = [] - with TemporaryWorkingDirectory(): + with TemporaryDirectory() as td, _contextlib_chdir.chdir(td): notebook_name = 'notebook' resources['output_extension'] = '.tex' tex_file = self.writer.write(latex, resources, notebook_name=notebook_name) diff --git a/nbconvert/exporters/tests/test_templateexporter.py b/nbconvert/exporters/tests/test_templateexporter.py index aee6a60dd..26caa8456 100644 --- a/nbconvert/exporters/tests/test_templateexporter.py +++ b/nbconvert/exporters/tests/test_templateexporter.py @@ -14,6 +14,7 @@ from nbformat import v4 from unittest.mock import patch from concurrent.futures import ProcessPoolExecutor +from tempfile import TemporaryDirectory from .base import ExportersTestsBase from .cheese import CheesePreprocessor @@ -21,7 +22,7 @@ from ..rst import RSTExporter from ..html import HTMLExporter from ..markdown import MarkdownExporter -from testpath import tempdir +from ...utils import _contextlib_chdir import pytest @@ -128,7 +129,7 @@ def test_pickle(self): assert len(output) > 0 def test_absolute_template_file(self): - with tempdir.TemporaryDirectory() as td: + with TemporaryDirectory() as td: template = os.path.join(td, 'abstemplate.ext.j2') test_output = 'absolute!' with open(template, 'w') as f: @@ -140,7 +141,7 @@ def test_absolute_template_file(self): assert os.path.dirname(template) in exporter.template_paths def test_relative_template_file(self): - with tempdir.TemporaryWorkingDirectory() as td: + with TemporaryDirectory() as td, _contextlib_chdir.chdir(td): with patch('os.getcwd', return_value=os.path.abspath(td)): template = os.path.join('relative', 'relative_template.ext.j2') template_abs = os.path.abspath(os.path.join(td, template)) @@ -155,7 +156,7 @@ def test_relative_template_file(self): assert os.path.dirname(template_abs) in [os.path.abspath(d) for d in exporter.template_paths] def test_absolute_template_file_compatibility(self): - with tempdir.TemporaryDirectory() as td: + with TemporaryDirectory() as td: template = os.path.join(td, 'abstemplate.tpl') test_output = 'absolute!' with open(template, 'w') as f: @@ -168,7 +169,7 @@ def test_absolute_template_file_compatibility(self): assert os.path.dirname(template) in exporter.template_paths def test_relative_template_file_compatibility(self): - with tempdir.TemporaryWorkingDirectory() as td: + with TemporaryDirectory() as td, _contextlib_chdir.chdir(td): with patch('os.getcwd', return_value=os.path.abspath(td)): template = os.path.join('relative', 'relative_template.tpl') template_abs = os.path.abspath(os.path.join(td, template)) @@ -184,7 +185,7 @@ def test_relative_template_file_compatibility(self): assert os.path.dirname(template_abs) in [os.path.abspath(d) for d in exporter.template_paths] def test_absolute_template_name_tpl_compatibility(self): - with tempdir.TemporaryDirectory() as td: + with TemporaryDirectory() as td: template = os.path.join(td, 'abstemplate.tpl') test_output = 'absolute!' with open(template, 'w') as f: @@ -218,7 +219,7 @@ def test_absolute_template_name_5x_compatibility_display_priority(self): # Can't use @pytest.mark.parametrize without removing all self.assert calls in all tests... repeating some here def relative_template_test(self, template): - with tempdir.TemporaryWorkingDirectory() as td: + with TemporaryDirectory() as td, _contextlib_chdir.chdir(td): with patch('os.getcwd', return_value=os.path.abspath(td)): template_abs = os.path.abspath(os.path.join(td, template)) dirname = os.path.dirname(template_abs) @@ -248,7 +249,7 @@ def test_relative_template_name_tpl_compatibility_dot_nested(self): self.relative_template_test(os.path.join('.', 'relative', 'relative_template.tpl')) def test_absolute_template_dir(self): - with tempdir.TemporaryDirectory() as td: + with TemporaryDirectory() as td: template = 'mytemplate' template_file = os.path.join(td, template, 'index.py.j2') template_dir = os.path.dirname(template_file) @@ -265,7 +266,7 @@ def test_absolute_template_dir(self): assert os.path.join(td, template) in exporter.template_paths def test_local_template_dir(self): - with tempdir.TemporaryWorkingDirectory() as td: + with TemporaryDirectory() as td, _contextlib_chdir.chdir(td): with patch('os.getcwd', return_value=os.path.abspath(td)): template = 'mytemplate' template_file = os.path.join(template, 'index.py.j2') diff --git a/nbconvert/tests/base.py b/nbconvert/tests/base.py index 9f6d7a8e0..c5bfe4147 100644 --- a/nbconvert/tests/base.py +++ b/nbconvert/tests/base.py @@ -3,6 +3,7 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import contextlib import io import os import glob @@ -12,9 +13,11 @@ import unittest import nbconvert from subprocess import Popen, PIPE +from tempfile import TemporaryDirectory from nbformat import v4, write -from testpath.tempdir import TemporaryWorkingDirectory + +from ..utils import _contextlib_chdir class TestsBase(unittest.TestCase): @@ -86,15 +89,12 @@ def recursive_replace(self, text, search, replacement): text = text.replace(search, replacement) return text + @contextlib.contextmanager def create_temp_cwd(self, copy_filenames=None): - temp_dir = TemporaryWorkingDirectory() - - #Copy the files if requested. - if copy_filenames is not None: - self.copy_files_to(copy_filenames, dest=temp_dir.name) - - #Return directory handler - return temp_dir + with TemporaryDirectory() as td, _contextlib_chdir.chdir(td): + if copy_filenames is not None: # Copy the files if requested. + self.copy_files_to(copy_filenames, dest=td) + yield td # Return directory handler @classmethod def merge_dicts(cls, *dict_args): diff --git a/nbconvert/utils/_contextlib_chdir.py b/nbconvert/utils/_contextlib_chdir.py new file mode 100644 index 000000000..70f1ee997 --- /dev/null +++ b/nbconvert/utils/_contextlib_chdir.py @@ -0,0 +1,20 @@ +"""Backport of Python 3.11's contextlib.chdir.""" + + +from contextlib import AbstractContextManager +import os + + +class chdir(AbstractContextManager): + """Non thread-safe context manager to change the current working directory.""" + + def __init__(self, path): + self.path = path + self._old_cwd = [] + + def __enter__(self): + self._old_cwd.append(os.getcwd()) + os.chdir(self.path) + + def __exit__(self, *excinfo): + os.chdir(self._old_cwd.pop()) diff --git a/setup.py b/setup.py index 1aad5035b..80ee6471b 100644 --- a/setup.py +++ b/setup.py @@ -222,7 +222,6 @@ def get_data_files(): 'entrypoints>=0.2.2', 'bleach', 'pandocfilters>=1.4.1', - 'testpath', 'defusedxml', 'nbclient>=0.5.0,<0.6.0' ]