Commit Graph

13326 Commits

Author SHA1 Message Date
Alex Waygood
3c5956e93d [ty] Include the specialization of a generic TypedDict as part of its display (#22174)
## Summary

This is the easy bit of https://github.com/astral-sh/ty/issues/2190

## Test Plan

mdtests updated
2025-12-24 14:39:46 +00:00
Charlie Marsh
81f34fbc8e [ty] Store un-widened type in Place (#22093)
## Summary

See: https://github.com/astral-sh/ruff/pull/22025#discussion_r2632724156
2025-12-23 23:19:57 -05:00
Charlie Marsh
184f487c84 [ty] Add a dedicated diagnostic for TypedDict deletions (#22123)
## Summary

Provides a message like:

```
  error[invalid-argument-type]: Cannot delete required key "name" from TypedDict `Movie`
    --> test.py:15:7
     |
  15 | del m["name"]
     |       ^^^^^^
     |
  info: Field defined here
   --> test.py:4:5
    |
  4 |     name: str
    |     --------- `name` declared as required here; consider making it `NotRequired`
    |
  info: Only keys marked as `NotRequired` (or in a TypedDict with `total=False`) can be deleted
```
2025-12-24 03:49:42 +00:00
Charlie Marsh
969c8a547e [ty] Synthesize __delitem__ for TypedDict to allow deleting non-required keys (#22122)
## Summary

TypedDict now synthesizes a proper `__delitem__` method that...

- ...allows deletion of `NotRequired` keys and keys in `total=False`
TypedDicts.
- ...rejects deletion of required keys (synthesizes `__delitem__(k:
Never)`).
2025-12-24 03:39:54 +00:00
Charlie Marsh
acdda78189 [ty] Fix @staticmethod combined with other decorators incorrectly binding self (#22128)
## Summary

We already had `CallableTypeKind::ClassMethodLike` to track callables
that behave like `classmethods` (always bind the first argument). This
PR adds the symmetric `CallableTypeKind::StaticMethodLike` for callables
that behave like `staticmethods` (never bind `self`).

Closes https://github.com/astral-sh/ty/issues/2114.
2025-12-24 03:35:09 +00:00
Charlie Marsh
c28c1f534d [ty] Check __delitem__ instead of __getitem__ for del x[k] (#22121)
## Summary

Previously, `del x[k]` incorrectly required the object to have a
`__getitem__` method. This was wrong because deletion only needs
`__delitem__`, which is independent of `__getitem__`.

Closes https://github.com/astral-sh/ty/issues/1799.
2025-12-24 03:34:20 +00:00
Charlie Marsh
b723917463 [ty] Support tuple narrowing based on member checks (#22167)
## Summary

Closes https://github.com/astral-sh/ty/issues/2179.
2025-12-23 20:15:50 -05:00
Charlie Marsh
5decf94644 [ty] Synthesize a _fields attribute for NamedTuples (#22163)
## Summary

Closes #2176.
2025-12-23 21:38:41 +00:00
Charlie Marsh
89a55dd09f [ty] Synthesize a _replace method for NamedTuples (#22153)
## Summary

Closes https://github.com/astral-sh/ty/issues/2170.
2025-12-23 16:33:55 -05:00
Shunsuke Shibayama
8710a8c4ac [ty] don't expand type aliases in implicit tuple aliases (#22015)
## Summary

This PR fixes https://github.com/astral-sh/ty/issues/1848.

```python
T = tuple[int, 'U']

class C(set['U']):
    pass

type U = T | C
```

The reason why the fixed point iteration did not converge was because
the types stored in the implicit tuple type alias `Specialization`
changed each time.

```
1st: <class 'tuple[int, C]'>
2nd: <class 'tuple[int, tuple[int, C] | C]'>
3rd: <class 'tuple[int, tuple[int, tuple[int, C] | C] | C]'>
...
```

And this was because `UnionType::from_elements` was used when creating
union types for tuple operations, which causes type aliases inside to be
expanded.
This PR replaces these with `UnionType::from_elements_leave_aliases`.

## Test Plan

New corpus test
2025-12-23 13:22:54 -08:00
Jack O'Connor
e245c1d76e [ty] narrow tagged unions of TypedDict (#22104)
Identify and narrow cases like this:

```py
class Foo(TypedDict):
    tag: Literal["foo"]

class Bar(TypedDict):
    tag: Literal["bar"]

def _(union: Foo | Bar):
    if union["tag"] == "foo":
        reveal_type(union)  # Foo
```

Fixes part of https://github.com/astral-sh/ty/issues/1479.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-12-23 19:30:08 +00:00
Charlie Marsh
4c175fa0e1 [ty] Bind self with instance in __get__ (#22155)
## Summary

See: https://github.com/astral-sh/ruff/pull/22153/changes#r2641788438.
2025-12-23 11:25:58 -08:00
Micha Reiser
ed64c4d943 [ty] Abort printing diagnostics when pressing Ctrl+C (#22083) 2025-12-23 18:03:58 +01:00
Matthew Mckee
f1e6c9c3a0 [ty] Use markdown for completions documentation (#21752) 2025-12-23 17:07:53 +01:00
Wizzerinus | Alex K.
d9fe996e64 [ty] Support custom builtins (#22021) 2025-12-23 13:48:14 +00:00
Matthew Mckee
5ea30c4c53 Show both ty.toml and pyproject.toml examples in configuration reference (#22144)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-23 09:49:44 +01:00
Micha Reiser
ccc9132f73 [ty] Use ModuleName::new_static in more places (#22156) 2025-12-23 09:24:49 +01:00
Ibraheem Ahmed
65021fcee9 [ty] Support type inference between protocol instances (#22120) 2025-12-23 09:24:01 +01:00
Micha Reiser
aa21b70a8b [ty] Fix path of instrumented benchnmark binary (#22157) 2025-12-23 09:12:18 +01:00
Vincent Ging Ho Yim
22ce0c8a51 Decrease Markdown heading level (#22152) 2025-12-23 07:44:45 +00:00
Micha Reiser
d6a7c9b4ed [ty] Add respect-type-ignore-comments configuration option (#22137) 2025-12-23 08:36:51 +01:00
Charlie Marsh
4745d15fff [ty] Respect debug text interpolation in f-strings (#22151)
## Summary

Per @carljm's comment, we just fall back to `str`.

Closes https://github.com/astral-sh/ty/issues/2151.
2025-12-22 20:21:28 -05:00
Shunsuke Shibayama
06db474f20 [ty] stabilize union-type ordering in fixed-point iteration (#22070)
## Summary

This PR fixes https://github.com/astral-sh/ty/issues/2085.

Based on the reported code, the panicking MRE is:

```python
class Test:
    def __init__(self, x: int):
        self.left = x
        self.right = x
    def method(self):
        self.left, self.right = self.right, self.left
        if self.right:
            self.right = self.right
```

The type inference (`implicit_attribute_inner`) for `self.right`
proceeds as follows:

```
0: Divergent(Id(6c07))
1: Unknown | int | (Divergent(Id(1c00)) & ~AlwaysFalsy)
2: Unknown | int | (Divergent(Id(6c07)) & ~AlwaysFalsy) | (Divergent(Id(1c00)) & ~AlwaysFalsy)
3: Unknown | int | (Divergent(Id(1c00)) & ~AlwaysFalsy) | (Divergent(Id(6c07)) & ~AlwaysFalsy)
4: Unknown | int | (Divergent(Id(6c07)) & ~AlwaysFalsy) | (Divergent(Id(1c00)) & ~AlwaysFalsy)
...
```

The problem is that the order of union types is not stable between
cycles. To solve this, when unioning the previous union type with the
current union type, we should use the previous type as the base and add
only the new elements in this cycle (In the current implementation, this
unioning order was reversed).

## Test Plan

New corpus test
2025-12-22 16:16:03 -08:00
Charlie Marsh
664686bdbc [ty] Exclude parameterized tuple types from narrowing when disjoint from comparison values (#22129)
## Summary

IIUC, tuples with a known structure (`tuple_spec`) use the standard
tuple `__eq__` which only returns `True` for other tuples, so they can
be safely excluded when disjoint from string literals or other non-tuple
types.

Closes https://github.com/astral-sh/ty/issues/2140.
2025-12-22 20:44:49 +00:00
William Woodruff
4a937543b9 Ecosystem report: publish site via astral-sh-bot (#22142) 2025-12-22 12:57:49 -05:00
Micha Reiser
ec034fc359 [ty] Add new diagnosticMode: off (#22073) 2025-12-22 16:46:02 +01:00
Micha Reiser
29d7f22c1f [ty] Add ty.configuration and ty.configurationFile options (#22053) 2025-12-22 16:13:20 +01:00
Micha Reiser
8fc4349fd3 [ty] Split suppression.rs into multiple smaller modules (#22141) 2025-12-22 16:08:56 +01:00
Matthew Mckee
816b19c4a6 [ty] Rename set_invalid_syntax to set_invalid_type_annotation (#22140) 2025-12-22 14:29:03 +00:00
Peter Law
87406b43ea Fix iter example in usafe fixes doc (#22118)
## Summary

This appears to have been a copy/paste error from the list example, as
the subscript is not present in the original next/iter example only in
the case where the error case is shown. While in the specific example
code the subscript actually has no effect, it does make the example
slightly confusing.

Consider the following variations, first the example from the docs
unchanged and second the same code but not hitting the intended error
case (due to using a non-empty collection):
```console
$ python3 -c 'next(iter(range(0)))[0]'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
StopIteration

$ python3 -c 'next(iter(range(1)))[0]'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: 'int' object is not subscriptable
```

## Test Plan

Not directly tested, however see inline snippets above.
2025-12-22 09:25:28 -05:00
Matthew Mckee
422e99ea70 [ty] Add inlay hint request time log (#22138) 2025-12-22 15:08:49 +01:00
Micha Reiser
ea6730f546 [ty] Speed-up instrumented benchmarks (#22133) 2025-12-22 15:06:22 +01:00
Matthew Mckee
a46835c224 [ty] Set flag to avoid type[T@f] being inserted when you double-click on the inlay (#22139) 2025-12-22 14:00:45 +00:00
Micha Reiser
884e83591e [ty] Update salsa (#22072) 2025-12-22 13:22:54 +01:00
Alex Waygood
6b3dd28e63 [ty] Make a server snapshot less painful to update (#22132) 2025-12-22 12:13:58 +00:00
Harutaka Kawamura
572f57aa3c Fix GitHub Actions output format for multi-line diagnostics (#22108) 2025-12-22 10:08:07 +01:00
Micha Reiser
ed423e0ae2 [ty] Speedup ty-walltime benchmarks (#22126)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-12-22 08:44:17 +01:00
Charlie Marsh
fee4e2d72a [ty] Distribute type[] over unions (#22115)
## Summary

Closes https://github.com/astral-sh/ty/issues/2121.
2025-12-21 18:45:29 -05:00
Will Duke
b6e84eca16 [ty] Document invalid-syntax-in-forward-annotation and escape-character-in-forward-annotation (#22130)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-12-21 19:35:44 +00:00
Micha Reiser
b4c2825afd [ty] Move module resolver code into its own crate (#22106) 2025-12-21 11:00:34 +00:00
Ibraheem Ahmed
ad41728204 [ty] Avoid temporarily storing invalid multi-inference attempts (#22103)
## Summary

I missed this in https://github.com/astral-sh/ruff/pull/22062. This
avoids exponential runtime in the following snippet:

```py
class X1: ...
class X2: ...
class X3: ...
class X4: ...
class X5: ...
class X6: ...
...

def f(
    x:
        list[X1 | None]
      | list[X2 | None]
      | list[X3 | None]
      | list[X4 | None]
      | list[X5 | None]
      | list[X6 | None]
      ...
):
    ...

def g[T](x: T) -> list[T]:
    return [x]

def id[T](x: T) -> T:
    return x

f(id(id(id(id(g(X64()))))))
```

Eventually I want to refactor our multi-inference infrastructure (which
is currently very brittle) to handle this implicitly, but this is a
temporary performance fix until that happens.
2025-12-20 08:20:53 -08:00
Hugo
3ec63b964c [ty] Add support for dict(...) calls in typed dict contexts (#22113)
## Summary

fixes https://github.com/astral-sh/ty/issues/2127
- handle `dict(...)` calls in TypedDict context with bidirectional
inference
- validate keys/values using the existing TypedDict constructor implem
- mdtest: add 1 positive test, 1 negative test for invalid coverage

## Test Plan

```sh
cargo clippy --workspace --all-targets --all-features -- -D warnings  # Rust linting
cargo test  # Rust testing
uvx pre-commit run --all-files --show-diff-on-failure  # Rust and Python formatting, Markdown and Python linting, etc.
```
fully green
2025-12-20 07:59:03 -08:00
Matthew Mckee
f9a0e1e3f6 [ty] Fix panic introduced in #22076 (#22112)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Was looking over that PR and this looked wrong.

panic introduced in #22076 

## Test Plan

Before running:

```bash
cargo run -p ty check test.py --force-exclude --no-progress 
```

would result in a panic

```text
thread 'main' (162713) panicked at crates/ty/src/args.rs:459:17:
internal error: entered unreachable code: Clap should make this impossible
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

Now it does not.
2025-12-20 07:53:45 -08:00
William Woodruff
ef4507be96 Remove in-workflow deployment to Cloudflare Pages (#22098) 2025-12-20 10:21:47 -05:00
Alex Waygood
3398ab23a9 Update comments in sync_typeshed.yaml workflow (#22109) 2025-12-20 12:19:04 +00:00
Micha Reiser
5b475b45aa [ty] Add --force-exclude option (#22076) 2025-12-20 10:03:41 +01:00
Ibraheem Ahmed
2a959ef3f2 [ty] Avoid narrowing on non-generic calls (#22102)
## Summary

Resolves https://github.com/astral-sh/ty/issues/2026.
2025-12-19 23:18:07 -05:00
Ibraheem Ahmed
674d3902c6 [ty] Only prefer declared types in non-covariant positions (#22068)
## Summary

The following snippet currently errors because we widen the inferred
type, even though `X` is covariant over `T`. If `T` was contravariant or
invariant, this would be fine, as it would lead to an assignability
error anyways.

```python
class X[T]:
    def __init__(self: X[None]): ...

    def pop(self) -> T:
        raise NotImplementedError

# error: Argument to bound method `__init__` is incorrect: Expected `X[None]`, found `X[int | None]`
x: X[int | None] = X()
```

There are some cases where it is still helpful to prefer covariant
declared types, but this error seems hard to fix otherwise, and makes
our heuristics more consistent overall.
2025-12-19 17:27:31 -05:00
Alex Waygood
1a18ada931 [ty] Run mypy_primer and typing-conformance workflows on fewer PRs (#22096)
## Summary

Fixes https://github.com/astral-sh/ty/issues/2090. Quoting my rationale
from that issue:

> A PR that only touches code in [one of these crates] should never have
any impact on memory usage or diagnostics produced. And the comments
from the bot just lead to additional notifications which is annoying.

I _think_ I've got the syntax right here. The
[docs](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax)
say:

>  The order that you define paths patterns matters:
>
> - A matching negative pattern (prefixed with !) after a positive match
will exclude the path.
> - A matching positive pattern after a negative match will include the
path again.

## Test Plan

No idea? Merge it and see?
2025-12-19 19:31:20 +00:00
Alex Waygood
dde0d0af68 [ty] List rules in alphabetical order in the reference docs (#22097)
## Summary

Fixes https://github.com/astral-sh/ty/issues/1885.

It wasn't obvious to me that there was a deliberate order to the way
these rules were listed in our reference docs -- it looked like it was
_nearly_ alphabetical, but not quite. I think it's simpler if we just
list them in alphabetical order.

## Test Plan

The output from running `cargo dev generate-all` (committed as part of
this PR) looks correct!
2025-12-19 19:11:05 +00:00