From 1f77e92744bd502480c4769d1775bdddc114dca5 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sun, 28 Sep 2025 17:25:50 +0200 Subject: [PATCH 1/3] Split out Mypy-dependant typing examples, add more type checkers --- .github/workflows/ci.yml | 8 +- pyproject.toml | 13 +- tox.ini | 21 ++- typing-examples/baseline_examples.py | 177 ++++++++++++++++++ .../mypy_examples.py | 95 +--------- uv.lock | 77 +++++++- 6 files changed, 285 insertions(+), 106 deletions(-) create mode 100644 typing-examples/baseline_examples.py rename tests/typing_example.py => typing-examples/mypy_examples.py (80%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 401e09ef9..96c5eb415 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -179,8 +179,8 @@ jobs: - run: uvx --with=tox-uv tox run -e docs-doctests,changelog - pyright: - name: Check types using pyright + typing: + name: Check types using supported type checkers runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -191,7 +191,7 @@ jobs: - run: > uvx --with=tox-uv --python $(cat .python-version-default) - tox run -e pyright + tox run -f typing install-dev: name: Verify dev env @@ -222,7 +222,7 @@ jobs: - tests-pypy - docs - install-dev - - pyright + - typing runs-on: ubuntu-latest diff --git a/pyproject.toml b/pyproject.toml index 588b4cab1..3b7fe2f3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,8 @@ tests = [ ] cov = [{ include-group = "tests" }, "coverage[toml]"] pyright = ["pyright", { include-group = "tests" }] +ty = ["ty", { include-group = "tests" }] +pyrefly = ["pyrefly", { include-group = "tests" }] benchmark = [ { include-group = "tests" }, "pytest-codspeed", @@ -254,11 +256,12 @@ ignore = [ "src/*/*.pyi" = ["ALL"] # TODO "tests/test_annotations.py" = ["FA100"] -"tests/typing_example.py" = [ - "E741", # ambiguous variable names don't matter in type checks - "B018", # useless expressions aren't useless in type checks - "B015", # pointless comparison in type checks aren't pointless - "UP037", # we test some older syntaxes on purpose +"typing-examples/*" = [ + "INP001", # not for imports + "E741", # ambiguous variable names don't matter in type checks + "B018", # useless expressions aren't useless in type checks + "B015", # pointless comparison in type checks aren't pointless + "UP037", # we test some older syntaxes on purpose ] [tool.ruff.lint.isort] diff --git a/tox.ini b/tox.ini index 72e9977ba..4d2ea2300 100644 --- a/tox.ini +++ b/tox.ini @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MIT + [tox] min_version = 4 env_list = @@ -5,7 +7,8 @@ env_list = py3{9,10,11,12,13,14}-tests, py3{10,11,12,13,14}-mypy, pypy3-tests, - pyright, + # Mypy needs to run within the respective Python version + typing-{pyright,ty,pyrefly} docs-{sponsors,doctests}, changelog, coverage-combine, @@ -26,7 +29,7 @@ dependency_groups = commands = tests: pytest {posargs:-n auto} mypy: pytest -k test_mypy - mypy: mypy tests/typing_example.py + mypy: mypy typing-examples mypy: mypy src/attrs/__init__.pyi src/attr/__init__.pyi src/attr/_typing_compat.pyi src/attr/_version_info.pyi src/attr/converters.pyi src/attr/exceptions.pyi src/attr/filters.pyi src/attr/setters.pyi src/attr/validators.pyi [testenv:pypy3-tests] @@ -121,9 +124,19 @@ commands = towncrier build --version main --draft -[testenv:pyright] +[testenv:typing-pyright] dependency_groups = pyright -commands = pytest tests/test_pyright.py -vv +commands = + pyright typing-examples/baseline_examples.py + pytest tests/test_pyright.py -vv + +[testenv:typing-ty] +dependency_groups = ty +commands = ty check typing-examples/baseline_examples.py + +[testenv:typing-pyrefly] +dependency_groups = pyrefly +commands = pyrefly check typing-examples/baseline_examples.py [testenv:docset] diff --git a/typing-examples/baseline_examples.py b/typing-examples/baseline_examples.py new file mode 100644 index 000000000..6372a15df --- /dev/null +++ b/typing-examples/baseline_examples.py @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: MIT + +""" +Baseline features that should be supported by all type checkers. +""" + +from __future__ import annotations + +from typing import Any + +import attrs + + +@attrs.define(order=True) +class NGClass: + x: int = attrs.field(default=42) + + +ngc = NGClass(1) + + +@attrs.mutable(slots=False) +class NGClass2: + x: int + + +ngc2 = NGClass2(1) + + +@attrs.frozen(str=True) +class NGFrozen: + x: int + + +ngf = NGFrozen(1) + +attrs.fields(NGFrozen).x.evolve(eq=False) +a = attrs.fields(NGFrozen).x +a.evolve(repr=False) + + +@attrs.define +class C: + a: int + + +c = C(1) +c.a + + +@attrs.frozen +class D: + a: int + + +D(1).a + + +@attrs.define +class Derived(C): + b: int + + +Derived(1, 2).a +Derived(1, 2).b + + +@attrs.define +class Error(Exception): + x: int + + +try: + raise Error(1) +except Error as e: + e.x + e.args + str(e) + + +@attrs.define +class AliasExample: + without_alias: int + _with_alias: int = attrs.field(alias="_with_alias") + + +attrs.fields(AliasExample).without_alias.alias +attrs.fields(AliasExample)._with_alias.alias + + +@attrs.define +class Validated: + num: int = attrs.field(validator=attrs.validators.ge(0)) + + +attrs.validators.set_disabled(True) +attrs.validators.set_disabled(False) + + +with attrs.validators.disabled(): + Validated(num=-1) + + +@attrs.define +class WithCustomRepr: + a: int = attrs.field(repr=True) + b: str = attrs.field(repr=False) + c: str = attrs.field(repr=lambda value: "c is for cookie") + d: bool = attrs.field(repr=str) + + +@attrs.define(on_setattr=attrs.setters.validate) +class ValidatedSetter2: + a: int + b: str = attrs.field(on_setattr=attrs.setters.NO_OP) + c: bool = attrs.field(on_setattr=attrs.setters.frozen) + d: int = attrs.field( + on_setattr=[attrs.setters.convert, attrs.setters.validate] + ) + e: bool = attrs.field( + on_setattr=attrs.setters.pipe( + attrs.setters.convert, attrs.setters.validate + ) + ) + + +@attrs.define(eq=True, order=True) +class OrderFlags: + a: int = attrs.field(eq=False, order=False) + b: int = attrs.field(eq=True, order=True) + + +# field_transformer +def ft_hook2( + cls: type, attribs: list[attrs.Attribute] +) -> list[attrs.Attribute]: + return attribs + + +@attrs.define(field_transformer=ft_hook2) +class TransformedAttrs2: + x: int + + +@attrs.define +class FactoryTest: + a: list[int] = attrs.field(default=attrs.Factory(list)) + b: list[Any] = attrs.field(default=attrs.Factory(list, False)) + c: list[int] = attrs.field(default=attrs.Factory((lambda s: s.a), True)) + + +attrs.asdict(FactoryTest()) +attrs.asdict(FactoryTest(), retain_collection_types=False) + + +@attrs.define(match_args=False) +class MatchArgs2: + a: int + b: int + + +# NG versions of asdict/astuple +attrs.asdict(MatchArgs2(1, 2)) +attrs.astuple(MatchArgs2(1, 2)) + + +def accessing_from_attrs() -> None: + """ + Use a function to keep the ns clean. + """ + attrs.converters.optional + attrs.exceptions.FrozenError + attrs.filters.include + attrs.filters.exclude + attrs.setters.frozen + attrs.validators.and_ + attrs.cmp_using diff --git a/tests/typing_example.py b/typing-examples/mypy_examples.py similarity index 80% rename from tests/typing_example.py rename to typing-examples/mypy_examples.py index 89fccff3b..d22cea1bd 100644 --- a/tests/typing_example.py +++ b/typing-examples/mypy_examples.py @@ -1,5 +1,9 @@ # SPDX-License-Identifier: MIT +""" +Typing examples that rely on Mypy-specific features (mostly the attrs plugin). +""" + from __future__ import annotations import re @@ -292,16 +296,9 @@ class Validated2: num: int = attr.field(validator=attr.validators.ge(0)) -@attrs.define -class Validated3: - num: int = attrs.field(validator=attrs.validators.ge(0)) - - with attr.validators.disabled(): Validated2(num=-1) -with attrs.validators.disabled(): - Validated3(num=-1) try: attr.validators.set_disabled(True) @@ -319,14 +316,6 @@ class WithCustomRepr: d: bool = attr.ib(repr=str) -@attrs.define -class WithCustomRepr2: - a: int = attrs.field(repr=True) - b: str = attrs.field(repr=False) - c: str = attrs.field(repr=lambda value: "c is for cookie") - d: bool = attrs.field(repr=str) - - # Check some of our own types @attr.s(eq=True, order=False) class OrderFlags: @@ -348,43 +337,16 @@ class ValidatedSetter: ) -@attrs.define(on_setattr=attr.setters.validate) -class ValidatedSetter2: - a: int - b: str = attrs.field(on_setattr=attrs.setters.NO_OP) - c: bool = attrs.field(on_setattr=attrs.setters.frozen) - d: int = attrs.field( - on_setattr=[attrs.setters.convert, attrs.setters.validate] - ) - e: bool = attrs.field( - on_setattr=attrs.setters.pipe( - attrs.setters.convert, attrs.setters.validate - ) - ) - - # field_transformer def ft_hook(cls: type, attribs: list[attr.Attribute]) -> list[attr.Attribute]: return attribs -# field_transformer -def ft_hook2( - cls: type, attribs: list[attrs.Attribute] -) -> list[attrs.Attribute]: - return attribs - - @attr.s(field_transformer=ft_hook) class TransformedAttrs: x: int -@attrs.define(field_transformer=ft_hook2) -class TransformedAttrs2: - x: int - - # Auto-detect @attr.s(auto_detect=True) class AutoDetect: @@ -394,7 +356,6 @@ def __init__(self, x: int): self.x = x -# Provisional APIs @attr.define(order=True) class NGClass: x: int = attr.field(default=42) @@ -403,27 +364,12 @@ class NGClass: ngc = NGClass(1) -@attr.mutable(slots=False) -class NGClass2: - x: int - - -ngc2 = NGClass2(1) - - @attr.frozen(str=True) class NGFrozen: x: int -ngf = NGFrozen(1) - attr.fields(NGFrozen).x.evolve(eq=False) -a = attr.fields(NGFrozen).x -a.evolve(repr=False) - - -attrs.fields(NGFrozen).x.evolve(eq=False) a = attrs.fields(NGFrozen).x a.evolve(repr=False) @@ -440,14 +386,6 @@ class FactoryTest: c: list[int] = attr.ib(default=attr.Factory((lambda s: s.a), True)) -@attrs.define -class FactoryTest2: - a: list[int] = attrs.field(default=attrs.Factory(list)) - b: list[Any] = attrs.field(default=attrs.Factory(list, False)) - c: list[int] = attrs.field(default=attrs.Factory((lambda s: s.a), True)) - - -attrs.asdict(FactoryTest2()) attr.asdict(FactoryTest(), tuple_keys=True) @@ -462,18 +400,6 @@ class MatchArgs: attr.asdict(FactoryTest(), retain_collection_types=False) -# Check match_args stub -@attrs.define(match_args=False) -class MatchArgs2: - a: int - b: int - - -# NG versions of asdict/astuple -attrs.asdict(MatchArgs2(1, 2)) -attrs.astuple(MatchArgs2(1, 2)) - - def accessing_from_attr() -> None: """ Use a function to keep the ns clean. @@ -487,19 +413,6 @@ def accessing_from_attr() -> None: attr.cmp_using -def accessing_from_attrs() -> None: - """ - Use a function to keep the ns clean. - """ - attrs.converters.optional - attrs.exceptions.FrozenError - attrs.filters.include - attrs.filters.exclude - attrs.setters.frozen - attrs.validators.and_ - attrs.cmp_using - - foo = object if attrs.has(foo) or attr.has(foo): foo.__attrs_attrs__ diff --git a/uv.lock b/uv.lock index db25decf5..a4c75f251 100644 --- a/uv.lock +++ b/uv.lock @@ -125,6 +125,14 @@ mypy = [ { name = "pytest-mypy-plugins", marker = "python_full_version >= '3.10' and platform_python_implementation == 'CPython'" }, { name = "pytest-xdist", extra = ["psutil"] }, ] +pyrefly = [ + { name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" }, + { name = "hypothesis" }, + { name = "pympler" }, + { name = "pyrefly" }, + { name = "pytest" }, + { name = "pytest-xdist", extra = ["psutil"] }, +] pyright = [ { name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" }, { name = "hypothesis" }, @@ -140,6 +148,14 @@ tests = [ { name = "pytest" }, { name = "pytest-xdist", extra = ["psutil"] }, ] +ty = [ + { name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" }, + { name = "hypothesis" }, + { name = "pympler" }, + { name = "pytest" }, + { name = "pytest-xdist", extra = ["psutil"] }, + { name = "ty" }, +] [package.metadata] @@ -195,6 +211,14 @@ mypy = [ { name = "pytest-mypy-plugins", marker = "python_full_version >= '3.10' and platform_python_implementation == 'CPython'" }, { name = "pytest-xdist", extras = ["psutil"] }, ] +pyrefly = [ + { name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" }, + { name = "hypothesis" }, + { name = "pympler" }, + { name = "pyrefly" }, + { name = "pytest" }, + { name = "pytest-xdist", extras = ["psutil"] }, +] pyright = [ { name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" }, { name = "hypothesis" }, @@ -210,6 +234,14 @@ tests = [ { name = "pytest" }, { name = "pytest-xdist", extras = ["psutil"] }, ] +ty = [ + { name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" }, + { name = "hypothesis" }, + { name = "pympler" }, + { name = "pytest" }, + { name = "pytest-xdist", extras = ["psutil"] }, + { name = "ty" }, +] [[package]] name = "babel" @@ -594,7 +626,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ @@ -665,7 +697,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10' or platform_python_implementation != 'CPython'" }, + { name = "zipp", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ @@ -1040,6 +1072,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/4f/a6a2e2b202d7fd97eadfe90979845b8706676b41cbd3b42ba75adf329d1f/Pympler-1.1-py3-none-any.whl", hash = "sha256:5b223d6027d0619584116a0cbc28e8d2e378f7a79c1e5e024f9ff3b673c58506", size = 165766, upload-time = "2024-06-28T19:56:05.087Z" }, ] +[[package]] +name = "pyrefly" +version = "0.34.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/db/b9bdb8c2f538cfa3338d368927b98cd573131804e6d57857ea9c7d0dbc56/pyrefly-0.34.0.tar.gz", hash = "sha256:eb18f4ec341105d5463b5c5844b25a2d520d36d5294302fcb3fd1de2868246e1", size = 1461134, upload-time = "2025-09-22T17:46:56.579Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/8c/decdf3c66207f21f71ca0a2919650499aa2afcb3f22e0f2fbffb002e3754/pyrefly-0.34.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c5b212f557c19bbc9f02377d25c0380ea9987e320a1ed69f625fe817a1163774", size = 6722736, upload-time = "2025-09-22T17:46:41.432Z" }, + { url = "https://files.pythonhosted.org/packages/4a/80/4262da89aef2b14fde6bf0346fe63dc710742e2854b26a929f78160d594a/pyrefly-0.34.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:87181abe763ed19fd9e107a9ae5982f852f18f93de2957a731c9b40048174552", size = 6267483, upload-time = "2025-09-22T17:46:43.533Z" }, + { url = "https://files.pythonhosted.org/packages/53/64/36db94740c2a413a1255540da6103d82bd2c451d526a1c405d341a75a50f/pyrefly-0.34.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1085c186cde52c79f42b8c654aa0840c1028601fbd9c35d981d60453008498ec", size = 6505495, upload-time = "2025-09-22T17:46:45.041Z" }, + { url = "https://files.pythonhosted.org/packages/c3/2b/6606fcb784770cd36c5cafc5c940c796753772d5c64be82bed1bf5cce9aa/pyrefly-0.34.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcb92520e4dac3967744f44bd3145e8d029d313076b06277f15b693869f4eb4a", size = 7334795, upload-time = "2025-09-22T17:46:47.472Z" }, + { url = "https://files.pythonhosted.org/packages/69/b3/4353e744e29fa95c0e9f70ababe15fd019df252f8e90ecd2d15620adcfc8/pyrefly-0.34.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f840afa73371aa56f8e6666f2733b4b8a85620329aa385d7a39bb226983b43b", size = 6999090, upload-time = "2025-09-22T17:46:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/60/1e/7d13a42f680c98ed0365295d3bb09d7e11c97aab563154dcfa62a7448a25/pyrefly-0.34.0-py3-none-win32.whl", hash = "sha256:a22f1fd6f03c6fe11c6b491c12a70bb07b3025cd316faa9f98729d42e410bbcd", size = 6500766, upload-time = "2025-09-22T17:46:51.194Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/e2285197587d125473237f9daf3878edeab9d03918985db89f294bdebc86/pyrefly-0.34.0-py3-none-win_amd64.whl", hash = "sha256:ac6e33b6a2f90aea9bdf54c7f78ff29b9f71ffcf55f2636b7da513df74bd4b56", size = 6938350, upload-time = "2025-09-22T17:46:53.08Z" }, + { url = "https://files.pythonhosted.org/packages/cc/34/0653bf969bd3f0dbb86083ff73f0a381e46239aac21b7d6dfec403959832/pyrefly-0.34.0-py3-none-win_arm64.whl", hash = "sha256:34517123a61caef9d19a73ebb04b5bb8bde6efe999daf89fd7a200bb755e3e73", size = 6527162, upload-time = "2025-09-22T17:46:54.954Z" }, +] + [[package]] name = "pyright" version = "1.1.405" @@ -1867,6 +1915,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/06/8ba22ec32c74ac1be3baa26116e3c28bc0e76a5387476921d20b6fdade11/towncrier-25.8.0-py3-none-any.whl", hash = "sha256:b953d133d98f9aeae9084b56a3563fd2519dfc6ec33f61c9cd2c61ff243fb513", size = 65101, upload-time = "2025-08-30T11:41:53.644Z" }, ] +[[package]] +name = "ty" +version = "0.0.1a21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/0f/65606ccee2da5a05a3c3362f5233f058e9d29d3c5521697c7ae79545d246/ty-0.0.1a21.tar.gz", hash = "sha256:e941e9a9d1e54b03eeaf9c3197c26a19cf76009fd5e41e16e5657c1c827bd6d3", size = 4263980, upload-time = "2025-09-19T06:54:06.412Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/7a/c87a42d0a45cfa2d5c06c8d66aa1b243db16dc31b25e545fb0263308523b/ty-0.0.1a21-py3-none-linux_armv6l.whl", hash = "sha256:1f276ceab23a1410aec09508248c76ae0989c67fb7a0c287e0d4564994295531", size = 8421116, upload-time = "2025-09-19T06:53:35.029Z" }, + { url = "https://files.pythonhosted.org/packages/99/c2/721bf4fa21c84d4cdae0e57a06a88e7e64fc2dca38820232bd6cbeef644f/ty-0.0.1a21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c3bc66fcae41eff133cfe326dd65d82567a2fb5d4efe2128773b10ec2766819", size = 8512556, upload-time = "2025-09-19T06:53:37.455Z" }, + { url = "https://files.pythonhosted.org/packages/6c/58/b0585d9d61673e864a87e95760dfa2a90ac15702e7612ab064d354f6752a/ty-0.0.1a21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cc0880ec344fbdf736b05d8d0da01f0caaaa02409bd9a24b68d18d0127a79b0e", size = 8109188, upload-time = "2025-09-19T06:53:39.469Z" }, + { url = "https://files.pythonhosted.org/packages/ea/08/edf7b59ba24bb1a1af341207fc5a0106eb1fe4264c1d7fb672c171dd2daf/ty-0.0.1a21-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:334d2a212ebf42a0e55d57561926af7679fe1e878175e11dcb81ad8df892844e", size = 8279000, upload-time = "2025-09-19T06:53:41.309Z" }, + { url = "https://files.pythonhosted.org/packages/05/8e/4b5e562623e0aa24a3972510287b4bc5d98251afb353388d14008ea99954/ty-0.0.1a21-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8c769987d00fbc33054ff7e342633f475ea10dc43bc60fb9fb056159d48cb90", size = 8243261, upload-time = "2025-09-19T06:53:42.736Z" }, + { url = "https://files.pythonhosted.org/packages/c3/09/6476fa21f9962d5b9c8e8053fd0442ed8e3ceb7502e39700ab1935555199/ty-0.0.1a21-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:218d53e7919e885bd98e9196d9cb952d82178b299aa36da6f7f39333eb7400ed", size = 9150228, upload-time = "2025-09-19T06:53:44.242Z" }, + { url = "https://files.pythonhosted.org/packages/d2/96/49c158b6255fc1e22a5701c38f7d4c1b7f8be17a476ce9226fcae82a7b36/ty-0.0.1a21-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:84243455f295ed850bd53f7089819321807d4e6ee3b1cbff6086137ae0259466", size = 9628323, upload-time = "2025-09-19T06:53:45.998Z" }, + { url = "https://files.pythonhosted.org/packages/f4/65/37a8a5cb7b3254365c54b5e10f069e311c4252ed160b86fabd1203fbca5c/ty-0.0.1a21-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87a200c21e02962e8a27374d9d152582331d57d709672431be58f4f898bf6cad", size = 9251233, upload-time = "2025-09-19T06:53:48.042Z" }, + { url = "https://files.pythonhosted.org/packages/a3/30/5b06120747da4a0f0bc54a4b051b42172603033dbee0bcf51bce7c21ada9/ty-0.0.1a21-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be8f457d7841b7ead2a3f6b65ba668abc172a1150a0f1f6c0958af3725dbb61a", size = 8996186, upload-time = "2025-09-19T06:53:49.753Z" }, + { url = "https://files.pythonhosted.org/packages/af/fc/5aa122536b1acb57389f404f6328c20342242b78513a60459fee9b7d6f27/ty-0.0.1a21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1474d883129bb63da3b2380fc7ead824cd3baf6a9551e6aa476ffefc58057af3", size = 8722848, upload-time = "2025-09-19T06:53:51.566Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c1/456dcc65a149df8410b1d75f0197a31d4beef74b7bb44cce42b03bf074e8/ty-0.0.1a21-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0efba2e52b58f536f4198ba5c4a36cac2ba67d83ec6f429ebc7704233bcda4c3", size = 8220727, upload-time = "2025-09-19T06:53:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/a4/86/b37505d942cd68235be5be407e43e15afa36669aaa2db9b6e5b43c1d9f91/ty-0.0.1a21-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5dfc73299d441cc6454e36ed0a976877415024143dfca6592dc36f7701424383", size = 8279114, upload-time = "2025-09-19T06:53:55.343Z" }, + { url = "https://files.pythonhosted.org/packages/55/fe/0d9816f36d258e6b2a3d7518421be17c68954ea9a66b638de49588cc2e27/ty-0.0.1a21-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba13d03b9e095216ceb4e4d554a308517f28ab0a6e4dcd07cfe94563e4c2c489", size = 8701798, upload-time = "2025-09-19T06:53:57.17Z" }, + { url = "https://files.pythonhosted.org/packages/4e/7a/70539932e3e5a36c54bd5432ff44ed0c301c41a528365d8de5b8f79f4317/ty-0.0.1a21-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9463cac96b8f1bb5ba740fe1d42cd6bd152b43c5b159b2f07f8fd629bcdded34", size = 8872676, upload-time = "2025-09-19T06:53:59.357Z" }, + { url = "https://files.pythonhosted.org/packages/ea/94/809d85f6982841fe28526ace3b282b0458d0a96bbc6b1a982d9269a5e481/ty-0.0.1a21-py3-none-win32.whl", hash = "sha256:ecf41706b803827b0de8717f32a434dad1e67be9f4b8caf403e12013179ea06a", size = 8003866, upload-time = "2025-09-19T06:54:01.393Z" }, + { url = "https://files.pythonhosted.org/packages/50/16/b3e914cec2a6344d2c30d3780ca6ecd39667173611f8776cecfd1294eab9/ty-0.0.1a21-py3-none-win_amd64.whl", hash = "sha256:7505aeb8bf2a62f00f12cfa496f6c965074d75c8126268776565284c8a12d5dd", size = 8675300, upload-time = "2025-09-19T06:54:02.893Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/293be6bc19f6da5e9b15e615a7100504f307dd4294d2c61cee3de91198e5/ty-0.0.1a21-py3-none-win_arm64.whl", hash = "sha256:21f708d02b6588323ffdbfdba38830dd0ecfd626db50aa6006b296b5470e52f9", size = 8193800, upload-time = "2025-09-19T06:54:04.583Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 347f02caa324eeed234e608a6256b755c2c1a591 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sun, 28 Sep 2025 17:29:08 +0200 Subject: [PATCH 2/3] Add explanation --- typing-examples/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 typing-examples/README.md diff --git a/typing-examples/README.md b/typing-examples/README.md new file mode 100644 index 000000000..5a02c875e --- /dev/null +++ b/typing-examples/README.md @@ -0,0 +1,7 @@ +# Typing examples + +The files in this directory are **not** meant to be executed. + +They are type-checked using various type checkers to ensure our type stubs are correct. + +See the environments starting with `typing-` in <../tox.ini>. From fef78ca8291d171aa2662d7fd16163f5d8e9d7f4 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sun, 28 Sep 2025 17:32:09 +0200 Subject: [PATCH 3/3] link --- typing-examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typing-examples/README.md b/typing-examples/README.md index 5a02c875e..b3e8d300a 100644 --- a/typing-examples/README.md +++ b/typing-examples/README.md @@ -4,4 +4,4 @@ The files in this directory are **not** meant to be executed. They are type-checked using various type checkers to ensure our type stubs are correct. -See the environments starting with `typing-` in <../tox.ini>. +See the environments starting with `typing-` in [`tox.ini`](../tox.ini).