diff --git a/.github/workflows/docutils_setup.py b/.github/workflows/docutils_setup.py index 9d745222..5e371531 100755 --- a/.github/workflows/docutils_setup.py +++ b/.github/workflows/docutils_setup.py @@ -16,7 +16,7 @@ def modify_toml(content: str) -> str: dependencies = [] sphinx_extra = [] for dep in doc["project"]["dependencies"]: - if dep.startswith("docutils") or dep.startswith("sphinx"): + if dep.startswith(("docutils", "sphinx")): sphinx_extra.append(dep) else: dependencies.append(dep) diff --git a/myst_parser/_docs.py b/myst_parser/_docs.py index 964c5aaa..d9c58e6d 100644 --- a/myst_parser/_docs.py +++ b/myst_parser/_docs.py @@ -96,7 +96,7 @@ def field_type(field): get_args(field.type) if get_origin(field.type) is Union else [field.type] ) ctype = " | ".join( - str("None" if ftype == type(None) else ftype) # type: ignore + str("None" if ftype == type(None) else ftype) # type: ignore[comparison-overlap] for ftype in ftypes ) ctype = " ".join(ctype.splitlines()) diff --git a/myst_parser/config/main.py b/myst_parser/config/main.py index ae29dfad..32587693 100644 --- a/myst_parser/config/main.py +++ b/myst_parser/config/main.py @@ -104,7 +104,7 @@ def check_url_schemes(inst: "MdParserConfig", field: dc.Field, value: Any) -> No raise TypeError( f"'{field.name}[{key}][classes]' is not a list of str: {val['classes']!r}" ) - new_dict[key] = val # type: ignore + new_dict[key] = val # type: ignore[assignment] else: raise TypeError( f"'{field.name}[{key}]' value is not a string or dict: {val!r}" @@ -577,7 +577,7 @@ def read_topmatter(text: Union[str, Iterator[str]]) -> Optional[Dict[str, Any]]: return None top_matter = [] for line in text: - if line.startswith("---") or line.startswith("..."): + if line.startswith(("---", "...")): break top_matter.append(line.rstrip() + "\n") try: diff --git a/myst_parser/inventory.py b/myst_parser/inventory.py index ad85e777..bdcd8bb0 100644 --- a/myst_parser/inventory.py +++ b/myst_parser/inventory.py @@ -404,7 +404,7 @@ def fetch_inventory( uri: str, *, timeout: None | float = None, base_url: None | str = None ) -> InventoryType: """Fetch an inventory from a URL or local path.""" - if uri.startswith("http://") or uri.startswith("https://"): + if uri.startswith(("http://", "https://")): with urlopen(uri, timeout=timeout) as stream: return load(stream, base_url=base_url) with open(uri, "rb") as stream: diff --git a/myst_parser/mdit_to_docutils/base.py b/myst_parser/mdit_to_docutils/base.py index 0b3904ad..0817f5ca 100644 --- a/myst_parser/mdit_to_docutils/base.py +++ b/myst_parser/mdit_to_docutils/base.py @@ -1589,9 +1589,9 @@ def render_dl(self, token: SyntaxTreeNode) -> None: from sphinx.domains.std import make_glossary_term term = make_glossary_term( - self.sphinx_env, # type: ignore + self.sphinx_env, # type: ignore[arg-type] term.children, - None, # type: ignore + None, # type: ignore[arg-type] term.source, term.line, node_id=None, diff --git a/myst_parser/mocking.py b/myst_parser/mocking.py index f3c21f69..3fedbaed 100644 --- a/myst_parser/mocking.py +++ b/myst_parser/mocking.py @@ -371,14 +371,14 @@ def run(self) -> list[nodes.Element]: # tab_width = self.options.get("tab-width", self.document.settings.tab_width) try: file_content = path.read_text(encoding=encoding, errors=error_handler) - except FileNotFoundError: + except FileNotFoundError as error: raise DirectiveError( 4, f'Directive "{self.name}": file not found: {str(path)!r}' - ) + ) from error except Exception as error: raise DirectiveError( 4, f'Directive "{self.name}": error reading file: {path}\n{error}.' - ) + ) from error # get required section of text startline = self.options.get("start-line", None) @@ -412,10 +412,10 @@ def run(self) -> list[nodes.Element]: if "number-lines" in self.options: try: startline = int(self.options["number-lines"] or 1) - except ValueError: + except ValueError as err: raise DirectiveError( 3, ":number-lines: with non-integer start value" - ) + ) from err endline = startline + len(file_content.splitlines()) if file_content.endswith("\n"): file_content = file_content[:-1] diff --git a/myst_parser/parsers/docutils_.py b/myst_parser/parsers/docutils_.py index d9997b75..89a78b65 100644 --- a/myst_parser/parsers/docutils_.py +++ b/myst_parser/parsers/docutils_.py @@ -97,8 +97,8 @@ def _validate_yaml( """ try: output = yaml.safe_load(value) - except Exception: - raise ValueError("Invalid YAML string") + except Exception as err: + raise ValueError("Invalid YAML string") from err if not isinstance(output, dict): raise ValueError("Expecting a YAML dictionary") return output @@ -115,8 +115,8 @@ def _validate_url_schemes( """ try: output = yaml.safe_load(value) - except Exception: - raise ValueError("Invalid YAML string") + except Exception as err: + raise ValueError("Invalid YAML string") from err if isinstance(output, str): output = {k: None for k in output.split(",")} if not isinstance(output, dict): diff --git a/myst_parser/parsers/parse_html.py b/myst_parser/parsers/parse_html.py index 5add3582..971c3a28 100644 --- a/myst_parser/parsers/parse_html.py +++ b/myst_parser/parsers/parse_html.py @@ -362,7 +362,7 @@ def enclose(self, name: str): count = 0 # It pops all the items which do not match with the closing tag. - for _ in range(0, count): + for _ in range(count): self.stack.pop() diff --git a/myst_parser/sphinx_ext/directives.py b/myst_parser/sphinx_ext/directives.py index 415c5498..ebd88295 100644 --- a/myst_parser/sphinx_ext/directives.py +++ b/myst_parser/sphinx_ext/directives.py @@ -1,6 +1,8 @@ """MyST specific directives""" +from __future__ import annotations + from copy import copy -from typing import List, Tuple, cast +from typing import cast from docutils import nodes from docutils.parsers.rst import directives @@ -27,7 +29,7 @@ class SubstitutionReferenceRole(SphinxRole): Note, in ``docutils/parsers/rst/roles.py`` this is left unimplemented. """ - def run(self) -> Tuple[List[nodes.Node], List[nodes.system_message]]: + def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: subref_node = nodes.substitution_reference(self.rawtext, self.text) self.set_source_info(subref_node, self.lineno) subref_node["refname"] = nodes.fully_normalize_name(self.text) @@ -59,7 +61,7 @@ class FigureMarkdown(SphinxDirective): "name": directives.unchanged, } - def run(self) -> List[nodes.Node]: + def run(self) -> list[nodes.Node]: figwidth = self.options.pop("width", None) figclasses = self.options.pop("class", None) align = self.options.pop("align", None) diff --git a/myst_parser/sphinx_ext/mathjax.py b/myst_parser/sphinx_ext/mathjax.py index 260f0080..e430c3ba 100644 --- a/myst_parser/sphinx_ext/mathjax.py +++ b/myst_parser/sphinx_ext/mathjax.py @@ -53,14 +53,14 @@ def override_mathjax(app: Sphinx): if "dollarmath" not in app.config["myst_enable_extensions"]: return - if not app.env.myst_config.update_mathjax: # type: ignore + if not app.env.myst_config.update_mathjax: # type: ignore[attr-defined] return - mjax_classes = app.env.myst_config.mathjax_classes # type: ignore + mjax_classes = app.env.myst_config.mathjax_classes # type: ignore[attr-defined] if "mathjax3_config" in app.config: # sphinx 4 + mathjax 3 - app.config.mathjax3_config = app.config.mathjax3_config or {} # type: ignore + app.config.mathjax3_config = app.config.mathjax3_config or {} # type: ignore[attr-defined] app.config.mathjax3_config.setdefault("options", {}) if ( "processHtmlClass" in app.config.mathjax3_config["options"] diff --git a/myst_parser/warnings_.py b/myst_parser/warnings_.py index c101f26d..30a8fa8d 100644 --- a/myst_parser/warnings_.py +++ b/myst_parser/warnings_.py @@ -24,7 +24,7 @@ class MystWarnings(Enum): """Duplicate Markdown reference definition.""" MD_FOOTNOTE_DUPE = "footnote" """Duplicate Markdown footnote definition.""" - MD_FOOTNOTE_MISSING = "footnote" + MD_FOOTNOTE_MISSING = "footnote" # noqa: PIE796 """Missing Markdown footnote definition.""" MD_HEADING_NON_CONSECUTIVE = "header" """Non-consecutive heading levels.""" diff --git a/pyproject.toml b/pyproject.toml index c8c13109..836473b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,9 +102,13 @@ exclude = [ ] [tool.ruff] -extend-select = ["B0", "C4", "I", "ICN", "ISC", "N", "RUF", "SIM", "UP"] +extend-select = ["B", "C4", "FA", "FURB", "I", "ICN", "ISC", "N", "PERF", "PGH", "PIE", "RUF", "SIM", "UP"] extend-ignore = ["ISC001", "RUF005", "RUF012"] +[tool.ruff.per-file-ignores] +"myst_parser/parsers/docutils_.py" = ["FA"] +"myst_parser/config/main.py" = ["FA"] + [tool.mypy] show_error_codes = true check_untyped_defs = true diff --git a/tests/test_renderers/test_fixtures_docutils.py b/tests/test_renderers/test_fixtures_docutils.py index 10c9dd87..889d9eed 100644 --- a/tests/test_renderers/test_fixtures_docutils.py +++ b/tests/test_renderers/test_fixtures_docutils.py @@ -131,5 +131,5 @@ def settings_from_cmdline(cmdline: str | None) -> dict[str, Any]: try: pub.process_command_line(shlex.split(cmdline)) except Exception as err: - raise AssertionError(f"Failed to parse commandline: {cmdline}\n{err}") + raise AssertionError(f"Failed to parse commandline: {cmdline}\n{err}") from err return vars(pub.settings) diff --git a/tests/test_renderers/test_fixtures_sphinx.py b/tests/test_renderers/test_fixtures_sphinx.py index 3dfba8f9..443fc9db 100644 --- a/tests/test_renderers/test_fixtures_sphinx.py +++ b/tests/test_renderers/test_fixtures_sphinx.py @@ -46,7 +46,7 @@ def settings_from_json(string: str | None): data = json.loads(string) assert isinstance(data, dict), "settings must be a JSON object" except Exception as err: - raise AssertionError(f"Failed to parse JSON settings: {string}\n{err}") + raise AssertionError(f"Failed to parse JSON settings: {string}\n{err}") from err return data diff --git a/tests/test_renderers/test_myst_config.py b/tests/test_renderers/test_myst_config.py index 4c9c0bc8..e35aee40 100644 --- a/tests/test_renderers/test_myst_config.py +++ b/tests/test_renderers/test_myst_config.py @@ -27,7 +27,7 @@ def test_cmdline(file_params: ParamTestData): except Exception as err: raise AssertionError( f"Failed to parse commandline: {file_params.description}\n{err}" - ) + ) from err settings = vars(pub.settings) report_stream = StringIO() settings["output_encoding"] = "unicode"