From a7df9afbf165e397c31d6b79c66cff8288e4a42e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 12 Dec 2023 07:23:22 -0600 Subject: [PATCH] Update ruff config (#2079) --- .github/workflows/tests.yml | 14 +---- .pre-commit-config.yaml | 4 +- docs/autogen_config.py | 1 - docs/source/conf.py | 10 ++-- hatch_build.py | 2 +- nbconvert/_version.py | 2 +- nbconvert/exporters/base.py | 18 ++---- nbconvert/exporters/exporter.py | 19 ++++--- nbconvert/exporters/html.py | 9 +-- nbconvert/exporters/pdf.py | 13 ++--- nbconvert/exporters/qt_exporter.py | 8 +-- nbconvert/exporters/qtpdf.py | 2 +- nbconvert/exporters/qtpng.py | 2 +- nbconvert/exporters/script.py | 2 +- nbconvert/exporters/templateexporter.py | 33 ++++++----- nbconvert/exporters/webpdf.py | 2 +- nbconvert/filters/ansi.py | 10 ++-- nbconvert/filters/citation.py | 2 +- nbconvert/filters/filter_links.py | 3 +- nbconvert/filters/highlight.py | 3 +- nbconvert/filters/markdown_mistune.py | 8 +-- nbconvert/filters/strings.py | 6 +- nbconvert/nbconvertapp.py | 12 ++-- nbconvert/postprocessors/serve.py | 6 +- nbconvert/preprocessors/base.py | 3 +- nbconvert/preprocessors/clearmetadata.py | 7 +-- nbconvert/preprocessors/csshtmlheader.py | 2 +- nbconvert/preprocessors/execute.py | 6 +- nbconvert/preprocessors/extractattachments.py | 2 +- nbconvert/preprocessors/extractoutput.py | 2 +- nbconvert/preprocessors/highlightmagics.py | 3 +- nbconvert/preprocessors/sanitize.py | 12 ++-- nbconvert/preprocessors/svg2pdf.py | 2 +- nbconvert/utils/_contextlib_chdir.py | 2 +- nbconvert/utils/exceptions.py | 4 +- nbconvert/utils/io.py | 4 +- nbconvert/utils/lexers.py | 2 +- nbconvert/utils/pandoc.py | 8 +-- nbconvert/utils/text.py | 5 +- nbconvert/writers/debug.py | 2 +- nbconvert/writers/stdout.py | 3 +- pyproject.toml | 55 +++++++++++-------- tests/base.py | 4 +- tests/exporters/test_templateexporter.py | 3 +- tests/exporters/test_webpdf.py | 4 +- tests/filters/test_citation.py | 2 +- tests/filters/test_markdown.py | 2 +- tests/test_nbconvertapp.py | 4 +- tests/utils/test_io.py | 3 +- 49 files changed, 159 insertions(+), 178 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62d332128..c3ff24be8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,13 +23,14 @@ jobs: strategy: matrix: os: ["ubuntu-20.04", "macos-latest", "windows-latest"] - python-version: ["3.8", "3.11"] + python-version: ["3.8", "3.12"] include: - os: "windows-latest" python-version: "3.9" - - os: "ubuntu-20.04" + - os: "macos-latest" python-version: "3.10" - os: "ubuntu-20.04" + python-version: "3.11" fail-fast: false steps: - uses: actions/checkout@v4 @@ -57,15 +58,6 @@ jobs: - uses: jupyterlab/maintainer-tools/.github/actions/upload-coverage@v1 - python312: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - with: - python_version: "3.12" - - run: hatch run test:test - coverage: runs-on: ubuntu-latest needs: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0fce3c940..06a2dd0e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.2 + rev: 0.27.3 hooks: - id: check-github-workflows @@ -81,7 +81,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.1.7 hooks: - id: ruff types_or: [python, jupyter] diff --git a/docs/autogen_config.py b/docs/autogen_config.py index 506211452..abd64c867 100644 --- a/docs/autogen_config.py +++ b/docs/autogen_config.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ autogen_config.py diff --git a/docs/source/conf.py b/docs/source/conf.py index b8c413bf3..f26770ed5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # nbconvert documentation build configuration file, created by # sphinx-quickstart on Tue Jun 9 17:11:30 2015. # @@ -25,7 +23,7 @@ # Automatically generate config_options.rst with open(os.path.join(HERE, "..", "autogen_config.py")) as f: - exec(compile(f.read(), "autogen_config.py", "exec"), {}) # noqa + exec(compile(f.read(), "autogen_config.py", "exec"), {}) # noqa: S102 print("Created docs for config options") # -- General configuration ------------------------------------------------ @@ -47,7 +45,7 @@ ] try: - import enchant # type:ignore # noqa + import enchant # noqa: F401 extensions += ["sphinxcontrib.spelling"] except ImportError: @@ -74,7 +72,7 @@ year = datetime.now(tz=timezone.utc).date().year -copyright = "2015-%s, Jupyter Development Team" % year # noqa +copyright = "2015-%s, Jupyter Development Team" % year author = "Jupyter Development Team" extlinks = {"ghpull": ("https://github.com/jupyter/nbconvert/pull/%s", "PR #%s")} @@ -90,7 +88,7 @@ # Get information from _version.py and use it to generate version and release _version_py = os.path.join(HERE, "../../nbconvert/_version.py") version_ns = {} -exec(compile(open(_version_py).read(), _version_py, "exec"), version_ns) # noqa +exec(compile(open(_version_py).read(), _version_py, "exec"), version_ns) # noqa: SIM115, S102 # The short X.Y version. version = "%i.%i" % version_ns["version_info"][:2] # The full version, including alpha/beta/rc tags. diff --git a/hatch_build.py b/hatch_build.py index 04958f928..777fbf949 100644 --- a/hatch_build.py +++ b/hatch_build.py @@ -47,7 +47,7 @@ def _get_css_file(template_name, url, filename): os.makedirs(directory) print("Downloading CSS: %s" % url) try: - css = urlopen(url).read() # noqa + css = urlopen(url).read() # noqa: S310 except Exception as e: msg = f"Failed to download css from {url}: {e}" print(msg, file=sys.stderr) diff --git a/nbconvert/_version.py b/nbconvert/_version.py index 562921411..ae30c74aa 100644 --- a/nbconvert/_version.py +++ b/nbconvert/_version.py @@ -8,7 +8,7 @@ # Build up version_info tuple for backwards compatibility pattern = r"(?P\d+).(?P\d+).(?P\d+)(?P.*)" match = re.match(pattern, __version__) -assert match is not None # noqa +assert match is not None parts: List[object] = [int(match[part]) for part in ["major", "minor", "patch"]] if match["rest"]: parts.append(match["rest"]) diff --git a/nbconvert/exporters/base.py b/nbconvert/exporters/base.py index bc19573bc..b71f10258 100644 --- a/nbconvert/exporters/base.py +++ b/nbconvert/exporters/base.py @@ -33,14 +33,10 @@ class ExporterNameError(NameError): """An exporter name error.""" - pass - class ExporterDisabledError(ValueError): """An exporter disabled error.""" - pass - def export(exporter, nb, **kw): """ @@ -74,7 +70,7 @@ def export(exporter, nb, **kw): if exporter is None: msg = "Exporter is None" raise TypeError(msg) - elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter): + if not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter): msg = "exporter does not inherit from Exporter (base)" raise TypeError(msg) if nb is None: @@ -95,7 +91,7 @@ def export(exporter, nb, **kw): return output, resources -def get_exporter(name, config=get_config()): # noqa +def get_exporter(name, config=get_config()): # noqa: B008 """Given an exporter name or import path, return a class ready to be instantiated Raises ExporterName if exporter is not found or ExporterDisabledError if not enabled @@ -110,8 +106,7 @@ def get_exporter(name, config=get_config()): # noqa exporter = items[0].load() if getattr(exporter(config=config), "enabled", True): return exporter - else: - raise ExporterDisabledError('Exporter "%s" disabled in configuration' % (name)) + raise ExporterDisabledError('Exporter "%s" disabled in configuration' % (name)) except IndexError: pass @@ -120,11 +115,10 @@ def get_exporter(name, config=get_config()): # noqa exporter = import_item(name) if getattr(exporter(config=config), "enabled", True): return exporter - else: - raise ExporterDisabledError('Exporter "%s" disabled in configuration' % (name)) + raise ExporterDisabledError('Exporter "%s" disabled in configuration' % (name)) except ImportError: log = get_logger() - log.error("Error importing %s" % name, exc_info=True) + log.error("Error importing %s", name, exc_info=True) # noqa: G201 msg = 'Unknown exporter "{}", did you mean one of: {}?'.format( name, ", ".join(get_export_names()) @@ -132,7 +126,7 @@ def get_exporter(name, config=get_config()): # noqa raise ExporterNameError(msg) -def get_export_names(config=get_config()): # noqa +def get_export_names(config=get_config()): # noqa: B008 """Return a list of the currently supported export targets Exporters can be found in external packages by registering diff --git a/nbconvert/exporters/exporter.py b/nbconvert/exporters/exporter.py index cd122d61b..309a22fca 100644 --- a/nbconvert/exporters/exporter.py +++ b/nbconvert/exporters/exporter.py @@ -253,7 +253,7 @@ def register_preprocessor(self, preprocessor, enabled=False): preprocessor_cls = import_item(preprocessor) return self.register_preprocessor(preprocessor_cls, enabled) - if constructed and hasattr(preprocessor, "__call__"): # noqa + if constructed and callable(preprocessor): # Preprocessor is a function, no need to construct it. # Register and return the preprocessor. if enabled: @@ -261,21 +261,22 @@ def register_preprocessor(self, preprocessor, enabled=False): self._preprocessors.append(preprocessor) return preprocessor - elif isclass and issubclass(preprocessor, HasTraits): + if isclass and issubclass(preprocessor, HasTraits): # Preprocessor is configurable. Make sure to pass in new default for # the enabled flag if one was specified. self.register_preprocessor(preprocessor(parent=self), enabled) + return None - elif isclass: + if isclass: # Preprocessor is not configurable, construct it self.register_preprocessor(preprocessor(), enabled) + return None - else: - # Preprocessor is an instance of something without a __call__ - # attribute. - raise TypeError( - "preprocessor must be callable or an importable constructor, got %r" % preprocessor - ) + # Preprocessor is an instance of something without a __call__ + # attribute. + raise TypeError( + "preprocessor must be callable or an importable constructor, got %r" % preprocessor + ) def _init_preprocessors(self): """ diff --git a/nbconvert/exporters/html.py b/nbconvert/exporters/html.py index c2d14be4d..9086c83b8 100644 --- a/nbconvert/exporters/html.py +++ b/nbconvert/exporters/html.py @@ -213,8 +213,9 @@ def default_config(self): def _valid_language_code(self, proposal): if self.language_code not in iso639_1: self.log.warning( - f'"{self.language_code}" is not an ISO 639-1 language code. ' - 'It has been replaced by the default value "en".' + '"%s" is not an ISO 639-1 language code. ' + 'It has been replaced by the default value "en".', + self.language_code, ) return proposal["trait"].default_value return proposal["value"] @@ -265,7 +266,7 @@ def from_notebook_node( # type:ignore[explicit-override, override] elem.attrs["alt"] = "No description has been provided for this image" missing_alt += 1 if missing_alt: - self.log.warning(f"Alternative text is missing on {missing_alt} image(s).") + self.log.warning("Alternative text is missing on %s image(s).", missing_alt) # Set input and output focusable for elem in soup.select(".jp-Notebook div.jp-Cell-inputWrapper"): elem.attrs["tabindex"] = "0" @@ -274,7 +275,7 @@ def from_notebook_node( # type:ignore[explicit-override, override] return str(soup), resources - def _init_resources(self, resources): # noqa + def _init_resources(self, resources): def resources_include_css(name): env = self.environment code = """""" % (env.loader.get_source(env, name)[0]) diff --git a/nbconvert/exporters/pdf.py b/nbconvert/exporters/pdf.py index de5dd0e41..4226b44b7 100644 --- a/nbconvert/exporters/pdf.py +++ b/nbconvert/exporters/pdf.py @@ -17,7 +17,7 @@ from .latex import LatexExporter -class LatexFailed(IOError): # noqa +class LatexFailed(IOError): """Exception for failed latex run Captured latex output is in error.output. @@ -33,8 +33,7 @@ def __unicode__(self): def __str__(self): """String representation.""" - u = self.__unicode__() - return u + return self.__unicode__() def prepend_to_env_search_path(varname, value, envdict): @@ -85,9 +84,7 @@ def _file_extension_default(self): def _template_extension_default(self): return ".tex.j2" - def run_command( # noqa - self, command_list, filename, count, log_function, raise_on_failure=None - ): + def run_command(self, command_list, filename, count, log_function, raise_on_failure=None): """Run command_list count times. Parameters @@ -141,12 +138,12 @@ def run_command( # noqa stdout=stdout, stderr=subprocess.STDOUT, stdin=null, - shell=shell, # noqa + shell=shell, # noqa: S603 env=env, ) out, _ = p.communicate() if p.returncode: - if self.verbose: # noqa + if self.verbose: # noqa: SIM108 # verbose means I didn't capture stdout with PIPE, # so it's already been displayed and `out` is None. out_str = "" diff --git a/nbconvert/exporters/qt_exporter.py b/nbconvert/exporters/qt_exporter.py index b31f10ca0..e81882bd4 100644 --- a/nbconvert/exporters/qt_exporter.py +++ b/nbconvert/exporters/qt_exporter.py @@ -12,7 +12,7 @@ class QtExporter(HTMLExporter): """A qt exporter.""" paginate = None - format = "" # noqa + format = "" @default("file_extension") def _file_extension_default(self): @@ -41,7 +41,7 @@ def _run_pyqtwebengine(self, html): with temp_file: temp_file.write(html.encode("utf-8")) try: - QtScreenshot = self._check_launch_reqs() # noqa + QtScreenshot = self._check_launch_reqs() s = QtScreenshot() s.capture(f"file://{temp_file.name}", filename, self.paginate) finally: @@ -54,9 +54,9 @@ def from_notebook_node(self, nb, resources=None, **kw): self._check_launch_reqs() html, resources = super().from_notebook_node(nb, resources=resources, **kw) - self.log.info(f"Building {self.format.upper()}") + self.log.info("Building %s", self.format.upper()) data = self._run_pyqtwebengine(html) - self.log.info(f"{self.format.upper()} successfully created") + self.log.info("%s successfully created", self.format.upper()) # convert output extension # the writer above required it to be html diff --git a/nbconvert/exporters/qtpdf.py b/nbconvert/exporters/qtpdf.py index 6cf0a21fc..9eeac1513 100644 --- a/nbconvert/exporters/qtpdf.py +++ b/nbconvert/exporters/qtpdf.py @@ -16,7 +16,7 @@ class QtPDFExporter(QtExporter): """ export_from_notebook = "PDF via HTML" - format = "pdf" # noqa + format = "pdf" paginate = Bool( # type:ignore[assignment] True, diff --git a/nbconvert/exporters/qtpng.py b/nbconvert/exporters/qtpng.py index 644285166..d506a432b 100644 --- a/nbconvert/exporters/qtpng.py +++ b/nbconvert/exporters/qtpng.py @@ -14,4 +14,4 @@ class QtPNGExporter(QtExporter): """ export_from_notebook = "PNG via HTML" - format = "png" # noqa + format = "png" diff --git a/nbconvert/exporters/script.py b/nbconvert/exporters/script.py index 3edb502ab..0d2a7172f 100644 --- a/nbconvert/exporters/script.py +++ b/nbconvert/exporters/script.py @@ -39,7 +39,7 @@ def _get_language_exporter(self, lang_name): if lang_name not in self._lang_exporters: try: exporters = entry_points(group="nbconvert.exporters.script") - exporter = [e for e in exporters if e.name == lang_name][0].load() # noqa + exporter = [e for e in exporters if e.name == lang_name][0].load() # noqa: RUF015 except (KeyError, IndexError): self._lang_exporters[lang_name] = None else: diff --git a/nbconvert/exporters/templateexporter.py b/nbconvert/exporters/templateexporter.py index 5bed5fdd0..c09e6583b 100644 --- a/nbconvert/exporters/templateexporter.py +++ b/nbconvert/exporters/templateexporter.py @@ -151,7 +151,7 @@ class TemplateExporter(Exporter): """ # finish the docstring - __doc__ = __doc__.format(filters="- " + "\n - ".join(sorted(default_filters.keys()))) # noqa + __doc__ = __doc__.format(filters="- " + "\n - ".join(sorted(default_filters.keys()))) _template_cached = None @@ -247,6 +247,7 @@ def _template_file_changed(self, change): def _template_file_default(self): if self.template_extension: return "index" + self.template_extension + return None @observe("raw_template") def _raw_template_changed(self, change): @@ -269,15 +270,11 @@ def _default_extra_template_basedirs(self): jupyter_path("nbconvert", "templates"), help="Path where templates can be installed too." ).tag(affects_environment=True) - # Extension that the template files use. - template_extension = Unicode().tag(config=True, affects_environment=True) - @default("template_extension") def _template_extension_default(self): if self.file_extension: return self.file_extension + ".j2" - else: - return self.file_extension + return self.file_extension exclude_input = Bool( False, help="This allows you to exclude code cell inputs from all templates if set to True." @@ -453,27 +450,28 @@ def _register_filter(self, environ, name, jinja_filter): filter_cls = import_item(jinja_filter) return self._register_filter(environ, name, filter_cls) - if constructed and hasattr(jinja_filter, "__call__"): # noqa + if constructed and callable(jinja_filter): # filter is a function, no need to construct it. environ.filters[name] = jinja_filter return jinja_filter - elif isclass and issubclass(jinja_filter, HasTraits): + if isclass and issubclass(jinja_filter, HasTraits): # filter is configurable. Make sure to pass in new default for # the enabled flag if one was specified. filter_instance = jinja_filter(parent=self) self._register_filter(environ, name, filter_instance) + return None - elif isclass: + if isclass: # filter is not configurable, construct it filter_instance = jinja_filter() self._register_filter(environ, name, filter_instance) + return None - else: - # filter is an instance of something without a __call__ - # attribute. - msg = "filter" - raise TypeError(msg) + # filter is an instance of something without a __call__ + # attribute. + msg = "filter" + raise TypeError(msg) def register_filter(self, name, jinja_filter): """ @@ -513,7 +511,7 @@ def _create_environment(self): ExtensionTolerantLoader(FileSystemLoader(paths), self.template_extension), DictLoader({self._raw_template_key: self.raw_template}), ] - environment = Environment( # noqa + environment = Environment( # noqa: S701 loader=ChoiceLoader(loaders), extensions=JINJA_EXTENSIONS, enable_async=self.enable_async, @@ -547,7 +545,7 @@ def _init_preprocessors(self): preprocessor_cls = import_item(preprocessor_cls) if preprocessor_cls.__name__ in self.config: kwargs.update(self.config[preprocessor_cls.__name__]) - preprocessor = preprocessor_cls(**kwargs) # noqa + preprocessor = preprocessor_cls(**kwargs) # noqa: PLW2901 self.register_preprocessor(preprocessor) def _get_conf(self): @@ -602,8 +600,9 @@ def get_compatibility_base_template_conf(cls, name): return {"base_template": "base"} if name == "full": return {"base_template": "classic", "mimetypes": {"text/html": True}} + return None - def get_template_names(self): # noqa + def get_template_names(self): """Finds a list of template names where each successive template name is the base template""" template_names = [] root_dirs = self.get_prefix_root_dirs() diff --git a/nbconvert/exporters/webpdf.py b/nbconvert/exporters/webpdf.py index fc9a952bc..5129a724b 100644 --- a/nbconvert/exporters/webpdf.py +++ b/nbconvert/exporters/webpdf.py @@ -85,7 +85,7 @@ async def main(temp_file): if self.allow_chromium_download: cmd = [sys.executable, "-m", "playwright", "install", "chromium"] - subprocess.check_call(cmd) # noqa + subprocess.check_call(cmd) # noqa: S603 playwright = await async_playwright().start() chromium = playwright.chromium diff --git a/nbconvert/filters/ansi.py b/nbconvert/filters/ansi.py index 07319de83..a9882e10e 100644 --- a/nbconvert/filters/ansi.py +++ b/nbconvert/filters/ansi.py @@ -71,7 +71,7 @@ def ansi2latex(text): return _ansi2anything(text, _latexconverter) -def _htmlconverter(fg, bg, bold, underline, inverse): # noqa +def _htmlconverter(fg, bg, bold, underline, inverse): """ Return start and end tags for given foreground/background/bold/underline. @@ -114,7 +114,7 @@ def _htmlconverter(fg, bg, bold, underline, inverse): # noqa return starttag, "" -def _latexconverter(fg, bg, bold, underline, inverse): # noqa +def _latexconverter(fg, bg, bold, underline, inverse): """ Return start and end markup given foreground/background/bold/underline. @@ -165,7 +165,7 @@ def _latexconverter(fg, bg, bold, underline, inverse): # noqa return starttag, endtag -def _ansi2anything(text, converter): # noqa +def _ansi2anything(text, converter): r""" Convert ANSI colors to HTML or LaTeX. @@ -275,10 +275,10 @@ def _get_extended_color(numbers): idx = numbers.pop(0) if idx < 0: raise ValueError() - elif idx < 16: + if idx < 16: # 16 default terminal colors return idx - elif idx < 232: + if idx < 232: # 6x6x6 color cube, see http://stackoverflow.com/a/27165165/500098 r = (idx - 16) // 36 r = 55 + r * 40 if r > 0 else 0 diff --git a/nbconvert/filters/citation.py b/nbconvert/filters/citation.py index dde295646..0db092a42 100644 --- a/nbconvert/filters/citation.py +++ b/nbconvert/filters/citation.py @@ -44,7 +44,7 @@ def citation2latex(s): for citation in parser.citelist: outtext += s[startpos : citation[1]] outtext += "\\cite{%s}" % citation[0] - startpos = citation[2] if len(citation) == 3 else -1 # noqa + startpos = citation[2] if len(citation) == 3 else -1 outtext += s[startpos:] if startpos != -1 else "" return outtext diff --git a/nbconvert/filters/filter_links.py b/nbconvert/filters/filter_links.py index f67698713..7b48f35cc 100644 --- a/nbconvert/filters/filter_links.py +++ b/nbconvert/filters/filter_links.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """A pandoc filter used in converting notebooks to Latex. Converts links between notebooks to Latex cross-references. """ @@ -37,5 +36,5 @@ def resolve_one_reference(key, val, fmt, meta): label = re.sub(r"[^\w-]+", "", label) # Strip HTML entities text = re.sub(r"_", r"\_", text) # Escape underscores in display text return RawInline("tex", rf"\hyperref[{label}]{{{text}}}") - + return None # Other elements will be returned unchanged. diff --git a/nbconvert/filters/highlight.py b/nbconvert/filters/highlight.py index 8b440a414..d29a4759a 100644 --- a/nbconvert/filters/highlight.py +++ b/nbconvert/filters/highlight.py @@ -133,8 +133,7 @@ def __call__(self, source, language=None, metadata=None, strip_verbatim=False): if strip_verbatim: latex = latex.replace(r"\begin{Verbatim}[commandchars=\\\{\}]" + "\n", "") return latex.replace("\n\\end{Verbatim}\n", "") - else: - return latex + return latex def _pygments_highlight(source, output_formatter, language="ipython", metadata=None): diff --git a/nbconvert/filters/markdown_mistune.py b/nbconvert/filters/markdown_mistune.py index 4ba355ed7..7551eed40 100644 --- a/nbconvert/filters/markdown_mistune.py +++ b/nbconvert/filters/markdown_mistune.py @@ -52,11 +52,9 @@ def import_plugin(name: str) -> "MarkdownPlugin": # type: ignore[misc] return PLUGINS[name] # type: ignore[no-any-return] -class InvalidNotebook(Exception): # noqa +class InvalidNotebook(Exception): """An invalid notebook model.""" - pass - def _dotall(pattern: str) -> str: """Makes the '.' special character match any character inside the pattern, including a newline. @@ -268,7 +266,7 @@ def parse_latex_environment(self, m: Match[str], state: Any) -> Tuple[str, str, class IPythonRenderer(HTMLRenderer): """An ipython html renderer.""" - def __init__( # noqa + def __init__( self, escape: bool = True, allow_harmful_protocols: bool = True, @@ -402,7 +400,7 @@ def _embed_image_or_attachment(self, src: str) -> str: default_mime_type = next(iter(attachment.keys())) return f"data:{default_mime_type};base64,{attachment[default_mime_type]}" - elif self.embed_images: + if self.embed_images: base64_url = self._src_to_base64(src) if base64_url is not None: return base64_url diff --git a/nbconvert/filters/strings.py b/nbconvert/filters/strings.py index e83091ab4..9284521ef 100644 --- a/nbconvert/filters/strings.py +++ b/nbconvert/filters/strings.py @@ -56,7 +56,7 @@ def wrap_text(text, width=100): """ split_text = text.split("\n") - wrp = map(lambda x: textwrap.wrap(x, width), split_text) # noqa + wrp = map(lambda x: textwrap.wrap(x, width), split_text) # noqa: C417 wrpd = map("\n".join, wrp) return "\n".join(wrpd) @@ -174,7 +174,7 @@ def strip_files_prefix(text): """ cleaned_text = files_url_pattern.sub(r"\1=\2", text) cleaned_text = markdown_url_pattern.sub(r"\1[\2](\3)", cleaned_text) - return cleaned_text + return cleaned_text # noqa: RET504 def comment_lines(text, prefix="# "): @@ -269,7 +269,7 @@ def prevent_list_blocks(s): out = re.sub(r"(^\s*)\-", r"\1\-", out) out = re.sub(r"(^\s*)\+", r"\1\+", out) out = re.sub(r"(^\s*)\*", r"\1\*", out) - return out + return out # noqa: RET504 def strip_trailing_newline(text): diff --git a/nbconvert/nbconvertapp.py b/nbconvert/nbconvertapp.py index 0b2a68f50..0bb71326b 100755 --- a/nbconvert/nbconvertapp.py +++ b/nbconvert/nbconvertapp.py @@ -25,7 +25,6 @@ from nbconvert.utils.text import indent from .exporters.base import get_export_names, get_exporter -from .filters.markdown_mistune import InvalidNotebook # noqa For backward compatibility from .utils.base import NbConvertBase from .utils.exceptions import ConversionException from .utils.io import unicode_stdin_stream @@ -46,8 +45,7 @@ def validate(self, obj, value): """Validate an input.""" if value is not None and len(value) > 0: return super().validate(obj, value) - else: - return value + return value nbconvert_aliases = {} @@ -422,7 +420,7 @@ def _notebook_filename_to_name(self, notebook_filename): notebook_name = basename[: basename.rfind(".")] notebook_name = self.output_base.format(notebook_name=notebook_name) - return notebook_name + return notebook_name # noqa: RET504 def init_single_notebook_resources(self, notebook_filename): """Step 1: Initialize resources @@ -482,7 +480,7 @@ def export_single_notebook(self, notebook_filename, resources, input_buffer=None notebook_filename, resources=resources ) except ConversionException: - self.log.error("Error while converting '%s'", notebook_filename, exc_info=True) + self.log.error("Error while converting '%s'", notebook_filename, exc_info=True) # noqa: G201 self.exit(1) return output, resources @@ -517,8 +515,7 @@ def write_single_notebook(self, output, resources): if not self.writer: msg = "No writer object defined!" raise ValueError(msg) - write_results = self.writer.write(output, resources, notebook_name=notebook_name) - return write_results + return self.writer.write(output, resources, notebook_name=notebook_name) def postprocess_single_notebook(self, write_results): """Step 4: Post-process the written file @@ -594,6 +591,7 @@ def convert_notebooks(self): input_buffer = unicode_stdin_stream() # default name when conversion from stdin self.convert_single_notebook("notebook.ipynb", input_buffer=input_buffer) + input_buffer.close() def document_flag_help(self): """ diff --git a/nbconvert/postprocessors/serve.py b/nbconvert/postprocessors/serve.py index c5391aa8c..2da8ecb19 100644 --- a/nbconvert/postprocessors/serve.py +++ b/nbconvert/postprocessors/serve.py @@ -61,7 +61,7 @@ class ServePostProcessor(PostProcessorBase): ip = Unicode("127.0.0.1", help="The IP address to listen on.").tag(config=True) port = Int(8000, help="port for the server to listen on.").tag(config=True) - def postprocess(self, input): # noqa + def postprocess(self, input): """Serve the build directory with a webserver.""" dirname, filename = os.path.split(input) handlers: list[tuple[t.Any, ...]] = [ @@ -96,10 +96,10 @@ def postprocess(self, input): # noqa if self.open_in_browser: try: browser = webbrowser.get(self.browser or None) - b = lambda: browser.open(url, new=2) # noqa + b = lambda: browser.open(url, new=2) # noqa: E731 threading.Thread(target=b).start() except webbrowser.Error as e: - self.log.warning("No web browser found: %s." % e) + self.log.warning("No web browser found: %s.", e) browser = None try: diff --git a/nbconvert/preprocessors/base.py b/nbconvert/preprocessors/base.py index a2c2c7407..0df08d9f8 100644 --- a/nbconvert/preprocessors/base.py +++ b/nbconvert/preprocessors/base.py @@ -46,8 +46,7 @@ def __call__(self, nb, resources): if self.enabled: self.log.debug("Applying preprocessor: %s", self.__class__.__name__) return self.preprocess(nb, resources) - else: - return nb, resources + return nb, resources def preprocess(self, nb, resources): """ diff --git a/nbconvert/preprocessors/clearmetadata.py b/nbconvert/preprocessors/clearmetadata.py index b77337de2..609a01653 100644 --- a/nbconvert/preprocessors/clearmetadata.py +++ b/nbconvert/preprocessors/clearmetadata.py @@ -41,11 +41,10 @@ def current_key(self, mask_key): """Get the current key for a mask key.""" if isinstance(mask_key, str): return mask_key - elif len(mask_key) == 0: + if len(mask_key) == 0: # Safeguard return None - else: - return mask_key[0] + return mask_key[0] def current_mask(self, mask): """Get the current mask for a mask.""" @@ -76,7 +75,7 @@ def preprocess_cell(self, cell, resources, cell_index): """ All the code cells are returned with an empty metadata field. """ - if self.clear_cell_metadata and cell.cell_type == "code": # noqa + if self.clear_cell_metadata and cell.cell_type == "code": # noqa: SIM102 # Remove metadata if "metadata" in cell: cell.metadata = dict( diff --git a/nbconvert/preprocessors/csshtmlheader.py b/nbconvert/preprocessors/csshtmlheader.py index 90703e6ac..30b687ad2 100644 --- a/nbconvert/preprocessors/csshtmlheader.py +++ b/nbconvert/preprocessors/csshtmlheader.py @@ -87,7 +87,7 @@ def _generate_header(self, resources): def _hash(self, filename): """Compute the hash of a file.""" - md5 = hashlib.md5() # noqa + md5 = hashlib.md5() # noqa: S324 with open(filename, "rb") as f: md5.update(f.read()) return md5.digest() diff --git a/nbconvert/preprocessors/execute.py b/nbconvert/preprocessors/execute.py index ab2b8c2cf..5e8403350 100644 --- a/nbconvert/preprocessors/execute.py +++ b/nbconvert/preprocessors/execute.py @@ -9,8 +9,6 @@ from nbclient.client import execute as _execute # Backwards compatibility for imported name -from nbclient.exceptions import CellExecutionError # noqa - # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from nbformat import NotebookNode @@ -94,9 +92,9 @@ def preprocess( self._check_assign_resources(resources) with self.setup_kernel(): - assert self.kc # noqa + assert self.kc info_msg = self.wait_for_reply(self.kc.kernel_info()) - assert info_msg # noqa + assert info_msg self.nb.metadata["language_info"] = info_msg["content"]["language_info"] for index, cell in enumerate(self.nb.cells): self.preprocess_cell(cell, resources, index) diff --git a/nbconvert/preprocessors/extractattachments.py b/nbconvert/preprocessors/extractattachments.py index 763e614f5..740e19603 100644 --- a/nbconvert/preprocessors/extractattachments.py +++ b/nbconvert/preprocessors/extractattachments.py @@ -80,7 +80,7 @@ def preprocess_cell(self, cell, resources, index): """ if "attachments" in cell: for fname in cell.attachments: - self.log.debug(f"Encountered attachment {fname}") + self.log.debug("Encountered attachment %s", fname) # Add file for writer diff --git a/nbconvert/preprocessors/extractoutput.py b/nbconvert/preprocessors/extractoutput.py index 4a9cf6f25..7240ea2e6 100644 --- a/nbconvert/preprocessors/extractoutput.py +++ b/nbconvert/preprocessors/extractoutput.py @@ -53,7 +53,7 @@ class ExtractOutputPreprocessor(Preprocessor): config=True ) - def preprocess_cell(self, cell, resources, cell_index): # noqa + def preprocess_cell(self, cell, resources, cell_index): """ Apply a transformation on each cell, diff --git a/nbconvert/preprocessors/highlightmagics.py b/nbconvert/preprocessors/highlightmagics.py index b27097590..7579ab20d 100644 --- a/nbconvert/preprocessors/highlightmagics.py +++ b/nbconvert/preprocessors/highlightmagics.py @@ -77,8 +77,7 @@ def which_magic_language(self, source): # By construction of the re, the matched language must be in the # languages dictionary return self.default_languages[m.group(1)] - else: - return None + return None def preprocess_cell(self, cell, resources, cell_index): """ diff --git a/nbconvert/preprocessors/sanitize.py b/nbconvert/preprocessors/sanitize.py index 0882a955b..1bddfbcf0 100644 --- a/nbconvert/preprocessors/sanitize.py +++ b/nbconvert/preprocessors/sanitize.py @@ -117,12 +117,13 @@ def preprocess_cell(self, cell, resources, cell_index): # but erring on the side of safety maybe. cell.source = self.sanitize_html_tags(cell.source) return cell, resources - elif cell.cell_type == "markdown": + if cell.cell_type == "markdown": cell.source = self.sanitize_html_tags(cell.source) return cell, resources - elif cell.cell_type == "code": + if cell.cell_type == "code": cell.outputs = self.sanitize_code_outputs(cell.outputs) return cell, resources + return None def sanitize_code_outputs(self, outputs): """ @@ -140,15 +141,15 @@ def sanitize_code_outputs(self, outputs): for key in data: if key in self.safe_output_keys: continue - elif key in self.sanitized_output_types: - self.log.info("Sanitizing %s" % key) + if key in self.sanitized_output_types: + self.log.info("Sanitizing %s", key) data[key] = self.sanitize_html_tags(data[key]) else: # Mark key for removal. (Python doesn't allow deletion of # keys from a dict during iteration) to_remove.append(key) for key in to_remove: - self.log.info("Removing %s" % key) + self.log.info("Removing %s", key) del data[key] return outputs @@ -175,3 +176,4 @@ def sanitize_html_tags(self, html_str): def _get_default_css_sanitizer(): if _USE_BLEACH_CSS_SANITIZER: return CSSSanitizer(allowed_css_properties=ALLOWED_STYLES) + return None diff --git a/nbconvert/preprocessors/svg2pdf.py b/nbconvert/preprocessors/svg2pdf.py index 61a22035c..5e52bdb2c 100644 --- a/nbconvert/preprocessors/svg2pdf.py +++ b/nbconvert/preprocessors/svg2pdf.py @@ -145,7 +145,7 @@ def convert_figure(self, data_format, data): # 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)) # noqa + subprocess.call(full_cmd, shell=isinstance(full_cmd, str)) # noqa: S603 # Read output from drive # return value expects a filename diff --git a/nbconvert/utils/_contextlib_chdir.py b/nbconvert/utils/_contextlib_chdir.py index 1a0b1b17a..0a79c4e5d 100644 --- a/nbconvert/utils/_contextlib_chdir.py +++ b/nbconvert/utils/_contextlib_chdir.py @@ -5,7 +5,7 @@ from contextlib import AbstractContextManager -class chdir(AbstractContextManager): # type:ignore[type-arg] # noqa +class chdir(AbstractContextManager): # type:ignore[type-arg] """Non thread-safe context manager to change the current working directory.""" def __init__(self, path): diff --git a/nbconvert/utils/exceptions.py b/nbconvert/utils/exceptions.py index bf82dcc0a..d9623dcf8 100644 --- a/nbconvert/utils/exceptions.py +++ b/nbconvert/utils/exceptions.py @@ -12,7 +12,5 @@ # ----------------------------------------------------------------------------- -class ConversionException(Exception): # noqa +class ConversionException(Exception): """An exception raised by the conversion process.""" - - pass diff --git a/nbconvert/utils/io.py b/nbconvert/utils/io.py index d8f552d5a..18fd3a46c 100644 --- a/nbconvert/utils/io.py +++ b/nbconvert/utils/io.py @@ -22,7 +22,7 @@ def unicode_std_stream(stream="stdout"): unicode_std_stream().write(u'ł@e¶ŧ←') """ - assert stream in ("stdout", "stderr") # noqa + assert stream in ("stdout", "stderr") stream = getattr(sys, stream) try: @@ -104,7 +104,7 @@ def link_or_copy(src, dst): # anyway, we get duplicate files - see http://bugs.python.org/issue21876 return - new_dst = dst + f"-temp-{random.randint(1, 16**4):04X}" # noqa + new_dst = dst + f"-temp-{random.randint(1, 16**4):04X}" # noqa: S311 try: link_or_copy(src, new_dst) except BaseException: diff --git a/nbconvert/utils/lexers.py b/nbconvert/utils/lexers.py index 7bfaf0a20..2a54ebe8a 100644 --- a/nbconvert/utils/lexers.py +++ b/nbconvert/utils/lexers.py @@ -3,4 +3,4 @@ warn("nbconvert.utils.lexers is deprecated as of 5.0. Use IPython.lib.lexers", stacklevel=2) -from IPython.lib.lexers import * # noqa +from IPython.lib.lexers import * # noqa: F403, E402 diff --git a/nbconvert/utils/pandoc.py b/nbconvert/utils/pandoc.py index 8f43e5272..a6b93a684 100644 --- a/nbconvert/utils/pandoc.py +++ b/nbconvert/utils/pandoc.py @@ -51,7 +51,7 @@ def pandoc(source, fmt, to, extra_args=None, encoding="utf-8"): check_pandoc_version() # we can safely continue - p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # noqa + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # noqa: S603 out, _ = p.communicate(source.encode()) out_str = TextIOWrapper(BytesIO(out), encoding, "replace").read() return out_str.rstrip("\n") @@ -69,13 +69,13 @@ def get_pandoc_version(): PandocMissing If pandoc is unavailable. """ - global __version # noqa + global __version # noqa: PLW0603 if __version is None: if not shutil.which("pandoc"): raise PandocMissing() - out = subprocess.check_output(["pandoc", "-v"]) # noqa + out = subprocess.check_output(["pandoc", "-v"]) # noqa: S607, S603 out_lines = out.splitlines() version_pattern = re.compile(r"^\d+(\.\d+){1,}$") for tok in out_lines[0].decode("ascii", "replace").split(): @@ -144,7 +144,7 @@ def __init__(self, *args, **kwargs): # ----------------------------------------------------------------------------- def clean_cache(): """Clean the internal cache.""" - global __version # noqa + global __version # noqa: PLW0603 __version = None diff --git a/nbconvert/utils/text.py b/nbconvert/utils/text.py index 7e2bbcae0..76d394c6e 100644 --- a/nbconvert/utils/text.py +++ b/nbconvert/utils/text.py @@ -29,11 +29,10 @@ def indent(instr, nspaces=4, ntabs=0, flatten=False): """ if instr is None: - return + return None ind = "\t" * ntabs + " " * nspaces pat = re.compile("^\\s*", re.MULTILINE) if flatten else re.compile("^", re.MULTILINE) outstr = re.sub(pat, ind, instr) if outstr.endswith(os.linesep + ind): return outstr[: -len(ind)] - else: - return outstr + return outstr diff --git a/nbconvert/writers/debug.py b/nbconvert/writers/debug.py index 2f64adffb..51d4a7a56 100644 --- a/nbconvert/writers/debug.py +++ b/nbconvert/writers/debug.py @@ -39,7 +39,7 @@ def write(self, output, resources, notebook_name="notebook", **kw): if isinstance(resources["outputs"], dict): print("outputs extracted from %s" % notebook_name) print("-" * 80) - pprint(resources["outputs"], indent=2, width=70) # noqa + pprint(resources["outputs"], indent=2, width=70) # noqa: T203 else: print("no outputs extracted from %s" % notebook_name) print("=" * 80) diff --git a/nbconvert/writers/stdout.py b/nbconvert/writers/stdout.py index 0d034b4a8..44b82fb57 100644 --- a/nbconvert/writers/stdout.py +++ b/nbconvert/writers/stdout.py @@ -20,4 +20,5 @@ def write(self, output, resources, **kw): See base for more... """ - io.unicode_std_stream().write(output) + stream = io.unicode_std_stream() + stream.write(output) diff --git a/pyproject.toml b/pyproject.toml index 80f7561dd..4474667ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ webpdf = ["playwright"] test = [ "pytest", "ipykernel", - "ipywidgets>=7", + "ipywidgets>=7.5", "flaky", ] serve = ["tornado>=6.1"] @@ -191,26 +191,34 @@ line-length = 100 exclude = [".*notebook1.ipynb$"] [tool.ruff.lint] -select = [ - "A", "B", "C", "DTZ", "E", "EM", "F", "FBT", "I", "ICN", "N", - "PLC", "PLE", "PLR", "PLW", "Q", "RUF", "S", "SIM", "T", "TID", "UP", - "W", "YTT", +extend-select = [ + "B", # flake8-bugbear + "I", # isort + "C4", # flake8-comprehensions + "EM", # flake8-errmsg + "ICN", # flake8-import-conventions + "G", # flake8-logging-format + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + #"PTH", # flake8-use-pathlib + "PT", # flake8-pytest-style + "RET", # flake8-return + "RUF", # Ruff-specific + "SIM", # flake8-simplify + "T20", # flake8-print + "UP", # pyupgrade + "YTT", # flake8-2020 + "EXE", # flake8-executable + "PYI", # flake8-pyi + "S", # flake8-bandit ] ignore = [ - # Q000 Single quotes found but double quotes preferred - "Q000", - # FBT001 Boolean positional arg in function definition - "FBT001", "FBT002", "FBT003", - # E501 Line too long (158 > 100 characters) - "E501", - # SIM105 Use `contextlib.suppress(...)` - "SIM105", - # T201 `print` found - "T201", - # N802 Function name `CreateWellKnownSid` should be lowercase - "N802", "N803", - # RUF012 Mutable class attributes should be annotated with `typing.ClassVar` - "RUF012", + "PLR", # Design related pylint codes + "S101", # Use of `assert` detected + "SIM105", # Use `contextlib.suppress(...)` + "T201", # `print` found + "RUF012", # Mutable class attributes should be annotated ] unfixable = [ # Don't touch print statements @@ -220,12 +228,15 @@ unfixable = [ ] [tool.ruff.lint.per-file-ignores] - # B011: Do not call assert False since python -O removes these calls # F841 local variable 'foo' is assigned to but never used # S101 Use of `assert` detected # TID252 Relative imports from parent modules are banned -# PLR2004 Magic value used in comparison -"tests/*" = ["B011", "F841", "S101", "TID252", "PLR2004"] +# PT009 Use a regular `assert` instead of unittest-style +# PGH003 Use specific rule codes when ignoring type issues +# PT027 Use `pytest.raises` instead of unittest-style +# PGH004 Use specific rule codes when using `noqa` +"tests/*" = ["F841", "S101", "TID252", "PT009", "PTH", "PGH003", "PT027", + "PGH004"] "tests/*/*.ipynb" = [ "EM", "B018", "E402", "F405", "SIM", "F403", "F821", "RUF100", "ICN001", "S605", "S607" diff --git a/tests/base.py b/tests/base.py index ca7415691..ff2bec3d5 100644 --- a/tests/base.py +++ b/tests/base.py @@ -22,7 +22,7 @@ class TestsBase(unittest.TestCase): """Base tests class. Contains useful fuzzy comparison and nbconvert functions.""" - def fuzzy_compare( # noqa + def fuzzy_compare( self, a, b, @@ -184,7 +184,7 @@ def assert_big_text_equal(a, b, chunk_size=80): raise AssertionError( "Length doesn't match (%i > %i). Extra text:\n%r" % (len(a), len(b), a[len(b) :]) ) - elif len(a) < len(b): + if len(a) < len(b): raise AssertionError( "Length doesn't match (%i < %i). Extra text:\n%r" % (len(a), len(b), a[len(b) :]) ) diff --git a/tests/exporters/test_templateexporter.py b/tests/exporters/test_templateexporter.py index 593cfc70a..d96f1c333 100644 --- a/tests/exporters/test_templateexporter.py +++ b/tests/exporters/test_templateexporter.py @@ -657,5 +657,4 @@ def test_remove_elements_with_tags(self): assert "(100,)" not in nb def _make_exporter(self, config=None): - exporter = SampleExporter(config=config) - return exporter + return SampleExporter(config=config) diff --git a/tests/exporters/test_webpdf.py b/tests/exporters/test_webpdf.py index 75596c3b2..ef9f8ed77 100644 --- a/tests/exporters/test_webpdf.py +++ b/tests/exporters/test_webpdf.py @@ -33,7 +33,7 @@ class TestWebPDFExporter(ExportersTestsBase): exporter_class = WebPDFExporter # type:ignore - @pytest.mark.network + @pytest.mark.network() def test_export(self): """ Can a TemplateExporter export something? @@ -56,7 +56,7 @@ def test_webpdf_without_playwright(self): """ Generate PDFs if playwright not installed? """ - with pytest.raises(RuntimeError): + with pytest.raises(RuntimeError): # noqa base_exporter = Exporter() exporter = WebPDFExporter() with open(self._get_notebook(), encoding="utf-8") as f: diff --git a/tests/filters/test_citation.py b/tests/filters/test_citation.py index a07535161..a953ab117 100644 --- a/tests/filters/test_citation.py +++ b/tests/filters/test_citation.py @@ -112,7 +112,7 @@ } -@pytest.mark.parametrize(["in_arg", "out_arg"], list(test_md.items())) +@pytest.mark.parametrize(["in_arg", "out_arg"], list(test_md.items())) # noqa def test_citation2latex(in_arg, out_arg): """Are citations parsed properly?""" assert citation2latex(in_arg) == out_arg diff --git a/tests/filters/test_markdown.py b/tests/filters/test_markdown.py index b8f13c429..4b1cdc354 100644 --- a/tests/filters/test_markdown.py +++ b/tests/filters/test_markdown.py @@ -68,7 +68,7 @@ def test_markdown2latex_markup(self): # sometimes pandoc uses $math$, sometimes it uses \(math\) expected = re.compile(r"(\$|\\\()\\alpha(\$|\\\)) latex math") - assertRegex = self.assertRegex # noqa + assertRegex = self.assertRegex assertRegex(convert_pandoc(s, "markdown_strict+tex_math_dollars", "latex"), expected) diff --git a/tests/test_nbconvertapp.py b/tests/test_nbconvertapp.py index de6976f25..764a79033 100644 --- a/tests/test_nbconvertapp.py +++ b/tests/test_nbconvertapp.py @@ -150,7 +150,7 @@ def test_filename_spaces(self): assert os.path.isfile("notebook with spaces.pdf") @flaky - @pytest.mark.network + @pytest.mark.network() @pytest.mark.skipif(not PLAYWRIGHT_INSTALLED, reason="Playwright not installed") def test_webpdf_with_chromium(self): """ @@ -419,7 +419,7 @@ def test_allow_errors(self): assert "42" in output3 # Executing the notebook should raise an exception if --allow-errors is not specified - with pytest.raises(OSError): + with pytest.raises(OSError): # noqa self.nbconvert("--execute --to markdown --stdout notebook3*.ipynb") def test_errors_print_traceback(self): diff --git a/tests/utils/test_io.py b/tests/utils/test_io.py index d90840041..7abc8b9b3 100644 --- a/tests/utils/test_io.py +++ b/tests/utils/test_io.py @@ -19,7 +19,8 @@ def test_UnicodeStdStream(): sys.stdout = stdout try: sample = "@łe¶ŧ←" - unicode_std_stream().write(sample) + stream = unicode_std_stream() + stream.write(sample) output = stdoutb.getvalue().decode("utf-8") assert output == sample