diff --git a/.copier-answers.yml b/.copier-answers.yml index dafd61dc..3c48bac1 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.11.2 +_commit: 1.11.6 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54dd741c..a8d0a185 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,11 @@ jobs: python-version: "3.12" - os: ubuntu-latest python-version: "3.13" + - os: ubuntu-latest + python-version: "3.15-dev" runs-on: ${{ matrix.os }} + continue-on-error: true steps: - name: Checkout @@ -103,7 +106,7 @@ jobs: - "3.12" - "3.13" - "3.14" - - "3.15" + - "3.15-dev" resolution: - highest - lowest-direct @@ -112,6 +115,7 @@ jobs: resolution: lowest-direct - os: windows-latest resolution: lowest-direct + runs-on: ${{ matrix.os }} continue-on-error: true diff --git a/.gitignore b/.gitignore index 9fea0472..faeb06ae 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,5 @@ uv.lock # cache .cache/ .pytest_cache/ -.mypy_cache/ .ruff_cache/ __pycache__/ diff --git a/CHANGELOG.md b/CHANGELOG.md index d1c96386..53065d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.3](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.3) - 2026-02-07 + +[Compare with 1.0.2](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.2...1.0.3) + +### Bug Fixes + +- Forward extension instances directly passed from Zensical ([65b27ec](https://github.com/mkdocstrings/mkdocstrings/commit/65b27ec8d1d671eddf021e48b0114cc3f8aca14a) by Timothée Mazzucotelli). +- Propagate Zensical's `zrelpath` processor ([dbf263d](https://github.com/mkdocstrings/mkdocstrings/commit/dbf263dfdd2fdd769d66fa62bdd388e05988bc78) by Timothée Mazzucotelli). + ## [1.0.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.2) - 2026-01-24 [Compare with 1.0.1](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.1...1.0.2) diff --git a/config/mypy.ini b/config/mypy.ini deleted file mode 100644 index 814e2ac8..00000000 --- a/config/mypy.ini +++ /dev/null @@ -1,5 +0,0 @@ -[mypy] -ignore_missing_imports = true -exclude = tests/fixtures/ -warn_unused_ignores = true -show_error_codes = true diff --git a/config/ruff.toml b/config/ruff.toml index 65416253..95d92083 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -5,30 +5,9 @@ line-length = 120 exclude = [ "tests/fixtures/*.py", ] -select = [ - "A", "ANN", "ARG", - "B", "BLE", - "C", "C4", - "COM", - "D", "DTZ", - "E", "ERA", "EXE", - "F", "FBT", - "G", - "I", "ICN", "INP", "ISC", - "N", - "PGH", "PIE", "PL", "PLC", "PLE", "PLR", "PLW", "PT", "PYI", - "Q", - "RUF", "RSE", "RET", - "S", "SIM", "SLF", - "T", "T10", "T20", "TCH", "TID", "TRY", - "UP", - "W", - "YTT", -] +select = ["ALL"] ignore = [ "A001", # Variable is shadowing a Python builtin - "ANN101", # Missing type annotation for self - "ANN102", # Missing type annotation for cls "ANN204", # Missing return type annotation for special method __str__ "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ARG005", # Unused lambda argument @@ -36,7 +15,10 @@ ignore = [ "D105", # Missing docstring in magic method "D417", # Missing argument description in the docstring "E501", # Line too long + "EM101", # String literal when raising exception + "EM102", # f-string when raising exception "ERA001", # Commented out code + "FIX", # TODO, FIXME, etc. "G004", # Logging statement uses f-string "PLR0911", # Too many return statements "PLR0912", # Too many branches @@ -44,6 +26,8 @@ ignore = [ "PLR0915", # Too many statements "SLF001", # Private member accessed "S704", # Unsafe use of `markupsafe.Markup` + "TD002", # Missing author in TODO + "TD003", # Missing issue link for TODO "TRY003", # Avoid specifying long messages outside the exception class ] diff --git a/config/ty.toml b/config/ty.toml new file mode 100644 index 00000000..545856a3 --- /dev/null +++ b/config/ty.toml @@ -0,0 +1,2 @@ +[src] +exclude = ["tests/fixtures"] diff --git a/config/vscode/settings.json b/config/vscode/settings.json index 949856d1..87ecd639 100644 --- a/config/vscode/settings.json +++ b/config/vscode/settings.json @@ -4,9 +4,6 @@ "**/.venvs*/**": true, "**/venv*/**": true }, - "mypy-type-checker.args": [ - "--config-file=config/mypy.ini" - ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.testing.pytestArgs": [ diff --git a/duties.py b/duties.py index 0759f7a5..974ed169 100644 --- a/duties.py +++ b/duties.py @@ -22,7 +22,7 @@ PTY = not WINDOWS and not CI MULTIRUN = os.environ.get("MULTIRUN", "0") == "1" PY_VERSION = f"{sys.version_info.major}{sys.version_info.minor}" -PY_DEV = "314" +PY_DEV = "315" def pyprefix(title: str) -> str: @@ -35,7 +35,7 @@ def pyprefix(title: str) -> str: def _get_changelog_version() -> str: changelog_version_re = re.compile(r"^## \[(\d+\.\d+\.\d+)\].*$") with Path(__file__).parent.joinpath("CHANGELOG.md").open("r", encoding="utf8") as file: - return next(filter(bool, map(changelog_version_re.match, file))).group(1) # type: ignore[union-attr] + return next(filter(bool, map(changelog_version_re.match, file))).group(1) # ty: ignore[invalid-argument-type] @duty @@ -77,10 +77,15 @@ def check_docs(ctx: Context) -> None: @duty(nofail=PY_VERSION == PY_DEV) def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" - os.environ["MYPYPATH"] = "src" - os.environ["FORCE_COLOR"] = "1" + py = f"{sys.version_info.major}.{sys.version_info.minor}" ctx.run( - tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"), + tools.ty.check( + *PY_SRC_LIST, + config_file="config/ty.toml", + color=True, + error_on_warning=True, + python_version=py, + ), title=pyprefix("Type-checking"), ) diff --git a/pyproject.toml b/pyproject.toml index 7f033477..f661a98b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ getter = "scripts.get_version:get_version" [tool.pdm.build] # Include as much as possible in the source distribution, to help redistributors. -excludes = ["**/.pytest_cache", "**/.mypy_cache"] +excludes = ["**/.pytest_cache"] source-includes = [ "config", "docs", @@ -98,7 +98,7 @@ ci = [ "pytest-cov>=5.0", "pytest-randomly>=3.15", "pytest-xdist>=3.6", - "mypy>=1.10", + "ty>=0.0.14", "types-markdown>=3.6", "types-pyyaml>=6.0", ] diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py index b5499b7a..8e8a7bf8 100644 --- a/scripts/gen_credits.py +++ b/scripts/gen_credits.py @@ -47,7 +47,7 @@ def _norm_name(name: str) -> str: return name.replace("_", "-").replace(".", "-").lower() -def _requirements(deps: list[str]) -> dict[str, Requirement]: +def _requirements(deps: Iterable[str]) -> dict[str, Requirement]: return {_norm_name((req := Requirement(dep)).name): req for dep in deps} @@ -63,8 +63,8 @@ def _extra_marker(req: Requirement) -> str | None: def _get_metadata() -> Metadata: metadata = {} for pkg in distributions(): - name = _norm_name(pkg.name) # type: ignore[attr-defined,unused-ignore] - metadata[name] = _merge_fields(pkg.metadata) # type: ignore[arg-type] + name = _norm_name(pkg.name) + metadata[name] = _merge_fields(pkg.metadata) # ty: ignore[invalid-argument-type] metadata[name]["spec"] = set() metadata[name]["extras"] = set() metadata[name].setdefault("summary", "") @@ -77,10 +77,11 @@ def _set_license(metadata: PackageMetadata) -> None: license_name = license_field if isinstance(license_field, str) else " + ".join(license_field) check_classifiers = license_name in ("UNKNOWN", "Dual License", "") or license_name.count("\n") if check_classifiers: - license_names = [] - for classifier in metadata["classifier"]: - if classifier.startswith("License ::"): - license_names.append(classifier.rsplit("::", 1)[1].strip()) + license_names = [ + classifier.rsplit("::", 1)[1].strip() + for classifier in metadata["classifier"] + if classifier.startswith("License ::") + ] license_name = " + ".join(license_names) metadata["license"] = license_name or "?" @@ -90,8 +91,8 @@ def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata for dep_name, dep_req in base_deps.items(): if dep_name not in metadata or dep_name == "mkdocstrings": continue - metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # type: ignore[operator] - metadata[dep_name]["extras"] |= dep_req.extras # type: ignore[operator] + metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # ty: ignore[unsupported-operator] + metadata[dep_name]["extras"] |= dep_req.extras # ty: ignore[unsupported-operator] deps[dep_name] = metadata[dep_name] again = True @@ -109,7 +110,7 @@ def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata and dep_name != project["name"] and (not extra_marker or extra_marker in deps[pkg_name]["extras"]) ): - metadata[dep_name]["spec"] |= {str(spec) for spec in requirement.specifier} # type: ignore[operator] + metadata[dep_name]["spec"] |= {str(spec) for spec in requirement.specifier} # ty: ignore[unsupported-operator] deps[dep_name] = metadata[dep_name] again = True @@ -121,7 +122,7 @@ def _render_credits() -> str: dev_dependencies = _get_deps(_requirements(devdeps), metadata) prod_dependencies = _get_deps( _requirements( - chain( # type: ignore[arg-type] + chain( project.get("dependencies", []), chain(*project.get("optional-dependencies", {}).values()), ), diff --git a/scripts/get_version.py b/scripts/get_version.py index 3c425a73..d56f5858 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -22,7 +22,7 @@ def get_version() -> str: if scm_version.version <= Version("0.1"): # Missing Git tags? with suppress(OSError, StopIteration): # noqa: SIM117 with _changelog.open("r", encoding="utf8") as file: - match = next(filter(None, map(_changelog_version_re.match, file))) + match = next(filter(None, map(_changelog_version_re.match, file))) # ty: ignore[invalid-argument-type] scm_version = scm_version._replace(version=Version(match.group(1))) return default_version_formatter(scm_version) diff --git a/scripts/make.py b/scripts/make.py index b741a366..7fa7b56d 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -117,8 +117,8 @@ def clean() -> None: for path in paths_to_clean: shutil.rmtree(path, ignore_errors=True) - cache_dirs = {".cache", ".pytest_cache", ".mypy_cache", ".ruff_cache", "__pycache__"} - for dirpath in Path(".").rglob("*/"): + cache_dirs = {".cache", ".pytest_cache", ".ruff_cache", "__pycache__"} + for dirpath in Path().rglob("*/"): if dirpath.parts[0] not in (".venv", ".venvs") and dirpath.name in cache_dirs: shutil.rmtree(dirpath, ignore_errors=True) @@ -151,7 +151,7 @@ def main() -> int: ), flush=True, ) - if os.path.exists(".venv"): + if Path(".venv").exists(): print("\nAvailable tasks", flush=True) run("default", "duty", "--list") return 0 @@ -163,28 +163,28 @@ def main() -> int: if not args: print("make: run: missing command", file=sys.stderr) return 1 - run("default", *args) # ty: ignore[missing-argument] + run("default", *args) return 0 if cmd == "multirun": if not args: print("make: run: missing command", file=sys.stderr) return 1 - multirun(*args) # ty: ignore[missing-argument] + multirun(*args) return 0 if cmd == "allrun": if not args: print("make: run: missing command", file=sys.stderr) return 1 - allrun(*args) # ty: ignore[missing-argument] + allrun(*args) return 0 if cmd.startswith("3."): if not args: print("make: run: missing command", file=sys.stderr) return 1 - run(cmd, *args) # ty: ignore[missing-argument] + run(cmd, *args) return 0 opts = [] diff --git a/src/mkdocstrings/_internal/debug.py b/src/mkdocstrings/_internal/debug.py index f6b11600..7b56409b 100644 --- a/src/mkdocstrings/_internal/debug.py +++ b/src/mkdocstrings/_internal/debug.py @@ -85,7 +85,7 @@ def _get_debug_info() -> _Environment: interpreter_version=py_version, interpreter_path=sys.executable, platform=platform.platform(), - variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], # ty: ignore[invalid-argument-type] + variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) diff --git a/src/mkdocstrings/_internal/download.py b/src/mkdocstrings/_internal/download.py index ffe25e6b..2d8b25f1 100644 --- a/src/mkdocstrings/_internal/download.py +++ b/src/mkdocstrings/_internal/download.py @@ -1,14 +1,19 @@ +from __future__ import annotations + import base64 import gzip import os import re import urllib.parse import urllib.request -from collections.abc import Mapping -from typing import BinaryIO, Optional +from typing import TYPE_CHECKING, BinaryIO from mkdocstrings._internal.loggers import get_logger +if TYPE_CHECKING: + from collections.abc import Mapping + + _logger = get_logger("mkdocstrings") # Regex pattern for an environment variable in the form ${ENV_VAR}. @@ -25,11 +30,11 @@ def _download_url_with_gz(url: str) -> bytes: with urllib.request.urlopen(req) as resp: # noqa: S310 content: BinaryIO = resp if "gzip" in resp.headers.get("content-encoding", ""): - content = gzip.GzipFile(fileobj=resp) # type: ignore[assignment] + content = gzip.GzipFile(fileobj=resp) # ty: ignore[invalid-assignment] return content.read() -def _expand_env_vars(credential: str, url: str, env: Optional[Mapping[str, str]] = None) -> str: +def _expand_env_vars(credential: str, url: str, env: Mapping[str, str] | None = None) -> str: """A safe implementation of environment variable substitution. It only supports the following forms: `${ENV_VAR}`. diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index d20e57ac..4439d400 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -189,7 +189,7 @@ def _process_block( if "locale" in signature(handler.render).parameters: render = partial(handler.render, locale=self._handlers._locale) else: - render = handler.render # type: ignore[assignment] + render = handler.render try: rendered = render(data, options) except TemplateNotFound as exc: @@ -301,7 +301,7 @@ def _remove_duplicated_headings(self, parent: Element) -> None: class _TocLabelsTreeProcessor(Treeprocessor): def run(self, root: Element) -> None: # noqa: ARG002 - self._override_toc_labels(self.md.toc_tokens) # type: ignore[attr-defined] + self._override_toc_labels(self.md.toc_tokens) # ty: ignore[unresolved-attribute] def _override_toc_labels(self, tokens: list[dict[str, Any]]) -> None: for token in tokens: @@ -349,7 +349,7 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me # Zensical integration: get the current page from the Zensical-specific preprocessor. if "zensical_current_page" in md.preprocessors: - self._autorefs.current_page = md.preprocessors["zensical_current_page"] # type: ignore[assignment] + self._autorefs.current_page = md.preprocessors["zensical_current_page"] md.parser.blockprocessors.register( AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs), @@ -387,19 +387,21 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me } -def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str | Extension], dict[str, Any]]: +def _split_configs( + markdown_extensions: list[str | dict[str, dict[str, Any]] | Extension], +) -> tuple[list[str | Extension], dict[str, dict[str, Any]]]: # Split markdown extensions and their configs from mkdocs.yml - mdx: list[str] = [] - mdx_config: dict[str, Any] = {} + mdx: list[str | Extension] = [] + mdx_config: dict[str, dict[str, Any]] = {} for item in markdown_extensions: - if isinstance(item, str): + if isinstance(item, (str, Extension)): mdx.append(item) elif isinstance(item, dict): for key, value in item.items(): mdx.append(key) mdx_config[key] = value break # Only one item per dict - return mdx, mdx_config # type: ignore[return-value] + return mdx, mdx_config class _ToolConfig: @@ -418,7 +420,7 @@ def makeExtension( # noqa: N802 inventory_version: str | None = None, handlers: dict[str, dict] | None = None, custom_templates: str | None = None, - markdown_extensions: list[str | dict] | None = None, + markdown_extensions: list[str | dict | Extension] | None = None, locale: str | None = None, config_file_path: str | None = None, ) -> MkdocstringsExtension: diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 05808ef9..41d89da9 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -150,16 +150,14 @@ def __init__( # add extended theme templates extended_templates_dirs = self.get_extended_templates_dirs(self.name) - for templates_dir in extended_templates_dirs: - paths.append(templates_dir / self.theme) + paths.extend(templates_dir / self.theme for templates_dir in extended_templates_dirs) # add fallback theme templates if self.fallback_theme and self.fallback_theme != self.theme: paths.append(themes_dir / self.fallback_theme) # add fallback theme of extended templates - for templates_dir in extended_templates_dirs: - paths.append(templates_dir / self.fallback_theme) + paths.extend(templates_dir / self.fallback_theme for templates_dir in extended_templates_dirs) for path in paths: css_path = path / "style.css" @@ -361,24 +359,24 @@ def do_convert_markdown( global _markdown_conversion_layer # noqa: PLW0603 _markdown_conversion_layer += 1 treeprocessors = self.md.treeprocessors - treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level # type: ignore[attr-defined] - treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" # type: ignore[attr-defined] - treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph # type: ignore[attr-defined] + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph if BacklinksTreeProcessor.name in treeprocessors: - treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id # type: ignore[attr-defined] + treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # type: ignore[attr-defined] + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # ty: ignore[unresolved-attribute] try: return Markup(self.md.convert(text)) finally: - treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 # type: ignore[attr-defined] - treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" # type: ignore[attr-defined] - treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False # type: ignore[attr-defined] + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False if BacklinksTreeProcessor.name in treeprocessors: - treeprocessors[BacklinksTreeProcessor.name].initial_id = None # type: ignore[attr-defined] + treeprocessors[BacklinksTreeProcessor.name].initial_id = None if AutorefsInlineProcessor.name in self.md.inlinePatterns: - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None self.md.reset() _markdown_conversion_layer -= 1 @@ -475,8 +473,12 @@ def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: # MkDocs adds its own (required) extension that's not part of the config. Propagate it. if "relpath" in md.treeprocessors: relpath = md.treeprocessors["relpath"] - new_relpath = type(relpath)(relpath.file, relpath.files, relpath.config) # type: ignore[attr-defined,call-arg] + new_relpath = type(relpath)(relpath.file, relpath.files, relpath.config) new_md.treeprocessors.register(new_relpath, "relpath", priority=0) + elif "zrelpath" in md.treeprocessors: + zrelpath = md.treeprocessors["zrelpath"] + new_zrelpath = type(zrelpath)(new_md, zrelpath.path, zrelpath.use_directory_urls) + new_md.treeprocessors.register(new_zrelpath, "zrelpath", priority=0) self._md = new_md @@ -599,7 +601,7 @@ def _download_inventories(self) -> None: for handler_name, conf in self._handlers_config.items(): handler = self.get_handler(handler_name) - if handler.get_inventory_urls.__func__ is BaseHandler.get_inventory_urls: # type: ignore[attr-defined] + if handler.get_inventory_urls.__func__ is BaseHandler.get_inventory_urls: if inv_configs := conf.pop("import", ()): warn( "mkdocstrings v1 will stop handling 'import' in handlers configuration. " @@ -641,7 +643,7 @@ def _yield_inventory_items(self) -> Iterator[tuple[str, str]]: for fut, (handler, url, conf) in reversed(self._inv_futures.items()): try: yield from handler.load_inventory(BytesIO(fut.result()), url, **conf) - except Exception as error: # noqa: BLE001 + except Exception as error: # noqa: BLE001,PERF203 _logger.error("Couldn't load inventory %s through handler '%s': %s", url, handler.name, error) # noqa: TRY400 self._inv_futures = {} diff --git a/src/mkdocstrings/_internal/handlers/rendering.py b/src/mkdocstrings/_internal/handlers/rendering.py index 25db87e1..264a77ef 100644 --- a/src/mkdocstrings/_internal/handlers/rendering.py +++ b/src/mkdocstrings/_internal/handlers/rendering.py @@ -84,7 +84,7 @@ def __init__(self, md: Markdown): self._css_class = config.pop("css_class", "highlight") super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys}) - def highlight( + def highlight( # ty: ignore[invalid-method-override] self, src: str, language: str | None = None, @@ -113,7 +113,7 @@ def highlight( src = textwrap.dedent(src) kwargs.setdefault("css_class", self._css_class) - old_linenums = self.linenums # type: ignore[has-type] + old_linenums = self.linenums if linenums is not None: self.linenums = linenums try: @@ -240,7 +240,7 @@ def __init__(self, md: Markdown, headings: list[Element]): def run(self, root: Element) -> None: """Record all heading elements encountered in the document.""" - permalink_class = self.md.treeprocessors["toc"].permalink_class # type: ignore[attr-defined] + permalink_class = self.md.treeprocessors["toc"].permalink_class for el in root.iter(): if self.regex.fullmatch(el.tag): el = copy.copy(el) # noqa: PLW2901 diff --git a/src/mkdocstrings/_internal/loggers.py b/src/mkdocstrings/_internal/loggers.py index c67a7f4e..29f620a3 100644 --- a/src/mkdocstrings/_internal/loggers.py +++ b/src/mkdocstrings/_internal/loggers.py @@ -82,7 +82,7 @@ def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None: if (key := (self, str(msg))) in self._logged: return self._logged.add(key) - super().log(level, msg, *args, **kwargs) # type: ignore[arg-type] + super().log(level, msg, *args, **kwargs) # ty: ignore[invalid-argument-type] class TemplateLogger: diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index 2c27a60a..2e7a8d39 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -140,7 +140,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: handlers = Handlers( default=self.config.default_handler, handlers_config=self.config.handlers, - theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), + theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), # noqa: PTH120 custom_templates=self.config.custom_templates, mdx=config.markdown_extensions, mdx_config=config.mdx_configs, @@ -156,7 +156,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: autorefs: AutorefsPlugin try: # If autorefs plugin is explicitly enabled, just use it. - autorefs = config.plugins["autorefs"] # type: ignore[assignment] + autorefs = config.plugins["autorefs"] # ty: ignore[invalid-assignment] _logger.debug("Picked up existing autorefs instance %r", autorefs) except KeyError: # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`. @@ -167,7 +167,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: _logger.debug("Added a subdued autorefs instance %r", autorefs) mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) - config.markdown_extensions.append(mkdocstrings_extension) # type: ignore[arg-type] + config.markdown_extensions.append(mkdocstrings_extension) # ty: ignore[invalid-argument-type] config.extra_css.insert(0, self.css_filename) # So that it has lower priority than user files. @@ -199,7 +199,7 @@ def plugin_enabled(self) -> bool: @event_priority(50) # Early, before autorefs' starts applying cross-refs and collecting backlinks. def _on_env_load_inventories(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 if self.plugin_enabled and self._handlers: - register = config.plugins["autorefs"].register_url # type: ignore[attr-defined] + register = config.plugins["autorefs"].register_url # ty: ignore[possibly-missing-attribute] for identifier, url in self._handlers._yield_inventory_items(): register(identifier, url) @@ -207,14 +207,14 @@ def _on_env_load_inventories(self, env: Environment, config: MkDocsConfig, *args def _on_env_add_css(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 if self.plugin_enabled and self._handlers: css_content = "\n".join(handler.extra_css for handler in self.handlers.seen_handlers) - write_file(css_content.encode("utf-8"), os.path.join(config.site_dir, self.css_filename)) + write_file(css_content.encode("utf-8"), os.path.join(config.site_dir, self.css_filename)) # noqa: PTH118 @event_priority(-20) # Late, not important. def _on_env_write_inventory(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 if self.plugin_enabled and self._handlers and self.inventory_enabled: _logger.debug("Creating inventory file objects.inv") inv_contents = self.handlers.inventory.format_sphinx() - write_file(inv_contents, os.path.join(config.site_dir, "objects.inv")) + write_file(inv_contents, os.path.join(config.site_dir, "objects.inv")) # noqa: PTH118 @event_priority(-100) # Last, after autorefs has finished applying cross-refs and collecting backlinks. def _on_env_apply_backlinks(self, env: Environment, /, *, config: MkDocsConfig, files: Files) -> Environment: # noqa: ARG002 @@ -226,12 +226,12 @@ def repl(match: Match) -> str: # The handler doesn't implement backlinks, # return early to avoid computing them. - if handler.render_backlinks.__func__ is BaseHandler.render_backlinks: # type: ignore[attr-defined] + if handler.render_backlinks.__func__ is BaseHandler.render_backlinks: return "" identifier = match.group(1) aliases = handler.get_aliases(identifier) - backlinks = self._autorefs.get_backlinks(identifier, *aliases, from_url=file.page.url) # type: ignore[union-attr] + backlinks = self._autorefs.get_backlinks(identifier, *aliases, from_url=file.page.url) # No backlinks, avoid calling the handler's method. if not backlinks: @@ -240,7 +240,7 @@ def repl(match: Match) -> str: if "locale" in signature(handler.render_backlinks).parameters: render_backlinks = partial(handler.render_backlinks, locale=self.handlers._locale) else: - render_backlinks = handler.render_backlinks # type: ignore[assignment] + render_backlinks = handler.render_backlinks return render_backlinks(backlinks) diff --git a/tests/conftest.py b/tests/conftest.py index 8a132e29..0c9a89ad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,7 +23,7 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera """Yield a MkDocs configuration object.""" conf = MkDocsConfig() while hasattr(request, "_parent_request") and hasattr(request._parent_request, "_parent_request"): - request = request._parent_request + request = request._parent_request # ty: ignore[invalid-assignment] conf_dict = { "site_name": "foo", @@ -33,7 +33,7 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera **getattr(request, "param", {}), } # Re-create it manually as a workaround for https://github.com/mkdocs/mkdocs/issues/2289 - mdx_configs: dict[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) + mdx_configs: dict[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) # ty: ignore[invalid-argument-type] conf.load_dict(conf_dict) assert conf.validate() == ([], []) diff --git a/tests/test_api.py b/tests/test_api.py index 9821e65c..0d714f0a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -50,7 +50,7 @@ def _yield_public_objects( if modules: yield member yield from _yield_public_objects( - member, # type: ignore[arg-type] + member, # ty: ignore[invalid-argument-type] modules=modules, modulelevel=modulelevel, inherited=inherited, @@ -62,7 +62,7 @@ def _yield_public_objects( continue if member.is_class and not modulelevel: yield from _yield_public_objects( - member, # type: ignore[arg-type] + member, # ty: ignore[invalid-argument-type] modules=modules, modulelevel=False, inherited=inherited, @@ -91,7 +91,7 @@ def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | g def _fixture_inventory() -> Inventory: inventory_file = Path(__file__).parent.parent / "site" / "objects.inv" if not inventory_file.exists(): - pytest.skip("The objects inventory is not available.") # ty: ignore[call-non-callable] + pytest.skip("The objects inventory is not available.") with inventory_file.open("rb") as file: return Inventory.parse_sphinx(file) diff --git a/tests/test_extension.py b/tests/test_extension.py index c283f9c0..6ba8adc1 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -99,7 +99,7 @@ def test_no_double_toc(ext_markdown: Markdown, expect_permalink: str) -> None: ) assert output.count(expect_permalink) == 5 assert 'id="tests.fixtures.headings--foo"' in output - assert ext_markdown.toc_tokens == [ # type: ignore[attr-defined] # the member gets populated only with 'toc' extension + assert ext_markdown.toc_tokens == [ # ty: ignore[unresolved-attribute] { "level": 1, "id": "aa", @@ -154,10 +154,10 @@ def test_use_custom_handler(ext_markdown: Markdown) -> None: def test_register_every_identifier_alias(plugin: MkdocstringsPlugin, ext_markdown: Markdown) -> None: """Assert that we don't preemptively register all identifiers of a rendered object.""" - handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] + handler = plugin._handlers.get_handler("python") # ty: ignore[possibly-missing-attribute] ids = ("id1", "id2", "id3") - handler.get_aliases = lambda _: ids # type: ignore[method-assign] - autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs # type: ignore[attr-defined] + handler.get_aliases = lambda _: ids # ty: ignore[invalid-assignment] + autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs class Page: url = "foo" diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 30bdbdfc..fd9eb714 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -62,7 +62,7 @@ def test_extended_templates(tmp_path: Path, plugin: MkdocstringsPlugin) -> None: tmp_path: Temporary folder. plugin: Instance of our plugin. """ - handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] + handler = plugin._handlers.get_handler("python") # ty: ignore[possibly-missing-attribute] # monkeypatch Jinja env search path search_paths = [ @@ -71,7 +71,7 @@ def test_extended_templates(tmp_path: Path, plugin: MkdocstringsPlugin) -> None: extended_theme := tmp_path / "extended_theme", extended_fallback_theme := tmp_path / "extended_fallback_theme", ] - handler.env.loader.searchpath = search_paths # type: ignore[union-attr] + handler.env.loader.searchpath = search_paths # ty: ignore[invalid-assignment] # assert "new" template is not found with pytest.raises(expected_exception=TemplateNotFound): @@ -117,7 +117,7 @@ def test_nested_autodoc(ext_markdown: Markdown) -> None: ) assert 'id="tests.fixtures.nesting.Class"' in output assert 'id="tests.fixtures.nesting.Class.method"' in output - assert ext_markdown.toc_tokens == [ # type: ignore[attr-defined] + assert ext_markdown.toc_tokens == [ # ty: ignore[unresolved-attribute] { "level": 1, "id": "tests.fixtures.nesting.Class", diff --git a/tests/test_inventory.py b/tests/test_inventory.py index ab61e599..858ac340 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -4,6 +4,7 @@ from io import BytesIO from os.path import join +from pathlib import Path import pytest from mkdocs.commands.build import build @@ -48,7 +49,7 @@ def test_sphinx_load_mkdocstrings_inventory_file() -> None: mkdocs_config["plugins"].run_event("shutdown") own_inv = mkdocs_config["plugins"]["mkdocstrings"].handlers.inventory - with open("site/objects.inv", "rb") as fp: + with Path("site/objects.inv").open("rb") as fp: sphinx_inv = sphinx.InventoryFile.load(fp, "", join) sphinx_inv_length = sum(len(sphinx_inv[key]) for key in sphinx_inv)