Commit Graph

1246 Commits

Author SHA1 Message Date
Matthew Mckee
a2e0ff57c3 Run cargo sort (#22310) 2026-01-02 19:58:15 +00:00
Alex Waygood
26230b1ed3 [ty] Use IntersectionType::from_elements more (#22329) 2026-01-01 15:01:00 +00:00
github-actions[bot]
8e45bac3c1 [ty] Sync vendored typeshed stubs (#22321)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2026-01-01 01:29:12 +00:00
Charlie Marsh
f619783066 [ty] Treat __setattr__ as fallback-only (#22014)
## Summary

Closes https://github.com/astral-sh/ty/issues/1460.
2025-12-30 19:01:10 -05:00
Ibraheem Ahmed
ff05428ce6 [ty] Subtyping for bidirectional inference (#21930)
## Summary

Supersedes https://github.com/astral-sh/ruff/pull/21747. This version
uses the constraint solver directly, which means we should benefit from
constraint solver improvements for free.

Resolves https://github.com/astral-sh/ty/issues/1576.
2025-12-30 17:03:20 -05:00
Charlie Marsh
12dd27da52 [ty] Support narrowing for tuple matches with literal elements (#22303)
## Summary

See:
https://github.com/astral-sh/ruff/pull/22299#issuecomment-3699913849.
2025-12-30 13:45:07 -05:00
github-actions[bot]
7173c7ea3f [ty] Sync vendored typeshed stubs (#22302)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-12-30 17:24:13 +00:00
Charlie Marsh
57218753be [ty] Narrow TypedDict literal access in match statements (#22299)
## Summary

Closes https://github.com/astral-sh/ty/issues/2279.
2025-12-30 11:29:09 -05:00
RasmusNygren
0edd97dd41 [ty] Add autocomplete suggestions for class arguments (#22110) 2025-12-30 13:10:56 +00:00
Charlie Marsh
b925ae5061 [ty] Avoid including property in subclasses properties (#22088)
## Summary

As-is, the following rejects `return self.value` in `def other` in the
subclass
([link](https://play.ty.dev/f55b47b2-313e-45d1-ba45-fde410bed32e))
because `self.value` is resolving to `Unknown | int | float | property`:

```python
class Base:
    _value: float = 0.0

    @property
    def value(self) -> float:
        return self._value

    @value.setter
    def value(self, v: float) -> None:
        self._value = v

    @property
    def other(self) -> float:
        return self.value

    @other.setter
    def other(self, v: float) -> None:
        self.value = v

class Derived(Base):
    @property
    def other(self) -> float:
        return self.value

    @other.setter
    def other(self, v: float) -> None:
        reveal_type(self.value)  # revealed: int | float
        self.value = v
```

I believe the root cause is that we're not excluding properties when
searching for class methods, so we're treating the `other` setter as a
classmethod. I don't fully understand how that ends up materializing as
`| property` on the union though.
2025-12-30 03:28:03 +00:00
Charlie Marsh
9333f15433 [ty] Fix match exhaustiveness for enum | None unions (#22290)
## Summary

If we match on an `TestEnum | None`, then when adding a case like
`~Literal[TestEnum.FOO]` (i.e., after `if value == TestEnum.FOO:
return`), we'd distribute `Literal[TestEnum.BAR]` on the entire builder,
creating `None & Literal[TestEnum.BAR]` which simplified to `Never`.
Instead, we should only expand to the remaining members for pieces of
the intersection that contain the enum.

Now, `(TestEnum | None) & ~Literal[TestEnum.FOO] &
~Literal[TestEnum.BAR]` correctly simplifies to `None` instead of
`Never`.

Closes https://github.com/astral-sh/ty/issues/2260.
2025-12-29 22:19:28 -05:00
Shunsuke Shibayama
c429ef8407 [ty] don't expand type aliases via type mappings unless necessary (#22241)
## Summary

`apply_type_mapping` always expands type aliases and operates on the
resulting types, which can lead to cluttered results due to excessive
type alias expansion in places where it is not actually needed.

Specifically, type aliases are expanded when displaying method
signatures, because we use `TypeMapping::BindSelf` to get the method
signature.

```python
type Scalar = int | float
type Array1d = list[Scalar] | tuple[Scalar]

def f(x: Scalar | Array1d) -> None: pass
reveal_type(f)  # revealed: def f(x: Scalar | Array1d) -> None

class Foo:
    def f(self, x: Scalar | Array1d) -> None: pass
# should be `bound method Foo.f(x: Scalar | Array1d) -> None`
reveal_type(Foo().f)  # revealed: bound method Foo.f(x: int | float | list[int | float] | tuple[int | float]) -> None
```

In this PR, when type mapping is performed on a type alias, the
expansion result without type mapping is compared with the expansion
result after type mapping, and if the two are equivalent, the expansion
is deemed redundant and canceled.

## Test Plan

mdtest updated
2025-12-29 19:02:56 -08:00
Eric Mark Martin
8716b4e230 [ty] implement typing.TypeGuard (#20974)
## Summary

Resolve(s) astral-sh/ty#117, astral-sh/ty#1569

Implement `typing.TypeGuard`. Due to the fact that it [overrides
anything previously known about the checked
value](https://typing.python.org/en/latest/spec/narrowing.html#typeguard)---

> When a conditional statement includes a call to a user-defined type
guard function, and that function returns true, the expression passed as
the first positional argument to the type guard function should be
assumed by a static type checker to take on the type specified in the
TypeGuard return type, unless and until it is further narrowed within
the conditional code block.

---we have to substantially rework the constraints system. In
particular, we make constraints represented as a disjunctive normal form
(DNF) where each term includes a regular constraint, and one or more
disjuncts with a typeguard constraint. Some test cases (including some
with more complex boolean logic) are added to `type_guards.md`.


## Test Plan

- update existing tests
- add new tests for more complex boolean logic with `TypeGuard`
- add new tests for `TypeGuard` variance

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-12-29 17:54:17 -08:00
Charlie Marsh
3d8ae2e476 [ty] Avoid showing misleading hint for unpacked tuple arguments (#22286)
## Summary

We could implement support for showing multiple argument names, though
this seems to match PyCharm.

Closes https://github.com/astral-sh/ty/issues/2250.
2025-12-29 13:25:08 -05:00
renovate[bot]
a737a56c53 Move quickcheck dependeny pins to workspace Cargo.toml (#22247) 2025-12-29 17:12:31 +00:00
renovate[bot]
a1c3f16358 Update pre-commit dependencies (#22281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-29 17:11:28 +00:00
Alex Waygood
fbb5c8aa3c [ty] Fix LiteralString import in ty_extensions.pyi (#22244) 2025-12-29 14:59:19 +00:00
Matthew Mckee
fde33baaa5 [ty] Make the implicit shadowing message on invalid assignment diagnostic info (#22219) 2025-12-29 10:30:48 +01:00
Micha Reiser
6776543b62 [ty] Reduce tracing level for constriant.rs logs (#22239) 2025-12-29 09:22:17 +01:00
Carl Meyer
4c4e652b38 [ty] callable type of a type object is not function-like (#22226) 2025-12-28 11:24:45 -08:00
Micha Reiser
d5c39d3f9f [ty] Fix property-tests (#22229) 2025-12-28 09:58:48 +01:00
Carl Meyer
55c8707be6 [ty] fix display of top ParamSpec specialization (#22227)
## Summary

I only noticed this in the ecosystem report of
https://github.com/astral-sh/ruff/pull/22213 after merging it. The
change to displaying `Top[]` wrapper around the entire signature instead
of just the parameters had the side effect of not showing it at all when
displaying a top ParamSpec specialization. This PR fixes that.

Marking internal since this is a fixup of a not-released PR.

## Test Plan

Added mdtest that fails without this PR.
2025-12-27 11:14:22 -08:00
Carl Meyer
6d5fb09e92 [ty] fix and simplify callable type materializations (#22213)
## Summary

A couple things I noticed when taking another look at the callable type
materializations.

1) Previously we wrongly ignored the return type when
bottom-materializing a callable with gradual signature, and always
changed it to `Never`.
2) We weren't correctly handling overloads that included a gradual
signature. Rather than separately materializing each overload, we would
just mark the entire callable as "top" or replace the entire callable
with the bottom signature.

Really, "top parameters" is something that belongs on the `Parameters`,
not on the entire `CallableType`. Conveniently, we already have
`ParametersKind` where we can track this, right next to where we already
track `ParametersKind::Gradual`. This saves a bit of memory, fixes the
two bugs above, and simplifies the implementation considerably (net
removal of 100+ LOC, a bunch of places that shouldn't need to care about
topness of a callable no longer need to.)

One user-visible change from this is that I now display the "top
callable" as `(Top[...]) -> object` instead of `Top[(...) -> object]`. I
think this is a (minor) improvement, because it wraps exactly the part
in `Top` that needs to be, rather than misleadingly wrapping the entire
callable type, including the return type (which has already been
separately materialized). I think the prior display would be
particularly confusing if the return type also has its own `Top` in it:
previously we could have e.g. `Top[(...) -> Top[list[Unknown]]]`, which
I think is less clear than the new `(Top[...]) -> Top[list[Unknown]]`.

## Test Plan

Added mdtests that failed before this PR and pass after it.

### Ecosystem

The changed diagnostics are all either the change to `Top` display, or
else known non-deterministic output. The added diagnostics are all true
positives:

The added diagnostic at
aa35ca1965/torchvision/transforms/v2/_utils.py (L149)
is a true positive that wasn't caught by the previous version. `str` is
not assignable to `Callable[[Any], Any]` (strings are not callable), nor
is the top callable (top callable includes callables that do not take a
single required positional argument.)

The added diagnostic at
081535ad9b/starlette/routing.py (L67)
is also a (pedantic) true positive. It's the same case as #1567 -- the
code assumes that it is impossible for a subclass of `Response` to
implement `__await__` (yielding something other than a `Response`).

The pytest added diagnostics are also both similar true positives: they
make the assumption that an object cannot simultaneously be a `Sequence`
and callable, or an `Iterable` and callable.
2025-12-27 10:45:07 -08:00
Micha Reiser
fffd3e5cfb [ty] Re-use vec when building a VariableLengthTypeVarTuple with the builder (#22225) 2025-12-27 17:06:30 +01:00
Alex Waygood
7290bdc41e [ty] Use a length-2 array for UnionTypeInstance::_value_expr_types (#22222) 2025-12-27 11:47:46 +00:00
Simon Lamon
c032e27566 [ty] Rename non-subscriptable error code to not-subscriptable (#22193) 2025-12-27 11:44:35 +00:00
Micha Reiser
da188d5cf6 [ty] Reduce monomorphization in add_binding (#22196) 2025-12-27 11:17:27 +01:00
Micha Reiser
5d32ab8175 [ty] Return slices for Tuple methods (#22192) 2025-12-27 10:30:34 +01:00
Matthew Mckee
6342cec842 [ty] Promote float and complex when promoting literals (#22215)
## Summary

Resolve https://github.com/astral-sh/ty/issues/2226

We need to add a special case in `apply_type_mapping` instead of
directly in `promote_literals_impl` because we do not reach this with
non generic non tuple nominal instances. We still ensure we apply the
normal mapping if we do not see `float` or `complex` instances.

## Test Plan

Update existing mdtest and add a new case to `literal_promotion.md`
2025-12-26 16:19:23 -08:00
Micha Reiser
9693375e10 [ty] Reduce monomorphization (#22195) 2025-12-26 10:02:20 +01:00
Simon Lamon
dd3a985109 [ty] Spell out "method resolution order" in unsupported-base subdiagnostic (#22194) 2025-12-25 10:26:44 +00:00
Micha Reiser
12f5ea51e3 [ty] Invert dependencies of ty_combine and ty_python_semantic (#22191) 2025-12-25 10:06:06 +01:00
Alex Waygood
f9afcc400c [ty] Improve diagnostic when a user tries to access a function attribute on a Callable type (#22182)
## Summary

Other type checkers allow you to access all `FunctionType` attributes on
any object with a `Callable` type. ty does not, because this is
demonstrably unsound, but this is often a source of confusion for users.
And there were lots of diagnostics in the ecosystem report for
https://github.com/astral-sh/ruff/pull/22145 that were complaining that
"Object of type `(...) -> Unknown` has no attribute `__name__`", for
example.

The discrepancy between what ty does here and what other type checkers
do is discussed a bit in https://github.com/astral-sh/ty/issues/1495.
You can see that there have been lots of issues closed as duplicates of
that issue; we should probably also add an FAQ entry for it.

Anyway, this PR adds a subdiagnostic to help users out when they hit
this diagnostic. Unfortunately something I did meant that rustfmt
increased the indentation of the whole of this huge closure, so this PR
is best reviewed with the "No whitespace" option selected for viewing
the diff.

## Test Plan

Snapshot added
2025-12-24 15:47:11 -05:00
Alex Waygood
768c5a2285 [ty] Use Type::string_literal() more (#22184) 2025-12-24 20:06:57 +00:00
Alex Waygood
139149f87b [ty] Improve diagnostic when callable is used in a type expression instead of collections.abc.Callable or typing.Callable (#22180) 2025-12-24 19:18:51 +00:00
Charlie Marsh
2de4464e92 [ty] Fix implementation of Top[Callable[..., object]] (#22145)
## Summary

Add a proper representation for the `Callable` top type, and use it to
get `callable()` narrowing right.

Closes https://github.com/astral-sh/ty/issues/1426.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-12-24 12:49:09 -05:00
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
Wizzerinus | Alex K.
d9fe996e64 [ty] Support custom builtins (#22021) 2025-12-23 13:48:14 +00:00
Micha Reiser
ccc9132f73 [ty] Use ModuleName::new_static in more places (#22156) 2025-12-23 09:24:49 +01:00