loopty 0.0.1__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.
@@ -0,0 +1,30 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v7
20
+ with:
21
+ enable-cache: true
22
+
23
+ - name: Sync dependencies
24
+ run: uv sync --group dev --python ${{ matrix.python-version }}
25
+
26
+ - name: Ruff
27
+ run: uv run ruff check .
28
+
29
+ - name: Pytest
30
+ run: uv run pytest -q
@@ -0,0 +1,26 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ # PyPI Trusted Publishing: no API token is stored in this repository.
12
+ # A pending publisher must be configured on PyPI before this workflow can
13
+ # publish the first release of a brand-new project name.
14
+ permissions:
15
+ id-token: write
16
+ steps:
17
+ - uses: actions/checkout@v5
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v7
21
+
22
+ - name: Build sdist and wheel
23
+ run: uv build
24
+
25
+ - name: Publish to PyPI
26
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,68 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ .Python
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ wheels/
20
+ share/python-wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+ MANIFEST
25
+
26
+ # Unit test / coverage reports
27
+ htmlcov/
28
+ .tox/
29
+ .nox/
30
+ .coverage
31
+ .coverage.*
32
+ .cache
33
+ nosetests.xml
34
+ coverage.xml
35
+ *.cover
36
+ *.py,cover
37
+ .hypothesis/
38
+ .pytest_cache/
39
+ cover/
40
+
41
+ # Environments
42
+ .env
43
+ .venv
44
+ env/
45
+ venv/
46
+ ENV/
47
+ env.bak/
48
+ venv.bak/
49
+
50
+ # Type checkers, linters, tooling caches
51
+ .mypy_cache/
52
+ .dmypy.json
53
+ dmypy.json
54
+ .pyre/
55
+ .pytype/
56
+ .ruff_cache/
57
+
58
+ # uv
59
+ # (uv.lock is intentionally not ignored; it is committed for reproducible CI.)
60
+
61
+ # Editors / OS
62
+ .idea/
63
+ .vscode/
64
+ *.swp
65
+ .DS_Store
66
+
67
+ # Jupyter
68
+ .ipynb_checkpoints
loopty-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Xiaoyu Wei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
loopty-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.5
2
+ Name: loopty
3
+ Version: 0.0.1
4
+ Summary: loopy, with types. A typed polyhedral layer over loopy.
5
+ Project-URL: Homepage, https://github.com/xywei/loopty
6
+ Project-URL: Repository, https://github.com/xywei/loopty
7
+ Author: Xiaoyu Wei
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: code-generation,isl,lanky,loopy,polyhedral,types
11
+ Classifier: Development Status :: 1 - Planning
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Classifier: Topic :: Software Development :: Compilers
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Provides-Extra: opencl
24
+ Description-Content-Type: text/markdown
25
+
26
+ # loopty
27
+
28
+ loopy, with types.
29
+
30
+ loopty is loop + ty, for types: a typed polyhedral layer over
31
+ [loopy](https://github.com/inducer/loopy). Kernels are decorated Python
32
+ functions whose bodies run natively under plain `python` as the reference
33
+ implementation and are traced under `loopty` to build a typed term. Types are
34
+ isl objects: a statement's type is its iteration domain (an isl set) and its
35
+ read, write, and accumulation footprints (isl maps); dependences are derived by
36
+ isl flow analysis, not declared; index types include ragged, dependent shapes
37
+ (CSR-style data as dependent sums) so disjointness and in-bounds facts come from
38
+ the shape rather than from offset arithmetic; loop transformations are checked
39
+ as casts along bijections, with a concrete witness on failure; loopy generates
40
+ the code (OpenCL, CUDA, C). loopty is the first plugin for its sister project
41
+ [lanky](https://github.com/xywei/lanky) (a Python-hosted proof language over
42
+ Lean 4): loopty's typing rules emit facts into lanky's ledger, its isl oracle
43
+ decides the Presburger ones, and residual obligations become lanky theorems.
44
+
45
+ ## Status
46
+
47
+ Work in progress. This is a placeholder release to reserve the name; nothing
48
+ works yet. loopty is designed as a plugin for
49
+ [lanky](https://github.com/xywei/lanky), the host it plugs into, and will land
50
+ as the interfaces on both sides settle.
51
+
52
+ ## Name
53
+
54
+ loopty is loop + ty, for types: loops, typed. It follows `loopy`, `sumpy`, and
55
+ `pytato` in the naming tradition of the loopy ecosystem.
56
+
57
+ ## Planned architecture
58
+
59
+ - Kernel bodies are traced: the decorated function runs natively under plain
60
+ `python`, and the same body is traced under `loopty` to build a typed term.
61
+ - Index types are isl sets, with dependent sums for ragged data, so CSR-style
62
+ shapes carry their own disjointness and in-bounds facts.
63
+ - Each statement carries read, write, and accumulation footprints; dependences
64
+ are derived by isl flow analysis rather than declared.
65
+ - Loop transformations are checked as casts along bijections, with a concrete
66
+ witness produced when a cast fails.
67
+ - Schedules and tags are maps into a target's execution and memory types.
68
+ - loopy is the code generator (OpenCL, CUDA, C).
69
+ - The plain `python` run is the reference implementation and the
70
+ differential-test oracle.
71
+ - With lanky, loopty registers a theory, an isl oracle, an executor, and a `run`
72
+ verb.
73
+
74
+ ## Install
75
+
76
+ ```sh
77
+ uv add loopty
78
+ ```
79
+
80
+ ```sh
81
+ pip install loopty
82
+ ```
83
+
84
+ Nothing works yet: installing loopty today gets you a placeholder that prints
85
+ its own status.
86
+
87
+ ## AI disclosure
88
+
89
+ This project is developed with substantial assistance from AI coding agents
90
+ (Anthropic's Claude, via Claude Code). Design, direction, and review are by
91
+ Xiaoyu Wei. Generated text and code are reviewed before release, but readers
92
+ should assume AI involvement throughout.
93
+
94
+ ## License
95
+
96
+ MIT. See [LICENSE](LICENSE).
loopty-0.0.1/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # loopty
2
+
3
+ loopy, with types.
4
+
5
+ loopty is loop + ty, for types: a typed polyhedral layer over
6
+ [loopy](https://github.com/inducer/loopy). Kernels are decorated Python
7
+ functions whose bodies run natively under plain `python` as the reference
8
+ implementation and are traced under `loopty` to build a typed term. Types are
9
+ isl objects: a statement's type is its iteration domain (an isl set) and its
10
+ read, write, and accumulation footprints (isl maps); dependences are derived by
11
+ isl flow analysis, not declared; index types include ragged, dependent shapes
12
+ (CSR-style data as dependent sums) so disjointness and in-bounds facts come from
13
+ the shape rather than from offset arithmetic; loop transformations are checked
14
+ as casts along bijections, with a concrete witness on failure; loopy generates
15
+ the code (OpenCL, CUDA, C). loopty is the first plugin for its sister project
16
+ [lanky](https://github.com/xywei/lanky) (a Python-hosted proof language over
17
+ Lean 4): loopty's typing rules emit facts into lanky's ledger, its isl oracle
18
+ decides the Presburger ones, and residual obligations become lanky theorems.
19
+
20
+ ## Status
21
+
22
+ Work in progress. This is a placeholder release to reserve the name; nothing
23
+ works yet. loopty is designed as a plugin for
24
+ [lanky](https://github.com/xywei/lanky), the host it plugs into, and will land
25
+ as the interfaces on both sides settle.
26
+
27
+ ## Name
28
+
29
+ loopty is loop + ty, for types: loops, typed. It follows `loopy`, `sumpy`, and
30
+ `pytato` in the naming tradition of the loopy ecosystem.
31
+
32
+ ## Planned architecture
33
+
34
+ - Kernel bodies are traced: the decorated function runs natively under plain
35
+ `python`, and the same body is traced under `loopty` to build a typed term.
36
+ - Index types are isl sets, with dependent sums for ragged data, so CSR-style
37
+ shapes carry their own disjointness and in-bounds facts.
38
+ - Each statement carries read, write, and accumulation footprints; dependences
39
+ are derived by isl flow analysis rather than declared.
40
+ - Loop transformations are checked as casts along bijections, with a concrete
41
+ witness produced when a cast fails.
42
+ - Schedules and tags are maps into a target's execution and memory types.
43
+ - loopy is the code generator (OpenCL, CUDA, C).
44
+ - The plain `python` run is the reference implementation and the
45
+ differential-test oracle.
46
+ - With lanky, loopty registers a theory, an isl oracle, an executor, and a `run`
47
+ verb.
48
+
49
+ ## Install
50
+
51
+ ```sh
52
+ uv add loopty
53
+ ```
54
+
55
+ ```sh
56
+ pip install loopty
57
+ ```
58
+
59
+ Nothing works yet: installing loopty today gets you a placeholder that prints
60
+ its own status.
61
+
62
+ ## AI disclosure
63
+
64
+ This project is developed with substantial assistance from AI coding agents
65
+ (Anthropic's Claude, via Claude Code). Design, direction, and review are by
66
+ Xiaoyu Wei. Generated text and code are reviewed before release, but readers
67
+ should assume AI involvement throughout.
68
+
69
+ ## License
70
+
71
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "loopty"
7
+ version = "0.0.1"
8
+ description = "loopy, with types. A typed polyhedral layer over loopy."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.12"
13
+ authors = [{ name = "Xiaoyu Wei" }]
14
+ keywords = [
15
+ "loopy",
16
+ "polyhedral",
17
+ "isl",
18
+ "code-generation",
19
+ "types",
20
+ "lanky",
21
+ ]
22
+ classifiers = [
23
+ "Development Status :: 1 - Planning",
24
+ "Intended Audience :: Developers",
25
+ "Intended Audience :: Science/Research",
26
+ "Programming Language :: Python",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Scientific/Engineering",
31
+ "Topic :: Software Development :: Code Generators",
32
+ "Topic :: Software Development :: Compilers",
33
+ "Typing :: Typed",
34
+ ]
35
+ dependencies = []
36
+
37
+ [project.optional-dependencies]
38
+ # pyopencl will go here once the loopy-backed executor lands.
39
+ opencl = []
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/xywei/loopty"
43
+ Repository = "https://github.com/xywei/loopty"
44
+
45
+ [project.scripts]
46
+ loopty = "loopty.cli:main"
47
+
48
+ [dependency-groups]
49
+ dev = ["pytest", "ruff"]
50
+
51
+ [tool.ruff]
52
+ line-length = 88
53
+ src = ["src", "tests"]
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I", "UP", "W"]
57
+
58
+ [tool.pytest.ini_options]
59
+ testpaths = ["tests"]
60
+ addopts = "-ra"
@@ -0,0 +1,24 @@
1
+ """loopty: loopy, with types.
2
+
3
+ loopty is loop + ty, for types: a typed polyhedral layer over loopy
4
+ (https://github.com/inducer/loopy). Kernels are decorated Python functions whose
5
+ bodies run natively under plain ``python`` as the reference implementation and
6
+ are traced under loopty to build a typed term. Types are isl objects: a
7
+ statement's type is its iteration domain (an isl set) and its read, write, and
8
+ accumulation footprints (isl maps); dependences are derived by isl flow
9
+ analysis, not declared; index types include ragged, dependent shapes (CSR-style
10
+ data as dependent sums) so disjointness and in-bounds facts come from the shape
11
+ rather than from offset arithmetic; loop transformations are checked as casts
12
+ along bijections, with a concrete witness on failure; loopy generates the code
13
+ (OpenCL, CUDA, C). loopty is the first plugin for its sister project lanky (a
14
+ Python-hosted proof language over Lean 4; https://github.com/xywei/lanky):
15
+ loopty's typing rules emit facts into lanky's ledger, its isl oracle decides the
16
+ Presburger ones, and residual obligations become lanky theorems.
17
+
18
+ Status: work in progress; this is a placeholder release to reserve the name.
19
+ Nothing works yet.
20
+ """
21
+
22
+ __version__ = "0.0.1"
23
+
24
+ __all__ = ["__version__"]
@@ -0,0 +1,24 @@
1
+ """Command line interface for loopty.
2
+
3
+ The placeholder release ships a single entry point that reports what loopty is
4
+ and that it does not do anything yet.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from loopty import __version__
10
+
11
+ REPO_URL = "https://github.com/xywei/loopty"
12
+
13
+
14
+ def main() -> int:
15
+ """Print the placeholder banner for loopty."""
16
+ print("loopty")
17
+ print(f"version: {__version__}")
18
+ print("work in progress: placeholder release")
19
+ print(REPO_URL)
20
+ return 0
21
+
22
+
23
+ if __name__ == "__main__":
24
+ raise SystemExit(main())
@@ -0,0 +1,74 @@
1
+ """PREPARED, NOT IMPLEMENTED: loopty's plugin surface for lanky.
2
+
3
+ loopty is the first plugin for lanky (https://github.com/xywei/lanky), a
4
+ Python-hosted proof language over Lean 4. When the plugin interfaces land,
5
+ loopty will register the following with the lanky host:
6
+
7
+ * :class:`KernelTheory` -- the ``@kernel`` and ``@program`` decorators, whose
8
+ typing rules emit facts into lanky's ledger.
9
+ * :class:`IslOracle` -- decides the Presburger facts, with witnesses; trusted in
10
+ development mode, re-derivable by Lean's ``omega``.
11
+ * :class:`LoopyExecutor` -- runs kernels through loopy.
12
+ * :class:`RunVerb` -- the ``loopty run`` / ``lanky run`` subcommand.
13
+
14
+ Every name below is a placeholder: it documents the intended role and has no
15
+ behaviour.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ # NOTE: the runtime dependency on `lanky` will be added when the plugin
21
+ # interfaces land. This placeholder release has no dependencies on purpose, so
22
+ # that reserving the name on PyPI does not pull anything into a user's
23
+ # environment.
24
+
25
+
26
+ class KernelTheory:
27
+ """The theory loopty contributes to lanky's ledger.
28
+
29
+ Will provide the ``@kernel`` and ``@program`` decorators. A decorated
30
+ function's body runs natively under plain ``python`` as the reference
31
+ implementation and is traced under loopty to build a typed term; the typing
32
+ rules for that term emit facts (domains, footprints, dependences,
33
+ transformation casts) into lanky's ledger.
34
+ """
35
+
36
+ ...
37
+
38
+
39
+ class IslOracle:
40
+ """The decision procedure for the Presburger fragment.
41
+
42
+ Will answer emptiness, subset, and bijectivity questions about isl sets and
43
+ maps, returning a concrete witness when a check fails. Trusted in
44
+ development mode; the facts it discharges are re-derivable by Lean's
45
+ ``omega``, and anything outside its fragment becomes a residual lanky
46
+ theorem.
47
+ """
48
+
49
+ ...
50
+
51
+
52
+ class LoopyExecutor:
53
+ """The executor that runs typed kernels through loopy.
54
+
55
+ Will lower a checked loopty term to a loopy kernel, generate code for the
56
+ requested target (OpenCL, CUDA, C), and run it -- with the plain ``python``
57
+ execution of the same body available as the differential-test oracle.
58
+ """
59
+
60
+ ...
61
+
62
+
63
+ class RunVerb:
64
+ """The ``run`` subcommand loopty registers with lanky.
65
+
66
+ Will back both ``loopty run`` and ``lanky run`` for loopty targets:
67
+ elaborate the kernel, check its obligations, generate code, execute, and
68
+ compare against the reference run.
69
+ """
70
+
71
+ ...
72
+
73
+
74
+ __all__ = ["IslOracle", "KernelTheory", "LoopyExecutor", "RunVerb"]
File without changes
@@ -0,0 +1,19 @@
1
+ """Smoke tests for the loopty placeholder release."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import loopty
6
+ from loopty import cli
7
+
8
+
9
+ def test_version() -> None:
10
+ assert loopty.__version__ == "0.0.1"
11
+
12
+
13
+ def test_cli_main(capsys) -> None:
14
+ assert cli.main() == 0
15
+ out = capsys.readouterr().out
16
+ assert "loopty" in out
17
+ assert loopty.__version__ in out
18
+ assert "work in progress: placeholder release" in out
19
+ assert "https://github.com/xywei/loopty" in out
loopty-0.0.1/uv.lock ADDED
@@ -0,0 +1,109 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.12"
4
+
5
+ [[package]]
6
+ name = "colorama"
7
+ version = "0.4.6"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
12
+ ]
13
+
14
+ [[package]]
15
+ name = "iniconfig"
16
+ version = "2.3.0"
17
+ source = { registry = "https://pypi.org/simple" }
18
+ sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
19
+ wheels = [
20
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
21
+ ]
22
+
23
+ [[package]]
24
+ name = "loopty"
25
+ version = "0.0.1"
26
+ source = { editable = "." }
27
+
28
+ [package.dev-dependencies]
29
+ dev = [
30
+ { name = "pytest" },
31
+ { name = "ruff" },
32
+ ]
33
+
34
+ [package.metadata]
35
+ provides-extras = ["opencl"]
36
+
37
+ [package.metadata.requires-dev]
38
+ dev = [
39
+ { name = "pytest" },
40
+ { name = "ruff" },
41
+ ]
42
+
43
+ [[package]]
44
+ name = "packaging"
45
+ version = "26.3"
46
+ source = { registry = "https://pypi.org/simple" }
47
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
48
+ wheels = [
49
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
50
+ ]
51
+
52
+ [[package]]
53
+ name = "pluggy"
54
+ version = "1.6.0"
55
+ source = { registry = "https://pypi.org/simple" }
56
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
57
+ wheels = [
58
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
59
+ ]
60
+
61
+ [[package]]
62
+ name = "pygments"
63
+ version = "2.21.0"
64
+ source = { registry = "https://pypi.org/simple" }
65
+ sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
66
+ wheels = [
67
+ { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
68
+ ]
69
+
70
+ [[package]]
71
+ name = "pytest"
72
+ version = "9.1.1"
73
+ source = { registry = "https://pypi.org/simple" }
74
+ dependencies = [
75
+ { name = "colorama", marker = "sys_platform == 'win32'" },
76
+ { name = "iniconfig" },
77
+ { name = "packaging" },
78
+ { name = "pluggy" },
79
+ { name = "pygments" },
80
+ ]
81
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
82
+ wheels = [
83
+ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
84
+ ]
85
+
86
+ [[package]]
87
+ name = "ruff"
88
+ version = "0.16.7"
89
+ source = { registry = "https://pypi.org/simple" }
90
+ sdist = { url = "https://files.pythonhosted.org/packages/82/bb/5a449b9162e49b139d72f61672bd3ac1d790221f796d3304e2241fff4c58/ruff-0.16.7.tar.gz", hash = "sha256:5f71d004ac1263b22fa39462ac5ae618a4b77d58981af2cc79bf79a29c12b1a6", size = 4924184, upload-time = "2026-09-10T18:04:06.336Z" }
91
+ wheels = [
92
+ { url = "https://files.pythonhosted.org/packages/e3/b2/c80aeeb7f9e469c0d63a85d2f1ab6e1ebfbe10ea7a8d2438b7e09e3ff09e/ruff-0.16.7-py3-none-linux_armv6l.whl", hash = "sha256:727307773e7c7f9181d3ed3a2484186e56c1fa1874255911c74585eb2c7c19f9", size = 10048917, upload-time = "2026-09-10T18:03:30.28Z" },
93
+ { url = "https://files.pythonhosted.org/packages/7b/96/20bb7bcae008004df52afcb7ac83432d4a467f2c17b672fe46d26be231c5/ruff-0.16.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9d61c258deabf58f34c67bd4bb4d939c7f2e6b5f0e59c1cdd1cf771b11cde929", size = 10242929, upload-time = "2026-09-10T18:03:32.706Z" },
94
+ { url = "https://files.pythonhosted.org/packages/90/b2/f184b0d5abec02db69cfd7e49b688ae0237554528ca777136c613bf36bee/ruff-0.16.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ab81118df8945e0193d0240712aa4496573595b75185c3636ed825592a0f728", size = 9847245, upload-time = "2026-09-10T18:03:34.509Z" },
95
+ { url = "https://files.pythonhosted.org/packages/eb/2d/db1633a641866ed801e34cc6b60ef236c5e16f9b2124ab1d49cc24a5fe4f/ruff-0.16.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c196c968874fc8019da8e7163de7a1a370f111e2309b4b7dfea0fce950198d0", size = 9961780, upload-time = "2026-09-10T18:03:36.618Z" },
96
+ { url = "https://files.pythonhosted.org/packages/4d/98/edea21e1a3e38dbbc3bf6bb068b863b3b06184cf8533a4c7dbbe208a89d5/ruff-0.16.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac8c3bd0a7e10ad31e6ce51e7a99f3cb772e69aecdd6b9ea7e99b362f62a62c0", size = 9866337, upload-time = "2026-09-10T18:03:38.805Z" },
97
+ { url = "https://files.pythonhosted.org/packages/0b/11/a15e60d4c87b214646f116ca9d204475bf993ee1047459bc9a360fd4d6d1/ruff-0.16.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d3988edde000b5c75dc1b3f584708da9bc990de069c18909142580fec1af9", size = 10562512, upload-time = "2026-09-10T18:03:40.71Z" },
98
+ { url = "https://files.pythonhosted.org/packages/29/42/eaff4c9b6d0c7cdf56df313a17e89ae854f5bbc0b0c8f9cce19be0ab7a8f/ruff-0.16.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce05b62b770a8217c4646a9c4139fca00efe8fe5d71f87df2b243ff20d4584d1", size = 11302938, upload-time = "2026-09-10T18:03:42.607Z" },
99
+ { url = "https://files.pythonhosted.org/packages/5d/43/c75aa59a4ec181fe2ec06cab30e198c1c6d107229a9f008ae3a7c16cabd8/ruff-0.16.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af1b576fddb9d9ef2ececfb5fadcd6a624b25070ed85e3cfcfe449fc3ff6a7b9", size = 10840857, upload-time = "2026-09-10T18:03:44.604Z" },
100
+ { url = "https://files.pythonhosted.org/packages/21/33/81f3da371942ea031105ba679d8d6e28ec1660ccd690a45f42d381161356/ruff-0.16.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce7f8f22df67c93ed96c717f9128eadb797144ac2bad475cf536f31d6100c55", size = 10370001, upload-time = "2026-09-10T18:03:46.706Z" },
101
+ { url = "https://files.pythonhosted.org/packages/fa/0b/6345fb4dbf6dd0ed1cfe5d18391dc9c3f59cc81622a7b0a65b84b3e730ba/ruff-0.16.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:06d0e93d04f392996435ebd600c153f65b47d73fbec2415aa99c5ee5756b3a5f", size = 10548735, upload-time = "2026-09-10T18:03:48.658Z" },
102
+ { url = "https://files.pythonhosted.org/packages/3f/4d/c5576adf511f92a328e5569dda190ecdd430da51f1a649f3a4a2fd73e21e/ruff-0.16.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:142151a5e7b93c1b11111337142f89dd2fbfee92161225c99a97222f22e32656", size = 10108496, upload-time = "2026-09-10T18:03:50.563Z" },
103
+ { url = "https://files.pythonhosted.org/packages/ff/8c/667d83c16199a17a56adc6b0bd4c3beb5b767a2babcd16a56f76f9be7fd6/ruff-0.16.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e6651f97a342d8b35d54d8991544ca22169b86dc54111cb604666940c431b750", size = 9860136, upload-time = "2026-09-10T18:03:52.621Z" },
104
+ { url = "https://files.pythonhosted.org/packages/99/75/78d401106731999a1dd20cc5a6961e37e1eb9397a3b589f73f3a5ce146a3/ruff-0.16.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ef140c6eb935fa9a84c9c607dfb2cb1b85843c192e79265b0c54f35f557ea8e5", size = 10286290, upload-time = "2026-09-10T18:03:55.207Z" },
105
+ { url = "https://files.pythonhosted.org/packages/68/49/56f9c3a8b755df93a0ad318b2147bf4ef5dae9a7e5ec61c460109c67957f/ruff-0.16.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:53e39506a730fadeee0d998ed5946f30671f0db240c6c7c73bdabbe33604bb6f", size = 10745048, upload-time = "2026-09-10T18:03:57.299Z" },
106
+ { url = "https://files.pythonhosted.org/packages/5f/ea/7f9b938a63ece4bec677ad7f9f7fa02df3383db1949ed93a382441c09a87/ruff-0.16.7-py3-none-win32.whl", hash = "sha256:2ea3470fcebcbc5df2fb0c6f3b90333fa9084c534e0111c038fa4a6ab9f1c4b7", size = 10059082, upload-time = "2026-09-10T18:03:59.632Z" },
107
+ { url = "https://files.pythonhosted.org/packages/39/11/480a6973a927aa653e1cead6a6416008640e03a99d05b34c0434b8c6c366/ruff-0.16.7-py3-none-win_amd64.whl", hash = "sha256:7ac26aca826e9e21d0f1cb25b54ac660760a9fdd094d3e4df9848232be98cfc6", size = 10593368, upload-time = "2026-09-10T18:04:01.999Z" },
108
+ { url = "https://files.pythonhosted.org/packages/8b/4b/51327018d056f0dad2c2238f26d1fb0f53707a9d91b75dea6d1b3039f136/ruff-0.16.7-py3-none-win_arm64.whl", hash = "sha256:aab7f39e2c9df6c596216070f98eef1207b94f8516cca20c808826974971855b", size = 10412401, upload-time = "2026-09-10T18:04:04.098Z" },
109
+ ]