Commit Graph

13206 Commits

Author SHA1 Message Date
David Peter 2acf1cc0fd
[ty] Infer precise types for `isinstance(…)` calls involving typevars (#21999)
## Summary

Infer `Literal[True]` for `isinstance(x, C)` calls when `x: T` and `T`
has a bound `B` that satisfies the `isinstance` check against `C`.
Similar for constrained typevars.

closes https://github.com/astral-sh/ty/issues/1895

## Test Plan

* New Markdown tests
* Verified the the example in the linked ticket checks without errors
2025-12-16 10:34:30 +01:00
Micha Reiser 4fdbe26445
[ty] Use `FxHashMap` in `Signature::has_relation_to` (#21997) 2025-12-16 10:10:45 +01:00
Charlie Marsh 682d29c256
[ty] Avoid enforcing standalone expression for tests in f-strings (#21967)
## Summary

Based on what we do elsewhere and my understanding of "standalone"
here...

Closes https://github.com/astral-sh/ty/issues/1865.
2025-12-15 22:31:04 -05:00
Zanie Blue 8e13765b57
[ty] Use `title` for configuration code fences in ty reference documentation (#21992)
Part of https://github.com/astral-sh/ty/pull/1904
2025-12-15 16:36:08 -05:00
Douglas Creager 7d3b7c5754
[ty] Consistent ordering of constraint set specializations, take 2 (#21983)
In https://github.com/astral-sh/ruff/pull/21957, we tried to use
`union_or_intersection_elements_ordering` to provide a stable ordering
of the union and intersection elements that are created when determining
which type a typevar should specialize to. @AlexWaygood [pointed
out](https://github.com/astral-sh/ruff/pull/21551#discussion_r2616543762)
that this won't work, since that provides a consistent ordering within a
single process run, but does not provide a stable ordering across runs.

This is an attempt to produce a proper stable ordering for constraint
sets, so that we end up with consistent diagnostic and test output.

We do this by maintaining a new `source_order` field on each interior
BDD node, which records when that node's constraint was added to the
set. Several of the BDD operators (`and`, `or`, etc) now have
`_with_offset` variants, which update each `source_order` in the rhs to
be larger than any of the `source_order`s in the lhs. This is what
causes that field to be in line with (a) when you add each constraint to
the set, and (b) the order of the parameters you provide to `and`, `or`,
etc. Then we sort by that new field before constructing the
union/intersection types when creating a specialization.
2025-12-15 14:24:08 -05:00
RasmusNygren d6a5bbd91c
[ty] Remove invalid statement-keyword completions in for-statements (#21979)
In `for x in <CURSOR>` statements it's only valid to provide expressions
that eventually evaluate to an iterable. While it's extremely difficult
to know if something can evaulate to an iterable in a general case,
there are some suggestions we know can never lead to an iterable. Most
keywords are such and hence we remove them here.

## Summary
This suppresses statement-keywords from auto-complete suggestions in
`for x in <CURSOR>` statements where we know they can never be valid, as
whatever is typed has to (at some point) evaluate to an iterable.

It handles the core issue from
https://github.com/astral-sh/ty/issues/1774 but there's a lot of related
cases that probably has to be handled piece-wise.

## Test Plan
New tests and verifying in the playground.
2025-12-15 12:56:34 -05:00
Micha Reiser 1df6544ad8
[ty] Avoid caching trivial is-redundant-with calls (#21989) 2025-12-15 18:45:03 +01:00
Dylan 4e1cf5747a
Fluent formatting of method chains (#21369)
This PR implements a modification (in preview) to fluent formatting for
method chains: We break _at_ the first call instead of _after_.

For example, we have the following diff between `main` and this PR (with
`line-length=8` so I don't have to stretch out the text):

```diff
 x = (
-    df.merge()
+    df
+    .merge()
     .groupby()
     .agg()
     .filter()
 )
```

## Explanation of current implementation

Recall that we traverse the AST to apply formatting. A method chain,
while read left-to-right, is stored in the AST "in reverse". So if we
start with something like

```python
a.b.c.d().e.f()
```

then the first syntax node we meet is essentially `.f()`. So we have to
peek ahead. And we actually _already_ do this in our current fluent
formatting logic: we peek ahead to count how many calls we have in the
chain to see whether we should be using fluent formatting or now.

In this implementation, we actually _record_ this number inside the enum
for `CallChainLayout`. That is, we make the variant `Fluent` hold an
`AttributeState`. This state can either be:

- The number of call-like attributes preceding the current attribute
- The state `FirstCallOrSubscript` which means we are at the first
call-like attribute in the chain (reading from left to right)
- The state `BeforeFirstCallOrSubscript` which means we are in the
"first group" of attributes, preceding that first call.

In our example, here's what it looks like at each attribute:

```
a.b.c.d().e.f @ Fluent(CallsOrSubscriptsPreceding(1))
a.b.c.d().e @ Fluent(CallsOrSubscriptsPreceding(1))
a.b.c.d @ Fluent(FirstCallOrSubscript)
a.b.c @ Fluent(BeforeFirstCallOrSubscript)
a.b @ Fluent(BeforeFirstCallOrSubscript)
```

Now, as we descend down from the parent expression, we pass along this
little piece of state and modify it as we go to track where we are. This
state doesn't do anything except when we are in `FirstCallOrSubscript`,
in which case we add a soft line break.

Closes #8598

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-12-15 09:29:50 -06:00
Douglas Creager cbfecfaf41
[ty] Avoid stack overflow when calculating inferable typevars (#21971)
When we calculate which typevars are inferable in a generic context, the
result might include more than the typevars bound by the generic
context. The canonical example is a generic method of a generic class:

```py
class C[A]:
    def method[T](self, t: T): ...
```

Here, the inferable typevar set of `method` contains `Self` and `T`, as
you'd expect. (Those are the typevars bound by the method.) But it also
contains `A@C`, since the implicit `Self` typevar is defined as `Self:
C[A]`. That means when we call `method`, we need to mark `A@C` as
inferable, so that we can determine the correct mapping for `A@C` at the
call site.

Fixes https://github.com/astral-sh/ty/issues/1874
2025-12-15 10:25:33 -05:00
Aria Desires 8f530a7ab0
[ty] Add "qualify ..." code fix for undefined references (#21968)
## Summary

If `import warnings` exists in the file, we will suggest an edit of
`deprecated -> warnings.deprecated` as "qualify warnings.deprecated"

## Test Plan

Should test more cases...
2025-12-15 10:14:36 -05:00
Micha Reiser 5372bb3440
[ty] Use jemalloc on linux (#21975)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-15 16:04:34 +01:00
Micha Reiser d08e414179
Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
Alex Waygood 0b918ae4d5
[ty] Improve check enforcing that an overloaded function must have an implementation (#21978)
## Summary

- Treat `if TYPE_CHECKING` blocks the same as stub files (the feature
requested in https://github.com/astral-sh/ty/issues/1216)
- We currently only allow `@abstractmethod`-decorated methods to omit
the implementation if they're methods in classes that have _exactly_
`ABCMeta` as their metaclass. That seems wrong -- `@abstractmethod` has
the same semantics if a class has a subclass of `ABCMeta` as its
metaclass. This PR fixes that too. (I'm actually not _totally_ sure we
should care what the class's metaclass is at all -- see discussion in
https://github.com/astral-sh/ty/issues/1877#issue-3725937441... but the
change this PR is making seems less wrong than what we have currently,
anyway.)

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

## Test Plan

Mdtests and snapshots
2025-12-15 08:56:35 +00:00
renovate[bot] 9838f81baf
Update actions/checkout digest to 8e8c483 (#21982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-15 06:52:52 +00:00
Dhruv Manilawala ba47349c2e
[ty] Use `ParamSpec` without the attr for inferable check (#21934)
## Summary

fixes: https://github.com/astral-sh/ty/issues/1820

## Test Plan

Add new mdtests.

Ecosystem changes removes all false positives.
2025-12-15 11:04:28 +05:30
Bhuminjay Soni 04f9949711
[ty] Emit diagnostic when a type variable with a default is followed by one without a default (#21787)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-12-14 19:35:37 +00:00
Leandro Braga 8bc753b842
[ty] Fix callout syntax in configuration mkdocs (#1875) (#21961) 2025-12-14 10:21:54 +01:00
Peter Law c7eea1f2e3
Update debug_assert which pointed at missing method (#21969)
## Summary

I assume that the class has been renamed or split since this assertion
was created.

## Test Plan

Compiled locally, nothing more. Relying on CI given the triviality of
this change.
2025-12-13 17:56:59 -05:00
Charlie Marsh be8eb92946
[ty] Add support for `__qualname__` and other implicit class attributes (#21966)
## Summary

Closes https://github.com/astral-sh/ty/issues/1873
2025-12-13 17:10:25 -05:00
Simon Lamon a544c59186
[ty] Emit a diagnostic when frozen dataclass inherits a non-frozen dataclass and the other way around (#21962)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-12-13 20:59:26 +00:00
Alex Waygood bb464ed924
[ty] Use unqualified names for displays of `TypeAliasType`s and unbound `ParamSpec`s/`TypeVar`s (#21960) 2025-12-13 20:23:16 +00:00
Alex Waygood f57917becd
fix typo in `fuzz/README.md` (#21963) 2025-12-13 18:21:46 +00:00
David Peter 82a7598aa8
[ty] Remove now-unnecessary Divergent check (#21935)
## Summary

This check is not necessary thanks to
https://github.com/astral-sh/ruff/pull/21906.
2025-12-13 16:32:09 +01:00
Micha Reiser e2ec2bc306
Use datatest for formatter tests (#21933) 2025-12-13 08:02:22 +00:00
Douglas Creager b413a6dec4
[ty] Allow gradual lower/upper bounds in a constraint set (#21957)
We now allow the lower and upper bounds of a constraint to be gradual.
Before, we would take the top/bottom materializations of the bounds.
This required us to pass in whether the constraint was intended for a
subtyping check or an assignability check, since that would control
whether we took the "restrictive" or "permissive" materializations,
respectively.

Unfortunately, doing so means that we lost information about whether the
original query involves a non-fully-static type. This would cause us to
create specializations like `T = object` for the constraint `T ≤ Any`,
when it would be nicer to carry through the gradual type and produce `T
= Any`.

We're not currently using constraint sets for subtyping checks, nor are
we going to in the very near future. So for now, we're going to assume
that constraint sets are always used for assignability checks, and allow
the lower/upper bounds to not be fully static. Once we get to the point
where we need to use constraint sets for subtyping checks, we will
consider how best to record this information in constraints.
2025-12-12 22:18:30 -05:00
Shunsuke Shibayama e19c050386
[ty] disallow explicit specialization of type variables themselves (#21938)
## Summary

This PR makes explicit specialization of a type variable itself an
error, and the result of the specialization is `Unknown`.

The change also fixes https://github.com/astral-sh/ty/issues/1794.

## Test Plan

mdtests updated
new corpus test

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-12-12 15:49:20 -08:00
Alex Waygood 5a2aba237b
[ty] Improve diagnostics for unsupported binary operations and unsupported augmented assignments (#21947)
## Summary

This PR takes the improvements we made to unsupported-comparison
diagnostics in https://github.com/astral-sh/ruff/pull/21737, and extends
them to other `unsupported-operator` diagnostics.

## Test Plan

Mdtests and snapshots
2025-12-12 21:53:29 +00:00
Aria Desires ca5f099481
[ty] update implicit root docs (#21955)
## Summary

./tests is now no longer an implicit root, per
https://github.com/astral-sh/ruff/pull/21817
2025-12-12 16:30:23 -05:00
Alex Waygood a722df6a73
[ty] Enable even more goto-definition on inlay hints (#21950)
## Summary

Working on py-fuzzer recently (AKA, a Python project!) reminded me how
cool our "inlay hint goto-definition feature" is. So this PR adds a
bunch more of that!

I also made a couple of other minor changes to type display. For
example, in the playground, this snippet:

```py
def f(): ...
reveal_type(f.__get__)
```

currently leads to this diagnostic:

```
Revealed type: `<method-wrapper `__get__` of `f`>` (revealed-type) [Ln 2, Col 13]
```

But the fact that we have backticks both around the type display and
inside the type display isn't _great_ there. This PR changes it to

```
Revealed type: `<method-wrapper '__get__' of function 'f'>` (revealed-type) [Ln 2, Col 13]
```

which avoids the nested-backticks issue in diagnostics, and is more
similar to our display for various other `Type` variants such as
class-literal types (`<class 'Foo'>`, etc., not ``<class `Foo`>``).

## Test Plan

inlay snapshots added; mdtests updated
2025-12-12 12:57:38 -05:00
Brent Westbrook dec4154c8a
Document known lambda formatting deviations from Black (#21954)
Summary
--

Following #8179, we now format long lambda expressions a bit more like
Black, preferring to keep long parameter lists on a single line, but we
go one step further to break the body itself across multiple lines and
parenthesize it if it's still too long. This PR documents both the
stable deviation that breaks parameters across multiple lines, and the
new preview deviation that breaks the body instead.

I also fixed a couple of typos in the section immediately above my
addition.

Test Plan
--

I tested all of the snippets here against `main` for the preview
behavior, our playground for the stable behavior, and Black's playground
for their behavior
2025-12-12 12:57:09 -05:00
Carl Meyer 69d1bfbebc
[ty] fix hover type on named expression target (#21952)
## Summary

What it says on the tin.

## Test Plan

Added hover test.
2025-12-12 09:30:50 -08:00
Micha Reiser 90b29c9e87
Bump benchmark dependencies (#21951) 2025-12-12 17:05:57 +00:00
Brent Westbrook 0ebdebddd8
Keep lambda parameters on one line and parenthesize the body if it expands (#21385)
## Summary

This PR makes two changes to our formatting of `lambda` expressions:
1. We now parenthesize the body expression if it expands
2. We now try to keep the parameters on a single line

The latter of these fixes #8179:

Black formatting and this PR's formatting:

```py
def a():
    return b(
        c,
        d,
        e,
        f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
            *args, **kwargs
        ),
    )
```

Stable Ruff formatting

```py
def a():
    return b(
        c,
        d,
        e,
        f=lambda self,
        *args,
        **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs),
    )
```

We don't parenthesize the body expression here because the call to
`aaaa...` has its own parentheses, but adding a binary operator shows
the new parenthesization:

```diff
@@ -3,7 +3,7 @@
         c,
         d,
         e,
-        f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
-            *args, **kwargs
-        ) + 1,
+        f=lambda self, *args, **kwargs: (
+            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1
+        ),
     )
```

This is actually a new divergence from Black, which formats this input
like this:

```py
def a():
    return b(
        c,
        d,
        e,
        f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
            *args, **kwargs
        )
        + 1,
    )
```

But I think this is an improvement, unlike the case from #8179.

One other, smaller benefit is that because we now add parentheses to
lambda bodies, we also remove redundant parentheses:

```diff
 @pytest.mark.parametrize(
     "f",
     [
-        lambda x: (x.expanding(min_periods=5).cov(x, pairwise=True)),
-        lambda x: (x.expanding(min_periods=5).corr(x, pairwise=True)),
+        lambda x: x.expanding(min_periods=5).cov(x, pairwise=True),
+        lambda x: x.expanding(min_periods=5).corr(x, pairwise=True),
     ],
 )
 def test_moment_functions_zero_length_pairwise(f):
```

## Test Plan

New tests taken from #8465 and probably a few more I should grab from
the ecosystem results.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-12 12:02:25 -05:00
Aria Desires d5546508cf
[ty] Improve resolution of absolute imports in tests (#21817)
By teaching desperate resolution to try every possible ancestor that
doesn't have an `__init__.py(i)` when resolving absolute imports.

* Fixes https://github.com/astral-sh/ty/issues/1782
2025-12-12 11:59:06 -05:00
Andrew Gallant 3ac58b47bd [ty] Support `__all__ += submodule.__all__`
... and also `__all__.extend(submodule.__all__)`.

I originally left out support for this since I was unclear on whether
we'd really need it. But it turns out this is used somewhat frequently.
For example, in `numpy`.

See the comments on the new `Imports` type for how we approach this.
2025-12-12 10:11:04 -05:00
Andrew Gallant a2b138e789 [ty] Change frequency of invalid `__all__` debug message
This was being emitted for every symbol we checked, which
is clearly too frequent. This switches to emitting it once
per module.
2025-12-12 10:11:04 -05:00
Alex Waygood ff0ed4e752
[ty] Add `KnownUnion::to_type()` (#21948) 2025-12-12 14:06:35 +00:00
Micha Reiser bc8efa2fd8
[ty] Classify `cls` as class parameter (#21944) 2025-12-12 13:54:37 +01:00
Micha Reiser 4249736d74
[ty] Stabilize rename (#21940) 2025-12-12 13:52:47 +01:00
Andrew Gallant 0181568fb5 [ty] Ignore `__all__` for document and workspace symbol requests
We also ignore names introduced by import statements, which seems to
match pylance behavior.

Fixes astral-sh/ty#1856
2025-12-12 07:29:29 -05:00
Micha Reiser 8cc7c993de
[ty] Attach db to background request handler task (#21941) 2025-12-12 11:31:13 +00:00
Micha Reiser 315bf80eed
[ty] Fix outdated version in publish diagnostics after `didChange` (#21943) 2025-12-12 11:30:56 +00:00
Carl Meyer 0138cd238a
[ty] avoid fixpoint unioning of types containing current-cycle Divergent (#21910)
Partially addresses https://github.com/astral-sh/ty/issues/1732

## Summary

Don't union the previous type in fixpoint iteration if the previous type
contains a `Divergent` from the current cycle and the latest type does
not. The theory here, as outlined by @mtshiba at
https://github.com/astral-sh/ty/issues/1732#issuecomment-3609937420, is
that oscillation can't occur by removing and then reintroducing a
`Divergent` type repeatedly, since `Divergent` types are only introduced
at the start of fixpoint iteration.

## Test Plan

Removes a `Divergent` type from the added mdtest, doesn't otherwise
regress any tests.
2025-12-11 19:52:34 -08:00
Shunsuke Shibayama 5e42926eee
[ty] improve bad specialization results & error messages (#21840)
## Summary

This PR includes the following changes:

* When attempting to specialize a non-generic type (or a type that is
already specialized), the result is `Unknown`. Also, the error message
is improved.
* When an implicit type alias is incorrectly specialized, the result is
`Unknown`. Also, the error message is improved.
* When only some of the type alias bounds and constraints are not
satisfied, not all substitutions are `Unknown`.
* Double specialization is prohibited. e.g. `G[int][int]`

Furthermore, after applying this PR, the fuzzing tests for seeds 1052
and 4419, which panic in main, now pass.
This is because the false recursions on type variables have been
removed.

```python
# name_2[0] => Unknown
class name_1[name_2: name_2[0]]:
    def name_4(name_3: name_2, /):
        if name_3:
            pass

#  (name_5 if unique_name_0 else name_1)[0] => Unknown
def name_4[name_5: (name_5 if unique_name_0 else name_1)[0], **name_1](): ...
```

## Test Plan

New corpus test
mdtest files updated
2025-12-11 19:21:34 -08:00
Jack O'Connor ddb7645e9d
[ty] support `NewType`s of `float` and `complex` (#21886)
Fixes https://github.com/astral-sh/ty/issues/1818.
2025-12-12 00:43:09 +00:00
Amethyst Reese 3f63ea4b50
Prepare 0.14.9 release (#21927)
- **Changelog and docs**
- **metadata**
2025-12-11 13:17:52 -08:00
Douglas Creager c8851ecf70
[ty] Defer all parameter and return type annotations (#21906)
As described in astral-sh/ty#1729, we previously had a salsa cycle when
inferring the signature of many function definitions.

The most obvious case happened when (a) the function was decorated, (b)
it had no PEP-695 type params, and (c) annotations were not always
deferred (e.g. in a stub file). We currently evaluate and apply function
decorators eagerly, as part of `infer_function_definition`. Applying a
decorator requires knowing the signature of the function being
decorated. There were two places where signature construction called
`infer_definition_types` cyclically.

The simpler case was that we were looking up the generic context and
decorator list of the function to determine whether it has an implicit
`self` parameter. Before, we used `infer_definition_types` to determine
that information. But since we're in the middle of signature
construction for the function, we can just thread the information
through directly.

The harder case is that signature construction requires knowing the
inferred parameter and return type annotations. When (b) and (c) hold,
those type annotations are inferred in `infer_function_definition`! (In
theory, we've already finished that by the time we start applying
decorators, but signature construction doesn't know that.)

If annotations are deferred, the params/return annotations are inferred
in `infer_deferred_types`; if there are PEP-695 type params, they're
inferred in `infer_function_type_params`. Both of those are different
salsa queries, and don't induce this cycle.

So the quick fix here is to always defer inference of the function
params/return, so that they are always inferred under a different salsa
query.

A more principled fix would be to apply decorators lazily, just like we
construct signatures lazily. But that is a more invasive fix.

Fixes astral-sh/ty#1729

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-12-11 15:00:18 -05:00
Micha Reiser d442433e93
[ty] Fix workspace symbols to return members too (#21926) 2025-12-11 20:22:21 +01:00
Amethyst Reese c055d665ef
Document range suppressions, reorganize suppression docs (#21884)
- **Reorganize suppression documentation, document range suppressions**
- **Note preview mode requirement**

Issue #21874, #3711
2025-12-11 11:16:36 -08:00
Amethyst Reese 7a578ce833
Ignore ruff:isort like ruff:noqa in new suppressions (#21922)
## Summary

Ignores `#ruff:isort` when parsing suppressions similar to `#ruff:noqa`.
Should clear up ecosystem issues in #21908

## Test Plan

cargo tests
2025-12-11 11:04:28 -08:00