From 5035e9269fe11664fd25e438ac8f746721b3de0a Mon Sep 17 00:00:00 2001
From: Waylan Limberg
Date: Tue, 31 Oct 2023 15:26:07 -0400
Subject: [PATCH 1/6] fix: Make extension paths relative to config file
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PR #112: https://github.com/mkdocstrings/python/pull/112
Co-authored-by: Timothée Mazzucotelli
---
src/mkdocstrings_handlers/python/handler.py | 34 ++++++++++++++++--
tests/test_handler.py | 40 +++++++++++++++++++++
2 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py
index 056429e8..169546fd 100644
--- a/src/mkdocstrings_handlers/python/handler.py
+++ b/src/mkdocstrings_handlers/python/handler.py
@@ -9,7 +9,7 @@
import sys
from collections import ChainMap
from contextlib import suppress
-from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, Iterator, Mapping
+from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, Iterator, Mapping, Sequence
from griffe.collections import LinesCollection, ModulesCollection
from griffe.docstrings.parsers import Parser
@@ -265,8 +265,9 @@ def collect(self, identifier: str, config: Mapping[str, Any]) -> CollectorItem:
parser = parser_name and Parser(parser_name)
if unknown_module:
+ extensions = self.normalize_extension_paths(final_config.get("extensions", []))
loader = GriffeLoader(
- extensions=load_extensions(final_config.get("extensions", [])),
+ extensions=load_extensions(extensions),
search_paths=self._paths,
docstring_parser=parser,
docstring_options=parser_options,
@@ -369,6 +370,35 @@ def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: D102 (ig
return tuple(anchors)
return tuple(anchors)
+ def normalize_extension_paths(self, extensions: Sequence) -> Sequence:
+ """Resolve extension paths relative to config file."""
+ if self._config_file_path is None:
+ return extensions
+
+ base_path = os.path.dirname(self._config_file_path)
+ normalized = []
+
+ for ext in extensions:
+ if isinstance(ext, dict):
+ pth, options = next(iter(ext.items()))
+ pth = str(pth)
+ else:
+ pth = str(ext)
+ options = None
+
+ if pth.endswith(".py") or ".py:" in pth or "/" in pth or "\\" in pth: # noqa: SIM102
+ # This is a sytem path. Normalize it.
+ if not os.path.isabs(pth):
+ # Make path absolute relative to config file path.
+ pth = os.path.normpath(os.path.join(base_path, pth))
+
+ if options is not None:
+ normalized.append({pth: options})
+ else:
+ normalized.append(pth)
+
+ return normalized
+
def get_handler(
*,
diff --git a/tests/test_handler.py b/tests/test_handler.py
index 4971e132..e1d92c18 100644
--- a/tests/test_handler.py
+++ b/tests/test_handler.py
@@ -105,3 +105,43 @@ def test_expand_globs_without_changing_directory() -> None:
)
for path in list(glob(os.path.abspath(".") + "/*.md")):
assert path in handler._paths
+
+
+@pytest.mark.parametrize(
+ ("expect_change", "extension"),
+ [
+ (True, "extension.py"),
+ (True, "extension.py:SomeExtension"),
+ (True, "path/to/extension.py"),
+ (True, "path/to/extension.py:SomeExtension"),
+ (True, {"extension.py": {"option": "value"}}),
+ (True, {"extension.py:SomeExtension": {"option": "value"}}),
+ (True, {"path/to/extension.py": {"option": "value"}}),
+ (True, {"path/to/extension.py:SomeExtension": {"option": "value"}}),
+ (False, "/absolute/path/to/extension.py"),
+ (False, "/absolute/path/to/extension.py:SomeExtension"),
+ (False, {"/absolute/path/to/extension.py": {"option": "value"}}),
+ (False, {"/absolute/path/to/extension.py:SomeExtension": {"option": "value"}}),
+ (False, "dot.notation.path.to.extension"),
+ (False, "dot.notation.path.to.pyextension"),
+ (False, {"dot.notation.path.to.extension": {"option": "value"}}),
+ (False, {"dot.notation.path.to.pyextension": {"option": "value"}}),
+ ],
+)
+def test_extension_paths(tmp_path: Path, expect_change: bool, extension: str | dict) -> None:
+ """Assert extension paths are resolved relative to config file."""
+ handler = get_handler(
+ theme="material",
+ config_file_path=str(tmp_path.joinpath("mkdocs.yml")),
+ )
+ normalized = handler.normalize_extension_paths([extension])[0]
+ if expect_change:
+ if isinstance(normalized, str) and isinstance(extension, str):
+ assert normalized == str(tmp_path.joinpath(extension))
+ elif isinstance(normalized, dict) and isinstance(extension, dict):
+ pth, options = next(iter(extension.items()))
+ assert normalized == {str(tmp_path.joinpath(pth)): options}
+ else:
+ raise ValueError("Normalization must not change extension items type")
+ else:
+ assert normalized == extension
From a9078a020e984f7d94e531644e613c08a5fc35ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?=
Date: Sun, 12 Nov 2023 18:03:31 +0100
Subject: [PATCH 2/6] tests: Modernize test fixtures
---
tests/conftest.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/tests/conftest.py b/tests/conftest.py
index 0801ec0b..1c1a1c54 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -7,12 +7,12 @@
import pytest
from markdown.core import Markdown
-from mkdocs import config
-from mkdocs.config.defaults import get_schema
+from mkdocs.config.defaults import MkDocsConfig
if TYPE_CHECKING:
from pathlib import Path
+ from mkdocs import config
from mkdocstrings.plugin import MkdocstringsPlugin
from mkdocstrings_handlers.python.handler import PythonHandler
@@ -29,12 +29,11 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera
Yields:
MkDocs config.
"""
- conf = config.Config(schema=get_schema()) # type: ignore[call-arg]
+ conf = MkDocsConfig()
while hasattr(request, "_parent_request") and hasattr(request._parent_request, "_parent_request"):
request = request._parent_request
conf_dict = {
- "config_file_path": "mkdocs.yml",
"site_name": "foo",
"site_url": "https://example.org/",
"site_dir": str(tmp_path),
From 574b234ec4d13dfde5ec906b356c71c3fcd63843 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?=
Date: Sun, 12 Nov 2023 18:20:12 +0100
Subject: [PATCH 3/6] chore: Template upgrade
---
.copier-answers.yml | 4 +-
.github/ISSUE_TEMPLATE/bug_report.md | 73 +++++++----
.github/ISSUE_TEMPLATE/config.yml | 5 +
.github/ISSUE_TEMPLATE/feature_request.md | 23 ++--
CONTRIBUTING.md | 5 +-
Makefile | 3 +-
README.md | 2 +-
config/git-changelog.toml | 8 ++
config/ruff.toml | 3 +
config/vscode/launch.json | 36 ++++++
config/vscode/settings.json | 52 ++++++++
config/vscode/tasks.json | 93 ++++++++++++++
docs/css/insiders.css | 5 +-
docs/insiders/index.md | 3 -
duties.py | 142 ++++++++++------------
mkdocs.insiders.yml | 4 -
mkdocs.yml | 11 +-
pyproject.toml | 43 ++++---
scripts/gen_credits.py | 19 ++-
scripts/gen_ref_nav.py | 10 +-
scripts/insiders.py | 6 +-
src/mkdocstrings_handlers/debug.py | 106 ++++++++++++++++
22 files changed, 496 insertions(+), 160 deletions(-)
create mode 100644 .github/ISSUE_TEMPLATE/config.yml
create mode 100644 config/git-changelog.toml
create mode 100644 config/vscode/launch.json
create mode 100644 config/vscode/settings.json
create mode 100644 config/vscode/tasks.json
delete mode 100644 mkdocs.insiders.yml
create mode 100644 src/mkdocstrings_handlers/debug.py
diff --git a/.copier-answers.yml b/.copier-answers.yml
index da168350..58ab94af 100644
--- a/.copier-answers.yml
+++ b/.copier-answers.yml
@@ -1,5 +1,5 @@
# Changes here will be overwritten by Copier
-_commit: 0.16.10
+_commit: 1.1.3
_src_path: gh:pawamoy/copier-pdm
author_email: pawamoy@pm.me
author_fullname: Timothée Mazzucotelli
@@ -9,8 +9,10 @@ copyright_holder: Timothée Mazzucotelli
copyright_holder_email: pawamoy@pm.me
copyright_license: ISC License
insiders: true
+insiders_repository_name: mkdocstrings-python
project_description: A Python handler for mkdocstrings.
project_name: mkdocstrings-python
+public_release: true
python_package_command_line_name: ''
python_package_distribution_name: mkdocstrings-python
python_package_import_name: mkdocstrings_handlers
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index ad93416e..ac47315f 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,32 +1,61 @@
---
name: Bug report
-about: Create a report to help us improve
-title: ''
+about: Create a bug report to help us improve.
+title: "bug: "
labels: unconfirmed
-assignees: ''
-
+assignees: [pawamoy]
---
-**Describe the bug**
-A clear and concise description of what the bug is.
+### Description of the bug
+
+
+### To Reproduce
+
+
+```
+WRITE MRE / INSTRUCTIONS HERE
+```
+
+### Full traceback
+
+
+Full traceback
+
+```python
+PASTE TRACEBACK HERE
+```
+
+
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Run command '...'
-3. Scroll down to '...'
-4. See error
+### Expected behavior
+
-**Expected behavior**
-A clear and concise description of what you expected to happen.
+### Environment information
+
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
+```bash
+python -m mkdocstrings_handlers.debug # | xclip -selection clipboard
+```
-**System (please complete the following information):**
-- `mkdocstrings-python` version: [e.g. 0.2.1]
-- Python version: [e.g. 3.8]
-- OS: [Windows/Linux]
+PASTE OUTPUT HERE
-**Additional context**
-Add any other context about the problem here.
+### Additional context
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..9c9765bc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+- name: I have a question / I need help
+ url: https://github.com/mkdocstrings/python/discussions/new?category=q-a
+ about: Ask and answer questions in the Discussions tab.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 4fe86d5e..2df98fbc 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,20 +1,19 @@
---
name: Feature request
-about: Suggest an idea for this project
-title: ''
+about: Suggest an idea for this project.
+title: "feature: "
labels: feature
-assignees: ''
-
+assignees: pawamoy
---
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+### Is your feature request related to a problem? Please describe.
+
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
+### Describe the solution you'd like
+
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
+### Describe alternatives you've considered
+
-**Additional context**
-Add any other context or screenshots about the feature request here.
+### Additional context
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0dafa847..dfe5a910 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -44,8 +44,9 @@ on multiple Python versions, you run the task directly with `pdm run duty TASK`.
The Makefile detects if a virtual environment is activated,
so `make` will work the same with the virtualenv activated or not.
-If you work in VSCode,
-[see examples of tasks and run configurations](https://pawamoy.github.io/copier-pdm/work/#vscode-setup).
+If you work in VSCode, we provide
+[an action to configure VSCode](https://pawamoy.github.io/copier-pdm/work/#vscode-setup)
+for the project.
## Development
diff --git a/Makefile b/Makefile
index 7e8de7cc..437880eb 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,8 @@ BASIC_DUTIES = \
docs \
docs-deploy \
format \
- release
+ release \
+ vscode
QUALITY_DUTIES = \
check-quality \
diff --git a/README.md b/README.md
index 7535de03..6b3afb5f 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
-
+
diff --git a/config/git-changelog.toml b/config/git-changelog.toml
new file mode 100644
index 00000000..44e2b1fb
--- /dev/null
+++ b/config/git-changelog.toml
@@ -0,0 +1,8 @@
+bump = "auto"
+convention = "angular"
+in-place = true
+output = "CHANGELOG.md"
+parse-refs = false
+parse-trailers = true
+sections = ["build", "deps", "feat", "fix", "refactor"]
+template = "keepachangelog"
diff --git a/config/ruff.toml b/config/ruff.toml
index 9925518c..99efa62b 100644
--- a/config/ruff.toml
+++ b/config/ruff.toml
@@ -77,6 +77,9 @@ ignore = [
"src/*/cli.py" = [
"T201", # Print statement
]
+"src/*/debug.py" = [
+ "T201", # Print statement
+]
"scripts/*.py" = [
"INP001", # File is part of an implicit namespace package
"T201", # Print statement
diff --git a/config/vscode/launch.json b/config/vscode/launch.json
new file mode 100644
index 00000000..2e0d651e
--- /dev/null
+++ b/config/vscode/launch.json
@@ -0,0 +1,36 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "python (current file)",
+ "type": "python",
+ "request": "launch",
+ "program": "${file}",
+ "console": "integratedTerminal",
+ "justMyCode": false
+ },
+ {
+ "name": "test",
+ "type": "python",
+ "request": "launch",
+ "module": "pytest",
+ "justMyCode": false,
+ "args": [
+ "-c=config/pytest.ini",
+ "-vvv",
+ "--no-cov",
+ "--dist=no",
+ "tests",
+ "-k=${input:tests_selection}"
+ ]
+ }
+ ],
+ "inputs": [
+ {
+ "id": "tests_selection",
+ "type": "promptString",
+ "description": "Tests selection",
+ "default": ""
+ }
+ ]
+}
\ No newline at end of file
diff --git a/config/vscode/settings.json b/config/vscode/settings.json
new file mode 100644
index 00000000..17beee4b
--- /dev/null
+++ b/config/vscode/settings.json
@@ -0,0 +1,52 @@
+{
+ "files.watcherExclude": {
+ "**/__pypackages__/**": true,
+ "**/.venv*/**": true,
+ "**/venv*/**": true
+ },
+ "[python]": {
+ "editor.defaultFormatter": "ms-python.black-formatter"
+ },
+ "python.autoComplete.extraPaths": [
+ "__pypackages__/3.8/lib",
+ "__pypackages__/3.9/lib",
+ "__pypackages__/3.10/lib",
+ "__pypackages__/3.11/lib",
+ "__pypackages__/3.12/lib"
+ ],
+ "python.analysis.extraPaths": [
+ "__pypackages__/3.8/lib",
+ "__pypackages__/3.9/lib",
+ "__pypackages__/3.10/lib",
+ "__pypackages__/3.11/lib",
+ "__pypackages__/3.12/lib"
+ ],
+ "black-formatter.args": [
+ "--config=config/black.toml"
+ ],
+ "mypy-type-checker.args": [
+ "--config-file=config/mypy.ini"
+ ],
+ "python.testing.unittestEnabled": false,
+ "python.testing.pytestEnabled": true,
+ "python.testing.pytestArgs": [
+ "--config-file=config/pytest.ini"
+ ],
+ "ruff.format.args": [
+ "--config=config/ruff.toml"
+ ],
+ "ruff.lint.args": [
+ "--config=config/ruff.toml"
+ ],
+ "yaml.schemas": {
+ "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
+ },
+ "yaml.customTags": [
+ "!ENV scalar",
+ "!ENV sequence",
+ "!relative scalar",
+ "tag:yaml.org,2002:python/name:materialx.emoji.to_svg",
+ "tag:yaml.org,2002:python/name:materialx.emoji.twemoji",
+ "tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
+ ]
+}
\ No newline at end of file
diff --git a/config/vscode/tasks.json b/config/vscode/tasks.json
new file mode 100644
index 00000000..80cd13d2
--- /dev/null
+++ b/config/vscode/tasks.json
@@ -0,0 +1,93 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "changelog",
+ "type": "shell",
+ "command": "pdm run duty changelog"
+ },
+ {
+ "label": "check",
+ "type": "shell",
+ "command": "pdm run duty check"
+ },
+ {
+ "label": "check-quality",
+ "type": "shell",
+ "command": "pdm run duty check-quality"
+ },
+ {
+ "label": "check-types",
+ "type": "shell",
+ "command": "pdm run duty check-types"
+ },
+ {
+ "label": "check-docs",
+ "type": "shell",
+ "command": "pdm run duty check-docs"
+ },
+ {
+ "label": "check-dependencies",
+ "type": "shell",
+ "command": "pdm run duty check-dependencies"
+ },
+ {
+ "label": "check-api",
+ "type": "shell",
+ "command": "pdm run duty check-api"
+ },
+ {
+ "label": "clean",
+ "type": "shell",
+ "command": "pdm run duty clean"
+ },
+ {
+ "label": "docs",
+ "type": "shell",
+ "command": "pdm run duty docs"
+ },
+ {
+ "label": "docs-deploy",
+ "type": "shell",
+ "command": "pdm run duty docs-deploy"
+ },
+ {
+ "label": "format",
+ "type": "shell",
+ "command": "pdm run duty format"
+ },
+ {
+ "label": "lock",
+ "type": "shell",
+ "command": "pdm lock -G:all"
+ },
+ {
+ "label": "release",
+ "type": "shell",
+ "command": "pdm run duty release ${input:version}"
+ },
+ {
+ "label": "setup",
+ "type": "shell",
+ "command": "bash scripts/setup.sh"
+ },
+ {
+ "label": "test",
+ "type": "shell",
+ "command": "pdm run duty test coverage",
+ "group": "test"
+ },
+ {
+ "label": "vscode",
+ "type": "shell",
+ "command": "pdm run duty vscode"
+ }
+ ],
+ "inputs": [
+ {
+ "id": "version",
+ "type": "promptString",
+ "description": "Version"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/css/insiders.css b/docs/css/insiders.css
index b5547bd1..e7b9c74f 100644
--- a/docs/css/insiders.css
+++ b/docs/css/insiders.css
@@ -53,11 +53,10 @@ a.insiders {
}
.sponsorship-item {
- float: left;
border-radius: 100%;
- display: block;
+ display: inline-block;
height: 1.6rem;
- margin: .2rem;
+ margin: 0.1rem;
overflow: hidden;
width: 1.6rem;
}
diff --git a/docs/insiders/index.md b/docs/insiders/index.md
index a6df4a4d..7c69b590 100644
--- a/docs/insiders/index.md
+++ b/docs/insiders/index.md
@@ -123,9 +123,6 @@ You can cancel your sponsorship anytime.[^5]
-
-
-
If you sponsor publicly, you're automatically added here with a link to
your profile and avatar to show your support for *mkdocstrings-python*.
diff --git a/duties.py b/duties.py
index 8e3dbf64..cfdc9376 100644
--- a/duties.py
+++ b/duties.py
@@ -4,12 +4,13 @@
import os
import sys
+from contextlib import contextmanager
from importlib.metadata import version as pkgversion
from pathlib import Path
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Iterator
from duty import duty
-from duty.callables import black, blacken_docs, coverage, lazy, mkdocs, mypy, pytest, ruff, safety
+from duty.callables import black, coverage, lazy, mkdocs, mypy, pytest, ruff, safety
if TYPE_CHECKING:
from duty.context import Context
@@ -31,32 +32,16 @@ def pyprefix(title: str) -> str: # noqa: D103
return title
-def merge(d1: Any, d2: Any) -> Any: # noqa: D103
- basic_types = (int, float, str, bool, complex)
- if isinstance(d1, dict) and isinstance(d2, dict):
- for key, value in d2.items():
- if key in d1:
- if isinstance(d1[key], basic_types):
- d1[key] = value
- else:
- d1[key] = merge(d1[key], value)
- else:
- d1[key] = value
- return d1
- if isinstance(d1, list) and isinstance(d2, list):
- return d1 + d2
- return d2
-
-
-def mkdocs_config() -> str: # noqa: D103
- import mergedeep
-
- # force YAML loader to merge arrays
- mergedeep.merge = merge
-
+@contextmanager
+def material_insiders() -> Iterator[bool]: # noqa: D103
if "+insiders" in pkgversion("mkdocs-material"):
- return "mkdocs.insiders.yml"
- return "mkdocs.yml"
+ os.environ["MATERIAL_INSIDERS"] = "true"
+ try:
+ yield True
+ finally:
+ os.environ.pop("MATERIAL_INSIDERS")
+ else:
+ yield False
@duty
@@ -66,23 +51,9 @@ def changelog(ctx: Context) -> None:
Parameters:
ctx: The context instance (passed automatically).
"""
- from git_changelog.cli import build_and_render
+ from git_changelog.cli import main as git_changelog
- git_changelog = lazy(build_and_render, name="git_changelog")
- ctx.run(
- git_changelog(
- repository=".",
- output="CHANGELOG.md",
- convention="angular",
- template="keepachangelog",
- parse_trailers=True,
- parse_refs=False,
- sections=["build", "deps", "feat", "fix", "refactor"],
- bump_latest=True,
- in_place=True,
- ),
- title="Updating changelog",
- )
+ ctx.run(git_changelog, args=[[]], title="Updating changelog")
@duty(pre=["check_quality", "check_types", "check_docs", "check_dependencies", "check-api"])
@@ -139,12 +110,12 @@ def check_docs(ctx: Context) -> None:
"""
Path("htmlcov").mkdir(parents=True, exist_ok=True)
Path("htmlcov/index.html").touch(exist_ok=True)
- config = mkdocs_config()
- ctx.run(
- mkdocs.build(strict=True, config_file=config, verbose=True),
- title=pyprefix("Building documentation"),
- command=f"mkdocs build -vsf {config}",
- )
+ with material_insiders():
+ ctx.run(
+ mkdocs.build(strict=True, verbose=True),
+ title=pyprefix("Building documentation"),
+ command="mkdocs build -vs",
+ )
@duty
@@ -209,11 +180,12 @@ def docs(ctx: Context, host: str = "127.0.0.1", port: int = 8000) -> None:
host: The host to serve the docs from.
port: The port to serve the docs on.
"""
- ctx.run(
- mkdocs.serve(dev_addr=f"{host}:{port}", config_file=mkdocs_config()),
- title="Serving documentation",
- capture=False,
- )
+ with material_insiders():
+ ctx.run(
+ mkdocs.serve(dev_addr=f"{host}:{port}"),
+ title="Serving documentation",
+ capture=False,
+ )
@duty
@@ -224,22 +196,22 @@ def docs_deploy(ctx: Context) -> None:
ctx: The context instance (passed automatically).
"""
os.environ["DEPLOY"] = "true"
- config_file = mkdocs_config()
- if config_file == "mkdocs.yml":
- ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!")
- origin = ctx.run("git config --get remote.origin.url", silent=True)
- if "pawamoy-insiders/mkdocstrings-python" in origin:
- ctx.run("git remote add upstream git@github.com:mkdocstrings/python", silent=True, nofail=True)
- ctx.run(
- mkdocs.gh_deploy(config_file=config_file, remote_name="upstream", force=True),
- title="Deploying documentation",
- )
- else:
- ctx.run(
- lambda: False,
- title="Not deploying docs from public repository (do that from insiders instead!)",
- nofail=True,
- )
+ with material_insiders() as insiders:
+ if not insiders:
+ ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!")
+ origin = ctx.run("git config --get remote.origin.url", silent=True)
+ if "pawamoy-insiders/mkdocstrings-python" in origin:
+ ctx.run("git remote add upstream git@github.com:mkdocstrings/python", silent=True, nofail=True)
+ ctx.run(
+ mkdocs.gh_deploy(remote_name="upstream", force=True),
+ title="Deploying documentation",
+ )
+ else:
+ ctx.run(
+ lambda: False,
+ title="Not deploying docs from public repository (do that from insiders instead!)",
+ nofail=True,
+ )
@duty
@@ -254,11 +226,6 @@ def format(ctx: Context) -> None:
title="Auto-fixing code",
)
ctx.run(black.run(*PY_SRC_LIST, config="config/black.toml"), title="Formatting code")
- ctx.run(
- blacken_docs.run(*PY_SRC_LIST, "docs", exts=["py", "md"], line_length=120, skip_errors=True),
- title="Formatting docs",
- nofail=True,
- )
@duty(post=["docs-deploy"])
@@ -311,3 +278,28 @@ def test(ctx: Context, match: str = "") -> None:
title=pyprefix("Running tests"),
command=f"pytest -c config/pytest.ini -n auto -k{match!r} --color=yes tests",
)
+
+
+@duty
+def vscode(ctx: Context) -> None:
+ """Configure VSCode.
+
+ This task will overwrite the following files,
+ so make sure to back them up:
+
+ - `.vscode/launch.json`
+ - `.vscode/settings.json`
+ - `.vscode/tasks.json`
+
+ Parameters:
+ ctx: The context instance (passed automatically).
+ """
+
+ def update_config(filename: str) -> None:
+ source_file = Path("config", "vscode", filename)
+ target_file = Path(".vscode", filename)
+ target_file.parent.mkdir(exist_ok=True)
+ target_file.write_text(source_file.read_text())
+
+ for filename in ("launch.json", "settings.json", "tasks.json"):
+ ctx.run(update_config, args=[filename], title=f"Update .vscode/{filename}")
diff --git a/mkdocs.insiders.yml b/mkdocs.insiders.yml
deleted file mode 100644
index 9afba9aa..00000000
--- a/mkdocs.insiders.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-INHERIT: mkdocs.yml
-
-plugins:
-- typeset
diff --git a/mkdocs.yml b/mkdocs.yml
index 17b92494..6c069795 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -109,11 +109,12 @@ markdown_extensions:
kwds:
case: lower
- pymdownx.emoji:
- emoji_index: !!python/name:materialx.emoji.twemoji
- emoji_generator: !!python/name:materialx.emoji.to_svg
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.magiclink
- pymdownx.snippets:
auto_append: [docs/.glossary.md]
+ base_path: [!relative $config_dir]
check_paths: true
- pymdownx.superfences
- pymdownx.tabbed:
@@ -134,7 +135,7 @@ plugins:
scripts:
- scripts/gen_ref_nav.py
- literate-nav:
- nav_file: SUMMARY.txt
+ nav_file: SUMMARY.md
- coverage
- mkdocstrings:
handlers:
@@ -167,6 +168,10 @@ plugins:
repository: mkdocstrings/python
- minify:
minify_html: !ENV [DEPLOY, false]
+- group:
+ enabled: !ENV [MATERIAL_INSIDERS, false]
+ plugins:
+ - typeset
extra:
social:
diff --git a/pyproject.toml b/pyproject.toml
index d7f58317..29927bde 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -59,38 +59,37 @@ duty = ["duty>=0.10"]
ci-quality = ["mkdocstrings-python[duty,docs,quality,typing,security]"]
ci-tests = ["mkdocstrings-python[duty,docs,tests]"]
docs = [
- "black>=23.1",
- "markdown-callouts>=0.2",
- "markdown-exec>=0.5",
+ "black>=23.9",
+ "markdown-callouts>=0.3",
+ "markdown-exec>=1.7",
"mkdocs>=1.5",
- "mkdocs-coverage>=0.2",
- "mkdocs-gen-files>=0.3",
- "mkdocs-git-committers-plugin-2>=1.1",
- "mkdocs-literate-nav>=0.4",
- "mkdocs-material>=7.3",
- "mkdocs-minify-plugin>=0.6.4",
- "toml>=0.10",
+ "mkdocs-coverage>=1.0",
+ "mkdocs-gen-files>=0.5",
+ "mkdocs-git-committers-plugin-2>=1.2",
+ "mkdocs-literate-nav>=0.6",
+ "mkdocs-material>=9.4",
+ "mkdocs-minify-plugin>=0.7",
+ "tomli>=2.0; python_version < '3.11'",
]
maintain = [
- "black>=23.1",
- "blacken-docs>=1.13",
- "git-changelog>=1.0",
+ "black>=23.9",
+ "blacken-docs>=1.16",
+ "git-changelog>=2.3",
]
quality = [
- "ruff>=0.0.246",
+ "ruff>=0.0",
]
tests = [
- "pytest>=6.2",
- "pytest-cov>=3.0",
- "pytest-randomly>=3.10",
- "pytest-xdist>=2.4",
+ "pytest>=7.4",
+ "pytest-cov>=4.1",
+ "pytest-randomly>=3.15",
+ "pytest-xdist>=3.3",
]
typing = [
- "mypy>=0.911",
- "types-markdown>=3.3",
+ "mypy>=1.5",
+ "types-markdown>=3.5",
"types-pyyaml>=6.0",
- "types-toml>=0.10",
]
security = [
- "safety>=2",
+ "safety>=2.3",
]
diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py
index 459f2939..bf35f0da 100644
--- a/scripts/gen_credits.py
+++ b/scripts/gen_credits.py
@@ -2,22 +2,31 @@
from __future__ import annotations
+import os
import re
+import sys
from importlib.metadata import PackageNotFoundError, metadata
from itertools import chain
from pathlib import Path
from textwrap import dedent
from typing import Mapping, cast
-import toml
from jinja2 import StrictUndefined
from jinja2.sandbox import SandboxedEnvironment
-project_dir = Path(".")
-pyproject = toml.load(project_dir / "pyproject.toml")
+# TODO: Remove once support for Python 3.10 is dropped.
+if sys.version_info >= (3, 11):
+ import tomllib
+else:
+ import tomli as tomllib
+
+project_dir = Path(os.getenv("MKDOCS_CONFIG_DIR", "."))
+with project_dir.joinpath("pyproject.toml").open("rb") as pyproject_file:
+ pyproject = tomllib.load(pyproject_file)
project = pyproject["project"]
pdm = pyproject["tool"]["pdm"]
-lock_data = toml.load(project_dir / "pdm.lock")
+with project_dir.joinpath("pdm.lock").open("rb") as lock_file:
+ lock_data = tomllib.load(lock_file)
lock_pkgs = {pkg["name"].lower(): pkg for pkg in lock_data["package"]}
project_name = project["name"]
regex = re.compile(r"(?P[\w.-]+)(?P.*)$")
@@ -30,7 +39,7 @@ def _get_license(pkg_name: str) -> str:
return "?"
license_name = cast(dict, data).get("License", "").strip()
multiple_lines = bool(license_name.count("\n"))
- # TODO: remove author logic once all my packages licenses are fixed
+ # TODO: Remove author logic once all my packages licenses are fixed.
author = ""
if multiple_lines or not license_name or license_name == "UNKNOWN":
for header, value in cast(dict, data).items():
diff --git a/scripts/gen_ref_nav.py b/scripts/gen_ref_nav.py
index 713522be..7285ac1c 100644
--- a/scripts/gen_ref_nav.py
+++ b/scripts/gen_ref_nav.py
@@ -7,9 +7,11 @@
nav = mkdocs_gen_files.Nav()
mod_symbol = '
'
-for path in sorted(Path("src").rglob("*.py")):
- module_path = path.relative_to("src").with_suffix("")
- doc_path = path.relative_to("src").with_suffix(".md")
+src = Path(__file__).parent.parent / "src"
+
+for path in sorted(src.rglob("*.py")):
+ module_path = path.relative_to(src).with_suffix("")
+ doc_path = path.relative_to(src).with_suffix(".md")
full_doc_path = Path("reference", doc_path)
parts = tuple(module_path.parts)
@@ -30,5 +32,5 @@
mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path)
-with mkdocs_gen_files.open("reference/SUMMARY.txt", "w") as nav_file:
+with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())
diff --git a/scripts/insiders.py b/scripts/insiders.py
index 28ca1c87..8f5e215e 100644
--- a/scripts/insiders.py
+++ b/scripts/insiders.py
@@ -4,6 +4,7 @@
import json
import logging
+import os
import posixpath
from dataclasses import dataclass
from datetime import date, datetime, timedelta
@@ -115,8 +116,9 @@ def load_goals(data: str, funding: int = 0, project: Project | None = None) -> d
def _load_goals_from_disk(path: str, funding: int = 0) -> dict[int, Goal]:
+ project_dir = os.getenv("MKDOCS_CONFIG_DIR", ".")
try:
- data = Path(path).read_text()
+ data = Path(project_dir, path).read_text()
except OSError as error:
raise RuntimeError(f"Could not load data from disk: {path}") from error
return load_goals(data, funding)
@@ -159,7 +161,7 @@ def funding_goals(source: str | list[str | tuple[str, str, str]], funding: int =
goals[amount] = goal
else:
goals[amount].features.extend(goal.features)
- return goals
+ return {amount: goals[amount] for amount in sorted(goals)}
def feature_list(goals: Iterable[Goal]) -> list[Feature]:
diff --git a/src/mkdocstrings_handlers/debug.py b/src/mkdocstrings_handlers/debug.py
new file mode 100644
index 00000000..ffebc12e
--- /dev/null
+++ b/src/mkdocstrings_handlers/debug.py
@@ -0,0 +1,106 @@
+"""Debugging utilities."""
+
+from __future__ import annotations
+
+import os
+import platform
+import sys
+from dataclasses import dataclass
+from importlib import metadata
+
+
+@dataclass
+class Variable:
+ """Dataclass describing an environment variable."""
+
+ name: str
+ """Variable name."""
+ value: str
+ """Variable value."""
+
+
+@dataclass
+class Package:
+ """Dataclass describing a Python package."""
+
+ name: str
+ """Package name."""
+ version: str
+ """Package version."""
+
+
+@dataclass
+class Environment:
+ """Dataclass to store environment information."""
+
+ interpreter_name: str
+ """Python interpreter name."""
+ interpreter_version: str
+ """Python interpreter version."""
+ platform: str
+ """Operating System."""
+ packages: list[Package]
+ """Installed packages."""
+ variables: list[Variable]
+ """Environment variables."""
+
+
+def _interpreter_name_version() -> tuple[str, str]:
+ if hasattr(sys, "implementation"):
+ impl = sys.implementation.version
+ version = f"{impl.major}.{impl.minor}.{impl.micro}"
+ kind = impl.releaselevel
+ if kind != "final":
+ version += kind[0] + str(impl.serial)
+ return sys.implementation.name, version
+ return "", "0.0.0"
+
+
+def get_version(dist: str = "mkdocstrings-python") -> str:
+ """Get version of the given distribution.
+
+ Parameters:
+ dist: A distribution name.
+
+ Returns:
+ A version number.
+ """
+ try:
+ return metadata.version(dist)
+ except metadata.PackageNotFoundError:
+ return "0.0.0"
+
+
+def get_debug_info() -> Environment:
+ """Get debug/environment information.
+
+ Returns:
+ Environment information.
+ """
+ py_name, py_version = _interpreter_name_version()
+ packages = ["mkdocstrings-python"]
+ variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS_PYTHON")]]
+ return Environment(
+ interpreter_name=py_name,
+ interpreter_version=py_version,
+ platform=platform.platform(),
+ variables=[Variable(var, val) for var in variables if (val := os.getenv(var))],
+ packages=[Package(pkg, get_version(pkg)) for pkg in packages],
+ )
+
+
+def print_debug_info() -> None:
+ """Print debug/environment information."""
+ info = get_debug_info()
+ print(f"- __System__: {info.platform}")
+ print(f"- __Python__: {info.interpreter_name} {info.interpreter_version}")
+ print("- __Environment variables__:")
+ for var in info.variables:
+ print(f" - `{var.name}`: `{var.value}`")
+ print("- __Installed packages__:")
+ for pkg in info.packages:
+ print(f" - `{pkg.name}` v{pkg.version}")
+
+
+if __name__ == "__main__":
+ print_debug_info()
From b5bb8a982e7a2ec97c73335e453d0033bf4987b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?=
Date: Sun, 12 Nov 2023 18:28:45 +0100
Subject: [PATCH 4/6] refactor: Prepare for Griffe 0.37
---
pyproject.toml | 2 +-
src/mkdocstrings_handlers/python/handler.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 29927bde..853cdf9e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,7 +30,7 @@ classifiers = [
]
dependencies = [
"mkdocstrings>=0.20",
- "griffe>=0.35",
+ "griffe>=0.37",
]
[project.urls]
diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py
index 169546fd..6fc2804f 100644
--- a/src/mkdocstrings_handlers/python/handler.py
+++ b/src/mkdocstrings_handlers/python/handler.py
@@ -278,8 +278,8 @@ def collect(self, identifier: str, config: Mapping[str, Any]) -> CollectorItem:
try:
for pre_loaded_module in final_config.get("preload_modules") or []:
if pre_loaded_module not in self._modules_collection:
- loader.load_module(pre_loaded_module)
- loader.load_module(module_name)
+ loader.load(pre_loaded_module)
+ loader.load(module_name)
except ImportError as error:
raise CollectionError(str(error)) from error
unresolved, iterations = loader.resolve_aliases(
From 3a100406445c2e431cee9683f845fd9d8d2e6736 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?=
Date: Sun, 12 Nov 2023 18:36:13 +0100
Subject: [PATCH 5/6] chore: Fix debug module location, add packages to debug
info
---
.github/ISSUE_TEMPLATE/bug_report.md | 2 +-
src/mkdocstrings_handlers/{ => python}/debug.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
rename src/mkdocstrings_handlers/{ => python}/debug.py (97%)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index ac47315f..ca545c26 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -50,7 +50,7 @@ PASTE TRACEBACK HERE
redacting sensitive information. -->
```bash
-python -m mkdocstrings_handlers.debug # | xclip -selection clipboard
+python -m mkdocstrings_handlers.python.debug # | xclip -selection clipboard
```
PASTE OUTPUT HERE
diff --git a/src/mkdocstrings_handlers/debug.py b/src/mkdocstrings_handlers/python/debug.py
similarity index 97%
rename from src/mkdocstrings_handlers/debug.py
rename to src/mkdocstrings_handlers/python/debug.py
index ffebc12e..7a4e8791 100644
--- a/src/mkdocstrings_handlers/debug.py
+++ b/src/mkdocstrings_handlers/python/debug.py
@@ -78,7 +78,7 @@ def get_debug_info() -> Environment:
Environment information.
"""
py_name, py_version = _interpreter_name_version()
- packages = ["mkdocstrings-python"]
+ packages = ["mkdocs", "mkdocstrings", "mkdocstrings-python", "griffe"]
variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS_PYTHON")]]
return Environment(
interpreter_name=py_name,
From dde658e77ef79e92ed380431f35c478ec0b27d24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?=
Date: Sun, 12 Nov 2023 18:52:36 +0100
Subject: [PATCH 6/6] chore: Prepare release 1.7.4
---
CHANGELOG.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab570436..db7fefc1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,18 @@ 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.7.4](https://github.com/mkdocstrings/python/releases/tag/1.7.4) - 2023-11-12
+
+[Compare with 1.7.3](https://github.com/mkdocstrings/python/compare/1.7.3...1.7.4)
+
+### Bug Fixes
+
+- Make extension paths relative to config file ([5035e92](https://github.com/mkdocstrings/python/commit/5035e9269fe11664fd25e438ac8f746721b3de0a) by Waylan Limberg). [PR #112](https://github.com/mkdocstrings/python/pull/112), Co-authored-by: Timothée Mazzucotelli
+
+### Code Refactoring
+
+- Prepare for Griffe 0.37 ([b5bb8a9](https://github.com/mkdocstrings/python/commit/b5bb8a982e7a2ec97c73335e453d0033bf4987b6) by Timothée Mazzucotelli).
+
## [1.7.3](https://github.com/mkdocstrings/python/releases/tag/1.7.3) - 2023-10-09
[Compare with 1.7.2](https://github.com/mkdocstrings/python/compare/1.7.2...1.7.3)