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)