From daaa40454966558ffd2a0ea3dcf86bc1749c8715 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 15 Apr 2026 11:32:31 -0400 Subject: [PATCH] Bump version to 0.0.31 (#3280) --- CHANGELOG.md | 35 ++++++ dist-workspace.toml | 2 +- docs/installation.md | 6 +- docs/reference/cli.md | 1 + docs/reference/rules.md | 257 +++++++++++++++++++++++----------------- pyproject.toml | 2 +- ruff | 2 +- uv.lock | 2 +- 8 files changed, 192 insertions(+), 115 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3e7ae1..46fdc3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Changelog +## 0.0.31 + +Released on 2026-04-15. + +### Bug fixes + +- Avoid panic from double inference for `namedtuple(typename=T, field_names=x, **{})` ([#24641](https://github.com/astral-sh/ruff/pull/24641)) +- Avoid panic from double inference with missing functional `Enum(...)` names ([#24638](https://github.com/astral-sh/ruff/pull/24638)) +- Avoid panic from double inference with functional `Enum(value=...)` ([#24639](https://github.com/astral-sh/ruff/pull/24639)) +- Fix cases where `invalid-key` fix doesn't converge, and `override-of-final-method` produces invalid syntax ([#24649](https://github.com/astral-sh/ruff/pull/24649)) +- Fix unnecessary `ty:ignore` comments inserted by `--add-ignore` for diagnostics starting on the same line ([#24651](https://github.com/astral-sh/ruff/pull/24651)) + +### CLI + +- Add `--fix` mode to enable auto-fix for diagnostics ([#24097](https://github.com/astral-sh/ruff/pull/24097)) + +### Performance + +- Avoid excessive memory usage for dataclasses with many fields ([#24620](https://github.com/astral-sh/ruff/pull/24620)) + +### Core type checking + +- Check inherited `NamedTuple` field conflicts ([#24542](https://github.com/astral-sh/ruff/pull/24542)) +- Error when duplicate keywords are provided to TypedDict constructors ([#24449](https://github.com/astral-sh/ruff/pull/24449)) +- Respect mixed positional and keyword arguments in TypedDict constructor ([#24448](https://github.com/astral-sh/ruff/pull/24448)) +- Respect subclass shadowing for inherited NamedTuple fields ([#24640](https://github.com/astral-sh/ruff/pull/24640)) +- Skip `EnumMeta.__call__` for enum constructor signatures ([#24513](https://github.com/astral-sh/ruff/pull/24513)) + +### Contributors + +- [@sharkdp](https://github.com/sharkdp) +- [@Glyphack](https://github.com/Glyphack) +- [@MichaReiser](https://github.com/MichaReiser) +- [@charliermarsh](https://github.com/charliermarsh) + ## 0.0.30 Released on 2026-04-13. diff --git a/dist-workspace.toml b/dist-workspace.toml index a399f35..0d7bb20 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -1,7 +1,7 @@ [workspace] members = ["cargo:./ruff"] packages = ["ty"] -version = "0.0.30" +version = "0.0.31" # Config for 'dist' [dist] diff --git a/docs/installation.md b/docs/installation.md index acb8f81..b8de2c1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -71,7 +71,7 @@ ty includes a standalone installer. Request a specific version by including it in the URL: ```console - $ curl -LsSf https://astral.sh/ty/0.0.30/install.sh | sh + $ curl -LsSf https://astral.sh/ty/0.0.31/install.sh | sh ``` === "Windows" @@ -87,7 +87,7 @@ ty includes a standalone installer. Request a specific version by including it in the URL: ```pwsh-session - PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/ty/0.0.30/install.ps1 | iex" + PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/ty/0.0.31/install.ps1 | iex" ``` !!! tip @@ -163,7 +163,7 @@ COPY --from=ghcr.io/astral-sh/ty:latest /ty /bin/ The following tags are available: - `ghcr.io/astral-sh/ty:latest` -- `ghcr.io/astral-sh/ty:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/ty:0.0.30` +- `ghcr.io/astral-sh/ty:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/ty:0.0.31` - `ghcr.io/astral-sh/ty:{major}.{minor}`, e.g., `ghcr.io/astral-sh/ty:0.0` (the latest patch version) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 8f543c8..35a4019 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -58,6 +58,7 @@ over all configuration files.

--exit-zero

Always use exit code 0, even when there are error-level diagnostics

--extra-search-path path

Additional path to use as a module-resolution source (can be passed multiple times).

This is an advanced option that should usually only be used for first-party or third-party modules that are not installed into your Python environment in a conventional way. Use --python to point ty to your Python environment if it is in an unusual location.

+
--fix

Apply fixes to resolve errors

--force-exclude

Enforce exclusions, even for paths passed to ty directly on the command-line. Use --no-force-exclude to disable

--help, -h

Print help (see a summary with '-h')

--ignore rule

Disables the rule. Can be specified multiple times. Use 'all' to apply to all rules.

diff --git a/docs/reference/rules.md b/docs/reference/rules.md index a8de275..08ad691 100644 --- a/docs/reference/rules.md +++ b/docs/reference/rules.md @@ -8,7 +8,7 @@ Default level: error · Added in 0.0.13 · Related issues · -View source +View source @@ -49,7 +49,7 @@ class Derived(Base): # Error: `Derived` does not implement `method` Default level: warn · Added in 0.0.1-alpha.20 · Related issues · -View source +View source @@ -90,7 +90,7 @@ class SubProto(BaseProto, Protocol): Default level: error · Added in 0.0.14 · Related issues · -View source +View source @@ -126,7 +126,7 @@ def _(x: int): Default level: error · Preview (since 0.0.16) · Related issues · -View source +View source @@ -175,7 +175,7 @@ Foo.method() # Error: cannot call abstract classmethod Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -199,7 +199,7 @@ Calling a non-callable object will raise a `TypeError` at runtime. Default level: error · Added in 0.0.7 · Related issues · -View source +View source @@ -230,7 +230,7 @@ def f(x: object): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -262,7 +262,7 @@ f(int) # error Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -293,7 +293,7 @@ a = 1 Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -325,7 +325,7 @@ class C(A, B): ... Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -357,7 +357,7 @@ class B(A): ... Default level: error · Added in 0.0.1-alpha.29 · Related issues · -View source +View source @@ -385,7 +385,7 @@ type B = A Default level: error · Added in 0.0.15 · Related issues · -View source +View source @@ -417,7 +417,7 @@ class Example: Default level: warn · Added in 0.0.1-alpha.16 · Related issues · -View source +View source @@ -444,7 +444,7 @@ old_func() # emits [deprecated] diagnostic Default level: ignore · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -473,7 +473,7 @@ false positives it can produce. Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -500,7 +500,7 @@ class B(A, A): ... Default level: error · Added in 0.0.1-alpha.12 · Related issues · -View source +View source @@ -538,7 +538,7 @@ class A: # Crash at runtime Default level: error · Added in 0.0.14 · Related issues · -View source +View source @@ -609,7 +609,7 @@ def foo() -> "intt\b": ... Default level: error · Added in 0.0.20 · Related issues · -View source +View source @@ -641,7 +641,7 @@ def my_function() -> int: Default level: error · Added in 0.0.15 · Related issues · -View source +View source @@ -736,7 +736,7 @@ def test(): -> "Literal[5]": Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -766,7 +766,7 @@ class C(A, B): ... Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -792,7 +792,7 @@ t[3] # IndexError: tuple index out of range Default level: warn · Added in 0.0.1-alpha.33 · Related issues · -View source +View source @@ -826,7 +826,7 @@ class MyClass: ... Default level: error · Added in 0.0.1-alpha.12 · Related issues · -View source +View source @@ -915,7 +915,7 @@ an atypical memory layout. Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -942,7 +942,7 @@ func("foo") # error: [invalid-argument-type] Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -970,7 +970,7 @@ a: int = '' Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1004,7 +1004,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable Default level: error · Added in 0.0.1-alpha.19 · Related issues · -View source +View source @@ -1040,7 +1040,7 @@ asyncio.run(main()) Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1064,7 +1064,7 @@ class A(42): ... # error: [invalid-base] Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1091,7 +1091,7 @@ with 1: Default level: error · Added in 0.0.12 · Related issues · -View source +View source @@ -1128,7 +1128,7 @@ class Foo(NamedTuple): Default level: error · Added in 0.0.13 · Related issues · -View source +View source @@ -1160,7 +1160,7 @@ class A: Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1189,7 +1189,7 @@ a: str Default level: warn · Added in 0.0.20 · Related issues · -View source +View source @@ -1238,7 +1238,7 @@ class Pet(Enum): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1282,7 +1282,7 @@ except ZeroDivisionError: Default level: error · Added in 0.0.1-alpha.28 · Related issues · -View source +View source @@ -1324,7 +1324,7 @@ class D(A): Default level: error · Added in 0.0.1-alpha.35 · Related issues · -View source +View source @@ -1368,7 +1368,7 @@ class NonFrozenChild(FrozenBase): # Error raised here Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1406,7 +1406,7 @@ class D(Generic[U, T]): ... Default level: error · Added in 0.0.12 · Related issues · -View source +View source @@ -1485,7 +1485,7 @@ a = 20 / 0 # type: ignore Default level: error · Added in 0.0.1-alpha.17 · Related issues · -View source +View source @@ -1524,7 +1524,7 @@ carol = Person(name="Carol", aeg=25) # typo! Default level: warn · Added in 0.0.15 · Related issues · -View source +View source @@ -1585,7 +1585,7 @@ def f(x, y, /): # Python 3.8+ syntax Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1620,7 +1620,7 @@ def f(t: TypeVar("U")): ... Default level: error · Added in 0.0.18 · Related issues · -View source +View source @@ -1648,7 +1648,7 @@ match x: Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1682,7 +1682,7 @@ class B(metaclass=f): ... Default level: error · Added in 0.0.1-alpha.20 · Related issues · -View source +View source @@ -1789,7 +1789,7 @@ Correct use of `@override` is enforced by ty's [`invalid-explicit-override`](#in Default level: error · Added in 0.0.1-alpha.19 · Related issues · -View source +View source @@ -1837,13 +1837,54 @@ without a type annotation will raise an `AttributeError` at runtime. AttributeError: Cannot overwrite NamedTuple attribute _asdict ``` +## `invalid-named-tuple-override` + + +Default level: warn · +Added in 0.0.31 · +Related issues · +View source + + + +**What it does** + +Checks for subclass members that override inherited `NamedTuple` fields. + +**Why is this bad?** + +Reusing an inherited `NamedTuple` field name in a subclass creates a +class where tuple indexing and `repr()` still reflect the original +field, while attribute access follows the subclass member. + +**Default level** + +This rule is a warning by default because these overrides do not make +the class invalid at runtime. + +**Examples** + +```python +from typing import NamedTuple + +class User(NamedTuple): + name: str + +class Admin(User): + name = "shadowed" # error: [invalid-named-tuple-override] + +admin = Admin("Alice") +admin.name # "shadowed" +admin[0] # "Alice" +``` + ## `invalid-newtype` Default level: error · Added in 0.0.1-alpha.27 · Related issues · -View source +View source @@ -1873,7 +1914,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType` Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1923,7 +1964,7 @@ def foo(x: int) -> int: ... Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1949,7 +1990,7 @@ def f(a: int = ''): ... Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -1980,7 +2021,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2014,7 +2055,7 @@ TypeError: Protocols can only inherit from other protocols, got Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2063,7 +2104,7 @@ def g(): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2092,7 +2133,7 @@ def func() -> int: Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2188,7 +2229,7 @@ class C: ... Default level: error · Added in 0.0.10 · Related issues · -View source +View source @@ -2234,7 +2275,7 @@ class MyClass: Default level: error · Added in 0.0.1-alpha.6 · Related issues · -View source +View source @@ -2261,7 +2302,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus Default level: error · Added in 0.0.1-alpha.29 · Related issues · -View source +View source @@ -2308,7 +2349,7 @@ Bar[int] # error: too few arguments Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2338,7 +2379,7 @@ TYPE_CHECKING = '' Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2368,7 +2409,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments Default level: error · Added in 0.0.1-alpha.11 · Related issues · -View source +View source @@ -2402,7 +2443,7 @@ f(10) # Error Default level: error · Added in 0.0.1-alpha.11 · Related issues · -View source +View source @@ -2436,7 +2477,7 @@ class C: Default level: error · Added in 0.0.15 · Related issues · -View source +View source @@ -2467,7 +2508,7 @@ def g[U, T: U](): ... # error: [invalid-type-variable-bound] Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2514,7 +2555,7 @@ U = TypeVar('U', list[int], int) # valid constrained Type Default level: error · Added in 0.0.16 · Related issues · -View source +View source @@ -2546,7 +2587,7 @@ U = TypeVar("U", int, str, default=bytes) # error: [invalid-type-variable-defau Default level: error · Added in 0.0.28 · Related issues · -View source +View source @@ -2577,7 +2618,7 @@ class Child(Base): Default level: error · Added in 0.0.14 · Related issues · -View source +View source @@ -2612,7 +2653,7 @@ def f(x: dict): Default level: error · Added in 0.0.9 · Related issues · -View source +View source @@ -2643,7 +2684,7 @@ class Foo(TypedDict): Default level: error · Added in 0.0.25 · Related issues · -View source +View source @@ -2674,7 +2715,7 @@ def gen() -> Iterator[int]: Default level: error · Added in 0.0.14 · Related issues · -View source +View source @@ -2729,7 +2770,7 @@ def h(arg2: type): Default level: error · Added in 0.0.15 · Related issues · -View source +View source @@ -2772,7 +2813,7 @@ def g(arg: object): Default level: warn · Added in 0.0.30 · Related issues · -View source +View source @@ -2810,7 +2851,7 @@ Movie = TypedDict("Film", {"title": str}) # error: [mismatched-type-name] Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2835,7 +2876,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x' Default level: error · Added in 0.0.1-alpha.20 · Related issues · -View source +View source @@ -2868,7 +2909,7 @@ alice["age"] # KeyError Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2897,7 +2938,7 @@ func("string") # error: [no-matching-overload] Default level: error · Added in 0.0.30 · Related issues · -View source +View source @@ -2930,7 +2971,7 @@ class Sub(Super): ... # error: [non-callable-init-subclass] Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2956,7 +2997,7 @@ for i in 34: # TypeError: 'int' object is not iterable Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -2980,7 +3021,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt Default level: error · Added in 0.0.1-alpha.29 · Related issues · -View source +View source @@ -3013,7 +3054,7 @@ class B(A): Default level: error · Added in 0.0.16 · Related issues · -View source +View source @@ -3046,7 +3087,7 @@ class B(A): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3073,7 +3114,7 @@ f(1, x=2) # Error raised here Default level: error · Added in 0.0.1-alpha.22 · Related issues · -View source +View source @@ -3100,7 +3141,7 @@ f(x=1) # Error raised here Default level: ignore · Added in 0.0.1-alpha.22 · Related issues · -View source +View source @@ -3133,7 +3174,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c' Default level: warn · Added in 0.0.1-alpha.22 · Related issues · -View source +View source @@ -3165,7 +3206,7 @@ A()[0] # TypeError: 'A' object is not subscriptable Default level: ignore · Added in 0.0.1-alpha.22 · Related issues · -View source +View source @@ -3202,7 +3243,7 @@ from module import a # ImportError: cannot import name 'a' from 'module' Default level: warn · Added in 0.0.23 · Related issues · -View source +View source @@ -3229,7 +3270,7 @@ html.parser # AttributeError: module 'html' has no attribute 'parser' Default level: ignore · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3293,7 +3334,7 @@ def test(): -> "int": Default level: warn · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3320,7 +3361,7 @@ cast(int, f()) # Redundant Default level: warn · Added in 0.0.18 · Related issues · -View source +View source @@ -3352,7 +3393,7 @@ class C: Default level: error · Added in 0.0.20 · Related issues · -View source +View source @@ -3386,7 +3427,7 @@ class Outer[T]: Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3416,7 +3457,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3445,7 +3486,7 @@ class B(A): ... # Error raised here Default level: error · Added in 0.0.1-alpha.30 · Related issues · -View source +View source @@ -3479,7 +3520,7 @@ class F(NamedTuple): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3506,7 +3547,7 @@ f("foo") # Error raised here Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3534,7 +3575,7 @@ def _(x: int): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3580,7 +3621,7 @@ class A: Default level: error · Added in 0.0.20 · Related issues · -View source +View source @@ -3617,7 +3658,7 @@ class C(Generic[T]): Default level: warn · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3641,7 +3682,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3668,7 +3709,7 @@ f(x=1, y=2) # Error raised here Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3696,7 +3737,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo' Default level: warn · Added in 0.0.1-alpha.15 · Related issues · -View source +View source @@ -3754,7 +3795,7 @@ def g(): Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3779,7 +3820,7 @@ import foo # ModuleNotFoundError: No module named 'foo' Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3804,7 +3845,7 @@ print(x) # NameError: name 'x' is not defined Default level: warn · Added in 0.0.1-alpha.7 · Related issues · -View source +View source @@ -3843,7 +3884,7 @@ class D(C): ... # error: [unsupported-base] Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3880,7 +3921,7 @@ b1 < b2 < b1 # exception raised here Default level: ignore · Added in 0.0.12 · Related issues · -View source +View source @@ -3920,7 +3961,7 @@ def factory(base: type[Base]) -> type: Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source @@ -3948,7 +3989,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A' Default level: warn · Preview (since 0.0.21) · Related issues · -View source +View source @@ -4054,7 +4095,7 @@ to `false`. Default level: warn · Added in 0.0.1-alpha.22 · Related issues · -View source +View source @@ -4117,7 +4158,7 @@ def foo(x: int | str) -> int | str: Default level: error · Added in 0.0.1-alpha.1 · Related issues · -View source +View source diff --git a/pyproject.toml b/pyproject.toml index 807a82b..72b9edb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ty" -version = "0.0.30" +version = "0.0.31" requires-python = ">=3.8" dependencies = [] description = "An extremely fast Python type checker, written in Rust." diff --git a/ruff b/ruff index dc4df9c..ee9088e 160000 --- a/ruff +++ b/ruff @@ -1 +1 @@ -Subproject commit dc4df9c7019b9c68af25778d68edbd68f0bc74d9 +Subproject commit ee9088eebf068f4b3a14f5ee34607f72d3513186 diff --git a/uv.lock b/uv.lock index 01563d7..c3f4399 100644 --- a/uv.lock +++ b/uv.lock @@ -624,7 +624,7 @@ wheels = [ [[package]] name = "ty" -version = "0.0.30" +version = "0.0.31" source = { editable = "." } [package.dev-dependencies]