pytest-markdown-docs 0.7.1__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/.github/workflows/check.yml +3 -3
  2. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/.github/workflows/ci.yml +4 -4
  3. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/.github/workflows/codeql.yml +4 -4
  4. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/.pre-commit-config.yaml +1 -1
  5. pytest_markdown_docs-0.7.1/README.md → pytest_markdown_docs-0.9.0/PKG-INFO +28 -0
  6. pytest_markdown_docs-0.7.1/PKG-INFO → pytest_markdown_docs-0.9.0/README.md +15 -12
  7. pytest_markdown_docs-0.9.0/playground/conftest.py +17 -0
  8. pytest_markdown_docs-0.9.0/playground/foo.md +6 -0
  9. pytest_markdown_docs-0.9.0/playground/foo.py +3 -0
  10. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/pyproject.toml +4 -3
  11. pytest_markdown_docs-0.9.0/src/pytest_markdown_docs/_runners.py +133 -0
  12. pytest_markdown_docs-0.9.0/src/pytest_markdown_docs/definitions.py +19 -0
  13. pytest_markdown_docs-0.9.0/src/pytest_markdown_docs/hooks.py +15 -0
  14. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/src/pytest_markdown_docs/plugin.py +68 -101
  15. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/tests/plugin_test.py +82 -3
  16. pytest_markdown_docs-0.9.0/uv.lock +393 -0
  17. pytest_markdown_docs-0.7.1/src/pytest_markdown_docs/hooks.py +0 -5
  18. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/.github/pull_request_template.md +0 -0
  19. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/.gitignore +0 -0
  20. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/LICENSE +0 -0
  21. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/Makefile +0 -0
  22. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/pytest-markdown-docs.iml +0 -0
  23. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/src/pytest_markdown_docs/__init__.py +0 -0
  24. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/src/pytest_markdown_docs/py.typed +0 -0
  25. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/tests/conftest.py +0 -0
  26. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/tests/support/docstring_error_after.py +0 -0
  27. {pytest_markdown_docs-0.7.1 → pytest_markdown_docs-0.9.0}/tests/support/docstring_error_before.py +0 -0
@@ -5,12 +5,12 @@ jobs:
5
5
  build:
6
6
  runs-on: ubuntu-latest
7
7
  steps:
8
- - uses: actions/checkout@v3
8
+ - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
9
9
  - name: Install uv
10
- uses: astral-sh/setup-uv@v3
10
+ uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
11
11
 
12
12
  - name: Install Python (3.11)
13
- uses: actions/setup-python@v5
13
+ uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
14
14
  with:
15
15
  python-version: 3.11
16
16
 
@@ -6,15 +6,15 @@ jobs:
6
6
  runs-on: ubuntu-latest
7
7
  strategy:
8
8
  matrix:
9
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
9
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
10
10
  steps:
11
- - uses: actions/checkout@v4
11
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
12
12
 
13
13
  - name: Install uv
14
- uses: astral-sh/setup-uv@v3
14
+ uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
15
15
 
16
16
  - name: Install Python ${{ matrix.python-version }}
17
- uses: actions/setup-python@v5
17
+ uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
18
18
  with:
19
19
  python-version: ${{ matrix.python-version }}
20
20
 
@@ -38,11 +38,11 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout repository
41
- uses: actions/checkout@v3
41
+ uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
42
42
 
43
43
  # Initializes the CodeQL tools for scanning.
44
44
  - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v2
45
+ uses: github/codeql-action/init@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # v2
46
46
  with:
47
47
  languages: ${{ matrix.language }}
48
48
  # If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
56
56
  # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
57
57
  # If this step fails, then you should remove it and run the build manually (see below)
58
58
  - name: Autobuild
59
- uses: github/codeql-action/autobuild@v2
59
+ uses: github/codeql-action/autobuild@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # v2
60
60
 
61
61
  # ℹ️ Command-line programs to run using the OS shell.
62
62
  # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,6 +69,6 @@ jobs:
69
69
  # ./location_of_script_within_repo/buildscript.sh
70
70
 
71
71
  - name: Perform CodeQL Analysis
72
- uses: github/codeql-action/analyze@v2
72
+ uses: github/codeql-action/analyze@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # v2
73
73
  with:
74
74
  category: "/language:${{matrix.language}}"
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/charliermarsh/ruff-pre-commit
3
- rev: "v0.2.1"
3
+ rev: "v0.9.10"
4
4
  hooks:
5
5
  - id: ruff
6
6
  # Autofix, and respect `exclude` and `extend-exclude` settings.
@@ -1,3 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-markdown-docs
3
+ Version: 0.9.0
4
+ Summary: Run markdown code fences through pytest
5
+ Author: Modal Labs
6
+ Author-email: Elias Freider <elias@modal.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.9
10
+ Requires-Dist: markdown-it-py<4.0,>=2.2.0
11
+ Requires-Dist: pytest>=7.0.0
12
+ Description-Content-Type: text/markdown
13
+
1
14
  # Pytest Markdown Docs
2
15
 
3
16
  A plugin for [pytest](https://docs.pytest.org) that uses markdown code snippets from markdown files and docstrings as tests.
@@ -169,6 +182,21 @@ The following options can be specified using MDX comments:
169
182
 
170
183
  This approach allows you to add metadata to the code block without modifying the code fence itself, making it particularly useful in MDX environments.
171
184
 
185
+ ## Customizing your own custom MarkdownIt parser
186
+
187
+ You can configure your own [Markdown-it-py](https://pypi.org/project/markdown-it-py/) parser used by `pytest-markdown-docs` by defining a `pytest_markdown_docs_markdown_it`. For example, you can support
188
+ `mkdocs`'s admonitions with:
189
+
190
+ ```python
191
+ def pytest_markdown_docs_markdown_it():
192
+ import markdown_it
193
+ from mdit_py_plugins.admon import admon_plugin
194
+
195
+ mi = markdown_it.MarkdownIt(config="commonmark")
196
+ mi.use(admon_plugin)
197
+ return mi
198
+ ```
199
+
172
200
  ## Testing of this plugin
173
201
 
174
202
  You can test this module itself (sadly not using markdown tests at the moment) using pytest:
@@ -1,15 +1,3 @@
1
- Metadata-Version: 2.3
2
- Name: pytest-markdown-docs
3
- Version: 0.7.1
4
- Summary: Run markdown code fences through pytest
5
- Author: Modal Labs
6
- Author-email: Elias Freider <elias@modal.com>
7
- License: MIT
8
- Requires-Python: >=3.8
9
- Requires-Dist: markdown-it-py<4.0,>=2.2.0
10
- Requires-Dist: pytest>=7.0.0
11
- Description-Content-Type: text/markdown
12
-
13
1
  # Pytest Markdown Docs
14
2
 
15
3
  A plugin for [pytest](https://docs.pytest.org) that uses markdown code snippets from markdown files and docstrings as tests.
@@ -181,6 +169,21 @@ The following options can be specified using MDX comments:
181
169
 
182
170
  This approach allows you to add metadata to the code block without modifying the code fence itself, making it particularly useful in MDX environments.
183
171
 
172
+ ## Customizing your own custom MarkdownIt parser
173
+
174
+ You can configure your own [Markdown-it-py](https://pypi.org/project/markdown-it-py/) parser used by `pytest-markdown-docs` by defining a `pytest_markdown_docs_markdown_it`. For example, you can support
175
+ `mkdocs`'s admonitions with:
176
+
177
+ ```python
178
+ def pytest_markdown_docs_markdown_it():
179
+ import markdown_it
180
+ from mdit_py_plugins.admon import admon_plugin
181
+
182
+ mi = markdown_it.MarkdownIt(config="commonmark")
183
+ mi.use(admon_plugin)
184
+ return mi
185
+ ```
186
+
184
187
  ## Testing of this plugin
185
188
 
186
189
  You can test this module itself (sadly not using markdown tests at the moment) using pytest:
@@ -0,0 +1,17 @@
1
+ import time
2
+
3
+ import pytest
4
+ import pytest_asyncio
5
+ import asyncio
6
+
7
+
8
+ @pytest_asyncio.fixture()
9
+ async def async_fix():
10
+ await asyncio.sleep(1)
11
+ yield "ahello"
12
+
13
+
14
+ @pytest.fixture()
15
+ def non_async_fix():
16
+ time.sleep(1)
17
+ yield "hello"
@@ -0,0 +1,6 @@
1
+ from playground.conftest import non_async_fix
2
+
3
+ ```py fixture:async_fix fixture:non_async_fix
4
+ print(async_fix)
5
+ print(non_async_fix)
6
+ ```
@@ -0,0 +1,3 @@
1
+ def test_foo(async_fix, non_async_fix):
2
+ print(async_fix)
3
+ print(non_async_fix)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pytest-markdown-docs"
3
- version = "0.7.1"
3
+ version = "0.9.0"
4
4
  description = "Run markdown code fences through pytest"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -8,7 +8,7 @@ authors = [
8
8
  { name = "Elias Freider", email = "elias@modal.com" }
9
9
  ]
10
10
  license = "MIT"
11
- requires-python = ">=3.8"
11
+ requires-python = ">=3.9"
12
12
  dependencies = [
13
13
  "markdown-it-py>=2.2.0,<4.0",
14
14
  "pytest>=7.0.0",
@@ -28,5 +28,6 @@ dev-dependencies = [
28
28
  "mypy>=1.12.1",
29
29
  "pre-commit>=3.5.0",
30
30
  "pytest~=8.1.0",
31
- "ruff>=0.7.0",
31
+ "ruff~=0.9.10",
32
+ "mdit-py-plugins~=0.4.2"
32
33
  ]
@@ -0,0 +1,133 @@
1
+ import abc
2
+ import ast
3
+ import traceback
4
+ import typing
5
+ from abc import abstractmethod
6
+
7
+ import pytest
8
+
9
+ from pytest_markdown_docs.definitions import FenceTestDefinition
10
+
11
+ _default_runner: typing.Optional["_Runner"] = None
12
+ _registered_runners = {}
13
+
14
+
15
+ class _Runner(metaclass=abc.ABCMeta):
16
+ @abstractmethod
17
+ def runtest(self, test: FenceTestDefinition, args: dict[str, typing.Any]): ...
18
+
19
+ @abstractmethod
20
+ def repr_failure(
21
+ self,
22
+ test: FenceTestDefinition,
23
+ excinfo: pytest.ExceptionInfo[BaseException],
24
+ style=None,
25
+ ): ...
26
+
27
+
28
+ RUNNER_TYPE = typing.TypeVar("RUNNER_TYPE", bound=type[_Runner])
29
+
30
+
31
+ def register_runner(*, default: bool = False):
32
+ """Decorator for adding custom runners
33
+
34
+ e.g.
35
+ @register_runner()
36
+ def my_runner(src):
37
+ exec(src)
38
+ """
39
+
40
+ def decorator(r: RUNNER_TYPE) -> RUNNER_TYPE:
41
+ global _default_runner
42
+ runner = r()
43
+ _registered_runners[r.__name__] = runner
44
+ if default:
45
+ _default_runner = runner
46
+ return r
47
+
48
+ return decorator
49
+
50
+
51
+ @register_runner(default=True)
52
+ class DefaultRunner(_Runner):
53
+ def runtest(self, test: FenceTestDefinition, args):
54
+ try:
55
+ tree = ast.parse(test.source, filename=test.source_path)
56
+ except SyntaxError:
57
+ raise
58
+
59
+ try:
60
+ # if we don't compile the code, it seems we get name lookup errors
61
+ # for functions etc. when doing cross-calls across inline functions
62
+ compiled = compile(
63
+ tree, filename=test.source_path, mode="exec", dont_inherit=True
64
+ )
65
+ except SyntaxError:
66
+ raise
67
+
68
+ exec(compiled, args)
69
+
70
+ def repr_failure(
71
+ self,
72
+ test: FenceTestDefinition,
73
+ excinfo: pytest.ExceptionInfo[BaseException],
74
+ style=None,
75
+ ):
76
+ """This renders a traceback starting at the stack from of the code fence
77
+
78
+ Also displays a line-numbered excerpt of the code fence that ran.
79
+ """
80
+
81
+ rawlines = test.source.rstrip("\n").split("\n")
82
+
83
+ # custom formatted traceback to translate line numbers and markdown files
84
+ traceback_lines = []
85
+ stack_summary = traceback.StackSummary.extract(traceback.walk_tb(excinfo.tb))
86
+ start_capture = False
87
+
88
+ start_line = test.start_line
89
+
90
+ for frame_summary in stack_summary:
91
+ if frame_summary.filename == str(test.source_path):
92
+ # start capturing frames the first time we enter user code
93
+ start_capture = True
94
+
95
+ if start_capture:
96
+ lineno = frame_summary.lineno
97
+ line = frame_summary.line or ""
98
+ linespec = f"line {lineno}"
99
+ traceback_lines.append(
100
+ f""" File "{frame_summary.filename}", {linespec}, in {frame_summary.name}"""
101
+ )
102
+ traceback_lines.append(f" {line.lstrip()}")
103
+
104
+ maxdigits = len(str(len(rawlines)))
105
+ code_margin = " "
106
+ numbered_code = "\n".join(
107
+ [
108
+ f"{i:>{maxdigits}}{code_margin}{line}"
109
+ for i, line in enumerate(rawlines[start_line:], start_line + 1)
110
+ ]
111
+ )
112
+
113
+ pretty_traceback = "\n".join(traceback_lines)
114
+ pt = f"""Traceback (most recent call last):
115
+ {pretty_traceback}
116
+ {excinfo.exconly()}"""
117
+
118
+ return f"""Error in code block:
119
+ {maxdigits * " "}{code_margin}```
120
+ {numbered_code}
121
+ {maxdigits * " "}{code_margin}```
122
+ {pt}
123
+ """
124
+
125
+
126
+ def get_runner(name: typing.Optional[str]) -> _Runner:
127
+ if name is None:
128
+ assert _default_runner is not None
129
+ return _default_runner
130
+
131
+ if name not in _registered_runners:
132
+ raise Exception(f"No such pytest-markdown-docs runner: {name}")
133
+ return _registered_runners[name]
@@ -0,0 +1,19 @@
1
+ import pathlib
2
+ import typing
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class FenceTestDefinition:
8
+ source: str
9
+ fixture_names: typing.Sequence[str]
10
+ start_line: int
11
+ source_path: pathlib.Path
12
+ runner_name: typing.Optional[str]
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class ObjectTestDefinition:
17
+ intra_object_index: int
18
+ object_name: str
19
+ fence_test: FenceTestDefinition
@@ -0,0 +1,15 @@
1
+ import pytest
2
+ import typing
3
+
4
+ if typing.TYPE_CHECKING:
5
+ from markdown_it import MarkdownIt
6
+
7
+
8
+ def pytest_markdown_docs_globals() -> typing.Dict[str, typing.Any]:
9
+ return {}
10
+
11
+
12
+ @pytest.hookspec(firstresult=True)
13
+ def pytest_markdown_docs_markdown_it() -> "MarkdownIt":
14
+ """Configure a custom markdown_it.MarkdownIt parser."""
15
+ return MarkdownIt()
@@ -1,9 +1,6 @@
1
- import ast
2
1
  import inspect
3
- import traceback
4
2
  import types
5
3
  import pathlib
6
- from dataclasses import dataclass
7
4
 
8
5
  import pytest
9
6
  import typing
@@ -15,7 +12,8 @@ from _pytest.pathlib import import_path
15
12
  import logging
16
13
 
17
14
  from pytest_markdown_docs import hooks
18
-
15
+ from pytest_markdown_docs.definitions import FenceTestDefinition, ObjectTestDefinition
16
+ from pytest_markdown_docs._runners import get_runner
19
17
 
20
18
  if pytest.version_tuple >= (8, 0, 0):
21
19
  from _pytest.fixtures import TopRequest
@@ -26,6 +24,7 @@ else:
26
24
 
27
25
  if typing.TYPE_CHECKING:
28
26
  from markdown_it.token import Token
27
+ from markdown_it import MarkdownIt
29
28
 
30
29
  logger = logging.getLogger("pytest-markdown-docs")
31
30
 
@@ -37,20 +36,6 @@ class FenceSyntax(Enum):
37
36
  superfences = "superfences"
38
37
 
39
38
 
40
- @dataclass(frozen=True)
41
- class FenceTest:
42
- source: str
43
- fixture_names: typing.Tuple[str, ...]
44
- start_line: int
45
-
46
-
47
- @dataclass(frozen=True)
48
- class ObjectTest:
49
- intra_object_index: int
50
- object_name: str
51
- fence_test: FenceTest
52
-
53
-
54
39
  def get_docstring_start_line(obj) -> typing.Optional[int]:
55
40
  # Get the source lines and the starting line number of the object
56
41
  try:
@@ -72,18 +57,18 @@ class MarkdownInlinePythonItem(pytest.Item):
72
57
  self,
73
58
  name: str,
74
59
  parent: typing.Union["MarkdownDocstringCodeModule", "MarkdownTextFile"],
75
- code: str,
76
- fixture_names: typing.List[str],
77
- start_line: int,
60
+ test_definition: FenceTestDefinition,
78
61
  ) -> None:
79
62
  super().__init__(name, parent)
80
63
  self.add_marker(MARKER_NAME)
81
- self.code = code
64
+ self.code = test_definition.source
82
65
  self.obj = None
83
- self.user_properties.append(("code", code))
84
- self.start_line = start_line
85
- self.fixturenames = fixture_names
66
+ self.test_definition = test_definition
67
+ self.user_properties.append(("code", test_definition.source))
68
+ self.start_line = test_definition.start_line
69
+ self.fixturenames = test_definition.fixture_names
86
70
  self.nofuncargs = True
71
+ self.runner_name = test_definition.runner_name
87
72
 
88
73
  def setup(self):
89
74
  def func() -> None:
@@ -95,6 +80,7 @@ class MarkdownInlinePythonItem(pytest.Item):
95
80
  )
96
81
  self.fixture_request = TopRequest(self, _ispytest=True)
97
82
  self.fixture_request._fillfixtures()
83
+ self.runner = get_runner(self.runner_name)
98
84
 
99
85
  def runtest(self):
100
86
  global_sets = self.parent.config.hook.pytest_markdown_docs_globals()
@@ -114,83 +100,38 @@ class MarkdownInlinePythonItem(pytest.Item):
114
100
  for argname, value in self.funcargs.items():
115
101
  all_globals[argname] = value
116
102
 
117
- try:
118
- tree = ast.parse(self.code, filename=self.path)
119
- except SyntaxError:
120
- raise
121
-
122
- try:
123
- # if we don't compile the code, it seems we get name lookup errors
124
- # for functions etc. when doing cross-calls across inline functions
125
- compiled = compile(tree, filename=self.path, mode="exec", dont_inherit=True)
126
- except SyntaxError:
127
- raise
128
-
129
- exec(compiled, all_globals)
103
+ # this ensures that pytest's stdout/stderr capture works during the test:
104
+ capman = self.config.pluginmanager.getplugin("capturemanager")
105
+ with capman.global_and_fixture_disabled():
106
+ self.runner.runtest(self.test_definition, all_globals)
130
107
 
131
108
  def repr_failure(
132
109
  self,
133
110
  excinfo: ExceptionInfo[BaseException],
134
111
  style=None,
135
- ):
136
- rawlines = self.code.rstrip("\n").split("\n")
137
-
138
- # custom formatted traceback to translate line numbers and markdown files
139
- traceback_lines = []
140
- stack_summary = traceback.StackSummary.extract(traceback.walk_tb(excinfo.tb))
141
- start_capture = False
142
-
143
- start_line = self.start_line
144
-
145
- for frame_summary in stack_summary:
146
- if frame_summary.filename == str(self.path):
147
- # start capturing frames the first time we enter user code
148
- start_capture = True
149
-
150
- if start_capture:
151
- lineno = frame_summary.lineno
152
- line = frame_summary.line or ""
153
- linespec = f"line {lineno}"
154
- traceback_lines.append(
155
- f""" File "{frame_summary.filename}", {linespec}, in {frame_summary.name}"""
156
- )
157
- traceback_lines.append(f" {line.lstrip()}")
158
-
159
- maxdigits = len(str(len(rawlines)))
160
- code_margin = " "
161
- numbered_code = "\n".join(
162
- [
163
- f"{i:>{maxdigits}}{code_margin}{line}"
164
- for i, line in enumerate(rawlines[start_line:], start_line + 1)
165
- ]
166
- )
112
+ ) -> str:
113
+ return self.runner.repr_failure(self.test_definition, excinfo, style)
167
114
 
168
- pretty_traceback = "\n".join(traceback_lines)
169
- pt = f"""Traceback (most recent call last):
170
- {pretty_traceback}
171
- {excinfo.exconly()}"""
115
+ def reportinfo(self):
116
+ return self.path, self.start_line, self.name
172
117
 
173
- return f"""Error in code block:
174
- {maxdigits * " "}{code_margin}```
175
- {numbered_code}
176
- {maxdigits * " "}{code_margin}```
177
- {pt}
178
- """
179
118
 
180
- def reportinfo(self):
181
- return self.name, 0, self.nodeid
119
+ def get_prefixed_strings(
120
+ seq: typing.Collection[str], prefix: str
121
+ ) -> typing.Sequence[str]:
122
+ # return strings matching a prefix, with the prefix stripped
123
+ return tuple(s[len(prefix) :] for s in seq if s.startswith(prefix))
182
124
 
183
125
 
184
126
  def extract_fence_tests(
127
+ markdown_it_parser: "MarkdownIt",
185
128
  markdown_string: str,
186
129
  start_line_offset: int,
130
+ source_path: pathlib.Path,
187
131
  markdown_type: str = "md",
188
132
  fence_syntax: FenceSyntax = FenceSyntax.default,
189
- ) -> typing.Generator[FenceTest, None, None]:
190
- import markdown_it
191
-
192
- mi = markdown_it.MarkdownIt(config="commonmark")
193
- tokens = mi.parse(markdown_string)
133
+ ) -> typing.Generator[FenceTestDefinition, None, None]:
134
+ tokens = markdown_it_parser.parse(markdown_string)
194
135
 
195
136
  prev = ""
196
137
  for i, block in enumerate(tokens):
@@ -229,10 +170,23 @@ def extract_fence_tests(
229
170
  add_blank_lines = start_line - prev.count("\n")
230
171
  code_block = prev + ("\n" * add_blank_lines) + block.content
231
172
 
232
- fixture_names = tuple(
233
- f[len("fixture:") :] for f in code_options if f.startswith("fixture:")
173
+ fixture_names = get_prefixed_strings(code_options, "fixture:")
174
+ runner_names = get_prefixed_strings(code_options, "runner:")
175
+ if len(runner_names) == 0:
176
+ runner_name = None
177
+ elif len(runner_names) > 1:
178
+ raise Exception(
179
+ f"Multiple runners are not supported, use a single one instead: {runner_names}"
180
+ )
181
+ else:
182
+ runner_name = runner_names[0]
183
+ yield FenceTestDefinition(
184
+ code_block,
185
+ fixture_names,
186
+ start_line,
187
+ source_path=source_path,
188
+ runner_name=runner_name,
234
189
  )
235
- yield FenceTest(code_block, fixture_names, start_line)
236
190
  prev = code_block
237
191
 
238
192
 
@@ -300,10 +254,8 @@ class MarkdownDocstringCodeModule(pytest.Module):
300
254
  fence_test = object_test.fence_test
301
255
  yield MarkdownInlinePythonItem.from_parent(
302
256
  self,
303
- name=f"{object_test.object_name}[CodeFence#{object_test.intra_object_index+1}][line:{fence_test.start_line}]",
304
- code=fence_test.source,
305
- fixture_names=fence_test.fixture_names,
306
- start_line=fence_test.start_line,
257
+ name=f"{object_test.object_name}[CodeFence#{object_test.intra_object_index + 1}][line:{fence_test.start_line}]",
258
+ test_definition=fence_test,
307
259
  )
308
260
 
309
261
  def find_object_tests_recursive(
@@ -312,7 +264,7 @@ class MarkdownDocstringCodeModule(pytest.Module):
312
264
  object: typing.Any,
313
265
  _visited_objects: typing.Set[int],
314
266
  _found_tests: typing.Set[typing.Tuple[str, int]],
315
- ) -> typing.Generator[ObjectTest, None, None]:
267
+ ) -> typing.Generator[ObjectTestDefinition, None, None]:
316
268
  if id(object) in _visited_objects:
317
269
  return
318
270
  _visited_objects.add(id(object))
@@ -341,12 +293,18 @@ class MarkdownDocstringCodeModule(pytest.Module):
341
293
  or "<Unnamed obj>"
342
294
  )
343
295
  fence_syntax = FenceSyntax(self.config.option.markdowndocs_syntax)
296
+ markdown_it_parser = self.config.hook.pytest_markdown_docs_markdown_it()
297
+
344
298
  for i, fence_test in enumerate(
345
299
  extract_fence_tests(
346
- docstr, docstring_offset, fence_syntax=fence_syntax
300
+ markdown_it_parser,
301
+ docstr,
302
+ docstring_offset,
303
+ source_path=self.path,
304
+ fence_syntax=fence_syntax,
347
305
  )
348
306
  ):
349
- found_test = ObjectTest(i, obj_name, fence_test)
307
+ found_test = ObjectTestDefinition(i, obj_name, fence_test)
350
308
  found_test_location = (
351
309
  module_name,
352
310
  found_test.fence_test.start_line,
@@ -361,9 +319,13 @@ class MarkdownTextFile(pytest.File):
361
319
  markdown_content = self.path.read_text("utf8")
362
320
  fence_syntax = FenceSyntax(self.config.option.markdowndocs_syntax)
363
321
 
322
+ markdown_it_parser = self.config.hook.pytest_markdown_docs_markdown_it()
323
+
364
324
  for i, fence_test in enumerate(
365
325
  extract_fence_tests(
326
+ markdown_it_parser,
366
327
  markdown_content,
328
+ source_path=self.path,
367
329
  start_line_offset=0,
368
330
  markdown_type=self.path.suffix.replace(".", ""),
369
331
  fence_syntax=fence_syntax,
@@ -371,10 +333,8 @@ class MarkdownTextFile(pytest.File):
371
333
  ):
372
334
  yield MarkdownInlinePythonItem.from_parent(
373
335
  self,
374
- name=f"[CodeFence#{i+1}][line:{fence_test.start_line}]",
375
- code=fence_test.source,
376
- fixture_names=fence_test.fixture_names,
377
- start_line=fence_test.start_line,
336
+ name=f"[CodeFence#{i + 1}][line:{fence_test.start_line}]",
337
+ test_definition=fence_test,
378
338
  )
379
339
 
380
340
 
@@ -419,3 +379,10 @@ def pytest_addoption(parser: Parser) -> None:
419
379
 
420
380
  def pytest_addhooks(pluginmanager):
421
381
  pluginmanager.add_hookspecs(hooks)
382
+
383
+
384
+ @pytest.hookimpl
385
+ def pytest_markdown_docs_markdown_it() -> "MarkdownIt":
386
+ from markdown_it import MarkdownIt
387
+
388
+ return MarkdownIt(config="commonmark")
@@ -142,9 +142,9 @@ Traceback \(most recent call last\):
142
142
  Exception: doh
143
143
  """.strip()
144
144
  pytest_output = "\n".join(line.rstrip() for line in result.outlines).strip()
145
- assert (
146
- re.search(expected_output_pattern, pytest_output) is not None
147
- ), "Output traceback doesn't match expected value"
145
+ assert re.search(expected_output_pattern, pytest_output) is not None, (
146
+ "Output traceback doesn't match expected value"
147
+ )
148
148
 
149
149
 
150
150
  def test_autouse_fixtures(testdir):
@@ -431,3 +431,82 @@ def test_error_origin_before_docstring_traceback(testdir, support_dir):
431
431
  ],
432
432
  consecutive=True,
433
433
  )
434
+
435
+
436
+ def test_custom_runner(testdir):
437
+ testdir.makeconftest(
438
+ """
439
+ import pytest_markdown_docs._runners
440
+
441
+ @pytest_markdown_docs._runners.register_runner()
442
+ class LinesAreAllFoo(pytest_markdown_docs._runners.DefaultRunner):
443
+ def runtest(self, test, args):
444
+ lines = test.source.strip().split("\\n")
445
+ for line in lines:
446
+ assert line == "foo"
447
+ """
448
+ )
449
+ testdir.makefile(
450
+ ".md",
451
+ """
452
+ ```python runner:LinesAreAllFoo
453
+ foo
454
+ foo
455
+ ```
456
+
457
+ ```python runner:LinesAreAllFoo
458
+ foo
459
+ bar
460
+ ```
461
+ """,
462
+ )
463
+
464
+ result = testdir.runpytest("-v", "--markdown-docs")
465
+ result.assert_outcomes(passed=1, failed=1)
466
+ result.stdout.re_match_lines(
467
+ [
468
+ r".*\[CodeFence#1\]\[line:1\].*PASSED.*",
469
+ r".*\[CodeFence#2\]\[line:6\].*FAILED.*",
470
+ ],
471
+ consecutive=True,
472
+ )
473
+
474
+
475
+ def test_admonition_markdown_text_file(testdir):
476
+ testdir.makeconftest(
477
+ """
478
+ def pytest_markdown_docs_globals():
479
+ return {"a": "hello"}
480
+
481
+ def pytest_markdown_docs_markdown_it():
482
+ import markdown_it
483
+ from mdit_py_plugins.admon import admon_plugin
484
+
485
+ mi = markdown_it.MarkdownIt(config="commonmark")
486
+ mi.use(admon_plugin)
487
+ return mi
488
+ """
489
+ )
490
+
491
+ testdir.makefile(
492
+ ".md",
493
+ """
494
+ ??? quote
495
+
496
+ ```python
497
+ assert a + " world" == "hello world"
498
+ ```
499
+
500
+ !!! info
501
+ ```python
502
+ assert False
503
+ ```
504
+
505
+ ???+ note
506
+ ```python
507
+ **@ # this is a syntax error
508
+ ```
509
+ """,
510
+ )
511
+ result = testdir.runpytest("--markdown-docs")
512
+ result.assert_outcomes(passed=1, failed=2)
@@ -0,0 +1,393 @@
1
+ version = 1
2
+ requires-python = ">=3.9"
3
+
4
+ [[package]]
5
+ name = "cfgv"
6
+ version = "3.4.0"
7
+ source = { registry = "https://pypi.org/simple" }
8
+ sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
9
+ wheels = [
10
+ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
11
+ ]
12
+
13
+ [[package]]
14
+ name = "colorama"
15
+ version = "0.4.6"
16
+ source = { registry = "https://pypi.org/simple" }
17
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
18
+ wheels = [
19
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
20
+ ]
21
+
22
+ [[package]]
23
+ name = "distlib"
24
+ version = "0.3.9"
25
+ source = { registry = "https://pypi.org/simple" }
26
+ sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 }
27
+ wheels = [
28
+ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
29
+ ]
30
+
31
+ [[package]]
32
+ name = "exceptiongroup"
33
+ version = "1.2.2"
34
+ source = { registry = "https://pypi.org/simple" }
35
+ sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
36
+ wheels = [
37
+ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
38
+ ]
39
+
40
+ [[package]]
41
+ name = "filelock"
42
+ version = "3.17.0"
43
+ source = { registry = "https://pypi.org/simple" }
44
+ sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 }
45
+ wheels = [
46
+ { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 },
47
+ ]
48
+
49
+ [[package]]
50
+ name = "identify"
51
+ version = "2.6.9"
52
+ source = { registry = "https://pypi.org/simple" }
53
+ sdist = { url = "https://files.pythonhosted.org/packages/9b/98/a71ab060daec766acc30fb47dfca219d03de34a70d616a79a38c6066c5bf/identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf", size = 99249 }
54
+ wheels = [
55
+ { url = "https://files.pythonhosted.org/packages/07/ce/0845144ed1f0e25db5e7a79c2354c1da4b5ce392b8966449d5db8dca18f1/identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150", size = 99101 },
56
+ ]
57
+
58
+ [[package]]
59
+ name = "iniconfig"
60
+ version = "2.0.0"
61
+ source = { registry = "https://pypi.org/simple" }
62
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
63
+ wheels = [
64
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
65
+ ]
66
+
67
+ [[package]]
68
+ name = "markdown-it-py"
69
+ version = "3.0.0"
70
+ source = { registry = "https://pypi.org/simple" }
71
+ dependencies = [
72
+ { name = "mdurl" },
73
+ ]
74
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
75
+ wheels = [
76
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
77
+ ]
78
+
79
+ [[package]]
80
+ name = "mdit-py-plugins"
81
+ version = "0.4.2"
82
+ source = { registry = "https://pypi.org/simple" }
83
+ dependencies = [
84
+ { name = "markdown-it-py" },
85
+ ]
86
+ sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 }
87
+ wheels = [
88
+ { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 },
89
+ ]
90
+
91
+ [[package]]
92
+ name = "mdurl"
93
+ version = "0.1.2"
94
+ source = { registry = "https://pypi.org/simple" }
95
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
96
+ wheels = [
97
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
98
+ ]
99
+
100
+ [[package]]
101
+ name = "mypy"
102
+ version = "1.15.0"
103
+ source = { registry = "https://pypi.org/simple" }
104
+ dependencies = [
105
+ { name = "mypy-extensions" },
106
+ { name = "tomli", marker = "python_full_version < '3.11'" },
107
+ { name = "typing-extensions" },
108
+ ]
109
+ sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 }
110
+ wheels = [
111
+ { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 },
112
+ { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 },
113
+ { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 },
114
+ { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 },
115
+ { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 },
116
+ { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 },
117
+ { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 },
118
+ { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 },
119
+ { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 },
120
+ { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 },
121
+ { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 },
122
+ { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 },
123
+ { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 },
124
+ { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 },
125
+ { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 },
126
+ { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 },
127
+ { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 },
128
+ { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 },
129
+ { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 },
130
+ { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 },
131
+ { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 },
132
+ { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 },
133
+ { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 },
134
+ { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 },
135
+ { url = "https://files.pythonhosted.org/packages/5a/fa/79cf41a55b682794abe71372151dbbf856e3008f6767057229e6649d294a/mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078", size = 10737129 },
136
+ { url = "https://files.pythonhosted.org/packages/d3/33/dd8feb2597d648de29e3da0a8bf4e1afbda472964d2a4a0052203a6f3594/mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba", size = 9856335 },
137
+ { url = "https://files.pythonhosted.org/packages/e4/b5/74508959c1b06b96674b364ffeb7ae5802646b32929b7701fc6b18447592/mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5", size = 11611935 },
138
+ { url = "https://files.pythonhosted.org/packages/6c/53/da61b9d9973efcd6507183fdad96606996191657fe79701b2c818714d573/mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b", size = 12365827 },
139
+ { url = "https://files.pythonhosted.org/packages/c1/72/965bd9ee89540c79a25778cc080c7e6ef40aa1eeac4d52cec7eae6eb5228/mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2", size = 12541924 },
140
+ { url = "https://files.pythonhosted.org/packages/46/d0/f41645c2eb263e6c77ada7d76f894c580c9ddb20d77f0c24d34273a4dab2/mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980", size = 9271176 },
141
+ { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 },
142
+ ]
143
+
144
+ [[package]]
145
+ name = "mypy-extensions"
146
+ version = "1.0.0"
147
+ source = { registry = "https://pypi.org/simple" }
148
+ sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
149
+ wheels = [
150
+ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
151
+ ]
152
+
153
+ [[package]]
154
+ name = "nodeenv"
155
+ version = "1.9.1"
156
+ source = { registry = "https://pypi.org/simple" }
157
+ sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
158
+ wheels = [
159
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
160
+ ]
161
+
162
+ [[package]]
163
+ name = "packaging"
164
+ version = "24.2"
165
+ source = { registry = "https://pypi.org/simple" }
166
+ sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
167
+ wheels = [
168
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
169
+ ]
170
+
171
+ [[package]]
172
+ name = "platformdirs"
173
+ version = "4.3.6"
174
+ source = { registry = "https://pypi.org/simple" }
175
+ sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
176
+ wheels = [
177
+ { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
178
+ ]
179
+
180
+ [[package]]
181
+ name = "pluggy"
182
+ version = "1.5.0"
183
+ source = { registry = "https://pypi.org/simple" }
184
+ sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
185
+ wheels = [
186
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
187
+ ]
188
+
189
+ [[package]]
190
+ name = "pre-commit"
191
+ version = "4.1.0"
192
+ source = { registry = "https://pypi.org/simple" }
193
+ dependencies = [
194
+ { name = "cfgv" },
195
+ { name = "identify" },
196
+ { name = "nodeenv" },
197
+ { name = "pyyaml" },
198
+ { name = "virtualenv" },
199
+ ]
200
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 }
201
+ wheels = [
202
+ { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 },
203
+ ]
204
+
205
+ [[package]]
206
+ name = "pytest"
207
+ version = "8.1.2"
208
+ source = { registry = "https://pypi.org/simple" }
209
+ dependencies = [
210
+ { name = "colorama", marker = "sys_platform == 'win32'" },
211
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
212
+ { name = "iniconfig" },
213
+ { name = "packaging" },
214
+ { name = "pluggy" },
215
+ { name = "tomli", marker = "python_full_version < '3.11'" },
216
+ ]
217
+ sdist = { url = "https://files.pythonhosted.org/packages/f8/1f/dd3960a2369182720e8cbd580523a4f75292c0c75197dd0254c95f4a0add/pytest-8.1.2.tar.gz", hash = "sha256:f3c45d1d5eed96b01a2aea70dee6a4a366d51d38f9957768083e4fecfc77f3ef", size = 1410060 }
218
+ wheels = [
219
+ { url = "https://files.pythonhosted.org/packages/f0/d7/a812a0bd0f5160823e647524488cc9734f93782f2c273b8f77e8afc60a37/pytest-8.1.2-py3-none-any.whl", hash = "sha256:6c06dc309ff46a05721e6fd48e492a775ed8165d2ecdf57f156a80c7e95bb142", size = 337456 },
220
+ ]
221
+
222
+ [[package]]
223
+ name = "pytest-markdown-docs"
224
+ version = "0.8.0"
225
+ source = { editable = "." }
226
+ dependencies = [
227
+ { name = "markdown-it-py" },
228
+ { name = "pytest" },
229
+ ]
230
+
231
+ [package.dev-dependencies]
232
+ dev = [
233
+ { name = "mdit-py-plugins" },
234
+ { name = "mypy" },
235
+ { name = "pre-commit" },
236
+ { name = "pytest" },
237
+ { name = "ruff" },
238
+ ]
239
+
240
+ [package.metadata]
241
+ requires-dist = [
242
+ { name = "markdown-it-py", specifier = ">=2.2.0,<4.0" },
243
+ { name = "pytest", specifier = ">=7.0.0" },
244
+ ]
245
+
246
+ [package.metadata.requires-dev]
247
+ dev = [
248
+ { name = "mdit-py-plugins", specifier = "~=0.4.2" },
249
+ { name = "mypy", specifier = ">=1.12.1" },
250
+ { name = "pre-commit", specifier = ">=3.5.0" },
251
+ { name = "pytest", specifier = "~=8.1.0" },
252
+ { name = "ruff", specifier = "~=0.9.10" },
253
+ ]
254
+
255
+ [[package]]
256
+ name = "pyyaml"
257
+ version = "6.0.2"
258
+ source = { registry = "https://pypi.org/simple" }
259
+ sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
260
+ wheels = [
261
+ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 },
262
+ { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 },
263
+ { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 },
264
+ { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 },
265
+ { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 },
266
+ { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 },
267
+ { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 },
268
+ { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 },
269
+ { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 },
270
+ { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
271
+ { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
272
+ { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
273
+ { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
274
+ { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
275
+ { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
276
+ { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
277
+ { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
278
+ { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
279
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
280
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
281
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
282
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
283
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
284
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
285
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
286
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
287
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
288
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
289
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
290
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
291
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
292
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
293
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
294
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
295
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
296
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
297
+ { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
298
+ { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
299
+ { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
300
+ { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
301
+ { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
302
+ { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
303
+ { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
304
+ { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
305
+ { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
306
+ ]
307
+
308
+ [[package]]
309
+ name = "ruff"
310
+ version = "0.9.10"
311
+ source = { registry = "https://pypi.org/simple" }
312
+ sdist = { url = "https://files.pythonhosted.org/packages/20/8e/fafaa6f15c332e73425d9c44ada85360501045d5ab0b81400076aff27cf6/ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7", size = 3759776 }
313
+ wheels = [
314
+ { url = "https://files.pythonhosted.org/packages/73/b2/af7c2cc9e438cbc19fafeec4f20bfcd72165460fe75b2b6e9a0958c8c62b/ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d", size = 10049494 },
315
+ { url = "https://files.pythonhosted.org/packages/6d/12/03f6dfa1b95ddd47e6969f0225d60d9d7437c91938a310835feb27927ca0/ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d", size = 10853584 },
316
+ { url = "https://files.pythonhosted.org/packages/02/49/1c79e0906b6ff551fb0894168763f705bf980864739572b2815ecd3c9df0/ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d", size = 10155692 },
317
+ { url = "https://files.pythonhosted.org/packages/5b/01/85e8082e41585e0e1ceb11e41c054e9e36fed45f4b210991052d8a75089f/ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c", size = 10369760 },
318
+ { url = "https://files.pythonhosted.org/packages/a1/90/0bc60bd4e5db051f12445046d0c85cc2c617095c0904f1aa81067dc64aea/ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e", size = 9912196 },
319
+ { url = "https://files.pythonhosted.org/packages/66/ea/0b7e8c42b1ec608033c4d5a02939c82097ddcb0b3e393e4238584b7054ab/ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12", size = 11434985 },
320
+ { url = "https://files.pythonhosted.org/packages/d5/86/3171d1eff893db4f91755175a6e1163c5887be1f1e2f4f6c0c59527c2bfd/ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16", size = 12155842 },
321
+ { url = "https://files.pythonhosted.org/packages/89/9e/700ca289f172a38eb0bca752056d0a42637fa17b81649b9331786cb791d7/ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52", size = 11613804 },
322
+ { url = "https://files.pythonhosted.org/packages/f2/92/648020b3b5db180f41a931a68b1c8575cca3e63cec86fd26807422a0dbad/ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1", size = 13823776 },
323
+ { url = "https://files.pythonhosted.org/packages/5e/a6/cc472161cd04d30a09d5c90698696b70c169eeba2c41030344194242db45/ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c", size = 11302673 },
324
+ { url = "https://files.pythonhosted.org/packages/6c/db/d31c361c4025b1b9102b4d032c70a69adb9ee6fde093f6c3bf29f831c85c/ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43", size = 10235358 },
325
+ { url = "https://files.pythonhosted.org/packages/d1/86/d6374e24a14d4d93ebe120f45edd82ad7dcf3ef999ffc92b197d81cdc2a5/ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c", size = 9886177 },
326
+ { url = "https://files.pythonhosted.org/packages/00/62/a61691f6eaaac1e945a1f3f59f1eea9a218513139d5b6c2b8f88b43b5b8f/ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5", size = 10864747 },
327
+ { url = "https://files.pythonhosted.org/packages/ee/94/2c7065e1d92a8a8a46d46d9c3cf07b0aa7e0a1e0153d74baa5e6620b4102/ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8", size = 11360441 },
328
+ { url = "https://files.pythonhosted.org/packages/a7/8f/1f545ea6f9fcd7bf4368551fb91d2064d8f0577b3079bb3f0ae5779fb773/ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029", size = 10247401 },
329
+ { url = "https://files.pythonhosted.org/packages/4f/18/fb703603ab108e5c165f52f5b86ee2aa9be43bb781703ec87c66a5f5d604/ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1", size = 11366360 },
330
+ { url = "https://files.pythonhosted.org/packages/35/85/338e603dc68e7d9994d5d84f24adbf69bae760ba5efd3e20f5ff2cec18da/ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69", size = 10436892 },
331
+ ]
332
+
333
+ [[package]]
334
+ name = "tomli"
335
+ version = "2.2.1"
336
+ source = { registry = "https://pypi.org/simple" }
337
+ sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
338
+ wheels = [
339
+ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 },
340
+ { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 },
341
+ { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 },
342
+ { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 },
343
+ { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 },
344
+ { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 },
345
+ { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 },
346
+ { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 },
347
+ { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
348
+ { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
349
+ { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 },
350
+ { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 },
351
+ { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 },
352
+ { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 },
353
+ { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 },
354
+ { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 },
355
+ { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 },
356
+ { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 },
357
+ { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
358
+ { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
359
+ { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 },
360
+ { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 },
361
+ { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 },
362
+ { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 },
363
+ { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 },
364
+ { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 },
365
+ { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 },
366
+ { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 },
367
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
368
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
369
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
370
+ ]
371
+
372
+ [[package]]
373
+ name = "typing-extensions"
374
+ version = "4.12.2"
375
+ source = { registry = "https://pypi.org/simple" }
376
+ sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
377
+ wheels = [
378
+ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
379
+ ]
380
+
381
+ [[package]]
382
+ name = "virtualenv"
383
+ version = "20.29.3"
384
+ source = { registry = "https://pypi.org/simple" }
385
+ dependencies = [
386
+ { name = "distlib" },
387
+ { name = "filelock" },
388
+ { name = "platformdirs" },
389
+ ]
390
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/9c/57d19fa093bcf5ac61a48087dd44d00655f85421d1aa9722f8befbf3f40a/virtualenv-20.29.3.tar.gz", hash = "sha256:95e39403fcf3940ac45bc717597dba16110b74506131845d9b687d5e73d947ac", size = 4320280 }
391
+ wheels = [
392
+ { url = "https://files.pythonhosted.org/packages/c2/eb/c6db6e3001d58c6a9e67c74bb7b4206767caa3ccc28c6b9eaf4c23fb4e34/virtualenv-20.29.3-py3-none-any.whl", hash = "sha256:3e3d00f5807e83b234dfb6122bf37cfadf4be216c53a49ac059d02414f819170", size = 4301458 },
393
+ ]
@@ -1,5 +0,0 @@
1
- import typing
2
-
3
-
4
- def pytest_markdown_docs_globals() -> typing.Dict[str, typing.Any]:
5
- return {}