Compare commits

...

1030 Commits

Author SHA1 Message Date
Alex Waygood
b549d6d47c [ty] Add from imports to imported_modules *if* the module being imported is not relative to the current module 2025-11-22 15:32:06 +00:00
Alex Waygood
3410041b4c [ty] Improve diagnostics when a submodule is not available as an attribute on a module-literal type (#21561) 2025-11-22 14:07:48 +00:00
Alex Waygood
f2ce5e561a [ty] Improve concise diagnostics for invalid exceptions when a user catches a tuple of objects (#21578) 2025-11-22 13:46:46 +00:00
Carl Meyer
f495c6d4ae [ty] upgrade salsa (#21575) 2025-11-22 11:46:57 +01:00
Aria Desires
768bb24cdf [ty] make implicit submodule imports re-exported (#21573)
Thus they work in `.pyi` files

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

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-11-21 17:42:11 -08:00
Prakhar Pratyush
492d676736 [flake8-implicit-str-concat] Avoid invalid fix generated by autofix (ISC003) (#21517)
## Summary

As reported in #19757:
While attempting ISC003 autofix for an expression with explicit string
concatenation, with either operand being a string literal that wraps
across multiple lines (in parentheses) - it resulted in generating a fix
which caused runtime error.

Example:
```
_ = "abc" + (
    "def"
    "ghi"
)
```
was being auto-fixed to:
```
_ = "abc" (
    "def"
    "ghi"
)
```
which raised `TypeError: 'str' object is not callable`

This commit makes changes to just report diagnostic - no autofix in such
cases.

Fixes #19757.

## Test Plan
Added example scenarios in
`crates/ruff_linter/resources/test/fixtures/flake8_implicit_str_concat/ISC.py`.

Signed-off-by: Prakhar Pratyush <prakhar1144@gmail.com>
2025-11-21 17:22:35 -08:00
Dan Parizher
ddc1417f22 [pylint] Fix suppression for empty dict without tuple key annotation (PLE1141) (#21290)
## Summary

Fixes the PLE1141 (`dict-iter-missing-items`) rule to allow fixes for
empty dictionaries unless they have type annotations indicating 2-tuple
keys. Previously, the fix was incorrectly suppressed for all empty dicts
due to vacuous truth in the `all()` function.

Fixes #21289

## Problem Analysis

The `is_dict_key_tuple_with_two_elements` function was designed to
suppress the fix when a dictionary's keys are all 2-tuples, as unpacking
tuple keys directly would change runtime behavior.

However, for empty dictionaries, `iter_keys()` returns an empty
iterator, and `all()` on an empty iterator returns `true` (vacuous
truth). This caused the function to incorrectly suppress fixes for empty
dicts, even when there was no indication that future keys would be
2-tuples.

## Approach

1. **Detect empty dictionaries**: Added a check to identify when a dict
literal has no keys.

2. **Handle annotated empty dicts**: For empty dicts with type
annotations:
- Parse the annotation to check if it's `dict[tuple[T1, T2], ...]` where
the tuple has exactly 2 elements
- Support both PEP 484 (`typing.Dict`, `typing.Tuple`) and PEP 585
(`dict`, `tuple`) syntax
   - If tuple keys are detected, suppress the fix (correct behavior)
   - Otherwise, allow the fix

3. **Handle unannotated empty dicts**: For empty dicts without
annotations, allow the fix since there's no indication that keys will be
2-tuples.

4. **Preserve existing behavior**: For non-empty dicts, the original
logic is unchanged - check if all existing keys are 2-tuples.

The implementation includes helper functions:
- `is_annotation_dict_with_tuple_keys()`: Checks if a type annotation
specifies dict with tuple keys
- `is_tuple_type_with_two_elements()`: Checks if a type expression
represents a 2-tuple

Test cases were added to verify:
- Empty dict without annotation triggers the error
- Empty dict with `dict[tuple[int, str], bool]` suppresses the error
- Empty dict with `dict[str, int]` triggers the error
- Existing tests remain unchanged

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-21 22:07:18 +00:00
Ibraheem Ahmed
040aa7463b [ty] Narrow type context during literal promotion in generic class constructors (#21574)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1603.
2025-11-21 17:05:32 -05:00
chiri
09d457aa52 [pylint] Fix PLR1708 false positives on nested functions (#21177)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/21162

## Test Plan

`cargo nextest run pylint`
2025-11-21 15:41:22 -05:00
Andrew Gallant
438ef334d3 [ty] Fix subtraction overflow bug
PR #21549 introduced a subtle overflow bug that seemed impossible, but
can empirically happen. This PR fixes it by saturating to zero.

I did try to write a regression test for this, but couldn't manage it.
Instead, I'll attach before-and-after screen recordings.
2025-11-21 15:07:37 -05:00
Douglas Creager
6cc502781f [ty] Remove brittle constraint set reveal tests (#21568)
These were added to try to make it clearer that assignability checks
will eventually return more detailed answers than true or false.
However, the constraint set display rendering is still more brittle than
I'd like it to be, and it's more trouble than it's worth to keep them
updated with semantically identically but textually different edits. The
`static_assert`s are sufficient to check correctness, and we can always
add `reveal_type` when needed for further debugging.
2025-11-21 13:57:55 -05:00
Mikko Leppänen
e2a1d1a8eb [ruff] Catch more dummy variable uses (RUF052) (#19799)
## Summary

Extends the `used-dummy-variable` rule
([RUF052](https://docs.astral.sh/ruff/rules/used-dummy-variable/)) to
detect dummy variables that are used within list comprehensions, dict
comprehensions, set comprehensions, and generator expressions, not just
regular for loops and function assignments.

### Problem

Previously, RUF052 only flagged dummy variables (variables with leading
underscores) that were used in function scopes via assignments or
regular for loops. It missed cases where dummy variables were used
within comprehensions:

```python
def example():
    my_list = [{"foo": 1}, {"foo": 2}]
    
    # These were not detected before:
    [_item["foo"] for _item in my_list]  # Should warn: _item is used
    {_item["key"]: _item["val"] for _item in my_list}  # Should warn: _item is used
    (_item["foo"] for _item in my_list)  # Should warn: _item is used
```

### Solution

- Extended scope checking to include all generator scopes () with any
(list/dict/set comprehensions and generator expressions)
`ScopeKind::Generator``GeneratorKind`
- Added support for bindings, which cover loop variables in both regular
for loops and comprehensions `BindingKind::LoopVar`
- Refactored the scope validation logic for better readability with a
descriptive variable `is_allowed_scope`



[ISSUE](https://github.com/astral-sh/ruff/issues/19732)

## Test Plan

```bash
cargo test
```

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-21 12:57:02 -05:00
Aria Desires
040b482cf7 [ty] Use the same snapshot handling as other tests (#21564)
Fixes https://github.com/astral-sh/ty/issues/1605
2025-11-21 17:48:01 +00:00
RasmusNygren
03dfbf21eb [ty] suppress autocomplete suggestions during variable binding (#21549)
Statements such as `def foo(p<CURSOR>`,
`def foo[T<CURSOR>` and `for foo<CURSOR>`
should not generate any suggestions as these
cases are introducing new names.

If it's not possible to determine that suggestions should be omitted
using token matching in an easy way, we turn
to traversing the AST to determine the context.

<!--
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

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

It keeps using the existing token matching pattern for the easy cases
(nothing typed and most recent token is a definition token) and
fallbacks to AST traveral for the slightly more difficult cases where
token matching becomes difficult and error prone.

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
New test cases and sanity-checking in the ty playground
<!-- How was it tested? -->
2025-11-21 12:06:46 -05:00
Remo Senekowitsch
e3c78d8203 Set severity for non-rule diagnostics (#21559) 2025-11-21 17:42:35 +01:00
Aria Desires
a9b3caf181 [ty] Add with_type convenience to display code (#21563)
Code is much more readable.
2025-11-21 16:36:22 +00:00
Aria Desires
629258241f [ty] Implement docstring rendering to markdown (#21550)
## Summary

This introduces a very bad and naive
python-docstring-flavoured-reStructuredText to github-flavor-markdown
translator. The main goal is to try to preserve a lot of the formatting
and plaintext, progressively enhance the content when we find things we
know about, and escape the text when we find things that might get
corrupt.

Previously I'd broken this out into rendering each different format, but
with this approach you don't really need to?

## Test Plan

Lots of snapshot tests, also messed around in some random stdlib
modules.
2025-11-21 10:47:38 -05:00
Alex Waygood
762c44527e [ty] Reduce indentation of TypeInferenceBuilder::infer_attribute_load (#21560) 2025-11-21 14:12:39 +00:00
Brent Westbrook
59c6cb521d Bump 0.14.6 (#21558) 2025-11-21 09:00:56 -05:00
Alex Waygood
54dba15088 [ty] Improve debug messages when imports fail (#21555) 2025-11-21 13:45:57 +00:00
Andrew Gallant
1af318534a [ty] Add support for relative import completions
We already supported `from .. import <CURSOR>`, but we didn't support
`from ..<CURSOR>`. This adds support for that.
2025-11-21 08:01:02 -05:00
Andrew Gallant
553e568624 [ty] Refactor detection of import statements for completions
This commit essentially does away of all our old heuristic and piecemeal
code for detecting different kinds of import statements. Instead, we
offer one single state machine that does everything. This on its own
fixes a few bugs. For example, `import collections.abc, unico<CURSOR>`
would previously offer global scope completions instead of module
completions.

For the most part though, this commit is a refactoring that preserves
parity. In the next commit, we'll add support for completions on
relative imports.
2025-11-21 08:01:02 -05:00
Andrew Gallant
cdef3f5ab8 [ty] Use dedicated collector for completions
This is a small refactor that helps centralize the
logic for how we gather, convert and possibly filter
completions.

Some of this logic was spread out before, which
motivated this refactor. Moreover, as part of other
refactoring, I found myself chaffing against the
lack of this abstraction.
2025-11-21 08:01:02 -05:00
Alex Waygood
6178822427 [ty] Attach subdiagnostics to unresolved-import errors for relative imports as well as absolute imports (#21554) 2025-11-21 12:40:53 +00:00
Carl Meyer
6b7adb0537 [ty] support PEP 613 type aliases (#21394)
Refs https://github.com/astral-sh/ty/issues/544

## Summary

Takes a more incremental approach to PEP 613 type alias support (vs
https://github.com/astral-sh/ruff/pull/20107). Instead of eagerly
inferring the RHS of a PEP 613 type alias as a type expression, infer it
as a value expression, just like we do for implicit type aliases, taking
advantage of the same support for e.g. unions and other type special
forms.

The main reason I'm following this path instead of the one in
https://github.com/astral-sh/ruff/pull/20107 is that we've realized that
people do sometimes use PEP 613 type aliases as values, not just as
types (because they are just a normal runtime assignment, unlike PEP 695
type aliases which create an opaque `TypeAliasType`).

This PR doesn't yet provide full support for recursive type aliases
(they don't panic, but they just fall back to `Unknown` at the recursion
point). This is future work.

## Test Plan

Added mdtests.

Many new ecosystem diagnostics, mostly because we
understand new types in lots of places.

Conformance suite changes are correct.

Performance regression is due to understanding lots of new
types; nothing we do in this PR is inherently expensive.
2025-11-20 17:59:35 -08:00
Alex Waygood
06941c1987 [ty] More low-hanging fruit for inlay hint goto-definition (#21548) 2025-11-20 23:15:59 +00:00
Jack O'Connor
eb7c098d6b [ty] implement TypedDict structural assignment (#21467)
Closes https://github.com/astral-sh/ty/issues/1387.
2025-11-20 13:15:28 -08:00
Aria Desires
1b28fc1f14 [ty] Add more random TypeDetails and tests (#21546) 2025-11-20 19:46:17 +00:00
Alex Waygood
290a5720cb [ty] Add goto for Unknown when it appears in an inlay hint (#21545) 2025-11-20 18:55:14 +00:00
Alex Waygood
c4767f5aa8 [ty] Add type definitions for Type::SpecialForms (#21544) 2025-11-20 18:14:30 +00:00
Aria Desires
6e84f4fd7a [ty] Resolve overloads for hovers (#21417)
This is a very conservative minimal implementation of applying overloads
to resolve a callable-type-being-called down to a single function
signature on hover. If we ever encounter a situation where the answer
doesn't simplify down to a single function call, we bail out to preserve
prettier printing of non-raw-Signatures.

The resulting Signatures are still a bit bare, I'm going to try to
improve that in a followup to improve our Signature printing in general.

Fixes https://github.com/astral-sh/ty/issues/73
2025-11-20 12:45:02 -05:00
Aria Desires
78ce17ce8f [ty] Add more TypeDetails to the display code (#21541)
As far as I know this change is largely non-functional, largely because
of https://github.com/astral-sh/ty/issues/1601

It's possible some of these like `Type::KnownInstance` produce something
useful sometimes. `LiteralString` is a new introduction, although its
goto-type jumps to `str` which is a bit sad (considering that part of
the SpecialForm discourse for now).

Also wrt the generics testing followup: turns out the snapshot tests
were full of those already.
2025-11-20 12:08:59 -05:00
David Peter
0761ea42d9 [ty] Eagerly evaluate types.UnionType elements as type expressions (#21531)
## Summary

Eagerly evaluate the elements of a PEP 604 union in value position (e.g.
`IntOrStr = int | str`) as type expressions and store the result (the
corresponding `Type::Union` if all elements are valid type expressions,
or the first encountered `InvalidTypeExpressionError`) on the
`UnionTypeInstance`, such that the `Type::Union(…)` does not need to be
recomputed every time the implicit type alias is used in a type
annotation.

This might lead to performance improvements for large unions, but is
also necessary for correctness, because the elements of the union might
refer to type variables that need to be looked up in the scope of the
type alias, not at the usage site.

## Test Plan

New Markdown tests
2025-11-20 17:28:48 +01:00
Aria Desires
416e2267da [ty] Implement goto-type for inlay type hints (#21533)
This PR generalizes the signature_help system's SignatureWriter which
could get the subspans of function parameters.
We now have TypeDetailsWriter which is threaded between type's display
implementations via a new `fmt_detailed` method that many of the Display
types now have.

With this information we can properly add goto-type targets to our inlay
hints. This also lays groundwork for any future "I want to render a type
but get spans" work.

Also a ton of lifetimes are introduced to avoid things getting conflated
with `'db`.

This PR is broken up into a series of commits:

* Generalizing `SignatureWriter` to `TypeDetailsWriter`, but not using
it anywhere else. This commit was confirmed to be a non-functional
change (no test results changed)
* Introducing `fmt_detailed` everywhere to thread through
`TypeDetailsWriter` and annotate various spans as "being" a given Type
-- this is also where I had to reckon with a ton of erroneous `&'db
self`. This commit was also confirmed to be a non-functional change.
* Finally, actually using the results for goto-type on inlay hints!
* Regenerating snapshots, fixups, etc.
2025-11-20 09:40:40 -05:00
David Peter
02c102da88 [ty] Add tests: types.UnionType in isinstance/issubclass (#21537)
## Summary

Add some tests documenting the fact that we don't support
`types.UnionType` in `isinstance`/`issubclass` at the moment.
2025-11-20 11:59:36 +00:00
David Peter
29c24bc8a6 [ty] Ecosystem analyzer: support custom ty_cmds (#21534)
## Summary

Pulls in
e26ebfb78d
in order to support some projects that now require a custom `ty_cmd`.

## Test Plan

CI on this PR
2025-11-20 09:10:56 +01:00
Douglas Creager
83134fb380 [ty] Handle nested types when creating specializations from constraint sets (#21530)
#21414 added the ability to create a specialization from a constraint
set. It handled mutually constrained typevars just fine, e.g. given `T ≤
int ∧ U = T` we can infer `T = int, U = int`.

But it didn't handle _nested_ constraints correctly, e.g. `T ≤ int ∧ U =
list[T]`. Now we do! This requires doing a fixed-point "apply the
specialization to itself" step to propagate the assignments of any
nested typevars, and then a cycle detection check to make sure we don't
have an infinite expansion in the specialization.

This gets at an interesting nuance in our constraint set structure that
@sharkdp has asked about before. Constraint sets are BDDs, and each
internal node represents an _individual constraint_, of the form `lower
≤ T ≤ upper`. `lower` and `upper` are allowed to be other typevars, but
only if they appear "later" in the arbitary ordering that we establish
over typevars. The main purpose of this is to avoid infinite expansion
for mutually constrained typevars.

However, that restriction doesn't help us here, because only applies
when `lower` and `upper` _are_ typevars, not when they _contain_
typevars. That distinction is important, since it means the restriction
does not affect our expressiveness: we can always rewrite `Never ≤ T ≤
U` (a constraint on `T`) into `T ≤ U ≤ object` (a constraint on `U`).
The same is not true of `Never ≤ T ≤ list[U]` — there is no "inverse" of
`list` that we could apply to both sides to transform this into a
constraint on a bare `U`.
2025-11-19 17:37:16 -05:00
Dan Parizher
0d47334f3b [flake8-bandit] Support new PySNMP API paths (S508, S509) (#21374)
## Summary

Updated `S508` (snmp-insecure-version) and `S509`
(snmp-weak-cryptography) rules to support both old and new PySNMP API
module paths. Previously, these rules only detected the old API path
`pysnmp.hlapi.*`, but now they correctly detect all PySNMP API variants
including `pysnmp.hlapi.asyncio.*`, `pysnmp.hlapi.v1arch.*`,
`pysnmp.hlapi.v3arch.*`, and `pysnmp.hlapi.auth.*`.

Fixes #21364

## Problem Analysis

The `S508` and `S509` rules used exact pattern matching on qualified
names:
- `S509` only matched `["pysnmp", "hlapi", "UsmUserData"]`
- `S508` only matched `["pysnmp", "hlapi", "CommunityData"]`

This meant that newer PySNMP API paths were not detected, such as:
- `pysnmp.hlapi.asyncio.UsmUserData`
- `pysnmp.hlapi.v3arch.asyncio.UsmUserData`
- `pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData`
- `pysnmp.hlapi.auth.UsmUserData`
- Similar variants for `CommunityData` in `S508`

Additionally, the old API path `pysnmp.hlapi.auth.*` was also missing
from both rules.

## Approach

Instead of exact pattern matching, both rules now check if:
1. The qualified name starts with `["pysnmp", "hlapi"]`
2. The qualified name ends with the target class name (`"UsmUserData"`
for `S509`, `"CommunityData"` for `S508`)

This flexible approach matches all PySNMP API paths without hardcoding
each variant, making the rules more maintainable and future-proof.

## Test Plan

Added comprehensive test cases to both `S508.py` and `S509.py` test
files covering:
- New API paths: `pysnmp.hlapi.asyncio.*`, `pysnmp.hlapi.v1arch.*`,
`pysnmp.hlapi.v3arch.*`
- Old API path: `pysnmp.hlapi.auth.*`
- Both insecure and secure usage patterns

All existing tests pass, and new snapshot tests were added and accepted.
Manual verification confirms both rules correctly detect all PySNMP API
variants.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-19 15:03:23 -05:00
Alex Waygood
a8f7ccf2ca [ty] Improve diagnostics when NotImplemented is called (#21523)
## Summary

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

I realised I was overcomplicating things when I described what we should
do in that issue description. The simplest thing to do here is just to
special-case call expressions and short-circuit the call-binding
machinery entirely if we see it's `NotImplemented` being called. It
doesn't really matter if the subdiagnostic doesn't fire when a union is
called and one element of the union is `NotImplemented` -- the
subdiagnostic doesn't need to be exhaustive; it's just to help people in
some common cases.

## Test Plan

Added snapshots
2025-11-19 19:27:12 +00:00
Alex Waygood
ce06094ada [ty] Remove unnecessary .expect() call from types/instance.rs (#21527)
## Summary

The `.expect()` call here:


5dd56264fb/crates/ty_python_semantic/src/types/instance.rs (L816-L827)

is the direct cause of the panic in
https://github.com/astral-sh/ty/issues/1587. This patch gets rid of the
panic by refactoring our `Protocol` enum so that the
`Protocol::FromClass` variant holds a `ProtocolClass` instance rather
than a `ClassType` instance (all the `.expect()` call was doing was
attempting to convert form a `ClassType` to a `ProtocolClass`).

I hoped that this would provide a fix for
https://github.com/astral-sh/ty/issues/1587, but we still panic on the
provided reproducible examples in that issue even with this PR.
Nonetheless, I think this PR is a worthwhile change to make because:
- It's probably slightly more efficient this way (we no longer have to
re-verify that the wrapped class in a `Protocol::FromClass()` variant is
a protocol class every time we want to access its interface)
- It's nice to get rid of `.expect()` calls where possible, and this one
seems definitely unnecessary
- The _new_ panic message on this PR branch makes it much clearer what
the underlying cause of the bug in
https://github.com/astral-sh/ty/issues/1587 is:

    <details>
    <summary>New panic message</summary>

    ```
error[panic]: Panicked at
/Users/alexw/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/a885bb4/src/function/execute.rs:321:21
when checking `/Users/alexw/dev/ruff/foo.py`: `ClassLiteral < 'db
>::explicit_bases_(Id(4c09)): execute: too many cycle iterations`
	info: This indicates a bug in ty.
info: If you could open an issue at
https://github.com/astral-sh/ty/issues/new?title=%5Bpanic%5D, we'd be
very appreciative!
	info: Platform: macos aarch64
	info: Version: ruff/0.14.5+60 (18a14bfaf 2025-11-19)
info: Args: ["target/debug/ty", "check", "foo.py",
"--python-version=3.14"]
info: run with `RUST_BACKTRACE=1` environment variable to show the full
backtrace information
	info: query stacktrace:
	   0: cached_protocol_interface(Id(6805))
at crates/ty_python_semantic/src/types/protocol_class.rs:790
	   1: is_equivalent_to_object_inner(Id(8003))
	             at crates/ty_python_semantic/src/types/instance.rs:667
	   2: infer_deferred_types(Id(1409))
	             at crates/ty_python_semantic/src/types/infer.rs:141
cycle heads: infer_definition_types(Id(140b)) -> iteration = 200,
TypeVarInstance < 'db >::lazy_bound_(Id(5803)) -> iteration = 200
	   3: TypeVarInstance < 'db >::lazy_bound_(Id(5802))
	             at crates/ty_python_semantic/src/types.rs:8734
	   4: infer_definition_types(Id(140c))
	             at crates/ty_python_semantic/src/types/infer.rs:94
	   5: infer_deferred_types(Id(140a))
	             at crates/ty_python_semantic/src/types/infer.rs:141
	   6: TypeVarInstance < 'db >::lazy_bound_(Id(5803))
	             at crates/ty_python_semantic/src/types.rs:8734
	   7: infer_definition_types(Id(140b))
	             at crates/ty_python_semantic/src/types/infer.rs:94
	   8: infer_scope_types(Id(1000))
	             at crates/ty_python_semantic/src/types/infer.rs:70
	   9: check_file_impl(Id(c00))
	             at crates/ty_project/src/lib.rs:535
	
	
	Found 1 diagnostic
WARN A fatal error occurred while checking some files. Not all project
files were analyzed. See the diagnostics list above for details.
    ```

    </details>

## Test Plan

All existing tests pass.
2025-11-19 19:26:36 +00:00
Douglas Creager
97935518e9 [ty] Create a specialization from a constraint set (#21414)
This patch lets us create specializations from a constraint set. The
constraint encodes the restrictions on which types each typevar can
specialize to. Given a generic context and a constraint set, we iterate
through all of the generic context's typevars. For each typevar, we
abstract the constraint set so that it only mentions the typevar in
question (propagating derived facts if needed). We then find the "best
representative type" for the typevar given the abstracted constraint
set.

When considering the BDD structure of the abstracted constraint set,
each path from the BDD root to the `true` terminal represents one way
that the constraint set can be satisfied. (This is also one of the
clauses in the DNF representation of the constraint set's boolean
formula.) Each of those paths is the conjunction of the individual
constraints of each internal node that we traverse as we walk that path,
giving a single lower/upper bound for the path. We use the upper bound
as the "best" (i.e. "closest to `object`") type for that path.

If there are multiple paths in the BDD, they technically represent
independent possible specializations. If there's a single specialization
that satisfies all of them, we will return that as the specialization.
If not, then the constraint set is ambiguous. (This happens most often
with constrained typevars.) We could in the future turn _each_ of the
paths into separate specializations, but it's not clear what we would do
with that, so instead we just report the ambiguity as a specialization
failure.
2025-11-19 14:20:33 -05:00
Douglas Creager
68ebd5132c [ty] Only normalize constraint bounds for display (#21516)
We were previously normalizing the upper and lower bounds of each
constraint when constructing constraint sets. Like in #21463, this was
for conflated reasons: It made constraint set displays nicer, since we
wouldn't render multiple constraints with obviously equivalent bounds.
(Think `T ≤ A & B` and `T ≤ B & A`) But it was also useful for
correctness, since prior to #21463 we were (trying to) add the full
transitive closure to a constraint set's BDD, and normalization gave a
useful reduction in the number of nodes in a typical BDD.

Now that we don't store the transitive closure explicitly, that second
reason is no longer relevant. Our sequent map can store that full
transitive closure much more efficiently than the expanded BDD would
have. This helps fix some false positives on #20933, where we're seeing
some (incorrect, need to be fixed, but ideally not blocking this effort)
assignability failures between a type and its normalization.

Normalization is still useful for display purposes, and so we do
normalize the upper/lower bounds before building up our display
representation of a constraint set BDD.

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-11-19 11:49:47 -05:00
Douglas Creager
ac9c83e581 [ty] Fix flaky tests on macos (#21524)
We're seeing flaky test failures on macos, which seems to be caused by
different Salsa ID orderings on the different platforms. Constraint set
BDDs order their internal nodes based on the Salsa IDs of the interned
typevar structs, and we had some code that depended on variable ordering
in an unexpected way.

This patch definitely fixes the macos test failure on #21414, and
hopefully fixes it on #21436, too.
2025-11-19 09:44:32 -05:00
David Peter
5dd56264fb [ty] Add tests for generic implicit type aliases (#21522)
## Summary

Add a set of comprehensive tests for generic implicit type aliases to
illustrate the current behavior with many flavors of `@Todo` types and
false positive diagnostics.

The tests are partially based on the typing conformance suite, and the
expected behavior has been checked against other type checkers.
2025-11-19 14:06:18 +00:00
Luca Chiodini
18a14bfaf1 [ty] Semantic tokens: consistently add the DEFINITION modifier (#21521) 2025-11-19 12:45:39 +01:00
Micha Reiser
ffce0de3c4 Only render hyperlinks for terminals known to support them (#21519) 2025-11-19 10:02:58 +01:00
David Peter
e29f3ed0fe [ty] Keep colorizing mypy_primer output (#21515)
## Summary

After an update to `mypy_primer`, we now need to set the environment
variable ourselves.
2025-11-19 08:52:51 +00:00
Micha Reiser
663f78e644 [ty] Exit with 2 if there's any IO error (#21508)
Co-authored-by: David Peter <mail@david-peter.de>
2025-11-19 09:39:19 +01:00
Dan Parizher
c796a70ec9 [ruff] Fix false positive for complex conversion specifiers in logging-eager-conversion (RUF065) (#21464)
Co-authored-by: Amethyst Reese <amethyst@n7.gg>
2025-11-19 09:38:33 +01:00
Carl Meyer
192c37d540 [ty] tighten up handling of subscripts in type expressions (#21503)
## Summary

Get rid of the catch-all todo type from subscripting a base type we
haven't implemented handling for yet in a type expression, and turn it
into a diagnostic instead.

Handle a few more cases explicitly, to avoid false positives from the
above change:
1. Subscripting any dynamic type (not just a todo type) in a type
expression should just result in that same dynamic type. This is
important for gradual guarantee, and matches other type checkers.
2. Subscripting a generic alias may be an error or not, depending
whether the specialization itself contains typevars. Don't try to handle
this yet (it should be handled in a later PR for specializing generic
non-PEP695 type aliases), just use a dedicated todo type for it.
3. Add a temporary todo branch to avoid false positives from string PEP
613 type aliases. This can be removed in the next PR, with PEP 613 type
alias support.

## Test Plan

Adjusted mdtests, ecosystem.

All new diagnostics in conformance suite are supposed to be diagnostics,
so this PR is a strict improvement there.

New diagnostics in the ecosystem are surfacing cases where we already
don't understand an annotation, but now we emit a diagnostic about it.
They are mostly intentional choices. Analysis of particular cases:

* `attrs`, `bokeh`, `django-stubs`, `dulwich`, `ibis`, `kornia`,
`mitmproxy`, `mongo-python-driver`, `mypy`, `pandas`, `poetry`,
`prefect`, `pydantic`, `pytest`, `scrapy`, `trio`, `werkzeug`, and
`xarray` are all cases where under `from __future__ import annotations`
or Python 3.14 deferred-annotations semantics, we follow normal
name-scoping rules, whereas some other type checkers prefer global names
over local names. This means we don't like it if e.g. you have a class
with a method or attribute named `type` or `tuple`, and you also try to
use `type` or `tuple` in method/attribute annotations of that class.
This PR isn't changing those semantics, just revealing them in more
cases where previously we just silently fell back to `Unknown`. I think
failing with a diagnostic (so authors can alias names as needed to avoid
relying on scoping rules that differ between type checkers) is better
than failing silently here.
* `beartype` assumes we support `TypeForm` (because it only supports
mypy and pyright, it uses `if MYPY:` to hide the `TypeForm` from mypy,
and pyright supports `TypeForm`), and we don't yet.
* `graphql-core` likes to use a `try: ... except ImportError: ...`
pattern for importing special forms from `typing` with fallback to
`typing_extensions`, instead of using `sys.version_info` checks. We
don't handle this well when type checking under an older Python version
(where the import from `typing` is not found); we see the imported name
as of type e.g. `Unknown | SpecialFormType(...)`, and because of the
union with `Unknown` we fail to handle it as the special form type. Mypy
and pyright also don't seem to support this pattern. They don't complain
about subscripting such special forms, but they do silently fail to
treat them as the desired special form. Again here, if we are going to
fail I'd rather fail with a diagnostic rather than silently.
* `ibis` is [trying to
use](https://github.com/ibis-project/ibis/blob/main/ibis/common/collections.py#L372)
`frozendict: type[FrozenDict]` as a way to create a "type alias" to
`FrozenDict`, but this is wrong: that means `frozendict:
type[FrozenDict[Any, Any]]`.
* `mypy` has some errors due to the fact that type-checking `typing.pyi`
itself (without knowing that it's the real `typing.pyi`) doesn't work
very well.
* `mypy-protobuf` imports some types from the protobufs library that end
up unioned with `Unknown` for some reason, and so we don't allow
explicit-specialization of them. Depending on the reason they end up
unioned with `Unknown`, we might want to better support this? But it's
orthogonal to this PR -- we aren't failing any worse here, just alerting
the author that we didn't understand their annotation.
* `pwndbg` has unresolved references due to star-importing from a
dependency that isn't installed, and uses un-imported names like `Dict`
in annotation expressions. Some of the unresolved references were hidden
by
https://github.com/astral-sh/ruff/blob/main/crates/ty_python_semantic/src/types/infer/builder.rs#L7223-L7228
when some annotations previously resolved to a Todo type that no longer
do.
2025-11-18 10:43:07 -08:00
Brent Westbrook
0645418f00 Set the diagnostic URL for lint errors (#21514)
Summary
--

This PR wires up the `Diagnostic::set_documentation_url` method from
#21502 to Ruff's lint diagnostics. This enables the links for the full
and concise output formats without any other changes.

I considered also including the URLs for the grouped and pylint output
formats, but the grouped format is still in `ruff_linter` instead of
`ruff_db`, so we'd have to export some additional functionality to wire
it up with `fmt_with_hyperlink`; and the pylint format doesn't currently
render with color, so I think it might actually be machine readable
rather than human readable?

The other ouput formats (json, json-lines, junit, github, gitlab,
rdjson, azure, sarif) seem more clearly not to need the links.

Test Plan
--

I guess you can't see my cursor or the browser opening, but it works for
lint rules, which have links, and doesn't include a link for syntax
errors, which don't have valid links.


![out](https://github.com/user-attachments/assets/a520c7f9-6d7b-4e5f-a1a9-3c5e21a51d3c)
2025-11-18 13:34:50 -05:00
Dylan
62343a101a Respect fmt: skip for compound statements on single line (#20633)
Closes #11216

Essentially the approach is to implement `Format` for a new struct
`FormatClause` which is just a clause header _and_ its body. We then
have the information we need to see whether there is a skip suppression
comment on the last child in the body and it all fits on one line.
2025-11-18 12:02:09 -06:00
Alex Waygood
8dad289062 [ty] Add Salsa caching to ClassLiteral::fields (#21512) 2025-11-18 17:48:36 +00:00
Douglas Creager
f67236b932 [ty] Better handling of "derived information" in constraint sets (#21463)
This saga began with a regression in how we handle constraint sets where
a typevar is constrained by another typevar, which #21068 first added
support for:

```py
def mutually_constrained[T, U]():
    # If [T = U ∧ U ≤ int], then [T ≤ int] must be true as well.
    given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(Never, U, int)
    static_assert(given_int.implies_subtype_of(T, int))
```

While working on #21414, I saw a regression in this test, which was
strange, since that PR has nothing to do with this logic! The issue is
that something in that PR made us instantiate the typevars `T` and `U`
in a different order, giving them differently ordered salsa IDs. And
importantly, we use these salsa IDs to define the variable ordering that
is used in our constraint set BDDs. This showed that our "mutually
constrained" logic only worked for one of the two possible orderings.
(We can — and now do — test this in a brute-force way by copy/pasting
the test with both typevar orderings.)

The underlying bug was in our `ConstraintSet::simplify_and_domain`
method. It would correctly detect `(U ≤ T ≤ U) ∧ (U ≤ int)`, because
those two constraints affect different typevars, and from that, infer `T
≤ int`. But it wouldn't detect the equivalent pattern in `(T ≤ U ≤ T) ∧
(U ≤ int)`, since those constraints affect the same typevar. At first I
tried adding that as yet more pattern-match logic in the ever-growing
`simplify_and_domain` method. But doing so caused other tests to start
failing.

At that point, I realized that `simplify_and_domain` had gotten to the
point where it was trying to do too much, and for conflicting consumers.
It was first written as part of our display logic, where the goal is to
remove redundant information from a BDD to make its string rendering
simpler. But we also started using it to add "derived facts" to a BDD. A
derived fact is a constraint that doesn't appear in the BDD directly,
but which we can still infer to be true. Our failing test relies on
derived facts — being able to infer that `T ≤ int` even though that
particular constraint doesn't appear in the original BDD. Before,
`simplify_and_domain` would trace through all of the constraints in a
BDD, figure out the full set of derived facts, and _add those derived
facts_ to the BDD structure. This is brittle, because those derived
facts are not universally true! In our example, `T ≤ int` only holds
along the BDD paths where both `T = U` and `U ≤ int`. Other paths will
test the negations of those constraints, and on those, we _shouldn't_
infer `T ≤ int`. In theory it's possible (and we were trying) to use BDD
operators to express that dependency...but that runs afoul of how we
were simultaneously trying to _remove_ information to make our displays
simpler.

So, I ripped off the band-aid. `simplify_and_domain` is now _only_ used
for display purposes. I have not touched it at all, except to remove
some logic that is definitely not used by our `Display` impl. Otherwise,
I did not want to touch that house of cards for now, since the display
logic is not load-bearing for any type inference logic.

For all non-display callers, we have a new **_sequent map_** data type,
which tracks exactly the same derived information. But it does so (a)
without trying to remove anything from the BDD, and (b) lazily, without
updating the BDD structure.

So the end result is that all of the tests (including the new
regressions) pass, via a more efficient (and hopefully better
structured/documented) implementation, at the cost of hanging onto a
pile of display-related tech debt that we'll want to clean up at some
point.
2025-11-18 12:02:25 -05:00
Brent Westbrook
cbc6863b8c Fix panic when formatting comments in unary expressions (#21501)
## Summary

This is another attempt at https://github.com/astral-sh/ruff/pull/21410
that fixes https://github.com/astral-sh/ruff/issues/19226.

@MichaReiser helped me get something working in a very helpful pairing
session. I pushed one additional commit moving the comments back from
leading comments to trailing comments, which I think retains more of the
input formatting.

I was inspired by Dylan's PR (#21185) to make one of these tables:

<table>
                <thead>
                    <tr>
                    <th scope="col">Input</th>
                    <th scope="col">Main</th>
                    <th scope="col">PR</th>
                    </tr>
                </thead>
                <tbody>
<tr>
<td><pre lang="python">
if (
    not
    # comment
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
):
    pass
</pre></td>
<td><pre lang="python">
if (
    # comment
    not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
):
    pass

</pre></td>
<td><pre lang="python">
if (
    not
    # comment
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
):
    pass

</pre></td>
</tr>
<tr>
<td><pre lang="python">
if (
    # unary comment
    not
    # operand comment
    (
        # comment
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    )
):
    pass
</pre></td>
<td><pre lang="python">
if (
    # unary comment
    # operand comment
    not (
        # comment
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    )
):
    pass

</pre></td>
<td><pre lang="python">
if (
    # unary comment
    not
    # operand comment
    (
        # comment
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    )
):
    pass

</pre></td>
</tr>
<tr>
<td><pre lang="python">
if (
    not # comment
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
):
    pass
</pre></td>
<td><pre lang="python">
if (  # comment
    not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
):
    pass

</pre></td>
<td><pre lang="python">
if (
    not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa  # comment
    + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
):
    pass

</pre></td>
</tr>
</tbody>
            </table>

hopefully it helps even though the snippets are much wider here.

The two main differences are (1) that we now retain own-line comments
between the unary operator and its operand instead of moving these to
leading comments on the operator itself, and (2) that we move
end-of-line comments between the operator and operand to dangling
end-of-line comments on the operand (the last example in the table).

## Test Plan

Existing tests, plus new ones based on the issue. As I noted below, I
also ran the output from main on the unary.py file back through this
branch to check that we don't reformat code from main. This made me feel
a bit better about not preview-gating the changes in this PR.

```shell
> git show main:crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/unary.py | ruff format - | ./target/debug/ruff format --diff -
> echo $?
0
```

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Takayuki Maeda <takoyaki0316@gmail.com>
2025-11-18 10:48:14 -05:00
Micha Reiser
7043d51df0 [ty] Add hyperlinks to rule codes in CLI (#21502) 2025-11-18 16:36:59 +01:00
David Peter
5ca9c15fc8 [ty] Better invalid-assignment diagnostics (#21476)
## Summary

Improve the diagnostic range for `invalid-assignment` diagnostics, and
add source annotations for the value and target type.

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

### Before

<img width="836" height="601" alt="image"
src="https://github.com/user-attachments/assets/a48219bb-58a8-4a83-b290-d09ef50ce5f0"
/>

### After

<img width="857" height="742" alt="image"
src="https://github.com/user-attachments/assets/cfcaa4f4-94fb-459e-8d64-97050dfecb50"
/>

## Ecosystem impact

Very good! Due to the wider diagnostic range, we now pick up more `#
type: ignore` directives that were supposed to suppress an invalid
assignment diagnostic.

## Test Plan

New snapshot tests
2025-11-18 14:31:04 +01:00
David Peter
7a739d6b76 [ty] Custom concise diagnostic messages (#21498)
## Summary

This PR proposes that we add a new `set_concise_message` functionality
to our `Diagnostic` construction API. When used, the concise message
that is otherwise auto-generated from the main diagnostic message and
the primary annotation will be overwritten with the custom message.

To understand why this is desirable, let's look at the `invalid-key`
diagnostic. This is how I *want* the full diagnostic to look like:

<img width="620" height="282" alt="image"
src="https://github.com/user-attachments/assets/3bf70f52-9d9f-4817-bc16-fb0ebf7c2113"
/>

However, without the change in this PR, the concise message would have
the following form:

```
error[invalid-key]: Unknown key "Age" for TypedDict `Person`: Unknown key "Age" - did you mean "age"?
```

This duplication is why the full `invalid-key` diagnostic used a main
diagnostic message that is only "Invalid key for TypedDict `Person`", to
make that bearable:

```
error[invalid-key] Invalid key for TypedDict `Person`: Unknown key "Age" - did you mean "age"?
```

This is still less than ideal, *and* we had to make the "full"
diagnostic worse. With the new API here, we have to make no such
compromises. We need to do slightly more work (provide one additional
custom-designed message), but we get to keep the "full" diagnostic that
we actually want, and we can make the concise message more terse and
readable:

```
error[invalid-key] Unknown key "Age" for TypedDict `Person` - did you mean "age"?
```

Similar problems exist for other diagnostics as well (I really want this
for https://github.com/astral-sh/ruff/pull/21476). In this PR, I only
changed `invalid-key` and `type-assertion-failure`.

The PR here is somewhat related to the discussion in
https://github.com/astral-sh/ty/issues/1418, but note that we are
solving a problem that is unrelated to sub-diagnostics.

## Test Plan

Updated tests
2025-11-18 09:35:40 +01:00
David Peter
d5a95ec824 [ty] Implicit type aliases: Add support for Callable (#21496)
## Summary

Add support for `Callable` special forms in implicit type aliases.

## Typing conformance

Four new tests are passing

## Ecosystem impact

* All of the `invalid-type-form` errors are from libraries that use
`mypy_extensions` and do something like `Callable[[NamedArg("x", str)],
int]`.
* A handful of new false positives because we do not support generic
specializations of implicit type aliases, yet. But other
* Everything else looks like true positives or known limitations

## Test Plan

New Markdown tests.
2025-11-18 09:06:05 +01:00
Ruchir
b1e354bd99 [ruff] Avoid false positive on ClassVar reassignment (RUF012) (#21478)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #21389

Avoid RUF012 false positives when reassigning a ClassVar

## Test Plan

<!-- How was it tested? -->

Added the new reassignment scenario to
`crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py`.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-17 15:52:24 -05:00
Douglas Creager
e4a32ba644 [ty] Constraint sets compare generic callables correctly (#21392)
Constraint sets can now track subtyping/assignability/etc of generic
callables correctly. For instance:

```py
def identity[T](t: T) -> T:
    return t

constraints = ConstraintSet.always()
static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[int], int]))
static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[str], str]))
```

A generic callable can be considered an intersection of all of its
possible specializations, and an assignability check with an
intersection as the lhs side succeeds of _any_ of the intersected types
satisfies the check. Put another way, if someone expects to receive any
function with a signature of `(int) -> int`, we can give them
`identity`.

Note that the corresponding check using `is_subtype_of` directly does
not yet work, since #20093 has not yet hooked up the core typing
relationship logic to use constraint sets:

```py
# These currently fail
static_assert(is_subtype_of(TypeOf[identity], Callable[[int], int]))
static_assert(is_subtype_of(TypeOf[identity], Callable[[str], str]))
```

To do this, we add a new _existential quantification_ operation on
constraint sets. This takes in a list of typevars and _removes_ those
typevars from the constraint set. Conceptually, we return a new
constraint set that evaluates to `true` when there was _any_ assignment
of the removed typevars that caused the old constraint set to evaluate
to `true`.

When comparing a generic constraint set, we add its typevars to the
`inferable` set, and figure out whatever constraints would allow any
specialization to satisfy the check. We then use the new existential
quantification operator to remove those new typevars, since the caller
doesn't (and shouldn't) know anything about them.

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-11-17 13:43:37 -05:00
Josh Cannon
ac2d07e83c Mention force-exclude in "Configuration > Python file discovery" (#21500) 2025-11-17 17:15:07 +01:00
Dylan
8156b45173 Avoid syntax error when formatting attribute expressions with outer parentheses, parenthesized value, and trailing comment on value (#20418)
Closes #19350 

This fixes a syntax error caused by formatting. However, the new tests reveal that there are some cases where formatting attributes with certain comments behaves strangely, both before and after this PR, so some more polish may be in order.

For example, without parentheses around the value, and both before and after this PR, we have:

```python
# unformatted
variable = (
    something # a comment
    .first_method("some string")
)

# formatted
variable = something.first_method("some string")  # a comment
```

which is probably not where the comment ought to go.
2025-11-17 09:11:36 -06:00
RasmusNygren
d063c71177 [ty] suppress invalid suggestions in import statements (#21484)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Partially addresses https://github.com/astral-sh/ty/issues/1562

Only suggest the keyword "as" in import statements when the user have
written `import foo a<CURSOR>` or `from foo import bar a<CURSOR>` as no
other suggestion makes sense here.

Re-uses the existing pattern for incomplete `import from` statements to
determine incomplete import alias statements and make the suggestions
more sane in those cases.

There was a potential suggestion from @BurntSushi in
https://github.com/astral-sh/ty/issues/1562#issue-3626853513 to move the
handling of import statements into one unified state machine but I acted
on the side of caution and fixed this with already established patterns,
pending a potential bigger re-write down the line.

## Test Plan

Added new tests and checked that it behaved reasonable in the
playground.

<!-- How was it tested? -->
2025-11-17 09:58:49 -05:00
Thamer Mahmoud
c16ef709f6 Limit eglot-format hook to eglot-managed Python buffers (#21459)
Running `eglot-format` in buffers not managed by Eglot causes a
`jsonrpc-error` in Emacs 30. It may also display a
`documentFormattingProvider` warning when the server does not support
formatting. Add checks for both.
2025-11-17 13:52:31 +00:00
Dylan
04a3ec3689 Adjust own-line comment placement between branches (#21185)
This PR attempts to improve the placement of own-line comments between
branches in the setting where the comment is more indented than the
preceding node.

There are two main changes.

### First change: Preceding node has leading content

If the preceding node has leading content, we now regard the comment as
automatically _less_ indented than the preceding node, and format
accordingly.

For example, 

```python
if True: preceding_node
# leading on `else`, not trailing on `preceding_node`
else: ...
```

This is more compatible with `black`, although there is a (presumably
very uncommon) edge case:

```python
if True:
    this;that
    # leading on `else`, but trailing in `black`
else: ...
```

I'm sort of okay with this - presumably if one wanted a comment for
those semi-colon separated statements, one should have put it _above_
them, and one wanted a comment only for `that` then it ought to have
been on the same line?

### Second change: searching for last child in body

While searching for the (recursively) last child in the body of the
preceding _branch_, we implicitly assumed that the preceding node had to
have a body to begin the recursion. But actually, in the base case, the
preceding node _is_ the last child in the body of the preceding branch.
So, for example:

```python
if True:
    something
    last_child_but_no_body
    # leading on else for `main` but trailing in this PR
else: ...
```

### More examples

The table below is an attempt to summarize the changes in behavior. The
rows alternate between an example snippet with `while` and the same
example with `if` - in the former case we do _not_ have an `else` node
and in the latter we do.

Notice that:

1. On `main` our handling of `if` vs. `while` is not consistent, whereas
it is consistent in the present PR
2. We disagree with `black` in all cases except that last example on
`main`, but agree in all cases for the present PR (though see above for
a wonky edge case where we disagree).

<table>
<tr>
<th>Original
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>

<th><code>main</code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
<th>This
PR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>

<th><code>black</code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
</tr>
<tr>
<td>

<pre lang="python">
while True: 
    pass
        # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
else:
    # comment
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
    # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
    # comment
else:
    pass
</pre>

</td>
</tr>
<tr>
<td>

<pre lang="python">
if True:
    pass
        # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
    # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
    # comment
else:
    pass
</pre>

</td>
</tr>
<tr>
<td>

<pre lang="python">
while True: pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
    # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
# comment
else:
    pass
</pre>

</td>
</tr>
<tr>
<td>

<pre lang="python">
if True: pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
    # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
# comment
else:
    pass
</pre>

</td>
</tr>
<tr>
<td>

<pre lang="python">
while True: pass
    # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
else:
    # comment
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
while True:
    pass
# comment
else:
    pass
</pre>

</td>
</tr>
<tr>
<td>

<pre lang="python">
if True: pass
    # comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
# comment
else:
    pass
</pre>

</td>
<td>

<pre lang="python">
if True:
    pass
# comment
else:
    pass
</pre>

</td>
</tr>
</table>
2025-11-17 07:30:34 -06:00
David Peter
1a86e13472 [ty] Subscript assignment diagnostics follow-up (#21452)
## Summary

Follow up from https://github.com/astral-sh/ruff/pull/21411. Again,
there are more things that could be improved here (like the diagnostics
for `lists`, or extending what we have for `dict` to `OrderedDict` etc),
but that will have to be postponed.
2025-11-17 11:14:58 +00:00
Matthew Mckee
901e9cdf49 [ty] Inlay hint call argument location (#20349)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-17 11:33:09 +01:00
Micha Reiser
58fa1d71b6 [ty] Use CompactStr for StringLiteralType (#21497) 2025-11-17 10:01:21 +01:00
renovate[bot]
d9fc0f08b4 Update CodSpeedHQ/action action to v4.3.4 (#21488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 08:06:30 +01:00
renovate[bot]
09deeabda5 Update salsa digest to a885bb4 (#21486)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 08:05:03 +01:00
renovate[bot]
1436e688cc Update dependency ruff to v0.14.5 (#21489)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [ruff](https://docs.astral.sh/ruff)
([source](https://redirect.github.com/astral-sh/ruff),
[changelog](https://redirect.github.com/astral-sh/ruff/blob/main/CHANGELOG.md))
| `==0.14.4` -> `==0.14.5` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.14.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.14.4/0.14.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/ruff (ruff)</summary>

###
[`v0.14.5`](https://redirect.github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#0145)

[Compare
Source](https://redirect.github.com/astral-sh/ruff/compare/0.14.4...0.14.5)

Released on 2025-11-13.

##### Preview features

- \[`flake8-simplify`] Apply `SIM113` when index variable is of type
`int`
([#&#8203;21395](https://redirect.github.com/astral-sh/ruff/pull/21395))
- \[`pydoclint`] Fix false positive when Sphinx directives follow a
"Raises" section (`DOC502`)
([#&#8203;20535](https://redirect.github.com/astral-sh/ruff/pull/20535))
- \[`pydoclint`] Support NumPy-style comma-separated parameters
(`DOC102`)
([#&#8203;20972](https://redirect.github.com/astral-sh/ruff/pull/20972))
- \[`refurb`] Auto-fix annotated assignments (`FURB101`)
([#&#8203;21278](https://redirect.github.com/astral-sh/ruff/pull/21278))
- \[`ruff`] Ignore `str()` when not used for simple conversion
(`RUF065`)
([#&#8203;21330](https://redirect.github.com/astral-sh/ruff/pull/21330))

##### Bug fixes

- Fix syntax error false positive on alternative `match` patterns
([#&#8203;21362](https://redirect.github.com/astral-sh/ruff/pull/21362))
- \[`flake8-simplify`] Fix false positive for iterable initializers with
generator arguments (`SIM222`)
([#&#8203;21187](https://redirect.github.com/astral-sh/ruff/pull/21187))
- \[`pyupgrade`] Fix false positive on relative imports from local
`.builtins` module (`UP029`)
([#&#8203;21309](https://redirect.github.com/astral-sh/ruff/pull/21309))
- \[`pyupgrade`] Consistently set the deprecated tag (`UP035`)
([#&#8203;21396](https://redirect.github.com/astral-sh/ruff/pull/21396))

##### Rule changes

- \[`refurb`] Detect empty f-strings (`FURB105`)
([#&#8203;21348](https://redirect.github.com/astral-sh/ruff/pull/21348))

##### CLI

- Add option to provide a reason to `--add-noqa`
([#&#8203;21294](https://redirect.github.com/astral-sh/ruff/pull/21294))
- Add upstream linter URL to `ruff linter --output-format=json`
([#&#8203;21316](https://redirect.github.com/astral-sh/ruff/pull/21316))
- Add color to `--help`
([#&#8203;21337](https://redirect.github.com/astral-sh/ruff/pull/21337))

##### Documentation

- Add a new "Opening a PR" section to the contribution guide
([#&#8203;21298](https://redirect.github.com/astral-sh/ruff/pull/21298))
- Added the PyScripter IDE to the list of "Who is using Ruff?"
([#&#8203;21402](https://redirect.github.com/astral-sh/ruff/pull/21402))
- Update PyCharm setup instructions
([#&#8203;21409](https://redirect.github.com/astral-sh/ruff/pull/21409))
- \[`flake8-annotations`] Add link to `allow-star-arg-any` option
(`ANN401`)
([#&#8203;21326](https://redirect.github.com/astral-sh/ruff/pull/21326))

##### Other changes

- \[`configuration`] Improve error message when `line-length` exceeds
`u16::MAX`
([#&#8203;21329](https://redirect.github.com/astral-sh/ruff/pull/21329))

##### Contributors

- [@&#8203;njhearp](https://redirect.github.com/njhearp)
- [@&#8203;11happy](https://redirect.github.com/11happy)
- [@&#8203;hugovk](https://redirect.github.com/hugovk)
- [@&#8203;Gankra](https://redirect.github.com/Gankra)
- [@&#8203;ntBre](https://redirect.github.com/ntBre)
- [@&#8203;pyscripter](https://redirect.github.com/pyscripter)
- [@&#8203;danparizher](https://redirect.github.com/danparizher)
- [@&#8203;MichaReiser](https://redirect.github.com/MichaReiser)
- [@&#8203;henryiii](https://redirect.github.com/henryiii)
-
[@&#8203;charliecloudberry](https://redirect.github.com/charliecloudberry)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 10:53:23 +05:30
renovate[bot]
d6c34b98a5 Update astral-sh/setup-uv action to v7.1.3 (#21487)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
action | patch | `v7.1.2` -> `v7.1.3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/setup-uv (astral-sh/setup-uv)</summary>

###
[`v7.1.3`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v7.1.3):
🌈 Support act

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v7.1.2...v7.1.3)

##### Changes

This bug fix release adds support for <https://github.com/nektos/act>
It was previously broken because of a too new `undici` version and TS
transpilation target.

Compatibility with act is now automatically tested.

##### 🐛 Bug fixes

- use old undici and ES2022 target for act support
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;678](https://redirect.github.com/astral-sh/setup-uv/issues/678))

##### 🧰 Maintenance

- chore: update known checksums for 0.9.8
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;677](https://redirect.github.com/astral-sh/setup-uv/issues/677))
- chore: update known checksums for 0.9.7
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;671](https://redirect.github.com/astral-sh/setup-uv/issues/671))
- chore: update known checksums for 0.9.6
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;670](https://redirect.github.com/astral-sh/setup-uv/issues/670))

##### 📚 Documentation

- Correct description of `cache-dependency-glob`
[@&#8203;allanlewis](https://redirect.github.com/allanlewis)
([#&#8203;676](https://redirect.github.com/astral-sh/setup-uv/issues/676))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 10:51:31 +05:30
renovate[bot]
1b50e032a4 Update Rust crate get-size2 to v0.7.2 (#21490)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [get-size2](https://redirect.github.com/bircni/get-size2) |
workspace.dependencies | patch | `0.7.1` -> `0.7.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>bircni/get-size2 (get-size2)</summary>

###
[`v0.7.2`](https://redirect.github.com/bircni/get-size2/blob/HEAD/CHANGELOG.md#072---2025-11-13)

[Compare
Source](https://redirect.github.com/bircni/get-size2/compare/0.7.1...0.7.2)

##### Documentation

- Update docs with correct links -
([b234d70](b234d70ece))
- Nicolas

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 10:50:43 +05:30
renovate[bot]
687ed292f6 Update Rust crate indicatif to v0.18.3 (#21491)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [indicatif](https://redirect.github.com/console-rs/indicatif) |
workspace.dependencies | patch | `0.18.2` -> `0.18.3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>console-rs/indicatif (indicatif)</summary>

###
[`v0.18.3`](https://redirect.github.com/console-rs/indicatif/releases/tag/0.18.3)

[Compare
Source](https://redirect.github.com/console-rs/indicatif/compare/0.18.2...0.18.3)

#### What's Changed

- Add ProgressBar::set\_elapsed by
[@&#8203;sunshowers](https://redirect.github.com/sunshowers) in
[#&#8203;742](https://redirect.github.com/console-rs/indicatif/pull/742)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 10:50:27 +05:30
renovate[bot]
0554b1ca8a Update Rust crate quick-junit to v0.5.2 (#21492)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [quick-junit](https://redirect.github.com/nextest-rs/quick-junit) |
workspace.dependencies | patch | `0.5.1` -> `0.5.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>nextest-rs/quick-junit (quick-junit)</summary>

###
[`v0.5.2`](https://redirect.github.com/nextest-rs/quick-junit/blob/HEAD/CHANGELOG.md#052---2025-11-10)

[Compare
Source](https://redirect.github.com/nextest-rs/quick-junit/compare/quick-junit-0.5.1...quick-junit-0.5.2)

##### Added

- A long-requested feature: deserialization support for reports! The new
deserializer has undergone fuzzing and property-based testing, and it is
known to work with JUnit reports generated by quick-junit. The
deserializer should work with JUnit reports generated by other tools as
well. If it doesn't, fixes are welcome.
- The new `proptest` feature allows for generation of arbitrary
`Report`s.

##### Updated

Internal dependency update: `quick-xml` updated to 0.38.3.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 10:49:21 +05:30
renovate[bot]
bbe42bc775 Update taiki-e/install-action action to v2.62.52 (#21493)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[taiki-e/install-action](https://redirect.github.com/taiki-e/install-action)
| action | patch | `v2.62.49` -> `v2.62.52` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>taiki-e/install-action (taiki-e/install-action)</summary>

###
[`v2.62.52`](https://redirect.github.com/taiki-e/install-action/blob/HEAD/CHANGELOG.md#100---2021-12-30)

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.62.51...v2.62.52)

Initial release

[Unreleased]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.52...HEAD

[2.62.52]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.51...v2.62.52

[2.62.51]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.50...v2.62.51

[2.62.50]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.49...v2.62.50

[2.62.49]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.48...v2.62.49

[2.62.48]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.47...v2.62.48

[2.62.47]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.46...v2.62.47

[2.62.46]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.45...v2.62.46

[2.62.45]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.44...v2.62.45

[2.62.44]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.43...v2.62.44

[2.62.43]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.42...v2.62.43

[2.62.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.41...v2.62.42

[2.62.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.40...v2.62.41

[2.62.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.39...v2.62.40

[2.62.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.38...v2.62.39

[2.62.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.37...v2.62.38

[2.62.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.36...v2.62.37

[2.62.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.35...v2.62.36

[2.62.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.34...v2.62.35

[2.62.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.33...v2.62.34

[2.62.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.32...v2.62.33

[2.62.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.31...v2.62.32

[2.62.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.30...v2.62.31

[2.62.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.29...v2.62.30

[2.62.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.28...v2.62.29

[2.62.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.27...v2.62.28

[2.62.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.26...v2.62.27

[2.62.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.25...v2.62.26

[2.62.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.24...v2.62.25

[2.62.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.23...v2.62.24

[2.62.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.22...v2.62.23

[2.62.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.21...v2.62.22

[2.62.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.20...v2.62.21

[2.62.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.19...v2.62.20

[2.62.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.18...v2.62.19

[2.62.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.17...v2.62.18

[2.62.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.16...v2.62.17

[2.62.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.15...v2.62.16

[2.62.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.14...v2.62.15

[2.62.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.13...v2.62.14

[2.62.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.12...v2.62.13

[2.62.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.11...v2.62.12

[2.62.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.10...v2.62.11

[2.62.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.9...v2.62.10

[2.62.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.8...v2.62.9

[2.62.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.7...v2.62.8

[2.62.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.6...v2.62.7

[2.62.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.5...v2.62.6

[2.62.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.4...v2.62.5

[2.62.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.3...v2.62.4

[2.62.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.2...v2.62.3

[2.62.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.1...v2.62.2

[2.62.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.0...v2.62.1

[2.62.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.13...v2.62.0

[2.61.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.12...v2.61.13

[2.61.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.11...v2.61.12

[2.61.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.10...v2.61.11

[2.61.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.9...v2.61.10

[2.61.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.8...v2.61.9

[2.61.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.7...v2.61.8

[2.61.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.6...v2.61.7

[2.61.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.5...v2.61.6

[2.61.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.4...v2.61.5

[2.61.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.3...v2.61.4

[2.61.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.2...v2.61.3

[2.61.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.1...v2.61.2

[2.61.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.0...v2.61.1

[2.61.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.60.0...v2.61.0

[2.60.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.59.1...v2.60.0

[2.59.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.59.0...v2.59.1

[2.59.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.33...v2.59.0

[2.58.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.32...v2.58.33

[2.58.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.31...v2.58.32

[2.58.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.30...v2.58.31

[2.58.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.29...v2.58.30

[2.58.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.28...v2.58.29

[2.58.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.27...v2.58.28

[2.58.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.26...v2.58.27

[2.58.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.25...v2.58.26

[2.58.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.24...v2.58.25

[2.58.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.23...v2.58.24

[2.58.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.22...v2.58.23

[2.58.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.21...v2.58.22

[2.58.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.20...v2.58.21

[2.58.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.19...v2.58.20

[2.58.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.18...v2.58.19

[2.58.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.17...v2.58.18

[2.58.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.16...v2.58.17

[2.58.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.15...v2.58.16

[2.58.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.14...v2.58.15

[2.58.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.13...v2.58.14

[2.58.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.12...v2.58.13

[2.58.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.11...v2.58.12

[2.58.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.10...v2.58.11

[2.58.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.9...v2.58.10

[2.58.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.8...v2.58.9

[2.58.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.7...v2.58.8

[2.58.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.6...v2.58.7

[2.58.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.5...v2.58.6

[2.58.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.4...v2.58.5

[2.58.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.3...v2.58.4

[2.58.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.2...v2.58.3

[2.58.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.1...v2.58.2

[2.58.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.0...v2.58.1

[2.58.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.8...v2.58.0

[2.57.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.7...v2.57.8

[2.57.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.6...v2.57.7

[2.57.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.5...v2.57.6

[2.57.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.4...v2.57.5

[2.57.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.3...v2.57.4

[2.57.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.2...v2.57.3

[2.57.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.1...v2.57.2

[2.57.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.0...v2.57.1

[2.57.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.24...v2.57.0

[2.56.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.23...v2.56.24

[2.56.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.22...v2.56.23

[2.56.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.21...v2.56.22

[2.56.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.20...v2.56.21

[2.56.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.19...v2.56.20

[2.56.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.18...v2.56.19

[2.56.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.17...v2.56.18

[2.56.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.16...v2.56.17

[2.56.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.15...v2.56.16

[2.56.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.14...v2.56.15

[2.56.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.13...v2.56.14

[2.56.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.12...v2.56.13

[2.56.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.11...v2.56.12

[2.56.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.10...v2.56.11

[2.56.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.9...v2.56.10

[2.56.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.8...v2.56.9

[2.56.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.7...v2.56.8

[2.56.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.6...v2.56.7

[2.56.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.5...v2.56.6

[2.56.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.4...v2.56.5

[2.56.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.3...v2.56.4

[2.56.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.2...v2.56.3

[2.56.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.1...v2.56.2

[2.56.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.0...v2.56.1

[2.56.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.4...v2.56.0

[2.55.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.3...v2.55.4

[2.55.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.2...v2.55.3

[2.55.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.1...v2.55.2

[2.55.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.0...v2.55.1

[2.55.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.3...v2.55.0

[2.54.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.2...v2.54.3

[2.54.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.1...v2.54.2

[2.54.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.0...v2.54.1

[2.54.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.53.2...v2.54.0

[2.53.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.53.1...v2.53.2

[2.53.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.53.0...v2.53.1

[2.53.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.8...v2.53.0

[2.52.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.7...v2.52.8

[2.52.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.6...v2.52.7

[2.52.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.5...v2.52.6

[2.52.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.4...v2.52.5

[2.52.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.3...v2.52.4

[2.52.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.2...v2.52.3

[2.52.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.1...v2.52.2

[2.52.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.0...v2.52.1

[2.52.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.3...v2.52.0

[2.51.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.2...v2.51.3

[2.51.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.1...v2.51.2

[2.51.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.0...v2.51.1

[2.51.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.10...v2.51.0

[2.50.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.9...v2.50.10

[2.50.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.8...v2.50.9

[2.50.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.7...v2.50.8

[2.50.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.6...v2.50.7

[2.50.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.5...v2.50.6

[2.50.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.4...v2.50.5

[2.50.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.3...v2.50.4

[2.50.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.2...v2.50.3

[2.50.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.1...v2.50.2

[2.50.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.0...v2.50.1

[2.50.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.50...v2.50.0

[2.49.50]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.49...v2.49.50

[2.49.49]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.48...v2.49.49

[2.49.48]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.47...v2.49.48

[2.49.47]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.46...v2.49.47

[2.49.46]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.45...v2.49.46

[2.49.45]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.44...v2.49.45

[2.49.44]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.43...v2.49.44

[2.49.43]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.42...v2.49.43

[2.49.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.41...v2.49.42

[2.49.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.40...v2.49.41

[2.49.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.39...v2.49.40

[2.49.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.38...v2.49.39

[2.49.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.37...v2.49.38

[2.49.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.36...v2.49.37

[2.49.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.35...v2.49.36

[2.49.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.34...v2.49.35

[2.49.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.33...v2.49.34

[2.49.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.32...v2.49.33

[2.49.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.31...v2.49.32

[2.49.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.30...v2.49.31

[2.49.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.29...v2.49.30

[2.49.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.28...v2.49.29

[2.49.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.27...v2.49.28

[2.49.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.26...v2.49.27

[2.49.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.25...v2.49.26

[2.49.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.24...v2.49.25

[2.49.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.23...v2.49.24

[2.49.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.22...v2.49.23

[2.49.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.21...v2.49.22

[2.49.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.20...v2.49.21

[2.49.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.19...v2.49.20

[2.49.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.18...v2.49.19

[2.49.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.17...v2.49.18

[2.49.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.16...v2.49.17

[2.49.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.15...v2.49.16

[2.49.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.14...v2.49.15

[2.49.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.13...v2.49.14

[2.49.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.12...v2.49.13

[2.49.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.11...v2.49.12

[2.49.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.10...v2.49.11

[2.49.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.9...v2.49.10

[2.49.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.8...v2.49.9

[2.49.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.7...v2.49.8

[2.49.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.6...v2.49.7

[2.49.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.5...v2.49.6

[2.49.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.4...v2.49.5

[2.49.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.3...v2.49.4

[2.49.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.2...v2.49.3

[2.49.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.1...v2.49.2

[2.49.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.0...v2.49.1

[2.49.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.22...v2.49.0

[2.48.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.21...v2.48.22

[2.48.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.20...v2.48.21

[2.48.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.19...v2.48.20

[2.48.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.18...v2.48.19

[2.48.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.17...v2.48.18

[2.48.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.16...v2.48.17

[2.48.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.15...v2.48.16

[2.48.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.14...v2.48.15

[2.48.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.13...v2.48.14

[2.48.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.12...v2.48.13

[2.48.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.11...v2.48.12

[2.48.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.10...v2.48.11

[2.48.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.9...v2.48.10

[2.48.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.8...v2.48.9

[2.48.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.7...v2.48.8

[2.48.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.6...v2.48.7

[2.48.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.5...v2.48.6

[2.48.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.4...v2.48.5

[2.48.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.3...v2.48.4

[2.48.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.2...v2.48.3

[2.48.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.1...v2.48.2

[2.48.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.0...v2.48.1

[2.48.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.32...v2.48.0

[2.47.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.31...v2.47.32

[2.47.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.30...v2.47.31

[2.47.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.29...v2.47.30

[2.47.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.28...v2.47.29

[2.47.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.27...v2.47.28

[2.47.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.26...v2.47.27

[2.47.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.25...v2.47.26

[2.47.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.24...v2.47.25

[2.47.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.23...v2.47.24

[2.47.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.22...v2.47.23

[2.47.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.21...v2.47.22

[2.47.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.20...v2.47.21

[2.47.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.19...v2.47.20

[2.47.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.18...v2.47.19

[2.47.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.17...v2.47.18

[2.47.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.16...v2.47.17

[2.47.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.15...v2.47.16

[2.47.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.14...v2.47.15

[2.47.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.13...v2.47.14

[2.47.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.12...v2.47.13

[2.47.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.11...v2.47.12

[2.47.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.10...v2.47.11

[2.47.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.9...v2.47.10

[2.47.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.8...v2.47.9

[2.47.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.7...v2.47.8

[2.47.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.6...v2.47.7

[2.47.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.5...v2.47.6

[2.47.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.4...v2.47.5

[2.47.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.3...v2.47.4

[2.47.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.2...v2.47.3

[2.47.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.1...v2.47.2

[2.47.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.0...v2.47.1

[2.47.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.20...v2.47.0

[2.46.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.19...v2.46.20

[2.46.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.18...v2.46.19

[2.46.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.17...v2.46.18

[2.46.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.16...v2.46.17

[2.46.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.15...v2.46.16

[2.46.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.14...v2.46.15

[2.46.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.13...v2.46.14

[2.46.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.12...v2.46.13

[2.46.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.11...v2.46.12

[2.46.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.10...v2.46.11

[2.46.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.9...v2.46.10

[2.46.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.8...v2.46.9

[2.46.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.7...v2.46.8

[2.46.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.6...v2.46.7

[2.46.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.5...v2.46.6

[2.46.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.4...v2.46.5

[2.46.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.3...v2.46.4

[2.46.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.2...v2.46.3

[2.46.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.1...v2.46.2

[2.46.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.0...v2.46.1

[2.46.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.15...v2.46.0

[2.45.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.14...v2.45.15

[2.45.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.13...v2.45.14

[2.45.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.12...v2.45.13

[2.45.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.11...v2.45.12

[2.45.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.10...v2.45.11

[2.45.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.9...v2.45.10

[2.45.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.8...v2.45.9

[2.45.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.7...v2.45.8

[2.45.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.6...v2.45.7

[2.45.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.5...v2.45.6

[2.45.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.4...v2.45.5

[2.45.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.3...v2.45.4

[2.45.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.2...v2.45.3

[2.45.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.1...v2.45.2

[2.45.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.0...v2.45.1

[2.45.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.72...v2.45.0

[2.44.72]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.71...v2.44.72

[2.44.71]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.70...v2.44.71

[2.44.70]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.69...v2.44.70

[2.44.69]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.68...v2.44.69

[2.44.68]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.67...v2.44.68

[2.44.67]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.66...v2.44.67

[2.44.66]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.65...v2.44.66

[2.44.65]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.64...v2.44.65

[2.44.64]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.63...v2.44.64

[2.44.63]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.62...v2.44.63

[2.44.62]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.61...v2.44.62

[2.44.61]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.60...v2.44.61

[2.44.60]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.59...v2.44.60

[2.44.59]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.58...v2.44.59

[2.44.58]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.57...v2.44.58

[2.44.57]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.56...v2.44.57

[2.44.56]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.55...v2.44.56

[2.44.55]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.54...v2.44.55

[2.44.54]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.53...v2.44.54

[2.44.53]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.52...v2.44.53

[2.44.52]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.51...v2.44.52

[2.44.51]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.50...v2.44.51

[2.44.50]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.49...v2.44.50

[2.44.49]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.48...v2.44.49

[2.44.48]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.47...v2.44.48

[2.44.47]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.46...v2.44.47

[2.44.46]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.45...v2.44.46

[2.44.45]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.44...v2.44.45

[2.44.44]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.43...v2.44.44

[2.44.43]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.42...v2.44.43

[2.44.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.41...v2.44.42

[2.44.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.40...v2.44.41

[2.44.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.39...v2.44.40

[2.44.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.38...v2.44.39

[2.44.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.37...v2.44.38

[2.44.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.36...v2.44.37

[2.44.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.35...v2.44.36

[2.44.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.34...v2.44.35

[2.44.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.33...v2.44.34

[2.44.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.32...v2.44.33

[2.44.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.31...v2.44.32

[2.44.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.30...v2.44.31

[2.44.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.29...v2.44.30

[2.44.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.28...v2.44.29

[2.44.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.27...v2.44.28

[2.44.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.26...v2.44.27

[2.44.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.25...v2.44.26

[2.44.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.24...v2.44.25

[2.44.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.23...v2.44.24

[2.44.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.22...v2.44.23

[2.44.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.21...v2.44.22

[2.44.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.20...v2.44.21

[2.44.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.19...v2.44.20

[2.44.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.18...v2.44.19

[2.44.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.17...v2.44.18

[2.44.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.16...v2.44.17

[2.44.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.15...v2.44.16

[2.44.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.14...v2.44.15

[2.44.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.13...v2.44.14

[2.44.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.12...v2.44.13

[2.44.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.11...v2.44.12

[2.44.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.10...v2.44.11

[2.44.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.9...v2.44.10

[2.44.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.8...v2.44.9

[2.44.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.7...v2.44.8

[2.44.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.6...v2.44.7

[2.44.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.5...v2.44.6

[2.44.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.4...v2.44.5

[2.44.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.3...v2.44.4

[2.44.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.2...v2.44.3

[2.44.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.1...v2.44.2

[2.44.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.0...v2.44.1

[2.44.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.7...v2.44.0

[2.43.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.6...v2.43.7

[2.43.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.5...v2.43.6

[2.43.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.4...v2.43.5

[2.43.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.3...v2.43.4

[2.43.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.2...v2.43.3

[2.43.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.1...v2.43.2

[2.43.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.0...v2.43.1

[2.43.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.42...v2.43.0

[2.42.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.41...v2.42.42

[2.42.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.40...v2.42.41

[2.42.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.39...v2.42.40

[2.42.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.38...v2.42.39

[2.42.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.37...v2.42.38

[2.42.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.36...v2.42.37

[2.42.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.35...v2.42.36

[2.42.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.34...v2.42.35

[2.42.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.33...v2.42.34

[2.42.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.32...v2.42.33

[2.42.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.31...v2.42.32

[2.42.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.30...v2.42.31

[2.42.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.29...v2.42.30

[2.42.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.28...v2.42.29

[2.42.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.27...v2.42.28

[2.42.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.26...v2.42.27

[2.42.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.25...v2.42.26

[2.42.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.24...v2.42.25

[2.42.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.23...v2.42.24

[2.42.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.22...v2.42.23

[2.42.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.21...v2.42.22

[2.42.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.20...v2.42.21

[2.42.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.19...v2.42.20

[2.42.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.18...v2.42.19

[2.42.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.17...v2.42.18

[2.42.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.16...v2.42.17

[2.42.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.15...v2.42.16

[2.42.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.14...v2.42.15

[2.42.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.13...v2.42.14

[2.42.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.12...v2.42.13

[2.42.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.11...v2.42.12

[2.42.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.10...v2.42.11

[2.42.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.9...v2.42.10

[2.42.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.8...v2.42.9

[2.42.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.7...v2.42.8

[2.42.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.6...v2.42.7

[2.42.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.5...v2.42.6

[2.42.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.4...v2.42.5

[2.42.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.3...v2.42.4

[2.42.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.2...v2.42.3

[2.42.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.1...v2.42.2

[2.42.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.0...v2.42.1

[2.42.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.18...v2.42.0

[2.41.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.17...v2.41.18

[2.41.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.16...v2.41.17

[2.41.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.15...v2.41.16

[2.41.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.14...v2.41.15

[2.41.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.13...v2.41.14

[2.41.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.12...v2.41.13

[2.41.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.11...v2.41.12

[2.41.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.10...v2.41.11

[2.41.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.9...v2.41.10

[2.41.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.8...v2.41.9

[2.41.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.7...v2.41.8

[2.41.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.6...v2.41.7

[2.41.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.5...v2.41.6

[2.41.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.4...v2.41.5

[2.41.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.3...v2.41.4

[2.41.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.2...v2.41.3

[2.41.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.1...v2.41.2

[2.41.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.0...v2.41.1

[2.41.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.40.2...v2.41.0

[2.40.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.40.1...v2.40.2

[2.40.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.40.0...v2.40.1

[2.40.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.39.2...v2.40.0

[2.39.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.39.1...v2.39.2

[2.39.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.39.0...v2.39.1

[2.39.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.7...v2.39.0

[2.38.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.6...v2.38.7

[2.38.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.5...v2.38.6

[2.38.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.4...v2.38.5

[2.38.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.3...v2.38.4

[2.38.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.2...v2.38.3

[2.38.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.1...v2.38.2

[2.38.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.0...v2.38.1

[2.38.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.37.0...v2.38.0

[2.37.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.36.0...v2.37.0

[2.36.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.35.0...v2.36.0

[2.35.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.3...v2.35.0

[2.34.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.2...v2.34.3

[2.34.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.1...v2.34.2

[2.34.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.0...v2.34.1

[2.34.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.36...v2.34.0

[2.33.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.35...v2.33.36

[2.33.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.34...v2.33.35

[2.33.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.33...v2.33.34

[2.33.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.32...v2.33.33

[2.33.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.31...v2.33.32

[2.33.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.30...v2.33.31

[2.33.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.29...v2.33.30

[2.33.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.28...v2.33.29

[2.33.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.27...v2.33.28

[2.33.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.26...v2.33.27

[2.33.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.25...v2.33.26

[2.33.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.24...v2.33.25

[2.33.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.23...v2.33.24

[2.33.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.22...v2.33.23

[2.33.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.21...v2.33.22

[2.33.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.20...v2.33.21

[2.33.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.19...v2.33.20

[2.33.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.18...v2.33.19

[2.33.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.17...v2.33.18

[2.33.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.16...v2.33.17

[2.33.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.15...v2.33.16

[2.33.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.14...v2.33.15

[2.33.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.13...v2.33.14

[2.33.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.12...v2.33.13

[2.33.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.11...v2.33.12

[2.33.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.10...v2.33.11

[2.33.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.9...v2.33.10

[2.33.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.8...v2.33.9

[2.33.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.7...v2.33.8

[2.33.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.6...v2.33.7

[2.33.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.5...v2.33.6

[2.33.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.4...v2.33.5

[2.33.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.3...v2.33.4

[2.33.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.2...v2.33.3

[2.33.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.1...v2.33.2

[2.33.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.0...v2.33.1

[2.33.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.20...v2.33.0

[2.32.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.19...v2.32.20

[2.32.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.18...v2.32.19

[2.32.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.17...v2.32.18

[2.32.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.16...v2.32.17

[2.32.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.15...v2.32.16

[2.32.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.14...v2.32.15

[2.32.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.13...v2.32.14

[2.32.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.12...v2.32.13

[2.32.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.11...v2.32.12

[2.32.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.10...v2.32.11

[2.32.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.9...v2.32.10

[2.32.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.8...v2.32.9

[2.32.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.7...v2.32.8

[2.32.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.6...v2.32.7

[2.32.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.5...v2.32.6

[2.32.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.4...v2.32.5

[2.32.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.3...v2.32.4

[2.32.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.2...v2.32.3

[2.32.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.1...v2.32.2

[2.32.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.0...v2.32.1

[2.32.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.3...v2.32.0

[2.31.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.2...v2.31.3

[2.31.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.1...v2.31.2

[2.31.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.0...v2.31.1

[2.31.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.30.0...v2.31.0

[2.30.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.8...v2.30.0

[2.29.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.7...v2.29.8

[2.29.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.6...v2.29.7

[2.29.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.5...v2.29.6

[2.29.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.4...v2.29.5

[2.29.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.3...v2.29.4

[2.29.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.2...v2.29.3

[2.29.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.1...v2.29.2

[2.29.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.0...v2.29.1

[2.29.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.16...v2.29.0

[2.28.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.15...v2.28.16

[2.28.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.14...v2.28.15

[2.28.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.13...v2.28.14

[2.28.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.12...v2.28.13

[2.28.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.11...v2.28.12

[2.28.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.10...v2.28.11

[2.28.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.9...v2.28.10

[2.28.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.8...v2.28.9

[2.28.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.7...v2.28.8

[2.28.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.6...v2.28.7

[2.28.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.5...v2.28.6

[2.28.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.4...v2.28.5

[2.28.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.3...v2.28.4

[2.28.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.2...v2.28.3

[2.28.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.1...v2.28.2

[2.28.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.0...v2.28.1

[2.28.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.15...v2.28.0

[2.27.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.14...v2.27.15

[2.27.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.13...v2.27.14

[2.27.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.12...v2.27.13

[2.27.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.11...v2.27.12

[2.27.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.10...v2.27.11

[2.27.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.9...v2.27.10

[2.27.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.8...v2.27.9

[2.27.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.7...v2.27.8

[2.27.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.6...v2.27.7

[2.27.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.5...v2.27.6

[2.27.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.4...v2.27.5

[2.27.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.3...v2.27.4

[2.27.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.2...v2.27.3

[2.27.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.1...v2.27.2

[2.27.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.0...v2.27.1

[2.27.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.20...v2.27.0

[2.26.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.19...v2.26.20

[2.26.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.18...v2.26.19

[2.26.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.17...v2.26.18

[2.26.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.16...v2.26.17

[2.26.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.15...v2.26.16

[2.26.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.14...v2.26.15

[2.26.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.13...v2.26.14

[2.26.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.12...v2.26.13

[2.26.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.11...v2.26.12

[2.26.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.10...v2.26.11

[2.26.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.9...v2.26.10

[2.26.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.8...v2.26.9

[2.26.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.7...v2.26.8

[2.26.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.6...v2.26.7

[2.26.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.5...v2.26.6

[2.26.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.4...v2.26.5

[2.26.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.3...v2.26.4

[2.26.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.2...v2.26.3

[2.26.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.1...v2.26.2

[2.26.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.0...v2.26.1

[2.26.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.11...v2.26.0

[2.25.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.10...v2.25.11

[2.25.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.9...v2.25.10

[2.25.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.8...v2.25.9

[2.25.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.7...v2.25.8

[2.25.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.6...v2.25.7

[2.25.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.5...v2.25.6

[2.25.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.4...v2.25.5

[2.25.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.3...v2.25.4

[2.25.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.2...v2.25.3

[2.25.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.1...v2.25.2

[2.25.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.0...v2.25.1

[2.25.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.4...v2.25.0

[2.24.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.3...v2.24.4

[2.24.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.2...v2.24.3

[2.24.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.1...v2.24.2

[2.24.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.0...v2.24.1

[2.24.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.9...v2.24.0

[2.23.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.8...v2.23.9

[2.23.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.7...v2.23.8

[2.23.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.6...v2.23.7

[2.23.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.5...v2.23.6

[2.23.5]: https://redirect.github.com/t

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 10:48:42 +05:30
Micha Reiser
0d2cd84df4 Fix analyze graph tests on windows (#21481) 2025-11-16 18:27:07 +00:00
Gautham Venkataraman
665f68036c analyze: Add option to skip over imports in TYPE_CHECKING blocks (#21472)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-16 12:30:24 +00:00
David Peter
f5fb5c388a [ty] Dataclasses: __hash__ semantics and unsafe_hash (#21470)
## Summary

Implement the semantics of `__hash__` for dataclasses and add support
for `unsafe_hash`

## Test Plan

New Markdown tests.
2025-11-16 09:52:30 +00:00
David Peter
dbd72480a9 [ty] Dataclass transform: complete set of parameters (#21474)
## Summary

We previously only allowed models to overwrite the
`{eq,order,kw_only,frozen}_defaults` of the dataclass-transformer, but
all other standard-dataclass parameters should be equally supported with
the same behavior.

## Test Plan

Added regression tests.
2025-11-16 09:46:45 +00:00
Hugo
75c1a0ae55 [ty] Provide proper error on dangling revealed (#21416)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-16 08:34:54 +00:00
chiri
7a546809c4 [refurb] Fix FURB103 autofix (#21454) 2025-11-16 09:32:41 +01:00
Alex Waygood
3065f8dbbc [ty] Improve diagnostics for invalid exceptions (#21475)
## Summary

Not a high-priority task... but it _is_ a weekend :P

This PR improves our diagnostics for invalid exceptions. Specifically:
- We now give a special-cased ``help: Did you mean
`NotImplementedError`` subdiagnostic for `except NotImplemented`, `raise
NotImplemented` and `raise <EXCEPTION> from NotImplemented`
- If the user catches a tuple of exceptions (`except (foo, bar, baz):`)
and multiple elements in the tuple are invalid, we now collect these
into a single diagnostic rather than emitting a separate diagnostic for
each tuple element
- The explanation of why the `except`/`raise` was invalid ("must be a
`BaseException` instance or `BaseException` subclass", etc.) is
relegated to a subdiagnostic. This makes the top-level diagnostic
summary much more concise.

## Test Plan

Lots of snapshots. And here's some screenshots:

<details>
<summary>Screenshots</summary>

<img width="1770" height="1520" alt="image"
src="https://github.com/user-attachments/assets/7f27fd61-c74d-4ddf-ad97-ea4fd24d06fd"
/>

<img width="1916" height="1392" alt="image"
src="https://github.com/user-attachments/assets/83e5027c-8798-48a6-a0ec-1babfc134000"
/>

<img width="1696" height="588" alt="image"
src="https://github.com/user-attachments/assets/1bc16048-6eb4-4dfa-9ace-dd271074530f"
/>

</details>
2025-11-15 22:12:00 +00:00
Brent Westbrook
fb5b8c3653 Fix default test executable in py-fuzzer (#21468)
Summary
--

I was firing up the fuzzer tonight and hit an assertion error here. We
now build with the `profiling` profile, so we need to use that
executable too.

This hasn't affected CI because we always set the `--test-executable`.

Test Plan
--

Ran the script again with the same arguments on this branch
2025-11-15 13:12:10 -05:00
github-actions[bot]
efa2b5167f [ty] Sync vendored typeshed stubs (#21466)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-11-15 17:12:32 +00:00
David Peter
29acc1e860 [ty] Support class-arguments for dataclass transformers (#21457)
## Summary

Allow metaclass-based and baseclass-based dataclass-transformers to
overwrite the default behavior using class arguments:

```py
class Person(Model, order=True):
    # ...
```

## Conformance tests

Four new tests passing!

## Test Plan

New Markdown tests
2025-11-15 17:47:48 +01:00
Douglas Creager
698231a47a [ty] Implement constraint implication for compound types (#21366)
This PR updates the constraint implication type relationship to work on
compound types as well. (A compound type is a non-atomic type, like
`list[T]`.)

The goal of constraint implication is to check whether the requirements
of a constraint imply that a particular subtyping relationship holds.
Before, we were only checking atomic typevars. That would let us verify
that the constraint set `T ≤ bool` implies that `T` is always a subtype
of `int`. (In this case, the lhs of the subtyping check, `T`, is an
atomic typevar.)

But we weren't recursing into compound types, to look for nested
occurrences of typevars. That means that we weren't able to see that `T
≤ bool` implies that `Covariant[T]` is always a subtype of
`Covariant[int]`.

Doing this recursion means that we have to carry the constraint set
along with us as we recurse into types as part of `has_relation_to`, by
adding constraint implication as a new `TypeRelation` variant. (Before
it was just a method on `ConstraintSet`.)

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-11-14 18:43:00 -05:00
Alex Waygood
d63b4b0383 [ty] Improve diagnostic range for non-subscriptable diagnostics (#21461)
## Summary

Currently our diagnostic only covers the range of the thing being
subscripted:

<img width="1702" height="312" alt="image"
src="https://github.com/user-attachments/assets/7e630431-e846-46ca-93c1-139f11aaba11"
/>

But it should probably cover the _whole_ subscript expression (arguably
the more "incorrect" bit is the `["foo"]` part of this expression, not
the `x` part of this expression!)

## Test Plan

Added a snapshot

Co-authored-by: Brent Westbrook
<36778786+ntBre@users.noreply.github.com>
2025-11-14 21:15:14 +00:00
Ibraheem Ahmed
c5d654bce8 [ty] Improve literal promotion heuristics (#21439)
## Summary

Extends literal promotion to apply to any generic method, as opposed to
only generic class constructors. This PR also improves our literal
promotion heuristics to only promote literals in non-covariant position
in the return type, and avoid promotion if the literal is present in
non-covariant position in any argument type.

Resolves https://github.com/astral-sh/ty/issues/1357.
2025-11-14 16:13:56 -05:00
Alex Waygood
3e7e91724c [ty] Further improve details around which expressions should be deferred in stub files (#21456)
## Summary

- Always restore the previous `deferred_state` after parsing a type
expression: we don't want that state leaking out into other contexts
where we shouldn't be deferring expression inference
- Always defer the right-hand-side of a PEP-613 type alias in a stub
file, allowing for forward references on the right-hand side of `T:
TypeAlias = X | Y` in a stub file

Addresses @carljm's review in
https://github.com/astral-sh/ruff/pull/21401#discussion_r2524260153

## Test Plan

I added a regression test for a regression that the first version of
this PR introduced (we need to make sure the r.h.s. of a PEP-613
`TypeAlias`es is always deferred in a stub file)
2025-11-14 21:07:02 +00:00
Ibraheem Ahmed
2a2b719f00 [ty] Improve generic class constructor inference (#21442)
## Summary

We currently fail to account for the type context when inferring generic
classes constructed with `__new__`, or synthesized `__init__` for
dataclasses.
2025-11-14 20:25:47 +00:00
Ibraheem Ahmed
ffb7bdd595 [ty] Propagate type context through conditional expressions (#21443)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1543.
2025-11-14 15:19:08 -05:00
Andrew Gallant
0a55327d64 [ty] Suppress completions when introducing names with as
There are a few places in Python where it is known that new names are
being introduced and thus we probably shouldn't offer completions. We
already handle this today for things like `class <CURSOR>` and `def
<CURSOR>`. But we didn't handle `as <CURSOR>`, which can appear in
`import`, `with`, `except` and `match` statements. Indeed, these are
exactly the 4 cases where the `as` keyword can occur. So we look for the
presence of `as` and suppress completions based on that.

While we're here, we also make the implementation a bit more robust with
respect to suppressing completions when the user hasn't typed anything.
Namely, previously, we'd still offer completions in a `class <CURSOR>`
context. But it looks like LSP clients (at least, VS Code) doesn't ask
for completions here, so we were "saved" incidentally. This PR detects
this case and suppresses completions there so we don't rely on LSP
client behavior to handle that case correctly.

Fixes astral-sh/ty#1287
2025-11-14 14:21:07 -05:00
Micha Reiser
008e9d06e1 [ty] Add panic-by-default await methods to TestServer (#21451) 2025-11-14 19:47:39 +01:00
Bhuminjay Soni
8529d79a70 [ty] name is parameter and global is a syntax error (#21312)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-11-14 18:15:34 +00:00
Alex Waygood
8599c7e5b3 [ty] Fixup a few details around version-specific dataclass features (#21453) 2025-11-14 15:04:55 +00:00
Alex Waygood
5f501374c4 [ty] Support attribute-expression TYPE_CHECKING conditionals (#21449) 2025-11-14 13:11:49 +00:00
David Peter
e9a5337136 [ty] Support stringified annotations in value-position Annotated instances (#21447)
## Summary

Infer the first argument `type` inside `Annotated[type, …]` as a type
expression. This allows us to support stringified annotations inside
`Annotated`.

## Ecosystem

* The removed diagnostic on `prefect` shows that we now understand the
`State.data` type annotation in
`src/prefect/client/schemas/objects.py:230`, which uses a stringified
annotation in `Annoated`. The other diagnostics are downstream changes
that result from this, it seems to be a commonly used data type.
* `artigraph` does something like `Annotated[cast(Any,
field_info.annotation), *field_info.metadata]` which I'm not sure we
need to allow? It's unfortunate since this is probably supported at
runtime, but it seems reasonable that they need to add a `# type:
ignore` for that.
* `pydantic` uses something like `Annotated[(self.annotation,
*self.metadata)]` but adds a `# type: ignore`

## Test Plan

New Markdown test
2025-11-14 13:09:09 +00:00
David Peter
05cf53aae8 [ty] Type inference for genererator expressions (#21437)
## Summary

Add type inference for (async) generator expressions.

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

## Test Plan

New Markdown tests.
2025-11-14 13:04:11 +00:00
David Peter
6a26f86778 [ty] Make __getattr__ available for ModuleType instances (#21450)
## Summary

Typeshed has a (fake) `__getattr__` method on `types.ModuleType` with a
return type of `Any`. We ignore this method when accessing attributes on
module *literals*, but with this PR, we respect this method when dealing
with `ModuleType` itself. That is, we allow arbitrary attribute accesses
on instances of `types.ModuleType`. This is useful because dynamic
import mechanisms such as `importlib.import_module` use `ModuleType` as
a return type.

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

## Ecosystem

Massive reduction in diagnostics. The few new diagnostics are true
positives.

## Test Plan

Added regression test.
2025-11-14 13:59:14 +01:00
Micha Reiser
d0314131fb [ty] Increase default receive timeout in tests to 10s (#21448) 2025-11-14 13:15:22 +01:00
David Peter
696d7a5d68 [ty] Add synthetic members to completions on dataclasses (#21446)
## Summary

Add synthetic members to completions on dataclasses and dataclass
instances.

Also, while we're at it, add support for `__weakref__` and
`__match_args__`.

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

## Test Plan

New Markdown tests
2025-11-14 11:31:20 +01:00
David Peter
66e9d57797 [ty] Support legacy typing special forms in implicit type aliases (#21433)
## Summary

Support various legacy `typing` special forms (`List`, `Dict`, …) in
implicit type aliases.

## Ecosystem impact

A lot of true positives (e.g. on `alerta`)!

## Test Plan

New Markdown tests
2025-11-14 09:08:58 +01:00
Brent Westbrook
87dafb8787 Bump 0.14.5 (#21435) 2025-11-13 14:37:31 -05:00
David Peter
9e80e5a3a6 [ty] Support type[…] and Type[…] in implicit type aliases (#21421)
## Summary

Support `type[…]` in implicit type aliases, for example:
```py
SubclassOfInt = type[int]

reveal_type(SubclassOfInt)  # GenericAlias

def _(subclass_of_int: SubclassOfInt):
    reveal_type(subclass_of_int)  # type[int]
```

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

## Typing conformance

```diff
-specialtypes_type.py:138:5: error[type-assertion-failure] Argument does not have asserted type `type[Any]`
-specialtypes_type.py:140:5: error[type-assertion-failure] Argument does not have asserted type `type[Any]`
```

Two new tests passing ✔️ 

```diff
-specialtypes_type.py:146:1: error[unresolved-attribute] Object of type `GenericAlias` has no attribute `unknown`
```

An `TA4.unknown` attribute on a PEP 613 alias (`TA4: TypeAlias =
type[Any]`) is being accessed, and the conformance suite expects this to
be an error. Since we currently use the inferred type for these type
aliases (and possibly in the future as well), we treat this as a direct
access of the attribute on `type[Any]`, which falls back to an access on
`Any` itself, which succeeds. 🔴

```
+specialtypes_type.py:152:16: error[invalid-type-form] `typing.TypeVar` is not a generic class
+specialtypes_type.py:156:16: error[invalid-type-form] `typing.TypeVar` is not a generic class
```

New errors because we don't handle `T = TypeVar("T"); MyType = type[T];
MyType[T]` yet. Support for this is being tracked in
https://github.com/astral-sh/ty/issues/221 🔴

## Ecosystem impact

Looks mostly good, a few known problems. 

## Test Plan

New Markdown tests
2025-11-13 19:02:24 +01:00
Micha Reiser
f9cc26aa12 [ty] Respect notebook cell boundaries when adding an auto import (#21322) 2025-11-13 18:58:08 +01:00
charliecloudberry
d49c326309 Update PyCharm setup instructions (#21409)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-13 18:54:08 +01:00
Micha Reiser
e70fccbf25 [ty] Improve LSP test server logging (#21432) 2025-11-13 18:29:54 +01:00
Alex Waygood
90b32f3b3b [ty] Ensure annotation/type expressions in stub files are always deferred (#21401) 2025-11-13 17:14:54 +00:00
Micha Reiser
99694b6e4a Use profiling profile for cargo test(linux, release) (#21429) 2025-11-13 16:27:36 +01:00
Micha Reiser
67e54fffe1 [ty] Fix panic for cyclic star imports (#21428) 2025-11-13 15:38:09 +01:00
David Peter
a01b0d7780 [ty] Press 'enter' to rerun all mdtests (#21427)
## Summary

Allow users of `mdtest.py` to press enter to rerun all mdtests without
recompiling (thanks @AlexWaygood).

I swear I tried three other approaches (including a fully async version)
before I settled on this solution. It is indeed silly, but works just
fine.

## Test Plan

Interactive playing around
2025-11-13 15:34:17 +01:00
David Peter
04ab9170d6 [ty] Further improve subscript assignment diagnostics (#21411)
## Summary

Further improve subscript assignment diagnostics, especially for
`dict`s:

```py
config: dict[str, int] = {}

config["retries"] = "three"
```

<img width="1276" height="274" alt="image"
src="https://github.com/user-attachments/assets/9762c733-8d1c-4a57-8c8a-99825071dc7d"
/>

I have many more ideas, but this looks like a reasonable first step.
Thank you @AlexWaygood for some of the suggestions here.

## Test Plan

Update tests
2025-11-13 13:31:14 +01:00
Micha Reiser
12e74ae894 [ty] Support for notebooks in VS Code (#21175) 2025-11-13 13:23:19 +01:00
David Peter
d64b2f747c [ty] Add filtering option for mdtest runner (#21422)
## Summary

This change to the mdtest runner makes it easy to run on a subset of
tests/files. For example:

```
▶ uv run crates/ty_python_semantic/mdtest.py implicit
running 1 test
test mdtest__implicit_type_aliases ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 281 filtered out; finished in 0.83s

Ready to watch for changes...
```

Subsequent changes to either that test file or the Rust source code will
also only rerun the `implicit_type_aliases` test.

Multiple arguments can be provided, and filters can either be partial
file paths (`loops/for.md`, `loops/for`, `for`) or mangled test names
(`loops_for`):
```
▶ uv run crates/ty_python_semantic/mdtest.py implicit binary/union

running 2 tests
test mdtest__binary_unions ... ok
test mdtest__implicit_type_aliases ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 280 filtered out; finished in 0.85s

Ready to watch for changes...
```

## Test Plan

Tested it interactively for a while
2025-11-13 12:20:31 +00:00
Alex Waygood
cd183c5e1f [ty] Use the return type of __get__ for descriptor lookups even when __get__ is called with incorrect arguments (#21424) 2025-11-13 12:05:10 +00:00
Luca Chiodini
eb1957cd17 [ty] Set definition modifier for parameter declarations when computing semantic tokens (#21420) 2025-11-13 10:14:41 +01:00
Dhruv Manilawala
7e3dd0764a [ty] Rename matching overload index field (#21419)
## Summary

This PR renames the `CallableBinding::matching_overload_index` field to
`CallableBinding::matching_overload_after_parameter_matching` to clarify
the main use case of this field which is to surface type checking errors
on the matching overloads directly instead of using the
`no-matching-overload` diagnostic. This can only happen after parameter
matching as following steps could filter out this overload which should
then result in `no-matching-overload` diagnostic.

Callers should use the `matching_overload_index` _method_ to get the
matching overloads.
2025-11-13 08:36:59 +00:00
Dan Parizher
a6abd65c2c [pydoclint] Fix false positive when Sphinx directives follow Raises section (DOC502) (#20535)
## Summary

Fixes #18959

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-12 21:37:55 +00:00
Aria Desires
3d4b0559f1 [ty] remove erroneous canonicalize (#21405)
Alternative implementation to
https://github.com/astral-sh/ruff/pull/21052
2025-11-12 15:47:33 -05:00
David Peter
2f6f3e1042 [ty] Faster subscript assignment checks for (unions of) TypedDicts (#21378)
## Summary

We synthesize a (potentially large) set of `__setitem__` overloads for
every item in a `TypedDict`. Previously, validation of subscript
assignments on `TypedDict`s relied on actually calling `__setitem__`
with the provided key and value types, which implied that we needed to
do the full overload call evaluation for this large set of overloads.
This PR improves the performance of subscript assignment checks on
`TypedDict`s by validating the assignment directly instead of calling
`__setitem__`.

This PR also adds better handling for assignments to subscripts on union
and intersection types (but does not attempt to make it perfect). It
achieves this by distributing the check over unions and intersections,
instead of calling `__setitem__` on the union/intersection directly. We
already do something similar when validating *attribute* assignments.

## Ecosystem impact

* A lot of diagnostics change their rule type, and/or split into
multiple diagnostics. The new version is more verbose, but easier to
understand, in my opinion
* Almost all of the invalid-key diagnostics come from pydantic, and they
should all go away (including many more) when we implement
https://github.com/astral-sh/ty/issues/1479
* Everything else looks correct to me. There may be some new diagnostics
due to the fact that we now check intersections.

## Test Plan

New Markdown tests.
2025-11-12 20:16:38 +01:00
Shunsuke Shibayama
9dd666d677 [ty] fix global symbol lookup from eager scopes (#21317)
## Summary

cf. https://github.com/astral-sh/ruff/pull/20962

In the following code, `foo` in the comprehension was not reported as
unresolved:

```python
# error: [unresolved-reference] "Name `foo` used when not defined"
foo
foo = [
    # no error!
    # revealed: Divergent
    reveal_type(x) for _ in () for x in [foo]
]

baz = [
    # error: [unresolved-reference] "Name `baz` used when not defined"
    # revealed: Unknown
    reveal_type(x) for _ in () for x in [baz]
]
```

In fact, this is a more serious bug than it looks: for `foo`,
[`explicit_global_symbol` is
called](6cc3393ccd/crates/ty_python_semantic/src/types/infer/builder.rs (L8052)),
causing a symbol that should actually be `Undefined` to be reported as
being of type `Divergent`.

This PR fixes this bug. As a result, the code in
`mdtest/regression/pr_20962_comprehension_panics.md` no longer panics.

## Test Plan

`corpus\cyclic_symbol_in_comprehension.py` is added.
New tests are added in `mdtest/comprehensions/basic.md`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-11-12 10:15:51 -08:00
pyscripter
a1d9cb5830 Added the PyScripter IDE to the list of "Who is using Ruff?" (#21402)
## Summary

Added the PyScripter IDE to the list of "Who is using Ruff?".

PyScripter is a popular python IDE that is using ruff for code
diagnostics, fixes and code formatting.
2025-11-12 18:10:08 +00:00
Nikolas Hearp
8a85a2961e [flake8-simplify] Apply SIM113 when index variable is of type int (#21395)
## Summary

Fixes #21393

Now the rule checks if the index variable is initialized as an `int`
type rather than only flagging if the index variable is initialized to
`0`. I used `ResolvedPythonType` to check if the index variable is an
`int` type.

## Test Plan

Updated snapshot test for `SIM113`.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-11-12 17:54:39 +00:00
Micha Reiser
43427abb61 [ty] Improve semantic token classification for names (#21399) 2025-11-12 16:34:26 +00:00
David Peter
84c3cecad6 [ty] Baseline for subscript assignment diagnostics (#21404)
## Summary

Add (snapshot) tests for subscript assignment diagnostics. This is
mainly intended to establish a baseline before I hope to improve some of
these messages.
2025-11-12 15:29:26 +01:00
David Peter
e8e8180888 [ty] Implicit type aliases: Add support for typing.Union (#21363)
## Summary

Add support for `typing.Union` in implicit type aliases / in value
position.

## Typing conformance tests

Two new tests are passing

## Ecosystem impact

* The 2k new `invalid-key` diagnostics on pydantic are caused by
https://github.com/astral-sh/ty/issues/1479#issuecomment-3513854645.
* Everything else I've checked is either a known limitation (often
related to type narrowing, because union types are often narrowed down
to a subset of options), or a true positive.

## Test Plan

New Markdown tests
2025-11-12 12:59:14 +01:00
David Peter
f5cf672ed4 [ty] Reorganize walltime benchmarks (#21400) 2025-11-12 12:41:34 +01:00
David Peter
6322f37015 [ty] Better assertion message for benchmark diagnostics check (#21398)
I don't know why, but it always takes me an eternity to find the failing
project name a few lines below in the output. So I'm suggesting we just
add the project name to the assertion message.
2025-11-12 11:02:29 +01:00
Micha Reiser
d272a623d3 [ty] Fix goto for float and complex in type annotation positions (#21388) 2025-11-12 07:54:25 +00:00
Micha Reiser
19c7994e90 [ty] Fix Escape handler in playground (#21397) 2025-11-12 08:54:14 +01:00
Dan Parizher
725ae69773 [pydoclint] Support NumPy-style comma-separated parameters (DOC102) (#20972) 2025-11-12 08:29:23 +01:00
Bhuminjay Soni
d2c3996f4e UP035: Consistently set the deprecated tag (#21396) 2025-11-12 08:17:29 +01:00
Shaygan Hooshyari
988c38c013 [ty] Skip eagerly evaluated scopes for attribute storing (#20856)
## Summary

Fix https://github.com/astral-sh/ty/issues/664

This PR adds support for storing attributes in comprehension scopes (any
eager scope.)

For example in the following code we infer type of `z` correctly:

```py
class C:
    def __init__(self):
        [None for self.z in range(1)]
reveal_type(C().z) # previously [unresolved-attribute] but now shows Unknown | int
```

The fix works by adjusting the following logics:

To identify if an attriute is an assignment to self or cls we need to
check the scope is a method. To allow comprehension scopes here we skip
any eager scope in the check.
Also at this stage the code checks if self or the first method argument
is shadowed by another binding that eager scope to prevent this:

```py
class D:
    g: int

class C:
    def __init__(self):
        [[None for self.g in range(1)] for self in [D()]]
reveal_type(C().g) # [unresolved-attribute]
```

When determining scopes that attributes might be defined after
collecting all the methods of the class the code also returns any
decendant scope that is eager and only has eager parents until the
method scope.

When checking reachability of a attribute definition if the attribute is
defined in an eager scope we use the reachability of the first non eager
scope which must be a method. This allows attributes to be marked as
reachable and be seen.


There are also which I didn't add support for:

```py
class C:
    def __init__(self):
        def f():
            [None for self.z in range(1)]
        f()

reveal_type(C().z) # [unresolved-attribute]
```

In the above example we will not even return the comprehension scope as
an attribute scope because there is a non eager scope (`f` function)
between the comprehension and the `__init__` method

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-11-11 14:45:34 -08:00
Andrew Gallant
164c2a6cc6 [ty] Sort keyword completions above everything else
It looks like VS Code does this forcefully. As in, I don't think we can
override it. It also seems like a plausibly good idea. But by us doing
it too, it makes our completion evaluation framework match real world
conditions. (To the extent that "VS Code" and "real world conditions"
are the same. Which... they aren't. But it's close, since VS Code is so
popular.)
2025-11-11 17:20:55 -05:00
Andrew Gallant
1bbe4f0d5e [ty] Add more keyword completions to scope completions
This should round out the rest of the set. I think I had hesitated doing
this before because some of these don't make sense in every context. But
I think identifying the correct context for every keyword could be quite
difficult. And at the very least, I think offering these at least as a
choice---even if they aren't always correct---is better than not doing
it at all.
2025-11-11 17:20:55 -05:00
Andrew Gallant
cd7354a5c6 [ty] Add completion evaluation task for general keyword completions 2025-11-11 17:20:55 -05:00
Andrew Gallant
ec48a47a88 [ty] Add from <module> im<CURSOR> completion evaluation task
Ideally this would have been added as part of #21291, but I forgot.
2025-11-11 17:20:55 -05:00
Alex Waygood
43297d3455 [ty] Support isinstance() and issubclass() narrowing when the second argument is a typing.py stdlib alias (#21391)
## Summary

A followup to https://github.com/astral-sh/ruff/pull/21386

## Test Plan

New mdtests added
2025-11-11 21:09:24 +00:00
Mahmoud Saada
4373974dd9 [ty] Fix false positive for Final attribute assignment in __init__ (#21158)
## Summary

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

This PR allows `Final` instance attributes to be initialized in
`__init__` methods, as mandated by the Python typing specification (PEP
591). Previously, ty incorrectly prevented this initialization, causing
false positive errors.

The fix checks if we're inside an `__init__` method before rejecting
Final attribute assignments, allowing assignments during
instance initialization while still preventing reassignment elsewhere.

## Test Plan

- Added new test coverage in `final.md` for the reported issue with
`Self` annotations
- Updated existing tests that were incorrectly expecting errors 
- All 278 mdtest tests pass
- Manually tested with real-world code examples

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-11-11 12:54:05 -08:00
Aria Desires
e4374f14ed [ty] Consider from thispackage import y to re-export y in __init__.pyi (#21387)
Fixes https://github.com/astral-sh/ty/issues/1487

This one is a true extension of non-standard semantics, and is therefore
a certified Hot Take we might conclude is simply a Bad Take (let's see
what ecosystem tests say...).
2025-11-11 14:41:14 -05:00
Alex Waygood
03bd0619e9 [ty] Silence false-positive diagnostics when using typing.Dict or typing.Callable as the second argument to isinstance() (#21386) 2025-11-11 19:30:01 +00:00
Aria Desires
bd8812127d [ty] support absolute from imports introducing local submodules in __init__.py files (#21372)
By resolving `.` and the LHS of the from import during semantic
indexing, we can check if the LHS is a submodule of `.`, and handle
`from whatever.thispackage.x.y import z` exactly like we do `from .x.y
import z`.

Fixes https://github.com/astral-sh/ty/issues/1484
2025-11-11 13:04:42 -05:00
Alex Waygood
44b0c9ebac [ty] Allow PEP-604 unions in stubs and TYPE_CHECKING blocks prior to 3.10 (#21379) 2025-11-11 14:33:43 +00:00
Micha Reiser
7b237d316f Add option to provide a reason to --add-noqa (#21294)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-11 14:03:46 +01:00
Micha Reiser
36cce347fd Reduce notebook memory footprint (#21319) 2025-11-11 10:43:37 +01:00
Douglas Creager
33b942c7ad [ty] Handle annotated self parameter in constructor of non-invariant generic classes (#21325)
This manifested as an error when inferring the type of a PEP-695 generic
class via its constructor parameters:

```py
class D[T, U]:
    @overload
    def __init__(self: "D[str, U]", u: U) -> None: ...
    @overload
    def __init__(self, t: T, u: U) -> None: ...
    def __init__(self, *args) -> None: ...

# revealed: D[Unknown, str]
# SHOULD BE: D[str, str]
reveal_type(D("string"))
```

This manifested because `D` is inferred to be bivariant in both `T` and
`U`. We weren't seeing this in the equivalent example for legacy
typevars, since those default to invariant. (This issue also showed up
for _covariant_ typevars, so this issue was not limited to bivariance.)

The underlying cause was because of a heuristic that we have in our
current constraint solver, which attempts to handle situations like
this:

```py
def f[T](t: T | None): ...
f(None)
```

Here, the `None` argument matches the non-typevar union element, so this
argument should not add any constraints on what `T` can specialize to.
Our previous heuristic would check for this by seeing if the argument
type is a subtype of the parameter annotation as a whole — even if it
isn't a union! That would cause us to erroneously ignore the `self`
parameter in our constructor call, since bivariant classes are
equivalent to each other, regardless of their specializations.

The quick fix is to move this heuristic "down a level", so that we only
apply it when the parameter annotation is a union. This heuristic should
go away completely 🤞 with the new constraint solver.
2025-11-10 19:46:49 -05:00
Aria Desires
9ce3230add [ty] Make implicit submodule imports only occur in global scope (#21370)
This loses any ability to have "per-function" implicit submodule
imports, to avoid the "ok but now we need per-scope imports" and "ok but
this should actually introduce a global that only exists during this
function" problems. A simple and clean implementation with no weird
corners.

Fixes https://github.com/astral-sh/ty/issues/1482
2025-11-10 18:59:48 -05:00
Aria Desires
2bc6c78e26 [ty] introduce local variables for from imports of submodules in __init__.py(i) (#21173)
This rips out the previous implementation in favour of a new
implementation with 3 rules:

- **froms are locals**: a `from..import` can only define locals, it does
not have global
side-effects. Specifically any submodule attribute `a` that's implicitly
introduced by either
`from .a import b` or `from . import a as b` (in an `__init__.py(i)`) is
a local and not a
global. If you do such an import at the top of a file you won't notice
this. However if you do
such an import in a function, that means it will only be function-scoped
(so you'll need to do
it in every function that wants to access it, making your code less
sensitive to execution
    order).

- **first from first serve**: only the *first* `from..import` in an
`__init__.py(i)` that imports a
particular direct submodule of the current package introduces that
submodule as a local.
Subsequent imports of the submodule will not introduce that local. This
reflects the fact that
in actual python only the first import of a submodule (in the entire
execution of the program)
introduces it as an attribute of the package. By "first" we mean "the
first time in this scope
(or any parent scope)". This pairs well with the fact that we are
specifically introducing a
local (as long as you don't accidentally shadow or overwrite the local).

- **dot re-exports**: `from . import a` in an `__init__.pyi` is
considered a re-export of `a`
(equivalent to `from . import a as a`). This is required to properly
handle many stubs in the
    wild. Currently it must be *exactly* `from . import ...`.
    
This implementation is intentionally limited/conservative (notably,
often requiring a from import to be relative). I'm going to file a ton
of followups for improvements so that their impact can be evaluated
separately.


Fixes https://github.com/astral-sh/ty/issues/133
2025-11-10 23:04:56 +00:00
Dan Parizher
1fd852fb3f [ruff] Ignore str() when not used for simple conversion (RUF065) (#21330)
## Summary

Fixed RUF065 (`logging-eager-conversion`) to only flag `str()` calls
when they perform a simple conversion that can be safely removed. The
rule now ignores `str()` calls with no arguments, multiple arguments,
starred arguments, or keyword unpacking, preventing false positives.

Fixes #21315

## Problem Analysis

The RUF065 rule was incorrectly flagging all `str()` calls in logging
statements, even when `str()` was performing actual conversion work
beyond simple type coercion. Specifically, the rule flagged:

- `str()` with no arguments - which returns an empty string
- `str(b"data", "utf-8")` with multiple arguments - which performs
encoding conversion
- `str(*args)` with starred arguments - which unpacks arguments
- `str(**kwargs)` with keyword unpacking - which passes keyword
arguments

These cases cannot be safely removed because `str()` is doing meaningful
work (encoding conversion, argument unpacking, etc.), not just redundant
type conversion.

The root cause was that the rule only checked if the function was
`str()` without validating the call signature. It didn't distinguish
between simple `str(value)` conversions (which can be removed) and more
complex `str()` calls that perform actual work.

## Approach

The fix adds validation to the `str()` detection logic in
`logging_eager_conversion.rs`:

1. **Check argument count**: Only flag `str()` calls with exactly one
positional argument (`str_call_args.args.len() == 1`)
2. **Check for starred arguments**: Ensure the single argument is not
starred (`!str_call_args.args[0].is_starred_expr()`)
3. **Check for keyword arguments**: Ensure there are no keyword
arguments (`str_call_args.keywords.is_empty()`)

This ensures the rule only flags cases like `str(value)` where `str()`
is truly redundant and can be removed, while ignoring cases where
`str()` performs actual conversion work.

The fix maintains backward compatibility - all existing valid test cases
continue to be flagged correctly, while the new edge cases are properly
ignored.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-10 18:04:41 -05:00
Jack O'Connor
5f3e086ee4 [ty] implement typing.NewType by adding Type::NewTypeInstance 2025-11-10 14:55:47 -08:00
Aria Desires
039a69fa8c [ty] supress inlay hints for +1 and -1 (#21368)
It's everyone's favourite language corner case!

Also having kicked the tires on it, I'm pretty happy to call this (in
conjunction with #21367):

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

There's cases where you can make noisy Literal hints appear, so we can
always iterate on it, but this handles like, 98% of the cases in the
wild, which is great.

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-11-10 21:56:25 +00:00
Ibraheem Ahmed
3656b44877 [ty] Use type context for inference of generic constructors (#20933)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1228.

This PR is stacked on https://github.com/astral-sh/ruff/pull/21210.
2025-11-10 21:49:48 +00:00
Ibraheem Ahmed
98869f0307 [ty] Improve generic call expression inference (#21210)
## Summary

Implements https://github.com/astral-sh/ty/issues/1356 and https://github.com/astral-sh/ty/issues/136#issuecomment-3413669994.
2025-11-10 21:29:05 +00:00
Aria Desires
d258302b08 [ty] supress some trivial expr inlay hints (#21367)
I'm not 100% sold on this implementation, but it's a strict improvement
and it adds a ton of snapshot tests for future iteration.

Part of https://github.com/astral-sh/ty/issues/494
2025-11-10 19:51:14 +00:00
Dan Parizher
deeda56906 [configuration] Fix unclear error messages for line-length values exceeding u16::MAX (#21329)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-10 18:29:35 +00:00
justin
f63a9f2334 [ty] Fix incorrect inference of enum.auto() for enums with non-int mixins, and imprecise inference of enum.auto() for single-member enums (#20541)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-11-10 17:53:08 +00:00
Dan Parizher
e4dc406a3d [refurb] Detect empty f-strings (FURB105) (#21348)
## Summary

Fixes FURB105 (`print-empty-string`) to detect empty f-strings in
addition to regular empty strings. Previously, the rule only flagged
`print("")` but missed `print(f"")`. This fix ensures both cases are
detected and can be automatically fixed.

Fixes #21346

## Problem Analysis

The FURB105 rule checks for unnecessary empty strings passed to
`print()` calls. The `is_empty_string` helper function was only checking
for `Expr::StringLiteral` with empty values, but did not handle
`Expr::FString` (f-strings). As a result, `print(f"")` was not being
flagged as a violation, even though it's semantically equivalent to
`print("")` and should be simplified to `print()`.

The issue occurred because the function used a `matches!` macro that
only checked for string literals:

```rust
fn is_empty_string(expr: &Expr) -> bool {
    matches!(
        expr,
        Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) if value.is_empty()
    )
}
```

## Approach

1. **Import the helper function**: Added `is_empty_f_string` to the
imports from `ruff_python_ast::helpers`, which already provides logic to
detect empty f-strings.

2. **Update `is_empty_string` function**: Changed the implementation
from a `matches!` macro to a `match` expression that handles both string
literals and f-strings:

   ```rust
   fn is_empty_string(expr: &Expr) -> bool {
       match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) =>
value.is_empty(),
           Expr::FString(f_string) => is_empty_f_string(f_string),
           _ => false,
       }
   }
   ```

The fix leverages the existing `is_empty_f_string` helper function which
properly handles the complexity of f-strings, including nested f-strings
and interpolated expressions. This ensures the detection is accurate and
consistent with how empty strings are detected elsewhere in the
codebase.
2025-11-10 12:41:44 -05:00
Matthew Mckee
1d188476b6 [ty] provide import completion when in from <name> <name> statement (#21291)
<!--
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

Resolves https://github.com/astral-sh/ty/issues/1494

## Test Plan

Add a test showing if we are in `from <name> <name> ` we provide the
keyword completion "import"
2025-11-10 11:59:45 -05:00
Aria Desires
4821c050ef [ty] elide redundant inlay hints for function args (#21365)
This elides the following inlay hints:

```py
foo([x=]x)
foo([x=]y.x)
foo([x=]x[0])
foo([x=]x(...))

# composes to complex situations
foo([x=]y.x(..)[0])
```

Fixes https://github.com/astral-sh/ty/issues/1514
2025-11-10 11:42:12 -05:00
Brent Westbrook
835e31b3ff Fix syntax error false positive on alternative match patterns (#21362)
Summary
--

Fixes #21360 by using the union of names instead of overwriting them, as
Micha suggested originally on #21104.

This avoids overwriting the `n` name in the `Subscript` by the empty set
of names visited in the nested OR pattern before visiting the other arm
of the outer OR pattern.

Test Plan
--

A new inline test case taken from the issue
2025-11-10 10:51:51 -05:00
Brent Westbrook
8d1efe964a Add a new "Opening a PR" section to the contribution guide (#21298)
Summary
--

This PR adds a new section to CONTRIBUTING.md describing the expected
contents of the PR summary and test plan, using the ecosystem report,
and communicating the status of a PR.

This seemed like a pretty good place to insert this in the document, at
the end of the advice on preparing actual code changes, but I'm
certainly open to other suggestions about both the content and
placement.

Test Plan
--

Future PRs :)

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-10 14:12:32 +00:00
Dan Parizher
04e7cecab3 [flake8-simplify] Fix SIM222 false positive for tuple(generator) or None (SIM222) (#21187)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-10 14:27:31 +01:00
Micha Reiser
84a810736d Rebuild ruff binary instead of sharing it across jobs (#21361) 2025-11-10 14:27:07 +01:00
Micha Reiser
f44598dc11 [ty] Fix --exclude and src.exclude merging (#21341) 2025-11-10 12:52:45 +01:00
David Peter
ab46c8de0f [ty] Add support for properties that return Self (#21335)
## Summary

Detect usages of implicit `self` in property getters, which allows us to
treat their signature as being generic.

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

## Typing conformance

Two new type assertions that are succeeding.

## Ecosystem results

Mostly look good. There are a few new false positives related to a bug
with constrained typevars that is unrelated to the work here. I reported
this as https://github.com/astral-sh/ty/issues/1503.

## Test Plan

Added regression tests.
2025-11-10 11:13:36 +01:00
Henry Schreiner
a6f2dee33b Add upstream linter URL to ruff linter --output-format=json (#21316) 2025-11-10 10:42:09 +01:00
David Peter
238f151371 [ty] Add support for Optional and Annotated in implicit type aliases (#21321)
## Summary

Add support for `Optional` and `Annotated` in implicit type aliases

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

## Typing conformance changes

New expected diagnostics.

## Ecosystem

A lot of true positives, some known limitations unrelated to this PR.

## Test Plan

New Markdown tests
2025-11-10 10:24:38 +01:00
Micha Reiser
3fa609929f Save rust cache for CI jobs on main only (#21359) 2025-11-10 10:04:44 +01:00
Alex Waygood
73b1fce74a [ty] Add diagnostics for isinstance() and issubclass() calls that use invalid PEP-604 unions for their second argument (#21343)
## Summary

This PR adds extra validation for `isinstance()` and `issubclass()`
calls that use `UnionType` instances for their second argument.
According to typeshed's annotations, any `UnionType` is accepted for the
second argument, but this isn't true at runtime: at runtime, all
elements in the `UnionType` must either be class objects or be `None` in
order for the `isinstance()` or `issubclass()` call to reliably succeed:

```pycon
% uvx python3.14                            
Python 3.14.0 (main, Oct 10 2025, 12:54:13) [Clang 20.1.4 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import LiteralString
>>> import types
>>> type(LiteralString | int) is types.UnionType
True
>>> isinstance(42, LiteralString | int)
Traceback (most recent call last):
  File "<python-input-5>", line 1, in <module>
    isinstance(42, LiteralString | int)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/alexw/Library/Application Support/uv/python/cpython-3.14.0-macos-aarch64-none/lib/python3.14/typing.py", line 559, in __instancecheck__
    raise TypeError(f"{self} cannot be used with isinstance()")
TypeError: typing.LiteralString cannot be used with isinstance()
```

## Test Plan

Added mdtests/snapshots
2025-11-10 08:46:31 +00:00
renovate[bot]
52bd22003b Update Rust crate libcst to v1.8.6 (#21355)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:39:46 +01:00
renovate[bot]
0a6d6b6194 Update cargo-bins/cargo-binstall action to v1.15.11 (#21352)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cargo-bins/cargo-binstall](https://redirect.github.com/cargo-bins/cargo-binstall)
| action | patch | `v1.15.10` -> `v1.15.11` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>cargo-bins/cargo-binstall (cargo-bins/cargo-binstall)</summary>

###
[`v1.15.11`](https://redirect.github.com/cargo-bins/cargo-binstall/releases/tag/v1.15.11)

[Compare
Source](https://redirect.github.com/cargo-bins/cargo-binstall/compare/v1.15.10...v1.15.11)

*Binstall is a tool to fetch and install Rust-based executables as
binaries. It aims to be a drop-in replacement for `cargo install` in
most cases. Install it today with `cargo install cargo-binstall`, from
the binaries below, or if you already have it, upgrade with `cargo
binstall cargo-binstall`.*

##### In this release:

- Fix binstalk-downloader cannot decode some zip files on macos (x64 and
arm64) platforms
([#&#8203;2049](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2049)
[#&#8203;2362](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2362))
- Fix grammer in `HELP.md` and `--help` output
([#&#8203;2357](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2357)
[#&#8203;2359](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2359))
- Update documentation link in Cargo.toml
([#&#8203;2355](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2355))

##### Other changes:

- Upgrade dependencies

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:17:37 +01:00
renovate[bot]
ca51feb319 Update Rust crate jiff to v0.2.16 (#21354)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [jiff](https://redirect.github.com/BurntSushi/jiff) |
workspace.dependencies | patch | `0.2.15` -> `0.2.16` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>BurntSushi/jiff (jiff)</summary>

###
[`v0.2.16`](https://redirect.github.com/BurntSushi/jiff/blob/HEAD/CHANGELOG.md#0216-2025-11-07)

[Compare
Source](https://redirect.github.com/BurntSushi/jiff/compare/0.2.15...0.2.16)

\===================
This release contains a number of enhancements and bug fixes that have
accrued
over the last few months. Most are small polishes. A couple of the bug
fixes
apply to panics that could occur when parsing invalid `TZ` strings or
invalid
`strptime` format strings.

Also, parsing into a `Span` should now be much faster (for both the ISO
8601
and "friendly" duration formats).

Enhancements:

- [#&#8203;298](https://redirect.github.com/BurntSushi/jiff/issues/298):
  Add Serde helpers for (de)serializing `std::time::Duration` values.
- [#&#8203;396](https://redirect.github.com/BurntSushi/jiff/issues/396):
Add `Sub` and `Add` trait implementations for `Zoned` (in addition to
the
  already existing trait implementations for `&Zoned`).
- [#&#8203;397](https://redirect.github.com/BurntSushi/jiff/pull/397):
Add `BrokenDownTime::set_meridiem` and ensure it overrides the hour when
  formatting.
- [#&#8203;409](https://redirect.github.com/BurntSushi/jiff/pull/409):
Switch dependency on `serde` to `serde_core`. This should help speed up
  compilation times in some cases.
- [#&#8203;430](https://redirect.github.com/BurntSushi/jiff/pull/430):
Add new `Zoned::series` API, making it consistent with the same API on
other
  datetime types.
- [#&#8203;432](https://redirect.github.com/BurntSushi/jiff/pull/432):
When `lenient` mode is enabled for `strftime`, Jiff will no longer error
when
  the formatting string contains invalid UTF-8.
- [#&#8203;432](https://redirect.github.com/BurntSushi/jiff/pull/432):
Formatting of `%y` and `%g` no longer fails based on the specific year
value.
- [#&#8203;432](https://redirect.github.com/BurntSushi/jiff/pull/432):
Parsing of `%s` is now a bit more consistent with other fields.
Moreover,
`BrokenDownTime::{to_timestamp,to_zoned}` will now prefer timestamps
parsed
  with `%s` over any other fields that have been parsed.
- [#&#8203;433](https://redirect.github.com/BurntSushi/jiff/pull/433):
Allow parsing just a `%s` into a `Zoned` via the `Etc/Unknown` time
zone.

Bug fixes:

- [#&#8203;386](https://redirect.github.com/BurntSushi/jiff/issues/386):
Fix a bug where `2087-12-31T23:00:00Z` in the `Africa/Casablanca` time
zone
could not be round-tripped (because its offset was calculated
incorrectly as
  a result of not handling "permanent DST" POSIX time zones).
- [#&#8203;407](https://redirect.github.com/BurntSushi/jiff/issues/407):
Fix a panic that occurred when parsing an empty string as a POSIX time
zone.
- [#&#8203;410](https://redirect.github.com/BurntSushi/jiff/issues/410):
  Fix a panic that could occur when parsing `%:` via `strptime` APIs.
- [#&#8203;414](https://redirect.github.com/BurntSushi/jiff/pull/414):
Update some parts of the documentation to indicate that
`TimeZone::unknown()`
is a fallback for `TimeZone::system()` (instead of the `jiff 0.1`
behavior of
  using `TimeZone::UTC`).
- [#&#8203;423](https://redirect.github.com/BurntSushi/jiff/issues/423):
  Fix a panicking bug when reading malformed TZif data.
- [#&#8203;426](https://redirect.github.com/BurntSushi/jiff/issues/426):
  Fix a panicking bug when parsing century (`%C`) via `strptime`.
- [#&#8203;445](https://redirect.github.com/BurntSushi/jiff/pull/445):
  Fixed bugs with parsing durations like `-9223372036854775808s`
  and `-PT9223372036854775808S`.

Performance:

- [#&#8203;445](https://redirect.github.com/BurntSushi/jiff/pull/445):
Parsing into `Span` or `SignedDuration` is now a fair bit faster in some
cases.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:16:50 +01:00
renovate[bot]
e0a3cbb048 Update Rust crate quote to v1.0.42 (#21356)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [quote](https://redirect.github.com/dtolnay/quote) |
workspace.dependencies | patch | `1.0.41` -> `1.0.42` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>dtolnay/quote (quote)</summary>

###
[`v1.0.42`](https://redirect.github.com/dtolnay/quote/releases/tag/1.0.42)

[Compare
Source](https://redirect.github.com/dtolnay/quote/compare/1.0.41...1.0.42)

- Tweaks to improve build speed
([#&#8203;305](https://redirect.github.com/dtolnay/quote/issues/305),
[#&#8203;306](https://redirect.github.com/dtolnay/quote/issues/306),
[#&#8203;307](https://redirect.github.com/dtolnay/quote/issues/307),
[#&#8203;308](https://redirect.github.com/dtolnay/quote/issues/308),
thanks [@&#8203;dishmaker](https://redirect.github.com/dishmaker))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:15:51 +01:00
renovate[bot]
f4f259395c Update taiki-e/install-action action to v2.62.49 (#21358)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[taiki-e/install-action](https://redirect.github.com/taiki-e/install-action)
| action | patch | `v2.62.45` -> `v2.62.49` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>taiki-e/install-action (taiki-e/install-action)</summary>

###
[`v2.62.49`](https://redirect.github.com/taiki-e/install-action/blob/HEAD/CHANGELOG.md#100---2021-12-30)

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.62.48...v2.62.49)

Initial release

[Unreleased]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.49...HEAD

[2.62.49]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.48...v2.62.49

[2.62.48]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.47...v2.62.48

[2.62.47]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.46...v2.62.47

[2.62.46]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.45...v2.62.46

[2.62.45]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.44...v2.62.45

[2.62.44]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.43...v2.62.44

[2.62.43]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.42...v2.62.43

[2.62.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.41...v2.62.42

[2.62.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.40...v2.62.41

[2.62.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.39...v2.62.40

[2.62.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.38...v2.62.39

[2.62.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.37...v2.62.38

[2.62.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.36...v2.62.37

[2.62.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.35...v2.62.36

[2.62.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.34...v2.62.35

[2.62.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.33...v2.62.34

[2.62.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.32...v2.62.33

[2.62.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.31...v2.62.32

[2.62.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.30...v2.62.31

[2.62.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.29...v2.62.30

[2.62.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.28...v2.62.29

[2.62.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.27...v2.62.28

[2.62.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.26...v2.62.27

[2.62.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.25...v2.62.26

[2.62.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.24...v2.62.25

[2.62.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.23...v2.62.24

[2.62.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.22...v2.62.23

[2.62.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.21...v2.62.22

[2.62.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.20...v2.62.21

[2.62.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.19...v2.62.20

[2.62.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.18...v2.62.19

[2.62.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.17...v2.62.18

[2.62.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.16...v2.62.17

[2.62.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.15...v2.62.16

[2.62.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.14...v2.62.15

[2.62.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.13...v2.62.14

[2.62.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.12...v2.62.13

[2.62.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.11...v2.62.12

[2.62.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.10...v2.62.11

[2.62.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.9...v2.62.10

[2.62.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.8...v2.62.9

[2.62.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.7...v2.62.8

[2.62.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.6...v2.62.7

[2.62.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.5...v2.62.6

[2.62.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.4...v2.62.5

[2.62.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.3...v2.62.4

[2.62.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.2...v2.62.3

[2.62.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.1...v2.62.2

[2.62.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.62.0...v2.62.1

[2.62.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.13...v2.62.0

[2.61.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.12...v2.61.13

[2.61.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.11...v2.61.12

[2.61.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.10...v2.61.11

[2.61.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.9...v2.61.10

[2.61.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.8...v2.61.9

[2.61.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.7...v2.61.8

[2.61.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.6...v2.61.7

[2.61.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.5...v2.61.6

[2.61.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.4...v2.61.5

[2.61.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.3...v2.61.4

[2.61.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.2...v2.61.3

[2.61.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.1...v2.61.2

[2.61.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.61.0...v2.61.1

[2.61.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.60.0...v2.61.0

[2.60.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.59.1...v2.60.0

[2.59.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.59.0...v2.59.1

[2.59.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.33...v2.59.0

[2.58.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.32...v2.58.33

[2.58.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.31...v2.58.32

[2.58.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.30...v2.58.31

[2.58.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.29...v2.58.30

[2.58.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.28...v2.58.29

[2.58.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.27...v2.58.28

[2.58.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.26...v2.58.27

[2.58.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.25...v2.58.26

[2.58.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.24...v2.58.25

[2.58.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.23...v2.58.24

[2.58.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.22...v2.58.23

[2.58.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.21...v2.58.22

[2.58.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.20...v2.58.21

[2.58.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.19...v2.58.20

[2.58.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.18...v2.58.19

[2.58.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.17...v2.58.18

[2.58.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.16...v2.58.17

[2.58.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.15...v2.58.16

[2.58.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.14...v2.58.15

[2.58.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.13...v2.58.14

[2.58.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.12...v2.58.13

[2.58.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.11...v2.58.12

[2.58.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.10...v2.58.11

[2.58.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.9...v2.58.10

[2.58.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.8...v2.58.9

[2.58.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.7...v2.58.8

[2.58.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.6...v2.58.7

[2.58.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.5...v2.58.6

[2.58.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.4...v2.58.5

[2.58.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.3...v2.58.4

[2.58.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.2...v2.58.3

[2.58.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.1...v2.58.2

[2.58.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.58.0...v2.58.1

[2.58.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.8...v2.58.0

[2.57.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.7...v2.57.8

[2.57.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.6...v2.57.7

[2.57.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.5...v2.57.6

[2.57.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.4...v2.57.5

[2.57.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.3...v2.57.4

[2.57.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.2...v2.57.3

[2.57.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.1...v2.57.2

[2.57.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.57.0...v2.57.1

[2.57.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.24...v2.57.0

[2.56.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.23...v2.56.24

[2.56.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.22...v2.56.23

[2.56.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.21...v2.56.22

[2.56.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.20...v2.56.21

[2.56.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.19...v2.56.20

[2.56.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.18...v2.56.19

[2.56.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.17...v2.56.18

[2.56.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.16...v2.56.17

[2.56.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.15...v2.56.16

[2.56.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.14...v2.56.15

[2.56.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.13...v2.56.14

[2.56.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.12...v2.56.13

[2.56.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.11...v2.56.12

[2.56.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.10...v2.56.11

[2.56.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.9...v2.56.10

[2.56.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.8...v2.56.9

[2.56.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.7...v2.56.8

[2.56.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.6...v2.56.7

[2.56.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.5...v2.56.6

[2.56.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.4...v2.56.5

[2.56.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.3...v2.56.4

[2.56.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.2...v2.56.3

[2.56.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.1...v2.56.2

[2.56.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.56.0...v2.56.1

[2.56.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.4...v2.56.0

[2.55.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.3...v2.55.4

[2.55.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.2...v2.55.3

[2.55.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.1...v2.55.2

[2.55.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.55.0...v2.55.1

[2.55.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.3...v2.55.0

[2.54.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.2...v2.54.3

[2.54.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.1...v2.54.2

[2.54.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.54.0...v2.54.1

[2.54.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.53.2...v2.54.0

[2.53.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.53.1...v2.53.2

[2.53.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.53.0...v2.53.1

[2.53.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.8...v2.53.0

[2.52.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.7...v2.52.8

[2.52.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.6...v2.52.7

[2.52.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.5...v2.52.6

[2.52.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.4...v2.52.5

[2.52.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.3...v2.52.4

[2.52.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.2...v2.52.3

[2.52.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.1...v2.52.2

[2.52.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.52.0...v2.52.1

[2.52.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.3...v2.52.0

[2.51.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.2...v2.51.3

[2.51.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.1...v2.51.2

[2.51.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.51.0...v2.51.1

[2.51.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.10...v2.51.0

[2.50.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.9...v2.50.10

[2.50.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.8...v2.50.9

[2.50.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.7...v2.50.8

[2.50.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.6...v2.50.7

[2.50.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.5...v2.50.6

[2.50.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.4...v2.50.5

[2.50.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.3...v2.50.4

[2.50.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.2...v2.50.3

[2.50.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.1...v2.50.2

[2.50.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.50.0...v2.50.1

[2.50.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.50...v2.50.0

[2.49.50]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.49...v2.49.50

[2.49.49]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.48...v2.49.49

[2.49.48]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.47...v2.49.48

[2.49.47]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.46...v2.49.47

[2.49.46]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.45...v2.49.46

[2.49.45]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.44...v2.49.45

[2.49.44]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.43...v2.49.44

[2.49.43]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.42...v2.49.43

[2.49.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.41...v2.49.42

[2.49.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.40...v2.49.41

[2.49.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.39...v2.49.40

[2.49.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.38...v2.49.39

[2.49.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.37...v2.49.38

[2.49.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.36...v2.49.37

[2.49.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.35...v2.49.36

[2.49.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.34...v2.49.35

[2.49.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.33...v2.49.34

[2.49.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.32...v2.49.33

[2.49.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.31...v2.49.32

[2.49.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.30...v2.49.31

[2.49.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.29...v2.49.30

[2.49.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.28...v2.49.29

[2.49.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.27...v2.49.28

[2.49.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.26...v2.49.27

[2.49.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.25...v2.49.26

[2.49.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.24...v2.49.25

[2.49.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.23...v2.49.24

[2.49.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.22...v2.49.23

[2.49.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.21...v2.49.22

[2.49.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.20...v2.49.21

[2.49.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.19...v2.49.20

[2.49.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.18...v2.49.19

[2.49.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.17...v2.49.18

[2.49.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.16...v2.49.17

[2.49.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.15...v2.49.16

[2.49.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.14...v2.49.15

[2.49.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.13...v2.49.14

[2.49.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.12...v2.49.13

[2.49.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.11...v2.49.12

[2.49.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.10...v2.49.11

[2.49.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.9...v2.49.10

[2.49.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.8...v2.49.9

[2.49.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.7...v2.49.8

[2.49.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.6...v2.49.7

[2.49.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.5...v2.49.6

[2.49.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.4...v2.49.5

[2.49.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.3...v2.49.4

[2.49.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.2...v2.49.3

[2.49.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.1...v2.49.2

[2.49.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.49.0...v2.49.1

[2.49.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.22...v2.49.0

[2.48.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.21...v2.48.22

[2.48.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.20...v2.48.21

[2.48.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.19...v2.48.20

[2.48.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.18...v2.48.19

[2.48.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.17...v2.48.18

[2.48.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.16...v2.48.17

[2.48.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.15...v2.48.16

[2.48.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.14...v2.48.15

[2.48.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.13...v2.48.14

[2.48.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.12...v2.48.13

[2.48.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.11...v2.48.12

[2.48.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.10...v2.48.11

[2.48.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.9...v2.48.10

[2.48.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.8...v2.48.9

[2.48.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.7...v2.48.8

[2.48.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.6...v2.48.7

[2.48.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.5...v2.48.6

[2.48.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.4...v2.48.5

[2.48.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.3...v2.48.4

[2.48.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.2...v2.48.3

[2.48.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.1...v2.48.2

[2.48.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.48.0...v2.48.1

[2.48.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.32...v2.48.0

[2.47.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.31...v2.47.32

[2.47.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.30...v2.47.31

[2.47.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.29...v2.47.30

[2.47.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.28...v2.47.29

[2.47.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.27...v2.47.28

[2.47.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.26...v2.47.27

[2.47.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.25...v2.47.26

[2.47.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.24...v2.47.25

[2.47.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.23...v2.47.24

[2.47.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.22...v2.47.23

[2.47.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.21...v2.47.22

[2.47.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.20...v2.47.21

[2.47.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.19...v2.47.20

[2.47.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.18...v2.47.19

[2.47.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.17...v2.47.18

[2.47.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.16...v2.47.17

[2.47.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.15...v2.47.16

[2.47.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.14...v2.47.15

[2.47.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.13...v2.47.14

[2.47.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.12...v2.47.13

[2.47.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.11...v2.47.12

[2.47.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.10...v2.47.11

[2.47.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.9...v2.47.10

[2.47.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.8...v2.47.9

[2.47.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.7...v2.47.8

[2.47.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.6...v2.47.7

[2.47.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.5...v2.47.6

[2.47.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.4...v2.47.5

[2.47.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.3...v2.47.4

[2.47.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.2...v2.47.3

[2.47.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.1...v2.47.2

[2.47.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.47.0...v2.47.1

[2.47.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.20...v2.47.0

[2.46.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.19...v2.46.20

[2.46.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.18...v2.46.19

[2.46.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.17...v2.46.18

[2.46.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.16...v2.46.17

[2.46.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.15...v2.46.16

[2.46.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.14...v2.46.15

[2.46.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.13...v2.46.14

[2.46.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.12...v2.46.13

[2.46.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.11...v2.46.12

[2.46.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.10...v2.46.11

[2.46.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.9...v2.46.10

[2.46.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.8...v2.46.9

[2.46.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.7...v2.46.8

[2.46.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.6...v2.46.7

[2.46.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.5...v2.46.6

[2.46.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.4...v2.46.5

[2.46.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.3...v2.46.4

[2.46.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.2...v2.46.3

[2.46.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.1...v2.46.2

[2.46.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.46.0...v2.46.1

[2.46.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.15...v2.46.0

[2.45.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.14...v2.45.15

[2.45.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.13...v2.45.14

[2.45.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.12...v2.45.13

[2.45.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.11...v2.45.12

[2.45.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.10...v2.45.11

[2.45.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.9...v2.45.10

[2.45.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.8...v2.45.9

[2.45.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.7...v2.45.8

[2.45.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.6...v2.45.7

[2.45.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.5...v2.45.6

[2.45.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.4...v2.45.5

[2.45.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.3...v2.45.4

[2.45.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.2...v2.45.3

[2.45.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.1...v2.45.2

[2.45.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.45.0...v2.45.1

[2.45.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.72...v2.45.0

[2.44.72]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.71...v2.44.72

[2.44.71]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.70...v2.44.71

[2.44.70]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.69...v2.44.70

[2.44.69]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.68...v2.44.69

[2.44.68]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.67...v2.44.68

[2.44.67]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.66...v2.44.67

[2.44.66]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.65...v2.44.66

[2.44.65]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.64...v2.44.65

[2.44.64]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.63...v2.44.64

[2.44.63]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.62...v2.44.63

[2.44.62]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.61...v2.44.62

[2.44.61]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.60...v2.44.61

[2.44.60]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.59...v2.44.60

[2.44.59]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.58...v2.44.59

[2.44.58]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.57...v2.44.58

[2.44.57]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.56...v2.44.57

[2.44.56]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.55...v2.44.56

[2.44.55]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.54...v2.44.55

[2.44.54]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.53...v2.44.54

[2.44.53]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.52...v2.44.53

[2.44.52]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.51...v2.44.52

[2.44.51]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.50...v2.44.51

[2.44.50]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.49...v2.44.50

[2.44.49]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.48...v2.44.49

[2.44.48]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.47...v2.44.48

[2.44.47]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.46...v2.44.47

[2.44.46]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.45...v2.44.46

[2.44.45]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.44...v2.44.45

[2.44.44]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.43...v2.44.44

[2.44.43]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.42...v2.44.43

[2.44.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.41...v2.44.42

[2.44.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.40...v2.44.41

[2.44.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.39...v2.44.40

[2.44.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.38...v2.44.39

[2.44.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.37...v2.44.38

[2.44.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.36...v2.44.37

[2.44.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.35...v2.44.36

[2.44.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.34...v2.44.35

[2.44.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.33...v2.44.34

[2.44.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.32...v2.44.33

[2.44.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.31...v2.44.32

[2.44.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.30...v2.44.31

[2.44.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.29...v2.44.30

[2.44.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.28...v2.44.29

[2.44.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.27...v2.44.28

[2.44.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.26...v2.44.27

[2.44.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.25...v2.44.26

[2.44.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.24...v2.44.25

[2.44.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.23...v2.44.24

[2.44.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.22...v2.44.23

[2.44.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.21...v2.44.22

[2.44.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.20...v2.44.21

[2.44.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.19...v2.44.20

[2.44.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.18...v2.44.19

[2.44.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.17...v2.44.18

[2.44.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.16...v2.44.17

[2.44.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.15...v2.44.16

[2.44.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.14...v2.44.15

[2.44.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.13...v2.44.14

[2.44.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.12...v2.44.13

[2.44.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.11...v2.44.12

[2.44.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.10...v2.44.11

[2.44.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.9...v2.44.10

[2.44.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.8...v2.44.9

[2.44.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.7...v2.44.8

[2.44.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.6...v2.44.7

[2.44.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.5...v2.44.6

[2.44.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.4...v2.44.5

[2.44.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.3...v2.44.4

[2.44.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.2...v2.44.3

[2.44.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.1...v2.44.2

[2.44.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.44.0...v2.44.1

[2.44.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.7...v2.44.0

[2.43.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.6...v2.43.7

[2.43.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.5...v2.43.6

[2.43.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.4...v2.43.5

[2.43.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.3...v2.43.4

[2.43.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.2...v2.43.3

[2.43.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.1...v2.43.2

[2.43.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.43.0...v2.43.1

[2.43.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.42...v2.43.0

[2.42.42]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.41...v2.42.42

[2.42.41]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.40...v2.42.41

[2.42.40]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.39...v2.42.40

[2.42.39]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.38...v2.42.39

[2.42.38]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.37...v2.42.38

[2.42.37]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.36...v2.42.37

[2.42.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.35...v2.42.36

[2.42.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.34...v2.42.35

[2.42.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.33...v2.42.34

[2.42.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.32...v2.42.33

[2.42.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.31...v2.42.32

[2.42.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.30...v2.42.31

[2.42.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.29...v2.42.30

[2.42.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.28...v2.42.29

[2.42.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.27...v2.42.28

[2.42.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.26...v2.42.27

[2.42.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.25...v2.42.26

[2.42.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.24...v2.42.25

[2.42.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.23...v2.42.24

[2.42.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.22...v2.42.23

[2.42.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.21...v2.42.22

[2.42.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.20...v2.42.21

[2.42.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.19...v2.42.20

[2.42.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.18...v2.42.19

[2.42.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.17...v2.42.18

[2.42.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.16...v2.42.17

[2.42.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.15...v2.42.16

[2.42.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.14...v2.42.15

[2.42.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.13...v2.42.14

[2.42.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.12...v2.42.13

[2.42.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.11...v2.42.12

[2.42.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.10...v2.42.11

[2.42.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.9...v2.42.10

[2.42.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.8...v2.42.9

[2.42.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.7...v2.42.8

[2.42.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.6...v2.42.7

[2.42.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.5...v2.42.6

[2.42.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.4...v2.42.5

[2.42.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.3...v2.42.4

[2.42.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.2...v2.42.3

[2.42.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.1...v2.42.2

[2.42.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.42.0...v2.42.1

[2.42.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.18...v2.42.0

[2.41.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.17...v2.41.18

[2.41.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.16...v2.41.17

[2.41.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.15...v2.41.16

[2.41.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.14...v2.41.15

[2.41.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.13...v2.41.14

[2.41.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.12...v2.41.13

[2.41.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.11...v2.41.12

[2.41.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.10...v2.41.11

[2.41.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.9...v2.41.10

[2.41.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.8...v2.41.9

[2.41.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.7...v2.41.8

[2.41.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.6...v2.41.7

[2.41.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.5...v2.41.6

[2.41.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.4...v2.41.5

[2.41.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.3...v2.41.4

[2.41.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.2...v2.41.3

[2.41.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.1...v2.41.2

[2.41.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.41.0...v2.41.1

[2.41.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.40.2...v2.41.0

[2.40.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.40.1...v2.40.2

[2.40.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.40.0...v2.40.1

[2.40.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.39.2...v2.40.0

[2.39.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.39.1...v2.39.2

[2.39.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.39.0...v2.39.1

[2.39.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.7...v2.39.0

[2.38.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.6...v2.38.7

[2.38.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.5...v2.38.6

[2.38.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.4...v2.38.5

[2.38.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.3...v2.38.4

[2.38.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.2...v2.38.3

[2.38.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.1...v2.38.2

[2.38.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.38.0...v2.38.1

[2.38.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.37.0...v2.38.0

[2.37.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.36.0...v2.37.0

[2.36.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.35.0...v2.36.0

[2.35.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.3...v2.35.0

[2.34.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.2...v2.34.3

[2.34.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.1...v2.34.2

[2.34.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.34.0...v2.34.1

[2.34.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.36...v2.34.0

[2.33.36]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.35...v2.33.36

[2.33.35]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.34...v2.33.35

[2.33.34]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.33...v2.33.34

[2.33.33]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.32...v2.33.33

[2.33.32]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.31...v2.33.32

[2.33.31]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.30...v2.33.31

[2.33.30]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.29...v2.33.30

[2.33.29]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.28...v2.33.29

[2.33.28]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.27...v2.33.28

[2.33.27]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.26...v2.33.27

[2.33.26]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.25...v2.33.26

[2.33.25]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.24...v2.33.25

[2.33.24]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.23...v2.33.24

[2.33.23]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.22...v2.33.23

[2.33.22]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.21...v2.33.22

[2.33.21]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.20...v2.33.21

[2.33.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.19...v2.33.20

[2.33.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.18...v2.33.19

[2.33.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.17...v2.33.18

[2.33.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.16...v2.33.17

[2.33.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.15...v2.33.16

[2.33.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.14...v2.33.15

[2.33.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.13...v2.33.14

[2.33.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.12...v2.33.13

[2.33.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.11...v2.33.12

[2.33.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.10...v2.33.11

[2.33.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.9...v2.33.10

[2.33.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.8...v2.33.9

[2.33.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.7...v2.33.8

[2.33.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.6...v2.33.7

[2.33.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.5...v2.33.6

[2.33.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.4...v2.33.5

[2.33.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.3...v2.33.4

[2.33.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.2...v2.33.3

[2.33.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.1...v2.33.2

[2.33.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.33.0...v2.33.1

[2.33.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.20...v2.33.0

[2.32.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.19...v2.32.20

[2.32.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.18...v2.32.19

[2.32.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.17...v2.32.18

[2.32.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.16...v2.32.17

[2.32.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.15...v2.32.16

[2.32.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.14...v2.32.15

[2.32.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.13...v2.32.14

[2.32.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.12...v2.32.13

[2.32.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.11...v2.32.12

[2.32.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.10...v2.32.11

[2.32.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.9...v2.32.10

[2.32.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.8...v2.32.9

[2.32.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.7...v2.32.8

[2.32.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.6...v2.32.7

[2.32.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.5...v2.32.6

[2.32.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.4...v2.32.5

[2.32.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.3...v2.32.4

[2.32.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.2...v2.32.3

[2.32.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.1...v2.32.2

[2.32.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.32.0...v2.32.1

[2.32.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.3...v2.32.0

[2.31.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.2...v2.31.3

[2.31.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.1...v2.31.2

[2.31.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.31.0...v2.31.1

[2.31.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.30.0...v2.31.0

[2.30.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.8...v2.30.0

[2.29.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.7...v2.29.8

[2.29.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.6...v2.29.7

[2.29.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.5...v2.29.6

[2.29.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.4...v2.29.5

[2.29.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.3...v2.29.4

[2.29.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.2...v2.29.3

[2.29.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.1...v2.29.2

[2.29.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.29.0...v2.29.1

[2.29.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.16...v2.29.0

[2.28.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.15...v2.28.16

[2.28.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.14...v2.28.15

[2.28.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.13...v2.28.14

[2.28.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.12...v2.28.13

[2.28.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.11...v2.28.12

[2.28.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.10...v2.28.11

[2.28.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.9...v2.28.10

[2.28.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.8...v2.28.9

[2.28.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.7...v2.28.8

[2.28.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.6...v2.28.7

[2.28.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.5...v2.28.6

[2.28.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.4...v2.28.5

[2.28.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.3...v2.28.4

[2.28.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.2...v2.28.3

[2.28.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.1...v2.28.2

[2.28.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.28.0...v2.28.1

[2.28.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.15...v2.28.0

[2.27.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.14...v2.27.15

[2.27.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.13...v2.27.14

[2.27.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.12...v2.27.13

[2.27.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.11...v2.27.12

[2.27.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.10...v2.27.11

[2.27.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.9...v2.27.10

[2.27.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.8...v2.27.9

[2.27.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.7...v2.27.8

[2.27.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.6...v2.27.7

[2.27.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.5...v2.27.6

[2.27.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.4...v2.27.5

[2.27.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.3...v2.27.4

[2.27.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.2...v2.27.3

[2.27.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.1...v2.27.2

[2.27.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.27.0...v2.27.1

[2.27.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.20...v2.27.0

[2.26.20]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.19...v2.26.20

[2.26.19]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.18...v2.26.19

[2.26.18]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.17...v2.26.18

[2.26.17]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.16...v2.26.17

[2.26.16]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.15...v2.26.16

[2.26.15]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.14...v2.26.15

[2.26.14]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.13...v2.26.14

[2.26.13]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.12...v2.26.13

[2.26.12]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.11...v2.26.12

[2.26.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.10...v2.26.11

[2.26.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.9...v2.26.10

[2.26.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.8...v2.26.9

[2.26.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.7...v2.26.8

[2.26.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.6...v2.26.7

[2.26.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.5...v2.26.6

[2.26.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.4...v2.26.5

[2.26.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.3...v2.26.4

[2.26.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.2...v2.26.3

[2.26.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.1...v2.26.2

[2.26.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.26.0...v2.26.1

[2.26.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.11...v2.26.0

[2.25.11]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.10...v2.25.11

[2.25.10]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.9...v2.25.10

[2.25.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.8...v2.25.9

[2.25.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.7...v2.25.8

[2.25.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.6...v2.25.7

[2.25.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.5...v2.25.6

[2.25.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.4...v2.25.5

[2.25.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.3...v2.25.4

[2.25.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.2...v2.25.3

[2.25.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.1...v2.25.2

[2.25.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.25.0...v2.25.1

[2.25.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.4...v2.25.0

[2.24.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.3...v2.24.4

[2.24.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.2...v2.24.3

[2.24.2]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.1...v2.24.2

[2.24.1]:
https://redirect.github.com/taiki-e/install-action/compare/v2.24.0...v2.24.1

[2.24.0]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.9...v2.24.0

[2.23.9]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.8...v2.23.9

[2.23.8]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.7...v2.23.8

[2.23.7]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.6...v2.23.7

[2.23.6]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.5...v2.23.6

[2.23.5]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.4...v2.23.5

[2.23.4]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.3...v2.23.4

[2.23.3]:
https://redirect.github.com/taiki-e/install-action/compare/v2.23.2...v2.23.3

[2.23.2]: https://redirect.github.com/taiki-e/in

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:15:35 +01:00
renovate[bot]
2a1d412f72 Update Rust crate syn to v2.0.110 (#21357)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [syn](https://redirect.github.com/dtolnay/syn) |
workspace.dependencies | patch | `2.0.108` -> `2.0.110` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>dtolnay/syn (syn)</summary>

###
[`v2.0.110`](https://redirect.github.com/dtolnay/syn/releases/tag/2.0.110)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.109...2.0.110)

- Tweaks to improve build speed
([#&#8203;1939](https://redirect.github.com/dtolnay/syn/issues/1939),
thanks [@&#8203;dishmaker](https://redirect.github.com/dishmaker))
- Make `syn::ext::IdentExt::unraw` available without "parsing" feature
([#&#8203;1940](https://redirect.github.com/dtolnay/syn/issues/1940))
- Support parsing `syn::Meta` followed by `=>`
([#&#8203;1944](https://redirect.github.com/dtolnay/syn/issues/1944))

###
[`v2.0.109`](https://redirect.github.com/dtolnay/syn/releases/tag/2.0.109)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.108...2.0.109)

- Tweaks to improve build speed
([#&#8203;1927](https://redirect.github.com/dtolnay/syn/issues/1927),
[#&#8203;1928](https://redirect.github.com/dtolnay/syn/issues/1928),
[#&#8203;1930](https://redirect.github.com/dtolnay/syn/issues/1930),
[#&#8203;1932](https://redirect.github.com/dtolnay/syn/issues/1932),
[#&#8203;1934](https://redirect.github.com/dtolnay/syn/issues/1934),
thanks [@&#8203;dishmaker](https://redirect.github.com/dishmaker))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:14:48 +01:00
renovate[bot]
dd751e8d07 Update dependency ruff to v0.14.4 (#21353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 01:35:42 +00:00
Alex Waygood
020ff1723b [ty] Add narrowing for isinstance() and issubclass() checks that use PEP-604 unions (#21334) 2025-11-08 18:20:46 +00:00
Hugo van Kemenade
09e6af16c8 [ruff+ty] Add colour to --help (#21337) 2025-11-08 17:17:14 +01:00
Micha Reiser
76efc8061d [ty] Make variance_of logging trace only (#21339) 2025-11-08 14:10:24 +00:00
Dan Parizher
16de4aa3cc [refurb] Auto-fix annotated assignments (FURB101) (#21278)
## Summary

Fixed FURB101 (`read-whole-file`) to handle annotated assignments.
Previously, the rule would detect violations in code like `contents: str
= f.read()` but fail to generate a fix. Now it correctly generates fixes
that preserve type annotations (e.g., `contents: str =
Path("file.txt").read_text(encoding="utf-8")`).

Fixes #21274

## Problem Analysis

The FURB101 rule was only checking for `Stmt::Assign` statements when
determining whether a fix could be applied. When encountering annotated
assignments (`Stmt::AnnAssign`) like `contents: str = f.read()`, the
rule would:

1. Correctly detect the violation (the diagnostic was reported)
2. Fail to generate a fix because:
- The `visit_expr` method only matched `Stmt::Assign`, not
`Stmt::AnnAssign`
- The `generate_fix` function only accepted `Stmt::Assign` in its body
validation
   - The replacement code generation didn't account for type annotations

This occurred because Python's AST represents annotated assignments as a
different node type (`StmtAnnAssign`) with separate fields for the
target, annotation, and value, unlike regular assignments which use a
list of targets.

## Approach

The fix extends the rule to handle both assignment types:

1. **Updated `visit_expr` method**: Now matches both `Stmt::Assign` and
`Stmt::AnnAssign`, extracting:
   - Variable name from the target expression
   - Type annotation code (when present) using the code generator

2. **Updated `generate_fix` function**:
- Added `annotation: Option<String>` parameter to accept annotation code
- Updated body validation to accept both `Stmt::Assign` and
`Stmt::AnnAssign`
- Modified replacement code generation to preserve annotations: `{var}:
{annotation} = {binding}({filename_code}).{suggestion}`

3. **Added test case**: Added an annotated assignment test case to
verify the fix works correctly.

The implementation maintains backward compatibility with regular
assignments while adding support for annotated assignments, ensuring
type annotations are preserved in the generated fixes.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-07 19:04:45 -05:00
Brent Westbrook
e06e108095 [flake8-annotations] Add link to allow-star-arg-any option (ANN401) (#21326)
Summary
--

Addresses
https://github.com/astral-sh/ruff/issues/19152#issuecomment-3501373508
by adding a link to the configuration option to the rule page.

Test Plan
--

Built the docs locally and made sure the link was present and working
2025-11-07 18:45:53 -05:00
William Woodruff
b6add3ee6d chore: bump dist, remove old commenting workflows (#21302) 2025-11-07 17:09:29 -05:00
Alex Waygood
39c21d7c6c [ty] Generalize some infrastructure around type visitors (#21323)
We have lots of `TypeVisitor`s that end up having very similar
`visit_type` implementations. This PR consolidates some of the code for
these so that there's less repetition and duplication.
2025-11-07 16:26:30 -05:00
Micha Reiser
1617292e9f Update CodSpeedHQ/action action to v4.3.3 (#21254)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 19:39:52 +00:00
Douglas Creager
faae72b836 [ty] Clarify behavior of constraint sets for gradual upper bounds and constraints (#21287)
When checking whether a constraint set is satisfied, if a typevar has a
non-fully-static upper bound or constraint, we are free to choose any
materialization that makes the check succeed.

In non-inferable positions, we have to show that the constraint set is
satisfied for all valid specializations, so it's best to choose the most
restrictive materialization, since that minimizes the set of valid
specializations that have to pass.

In inferable positions, we only have to show that the constraint set is
satisfied for _some_ valid specializations, so it's best to choose the
most permissive materialization, since that maximizes our chances of
finding a specialization that passes.
2025-11-07 14:01:39 -05:00
Brent Westbrook
276f1d0d88 Remove duplicate preview tests for FURB101 and FURB103 (#21303)
Summary
--

These rules are themselves in preview, so we don't need the additional
preview checks on the fixes or the separate preview tests. This has
confused me in a couple of reviews of changes to the fixes.

Test Plan
--

Existing tests, with the fixes previously only shown in the preview
tests now in the "non-preview" tests.
2025-11-07 12:47:21 -05:00
David Peter
ed18112cfa [ty] Add support for Literals in implicit type aliases (#21296)
## Summary

Add support for `Literal` types in implicit type aliases.

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

## Ecosystem analysis

This looks good to me, true positives and known problems.

## Test Plan

New Markdown tests.
2025-11-07 17:46:55 +01:00
Micha Reiser
8ba1cfebed [ty] Add missing heap_size to variance_of queries (#21318) 2025-11-07 16:18:28 +00:00
Dan Parizher
6185a2af9e [pyupgrade] Fix false positive on relative imports from local .builtins module (UP029) (#21309) 2025-11-07 16:01:52 +00:00
Micha Reiser
6cc3393ccd [ty] Make range/position conversions fallible (#21297)
Co-authored-by: Aria Desires <aria.desires@gmail.com>
2025-11-07 14:44:23 +00:00
Dylan
c7ff9826d6 Bump 0.14.4 (#21306) 2025-11-06 15:47:29 -06:00
Dhruv Manilawala
35640dd853 Fix main by using infer_expression (#21299) 2025-11-06 20:10:43 +00:00
Dhruv Manilawala
cb2e277482 [ty] Understand legacy and PEP 695 ParamSpec (#21139)
## Summary

This PR adds support for understanding the legacy definition and PEP 695
definition for `ParamSpec`.

This is still very initial and doesn't really implement any of the
semantics.

Part of https://github.com/astral-sh/ty/issues/157

## Test Plan

Add mdtest cases.

## Ecosystem analysis

Most of the diagnostics in `starlette` are due to the fact that ty now
understands `ParamSpec` is not a `Todo` type, so the assignability check
fails. The code looks something like:

```py
class _MiddlewareFactory(Protocol[P]):
    def __call__(self, app: ASGIApp, /, *args: P.args, **kwargs: P.kwargs) -> ASGIApp: ...  # pragma: no cover

class Middleware:
    def __init__(
        self,
        cls: _MiddlewareFactory[P],
        *args: P.args,
        **kwargs: P.kwargs,
    ) -> None:
        self.cls = cls
        self.args = args
        self.kwargs = kwargs

# ty complains that `ServerErrorMiddleware` is not assignable to `_MiddlewareFactory[P]`
Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)
```

There are multiple diagnostics where there's an attribute access on the
`Wrapped` object of `functools` which Pyright also raises:
```py
from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        return f(*args, **kwds)

	# Pyright: Cannot access attribute "__signature__" for class "_Wrapped[..., Unknown, ..., Unknown]"
      Attribute "__signature__" is unknown [reportAttributeAccessIssue]
	# ty: Object of type `_Wrapped[Unknown, Unknown, Unknown, Unknown]` has no attribute `__signature__` [unresolved-attribute]
    wrapper.__signature__
    return wrapper
```

There are additional diagnostics that is due to the assignability checks
failing because ty now infers the `ParamSpec` instead of using the
`Todo` type which would always succeed. This results in a few
`no-matching-overload` diagnostics because the assignability checks
fail.

There are a few diagnostics related to
https://github.com/astral-sh/ty/issues/491 where there's a variable
which is either a bound method or a variable that's annotated with
`Callable` that doesn't contain the instance as the first parameter.

Another set of (valid) diagnostics are where the code hasn't provided
all the type variables. ty is now raising diagnostics for these because
we include `ParamSpec` type variable in the signature. For example,
`staticmethod[Any]` which contains two type variables.
2025-11-06 11:14:40 -05:00
Zanie Blue
132d10fb6f [ty] Discover site-packages from the environment that ty is installed in (#21286)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

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

There are various situations where users expect the Python packages
installed in the same environment as ty itself to be considered during
type checking. A minimal example would look like:

```
uv venv my-env
uv pip install my-env ty httpx
echo "import httpx" > foo.py
./my-env/bin/ty check foo.py
```

or

```
uv tool install ty --with httpx
echo "import httpx" > foo.py
ty check foo.py
```

While these are a bit contrived, there are real-world situations where a
user would expect a similar behavior to work. Notably, all of the other
type checkers consider their own environment when determining search
paths (though I'll admit that I have not verified when they choose not
to do this).

One common situation where users are encountering this today is with
`uvx --with-requirements script.py ty check script.py` — which is
currently our "best" recommendation for type checking a PEP 723 script,
but it doesn't work.

Of the options discussed in
https://github.com/astral-sh/ty/issues/989#issuecomment-3307417985, I've
chosen (2) as our criteria for including ty's environment in the search
paths.

- If no virtual environment is discovered, we will always include ty's
environment.
- If a `.venv` is discovered in the working directory, we will _prepend_
ty's environment to the search paths. The dependencies in ty's
environment (e.g., from `uvx --with`) will take precedence.
- If a virtual environment is active, e.g., `VIRTUAL_ENV` (i.e.,
including conda prefixes) is set, we will not include ty's environment.

The reason we need to special case the `.venv` case is that we both

1.  Recommend `uvx ty` today as a way to check your project
2. Want to enable `uvx --with <...> ty`

And I don't want (2) to break when you _happen_ to be in a project
(i.e., if we only included ty's environment when _no_ environment is
found) and don't want to remove support for (1).

I think long-term, I want to make `uvx <cmd>` layer the environment on
_top_ of the project environment (in uv), which would obviate the need
for this change when you're using uv. However, that change is breaking
and I think users will expect this behavior in contexts where they're
not using uv, so I think we should handle it in ty regardless.

I've opted not to include the environment if it's non-virtual (i.e., a
system environment) for now. It seems better to start by being more
restrictive. I left a comment in the code.

## Test Plan

I did some manual testing with the initial commit, then subsequently
added some unit tests.

```
❯ echo "import httpx" > example.py
❯ uvx --with httpx ty check example.py
Installed 8 packages in 19ms
error[unresolved-import]: Cannot resolve imported module `httpx`
 --> foo/example.py:1:8
  |
1 | import httpx
  |        ^^^^^
  |
info: Searched in the following paths during module resolution:
info:   1. /Users/zb/workspace/ty/python (first-party code)
info:   2. /Users/zb/workspace/ty (first-party code)
info:   3. vendored://stdlib (stdlib typeshed stubs vendored by ty)
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
info: rule `unresolved-import` is enabled by default

Found 1 diagnostic
❯ uvx --from . --with httpx ty check example.py
All checks passed!
```

```
❯ uv init --script foo.py
Initialized script at `foo.py`
❯ uv add --script foo.py httpx
warning: The Python request from `.python-version` resolved to Python 3.13.8, which is incompatible with the script's Python requirement: `>=3.14`
Updated `foo.py`
❯ echo "import httpx" >> foo.py
❯ uvx --with-requirements foo.py ty check foo.py
error[unresolved-import]: Cannot resolve imported module `httpx`
  --> foo.py:15:8
   |
13 | if __name__ == "__main__":
14 |     main()
15 | import httpx
   |        ^^^^^
   |
info: Searched in the following paths during module resolution:
info:   1. /Users/zb/workspace/ty/python (first-party code)
info:   2. /Users/zb/workspace/ty (first-party code)
info:   3. vendored://stdlib (stdlib typeshed stubs vendored by ty)
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
info: rule `unresolved-import` is enabled by default

Found 1 diagnostic
❯ uvx --from . --with-requirements foo.py ty check foo.py
All checks passed!
```

Notice we do not include ty's environment if `VIRTUAL_ENV` is set

```
❯ VIRTUAL_ENV=.venv uvx --with httpx ty check foo/example.py
error[unresolved-import]: Cannot resolve imported module `httpx`
 --> foo/example.py:1:8
  |
1 | import httpx
  |        ^^^^^
  |
info: Searched in the following paths during module resolution:
info:   1. /Users/zb/workspace/ty/python (first-party code)
info:   2. /Users/zb/workspace/ty (first-party code)
info:   3. vendored://stdlib (stdlib typeshed stubs vendored by ty)
info:   4. /Users/zb/workspace/ty/.venv/lib/python3.13/site-packages (site-packages)
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
info: rule `unresolved-import` is enabled by default

Found 1 diagnostic
```
2025-11-06 09:27:49 -05:00
Alex Waygood
f189aad6d2 [ty] Make special cases for UnionType slightly narrower (#21276)
Fixes https://github.com/astral-sh/ty/issues/1478
2025-11-06 09:00:43 -05:00
Ben Beasley
5517c9943a Require ignore 0.4.24 in Cargo.toml (#21292)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->
Since 4c4ddc8c29, ruff uses the `WalkBuilder::current_dir` API
[introduced in `ignore` version
0.4.24](https://diff.rs/ignore/0.4.23/0.4.24/src%2Fwalk.rs), so it
should explicitly depend on this minimum version.

See also https://github.com/astral-sh/ruff/pull/20979.

## Test Plan

<!-- How was it tested? -->
Source inspection verifies this version is necessary; no additional
testing is required since `Cargo.lock` already has (at least) this
version.
2025-11-06 07:43:32 -05:00
Matthew Mckee
b5ff96595d [ty] Favour imported symbols over builtin symbols (#21285)
<!--
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

Raised by @AlexWaygood.

We previously did not favour imported symbols, when we probably
should've

## Test Plan

Add test showing that we favour imported symbol even if it is
alphabetically after other symbols that are builtin.
2025-11-06 06:46:08 -05:00
Loïc Riegel
c6573b16ac docs: revise Ruff setup instructions for Zed editor (#20935)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-06 01:11:29 +00:00
Micha Reiser
76127e5fb5 [ty] Update salsa (#21281) 2025-11-05 22:15:01 +00:00
Bhuminjay Soni
cddc0fedc2 [syntax-error]: no binding for nonlocal PLE0117 as a semantic syntax error (#21032)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

This PR ports PLE0117 as a semantic syntax error.

## Test Plan

<!-- How was it tested? -->
Tests previously written

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-11-05 19:13:28 +00:00
Douglas Creager
eda85f3c64 [ty] Constraining a typevar with itself (possibly via union or intersection) (#21273)
This PR carries over some of the `has_relation_to` logic for comparing a
typevar with itself. A typevar will specialize to the same type if it's
mentioned multiple times, so it is always assignable to and a subtype of
itself. (Note that typevars can only specialize to fully static types.)
This is also true when the typevar appears in a union on the right-hand
side, or in an intersection on the left-hand side. Similarly, a typevar
is always disjoint from its negation, so when a negated typevar appears
on the left-hand side, the constraint set is never satisfiable.

(Eventually this will allow us to remove the corresponding clauses from
`has_relation_to`, but that can't happen until more of #20093 lands.)
2025-11-05 12:31:53 -05:00
chiri
cef6600cf3 [ruff] Fix false positives on starred arguments (RUF057) (#21256)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/21209

## Test Plan

`cargo nextest run ruf057`
2025-11-05 12:07:33 -05:00
Ibraheem Ahmed
5c69e00d1c [ty] Simplify unions containing multiple type variables during inference (#21275)
## Summary

Splitting this one out from https://github.com/astral-sh/ruff/pull/21210. This is also something that should be made obselete by the new constraint solver, but is easy enough to fix now.
2025-11-05 15:03:19 +00:00
Micha Reiser
7569b09bdd [ty] Add ty_server::Db trait (#21241) 2025-11-05 13:40:07 +00:00
Micha Reiser
7009d60260 [ty] Refactor Range to/from TextRange conversion as prep for notebook support (#21230) 2025-11-05 13:24:03 +00:00
David Peter
f79044478c [ty] Fix playground crash when file name includes path separator (#21151) 2025-11-04 18:36:36 +01:00
Dan Parizher
47e41ac6b6 [refurb] Fix false negative for underscores before sign in Decimal constructor (FURB157) (#21190)
## Summary

Fixes FURB157 false negative where `Decimal("_-1")` was not flagged as
verbose when underscores precede the sign character. This fixes #21186.

## Problem Analysis

The `verbose-decimal-constructor` (FURB157) rule failed to detect
verbose `Decimal` constructors when the sign character (`+` or `-`) was
preceded by underscores. For example, `Decimal("_-1")` was not flagged,
even though it can be simplified to `Decimal(-1)`.

The bug occurred because the rule checked for the sign character at the
start of the string before stripping leading underscores. According to
Python's `Decimal` parser behavior (as documented in CPython's
`_pydecimal.py`), underscores are removed before parsing the sign. The
rule's logic didn't match this behavior, causing a false negative for
cases like `"_-1"` where the underscore came before the sign.

This was a regression introduced in version 0.14.3, as these cases were
correctly flagged in version 0.14.2.

## Approach

The fix updates the sign extraction logic to:
1. Strip leading underscores first (matching Python's Decimal parser
behavior)
2. Extract the sign from the underscore-stripped string
3. Preserve the string after the sign for normalization purposes

This ensures that cases like `Decimal("_-1")`, `Decimal("_+1")`, and
`Decimal("_-1_000")` are correctly detected and flagged. The
normalization logic was also updated to use the string after the sign
(without underscores) to avoid double signs in the replacement output.
2025-11-04 11:02:50 -05:00
David Peter
2e7ab00d51 [ty] Allow values of type None in type expressions (#21263)
## Summary

Allow values of type `None` in type expressions. The [typing
spec](https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions)
could be more explicit on whether this is actually allowed or not, but
it seems relatively harmless and does help in some use cases like:

```py
try:
    from module import MyClass
except ImportError:
    MyClass = None  # ty: ignore


def f(m: MyClass):
    pass
``` 

## Test Plan

Updated tests, ecosystem check.
2025-11-04 16:29:55 +01:00
Ibraheem Ahmed
d8106d38a0 Run codspeed benchmarks with profiling profile (#21261)
## Summary

This reduces the walltime benchmarks from 15m to 10m, and we should see an even bigger improvement once build caching kicks in, so I think it's worth the downsides.
2025-11-04 09:59:40 -05:00
Micha Reiser
4fd8d4b0ee [ty] Update expected diagnostic count in benchmarks (#21269) 2025-11-04 03:18:12 +00:00
Brent Westbrook
63b1c1ea8b Avoid extra parentheses for long match patterns with as captures (#21176)
Summary
--

This PR fixes #17796 by taking the approach mentioned in
https://github.com/astral-sh/ruff/issues/17796#issuecomment-2847943862
of simply recursing into the `MatchAs` patterns when checking if we need
parentheses. This allows us to reuse the parentheses in the inner
pattern before also breaking the `MatchAs` pattern itself:

```diff
 match class_pattern:
     case Class(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) as capture:
         pass
-    case (
-        Class(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) as capture
-    ):
+    case Class(
+        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+    ) as capture:
         pass
-    case (
-        Class(
-            xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-        ) as capture
-    ):
+    case Class(
+        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+    ) as capture:
         pass
     case (
         Class(
@@ -685,13 +683,11 @@
 match sequence_pattern_brackets:
     case [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] as capture:
         pass
-    case (
-        [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] as capture
-    ):
+    case [
+        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+    ] as capture:
         pass
-    case (
-        [
-            xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-        ] as capture
-    ):
+    case [
+        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+    ] as capture:
         pass
```

I haven't really resolved the question of whether or not it's okay
always to recurse, but I'm hoping the ecosystem check on this PR might
shed some light on that.

Test Plan
--

New tests based on the issue and then reviewing the ecosystem check here
2025-11-03 17:06:52 -05:00
Micha Reiser
3c5e4e1477 [ty] Update salsa (#21265) 2025-11-03 22:00:30 +00:00
Ibraheem Ahmed
3c8fb68765 [ty] dict is not assignable to TypedDict (#21238)
## Summary

A lot of the bidirectional inference work relies on `dict` not being
assignable to `TypedDict`, so I think it makes sense to add this before
fully implementing https://github.com/astral-sh/ty/issues/1387.
2025-11-03 16:57:49 -05:00
Alex Waygood
42adfd40ea Run py-fuzzer with --profile=profiling locally and in CI (#21266) 2025-11-03 16:53:42 -05:00
chiri
79a02711c1 [refurb] Expand fix safety for keyword arguments and Decimals (FURB164) (#21259)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/21257

## Test Plan

`cargo nextest run furb164`
2025-11-03 16:09:02 -05:00
David Peter
d2fe6347fb [ty] Rename UnionType to types.UnionType (#21262) 2025-11-03 22:06:56 +01:00
David Peter
1fe958c694 [ty] Implicit type aliases: Support for PEP 604 unions (#21195)
## Summary

Add support for implicit type aliases that use PEP 604 unions:
```py
IntOrStr = int | str

reveal_type(IntOrStr)  # UnionType

def _(int_or_str: IntOrStr):
    reveal_type(int_or_str)  # int | str
```

## Typing conformance

The changes are either removed false positives, or new diagnostics due
to known limitations unrelated to this PR.

## Ecosystem impact

Spot checked, a mix of true positives and known limitations.

## Test Plan

New Markdown tests.
2025-11-03 21:50:25 +01:00
Carl Meyer
fe4ee81b97 [ty] prefer submodule over module __getattr__ in from-imports (#21260)
Fixes https://github.com/astral-sh/ty/issues/1053

## Summary

Other type checkers prioritize a submodule over a package `__getattr__`
in `from mod import sub`, even though the runtime precedence is the
other direction. In effect, this is making an implicit assumption that a
module `__getattr__` will not handle (that is, will raise
`AttributeError`) for names that are also actual submodules, rather than
shadowing them. In practice this seems like a realistic assumption in
the ecosystem? Or at least the ecosystem has adapted to it, and we need
to adapt this precedence also, for ecosystem compatibility.

The implementation is a bit ugly, precisely because it departs from the
runtime semantics, and our implementation is oriented toward modeling
runtime semantics accurately. That is, `__getattr__` is modeled within
the member-lookup code, so it's hard to split "member lookup result from
module `__getattr__`" apart from other member lookup results. I did this
via a synthetic `TypeQualifier::FROM_MODULE_GETATTR` that we attach to a
type resulting from a member lookup, which isn't beautiful but it works
well and doesn't introduce inefficiency (e.g. redundant member lookups).

## Test Plan

Updated mdtests.

Also added a related mdtest formalizing our support for a module
`__getattr__` that is explicitly annotated to accept a limited set of
names. In principle this could be an alternative (more explicit) way to
handle the precedence problem without departing from runtime semantics,
if the ecosystem would adopt it.

### Ecosystem analysis

Lots of removed diagnostics which are an improvement because we now
infer the expected submodule.

Added diagnostics are mostly unrelated issues surfaced now because we
previously had an earlier attribute error resulting in `Unknown`; now we
correctly resolve the module so that earlier attribute error goes away,
we get an actual type instead of `Unknown`, and that triggers a new
error.

In scipy and sklearn, the module `__getattr__` which we were respecting
previously is un-annotated so returned a forgiving `Unknown`; now we
correctly see the actual module, which reveals some cases of
https://github.com/astral-sh/ty/issues/133 that were previously hidden
(`scipy/optimize/__init__.py` [imports `from
._tnc`](eff82ca575/scipy/optimize/__init__.py (L429)).)

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-11-03 15:24:01 -05:00
Wei Lee
0433526897 [airflow] extend deprecated argument concurrency in airflow..DAG (AIR301) (#21220)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->
* extend AIR301 to include deprecated argument `concurrency` in
`airflow....DAG`

## Test Plan

<!-- How was it tested? -->

update the existing test fixture in the first commit and then reorganize
in the second one
2025-11-03 15:20:20 -05:00
Tom Kuson
78ee7ae925 [flake8-comprehensions] Fix typo in C416 documentation (#21184)
## Summary

Adds missing curly brace to the C416 documentation.

## Test Plan

Build the docs
2025-11-03 14:04:59 -05:00
renovate[bot]
21ec8aa7d4 Update Rust crate unicode-ident to v1.0.22 (#21228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 16:10:49 +00:00
renovate[bot]
64a255df49 Update to Unicode 17 for line-width calculations (#21231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 15:55:50 +00:00
Shunsuke Shibayama
b5305b5f32 [ty] Fix panic due to simplifying Divergent types out of intersections types (#21253) 2025-11-03 15:41:11 +00:00
Alex Waygood
39f105bc4a [ty] Use "cannot" consistently over "can not" (#21255) 2025-11-03 10:38:20 -05:00
Micha Reiser
e8c35b9704 [ty] Simplify semantic token tests (#21206) 2025-11-03 15:35:42 +00:00
Micha Reiser
1b2ed6a503 [ty] Fix caching of imported modules in playground (#21251) 2025-11-03 15:00:30 +00:00
Micha Reiser
de9df1b326 Revert "Update CodSpeedHQ/action action to v4.3.1" (#21252) 2025-11-03 14:53:04 +00:00
Matthew Mckee
e2e83acd2f [ty] Remove mentions of VS Code from server logs (#21155)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-03 14:49:58 +00:00
Dan Parizher
6ddfb51d71 [flake8-bugbear] Mark fix as unsafe for non-NFKC attribute names (B009, B010) (#21131) 2025-11-03 14:45:23 +00:00
Matthew Mckee
e017b039df [ty] Favor in scope completions (#21194)
<!--
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

Resolves https://github.com/astral-sh/ty/issues/1464

We sort the completions before we add the unimported ones, meaning that
imported completions show up before unimported ones.

This is also spoken about in
https://github.com/astral-sh/ty/issues/1274, and this is probably a
duplicate of that.

@AlexWaygood mentions this
[here](https://github.com/astral-sh/ty/issues/1274#issuecomment-3345942698)
too.

## Test Plan

Add a test showing even if an unimported completion "should"
(alphabetically before) come first, we favor the imported one.
2025-11-03 09:33:05 -05:00
Brent Westbrook
0dfd55babf Delete unused AsciiCharSet in FURB156 (#21181)
Summary
--

This code has been unused since #14233 but not detected by clippy I
guess. This should help to remove the temptation to use the set
comparison again like I suggested in #21144. And we shouldn't do the set
comparison because of #13802, which #14233 fixed.

Test Plan
--

Existing tests
2025-11-03 08:38:34 -05:00
renovate[bot]
f947c23cd7 Update Rust crate tempfile to v3.23.0 (#21248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:41:20 +00:00
renovate[bot]
02879fa377 Update actions/setup-node action to v6 (#21249)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:34:13 +00:00
renovate[bot]
7dbfb56c3d Update astral-sh/setup-uv action to v7 (#21250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:33:16 +00:00
renovate[bot]
f97c38dd88 Update Rust crate matchit to 0.9.0 (#21245)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:32:49 +00:00
renovate[bot]
14fce1f788 Update Rust crate regex to v1.12.2 (#21246)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:32:04 +00:00
renovate[bot]
31194d048d Update Rust crate serde_with to v3.15.1 (#21247)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:31:24 +00:00
renovate[bot]
fe95ff6b06 Update dependency pyodide to ^0.29.0 (#21236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:23:24 +00:00
renovate[bot]
61c1007137 Update Rust crate bitflags to v2.10.0 (#21242)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:21:41 +00:00
renovate[bot]
884c3b178e Update Rust crate indexmap to v2.12.0 (#21244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:21:03 +00:00
renovate[bot]
ade727ce66 Update Rust crate csv to v1.4.0 (#21243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:20:42 +00:00
renovate[bot]
3493c9b67a Update dependency tomli to v2.3.0 (#21240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:17:38 +00:00
renovate[bot]
666dd5fef1 Update dependency ruff to v0.14.3 (#21239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:13:38 +00:00
renovate[bot]
bb05527350 Update dependency monaco-editor to ^0.54.0 (#21235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:06:16 +00:00
renovate[bot]
dc373e639e Update CodSpeedHQ/action action to v4.3.1 (#21234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:04:20 +00:00
renovate[bot]
fc71c90de6 Update taiki-e/install-action action to v2.62.45 (#21233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 03:02:16 +00:00
renovate[bot]
c11b00bea0 Update Rust crate wasm-bindgen-test to v0.3.55 (#21232)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:57:13 +00:00
renovate[bot]
770b4d12ab Update Rust crate tikv-jemallocator to v0.6.1 (#21226)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:35:50 +00:00
renovate[bot]
c596a78c08 Update Rust crate schemars to v1.0.5 (#21222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:27:40 +00:00
renovate[bot]
cb98175a36 Update Rust crate syn to v2.0.108 (#21224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:22:04 +00:00
renovate[bot]
3bef60f69a Update Rust crate toml to v0.9.8 (#21227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:21:47 +00:00
renovate[bot]
f477e11d26 Update Rust crate thiserror to v2.0.17 (#21225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:21:21 +00:00
renovate[bot]
c0bd092fa9 Update Rust crate snapbox to v0.6.23 (#21223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:20:19 +00:00
renovate[bot]
73b9b8eb6b Update Rust crate proc-macro2 to v1.0.103 (#21221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:15:56 +00:00
renovate[bot]
80eeb1d64f Update Rust crate clap to v4.5.51 (#21214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 01:50:12 +00:00
renovate[bot]
50b75cfcc6 Update cargo-bins/cargo-binstall action to v1.15.10 (#21212)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 01:49:44 +00:00
renovate[bot]
1c4a9d6a06 Update Rust crate globset to v0.4.18 (#21216)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[globset](https://redirect.github.com/BurntSushi/ripgrep/tree/master/crates/globset)
([source](https://redirect.github.com/BurntSushi/ripgrep/tree/HEAD/crates/globset))
| workspace.dependencies | patch | `0.4.17` -> `0.4.18` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>BurntSushi/ripgrep (globset)</summary>

###
[`v0.4.18`](https://redirect.github.com/BurntSushi/ripgrep/compare/globset-0.4.17...globset-0.4.18)

[Compare
Source](https://redirect.github.com/BurntSushi/ripgrep/compare/globset-0.4.17...globset-0.4.18)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:48:22 +01:00
renovate[bot]
222c6fd496 Update Rust crate ctrlc to v3.5.1 (#21215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 01:48:02 +00:00
renovate[bot]
b754abff1b Update Rust crate aho-corasick to v1.1.4 (#21213)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [aho-corasick](https://redirect.github.com/BurntSushi/aho-corasick) |
workspace.dependencies | patch | `1.1.3` -> `1.1.4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>BurntSushi/aho-corasick (aho-corasick)</summary>

###
[`v1.1.4`](https://redirect.github.com/BurntSushi/aho-corasick/compare/1.1.3...1.1.4)

[Compare
Source](https://redirect.github.com/BurntSushi/aho-corasick/compare/1.1.3...1.1.4)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 02:47:30 +01:00
renovate[bot]
41fe4d7f8c Update Rust crate ignore to v0.4.25 (#21217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 01:47:12 +00:00
renovate[bot]
f14631e1cc Update Rust crate indicatif to v0.18.2 (#21218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 01:46:05 +00:00
renovate[bot]
02f2dba28e Update Rust crate indoc to v2.0.7 (#21219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 01:45:20 +00:00
Carl Meyer
0454a72674 [ty] don't union in default type for annotated parameters (#21208) 2025-11-02 18:21:54 -05:00
Carl Meyer
c32234cf0d [ty] support subscripting typing.Literal with a type alias (#21207)
Fixes https://github.com/astral-sh/ty/issues/1368

## Summary

Add support for patterns like this, where a type alias to a literal type
(or union of literal types) is used to subscript `typing.Literal`:

```py
type MyAlias = Literal[1]
def _(x: Literal[MyAlias]): ...
```

This shows up in the ecosystem report for PEP 613 type alias support.

One interesting case is an alias to `bool` or an enum type. `bool` is an
equivalent type to `Literal[True, False]`, which is a union of literal
types. Similarly an enum type `E` is also equivalent to a union of its
member literal types. Since (for explicit type aliases) we infer the RHS
directly as a type expression, this makes it difficult for us to
distinguish between `bool` and `Literal[True, False]`, so we allow
either one to (or an alias to either one) to appear inside `Literal`,
where other type checkers allow only the latter.

I think for implicit type aliases it may be simpler to support only
types derived from actually subscripting `typing.Literal`, though, so I
didn't make a TODO-comment commitment here.

## Test Plan

Added mdtests, including TODO-filled tests for PEP 613 and implicit type
aliases.

### Conformance suite

All changes here are positive -- we now emit errors on lines that should
be errors. This is a side effect of the new implementation, not the
primary purpose of this PR, but it's still a positive change.

### Ecosystem

Eliminates one ecosystem false positive, where a PEP 695 type alias for
a union of literal types is used to subscript `typing.Literal`.
2025-11-02 12:39:55 -05:00
David Peter
566d1d6497 [ty] Update to the latest version of the conformance suite (#21205)
## Summary

There have been some larger-scale updates to the conformance suite since
we introduced our CI job, so it seems sensible to bump the version of
the conformance suite to the latest state.

## Test plan

This is a bit awkward to test. Here is the diff of running ty on the
conformance suite before and after this bump. I filtered out line/column
information (`sed -re 's/\.py:[0-9]+:[0-9]+:/.py/'`) to avoid spurious
changes from content that has simply been moved around.

```diff
1,2c1
< fatal[panic] Panicked at /home/shark/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/cdd0b85/src/function/execute.rs:419:17 when checking `/home/shark/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(1a99c)): execute: too many cycle iterations`
< src/type_checker.py error[unresolved-import] Cannot resolve imported module `tqdm`
---
> fatal[panic] Panicked at /home/shark/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/cdd0b85/src/function/execute.rs:419:17 when checking `/home/shark/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(6e4c)): execute: too many cycle iterations`
205,206d203
< tests/constructors_call_metaclass.py error[type-assertion-failure] Argument does not have asserted type `Never`
< tests/constructors_call_metaclass.py error[missing-argument] No argument provided for required parameter `x` of function `__new__`
268a266,273
> tests/dataclasses_match_args.py error[type-assertion-failure] Argument does not have asserted type `tuple[Literal["x"]]`
> tests/dataclasses_match_args.py error[unresolved-attribute] Class `DC1` has no attribute `__match_args__`
> tests/dataclasses_match_args.py error[type-assertion-failure] Argument does not have asserted type `tuple[Literal["x"]]`
> tests/dataclasses_match_args.py error[unresolved-attribute] Class `DC2` has no attribute `__match_args__`
> tests/dataclasses_match_args.py error[type-assertion-failure] Argument does not have asserted type `tuple[Literal["x"]]`
> tests/dataclasses_match_args.py error[unresolved-attribute] Class `DC3` has no attribute `__match_args__`
> tests/dataclasses_match_args.py error[unresolved-attribute] Class `DC4` has no attribute `__match_args__`
> tests/dataclasses_match_args.py error[type-assertion-failure] Argument does not have asserted type `tuple[()]`
339a345
> tests/directives_assert_type.py error[type-assertion-failure] Argument does not have asserted type `Any`
424a431
> tests/generics_defaults.py error[type-assertion-failure] Argument does not have asserted type `Any`
520a528,529
> tests/generics_syntax_infer_variance.py error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `T@ShouldBeCovariant2 | Sequence[T@ShouldBeCovariant2]`
> tests/generics_syntax_infer_variance.py error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
711a721
> tests/namedtuples_define_class.py error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4
795d804
< tests/protocols_explicit.py error[invalid-attribute-access] Cannot assign to ClassVar `cm1` from an instance of type `Self@__init__`
822,823d830
< tests/qualifiers_annotated.py error[invalid-syntax] named expression cannot be used within a type annotation
< tests/qualifiers_annotated.py error[invalid-syntax] await expression cannot be used within a type annotation
922a930,953
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `Movie`: Unknown key "novel_adaptation"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `Movie`: Unknown key "year"
> tests/typeddicts_extra_items.py error[type-assertion-failure] Argument does not have asserted type `bool`
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `Movie`: Unknown key "novel_adaptation"
> tests/typeddicts_extra_items.py error[invalid-argument-type] Invalid argument to key "year" with declared type `int` on TypedDict `InheritedMovie`: value of type `None`
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `InheritedMovie`: Unknown key "other_extra_key"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `MovieEI`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `MovieExtraInt`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `MovieExtraStr`: Unknown key "description"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `MovieExtraInt`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `NonClosedMovie`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `ExtraMovie`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `ExtraMovie`: Unknown key "language"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `ClosedMovie`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `MovieExtraStr`: Unknown key "summary"
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `MovieExtraInt`: Unknown key "year"
> tests/typeddicts_extra_items.py error[invalid-assignment] Object of type `dict[Unknown | str, Unknown | str | int]` is not assignable to `Mapping[str, int]`
> tests/typeddicts_extra_items.py error[type-assertion-failure] Argument does not have asserted type `list[tuple[str, int | str]]`
> tests/typeddicts_extra_items.py error[type-assertion-failure] Argument does not have asserted type `list[int | str]`
> tests/typeddicts_extra_items.py error[unresolved-attribute] Object of type `IntDict` has no attribute `clear`
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `IntDictWithNum`: Unknown key "bar" - did you mean "num"?
> tests/typeddicts_extra_items.py error[type-assertion-failure] Argument does not have asserted type `tuple[str, int]`
> tests/typeddicts_extra_items.py error[invalid-key] Cannot access `IntDictWithNum` with a key of type `str`. Only string literals are allowed as keys on TypedDicts.
> tests/typeddicts_extra_items.py error[invalid-key] Invalid key for TypedDict `IntDictWithNum` of type `str`
950c981
< Found 949 diagnostics
---
> Found 980 diagnostics
```
2025-11-02 17:33:31 +01:00
Micha Reiser
6c3d6124c8 [ty] Fix range filtering for tokens starting at the end of the requested range (#21193)
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-11-02 14:58:36 +00:00
David Peter
73107a083c [ty] Type inference for comprehensions (#20962)
## Summary

Adds type inference for list/dict/set comprehensions, including
bidirectional inference:

```py
reveal_type({k: v for k, v in [("a", 1), ("b", 2)]})  # dict[Unknown | str, Unknown | int]

squares: list[int | None] = [x for x in range(10)]
reveal_type(squares)  # list[int | None]
```

## Ecosystem impact

I did spot check the changes and most of them seem like known
limitations or true positives. Without proper bidirectional inference,
we saw a lot of false positives.

## Test Plan

New Markdown tests
2025-11-02 14:35:33 +01:00
Matthew Mckee
de1a6fb8ad Clean up definition completions docs and tests (#21183)
<!--
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

@BurntSushi provided some feedback in #21146 so i address it here.
2025-11-02 08:01:06 -05:00
Alex Waygood
bff32a41dc [ty] Increase timeout-minutes to 10 for py-fuzzer job (#21196) 2025-11-01 22:06:03 -04:00
Micha Reiser
17c7b3cde1 Bump MSRV to Rust 1.89 (#21180) 2025-11-01 02:26:38 +00:00
Micha Reiser
921f409ee8 Update Rust toolchain to 1.91 (#21179) 2025-11-01 01:50:58 +00:00
github-actions[bot]
a151f9746d [ty] Sync vendored typeshed stubs (#21178)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
2025-10-31 21:03:40 -04:00
Gautham Venkataraman
521217bb90 [ruff]: Make ruff analyze graph work with jupyter notebooks (#21161)
Co-authored-by: Gautham Venkataraman <gautham@dexterenergy.ai>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-31 21:47:01 +00:00
Alex Waygood
a32d5b8dc4 [ty] Improve exhaustiveness analysis for type variables with bounds or constraints (#21172) 2025-10-31 16:51:11 -04:00
Micha Reiser
6337e22f0c [ty] Smaller refactors to server API in prep for notebook support (#21095) 2025-10-31 20:00:04 +00:00
Brent Westbrook
827d8ae5d4 Allow newlines after function headers without docstrings (#21110)
Summary
--

This is a first step toward fixing #9745. After reviewing our open
issues and several Black issues and PRs, I personally found the function
case the most compelling, especially with very long argument lists:

```py
def func(
	self,
	arg1: int,
	arg2: bool,
	arg3: bool,
	arg4: float,
	arg5: bool,
) -> tuple[...]:
	if arg2 and arg3:
		raise ValueError
```

or many annotations:

```py
def function(
    self, data: torch.Tensor | tuple[torch.Tensor, ...], other_argument: int
) -> torch.Tensor | tuple[torch.Tensor, ...]:
    do_something(data)
    return something
```

I think docstrings help the situation substantially both because syntax
highlighting will usually give a very clear separation between the
annotations and the docstring and because we already allow a blank line
_after_ the docstring:

```py
def function(
    self, data: torch.Tensor | tuple[torch.Tensor, ...], other_argument: int
) -> torch.Tensor | tuple[torch.Tensor, ...]:
    """
	A function doing something.

	And a longer description of the things it does.
	"""

    do_something(data)
    return something
```

There are still other comments on #9745, such as [this one] with 9
upvotes, where users specifically request blank lines in all block
types, or at least including conditionals and loops. I'm sympathetic to
that case as well, even if personally I don't find an [example] like
this:

```py
if blah:

    # Do some stuff that is logically related
    data = get_data()

    # Do some different stuff that is logically related
    results = calculate_results()

    return results
```

to be much more readable than:

```py
if blah:
    # Do some stuff that is logically related
    data = get_data()

    # Do some different stuff that is logically related
    results = calculate_results()

    return results
```

I'm probably just used to the latter from the formatters I've used, but
I do prefer it. I also think that functions are the least susceptible to
the accidental introduction of a newline after refactoring described in
Micha's [comment] on #8893.

I actually considered further restricting this change to functions with
multiline headers. I don't think very short functions like:

```py
def foo():

    return 1
```

benefit nearly as much from the allowed newline, but I just went with
any function without a docstring for now. I guess a marginal case like:

```py
def foo(a_long_parameter: ALongType, b_long_parameter: BLongType) -> CLongType:

    return 1
```

might be a good argument for not restricting it.

I caused a couple of syntax errors before adding special handling for
the ellipsis-only case, so I suspect that there are some other
interesting edge cases that may need to be handled better.

Test Plan
--

Existing tests, plus a few simple new ones. As noted above, I suspect
that we may need a few more for edge cases I haven't considered.

[this one]:
https://github.com/astral-sh/ruff/issues/9745#issuecomment-2876771400
[example]:
https://github.com/psf/black/issues/902#issuecomment-1562154809
[comment]:
https://github.com/astral-sh/ruff/issues/8893#issuecomment-1867259744
2025-10-31 14:53:40 -04:00
David Peter
1734ddfb3e [ty] Do not promote literals in contravariant positions of generic specializations (#21171)
## Summary

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

supersedes https://github.com/astral-sh/ruff/pull/20950 by @ibraheemdev 

## Test Plan

New regression test
2025-10-31 17:48:34 +01:00
Ibraheem Ahmed
ff3a6a8fbd [ty] Support type context of union attribute assignments (#21170)
## Summary

Turns out this is easy to implement. Resolves
https://github.com/astral-sh/ty/issues/1375.
2025-10-31 12:41:14 -04:00
Carl Meyer
9664474c51 [ty] rollback preferring declared type on invalid TypedDict creation (#21169)
## Summary

Discussion with @ibraheemdev clarified that
https://github.com/astral-sh/ruff/pull/21168 was incorrect. In a case of
failed inference of a dict literal as a `TypedDict`, we should store the
context-less inferred type of the dict literal as the type of the dict
literal expression itself; the fallback to declared type should happen
at the level of the overall assignment definition.

The reason the latter isn't working yet is because currently we
(wrongly) consider a homogeneous dict type as assignable to a
`TypedDict`, so we don't actually consider the assignment itself as
failed. So the "bug" I observed (and tried to fix) will naturally be
fixed by implementing TypedDict assignability rules.

Rollback https://github.com/astral-sh/ruff/pull/21168 except for the
tests, and modify the tests to include TODOs as needed.

## Test Plan

Updated mdtests.
2025-10-31 12:06:47 -04:00
Luca Chiodini
69b4c29924 Consistently wrap tokens in parser diagnostics in backticks instead of 'quotes' (#21163)
The parser currently uses single quotes to wrap tokens. This is
inconsistent with the rest of ruff/ty, which use backticks.

For example, see the inconsistent diagnostics produced in this simple
example: https://play.ty.dev/0a9d6eab-6599-4a1d-8e40-032091f7f50f

Consistently wrapping tokens in backticks produces uniform diagnostics.
Following the style decision of #723, in #2889 some quotes were already
switched into backticks.

This is also in line with Rust's guide on diagnostics
(https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure):

> When code or an identifier must appear in a message or label, it
should be surrounded with backticks
2025-10-31 11:59:11 -04:00
Ibraheem Ahmed
bb40c34361 [ty] Use declared attribute types as type context (#21143)
## Summary

For example:
```py
class X:
    x: list[int | str]

def _(x: X):
    x.x = [1]
```

Resolves https://github.com/astral-sh/ty/issues/1375.
2025-10-31 15:48:28 +00:00
chiri
b93d8f2b9f [refurb] Preserve argument ordering in autofix (FURB103) (#20790)
Fixes https://github.com/astral-sh/ruff/issues/20785
2025-10-31 11:16:09 -04:00
Carl Meyer
1d111c8780 [ty] prefer declared type on invalid TypedDict creation (#21168)
## Summary

In general, when we have an invalid assignment (inferred assigned type
is not assignable to declared type), we fall back to inferring the
declared type, since the declared type is a more explicit declaration of
the programmer's intent. This also maintains the invariant that our
inferred type for a name is always assignable to the declared type for
that same name. For example:

```py
x: str = 1
reveal_type(x)  # revealed: str
```

We weren't following this pattern for dictionary literals inferred (via
type context) as a typed dictionary; if the literal was not valid for
the annotated TypedDict type, we would just fall back to the normal
inferred type of the dict literal, effectively ignoring the annotation,
and resulting in inferred type not assignable to declared type.

## Test Plan

Added mdtest assertions.
2025-10-31 11:12:06 -04:00
chiri
9d7da914b9 Improve extend docs (#21135)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-31 15:10:14 +00:00
David Peter
0c2cf75869 [ty] Do not promote literals in contravariant position (#21164)
## Summary

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

## Test Plan

Regression tests
2025-10-31 16:00:30 +01:00
Ibraheem Ahmed
1d6ae8596a [ty] Prefer exact matches when solving constrained type variables (#21165)
## Summary

The solver is currently order-dependent, and will choose a supertype
over the exact type if it appears earlier in the list of constraints. We
could be smarter and try to choose the most precise subtype, but I
imagine this is something the new constraint solver will fix anyways,
and this fixes the issue showing up on
https://github.com/astral-sh/ruff/pull/21070.
2025-10-31 10:58:09 -04:00
Douglas Creager
cf4e82d4b0 [ty] Add and test when constraint sets are satisfied by their typevars (#21129)
This PR adds a new `satisfied_by_all_typevar` method, which implements
one of the final steps of actually using these dang constraint sets.
Constraint sets exist to help us check assignability and subtyping of
types in the presence of typevars. We construct a constraint set
describing the conditions under which assignability holds between the
two types. Then we check whether that constraint set is satisfied for
the valid specializations of the relevant typevars (which is this new
method).

We also add a new `ty_extensions.ConstraintSet` method so that we can
test this method's behavior in mdtests, before hooking it up to the rest
of the specialization inference machinery.
2025-10-31 10:53:37 -04:00
Ibraheem Ahmed
1baf98aab3 [ty] Fix is_disjoint_from with @final classes (#21167)
## Summary

We currently perform a subtyping check instead of the intended subclass
check (and the subtyping check is confusingly named `is_subclass_of`).
This showed up in https://github.com/astral-sh/ruff/pull/21070.
2025-10-31 14:50:54 +00:00
Carl Meyer
3179b05221 [ty] don't assume in diagnostic messages that a TypedDict key error is about subscript access (#21166)
## Summary

Before this PR, we would emit diagnostics like "Invalid key access" for
a TypedDict literal with invalid key, which doesn't make sense since
there's no "access" in that case. This PR just adjusts the wording to be
more general, and adjusts the documentation of the lint rule too.

I noticed this in the playground and thought it would be a quick fix. As
usual, it turned out to be a bit more subtle than I expected, but for
now I chose to punt on the complexity. We may ultimately want to have
different rules for invalid subscript vs invalid TypedDict literal,
because an invalid key in a TypedDict literal is low severity: it's a
typo detector, but not actually a type error. But then there's another
wrinkle there: if the TypedDict is `closed=True`, then it _is_ a type
error. So would we want to separate the open and closed cases into
separate rules, too? I decided to leave this as a question for future.

If we wanted to use separate rules, or use specific wording for each
case instead of the generalized wording I chose here, that would also
involve a bit of extra work to distinguish the cases, since we use a
generic set of functions for reporting these errors.

## Test Plan

Added and updated mdtests.
2025-10-31 10:49:59 -04:00
Aria Desires
172e8d4ae0 [ty] Support implicit imports of submodules in __init__.pyi (#20855)
This is a second take at the implicit imports approach, allowing `from .
import submodule` in an `__init__.pyi` to create the
`mypackage.submodule` attribute everyhere.

This implementation operates inside of the
available_submodule_attributes subsystem instead of as a re-export rule.

The upside of this is we are no longer purely syntactic, and absolute
from imports that happen to target submodules work (an intentional
discussed deviation from pyright which demands a relative from import).
Also we don't re-export functions or classes.

The downside(?) of this is star imports no longer see these attributes
(this may be either good or bad. I believe it's not a huge lift to make
it work with star imports but it's some non-trivial reworking).

I've also intentionally made `import mypackage.submodule` not trigger
this rule although it's trivial to change that.

I've tried to cover as many relevant cases as possible for discussion in
the new test file I've added (there are some random overlaps with
existing tests but trying to add them piecemeal felt confusing and
weird, so I just made a dedicated file for this extension to the rules).

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

<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
2025-10-31 14:29:24 +00:00
Mahmoud Saada
735ec0c1f9 [ty] Fix generic inference for non-dataclass inheriting from generic dataclass (#21159)
## Summary

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

This PR fixes a regression introduced in alpha.24 where non-dataclass
children of generic dataclasses lost generic type parameter information
during `__init__` synthesis.

The issue occurred because when looking up inherited members in the MRO,
the child class's `inherited_generic_context` was correctly passed down,
but `own_synthesized_member()` (which synthesizes dataclass `__init__`
methods) didn't accept this parameter. It only used
`self.inherited_generic_context(db)`, which returned the parent's
context instead of the child's.

The fix threads the child's generic context through to the synthesis
logic, allowing proper generic type inference for inherited dataclass
constructors.

## Test Plan

- Added regression test for non-dataclass inheriting from generic
dataclass
- Verified the exact repro case from the issue now works
- All 277 mdtest tests passing
- Clippy clean
- Manually verified with Python runtime, mypy, and pyright - all accept
this code pattern

## Verification

Tested against multiple type checkers:
-  Python runtime: Code works correctly
-  mypy: No issues found
-  pyright: 0 errors, 0 warnings
-  ty alpha.23: Worked (before regression)
-  ty alpha.24: Regression
-  ty with this fix: Works correctly

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: David Peter <mail@david-peter.de>
2025-10-31 13:55:17 +01:00
Ben Beasley
3585c96ea5 Update etcetera to 0.11.0 (#21160) 2025-10-31 12:53:18 +00:00
Micha Reiser
4b026c2a55 Fix missing diagnostics for notebooks (#21156) 2025-10-31 01:16:43 +00:00
Matthew Mckee
4b758b3746 [ty] Fix tests for definition completions (#21153) 2025-10-31 00:43:50 +00:00
Amethyst Reese
8737a2d5f5 Bump v0.14.3 (#21152)
- **Upgrade to rooster==0.1.1**
- **Changelog for v0.14.3**
- **Bump v0.14.3**
2025-10-30 17:06:29 -07:00
Matthew Mckee
3be3a10a2f [ty] Don't provide completions when in class or function definition (#21146) 2025-10-30 23:19:59 +00:00
Alex Waygood
13375d0e42 [ty] Use the top materialization of classes for narrowing in class-patterns for match statements (#21150) 2025-10-30 20:44:51 +00:00
Douglas Creager
c0b04d4b7c [ty] Update "constraint implication" relation to work on constraints between two typevars (#21068)
It's possible for a constraint to mention two typevars. For instance, in
the body of

```py
def f[S: int, T: S](): ...
```

the baseline constraint set would be `(T ≤ S) ∧ (S ≤ int)`. That is, `S`
must specialize to some subtype of `int`, and `T` must specialize to a
subtype of the type that `S` specializes to.

This PR updates the new "constraint implication" relationship from
#21010 to work on these kinds of constraint sets. For instance, in the
example above, we should be able to see that `T ≤ int` must always hold:

```py
def f[S, T]():
    constraints = ConstraintSet.range(Never, S, int) & ConstraintSet.range(Never, T, S)
    static_assert(constraints.implies_subtype_of(T, int))  # now succeeds!
```

This did not require major changes to the implementation of
`implies_subtype_of`. That method already relies on how our `simplify`
and `domain` methods expand a constraint set to include the transitive
closure of the constraints that it mentions, and to mark certain
combinations of constraints as impossible. Previously, that transitive
closure logic only looked at pairs of constraints that constrain the
same typevar. (For instance, to notice that `(T ≤ bool) ∧ ¬(T ≤ int)` is
impossible.)

Now we also look at pairs of constraints that constraint different
typevars, if one of the constraints is bound by the other — that is,
pairs of the form `T ≤ S` and `S ≤ something`, or `S ≤ T` and `something
≤ S`. In those cases, transitivity lets us add a new derived constraint
that `T ≤ something` or `something ≤ T`, respectively. Having done that,
our existing `implies_subtype_of` logic finds and takes into account
that derived constraint.
2025-10-30 16:11:04 -04:00
Brent Westbrook
1c7ea690a8 [flake8-type-checking] Fix TC003 false positive with future-annotations (#21125)
Summary
--

Fixes #21121 by upgrading `RuntimeEvaluated` annotations like
`dataclasses.KW_ONLY` to `RuntimeRequired`. We already had special
handling for
`TypingOnly` annotations in this context but not `RuntimeEvaluated`.
Combining
that with the `future-annotations` setting, which allowed ignoring the
`RuntimeEvaluated` flag, led to the reported bug where we would try to
move
`KW_ONLY` into a `TYPE_CHECKING` block.

Test Plan
--

A new test based on the issue
2025-10-30 14:14:29 -04:00
Alex Waygood
9bacd19c5a [ty] Fix lookup of __new__ on instances (#21147)
## Summary

We weren't correctly modeling it as a `staticmethod` in all cases,
leading us to incorrectly infer that the `cls` argument would be bound
if it was accessed on an instance (rather than the class object).

## Test Plan

Added mdtests that fail on `main`. The primer output also looks good!
2025-10-30 13:42:46 -04:00
Brent Westbrook
f0fe6d62fb Fix syntax error false positive on nested alternative patterns (#21104)
## Summary

Fixes #21101 by storing the child visitor's names in the parent visitor.
This makes sure that `visitor.names` on line 1818 isn't empty after we
visit a nested OR pattern.

## Test Plan

New inline test cases derived from the issue,
[playground](https://play.ruff.rs/7b6439ac-ee8f-4593-9a3e-c2aa34a595d0)
2025-10-30 13:40:03 -04:00
Prakhar Pratyush
10bda3df00 [pyupgrade] Fix false positive for TypeVar with default on Python <3.13 (UP046,UP047) (#21045)
## Summary

Type default for Type parameter was added in Python 3.13 (PEP 696).

`typing_extensions.TypeVar` backports the default argument to earlier
versions.

`UP046` & `UP047` were getting triggered when
`typing_extensions.TypeVar` with `default` argument was used on python
version < 3.13

It shouldn't be triggered for python version < 3.13

This commit fixes the bug by adding a python version check before
triggering them.

Fixes #20929.

## Test Plan

### Manual testing 1

As the issue author pointed out in
https://github.com/astral-sh/ruff/issues/20929#issuecomment-3413194511,
ran the following on `main` branch:
> % cargo run -p ruff -- check ../efax/ --target-version py312
--no-cache

<details><summary>Output</summary>

```zsh
   Compiling ruff_linter v0.14.1 (/Users/prakhar/ruff/crates/ruff_linter)
   Compiling ruff v0.14.1 (/Users/prakhar/ruff/crates/ruff)
   Compiling ruff_graph v0.1.0 (/Users/prakhar/ruff/crates/ruff_graph)
   Compiling ruff_workspace v0.0.0 (/Users/prakhar/ruff/crates/ruff_workspace)
   Compiling ruff_server v0.2.2 (/Users/prakhar/ruff/crates/ruff_server)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 6.72s
     Running `target/debug/ruff check ../efax/ --target-version py312 --no-cache`
UP046 Generic class `ExpectationParametrization` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/efax/_src/expectation_parametrization.py:17:48
   |
17 | class ExpectationParametrization(Distribution, Generic[NP]):
   |                                                ^^^^^^^^^^^
18 |     """The expectation parametrization of an exponential family distribution.
   |
help: Use type parameters

UP046 Generic class `ExpToNat` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/efax/_src/mixins/exp_to_nat/exp_to_nat.py:27:68
   |
26 | @dataclass
27 | class ExpToNat(ExpectationParametrization[NP], SimpleDistribution, Generic[NP]):
   |                                                                    ^^^^^^^^^^^
28 |     """This mixin implements the conversion from expectation to natural parameters.
   |
help: Use type parameters

UP046 Generic class `HasEntropyEP` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/efax/_src/mixins/has_entropy.py:25:20
   |
23 |                    HasEntropy,
24 |                    JaxAbstractClass,
25 |                    Generic[NP]):
   |                    ^^^^^^^^^^^
26 |     @abstract_jit
27 |     @abstractmethod
   |
help: Use type parameters

UP046 Generic class `HasEntropyNP` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/efax/_src/mixins/has_entropy.py:64:20
   |
62 | class HasEntropyNP(NaturalParametrization[EP],
63 |                    HasEntropy,
64 |                    Generic[EP]):
   |                    ^^^^^^^^^^^
65 |     @jit
66 |     @final
   |
help: Use type parameters

UP046 Generic class `NaturalParametrization` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/efax/_src/natural_parametrization.py:43:30
   |
41 | class NaturalParametrization(Distribution,
42 |                              JaxAbstractClass,
43 |                              Generic[EP, Domain]):
   |                              ^^^^^^^^^^^^^^^^^^^
44 |     """The natural parametrization of an exponential family distribution.
   |
help: Use type parameters

UP046 Generic class `Structure` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/efax/_src/structure/structure.py:31:17
   |
30 | @dataclass
31 | class Structure(Generic[P]):
   |                 ^^^^^^^^^^
32 |     """This class generalizes the notion of type for Distribution objects.
   |
help: Use type parameters

UP046 Generic class `DistributionInfo` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/efax/tests/distribution_info.py:20:24
   |
20 | class DistributionInfo(Generic[NP, EP, Domain]):
   |                        ^^^^^^^^^^^^^^^^^^^^^^^
21 |     def __init__(self, dimensions: int = 1, safety: float = 0.0) -> None:
22 |         super().__init__()
   |
help: Use type parameters

Found 7 errors.
No fixes available (7 hidden fixes can be enabled with the `--unsafe-fixes` option).
```
</details> 

Running it after the changes:
```zsh
ruff % cargo run -p ruff -- check ../efax/ --target-version py312 --no-cache
   Compiling ruff_linter v0.14.1 (/Users/prakhar/ruff/crates/ruff_linter)
   Compiling ruff v0.14.1 (/Users/prakhar/ruff/crates/ruff)
   Compiling ruff_graph v0.1.0 (/Users/prakhar/ruff/crates/ruff_graph)
   Compiling ruff_workspace v0.0.0 (/Users/prakhar/ruff/crates/ruff_workspace)
   Compiling ruff_server v0.2.2 (/Users/prakhar/ruff/crates/ruff_server)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.86s
     Running `target/debug/ruff check ../efax/ --target-version py312 --no-cache`
All checks passed!
```

---

### Manual testing 2

Ran the check on the following script (mainly to verify `UP047`):
```py
from __future__ import annotations                                                                                                                                                    

from typing import Generic

from typing_extensions import TypeVar

T = TypeVar("T", default=int)


def generic_function(var: T) -> T:
    return var


Q = TypeVar("Q", default=str)


class GenericClass(Generic[Q]):
    var: Q
```

On `main` branch:
> ruff % cargo run -p ruff -- check ~/up046.py --target-version py312
--preview --no-cache

<details><summary>Output</summary>

```zsh
   Compiling ruff_linter v0.14.1 (/Users/prakhar/ruff/crates/ruff_linter)
   Compiling ruff v0.14.1 (/Users/prakhar/ruff/crates/ruff)
   Compiling ruff_graph v0.1.0 (/Users/prakhar/ruff/crates/ruff_graph)
   Compiling ruff_workspace v0.0.0 (/Users/prakhar/ruff/crates/ruff_workspace)
   Compiling ruff_server v0.2.2 (/Users/prakhar/ruff/crates/ruff_server)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.43s
     Running `target/debug/ruff check /Users/prakhar/up046.py --target-version py312 --preview --no-cache`
UP047 Generic function `generic_function` should use type parameters
  --> /Users/prakhar/up046.py:10:5
   |
10 | def generic_function(var: T) -> T:
   |     ^^^^^^^^^^^^^^^^^^^^^^^^
11 |     return var
   |
help: Use type parameters

UP046 Generic class `GenericClass` uses `Generic` subclass instead of type parameters
  --> /Users/prakhar/up046.py:17:20
   |
17 | class GenericClass(Generic[Q]):
   |                    ^^^^^^^^^^
18 |     var: Q
   |
help: Use type parameters

Found 2 errors.
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
```

</details> 

After the fix (this branch):
```zsh
ruff % cargo run -p ruff -- check ~/up046.py --target-version py312 --preview --no-cache
   Compiling ruff_linter v0.14.1 (/Users/prakhar/ruff/crates/ruff_linter)
   Compiling ruff v0.14.1 (/Users/prakhar/ruff/crates/ruff)
   Compiling ruff_graph v0.1.0 (/Users/prakhar/ruff/crates/ruff_graph)
   Compiling ruff_workspace v0.0.0 (/Users/prakhar/ruff/crates/ruff_workspace)
   Compiling ruff_server v0.2.2 (/Users/prakhar/ruff/crates/ruff_server)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.40s
     Running `target/debug/ruff check /Users/prakhar/up046.py --target-version py312 --preview --no-cache`
All checks passed!
```

Signed-off-by: Prakhar Pratyush <prakhar1144@gmail.com>
2025-10-30 12:59:07 -04:00
David Peter
e55bc943e5 [ty] Reachability and narrowing for enum methods (#21130)
## Summary

Adds proper type narrowing and reachability analysis for matching on
non-inferable type variables bound to enums. For example:

```py
from enum import Enum

class Answer(Enum):
    NO = 0
    YES = 1

    def is_yes(self) -> bool:  # no error here!
        match self:
            case Answer.YES:
                return True
            case Answer.NO:
                return False
```

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

## Test Plan

Added regression tests
2025-10-30 15:38:57 +01:00
David Peter
1b0ee4677e [ty] Use range instead of custom IntIterable (#21138)
## Summary

We previously didn't understand `range` and wrote these custom
`IntIterable`/`IntIterator` classes for tests. We can now remove them
and make the tests shorter in some places.
2025-10-30 15:21:55 +01:00
Dan Parizher
1ebedf6df5 [ruff] Add support for additional eager conversion patterns (RUF065) (#20657)
## Summary

Fixes #20583
2025-10-29 21:45:08 +00:00
Dan Parizher
980b4c55b2 [ruff-ecosystem] Fix CLI crash on Python 3.14 (#21092) 2025-10-29 21:37:39 +00:00
David Peter
5139f76d1f [ty] Infer type of self for decorated methods and properties (#21123)
## Summary

Infer a type of unannotated `self` parameters in decorated methods /
properties.

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

## Test Plan

Existing tests, some new tests.
2025-10-29 21:22:38 +00:00
Jonas Vacek
aca8ba76a4 [flake8-bandit] Fix correct example for S308 (#21128)
<!--
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
Fixed the incorrect import example in the "correct exmaple"
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
🤷 
<!-- How was it tested? -->
2025-10-29 15:03:56 -04:00
Matthew Mckee
7045898ffa [ty] Dont provide goto definition for definitions which are not reexported in builtins (#21127) 2025-10-29 18:39:36 +00:00
Wei Lee
d38a5292d2 [airflow] warning airflow....DAG.create_dagrun has been removed (AIR301) (#21093) 2025-10-29 14:57:37 +00:00
Shunsuke Shibayama
83a00c0ac8 [ty] follow the breaking API changes made in salsa-rs/salsa#1015 (#21117) 2025-10-29 14:56:12 +00:00
Alex Waygood
8b22fd1a5f [ty] Rename Type::into_nominal_instance (#21124) 2025-10-29 10:18:33 -04:00
Andrew Gallant
765257bdce [ty] Filter out "unimported" from the current module
Note that this doesn't change the evaluation results unfortunately.
In particular, prior to this fix, the correct result was ranked above
the redundant result. Our MRR-based evaluation doesn't care about
anything below the rank of the correct answer, and so this change isn't
reflected in our evaluation.

Fixes astral-sh/ty#1445
2025-10-29 09:13:49 -04:00
Andrew Gallant
2d4e0edee4 [ty] Add evaluation test for auto-import including symbols in current module
This shouldn't happen. And indeed, currently, this results in a
sub-optimal ranking.
2025-10-29 09:13:49 -04:00
Andrew Gallant
9ce3fa3fe3 [ty] Refactor ty_ide completion tests
The status quo grew organically and didn't do well when one wanted to
mix and match different settings to generate a snapshot.

This does a small refactor to use more of a builder to generate
snapshots.
2025-10-29 09:13:49 -04:00
Andrew Gallant
196a68e4c8 [ty] Render import <...> in completions when "label details" isn't supported
This fixes a bug where the `import module` part of a completion for
unimported candidates would be missing. This makes it especially
confusing because the user can't tell where the symbol is coming from,
and there is no hint that an `import` statement will be inserted.

Previously, we were using [`CompletionItemLabelDetails`] to render the
`import module` part of the suggestion. But this is only supported in
clients that support version 3.17 (or newer) of the LSP specification.
It turns out that this support isn't widespread yet. In particular,
Heliex doesn't seem to support "label details."

To fix this, we take a [cue from rust-analyzer][rust-analyzer-details].
We detect if the client supports "label details," and if so, use it.
Otherwise, we push the `import module` text into the completion label
itself.

Fixes https://github.com/astral-sh/ruff/pull/20439#issuecomment-3313689568

[`CompletionItemLabelDetails`]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemLabelDetails
[rust-analyzer-details]: 5d905576d4/crates/rust-analyzer/src/lsp/to_proto.rs (L391-L404)
2025-10-29 08:50:41 -04:00
Dan Parizher
349061117c [refurb] Preserve digit separators in Decimal constructor (FURB157) (#20588)
## Summary

Fixes #20572

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-28 17:47:52 -04:00
Takayuki Maeda
d0aebaa253 [ISC001] fix panic when string literals are unclosed (#21034)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-28 19:14:58 +00:00
Douglas Creager
17850eee4b [ty] Reformat constraint set mdtests (#21111)
This PR updates the mdtests that test how our generics solver interacts
with our new constraint set implementation. Because the rendering of a
constraint set can get long, this standardizes on putting the `revealed`
assertion on a separate line. We also add a `static_assert` test for
each constraint set to verify that they are all coerced into simple
`bool`s correctly.

This is a pure reformatting (not even a refactoring!) that changes no
behavior. I've pulled it out of #20093 to reduce the amount of effort
that will be required to review that PR.
2025-10-28 14:59:49 -04:00
Douglas Creager
4d2ee41e24 [ty] Move constraint set mdtest functions into ConstraintSet class (#21108)
We have several functions in `ty_extensions` for testing our constraint
set implementation. This PR refactors those functions so that they are
all methods of the `ConstraintSet` class, rather than being standalone
top-level functions. 🎩 to @sharkdp for pointing out that
`KnownBoundMethod` gives us what we need to implement that!
2025-10-28 14:32:41 -04:00
Takayuki Maeda
7b959ef44b Avoid sending an unnecessary "clear diagnostics" message for clients supporting pull diagnostics (#21105) 2025-10-28 18:24:35 +00:00
renovate[bot]
4c4ddc8c29 Update Rust crate ignore to v0.4.24 (#20979)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-28 17:49:26 +00:00
Micha Reiser
ae0343f848 [ty] Rename inner query for better debugging experience (#21106) 2025-10-28 11:26:05 +00:00
Douglas Creager
29462ea1d4 [ty] Add new "constraint implication" typing relation (#21010)
This PR adds the new **_constraint implication_** relationship between
types, aka `is_subtype_of_given`, which tests whether one type is a
subtype of another _assuming that the constraints in a particular
constraint set hold_.

For concrete types, constraint implication is exactly the same as
subtyping. (A concrete type is any fully static type that is not a
typevar. It can _contain_ a typevar, though — `list[T]` is considered
concrete.)

The interesting case is typevars. The other typing relationships (TODO:
will) all "punt" on the question when considering a typevar, by
translating the desired relationship into a constraint set. At some
point, though, we need to resolve a constraint set; at that point, we
can no longer punt on the question. Unlike with concrete types, the
answer will depend on the constraint set that we are considering.
2025-10-27 22:01:08 -04:00
Bhuminjay Soni
7fee62b2de [semantic error tests]: refactor semantic error tests to separate files (#20926)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->
This PR refactors semantic error tests in each seperate file


## Test Plan

<!-- How was it tested? -->

## CC
- @ntBre

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-27 21:18:11 +00:00
Brent Westbrook
96b60c11d9 Respect --output-format with --watch (#21097)
Summary
--

Fixes #19550

This PR copies our non-watch diagnostic rendering code into
`Printer::write_continuously` in preview mode, allowing it to use
whatever output format is passed in.

I initially marked this as also fixing #19552, but I guess that's not
true currently but will be true once this is stabilized and we can
remove the warning.

Test Plan
--

Existing tests, but I don't think we have any `watch` tests, so some
manual testing as well. The default with just `ruff check --watch` is
still `concise`, adding just `--preview` still gives the `full` output,
and then specifying any other output format works, with JSON as one
example:

<img width="695" height="719" alt="Screenshot 2025-10-27 at 9 21 41 AM"
src="https://github.com/user-attachments/assets/98957911-d216-4fc4-8b6c-22c56c963b3f"
/>
2025-10-27 12:04:55 -04:00
Dylan
fffbe5a879 [pyflakes] Revert to stable behavior if imports for module lie in alternate branches for F401 (#20878)
Closes #20839
2025-10-27 10:23:36 -05:00
Dylan
116611bd39 Fix finding keyword range for clause header after statement ending with semicolon (#21067)
When formatting clause headers for clauses that are not their own node,
like an `else` clause or `finally` clause, we begin searching for the
keyword at the end of the previous statement. However, if the previous
statement ended in a semicolon this caused a panic because we only
expected trivia between the end of the last statement and the keyword.

This PR adjusts the starting point of our search for the keyword to
begin after the optional semicolon in these cases.

Closes #21065
2025-10-27 09:52:17 -05:00
Alex Waygood
db0e921db1 [ty] Fix bug where ty would think all types had an __mro__ attribute (#20995) 2025-10-27 11:19:12 +00:00
Micha Reiser
3c7f56f582 Restore indent.py (#21094) 2025-10-27 10:34:29 +00:00
Dan Parizher
8a73519b25 [flake8-django] Apply DJ001 to annotated fields (#20907) 2025-10-27 09:19:15 +01:00
Shahar Naveh
fa12fd0184 Clearer error message when line-length goes beyond threshold (#21072)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-27 07:42:48 +00:00
renovate[bot]
fdb8ea487c Update upload and download artifacts github actions (#21083)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-27 08:29:32 +01:00
renovate[bot]
d846a0319a Update dependency mdformat-mkdocs to v4.4.2 (#21088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 07:47:46 +01:00
renovate[bot]
bca5d33385 Update cargo-bins/cargo-binstall action to v1.15.9 (#21086)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 07:47:12 +01:00
renovate[bot]
c83c4d52a4 Update Rust crate clap to v4.5.50 (#21090)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 07:46:38 +01:00
renovate[bot]
e692b7f1ee Update Rust crate get-size2 to v0.7.1 (#21091)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 07:44:02 +01:00
renovate[bot]
8e51db3ecd Update Rust crate bstr to v1.12.1 (#21089)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 07:43:39 +01:00
Auguste Lalande
64ab79e572 Add missing docstring sections to the numpy list (#20931)
<!--
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

Add docstring sections which were missing from the numpy list as pointed
out here #20923. For now these are only the official sections as
documented
[here](https://numpydoc.readthedocs.io/en/latest/format.html#sections).

## Test Plan

Added a test case for DOC102
2025-10-24 17:19:30 -04:00
Dan Parizher
1ade9a5943 [pydoclint] Fix false positive on explicit exception re-raising (DOC501, DOC502) (#21011)
## Summary
Fixes #20973 (`docstring-extraneous-exception`) false positive when
exceptions mentioned in docstrings are caught and explicitly re-raised
using `raise e` or `raise e from None`.

## Problem Analysis
The DOC502 rule was incorrectly flagging exceptions mentioned in
docstrings as "not explicitly raised" when they were actually being
explicitly re-raised through exception variables bound in `except`
clauses.

**Root Cause**: The `BodyVisitor` in `check_docstring.rs` only checked
for direct exception references (like `raise OSError()`) but didn't
recognize when a variable bound to an exception in an `except` clause
was being re-raised.

**Example of the bug**:
```python
def f():
    """Do nothing.

    Raises
    ------
    OSError
        If the OS errors.
    """
    try:
        pass
    except OSError as e:
        raise e  # This was incorrectly flagged as not explicitly raising OSError
```

The issue occurred because `resolve_qualified_name(e)` couldn't resolve
the variable `e` to a qualified exception name, since `e` is just a
variable binding, not a direct reference to an exception class.

## Approach
Modified the `BodyVisitor` in
`crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs` to:

1. **Track exception variable bindings**: Added `exception_variables`
field to map exception variable names to their exception types within
`except` clauses
2. **Enhanced raise statement detection**: Updated `visit_stmt` to check
if a `raise` statement uses a variable name that's bound to an exception
in the current `except` clause
3. **Proper scope management**: Clear exception variable mappings when
leaving `except` handlers to prevent cross-contamination

**Key changes**:
- Added `exception_variables: FxHashMap<&'a str, QualifiedName<'a>>` to
track variable-to-exception mappings
- Enhanced `visit_except_handler` to store exception variable bindings
when entering `except` clauses
- Modified `visit_stmt` to check for variable-based re-raising: `raise
e` → lookup `e` in `exception_variables`
- Clear mappings when exiting `except` handlers to maintain proper scope

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-24 16:54:09 -04:00
Ibraheem Ahmed
304ac22e74 [ty] Use constructor parameter types as type context (#21054)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1408.
2025-10-24 20:14:18 +00:00
Douglas Creager
c3de8847d5 [ty] Consider domain of BDD when checking whether always satisfiable (#21050)
That PR title might be a bit inscrutable.

Consider the two constraints `T ≤ bool` and `T ≤ int`. Since `bool ≤
int`, by transitivity `T ≤ bool` implies `T ≤ int`. (Every type that is
a subtype of `bool` is necessarily also a subtype of `int`.) That means
that `T ≤ bool ∧ T ≰ int` is an impossible combination of constraints,
and is therefore not a valid input to any BDD. We say that that
assignment is not in the _domain_ of the BDD.

The implication `T ≤ bool → T ≤ int` can be rewritten as `T ≰ bool ∨ T ≤
int`. (That's the definition of implication.) If we construct that
constraint set in an mdtest, we should get a constraint set that is
always satisfiable. Previously, that constraint set would correctly
_display_ as `always`, but a `static_assert` on it would fail.

The underlying cause is that our `is_always_satisfied` method would only
test if the BDD was the `AlwaysTrue` terminal node. `T ≰ bool ∨ T ≤ int`
does not simplify that far, because we purposefully keep around those
constraints in the BDD structure so that it's easier to compare against
other BDDs that reference those constraints.

To fix this, we need a more nuanced definition of "always satisfied".
Instead of evaluating to `true` for _every_ input, we only need it to
evaluate to `true` for every _valid_ input — that is, every input in its
domain.
2025-10-24 13:37:56 -04:00
Ibraheem Ahmed
f17ddd62ad [ty] Avoid duplicate diagnostics during multi-inference of standalone expressions (#21056)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1428.
2025-10-24 13:21:39 -04:00
Micha Reiser
adbf05802a [ty] Fix rare panic with highly cyclic TypeVar definitions (#21059) 2025-10-24 18:30:54 +02:00
Micha Reiser
eb8c0ad87c [ty] Add --no-progress option (#21063) 2025-10-24 18:00:00 +02:00
Shahar Naveh
a2d0d39853 Configurable "unparse mode" for ruff_python_codegen::Generator (#21041)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-24 15:44:48 +00:00
Micha Reiser
f36fa7d6c1 [ty] Fix missing newline before first diagnostic (#21058) 2025-10-24 17:35:23 +02:00
William Woodruff
6f0982d2d6 chore: bump zizmor (#21064) 2025-10-24 10:58:23 -04:00
Dan Parizher
3e8685d2ec [pyflakes] Fix false positive for __class__ in lambda expressions within class definitions (F821) (#20564)
## Summary

Fixes #20562

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-24 10:07:19 -04:00
Dan Parizher
7576669297 [flake8-pyi] Fix PYI034 to not trigger on metaclasses (PYI034) (#20881)
## Summary

Fixes #20781

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-24 13:40:26 +00:00
Alex Waygood
bf74c824eb [ty] Delegate truthiness inference of an enum Literal type to its enum-instance supertype (#21060) 2025-10-24 14:34:16 +01:00
Alex Waygood
e196c2ab37 [ty] Consider __len__ when determining the truthiness of an instance of a tuple class or a @final class (#21049) 2025-10-24 09:29:55 +00:00
Micha Reiser
4522f35ea7 [ty] Add comment explaining why HasTrackedScope is implemented for Identifier and why it works (#21057) 2025-10-24 09:48:57 +02:00
Micha Reiser
be5a62f7e5 [ty] Timeout based workspace diagnostic progress reports (#21019) 2025-10-24 09:06:19 +02:00
wangxiaolei
28aed61a22 [pylint] Implement stop-iteration-return (PLR1708) (#20733)
## Summary

implement pylint rule stop-iteration-return / R1708

## Test Plan

<!-- How was it tested? -->

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-23 15:02:41 -07:00
Wei Lee
05cde8bd19 [airflow] Extend airflow.models..Param check (AIR311) (#21043)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

* Extend `airflow.models.Param` to include `airflow.models.param.Param`
case and include both `airflow.models.param.ParamDict` and
`airflow.models.param.DagParam` and their `airflow.models.` counter part

## Test Plan

<!-- How was it tested? -->

update the text fixture accordingly and reorganize them in the third
commit
2025-10-23 17:12:52 -04:00
Brent Westbrook
83a3bc4ee9 Bump 0.14.2 (#21051) 2025-10-23 15:17:22 -04:00
Brent Westbrook
155fd603e8 Document when a rule was added (#21035)
Summary
--

Inspired by #20859, this PR adds the version a rule was added, and the
file and line where it was defined, to `ViolationMetadata`. The file and
line just use the standard `file!` and `line!` macros, while the more
interesting version field uses a new `violation_metadata` attribute
parsed by our `ViolationMetadata` derive macro.

I moved the commit modifying all of the rule files to the end, so it
should be a lot easier to review by omitting that one.

As a curiosity and a bit of a sanity check, I also plotted the rule
numbers over time:

<img width="640" height="480" alt="image"
src="https://github.com/user-attachments/assets/75b0b5cc-3521-4d40-a395-8807e6f4925f"
/>

I think this looks pretty reasonable and avoids some of the artifacts
the earlier versions of the script ran into, such as the `rule`
sub-command not being available or `--explain` requiring a file
argument.

<details><summary>Script and summary data</summary>

```shell
gawk --csv '
NR > 1 {
    split($2, a, ".")
    major = a[1]; minor = a[2]; micro = a[3]
    # sum the number of rules added per minor version
    versions[minor] += 1
}
END {
    tot = 0
    for (i = 0; i <= 14; i++) {
        tot += versions[i]
        print i, tot
    }
}
' ruff_rules_metadata.csv > summary.dat
```

```
0 696
1 768
2 778
3 803
4 822
5 848
6 855
7 865
8 893
9 915
10 916
11 924
12 929
13 932
14 933
```

</details>

Test Plan
--

I built and viewed the documentation locally, and it looks pretty good!

<img width="1466" height="676" alt="image"
src="https://github.com/user-attachments/assets/5e227df4-7294-4d12-bdaa-31cac4e9ad5c"
/>

The spacing seems a bit awkward following the `h1` at the top, so I'm
wondering if this might look nicer as a footer in Ruff. The links work
well too:
- [v0.0.271](https://github.com/astral-sh/ruff/releases/tag/v0.0.271)
- [Related
issues](https://github.com/astral-sh/ruff/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20airflow-variable-name-task-id-mismatch)
- [View
source](https://github.com/astral-sh/ruff/blob/main/crates%2Fruff_linter%2Fsrc%2Frules%2Fairflow%2Frules%2Ftask_variable_name.rs#L34)

The last one even works on `main` now since it points to the
`derive(ViolationMetadata)` line.

In terms of binary size, this branch is a bit bigger than main with
38,654,520 bytes compared to 38,635,728 (+20 KB). I guess that's not
_too_ much of an increase, but I wanted to check since we're generating
a lot more code with macros.

---------

Co-authored-by: GiGaGon <107241144+MeGaGiGaGon@users.noreply.github.com>
2025-10-23 14:48:41 -04:00
Shunsuke Shibayama
48f1771877 [ty] fix infinite recursion with generic type aliases (#20969)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-23 14:14:30 +00:00
decorator-factory
4ca74593dd [ty] Consider type_check_only when ranking completions (#20910) 2025-10-23 15:09:13 +01:00
Alex Waygood
dab3d4e917 [ty] Improve invalid-argument-type diagnostics where a union type was provided (#21044) 2025-10-23 13:16:21 +00:00
Micha Reiser
01695513ce Disable npm caching for playground (#21039) 2025-10-23 11:51:29 +02:00
Micha Reiser
e92fd51a2c [ty] Add cycle handling to lazy_default (#20967) 2025-10-23 10:05:08 +02:00
Eric Mark Martin
c3631c78bd [ty] Add docstrings for ty_extensions functions (#21036)
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-10-23 09:50:21 +02:00
David Peter
589e8ac0d9 [ty] Infer type for implicit self parameters in method bodies (#20922)
## Summary

Infer a type of `Self` for unannotated `self` parameters in methods of
classes.

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

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

## Conformance tests changes

```diff
+enums_member_values.py:85:9: error[invalid-assignment] Object of type `int` is not assignable to attribute `_value_` of type `str`
```
A true positive ✔️ 

```diff
-generics_self_advanced.py:35:9: error[type-assertion-failure] Argument does not have asserted type `Self@method2`
-generics_self_basic.py:14:9: error[type-assertion-failure] Argument does not have asserted type `Self@set_scale
```

Two false positives going away ✔️ 

```diff
+generics_syntax_infer_variance.py:82:9: error[invalid-assignment] Cannot assign to final attribute `x` on type `Self@__init__`
```

This looks like a true positive to me, even if it's not marked with `#
E` ✔️

```diff
+protocols_explicit.py:56:9: error[invalid-assignment] Object of type `tuple[int, int, str]` is not assignable to attribute `rgb` of type `tuple[int, int, int]`
```

True positive ✔️ 

```
+protocols_explicit.py:85:9: error[invalid-attribute-access] Cannot assign to ClassVar `cm1` from an instance of type `Self@__init__`
```

This looks like a true positive to me, even if it's not marked with `#
E`. But this is consistent with our understanding of `ClassVar`, I
think. ✔️

```py
+qualifiers_final_annotation.py:52:9: error[invalid-assignment] Cannot assign to final attribute `ID4` on type `Self@__init__`
+qualifiers_final_annotation.py:65:9: error[invalid-assignment] Cannot assign to final attribute `ID7` on type `Self@method1`
```

New true positives ✔️ 

```py
+qualifiers_final_annotation.py:52:9: error[invalid-assignment] Cannot assign to final attribute `ID4` on type `Self@__init__`
+qualifiers_final_annotation.py:57:13: error[invalid-assignment] Cannot assign to final attribute `ID6` on type `Self@__init__`
+qualifiers_final_annotation.py:59:13: error[invalid-assignment] Cannot assign to final attribute `ID6` on type `Self@__init__`
```

This is a new false positive, but that's a pre-existing issue on main
(if you annotate with `Self`):
https://play.ty.dev/3ee1c56d-7e13-43bb-811a-7a81e236e6ab  => reported
as https://github.com/astral-sh/ty/issues/1409

## Ecosystem

* There are 5931 new `unresolved-attribute` and 3292 new
`possibly-missing-attribute` attribute errors, way too many to look at
all of them. I randomly sampled 15 of these errors and found:
* 13 instances where there was simply no such attribute that we could
plausibly see. Sometimes [I didn't find it
anywhere](8644d886c6/openlibrary/plugins/openlibrary/tests/test_listapi.py (L33)).
Sometimes it was set externally on the object. Sometimes there was some
[`setattr` dynamicness going
on](a49f6b927d/setuptools/wheel.py (L88-L94)).
I would consider all of them to be true positives.
* 1 instance where [attribute was set on `obj` in
`__new__`](9e87b44fd4/sympy/tensor/array/array_comprehension.py (L45C1-L45C36)),
which we don't support yet
  * 1 instance [where the attribute was defined via `__slots__`

](e250ec0fc8/lib/spack/spack/vendor/pyrsistent/_pdeque.py (L48C5-L48C14))
* I see 44 instances [of the false positive
above](https://github.com/astral-sh/ty/issues/1409) with `Final`
instance attributes being set in `__init__`. I don't think this should
block this PR.

## Test Plan

New Markdown tests.

---------

Co-authored-by: Shaygan Hooshyari <sh.hooshyari@gmail.com>
2025-10-23 09:34:39 +02:00
Micha Reiser
76a55314e4 Fix rare multithreaded related hang (#21038) 2025-10-23 09:25:16 +02:00
Takayuki Maeda
6c18f18450 [ruff] Fix UP032 conversion for decimal ints with underscores (#21022)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #21017

Taught UP032’s parenthesize check to ignore underscores when inspecting
decimal integer literals so the converter emits `f"{(1_2).real}"`
instead of invalid syntax.

## Test Plan

Added test cases to UP032_2.py.

<!-- How was it tested? -->
2025-10-22 18:11:50 -04:00
William Woodruff
7ba176d395 ci: adjust zizmor config, bump dist (#20999)
## Summary

Also bumps `cargo dist` to 0.30, and moves us
back to the upstream copy of `dist` now that
the latest version has integrated our fork's
patches.

## Test Plan

See what happens in CI 🙂

---------

Signed-off-by: William Woodruff <william@astral.sh>
2025-10-22 17:48:17 -04:00
Douglas Creager
766ed5b5f3 [ty] Some more simplifications when rendering constraint sets (#21009)
This PR adds another useful simplification when rendering constraint
sets: `T = int` instead of `T = int ∧ T ≠ str`. (The "smaller"
constraint `T = int` implies the "larger" constraint `T ≠ str`.
Constraint set clauses are intersections, and if one constraint in a
clause implies another, we can throw away the "larger" constraint.)

While we're here, we also normalize the bounds of a constraint, so that
we equate e.g. `T ≤ int | str` with `T ≤ str | int`, and change the
ordering of BDD variables so that all constraints with the same typevar
are ordered adjacent to each other.

Lastly, we also add a new `display_graph` helper method that prints out
the full graph structure of a BDD.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-22 13:38:44 -04:00
David Peter
81c1d36088 [ty] Make attributes.md mdtests faster (#21030)
## Summary

That example was too extreme for debug mode.
2025-10-22 16:40:58 +02:00
Alex Waygood
20510e1d71 [ty] Set INSTA_FORCE_PASS and INSTA_OUTPUT environment variables from mdtest.py (#21029) 2025-10-22 15:32:14 +01:00
David Peter
58a68f1bbd [ty] Fall back to Divergent for deeply nested specializations (#20988)
## Summary

Fall back to `C[Divergent]` if we are trying to specialize `C[T]` with a
type that itself already contains deeply nested specialized generic
classes. This is a way to prevent infinite recursion for cases like
`self.x = [self.x]` where type inference for the implicit instance
attribute would not converge.

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

## Test Plan

Regression tests.
2025-10-22 14:29:10 +02:00
Takayuki Maeda
2c9433796a [ruff] Autogenerate TypeParam nodes (#21028) 2025-10-22 14:06:24 +02:00
Alex Waygood
40148d7b11 [ty] Add assertions to ensure that we never call KnownClass::Tuple.to_instance() or similar (#21027) 2025-10-22 11:07:01 +00:00
Takayuki Maeda
6271fba1e1 [ruff] Auto generate ast Pattern nodes (#21024) 2025-10-22 08:24:34 +02:00
Takayuki Maeda
a51a0f16e4 [flake8-simplify] Skip SIM911 when unknown arguments are present (#20697)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #18778

Prevent SIM911 from triggering when zip() is called on .keys()/.values()
that take any positional or keyword arguments, so Ruff
never suggests the lossy rewrite.

## Test Plan

<!-- How was it tested? -->

Added a test case to SIM911.py.
2025-10-21 16:58:48 -04:00
Brent Westbrook
4b0fa5f270 Render a diagnostic for syntax errors introduced in formatter tests (#21021)
## Summary

I spun this out from #21005 because I thought it might be helpful
separately. It just renders a nice `Diagnostic` for syntax errors
pointing to the source of the error. This seemed a bit more helpful to
me than just the byte offset when working on #21005, and we had most of
the code around after #20443 anyway.

## Test Plan

This doesn't actually affect any passing tests, but here's an example of
the additional output I got when I broke the spacing after the `in`
token:

```
    error[internal-error]: Expected 'in', found name
      --> /home/brent/astral/ruff/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py:50:79
       |
    48 |     need_more_to_make_the_line_long_enough,
    49 | )
    50 | del ([], name_1, name_2), [(), [], name_4, name_3], name_1[[name_2 for name_1 inname_0]]
       |                                                                               ^^^^^^^^
    51 | del ()
       |
```

I just appended this to the other existing output for now.
2025-10-21 13:47:26 -04:00
Aria Desires
2e13b13012 [ty] Support goto-definition on vendored typeshed stubs (#21020)
This is an alternative to #21012 that more narrowly handles this logic
in the stub-mapping machinery rather than pervasively allowing us to
identify cached files as typeshed stubs. Much of the logic is the same
(pulling the logic out of ty_server so it can be reused).

I don't have a good sense for if one approach is "better" or "worse" in
terms of like, semantics and Weird Bugs that this can cause. This one is
just "less spooky in its broad consequences" and "less muddying of
separation of concerns" and puts the extra logic on a much colder path.
I won't be surprised if one day the previous implementation needs to be
revisited for its more sweeping effects but for now this is good.

Fixes https://github.com/astral-sh/ty/issues/1054
2025-10-21 13:38:40 -04:00
Micha Reiser
9d1ffd605c [ty] Implement go-to for binary and unary operators (#21001)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-21 19:25:41 +02:00
David Peter
2dbca6370b [ty] Avoid ever-growing default types (#20991)
## Summary

We currently panic in the seemingly rare case where the type of a
default value of a parameter depends on the callable itself:
```py
class C:
    def f(self: C):
        self.x = lambda a=self.x: a
```

Types of default values are only used for display reasons, and it's
unclear if we even want to track them (or if we should rather track the
actual value). So it didn't seem to me that we should spend a lot of
effort (and runtime) trying to achieve a theoretically correct type here
(which would be infinite).

Instead, we simply replace *nested* default types with `Unknown`, i.e.
only if the type of the default value is a callable itself.

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

## Test Plan

Regression tests
2025-10-21 19:13:36 +02:00
Bhuminjay Soni
3dd78e711e [syntax-errors] Name is parameter and global (#20426)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR implements a new semantic syntax error where name is parameter &
global.

## Test Plan

<!-- How was it tested? -->
I have written inline test as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-21 16:51:16 +00:00
David Peter
e1cada1ec3 [ty] Disable panicking mdtest (#21016)
## Summary

Only run the "pull types" test after performing the "actual" mdtest. We
observed that the order matters. There is currently one mdtest which
panics when checked in the CLI or the playground. With this change, it
also panics in the mdtest suite.

reopens https://github.com/astral-sh/ty/issues/837?
2025-10-21 14:40:23 +02:00
Micha Reiser
69ce064569 [ty] Fix completions at end of file (#20993) 2025-10-21 09:24:31 +00:00
Micha Reiser
523fc418ed [ty] Fix out-of-order semantic token for function with regular argument after kwargs (#21013) 2025-10-21 10:51:09 +02:00
Micha Reiser
24d0f65d62 [ty] Fix auto import for files with from __future__ import (#20987) 2025-10-21 08:14:39 +02:00
Hengky Kurniawan
a802d7a0ea [fastapi] Handle ellipsis defaults in FAST002 autofix (FAST002) (#20810)
## Summary

Implement handling of ellipsis (`...`) defaults in the `FAST002` autofix
to correctly differentiate between required and optional parameters in
FastAPI route definitions.

Previously, the autofix did not properly handle cases where parameters
used `...` as a default value (to indicate required parameters). This
could lead to incorrect transformations when applying the autofix.

This change updates the `FAST002` autofix logic to:
- Correctly recognize `...` as a valid FastAPI required default.
- Preserve the semantics of required parameters while still applying
other autofix improvements.
- Avoid incorrectly substituting or removing ellipsis defaults.

Fixes https://github.com/astral-sh/ruff/issues/20800

## Test Plan

Added a new test fixture at:
```crates/ruff_linter/resources/test/fixtures/fastapi/FAST002_2.py```
2025-10-21 02:47:13 +00:00
Takayuki Maeda
692b7d7f0c [ruff] Skip autofix for keyword and __debug__ path params (#20960)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #20941

Skip autofix for keyword and __debug__ path params

## Test Plan

<!-- How was it tested? -->

I added two test cases to
crates/ruff_linter/resources/test/fixtures/fastapi/FAST003.py.
2025-10-20 16:37:20 -07:00
Dylan
d363d49ab7 [flake8-bugbear] Skip B905 and B912 if <2 iterables and no starred arguments (#20998)
Closes #20997

This will _decrease_ the number of diagnostics emitted for
[zip-without-explicit-strict
(B905)](https://docs.astral.sh/ruff/rules/zip-without-explicit-strict/#zip-without-explicit-strict-b905),
since previously it triggered on any `zip` call no matter the number of
arguments. It may _increase_ the number of diagnostics for
[map-without-explicit-strict
(B912)](https://docs.astral.sh/ruff/rules/map-without-explicit-strict/#map-without-explicit-strict-b912)
since it will now trigger on a single starred argument where before it
would not. However, the latter rule is in `preview` so this is
acceptable.

Note - we do not need to make any changes to
[batched-without-explicit-strict
(B911)](https://docs.astral.sh/ruff/rules/batched-without-explicit-strict/#batched-without-explicit-strict-b911)
since that just takes a single iterable.

I am doing this in one PR rather than two because we should keep the
behavior of these rules consistent with one another.

For review: apologies for the unreadability of the snapshot for `B905`.
Unfortunately I saw no way of keeping a small diff and a correct fixture
(the fixture labeled a whole block as `# Error` whereas now several in
the block became `# Ok`).Probably simplest to just view the actual
snapshot - it's relatively small.
2025-10-20 18:35:32 -05:00
Dylan
281ae8eb34 [pyupgrade] Always parenthesize assignment expressions in fix for f-string (UP032) (#21003)
Closes #21000
2025-10-20 18:34:02 -05:00
renovate[bot]
d61902c89f Update dependency vite to v7.0.8 (#21007)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [vite](https://vite.dev)
([source](https://redirect.github.com/vitejs/vite/tree/HEAD/packages/vite))
| [`7.0.7` ->
`7.0.8`](https://renovatebot.com/diffs/npm/vite/7.0.7/7.0.8) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/vite/7.0.8?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite/7.0.7/7.0.8?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

### GitHub Vulnerability Alerts

####
[CVE-2025-62522](https://redirect.github.com/vitejs/vite/security/advisories/GHSA-93m4-6634-74q7)

### Summary
Files denied by
[`server.fs.deny`](https://vitejs.dev/config/server-options.html#server-fs-deny)
were sent if the URL ended with `\` when the dev server is running on
Windows.

### Impact
Only apps that match the following conditions are affected:

- explicitly exposes the Vite dev server to the network (using --host or
[`server.host` config
option](https://vitejs.dev/config/server-options.html#server-host))
- running the dev server on Windows

### Details
`server.fs.deny` can contain patterns matching against files (by default
it includes `.env`, `.env.*`, `*.{crt,pem}` as such patterns). These
patterns were able to bypass by using a back slash(`\`). The root cause
is that `fs.readFile('/foo.png/')` loads `/foo.png`.

### PoC
```shell
npm create vite@latest
cd vite-project/
cat "secret" > .env
npm install
npm run dev
curl --request-target /.env\ http://localhost:5173
```
<img width="1593" height="616" alt="image"
src="https://github.com/user-attachments/assets/36212f4e-1d3c-4686-b16f-16b35ca9e175"
/>

---

### Release Notes

<details>
<summary>vitejs/vite (vite)</summary>

###
[`v7.0.8`](https://redirect.github.com/vitejs/vite/releases/tag/v7.0.8)

[Compare
Source](https://redirect.github.com/vitejs/vite/compare/v7.0.7...v7.0.8)

Please refer to
[CHANGELOG.md](https://redirect.github.com/vitejs/vite/blob/v7.0.8/packages/vite/CHANGELOG.md)
for details.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE0My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCIsInNlY3VyaXR5Il19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 16:22:20 -07:00
Robsdedude
511710e1ef [flake8-gettext] Resolve qualified names and built-in bindings (INT001, INT002, INT003) (#19045)
## Summary
Make rules `INT001`, `INT002`, and `INT003` also 
* trigger on qualified names when we're sure the calls are calls to the
`gettext` module. For example
  ```python
  from gettext import gettext as foo
  
foo(f"{'bar'}") # very certain that this is a call to a real `gettext`
function => worth linting
  ```
* trigger on `builtins` bindings
  ```python
  from builtins, gettext
  
  gettext.install("...")  # binds `gettext.gettext` to `builtins._`
  builtins.__dict__["_"] = ...  # also a common pattern

  _(f"{'bar'}")  # should therefore also be linted
  ```

Fixes: https://github.com/astral-sh/ruff/issues/19028

## Test Plan

Tests have been added to all three rules.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-20 18:24:55 -04:00
Alex Waygood
da04d92ff2 remove playground/issue.py (#21002) 2025-10-20 21:00:05 +02:00
ShalokShalom
0f21567405 Add source to testimonial (#20971)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-20 19:48:07 +02:00
Micha Reiser
be32f8acb1 [ty] Fix inconsistent highlighting of self (#20986) 2025-10-20 19:32:48 +02:00
Takayuki Maeda
1197035c02 [ty] Add capabilities check for clear_diagnostics (#20989) 2025-10-20 19:31:01 +02:00
Shunsuke Shibayama
a7c38eb122 [ty] display variance on hover over type variables (#20900) 2025-10-20 17:28:36 +00:00
Micha Reiser
0520d11a66 [ty] Fix None, True and False highlighting in playground (#20994) 2025-10-20 19:25:08 +02:00
David Peter
54cd9d889d [ty] Refactor: Use let-chains in a few places (#20985) 2025-10-20 18:01:37 +01:00
Alex Waygood
44e678a222 Add language: python to several pre-commit hook entries (#20992) 2025-10-20 17:32:02 +01:00
Brent Westbrook
38c074e67d Catch syntax errors in nested interpolations before Python 3.12 (#20949)
Summary
--

This PR fixes the issue I added in #20867 and noticed in #20930. Cases
like this
cause an error on any Python version:

```py
f"{1:""}"
```

which gave me a false sense of security before. Cases like this are
still
invalid only before 3.12 and weren't flagged after the changes in
#20867:

```py
f'{1: abcd "{'aa'}" }'
           # ^  reused quote
f'{1: abcd "{"\n"}" }'
            # ^  backslash
```

I didn't recognize these as nested interpolations that also need to be
checked
for invalid expressions, so filtering out the whole format spec wasn't
quite
right. And `elements.interpolations()` only iterates over the outermost 
interpolations, not the nested ones.

There's basically no code change in this PR, I just moved the existing
check
from `parse_interpolated_string`, which parses the entire string, to
`parse_interpolated_element`. This kind of seems more natural anyway and
avoids
having to try to recursively visit nested elements after the fact in
`parse_interpolated_string`. So viewing the diff with something like

```
git diff --color-moved --ignore-space-change --color-moved-ws=allow-indentation-change main
```

should make this more clear.

Test Plan
--

New tests
2025-10-20 09:03:13 -04:00
Loïc Riegel
c2ae9c7806 feat: 'ruff rule' provides more easily parsable JSON ouput (#20168) 2025-10-20 09:09:51 +02:00
renovate[bot]
5a1201b868 Update Rust crate getrandom to v0.3.4 (#20977)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-20 07:00:08 +00:00
Takayuki Maeda
48b50128eb [ruff] Update schemars to v1 (#20942) 2025-10-20 08:59:52 +02:00
renovate[bot]
991e8ed178 Update Rust crate serde to v1.0.228 (#20982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 08:53:07 +02:00
renovate[bot]
0fc5520404 Update Rust crate regex-automata to v0.4.13 (#20981)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 08:45:17 +02:00
renovate[bot]
b82404f706 Update Rust crate globset to v0.4.17 (#20978)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 08:35:32 +02:00
renovate[bot]
4622b8f849 Update Rust crate quote to v1.0.41 (#20980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 08:33:26 +02:00
renovate[bot]
bb3a88cdd5 Update Rust crate clap to v4.5.49 (#20976)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 08:33:09 +02:00
renovate[bot]
029a7aa200 Update Rust crate snapbox to v0.6.22 (#20983)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 08:32:48 +02:00
Alex Waygood
590ce9d9ed Use uv more in CI workflows (#20968)
## Summary

More dogfooding of our own tools.

I didn't touch the build-binaries workflow (it's scary) or the
publish-docs workflow (which doesn't run on PRs) or the ruff-lsp job in
the ci.yaml workflow (ruff-lsp is deprecated; it doesn't seem worth
making changes there).

## Test Plan

CI on this PR
2025-10-19 14:59:10 +01:00
Renkai Ge
0b8de723c6 Refactor format tests to use CliTest helper (#20953)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-19 10:25:21 +00:00
Shunsuke Shibayama
36d4b02fa9 [ty] fix non-deterministic overload function inference (#20966) 2025-10-19 12:13:10 +02:00
Alex Waygood
1f8297cfe6 [ty] Improve error messages for unresolved attribute diagnostics (#20963)
## Summary

- Type checkers (and type-checker authors) think in terms of types, but
I think most Python users think in terms of values. Rather than saying
that a _type_ `X` "has no attribute `foo`" (which I think sounds strange
to many users), say that "an object of type `X` has no attribute `foo`"
- Special-case certain types so that the diagnostic messages read more
like normal English: rather than saying "Type `<class 'Foo'>` has no
attribute `bar`" or "Object of type `<class 'Foo'>` has no attribute
`bar`", just say "Class `Foo` has no attribute `bar`"

## Test Plan

Mdtests and snapshots updated
2025-10-19 10:58:25 +01:00
Takayuki Maeda
b6b96d75eb [ruff] Use DiagnosticTag for more pyflakes and panda rules (#20801) 2025-10-19 11:07:16 +02:00
David Peter
3c229ae58a [ty] dataclass_transform: Support for fields with an alias (#20961)
## Summary

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

## Conformance tests

Two false positives removed, as expected.

## Test Plan

New Markdown tests
2025-10-18 18:20:39 +00:00
Alex Waygood
44d9063058 Reduce duplicated logic between the macOS and Windows CI jobs (#20958) 2025-10-18 18:00:55 +01:00
Alex Waygood
09306a1d69 Run py-fuzzer using Python 3.14 in CI (#20957) 2025-10-18 15:42:39 +01:00
Alex Waygood
68c1fa86c8 [ty] Fix panic when attempting to validate the members of a protocol that inherits from a protocol in another module (#20956) 2025-10-18 15:01:46 +01:00
Alex Waygood
16efe53a72 [ty] Fix panic on recursive class definitions in a stub that use constrained type variables (#20955) 2025-10-18 13:02:55 +00:00
Micha Reiser
7532155c9b [ty] Add suggestion to unknown rule diagnostics, rename unknown-rule lint to ignore-comment-unknown-rule (#20948) 2025-10-18 12:44:21 +02:00
Bhuminjay Soni
7198e53182 [syntax-errors] Alternative match patterns bind different names (#20682)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->
This PR implements semantic syntax error where alternative patterns bind
different names

## Test Plan

<!-- How was it tested? -->
I have written inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-17 21:35:48 +00:00
Alex Waygood
8ca2b5555d Dogfood ty on py-fuzzer in CI (#20946) 2025-10-17 20:30:17 +01:00
David Peter
6d2cf3475f Only add the actual schema in schemastore PRs (#20947)
Same as https://github.com/astral-sh/ty/pull/1391:

> Last time I ran this script, due to what I assume was a `npm` version
mismatch, the `package-lock.json` file was updated while running `npm
install` in the `schemastore`. Due to the use of `git commit -a`, it was
accidentally included in the commit for the semi-automated schemastore
PR. The solution here is to only add the actual file that we want to
commit.
2025-10-17 21:14:04 +02:00
Shunsuke Shibayama
e4384fc212 [ty] impl VarianceInferable for KnownInstanceType (#20924)
## Summary

Derived from #20900

Implement `VarianceInferable` for `KnownInstanceType` (especially for
`KnownInstanceType::TypeAliasType`).

The variance of a type alias matches its value type. In normal usage,
type aliases are expanded to value types, so the variance of a type
alias can be obtained without implementing this. However, for example,
if we want to display the variance when hovering over a type alias, we
need to be able to obtain the variance of the type alias itself (cf.
#20900).

## Test Plan

I couldn't come up with a way to test this in mdtest, so I'm testing it
in a test submodule at the end of `types.rs`.
I also added a test to `mdtest/generics/pep695/variance.md`, but it
passes without the changes in this PR.
2025-10-17 21:12:19 +02:00
David Peter
6e7ff07065 [ty] Provide completions on TypeVars (#20943)
## Summary

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

## Test Plan

New snapshot tests
2025-10-17 20:05:20 +02:00
Alex Waygood
c7e2bfd759 [ty] continue and break statements outside loops are syntax errors (#20944)
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-17 17:13:40 +00:00
Alex Waygood
c424007645 Update usage instructions and lockfile for py-fuzzer script (#20940) 2025-10-17 15:57:17 +01:00
Brent Westbrook
0115fd3757 Avoid reusing nested, interpolated quotes before Python 3.12 (#20930)
## Summary

Fixes #20774 by tracking whether an `InterpolatedStringState` element is
nested inside of another interpolated element. This feels like kind of a
naive fix, so I'm welcome to other ideas. But it resolves the problem in
the issue and clears up the syntax error in the black compatibility
test, without affecting many other cases.

The other affected case is actually interesting too because the
[input](96b156303b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py (L707))
is invalid, but the previous quote selection fixed the invalid syntax:

```pycon
Python 3.11.13 (main, Sep  2 2025, 14:20:25) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f'{1: abcd "{'aa'}" }'  # input
  File "<stdin>", line 1
    f'{1: abcd "{'aa'}" }'
                  ^^
SyntaxError: f-string: expecting '}'
>>> f'{1: abcd "{"aa"}" }'  # old output
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier ' abcd "aa" ' for object of type 'int'
>>> f'{1: abcd "{'aa'}" }'  # new output
  File "<stdin>", line 1
    f'{1: abcd "{'aa'}" }'
                  ^^
SyntaxError: f-string: expecting '}'
```

We now preserve the invalid syntax in the input.

Unfortunately, this also seems to be another edge case I didn't consider
in https://github.com/astral-sh/ruff/pull/20867 because we don't flag
this as a syntax error after 0.14.1:

<details><summary>Shell output</summary>
<p>

```
> uvx ruff@0.14.0 check --ignore ALL --target-version py311 - <<EOF
f'{1: abcd "{'aa'}" }'
EOF
invalid-syntax: Cannot reuse outer quote character in f-strings on Python 3.11 (syntax was added in Python 3.12)
 --> -:1:14
  |
1 | f'{1: abcd "{'aa'}" }'
  |              ^
  |

Found 1 error.
> uvx ruff@0.14.1 check --ignore ALL --target-version py311 - <<EOF
f'{1: abcd "{'aa'}" }'
EOF
All checks passed!
> uvx python@3.11 -m ast <<EOF
f'{1: abcd "{'aa'}" }'
EOF
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/brent/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/lib/python3.11/ast.py", line 1752, in <module>
    main()
  File "/home/brent/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/lib/python3.11/ast.py", line 1748, in main
    tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/brent/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/lib/python3.11/ast.py", line 50, in parse
    return compile(source, filename, mode, flags,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    f'{1: abcd "{'aa'}" }'
                  ^^
SyntaxError: f-string: expecting '}'
```

</p>
</details> 


I assumed that was the same `ParseError` as the one caused by
`f"{1:""}"`, but this is a nested interpolation inside of the format
spec.

## Test Plan

New test copied from the black compatibility test. I guess this is a
duplicate now, I started working on this branch before the new black
tests were imported, so I could delete the separate test in our fixtures
if that's preferable.
2025-10-17 08:49:16 -04:00
David Peter
cfbd42c22a [ty] Support dataclass_transform for base class models (#20783)
## Summary

Support `dataclass_transform` when used on a (base) class.

## Typing conformance

* The changes in `dataclasses_transform_class.py` look good, just a few
mistakes due to missing `alias` support.
* I didn't look closely at the changes in
`dataclasses_transform_converter.py` since we don't support `converter`
yet.

## Ecosystem impact

The impact looks huge, but it's concentrated on a single project (ibis).
Their setup looks more or less like this:

* the real `Annotatable`:
d7083c2c96/ibis/common/grounds.py (L100-L101)
* the real `DataType`:
d7083c2c96/ibis/expr/datatypes/core.py (L161-L179)
* the real `Array`:
d7083c2c96/ibis/expr/datatypes/core.py (L1003-L1006)


```py
from typing import dataclass_transform

@dataclass_transform()
class Annotatable:
    pass

class DataType(Annotatable):
    nullable: bool = True

class Array[T](DataType):
    value_type: T
```

They expect something like `Array([1, 2])` to work, but ty, pyright,
mypy, and pyrefly would all expect there to be a first argument for the
`nullable` field on `DataType`. I don't really understand on what
grounds they expect the `nullable` field to be excluded from the
signature, but this seems to be the main reason for the new diagnostics
here. Not sure if related, but it looks like their typing setup is not
really complete
(https://github.com/ibis-project/ibis/issues/6844#issuecomment-1868274770,
this thread also mentions `dataclass_transform`).

## Test Plan

Update pre-existing tests.
2025-10-17 14:04:31 +02:00
Mark Z. Ding
fc3b341529 [ty] Truncate Literal type display in some situations (#20928) 2025-10-17 11:50:58 +00:00
Alex Waygood
baaa8dad3a [ty] Re-enable fuzzer seeds that are no longer slow (#20937) 2025-10-17 12:29:13 +01:00
Micha Reiser
a21cde8a5a [ty] Fix playground crash for very large files (#20934) 2025-10-17 09:15:33 +02:00
Aria Desires
64edfb6ef6 [ty] add legacy namespace package support (#20897)
Detect legacy namespace packages and treat them like namespace packages
when looking them up as the *parent* of the module we're interested in.
In all other cases treat them like a regular package.

(This PR is coauthored by @MichaReiser in a shared coding session)

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

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-17 03:16:37 +00:00
Ibraheem Ahmed
96b156303b [ty] Prefer declared type for invariant collection literals (#20927)
## Summary

Prefer the declared type for collection literals, e.g.,
```py
x: list[Any] = [1, "2", (3,)]
reveal_type(x)  # list[Any]
```

This solves a large part of https://github.com/astral-sh/ty/issues/136
for invariant generics, where respecting the declared type is a lot more
important. It also means that annotated dict literals with `dict[_,
Any]` is a way out of https://github.com/astral-sh/ty/issues/1248.
2025-10-16 16:11:28 -04:00
Douglas Creager
b0e10a9777 [ty] Don't track inferability via different Type variants (#20677)
We have to track whether a typevar appears in a position where it's
inferable or not. In a non-inferable position (in the body of the
generic class or function that binds it), assignability must hold for
every possible specialization of the typevar. In an inferable position,
it only needs to hold for _some_ specialization.
https://github.com/astral-sh/ruff/pull/20093 is working on using
constraint sets to model assignability of typevars, and the constraint
sets that we produce will be the same for inferable vs non-inferable
typevars; what changes is what we _compare_ that constraint set to. (For
a non-inferable typevar, the constraint set must equal the set of valid
specializations; for an inferable typevar, it must not be `never`.)

When I first added support for tracking inferable vs non-inferable
typevars, it seemed like it would be easiest to have separate `Type`
variants for each. The alternative (which lines up with the Δ set in
[POPL15](https://doi.org/10.1145/2676726.2676991)) would be to
explicitly plumb through a list of inferable typevars through our type
property methods. That seemed cumbersome.

In retrospect, that was the wrong decision. We've had to jump through
hoops to translate types between the inferable and non-inferable
variants, which has been quite brittle. Combined with the original point
above, that much of the assignability logic will become more identical
between inferable and non-inferable, there is less justification for the
two `Type` variants. And plumbing an extra `inferable` parameter through
all of these methods turns out to not be as bad as I anticipated.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-16 15:59:46 -04:00
Ibraheem Ahmed
25023cc0ea [ty] Use declared variable types as bidirectional type context (#20796)
## Summary

Use the declared type of variables as type context for the RHS of assignment expressions, e.g.,
```py
x: list[int | str]
x = [1]
reveal_type(x)  # revealed: list[int | str]
```
2025-10-16 15:40:39 -04:00
Ibraheem Ahmed
1ade4f2081 [ty] Avoid unnecessarily widening generic specializations (#20875)
## Summary

Ignore the type context when specializing a generic call if it leads to
an unnecessarily wide return type. For example, [the example mentioned
here](https://github.com/astral-sh/ruff/pull/20796#issuecomment-3403319536)
works as expected after this change:
```py
def id[T](x: T) -> T:
    return x

def _(i: int):
    x: int | None = id(i)
    y: int | None = i
    reveal_type(x)  # revealed: int
    reveal_type(y)  # revealed: int
```

I also added extended our usage of `filter_disjoint_elements` to tuple
and typed-dict inference, which resolves
https://github.com/astral-sh/ty/issues/1266.
2025-10-16 19:17:37 +00:00
David Peter
8dad58de37 [ty] Support dataclass-transform field_specifiers (#20888)
## Summary

Add support for the `field_specifiers` parameter on
`dataclass_transform` decorator calls.

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

## Conformance test results

All true positives ✔️ 

## Ecosystem analysis

* `trio`: this is the kind of change that I would expect from this PR.
The code makes use of a dataclass `Outcome` with a `_unwrapped: bool =
attr.ib(default=False, eq=False, init=False)` field that is excluded
from the `__init__` signature, so we now see a bunch of
constructor-call-related errors going away.
* `home-assistant/core`: They have a `domain: str = attr.ib(init=False,
repr=False)` field and then use
  ```py
    @domain.default
    def _domain_default(self) -> str:
        # …
  ```
This accesses the `default` attribute on `dataclasses.Field[…]` with a
type of `default: _T | Literal[_MISSING_TYPE.MISSING]`, so we get those
"Object of type `_MISSING_TYPE` is not callable" errors. I don't really
understand how that is supposed to work. Even if `_MISSING_TYPE` would
be absent from that union, what does this try to call? pyright also
issues an error and it doesn't seem to work at runtime? So this looks
like a true positive?
* `attrs`: Similar here. There are some new diagnostics on code that
tries to access `.validator` on a field. This *does* work at runtime,
but I'm not sure how that is supposed to type-check (without a [custom
plugin](2c6c395935/mypy/plugins/attrs.py (L575-L602))).
pyright errors on this as well.
* A handful of new false positives because we don't support `alias` yet

## Test Plan

Updated tests.
2025-10-16 20:49:11 +02:00
Dylan
2bffef5966 Bump 0.14.1 (#20925) 2025-10-16 12:44:13 -05:00
Brent Westbrook
e64d772788 Standardize syntax error construction (#20903)
Summary
--

This PR unifies the two different ways Ruff and ty construct syntax
errors. Ruff has been storing the primary message in the diagnostic
itself, while ty attached the message to the primary annotation:

```
> ruff check try.py
invalid-syntax: name capture `x` makes remaining patterns unreachable
 --> try.py:2:10
  |
1 | match 42:
2 |     case x: ...
  |          ^
3 |     case y: ...
  |

Found 1 error.
> uvx ty check try.py
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
Checking ------------------------------------------------------------ 1/1 files                                                                                                 
error[invalid-syntax]
 --> try.py:2:10
  |
1 | match 42:
2 |     case x: ...
  |          ^ name capture `x` makes remaining patterns unreachable
3 |     case y: ...
  |

Found 1 diagnostic
```

I think there are benefits to both approaches, and I do like ty's
version, but I feel like we should pick one (and it might help with
#20901 eventually). I slightly prefer Ruff's version, so I went with
that. Hopefully this isn't too controversial, but I'm happy to close
this if it is.

Note that this shouldn't change any other diagnostic formats in ty
because
[`Diagnostic::primary_message`](98d27c4128/crates/ruff_db/src/diagnostic/mod.rs (L177))
was already falling back to the primary annotation message if the
diagnostic message was empty. As a result, I think this change will
partially resolve the FIXME therein.

Test Plan
--

Existing tests with updated snapshots
2025-10-16 11:56:32 -04:00
Auguste Lalande
03696687ea [pydoclint] Implement docstring-extraneous-parameter (DOC102) (#20376)
## Summary

Implement `docstring-extraneous-parameter` (`DOC102`). This rule checks
that all parameters present in a functions docstring are also present in
its signature.

Split from #13280, per this
[comment](https://github.com/astral-sh/ruff/pull/13280#issuecomment-3280575506).

Part of #12434.

## Test Plan

Test cases added.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-16 11:26:51 -04:00
Micha Reiser
058fc37542 [ty] Fix panic 'missing root' when handling completion request (#20917) 2025-10-16 16:23:02 +02:00
Micha Reiser
ec9faa34be [ty] Run file watching tests serial when using nextest (#20918) 2025-10-16 16:08:37 +02:00
Aria Desires
7155a62e5c [ty] Add version hint for failed stdlib attribute accesses (#20909)
This is the ultra-minimal implementation of

* https://github.com/astral-sh/ty/issues/296

that was previously discussed as a good starting point. In particular we
don't actually bother trying to figure out the exact python versions,
but we still mention "hey btw for No Reason At All... you're on python
3.10" when you try to access something that has a definition rooted in
the stdlib that we believe exists sometimes.
2025-10-16 14:07:33 +00:00
Alex Waygood
a67e0690f2 More CI improvements (#20920) 2025-10-16 14:25:37 +01:00
Aria Desires
6a1e91ce97 [ty] Check typeshed VERSIONS for parent modules when reporting failed stdlib imports (#20908)
This is a drive-by improvement that I stumbled backwards into while
looking into

* https://github.com/astral-sh/ty/issues/296

I was writing some simple tests for "thing not in old version of stdlib"
diagnostics and checked what was added in 3.14, and saw
`compression.zstd` and to my surprise discovered that `import
compression.zstd` and `from compression import zstd` had completely
different quality diagnostics.

This is because `compression` and `compression.zstd` were *both*
introduced in 3.14, and so per VERSIONS policy only an entry for
`compression` was added, and so we don't actually have any definite info
on `compression.zstd` and give up on producing a diagnostic. However the
`from compression import zstd` form fails on looking up `compression`
and we *do* have an exact match for that, so it gets a better
diagnostic!

(aside: I have now learned about the VERSIONS format and I *really* wish
they would just enumerate all the submodules but, oh well!)

The fix is, when handling an import failure, if we fail to find an exact
match *we requery with the parent module*. In cases like
`compression.zstd` this lets us at least identify that, hey, not even
`compression` exists, and luckily that fixes the whole issue. In cases
where the parent module and submodule were introduced at different times
then we may discover that the parent module is in-range and that's fine,
we don't produce the richer stdlib diagnostic.
2025-10-16 13:25:08 +00:00
Alex Waygood
3db5d5906e Don't use codspeed or depot runners in CI jobs on forks (#20894) 2025-10-16 13:16:18 +01:00
Carl Meyer
d23826ce46 [ty] cache Type::is_redundant_with (#20477)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-10-16 13:46:56 +02:00
Micha Reiser
5fb142374d Fix run-away for mutually referential instance attributes (#20645) 2025-10-16 13:24:41 +02:00
Micha Reiser
9393279f65 [ty] Limit shown import paths to at most 5 unless ty runs with -v (#20912) 2025-10-16 13:18:09 +02:00
David Peter
c8133104e8 [ty] Use field-specifier return type as the default type for the field (#20915)
## Summary

`dataclasses.field` and field-specifier functions of commonly used
libraries like `pydantic`, `attrs`, and `SQLAlchemy` all return the
default type for the field (or `Any`) instead of an actual `Field`
instance, even if this is not what happens at runtime. Let's make use of
this fact and assume that *all* field specifiers return the type of the
default value of the field.

For standard dataclasses, this leads to more or less the same outcome
(see test diff for details), but this change is important for 3rd party
dataclass-transformers.

## Test Plan

Tested the consequences of this change on the field-specifiers branch as
well.
2025-10-16 13:13:45 +02:00
David Peter
0cc663efcd [ty] Do not assume that fields have a default value (#20914)
## Summary

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

## Test Plan

Added regression test
2025-10-16 12:49:24 +02:00
Eric Mark Martin
c9dfb51f49 [ty] Fix match pattern value narrowing to use equality semantics (#20882)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1349.

Fix match statement value patterns to use equality comparison semantics
instead of incorrectly narrowing to literal types directly. Value
patterns use equality for matching, and equality can be overridden, so
we can't always narrow to the matched literal.

## Test Plan

Updated match.md with corrected expected types and an additional example
with explanation

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-10-16 07:50:32 +00:00
Justin Su
fe4e3e2e75 Update setup instructions for Zed 0.208.0+ (#20902)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-16 07:39:48 +00:00
Emil Sadek
cb98933c50 Move TOML indent size config (#20905)
Co-authored-by: Emil Sadek <esadek@users.noreply.github.com>
2025-10-16 07:35:49 +00:00
Bhuminjay Soni
73520e4acd [syntax-errors]: implement F702 as semantic syntax error (#20869)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

This PR implements `F702`
https://docs.astral.sh/ruff/rules/continue-outside-loop/ as semantic
syntax error.

## Test Plan

<!-- How was it tested? -->
Tests are already previously written in F702

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
2025-10-15 19:27:15 +00:00
Alex Waygood
fd568f0221 [ty] Heterogeneous unpacking support for unions (#20377) 2025-10-15 19:30:03 +01:00
Shunsuke Shibayama
9de34e7ac1 [ty] refactor Place (#20871)
## Summary

Part of astral-sh/ty#1341

The following changes will be made to `Place`.

* Introduce `TypeOrigin`
* `Place::Type` -> `Place::Defined`
* `Place::Unbound` -> `Place::Undefined`
* `Boundness` -> `Definedness`

`TypeOrigin::Declared`+`Definedness::PossiblyUndefined` are patterns
that weren't considered before, but this PR doesn't address them yet,
only refactors.

## Test Plan

Refactoring
2025-10-15 20:19:19 +02:00
Alex Waygood
4b7f184ab7 Auto-accept snapshot changes as part of typeshed-sync PRs (#20892) 2025-10-15 17:37:08 +01:00
Wei Lee
d2a6ef7491 [airflow] Add warning to airflow.datasets.DatasetEvent usage (AIR301) (#20551)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

`airflow.datasets.DatasetEvent` has been removed in 3 but `AssetEvent`
might be added in the future

## Test Plan

<!-- How was it tested? -->

update the test fixture and reorg in the second commit
2025-10-15 12:19:55 -04:00
Dan Parizher
98d27c4128 [flake8-pyi] Fix operator precedence by adding parentheses when needed (PYI061) (#20508)
## Summary

Fixes #20265

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-15 15:06:03 +00:00
Dan Parizher
c06c3f9505 [pyupgrade] Fix false negative for TypeVar with default argument in non-pep695-generic-class (UP046) (#20660)
## Summary

Fixes #20656

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-15 14:51:55 +00:00
Alex Waygood
9e404a30c3 Update parser snapshots (#20893) 2025-10-15 14:21:24 +00:00
Brent Westbrook
8b9ab48ac6 Fix syntax error false positives for escapes and quotes in f-strings (#20867)
Summary
--

Fixes #20844 by refining the unsupported syntax error check for [PEP
701]
f-strings before Python 3.12 to allow backslash escapes and escaped
outer quotes
in the format spec part of f-strings. These are only disallowed within
the
f-string expression part on earlier versions. Using the examples from
the PR:

```pycon
>>> f"{1:\x64}"
'1'
>>> f"{1:\"d\"}"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier '"d"' for object of type 'int'
```

Note that the second case is a runtime error, but this is actually
avoidable if
you override `__format__`, so despite being pretty weird, this could
actually be
a valid use case.

```pycon
>>> class C:
...     def __format__(*args, **kwargs): return "<C>"
...
>>> f"{C():\"d\"}"
'<C>'
```

At first I thought narrowing the range we check to exclude the format
spec would
only work for escapes, but it turns out that cases like `f"{1:""}"` are
already
covered by an existing `ParseError`, so we can just narrow the range of
both our
escape and quote checks.

Our comment check also seems to be working correctly because it's based
on the
actual tokens. A case like
[this](https://play.ruff.rs/9f1c2ff2-cd8e-4ad7-9f40-56c0a524209f):

```python
f"""{1:# }"""
```

doesn't include a comment token, instead the `#` is part of an
`InterpolatedStringLiteralElement`.

Test Plan
--

New inline parser tests

[PEP 701]: https://peps.python.org/pep-0701/
2025-10-15 09:23:16 -04:00
Douglas Creager
8817ea5c84 [ty] Add (unused) inferable parameter to type property methods (#20865)
A large part of the diff on #20677 just involves threading a new
`inferable` parameter through all of the type property methods. In the
interests of making that PR easier to review, I've pulled that bit out
into here, so that it can be reviewed in isolation. This should be a
pure refactoring, with no logic changes or behavioral changes.
2025-10-15 09:05:15 -04:00
Micha Reiser
85ff4f3eef Run macos tests on macos (#20889) 2025-10-15 14:41:33 +02:00
Micha Reiser
c6959381f8 Remove release CI job (#20887) 2025-10-15 12:39:31 +02:00
David Peter
270ba71ad5 [ty] CI: Faster ecosystem analysis (#20886)
## Summary

I considered making a dedicated cargo profile for these, but the
`profiling` profile basically made all the modifications to `release`
that I would have also made.

## Test Plan

CI on this PR
2025-10-15 12:38:17 +02:00
Micha Reiser
cb4d4493d7 Remove strip from release profile (#20885) 2025-10-15 09:36:05 +00:00
github-actions[bot]
cafb96aa7a [ty] Sync vendored typeshed stubs (#20876)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
Co-authored-by: David Peter <mail@david-peter.de>
2025-10-15 11:13:32 +02:00
Andrew Gallant
651f7963a7 [ty] Add some completion ranking improvements (#20807)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-15 08:59:33 +00:00
Micha Reiser
4fc7dd300c Improved error recovery for unclosed strings (including f- and t-strings) (#20848) 2025-10-15 09:50:56 +02:00
Micha Reiser
a93618ed23 Enable lto=fat (#20863)
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-15 08:59:59 +02:00
Dan Parizher
9e1aafd0ce [pyupgrade] Extend UP019 to detect typing_extensions.Text (UP019) (#20825)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-15 06:52:14 +00:00
Dylan
abf685b030 [flake8-bugbear] Omit annotation in preview fix for B006 (#20877)
Closes #20864
2025-10-15 01:14:01 +00:00
Paillat
e1e3eb7209 fix(docs): Fix typo in RUF015 description (#20873)
## Summary
Fixed a typo. It should be "or", not "of". Both `.pop()` and `next()` on
an empty collection will raise `IndexError`, not "`[0]` of the `pop()`
function"

## Test Plan

n/a
2025-10-14 21:38:31 +00:00
Alex Waygood
43eddc566f [ty] Improve and extend tests for instance attributes redeclared in subclasses (#20866)
Part of https://github.com/astral-sh/ty/issues/1345
2025-10-14 19:31:34 +01:00
David Peter
f8e00e3cd9 [ty] Ignore slow seeds as a temporary measure (#20870)
## Summary

Basically what @AlexWaygood suggested
[here](https://github.com/astral-sh/ruff/pull/20802#issuecomment-3402218389)
(thank you).

## Test Plan

CI on this PR
2025-10-14 20:02:20 +02:00
Brent Westbrook
591e9bbccb Remove parentheses around multiple exception types on Python 3.14+ (#20768)
Summary
--

This PR implements the black preview style from
https://github.com/psf/black/pull/4720. As of Python 3.14, you're
allowed to omit the parentheses around groups of exceptions, as long as
there's no `as` binding:

**3.13**

```pycon
Python 3.13.4 (main, Jun  4 2025, 17:37:06) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try: ...
... except (Exception, BaseException): ...
...
Ellipsis
>>> try: ...
... except Exception, BaseException: ...
...
  File "<python-input-1>", line 2
    except Exception, BaseException: ...
           ^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized
```

**3.14**

```pycon
Python 3.14.0rc2 (main, Sep  2 2025, 14:20:56) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try: ...
... except Exception, BaseException: ...
...
Ellipsis
>>> try: ...
... except (Exception, BaseException): ...
...
Ellipsis
>>> try: ...
... except Exception, BaseException as e: ...
...
  File "<python-input-2>", line 2
    except Exception, BaseException as e: ...
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized when using 'as'
```

I think this ended up being pretty straightforward, at least once Micha
showed me where to start :)

Test Plan
--

New tests

At first I thought we were deviating from black in how we handle
comments within the exception type tuple, but I think this applies to
how we format all tuples, not specifically with the new preview style.
2025-10-14 11:17:45 -04:00
Brent Westbrook
1ed9b215b9 Update Black tests (#20794)
Summary
--

```shell
git clone git@github.com:psf/black.git ../other/black
crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py ../other/black
```

Then ran our tests and accepted the snapshots

I had to make a small fix to our tuple normalization logic for `del`
statements
in the second commit, otherwise the tests were panicking at a changed
AST. I
think the new implementation is closer to the intention described in the
nearby
comment anyway, though.

The first commit adds the new Python, settings, and `.expect` files, the
next three commits make some small
fixes to help get the tests running, and then the fifth commit accepts
all but one of the new snapshots. The last commit includes the new
unsupported syntax error for one f-string example, tracked in #20774.

Test Plan
--

Newly imported tests. I went through all of the new snapshots and added
review comments below. I think they're all expected, except a few cases
I wasn't 100% sure about.
2025-10-14 10:14:59 -04:00
Alex Waygood
9090aead0f [ty] Fix further issues in super() inference logic (#20843) 2025-10-14 12:48:47 +00:00
Micha Reiser
441ba20876 [ty] Document when a rule was added (#20859) 2025-10-14 14:33:48 +02:00
David Peter
6341bb7403 [ty] Treat Callable dunder members as bound method descriptors (#20860)
## Summary

Dunder methods (at least the ones defined in the standard library)
always take an instance of the class as the first parameter. So it seems
reasonable to generally treat them as bound method descriptors if they
are defined via a `Callable` type.

This removes just a few false positives from the ecosystem, but solves
three user-reported issues:

closes https://github.com/astral-sh/ty/issues/908
closes https://github.com/astral-sh/ty/issues/1143
closes https://github.com/astral-sh/ty/issues/1209

In addition to the change here, I also considered [making `ClassVar`s
bound method descriptors](https://github.com/astral-sh/ruff/pull/20861).
However, there was zero ecosystem impact. So I think we can also close
https://github.com/astral-sh/ty/issues/491 with this PR.

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

## Test Plan

Added regression test
2025-10-14 14:27:52 +02:00
David Peter
ac2c530377 [ty] Handle decorators which return unions of Callables (#20858)
## Summary

If a function is decorated with a decorator that returns a union of
`Callable`s, also treat it as a union of function-like `Callable`s.

Labeling as `internal`, since the previous change has not been released
yet.

## Test Plan

New regression test.
2025-10-14 09:47:50 +00:00
Dan Parizher
c69fa75cd5 Fix false negatives in Truthiness::from_expr for lambdas, generators, and f-strings (#20704) 2025-10-14 03:06:17 -05:00
David Peter
f73bb45be6 [ty] Rename Type unwrapping methods (#20857)
## Summary

Rename "unwrapping" methods on `Type` from e.g.
`Type::into_class_literal` to `Type::as_class_literal`. I personally
find that name more intuitive, since no transformation of any kind is
happening. We are just unwrapping from certain enum variants. An
alternative would be `try_as_class_literal`, which would follow the
[`strum` naming
scheme](https://docs.rs/strum/latest/strum/derive.EnumTryAs.html), but
is slightly longer.

Also rename `Type::into_callable` to `Type::try_upcast_to_callable`.
Note that I intentionally kept names like
`FunctionType::into_callable_type`, because those return `CallableType`,
not `Option<Type<…>>`.

## Test Plan

Pure refactoring
2025-10-14 09:53:29 +02:00
Matt Norton
e338d2095e Update lint.flake8-type-checking.quoted-annotations docs (#20765)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-14 06:43:24 +00:00
Douglas Creager
5e08e5451d [ty] Add separate type for typevar "identity" (#20813)
As part of #20598, we added `is_identical_to` methods to
`TypeVarInstance` and `BoundTypeVarInstance`, which compare when two
typevar instances refer to "the same" underlying typevar, even if we
have forced their lazy bounds/constraints as part of marking typevars as
inferable. (Doing so results in a different salsa interned struct ID,
since we've changed the contents of the `bounds_or_constraints` field.)

It turns out that marking typevars as inferable is not the only way that
we might force lazy bounds/constraints; it also happens when we
materialize a type containing a typevar. This surfaced as ecosystem
report failures on #20677.

That means that we need a more long-term fix to this problem.
(`is_identical_to`, and its underlying `original` field, were meant to
be a temporary fix until we removed the `MarkTypeVarsInferable` type
mapping.)

This PR extracts out a separate type (`TypeVarIdentity`) that only
includes the fields that actually inform whether two typevars are "the
same". All other properties of the typevar (default, bounds/constraints,
etc) still live in `TypeVarInstance`. Call sites that care about typevar
identity can now either store just `TypeVarIdentity` (if they never need
access to those other properties), or continue to store
`TypeVarInstance` but pull out its `identity` when performing those "are
they the same typevar" comparisons. (All of this also applies
respectively to `BoundTypeVar{Identity,Instance}`.) In particular,
constraint sets now work on `BoundTypeVarIdentity`, and generic contexts
still _store_ a `BoundTypeVarInstance` (since we might need access to
defaults when specializing), but are keyed on `BoundTypeVarIdentity`.
2025-10-13 20:09:27 -04:00
Douglas Creager
aba0bd568e [ty] Diagnostic for generic classes that reference typevars in enclosing scope (#20822)
Generic classes are not allowed to bind or reference a typevar from an
enclosing scope:

```py
def f[T](x: T, y: T) -> None:
    class Ok[S]: ...
    # error: [invalid-generic-class]
    class Bad1[T]: ...
    # error: [invalid-generic-class]
    class Bad2(Iterable[T]): ...

class C[T]:
    class Ok1[S]: ...
    # error: [invalid-generic-class]
    class Bad1[T]: ...
    # error: [invalid-generic-class]
    class Bad2(Iterable[T]): ...
```

It does not matter if the class uses PEP 695 or legacy syntax. It does
not matter if the enclosing scope is a generic class or function. The
generic class cannot even _reference_ an enclosing typevar in its base
class list.

This PR adds diagnostics for these cases.

In addition, the PR adds better fallback behavior for generic classes
that violate this rule: any enclosing typevars are not included in the
class's generic context. (That ensures that we don't inadvertently try
to infer specializations for those typevars in places where we
shouldn't.) The `dulwich` ecosystem project has [examples of
this](d912eaaffd/dulwich/config.py (L251))
that were causing new false positives on #20677.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-13 19:30:49 -04:00
Martín Gaitán
83b497ce88 Update Python compatibility from 3.13 to 3.14 in README.md (#20852)
After #20725 ruff is compatible with Python 3.14 without preview
enabled, so lets note it in the README
2025-10-13 20:49:11 +00:00
Bhuminjay Soni
2b729b4d52 [syntax-errors]: break outside loop F701 (#20556)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

This PR implements https://docs.astral.sh/ruff/rules/break-outside-loop/
(F701) as a semantic syntax error.

## Test Plan

<!-- How was it tested? -->

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-13 20:00:59 +00:00
David Peter
4b8e278a88 [ty] Treat Callables as bound-method descriptors in special cases (#20802)
## Summary

Treat `Callable`s as bound-method descriptors if `Callable` is the
return type of a decorator that is applied to a function definition. See
the [rendered version of the new test
file](https://github.com/astral-sh/ruff/blob/david/callables-as-descriptors/crates/ty_python_semantic/resources/mdtest/call/callables_as_descriptors.md)
for the full description of this new heuristic.

I could imagine that we want to treat `Callable`s as bound-method
descriptors in other cases as well, but this seems like a step in the
right direction. I am planning to add other "use cases" from
https://github.com/astral-sh/ty/issues/491 to this test suite.

partially addresses https://github.com/astral-sh/ty/issues/491
closes https://github.com/astral-sh/ty/issues/1333

## Ecosystem impact

All positive

* 2961 removed `unsupported-operator` diagnostics on `sympy`, which was
one of the main motivations for implementing this change
* 37 removed `missing-argument` diagnostics, and no added call-error
diagnostics, which is an indicator that this heuristic shouldn't cause
many false positives
* A few removed `possibly-missing-attribute` diagnostics when accessing
attributes like `__name__` on decorated functions. The two added
`unused-ignore-comment` diagnostics are also cases of this.
* One new `invalid-assignment` diagnostic on `dd-trace-py`, which looks
suspicious, but only because our `invalid-assignment` diagnostics are
not great. This is actually a "Implicit shadowing of function"
diagnostic that hides behind the `invalid-assignment` diagnostic,
because a module-global function is being patched through a
`module.func` attribute assignment.

## Test Plan

New Markdown tests.
2025-10-13 21:17:47 +02:00
David Peter
d912f13661 [ty] Do not bind self to non-positional parameters (#20850)
## Summary

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

## Test Plan

Regression test
2025-10-13 20:44:27 +02:00
Brent Westbrook
71f8389f61 Fix syntax error false positives on parenthesized context managers (#20846)
This PR resolves the issue noticed in
https://github.com/astral-sh/ruff/pull/20777#discussion_r2417233227.
Namely, cases like this were being flagged as syntax errors despite
being perfectly valid on Python 3.8:

```pycon
Python 3.8.20 (default, Oct  2 2024, 16:34:12)
[Clang 18.1.8 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with (open("foo.txt", "w")): ...
...
Ellipsis
>>> with (open("foo.txt", "w")) as f: print(f)
...
<_io.TextIOWrapper name='foo.txt' mode='w' encoding='UTF-8'>
```

The second of these was already allowed but not the first:

```shell
> ruff check --target-version py38 --ignore ALL - <<EOF
with (open("foo.txt", "w")): ...
with (open("foo.txt", "w")) as f: print(f)
EOF
invalid-syntax: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
 --> -:1:6
  |
1 | with (open("foo.txt", "w")): ...
  |      ^
2 | with (open("foo.txt", "w")) as f: print(f)
  |

Found 1 error.
```

There was some discussion of related cases in
https://github.com/astral-sh/ruff/pull/16523#discussion_r1984657793, but
it seems I overlooked the single-element case when flagging tuples. As
suggested in the other thread, we can just check if there's more than
one element or a trailing comma, which will cause the tuple parsing on
<=3.8 and avoid the false positives.
2025-10-13 14:13:27 -04:00
Micha Reiser
373fe8a39c [ty] Remove 'pre-release software' warning (#20817) 2025-10-13 19:50:19 +02:00
Brent Westbrook
975891fc90 Render unsupported syntax errors in formatter tests (#20777)
## Summary

Based on the suggestion in
https://github.com/astral-sh/ruff/issues/20774#issuecomment-3383153511,
I added rendering of unsupported syntax errors in our `format` test.

In support of this, I added a `DummyFileResolver` type to `ruff_db` to
pass to `DisplayDiagnostics::new` (first commit). Another option would
obviously be implementing this directly in the fixtures, but we'd have
to import a `NotebookIndex` somehow; either by depending directly on
`ruff_notebook` or re-exporting it from `ruff_db`. I thought it might be
convenient elsewhere to have a dummy resolver, for example in the
parser, where we currently have a separate rendering pipeline
[copied](https://github.com/astral-sh/ruff/blob/main/crates/ruff_python_parser/tests/fixtures.rs#L321)
from our old rendering code in `ruff_linter`. I also briefly tried
implementing a `TestDb` in the formatter since I noticed the
`ruff_python_formatter::db` module, but that was turning into a lot more
code than the dummy resolver.

We could also push this a bit further if we wanted. I didn't add the new
snapshots to the black compatibility tests or to the preview snapshots,
for example. I thought it was kind of noisy enough (and helpful enough)
already, though. We could also use a shorter diagnostic format, but the
full output seems most useful once we accept this initial large batch of
changes.

## Test Plan

I went through the baseline snapshots pretty quickly, but they all
looked reasonable to me, with one exception I noted below. I also tested
that the case from #20774 produces a new unsupported syntax error.
2025-10-13 10:00:37 -04:00
David Peter
195e8f0684 [ty] Treat functions, methods, and dynamic types as function-like Callables (#20842)
## Summary

Treat functions, methods, and dynamic types as function-like `Callable`s

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

## Ecosystem analysis

All removed diagnostics look like cases of
https://github.com/astral-sh/ty/issues/1344

## Test Plan

Added regression test
2025-10-13 15:21:55 +02:00
Alex Waygood
513d2996ec [ty] Move logic for super() inference to a new types::bound_super submodule (#20840) 2025-10-13 11:18:13 +00:00
Alex Waygood
d83d7a0dcd [ty] Fix false-positive diagnostics on super() calls (#20814) 2025-10-13 10:57:46 +00:00
David Peter
565dbf3c9d [ty] Move class_member to member module (#20837)
## Summary

Move the `class_member` function to the `member` module. This allows us
to move the `member` module into the `types` module and to reduce the
visibility of its contents to `pub(super)`. The drawback is that we need
to make `place::place_by_id` public.

## Test Plan

Pure refactoring.
2025-10-13 10:58:37 +02:00
Takayuki Maeda
f715d70be1 [ruff] Use DiagnosticTag for more flake8 and numpy rules (#20758) 2025-10-13 10:29:15 +02:00
David Peter
9b9c9ae092 [ty] Prefer declared base class attribute over inferred attribute on subclass (#20764)
## Summary

When accessing an (instance) attribute on a given class, we were
previously traversing its MRO, and building a union of types (if the
attribute was available on multiple classes in the MRO) until we found a
*definitely bound* symbol. The idea was that possibly unbound symbols in
a subclass might only partially shadow the underlying base class
attribute.

This behavior was problematic for two reasons:
* if the attribute was definitely bound on a class (e.g. `self.x =
None`), we would have stopped iterating, even if there might be a `x:
str | None` declaration in a base class (the bug reported in
https://github.com/astral-sh/ty/issues/1067).
* if the attribute originated from an implicit instance attribute
assignment (e.g. `self.x = 1` in method `Sub.foo`), we might stop
looking and miss another implicit instance attribute assignment in a
base class method (e.g. `self.x = 2` in method `Base.bar`).

With this fix, we still iterate the MRO of the class, but we only stop
iterating if we find a *definitely declared* symbol. In this case, we
only return the declared attribute type. Otherwise, we keep building a
union of inferred attribute types.

The implementation here seemed to be the easiest fix for
https://github.com/astral-sh/ty/issues/1067 that also kept the ecosystem
impact low (the changes that I see all look correct). However, as the
Markdown tests show, there are other things to fix in this area. For
example, we should do a similar thing for *class attributes*. This is
more involved, though (affects many different areas and probably
involves a change to our descriptor protocol implementation), so I'd
like to postpone this to a follow-up.

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

## Test Plan

Updated Markdown tests, including a regression test for
https://github.com/astral-sh/ty/issues/1067.
2025-10-13 09:28:57 +02:00
Micha Reiser
c80ee1a50b [ty] Log files that are slow to type check (#20836) 2025-10-13 09:15:54 +02:00
renovate[bot]
350042b801 Update cargo-bins/cargo-binstall action to v1.15.7 (#20827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 08:28:55 +02:00
renovate[bot]
e02cdd350e Update CodSpeedHQ/action action to v4.1.1 (#20828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 08:28:37 +02:00
renovate[bot]
e3b910c41a Update Rust crate pyproject-toml to v0.13.7 (#20835)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 08:28:06 +02:00
renovate[bot]
0e8c02aea6 Update Rust crate anstream to v0.6.21 (#20829) 2025-10-12 21:43:39 -04:00
renovate[bot]
74b2c4c2e4 Update Rust crate libc to v0.2.177 (#20832) 2025-10-12 21:43:10 -04:00
renovate[bot]
89b67a2448 Update Rust crate memchr to v2.7.6 (#20834) 2025-10-12 21:42:52 -04:00
renovate[bot]
6be344af65 Update Rust crate libcst to v1.8.5 (#20833) 2025-10-12 21:42:38 -04:00
renovate[bot]
89f9dd6b43 Update Rust crate camino to v1.2.1 (#20831) 2025-10-12 21:42:19 -04:00
renovate[bot]
1935896e6b Update Rust crate anstyle to v1.0.13 (#20830) 2025-10-12 21:42:06 -04:00
Alex Waygood
7064c38e53 [ty] Filter out revealed-type and undefined-reveal diagnostics from mdtest snapshots (#20820) 2025-10-12 18:39:32 +00:00
Shunsuke Shibayama
dc64c08633 [ty] bidirectional type inference using function return type annotations (#20528)
## Summary

Implements bidirectional type inference using function return type
annotations.

This PR was originally proposed to solve astral-sh/ty#1167, but this
does not fully resolve it on its own.
Additionally, I believe we need to allow dataclasses to generate their
own `__new__` methods, [use constructor return types ​​for
inference](5844c0103d/crates/ty_python_semantic/src/types.rs (L5326-L5328)),
and a mechanism to discard type narrowing like `& ~AlwaysFalsy` if
necessary (at a more general level than this PR).

## Test Plan

`mdtest/bidirectional.md` is added.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Ibraheem Ahmed <ibraheem@ibraheem.ca>
2025-10-11 00:38:35 +00:00
Shunsuke Shibayama
11a9e7ee44 [ty] use type context more aggressively to infer values ​​when constructing a TypedDict (#20806)
## Summary

Based on @ibraheemdev's comment on #20792:

> I think we can also update our bidirectional inference code, [which
makes the same
assumption](https://github.com/astral-sh/ruff/blob/main/crates/ty_python_semantic/src/types/infer/builder.rs?rgh-link-date=2025-10-09T21%3A30%3A31Z#L5860).

This PR also adds more test cases for how `TypedDict` annotations affect
generic call inference.

## Test Plan

New tests in `typed_dict.md`
2025-10-10 16:51:16 -07:00
ageorgou
bbd3856de8 [flake8-datetimez] Clarify docs for several rules (#20778)
## Summary

Resolves #19384.

- Distinguishes more clearly between `date` and `datetime` objects.
- Uniformly links to the relevant Python docs from rules in this
category.

I've tried to be clearer, but there's still a contradiction in the rules
as written: we say "use timezone-aware objects", but `date`s are
inherently timezone-naive.

Also, the full docs don't always match the error message: for instance,
in [DTZ012](https://docs.astral.sh/ruff/rules/call-date-fromtimestamp/),
the example says to use:
```python
datetime.datetime.fromtimestamp(946684800, tz=datetime.UTC)
```
while `fix_title` returns "Use `datetime.datetime.fromtimestamp(ts,
tz=...)**.date()**` instead".
I have left this as it was for now.

## Test Plan
Ran `mkdocs` locally and inspected result.
2025-10-10 13:02:24 +00:00
David Peter
ae83a1fd2d [ty] Additional tests for dataclass_transform (class-level overwrites, field_specifiers) (#20788)
## Summary

Adds a set of basic new tests corresponding to open points in
https://github.com/astral-sh/ty/issues/1327, to document the state of
support for `dataclass_transform`.
2025-10-10 11:22:06 +00:00
Alex Waygood
44807c4a05 [ty] Better implementation of assignability for intersections with negated gradual elements (#20773) 2025-10-10 11:10:17 +00:00
David Peter
69f9182033 [ty] Annotations are deferred by default for 3.14+ (#20799)
## Summary

Type annotations are deferred by default starting with Python 3.14. No
`from __future__ import annotations` import is necessary.

## Test Plan

New Markdown test
2025-10-10 12:05:03 +02:00
David Peter
949a4f1c42 [ty] Simplify and fix CallableTypeOf[..] implementation (#20797)
## Summary

Simplify and fix the implementation of
`ty_extensions.CallableTypeOf[..]`.

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

## Test Plan

Added regression test.
2025-10-10 12:04:37 +02:00
David Peter
a82833a998 [ty] Update mypy_primer and project lists (#20798)
## Summary

Pulls in two updates to `mypy_primer` projects:

* https://github.com/hauntsaninja/mypy_primer/pull/201 (add
`django-test-migrations`)
* https://github.com/hauntsaninja/mypy_primer/pull/122 (remove
`SinbadCogs`)

## Test Plan

CI on this PR
2025-10-10 11:08:39 +02:00
Micha Reiser
4bd454f9b5 Shard ty walltime benchmarks (#20791) 2025-10-10 07:55:50 +02:00
pieterh-oai
66885e4bce [flake8-logging-format] Avoid dropping implicitly concatenated pieces in the G004 fix (#20793)
## Summary

The original autofix for G004 was quietly dropping everything but the
f-string components of any implicit concatenation sequence; this
addresses that.

Side note: It looks like `f_strings` is a bit risky to use (since it
implicitly skips non-f-string parts); use iter and include implicitly
concatenated pieces. We should consider if it's worth having
(convenience vs. bit risky).

## Test Plan

```
cargo test -p ruff_linter
```

Backtest (run new testcases against previous implementation):
```
git checkout HEAD^ crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs
cargot test -p ruff_linter

```
2025-10-09 18:14:38 -04:00
Carl Meyer
8248193ed9 [ty] defer inference of legacy TypeVar bound/constraints/defaults (#20598)
## Summary

This allows us to handle self-referential bounds/constraints/defaults
without panicking.

Handles more cases from https://github.com/astral-sh/ty/issues/256

This also changes the way we infer the types of legacy TypeVars. Rather
than understanding a constructor call to `typing[_extension].TypeVar`
inside of any (arbitrarily nested) expression, and having to use a
special `assigned_to` field of the semantic index to try to best-effort
figure out what name the typevar was assigned to, we instead understand
the creation of a legacy `TypeVar` only in the supported syntactic
position (RHS of a simple un-annotated assignment with one target). In
any other position, we just infer it as creating an opaque instance of
`typing.TypeVar`. (This behavior matches all other type checkers.)

So we now special-case TypeVar creation in `TypeInferenceBuilder`, as a
special case of an assignment definition, rather than deeper inside call
binding. This does mean we re-implement slightly more of
argument-parsing, but in practice this is minimal and easy to handle
correctly.

This is easier to implement if we also make the RHS of a simple (no
unpacking) one-target assignment statement no longer a standalone
expression. Which is fine to do, because simple one-target assignments
don't need to infer the RHS more than once. This is a bonus performance
(0-3% across various projects) and significant memory-usage win, since
most assignment statements are simple one-target assignment statements,
meaning we now create many fewer standalone-expression salsa
ingredients.

This change does mean that inference of manually-constructed
`TypeAliasType` instances can no longer find its Definition in
`assigned_to`, which regresses go-to-definition for these aliases. In a
future PR, `TypeAliasType` will receive the same treatment that
`TypeVar` did in this PR (moving its special-case inference into
`TypeInferenceBuilder` and supporting it only in the correct syntactic
position, and lazily inferring its value type to support recursion),
which will also fix the go-to-definition regression. (I decided a
temporary edge-case regression is better in this case than doubling the
size of this PR.)

This PR also tightens up and fixes various aspects of the validation of
`TypeVar` creation, as seen in the tests.

We still (for now) treat all typevars as instances of `typing.TypeVar`,
even if they were created using `typing_extensions.TypeVar`. This means
we'll wrongly error on e.g. `T.__default__` on Python 3.11, even if `T`
is a `typing_extensions.TypeVar` instance at runtime. We share this
wrong behavior with both mypy and pyrefly. It will be easier to fix
after we pull in https://github.com/python/typeshed/pull/14840.

There are some issues that showed up here with typevar identity and
`MarkTypeVarsInferable`; the fix here (using the new `original` field
and `is_identical_to` methods on `BoundTypeVarInstance` and
`TypeVarInstance`) is a bit kludgy, but it can go away when we eliminate
`MarkTypeVarsInferable`.

## Test Plan

Added and updated mdtests.

### Conformance suite impact

The impact here is all positive:

* We now correctly error on a legacy TypeVar with exactly one constraint
type given.
* We now correctly error on a legacy TypeVar with both an upper bound
and constraints specified.

### Ecosystem impact

Basically none; in the setuptools case we just issue slightly different
errors on an invalid TypeVar definition, due to the modified validation
code.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-09 21:08:37 +00:00
Ibraheem Ahmed
b086ffe921 [ty] Type-context aware literal promotion (#20776)
## Summary

Avoid literal promotion when a literal type annotation is provided, e.g.,
```py
x: list[Literal[1]] = [1]
```

Resolves https://github.com/astral-sh/ty/issues/1198. This does not fix
issue https://github.com/astral-sh/ty/issues/1284, but it does make it
more relevant because after this change, it is possible to directly
instantiate a generic type with a literal specialization.
2025-10-09 16:53:53 -04:00
Dan Parizher
537ec5f012 [fastapi] Fix false positives for path parameters that FastAPI doesn't recognize (FAST003) (#20687)
## Summary

Fixes #20680

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-09 16:10:21 -04:00
Shunsuke Shibayama
db91ac7dce [ty] allow any string Literal type expression as a key when constructing a TypedDict (#20792) 2025-10-09 18:24:11 +00:00
David Peter
75f3c0e8e6 [ty] Respect dataclass_transform parameters for metaclass-based models (#20780)
## Summary

Respect parameters such as `frozen_default` for metaclass-based
`@dataclass_transformer` models.

Related to: https://github.com/astral-sh/ty/issues/1260

## Typing conformance changes

Those are all correct (new true positives)

## Test Plan

New Markdown tests
2025-10-09 13:24:20 +00:00
wangxiaolei
f0d0b57900 [ty] dataclass_transform: Support frozen_default and kw_only_default (#20761)
## Summary

- Add support for eq, kw_only, and frozen parameter overrides in
@dataclass_transform
- Previously only order parameter override was supported
- Update test documentation to reflect fixed behavior
- Resolves issue where kw_only_default and frozen_default could not be
overridden

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

## Test Plan

New Markdown tests

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-10-09 09:34:49 +02:00
Alex Waygood
b0c6217e0b [ty] Fix broken property tests for disjointness of intersections (#20775)
## Summary

Two stable property tests are currently failing on `main`, following
f054b8a55e
(of course, I only thought to run the property tests again around 30
minutes _after_ landing that PR...). The issue is quite subtle, and took
me an annoying amount of time to pin down: we're matching over `(self,
other)` in `Type::is_disjoint_from_impl`, but `other` here is shadowed
by the binding in the `match` branch, which means that the wrong key is
inserted into the cache of the `IsDisjointFrom` cycle detector:


f054b8a55e/crates/ty_python_semantic/src/types.rs (L2408-L2435)

This PR fixes that issue, and also adds a few `Debug` implementations to
our cycle detectors, so that issues like this are easier to debug in the
future.

I'm adding the `internal` label, as this fixes a bug that hasn't yet
appeared in any released version of ty, so it doesn't deserve its own
changelog entry.

## Test Plan

`QUICKCHECK_TESTS=1000000 cargo test --release -p ty_python_semantic --
--ignored types::property_tests::stable` now once again passes on `main`

I considered adding new mdtests as well, but the examples that the
property tests were throwing at me all seemed _quite_ obscure and
somewhat unlikely to occur in the real world. I don't think it's worth
it.
2025-10-08 22:28:56 +01:00
Alex Waygood
f054b8a55e [ty] Improve assignability/subtyping between two protocol types (#20368) 2025-10-08 18:37:30 +00:00
Alex Waygood
b9c84add07 [ty] Disambiguate classes that live in different modules but have the same fully qualified names (#20756)
## Summary

Even disambiguating classes using their fully qualified names is not
enough for some diagnostics. We've seen real-world examples in the
ecosystem (and https://github.com/astral-sh/ruff/pull/20368 introduces
some more!) where two types can be different, but can still have the
same fully qualified name. In these cases, our disambiguation machinery
needs to print the file path and line number of the class in order to
disambiguate classes with similar names in our diagnostics.

Helps with https://github.com/astral-sh/ty/issues/1306

## Test Plan

Mdtests
2025-10-08 18:27:40 +01:00
David Peter
150ea92d03 [ty] Add tests for instance attributes in class hierarchies (#20767)
## Summary

This adds a couple of new test cases related to
https://github.com/astral-sh/ty/issues/1067 and beyond that. For now,
they are just documenting the current (problematic) behavior. Since the
topic has some subtleties, I'd like to merge this prior to the actual
bugfix(es) in order to evaluate the changes in an easier way.
2025-10-08 17:46:47 +02:00
David Peter
697998f836 [ty] Do not re-export ide_support attributes from types (#20769)
## Summary

The `types` module currently re-exports a lot of functions and data
types from `types::ide_support`. One of these is called `Member`, a name
that is overloaded several times already. And I'd like to add one more
`Member` struct soon. Making the whole `ide_support` module public seems
cleaner to me, anyway.

## Test Plan

Pure refactoring.
2025-10-08 17:45:28 +02:00
Andrew Gallant
3771f1567c [ty] Add an evaluation for completions
This is still early days, but I hope the framework introduced here makes
it very easy to add new truth data. Truth data should be seen as a form
of regression test for non-ideal ranking of completion suggestions.

I think it would help to read `crates/ty_completion_eval/README.md`
first to get an idea of what you're reviewing.
2025-10-08 08:44:21 -04:00
David Peter
6b94e620fe [ty] Fix accidental Liskov violation in protocol tests (#20763)
## Summary

We have the following test in `protocols.md`:
```py
class HasX(Protocol):
    x: int

# […]

class Foo:
    x: int

# […]

class FooBool(Foo):
    x: bool

static_assert(not is_subtype_of(FooBool, HasX))
static_assert(not is_assignable_to(FooBool, HasX))
```

If `Foo` was indeed intended to be a base class of `FooBool`, then `x:
bool` should be reported as a Liskov violation. And then it's a matter
of definition whether or not these assertions should hold true or not
(should the incorrect override take precedence or not?). So it looks to
me like this is just an oversight, probably a copy-paste error from
another test right before it, where `FooSub` is indeed intended to be a
subclass of `Foo`.

I am fixing this because this test started to fail on a branch of mine
that changes how attribute lookup in inheritance chains works.
2025-10-08 14:04:37 +02:00
David Peter
db80febb6b [ty] Use 3.14 in the ty playground (#20760)
## Summary

Use 3.14 by default in the ty playground

## Test Plan

Opened the playground locally and made sure that the default
configuration uses 3.14.
2025-10-08 12:41:57 +02:00
Mark Z. Ding
f95eb90951 [ty] Truncate type display for long unions in some situations (#20730)
## Summary

Fixes [astral-sh/ty#1307](https://github.com/astral-sh/ty/issues/1307)

Unions with length <= 5 are unaffected to minimize test churn
Unions with length > 5 will only display the first 3 elements + "...
omitted x union elements"
Here "length" is defined as the number of elements after condensation to
literals

Edit: we no longer truncate in revel case. 
Before:

> info: Attempted to call union type `(def f1() -> int) | (def f2(name:
str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) ->
int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) |
(Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`

After:

> info: Attempted to call union type `(def f1() -> int) | (def f2(name:
str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union
elements`

The below comparisons are outdated, but left here as a reference.

Before:
```reveal_type(x)  # revealed: Literal[1, 2] | A | B | C | D | E | F | G```
```reveal_type(x) # revealed: Result1A | Result1B | Result2A | Result2B
| Result3 | Result4```
After:
```reveal_type(x)  # revealed: Literal[1, 2] | A | B | ... omitted 5 union elements```
```reveal_type(x) # revealed: Result1A | Result1B | Result2A | ...
omitted 3 union elements```

This formatting is consistent with
`crates/ty_python_semantic/src/types/call/bind.rs` line 2992

## Test Plan

Cosmetic only, covered and verified by changes in mdtest
2025-10-08 11:21:26 +01:00
David Peter
1f1542db51 [ty] Use 3.14 as the default version (#20759)
## Summary

Bump the latest supported Python version of ty to 3.14 and updates some
references from 3.13 to 3.14.

This also fixes a bug with `dataclasses.field` on 3.14 (which adds a new
keyword-only parameter to that function, breaking our previously naive
matching on the parameter structure of that function).

## Test Plan

A `ty check` on a file with template strings (without any further
configuration) doesn't raise errors anymore.
2025-10-08 11:38:47 +02:00
Takayuki Maeda
abbbe8f3af [ruff] Use DiagnosticTag for more pyupgrade rules (#20734) 2025-10-08 06:52:43 +02:00
Carl Meyer
5d3a35e071 [ty] fix implicit Self on generic class with typevar default (#20754)
## Summary

Typevar attributes (bound/constraints/default) can be either lazily
evaluated or eagerly evaluated. Currently they are lazily evaluated for
PEP 695 typevars, and eager for legacy and synthetic typevars.
https://github.com/astral-sh/ruff/pull/20598 will make them lazy also
for legacy typevars, and the ecosystem report on that PR surfaced the
issue fixed here (because legacy typevars are much more common in the
ecosystem than PEP 695 typevars.)

Applying a transform to a typevar (normalization, materialization, or
mark-inferable) will reify all lazy attributes and create a new typevar
with eager attributes. In terms of Salsa identity, this transformed
typevar will be considered different from the original typevar, whether
or not the attributes were actually transformed.

In general, this is not a problem, since all typevars in a given generic
context will be transformed, or not, together.

The exception to this was implicit-self vs explicit Self annotations.
The typevar we created for implicit self was created initially using
inferable typevars, whereas an explicit Self annotation is initially
non-inferable, then transformed via mark-inferable when accessed as part
of a function signature. If the containing class (which becomes the
upper bound of `Self`) is generic, and has e.g. a lazily-evaluated
default, then the explicit-Self annotation will reify that default in
the upper bound, and the implicit-self would not, leading them to be
treated as different typevars, and causing us to fail to solve a call to
a method such as `def method(self) -> Self` correctly.

The fix here is to treat implicit-self more like explicit-Self,
initially creating it as non-inferable and then using the mark-inferable
transform on it. This is less efficient, but restores the invariant that
all typevars in a given generic context are transformed together, or
not, fixing the bug.

In the improved-constraint-solver work, the separation of typevars into
"inferable" and "non-inferable" is expected to disappear, along with the
mark-inferable transform, which would render both this bug and the fix
moot. So this fix is really just temporary until that lands.

There is a performance regression, but not a huge one: 1-2% on most
projects, 5% on one outlier. This seems acceptable, given that it should
be fully recovered by removing the mark-inferable transform.

## Test Plan

Added mdtests that failed before this change.
2025-10-08 01:38:24 +00:00
Alex Waygood
ff386b4797 [ty] Improve diagnostics for bad @overload definitions (#20745) 2025-10-07 21:52:57 +00:00
Dan Parizher
1bf4969c96 [ruff] Suppress diagnostic for f-string interpolations with debug text (RUF010) (#20525)
## Summary

Fixes #20519
2025-10-07 16:57:59 -04:00
liam
2be73e9afb [flake8-bugbear] Mark B905 and B912 fixes as unsafe (#20695)
Resolves https://github.com/astral-sh/ruff/issues/20694

This PR updates the `zip_without_explicit_strict` and
`map_without_explicit_strict` rules so their fixes are always marked
unsafe, following Brent's guidance that adding `strict=False` can
silently preserve buggy behaviour when inputs differ. The fix safety
docs now spell out that reasoning, the applicability drops to `Unsafe`,
and the snapshots were refreshed so Ruff clearly warns users before
applying the edit.
2025-10-07 16:55:56 -04:00
Amethyst Reese
7a347c4370 [ruff] update the release process documentation (#20752) 2025-10-07 13:18:48 -07:00
renovate[bot]
70b23a4fd0 Update actions/cache action to v4.3.0 (#20709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 19:56:21 +01:00
Amethyst Reese
beea8cdfec Bump 0.14.0 (#20751) 2025-10-07 11:05:47 -07:00
Douglas Creager
416e956fe0 [ty] Infer better specializations of unions with None (etc) (#20749)
This PR adds a specialization inference special case that lets us handle
the following examples better:

```py
def f[T](t: T | None) -> T: ...
def g[T](t: T | int | None) -> T | int: ...

def _(x: str | None):
    reveal_type(f(x))  # revealed: str (previously str | None)

def _(y: str | int | None):
    reveal_type(g(x))  # revealed: str | int (previously str | int | None)
```

We already have a special case for when the formal is a union where one
element is a typevar, but it maps the entire actual type to the typevar
(as you can see in the "previously" results above).

The new special case kicks in when the actual is also a union. Now, we
filter out any actual union elements that are already subtypes of the
formal, and only bind whatever types remain to the typevar. (The `|
None` pattern appears quite often in the ecosystem results, but it's
more general and works with any number of non-typevar union elements.)

The new constraint solver should handle this case as well, but it's
worth adding this heuristic now with the old solver because it
eliminates some false positives from the ecosystem report, and makes the
ecosystem report less noisy on the other constraint solver PRs.
2025-10-07 13:33:42 -04:00
Brent Westbrook
88c0ce3e38 Update default and latest Python versions for 3.14 (#20725)
Summary
--

Closes #19467 and also removes the warning about using Python 3.14
without
preview enabled.

I also bumped `PythonVersion::default` to 3.9 because it reaches EOL
this month,
but we could also defer that for now if we wanted.

The first three commits are related to the `latest` bump to 3.14; the
fourth commit
bumps the default to 3.10.

Note that this PR also bumps the default Python version for ty to 3.10
because
there was a test asserting that it stays in sync with
`ast::PythonVersion`.

Test Plan
--

Existing tests

I spot-checked the ecosystem report, and I believe these are all
expected. Inbits doesn't specify a target Python version, so I guess
we're applying the default. UP007, UP035, and UP045 all use the new
default value to emit new diagnostics.
2025-10-07 12:23:11 -04:00
Amethyst Reese
8fb29eafb8 [ruff] improve handling of intermixed comments inside from-imports (#20561)
Resolves a crash when attempting to format code like:

```
from x import (a as # whatever
b)
```

Reworks the way comments are associated with nodes when parsing modules,
so that all possible comment positions can be retained and reproduced during
formatting.

Overall follows Black's formatting style for multi-line import statements.

Fixes issue #19138
2025-10-07 08:14:09 -07:00
David Peter
23ebfe7777 [ty] Fix tiny mistake in protocol tests (#20743) 2025-10-07 11:58:35 +00:00
David Peter
f90d6466e0 [ty] Make infer_method_information less confusing (#20740)
## Summary

`infer_method_information` was previously calling
`ClassLiteral::to_class_type`, which uses the default-specialization of
a generic class. This specialized `ClassType` was later only used if the
class was non-generic, making the specialization irrelevant. The
implementation was still a bit confusing, so this PR proposes a way to
avoid turning the class literal into a `ClassType`.
2025-10-07 10:12:23 +00:00
Micha Reiser
15af4c0a34 Move --show-settings snapshots to separate files (#20741) 2025-10-07 11:42:38 +02:00
Renkai Ge
76f8e5b755 Refactor Rust lint test structure to use RuffTestFixture (#20689)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-07 11:28:00 +02:00
chiri
b66a3e7451 [refurb] Add fixes for FURB101, FURB103 (#20520)
## Summary

Part of `PTH-*` fixes:
https://github.com/astral-sh/ruff/pull/19404#issuecomment-3089639686

## Test Plan
`cargo nextest run furb`
2025-10-06 18:09:07 -04:00
Alex Waygood
70f51e9648 [ty] Print display of types when a property test fails (#20720) 2025-10-06 14:44:24 +01:00
Dan Parizher
9a29f7a339 [isort] Fix inserting required imports before future imports (I002) (#20676)
## Summary

Fixes #20674

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-06 13:40:36 +00:00
Nikolas Hearp
1c5666ce5d [RUF051] Ignore if else/elif block is present (#20705)
## Summary

Fixes #20700

`else` and `elif` blocks could previously be deleted when applying a fix
for this rule. If an `else` or `elif` branch is detected the rule will
not trigger. So now the rule will only flag if it is safe.
2025-10-06 08:02:27 -05:00
Alex Waygood
42b297bf44 [ty] Improve documentation for extra-paths and python config settings (#20717)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-06 12:20:00 +00:00
Alex Waygood
80b337669f [ty] Add --venv as an alias to --python (#20718) 2025-10-06 13:03:05 +01:00
Alex Waygood
1ce57edf33 [ty] Enforce that typing_extensions must come from a stdlib search path (#20715) 2025-10-06 11:43:34 +00:00
renovate[bot]
1f8a74b5c6 Update astral-sh/setup-uv action to v6.8.0 (#20710)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:37:03 +02:00
renovate[bot]
c895b29f23 Update dependency ruff to v0.13.3 (#20707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:26:22 +02:00
renovate[bot]
a9f4852956 Update docker/login-action action to v3.6.0 (#20712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:26:10 +02:00
renovate[bot]
c20f489484 Update dependency uuid to v13 (#20714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:25:48 +02:00
renovate[bot]
6ae7e7ba6b Update Swatinem/rust-cache action to v2.8.1 (#20708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:25:00 +02:00
renovate[bot]
c05b172266 Update taiki-e/install-action action to v2.62.21 (#20713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:23:24 +02:00
renovate[bot]
10d23bb5e1 Update CodSpeedHQ/action action to v4.1.0 (#20711) 2025-10-06 07:00:11 +01:00
Dan Parizher
2d44ad2f8f [ty] Fix playground crashes when accessing vendored files with leading slashes (#20661) 2025-10-04 12:40:37 +01:00
Ibraheem Ahmed
2ce3aba458 [ty] Use annotated parameters as type context (#20635)
## Summary

Use the type annotation of function parameters as bidirectional type
context when inferring the argument expression. For example, the
following example now type-checks:

```py
class TD(TypedDict):
    x: int

def f(_: TD): ...

f({ "x": 1 })
```

Part of https://github.com/astral-sh/ty/issues/168.
2025-10-03 17:14:51 -04:00
Douglas Creager
b83ac5e234 [ty] Clean up inherited generic contexts (#20647)
We add an `inherited_generic_context` to the constructors of a generic
class. That lets us infer specializations of the class when invoking the
constructor. The constructor might itself be generic, in which case we
have to merge the list of typevars that we are willing to infer in the
constructor call.

Before we did that by tracking the two (and their specializations)
separately, with distinct `Option` fields/parameters. This PR updates
our call binding logic such that any given function call has _one_
optional generic context that we're willing to infer a specialization
for. If needed, we use the existing `GenericContext::merge` method to
create a new combined generic context for when the class and constructor
are both generic. This simplifies the call binding code considerably,
and is no more complex in the constructor call logic.

We also have a heuristic that we will promote any literals in the
specialized types of a generic class, but we don't promote literals in
the specialized types of the function itself. To handle this, we now
track this `should_promote_literals` property within `GenericContext`.
And moreover, we track this separately for each typevar, instead of a
single property for the generic context as a whole, so that we can
correctly merge the generic context of a constructor method (where the
option should be `false`) with the inherited generic context of its
containing class (where the option should be `true`).

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-03 13:55:43 -04:00
Alex Waygood
c91b457044 [ty] Introduce TypeRelation::Redundancy (#20602)
## Summary

The union `T | U` can be validly simplified to `U` iff:
1. `T` is a subtype of `U` OR
2. `T` is equivalent to `U` OR
3. `U` is a union and contains a type that is equivalent to `T` OR
4. `T` is an intersection and contains a type that is equivalent to `U`

(In practice, the only situation in which 2, 3 or 4 would be true when
(1) was not true would be if `T` or `U` is a dynamic type.)

Currently we achieve these simplifications in the union builder by doing
something along the lines of `t.is_subtype_of(db, u) ||
t.is_equivalent_to_(db, u) ||
t.into_intersection().is_some_and(|intersection|
intersection.positive(db).contains(&u)) ||
u.into_union().is_some_and(|union| union.elements(db).contains(&t))`.
But this is both slow and misses some cases (it doesn't simplify the
union `Any | (Unknown & ~None)` to `Any`, for example). We can improve
the consistency and performance of our union simplifications by adding a
third type relation that sits in between `TypeRelation::Subtyping` and
`TypeRelation::Assignability`: `TypeRelation::UnionSimplification`.

This change leads to simpler, more user-friendly types due to the more
consistent simplification. It also lead to a pretty huge performance
improvement!

## Test Plan

Existing tests, plus some new ones.
2025-10-03 18:35:30 +01:00
Igor Drokin
673167a565 [flake8-bugbear] Include certain guaranteed-mutable expressions: tuples, generators, and assignment expressions (B006) (#20024)
## Summary
Resolves #20004

The implementation now supports guaranteed-mutable expressions in the
following cases:
- Tuple literals with mutable elements (supporting deep nesting)
- Generator expressions
- Named expressions (walrus operator) containing mutable components

Preserves original formatting for assignment value:

```python
# Test case
def f5(x=([1, ])):
    print(x)
```
```python
# Fix before
def f5(x=(None)):
    if x is None:
        x = [1]
    print(x)
```
```python
# Fix after 
def f5(x=None):
    if x is None:
        x = ([1, ])
    print(x)
```
The expansion of detected expressions and the new fixes gated behind
previews.

## Test Plan
- Added B006_9.py with a bunch of test cases
- Generated snapshots

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-10-03 09:29:36 -05:00
Dan Parizher
805d179dc0 [flake8-comprehensions] Clarify fix safety documentation (C413) (#20640)
## Summary

Fixes #20632
2025-10-03 09:23:57 -05:00
Daniel Kongsgaard
f73ead11cb [ty] improve base conda distinction from child conda (#20675)
<!--
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

#19990 didn't completely fix the base vs. child conda environment
distinction, since it detected slightly different behavior than what I
usually see in conda. E.g., I see something like the following:
```
(didn't yet activate conda, but base is active)
➜ printenv | grep CONDA
CONDA_PYTHON_EXE=/opt/anaconda3/bin/python
CONDA_PREFIX=/opt/anaconda3
CONDA_DEFAULT_ENV=base
CONDA_EXE=/opt/anaconda3/bin/conda
CONDA_SHLVL=1
CONDA_PROMPT_MODIFIER=(base)

(activating conda)
➜ conda activate test

(test is an active conda environment)
❯ printenv | grep CONDA
CONDA_PREFIX=/opt/anaconda3/envs/test
CONDA_PYTHON_EXE=/opt/anaconda3/bin/python
CONDA_SHLVL=2
CONDA_PREFIX_1=/opt/anaconda3
CONDA_DEFAULT_ENV=test
CONDA_PROMPT_MODIFIER=(test)
CONDA_EXE=/opt/anaconda3/bin/conda
```

But the current behavior looks for `CONDA_DEFAULT_ENV =
basename(CONDA_PREFIX)` for the base environment instead of the child
environment, where we actually see this equality.

This pull request fixes that and updates the tests correspondingly.

## Test Plan

I updated the existing tests with the new behavior. Let me know if you
want more tests. Note: It shouldn't be necessary to test for the case
where we have `conda/envs/base`, since one should not be able to create
such an environment (one with the name of `CONDA_DEFAULT_ENV`).

---------

Co-authored-by: Aria Desires <aria.desires@gmail.com>
2025-10-03 13:56:06 +00:00
liam
ebfb33c30b [ruff] Extend FA102 with listed PEP 585-compatible APIs (#20659)
Resolves https://github.com/astral-sh/ruff/issues/20512

This PR expands FA102’s preview coverage to flag every
PEP 585-compatible API that breaks without from `from __future__ import
annotations`, including `collections.abc`. The rule now treats asyncio
futures, pathlib-style queues, weakref containers, shelve proxies, and
the full `collections.abc` family as generics once preview mode is
enabled.

Stable behavior is unchanged; the broader matching runs behind
`is_future_required_preview_generics_enabled`, letting us vet the new
diagnostics before marking them as stable.

I've also added a snapshot test that covers all of the newly supported
types.

Check out
https://docs.python.org/3/library/stdtypes.html#standard-generic-classes
for a list of commonly used PEP 585-compatible APIs.
2025-10-03 09:45:32 -04:00
Takayuki Maeda
7d7237c660 [ruff] Handle argfile expansion errors gracefully (#20691)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #20655

- Guard `argfile::expand_args_from` with contextual error handling so
missing @file arguments surface a friendly failure instead of panicking.
- Extract existing stderr reporting into `report_error` for reuse on
both CLI parsing failures and runtime errors.

## Test Plan

<!-- How was it tested? -->

Add a regression test to integration_test.rs.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-03 13:36:07 +00:00
Takayuki Maeda
542f080035 [flynt] Fix f-string quoting for mixed quote joiners (FLY002) (#20662)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #19837

Track quote usage across the joiner and parts to choose a safe f-string
quote or skip the fix when both appear.

## Test Plan

<!-- How was it tested? -->

Add regression coverage to FLY002.py
2025-10-03 09:15:57 -04:00
Andrew Gallant
92eee816ed [ty] Fix file root matching for /
Previously, we would always add `/{*filepath}` as our wildcard to match
descendant paths. But when the root is just `/` (as it can be in tests,
weird environments or in the ty playground), this causes a double `/`
and inhibits most descendant matches.

The regression test added in this commit fails without this fix.
Specifically, it panics because it can't find a file root for
`/project`.

Fixes #1277
2025-10-03 08:18:03 -04:00
Andrew Gallant
00c0c567dc [ruff,ty] Enable tracing's log feature
This has the effect of emitting tracing events via `log`
whenever there isn't an active tracing subscriber present.

This makes it so `ty_wasm` logs tracing messages to the
JavaScript console automatically (via our use of `console_log`).
2025-10-03 08:18:03 -04:00
Dan Parizher
f9688bd05c [flake8-annotations] Fix return type annotations to handle shadowed builtin symbols (ANN201, ANN202, ANN204, ANN205, ANN206) (#20612)
## Summary

Fixes #20610

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-02 22:44:06 +00:00
Dylan
188c0dce29 Bump 0.13.3 (#20685) 2025-10-02 14:14:05 -05:00
Brent Westbrook
c9b2bcbfb2 Update benchmarking CI for cargo-codspeed v4 (#20686)
Summary
--

A new codspeed
[release](https://github.com/CodSpeedHQ/codspeed-rust/releases/tag/v4.0.0)
came out, and our CI was failing. It looks like the
previously-positional list of benchmarks now corresponds to a `--bench`
[flag](7159cf86b9/crates/cargo-codspeed/src/app.rs (L39-L43)).

Test Plan
--

CI on this PR

## TODO

- [x] Drop whitespace change commit, just wanted to make sure the
benchmarks ran
2025-10-02 18:47:15 +00:00
Dhruv Manilawala
4e94b22815 [ty] Support single-starred argument for overload call (#20223)
## Summary

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

This PR adds support for variadic arguments to overload call evaluation.

This basically boils down to making sure that the overloads are not
filtered out incorrectly during the step 5 in the overload call
evaluation algorithm. For context, the step 5 tries to filter out the
remaining overloads after finding an overload where the materialization
of argument types are assignable to the parameter types.

The issue with the previous implementation was that it wouldn't unpack
the variadic argument and wouldn't consider the many-to-one (multiple
arguments mapping to a single variadic parameter) correctly. This PR
fixes that.

## Test Plan

Update existing test cases and resolve the TODOs.
2025-10-02 10:41:56 -04:00
Alex Waygood
0639da2552 [ty] ~T should never be assignable to T (#20606)
## Summary

Currently we do not emit an error on this code:

```py
from ty_extensions import Not

def f[T](x: T, y: Not[T]) -> T:
    x = y
    return x
```

But we should do! `~T` should never be assignable to `T`.

This fixes a small regression introduced in
14fe1228e7 (diff-8049ab5af787dba29daa389bbe2b691560c15461ef536f122b1beab112a4b48aR1443-R1446),
where a branch that previously returned `false` was replaced with a
branch that returns `C::always_satisfiable` -- the opposite of what it
used to be! The regression occurred because we didn't have any tests for
this -- so I added some tests in this PR that fail on `main`. I only
spotted the problem because I was going through the code of
`has_relation_to_impl` with a fine toothcomb for
https://github.com/astral-sh/ruff/pull/20602 😄
2025-10-02 07:52:47 +01:00
Dan Parizher
caf48f4bfc [pylint] Clarify fix safety to include left-hand hashability (PLR6201) (#20518)
## Summary

Fixes #20510
2025-10-01 13:58:24 -04:00
David Peter
71d711257a [ty] No union with Unknown for module-global symbols (#20664)
## Summary

Quoting from the newly added comment:

Module-level globals can be mutated externally. A `MY_CONSTANT = 1`
global might be changed to `"some string"` from code outside of the
module that we're looking at, and so from a gradual-guarantee
perspective, it makes sense to infer a type of `Literal[1] | Unknown`
for global symbols. This allows the code that does the mutation to type
check correctly, and for code that uses the global, it accurately
reflects the lack of knowledge about the type.

External modifications (or modifications through `global` statements)
that would require a wider type are relatively rare. From a practical
perspective, we can therefore achieve a better user experience by
trusting the inferred type. Users who need the external mutation to work
can always annotate the global with the wider type. And everyone else
benefits from more precise type inference.

I initially implemented this by applying literal promotion to the type
of the unannotated module globals (as suggested in
https://github.com/astral-sh/ty/issues/1069), but the ecosystem impact
showed a lot of problems (https://github.com/astral-sh/ruff/pull/20643).
I fixed/patched some of these problems, but this PR seems like a good
first step, and it seems sensible to apply the literal promotion change
in a second step that can be evaluated separately.

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

## Ecosystem impact

This seems like an (unexpectedly large) net positive with 650 fewer
diagnostics overall.. even though this change will certainly catch more
true positives.

* There are 666 removed `type-assertion-failure` diagnostics, where we
were previously used the correct type already, but removing the
`Unknown` now leads to an "exact" match.
* 1464 of the 1805 total new diagnostics are `unresolved-attribute`
errors, most (1365) of which were previously
`possibly-missing-attribute` errors. So they could also be counted as
"changed" diagnostics.
* For code that uses constants like
  ```py
  IS_PYTHON_AT_LEAST_3_10 = sys.version_info >= (3, 10)
  ```
where we would have previously inferred a type of `Literal[True/False] |
Unknown`, removing the `Unknown` now allows us to do reachability
analysis on branches that use these constants, and so we get a lot of
favorable ecosystem changes because of that.
* There is code like the following, where we previously emitted
`conflicting-argument-forms` diagnostics on calls to the aliased
`assert_type`, because its type was `Unknown | def …` (and the call to
`Unknown` "used" the type form argument in a non type-form way):
  ```py
  if sys.version_info >= (3, 11):
      import typing
  
      assert_type = typing.assert_type
  else:
      import typing_extensions
  
      assert_type = typing_extensions.assert_type
  ```
* ~100 new `invalid-argument-type` false positives, due to missing
`**kwargs` support (https://github.com/astral-sh/ty/issues/247)

## Typing conformance

```diff
+protocols_modules.py:25:1: error[invalid-assignment] Object of type `<module '_protocols_modules1'>` is not assignable to `Options1`
```

This diagnostic should apparently not be there, but it looks like we
also fail other tests in that file, so it seems to be a limitation that
was previously hidden by `Unknown` somehow.

## Test Plan

Updated tests and relatively thorough ecosystem analysis.
2025-10-01 16:40:30 +02:00
Takayuki Maeda
eb34d12151 [ty] Reject renaming files to start with slash in Playground (#20666) 2025-10-01 15:54:28 +02:00
David Peter
56d630e303 [ty] Enums: allow multiple aliases to point to the same member (#20669)
## Summary

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

## Test Plan

Regression test
2025-10-01 15:51:53 +02:00
David Peter
963bc8c228 [ty] Reformulation of public symbol inference test suite (#20667)
## Summary

Reformulation of the public symbol type inference test suite to use
class scopes instead of module scopes. This is in preparation for an
upcoming change to module-global scopes (#20664).

## Test Plan

Updated tests
2025-10-01 14:26:17 +02:00
Alex Waygood
20eb5b5b35 [ty] Fix subtyping of invariant generics specialized with Any (#20650) 2025-10-01 10:05:54 +00:00
github-actions[bot]
d9473a2fcf [ty] Sync vendored typeshed stubs (#20658)
---------

Co-authored-by: typeshedbot <>
Co-authored-by: David Peter <mail@david-peter.de>
2025-10-01 10:11:48 +02:00
Douglas Creager
a422716267 [ty] Fix flaky constraint set rendering (#20653)
This doesn't seem to be flaky in the sense of tests failing
non-deterministically, but they are flaky in the sense of unrelated
changes causing testing failures from the clauses of a constraint set
being rendered in different orders. This flakiness is because we're
using Salsa IDs to determine the order in which typevars appear in a
constraint set BDD, and those IDs are assigned non-deterministically.

The fix is ham-fisted but effective: sort the constraints in each
clause, and the clauses in each set, as part of the rendering process.
Constraint sets are only rendered in our test cases, so we don't need to
over-optimize this.
2025-10-01 09:14:35 +02:00
David Peter
a3e5c72537 [ty] Use release mode for ecosystem report (#20663)
## Summary

Prevent the ecosystem report workflow from timing out by running ty in
release mode.

## Test Plan

Manual workflow run:
https://github.com/astral-sh/ruff/actions/runs/18154035186
2025-10-01 09:06:24 +02:00
Igor Drokin
11dae2cf1b [pyupgrade] Prevent infinite loop with I002 and UP026 (#20634)
## Summary
Closes #20601

Do not treat imports as unused for the rule [unnecessary-builtin-import
(UP029)](https://docs.astral.sh/ruff/rules/unnecessary-builtin-import/)
if they are required by
`isort`([missing-required-import](https://docs.astral.sh/ruff/rules/missing-required-import/))

## Test Plan
- Added test case `i002_up029_conflict` to ensure there is no conflict

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
2025-09-30 17:11:34 -04:00
Wei Lee
7fee877c50 [airflow]: rename AutoImport as Rename (internal) (#20563)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Since we are trying to import both `AutoImport` and `SourceModuleMoved`,
the previous naming was not as descriptive. Renaming it to `Rename`
better reflects the intention.

## Test Plan

<!-- How was it tested? -->

no functionality change
2025-09-30 15:56:26 -04:00
Dan Parizher
7c87b31533 [ruff] Do not flag %r + repr() combinations (RUF065) (#20600)
## Summary

Fixes the first part of #20583
2025-09-30 15:49:50 -04:00
Brent Westbrook
2b1d3c60fa Display diffs for ruff format --check and add support for different output formats (#20443)
## Summary

This PR uses the new `Diagnostic` type for rendering formatter
diagnostics. This allows the formatter to inherit all of the output
formats already implemented in the linter and ty. For example, here's
the new `full` output format, with the formatting diff displayed using
the same infrastructure as the linter:

<img width="592" height="364" alt="image"
src="https://github.com/user-attachments/assets/6d09817d-3f27-4960-aa8b-41ba47fb4dc0"
/>


<details><summary>Resolved TODOs</summary>
<p>

~~There are several limitiations/todos here still, especially around the
`OutputFormat` type~~:
- [x] A few literal `todo!`s for the remaining `OutputFormat`s without
matching `DiagnosticFormat`s
- [x] The default output format is `full` instead of something more
concise like the current output
- [x] Some of the output formats (namely JSON) have information that
doesn't make much sense for these diagnostics

The first of these is definitely resolved, and I think the other two are
as well, based on discussion on the design document. In brief, we're
okay inheriting the default `OutputFormat` and can separate the global
option into `lint.output-format` and `format.output-format` in the
future, if needed; and we're okay including redundant information in the
non-human-readable output formats.

My last major concern is with the performance of the new code, as
discussed in the `Benchmarks` section below.

A smaller question is whether we should use `Diagnostic`s for formatting
errors too. I think the answer to this is yes, in line with changes
we're making in the linter too. I still need to implement that here.

</p>
</details> 

<details><summary>Benchmarks</summary>
<p>


The values in the table are from a large benchmark on the CPython 3.10
code
base, which involves checking 2011 files, 1872 of which need to be
reformatted.
`stable` corresponds to the same code used on `main`, while
`preview-full` and
`preview-concise` use the new `Diagnostic` code gated behind `--preview`
for the
`full` and `concise` output formats, respectively. `stable-diff` uses
the
`--diff` to compare the two diff rendering approaches. See the full
hyperfine
command below for more details. For a sense of scale, the `stable`
output format
produces 1873 lines on stdout, compared to 855,278 for `preview-full`
and
857,798 for `stable-diff`.

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |

|:------------------|--------------:|---------:|---------:|-------------:|
| `stable` | 201.2 ± 6.8 | 192.9 | 220.6 | 1.00 |
| `preview-full` | 9113.2 ± 31.2 | 9076.1 | 9152.0 | 45.29 ± 1.54 |
| `preview-concise` | 214.2 ± 1.4 | 212.0 | 217.6 | 1.06 ± 0.04 |
| `stable-diff` | 3308.6 ± 20.2 | 3278.6 | 3341.8 | 16.44 ± 0.56 |

In summary, the `preview-concise` diagnostics are ~6% slower than the
stable
output format, increasing the average runtime from 201.2 ms to 214.2 ms.
The
`full` preview diagnostics are much more expensive, taking over 9113.2
ms to
complete, which is ~3x more expensive even than the stable diffs
produced by the
`--diff` flag.

My main takeaways here are:
1. Rendering `Edit`s is much more expensive than rendering the diffs
from `--diff`
2. Constructing `Edit`s actually isn't too bad

### Constructing `Edit`s

I also took a closer look at `Edit` construction by modifying the code
and
repeating the `preview-concise` benchmark and found that the main issue
is
constructing a `SourceFile` for use in the `Edit` rendering. Commenting
out the
`Edit` construction itself has basically no effect:

| Command   |   Mean [ms] | Min [ms] | Max [ms] |    Relative |
|:----------|------------:|---------:|---------:|------------:|
| `stable`  | 197.5 ± 1.6 |    195.0 |    200.3 |        1.00 |
| `no-edit` | 208.9 ± 2.2 |    204.8 |    212.2 | 1.06 ± 0.01 |

However, also omitting the source text from the `SourceFile`
construction
resolves the slowdown compared to `stable`. So it seems that copying the
full
source text into a `SourceFile` is the main cause of the slowdown for
non-`full`
diagnostics.

| Command          |   Mean [ms] | Min [ms] | Max [ms] |    Relative |
|:-----------------|------------:|---------:|---------:|------------:|
| `stable`         | 202.4 ± 2.9 |    197.6 |    207.9 |        1.00 |
| `no-source-text` | 202.7 ± 3.3 |    196.3 |    209.1 | 1.00 ± 0.02 |

### Rendering diffs

The main difference between `stable-diff` and `preview-full` seems to be
the diffing strategy we use from `similar`. Both versions use the same
algorithm, but in the existing
[`CodeDiff`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_linter/src/source_kind.rs#L259)
rendering for the `--diff` flag, we only do line-level diffing, whereas
for `Diagnostic`s we use `TextDiff::iter_inline_changes` to highlight
word-level changes too. Skipping the word diff for `Diagnostic`s closes
most of the gap:

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `stable-diff` | 3.323 ± 0.015 | 3.297 | 3.341 | 1.00 |
| `preview-full` | 3.654 ± 0.019 | 3.618 | 3.682 | 1.10 ± 0.01 |

(In some repeated runs, I've seen as small as a ~5% difference, down
from 10% in the table)

This doesn't actually change any of our snapshots, but it would
obviously change the rendered result in a terminal since we wouldn't
highlight the specific words that changed within a line.

Another much smaller change that we can try is removing the deadline
from the `iter_inline_changes` call. It looks like there's a fair amount
of overhead from the default 500 ms deadline for computing these, and
using `iter_inline_changes(op, None)` (`None` for the optional deadline
argument) improves the runtime quite a bit:

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `stable-diff` | 3.322 ± 0.013 | 3.298 | 3.341 | 1.00 |
| `preview-full` | 5.296 ± 0.030 | 5.251 | 5.366 | 1.59 ± 0.01 |

<hr>

<details><summary>hyperfine command</summary>

```shell
cargo build --release --bin ruff && hyperfine --ignore-failure --warmup 10 --export-markdown /tmp/table.md \
  -n stable -n preview-full -n preview-concise -n stable-diff \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache" \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache --preview --output-format=full" \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache --preview --output-format=concise" \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache --diff"
```

</details>

</p>
</details> 

## Test Plan

Some new CLI tests and manual testing
2025-09-30 12:00:51 -04:00
David Peter
b483d3b0b9 [ty] Literal promotion refactor (#20646)
## Summary

Not sure if this was the original intention, but it looks to me like the
previous `Type::literal_promotion_type` was more of an implementation
detail for the actual operation of promoting all literals in a
possibly-nested position of a type.

This is not a pure refactor, as I'm technically changing the behavior
for that protocols diagnostic message suggestion.

## Test Plan

New Markdown test
2025-09-30 14:22:36 +02:00
David Peter
130a794c2b [ty] Add tests for nested generic functions (#20631)
## Summary

Add two simple tests that we recently discussed with @dcreager. They
demonstrate that the `TypeMapping::MarkTypeVarsInferable` operation
really does need to keep track of the binding context.

## Test Plan

Made sure that those tests fail if we create
`TypeMapping::MarkTypeVarsInferable(None)`s everywhere.
2025-09-30 08:44:18 +02:00
Dan Parizher
1c08f71a00 [cli] Add conflict between --add-noqa and --diff options (#20642) 2025-09-30 08:34:18 +02:00
Alex Waygood
8664842d00 [ty] Ensure first-party search paths always appear in a sensible order (#20629)
This PR ensures that we always put `./src` before `.` in our list of
first-party search paths. This better emulates the fact that at runtime,
the module name of a file `src/foo.py` would almost certainly be `foo`
rather than `src.foo`.

I wondered if fixing this might fix
https://github.com/astral-sh/ruff/pull/20603#issuecomment-3345317444. It
seems like that's not the case, but it also seems like it leads to
better diagnostics because we report much more intuitive module names to
the user in our error messages -- so, it's probably a good change
anyway.
2025-09-29 21:19:13 +01:00
David Peter
0092794302 [ty] Use typing.Self for the first parameter of instance methods (#20517)
## Summary

Modify the (external) signature of instance methods such that the first
parameter uses `Self` unless it is explicitly annotated. This allows us
to correctly type-check more code, and allows us to infer correct return
types for many functions that return `Self`. For example:

```py
from pathlib import Path
from datetime import datetime, timedelta

reveal_type(Path(".config") / ".ty")  # now Path, previously Unknown

def _(dt: datetime, delta: timedelta):
    reveal_type(dt - delta)  # now datetime, previously Unknown
```

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

## Performance

I ran benchmarks locally on `attrs`, `freqtrade` and `colour`, the
projects with the largest regressions on CodSpeed. I see much smaller
effects locally, but can definitely reproduce the regression on `attrs`.
From looking at the profiling results (on Codspeed), it seems that we
simply do more type inference work, which seems plausible, given that we
now understand much more return types (of many stdlib functions). In
particular, whenever a function uses an implicit `self` and returns
`Self` (without mentioning `Self` anywhere else in its signature), we
will now infer the correct type, whereas we would previously return
`Unknown`. This also means that we need to invoke the generics solver in
more cases. Comparing half a million lines of log output on attrs, I can
see that we do 5% more "work" (number of lines in the log), and have a
lot more `apply_specialization` events (7108 vs 4304). On freqtrade, I
see similar numbers for `apply_specialization` (11360 vs 5138 calls).
Given these results, I'm not sure if it's generally worth doing more
performance work, especially since none of the code modifications
themselves seem to be likely candidates for regressions.

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/attrs` | 92.6 ± 3.6 | 85.9 |
102.6 | 1.00 |
| `./ty_self check /home/shark/ecosystem/attrs` | 101.7 ± 3.5 | 96.9 |
113.8 | 1.10 ± 0.06 |

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/freqtrade` | 599.0 ± 20.2 |
568.2 | 627.5 | 1.00 |
| `./ty_self check /home/shark/ecosystem/freqtrade` | 607.9 ± 11.5 |
594.9 | 626.4 | 1.01 ± 0.04 |

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/colour` | 423.9 ± 17.9 | 394.6
| 447.4 | 1.00 |
| `./ty_self check /home/shark/ecosystem/colour` | 426.9 ± 24.9 | 373.8
| 456.6 | 1.01 ± 0.07 |

## Test Plan

New Markdown tests

## Ecosystem report

* apprise: ~300 new diagnostics related to problematic stubs in apprise
😩
* attrs: a new true positive, since [this
function](4e2c89c823/tests/test_make.py (L2135))
is missing a `@staticmethod`?
* Some legitimate true positives
* sympy: lots of new `invalid-operator` false positives in [matrix
multiplication](cf9f4b6805/sympy/matrices/matrixbase.py (L3267-L3269))
due to our limited understanding of [generic `Callable[[Callable[[T1,
T2], T3]], Callable[[T1, T2], T3]]` "identity"
types](cf9f4b6805/sympy/core/decorators.py (L83-L84))
of decorators. This is not related to type-of-self.

## Typing conformance results

The changes are all correct, except for
```diff
+generics_self_usage.py:50:5: error[invalid-assignment] Object of type `def foo(self) -> int` is not assignable to `(typing.Self, /) -> int`
```
which is related to an assignability problem involving type variables on
both sides:
```py
class CallableAttribute:
    def foo(self) -> int:
        return 0

    bar: Callable[[Self], int] = foo  # <- we currently error on this assignment
```

---------

Co-authored-by: Shaygan Hooshyari <sh.hooshyari@gmail.com>
2025-09-29 21:08:08 +02:00
Alex Waygood
1d3e4a9153 [ty] Remove unnecessary parsed_module() calls (#20630) 2025-09-29 16:05:12 +01:00
Brent Westbrook
00c8851ef8 Remove TextEmitter (#20595)
## Summary

Addresses
https://github.com/astral-sh/ruff/pull/20443#discussion_r2381237640 by
factoring out the `match` on the ruff output format in a way that should
be reusable by the formatter.

I didn't think this was going to work at first, but the fact that the
config holds options that apply only to certain output formats works in
our favor here. We can set up a single config for all of the output
formats and then use `try_from` to convert the `OutputFormat` to a
`DiagnosticFormat` later.

## Test Plan

Existing tests, plus a few new ones to make sure relocating the
`SHOW_FIX_SUMMARY` rendering worked, that was untested before. I deleted
a bunch of test code along with the `text` module, but I believe all of
it is now well-covered by the `full` and `concise` tests in `ruff_db`.

I also merged this branch into
https://github.com/astral-sh/ruff/pull/20443 locally and made sure that
the API actually helps. `render_diagnostics` dropped in perfectly and
passed the tests there too.
2025-09-29 08:46:25 -04:00
Alex Waygood
1cf19732b9 [ty] Use fully qualified names to distinguish ambiguous protocols in diagnostics (#20627) 2025-09-29 12:02:07 +00:00
David Peter
803d61e21f [ty] Ecosystem analyzer: relax timeout thresholds (#20626)
## Summary

Pull in a small upstream change
(6ce3a60957),
because some type check times were close to the previous limits, which
prevents us from seeing diagnostics diffs (in case they run into a
timeout).
2025-09-29 11:36:14 +00:00
Douglas Creager
cf2b083668 [ty] Apply type mappings to functions eagerly (#20596)
`TypeMapping` is no longer cow-shaped.

Before, `TypeMapping` defined a `to_owned` method, which would make an
owned copy of the type mapping. This let us apply type mappings to
function literals lazily. The primary part of a function that you have
to apply the type mapping to is its signature. The hypothesis was that
doing this lazily would prevent us from constructing the signature of a
function just to apply a type mapping; if you never ended up needed the
updated function signature, that would be extraneous work.

But looking at the CI for this PR, it looks like that hypothesis is
wrong! And this definitely cleans up the code quite a bit. It also means
that over time we can consider replacing all of these `TypeMapping` enum
variants with separate `TypeTransformer` impls.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-29 13:24:40 +02:00
Alex Waygood
3f640dacd4 [ty] Improve disambiguation of class names in diagnostics (#20603) 2025-09-29 11:43:11 +01:00
Micha Reiser
81f43a1fc8 Add the *The Basics* title back to CONTRIBUTING.md (#20624) 2025-09-29 07:46:01 +00:00
Dan Parizher
053c750c93 [playground] Fix quick fixes for empty ranges in playground (#20599)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-29 07:38:32 +00:00
renovate[bot]
e5faf6c268 Update dependency ruff to v0.13.2 (#20622)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [ruff](https://docs.astral.sh/ruff)
([source](https://redirect.github.com/astral-sh/ruff),
[changelog](https://redirect.github.com/astral-sh/ruff/blob/main/CHANGELOG.md))
| `==0.13.1` -> `==0.13.2` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.13.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.13.1/0.13.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/ruff (ruff)</summary>

###
[`v0.13.2`](https://redirect.github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#0132)

[Compare
Source](https://redirect.github.com/astral-sh/ruff/compare/0.13.1...0.13.2)

Released on 2025-09-25.

##### Preview features

- \[`flake8-async`] Implement `blocking-path-method` (`ASYNC240`)
([#&#8203;20264](https://redirect.github.com/astral-sh/ruff/pull/20264))
- \[`flake8-bugbear`] Implement `map-without-explicit-strict` (`B912`)
([#&#8203;20429](https://redirect.github.com/astral-sh/ruff/pull/20429))
- \[`flake8-bultins`] Detect class-scope builtin shadowing in
decorators, default args, and attribute initializers (`A003`)
([#&#8203;20178](https://redirect.github.com/astral-sh/ruff/pull/20178))
- \[`ruff`] Implement `logging-eager-conversion` (`RUF065`)
([#&#8203;19942](https://redirect.github.com/astral-sh/ruff/pull/19942))
- Include `.pyw` files by default when linting and formatting
([#&#8203;20458](https://redirect.github.com/astral-sh/ruff/pull/20458))

##### Bug fixes

- Deduplicate input paths
([#&#8203;20105](https://redirect.github.com/astral-sh/ruff/pull/20105))
- \[`flake8-comprehensions`] Preserve trailing commas for single-element
lists (`C409`)
([#&#8203;19571](https://redirect.github.com/astral-sh/ruff/pull/19571))
- \[`flake8-pyi`] Avoid syntax error from conflict with `PIE790`
(`PYI021`)
([#&#8203;20010](https://redirect.github.com/astral-sh/ruff/pull/20010))
- \[`flake8-simplify`] Correct fix for positive `maxsplit` without
separator (`SIM905`)
([#&#8203;20056](https://redirect.github.com/astral-sh/ruff/pull/20056))
- \[`pyupgrade`] Fix `UP008` not to apply when `__class__` is a local
variable
([#&#8203;20497](https://redirect.github.com/astral-sh/ruff/pull/20497))
- \[`ruff`] Fix `B004` to skip invalid `hasattr`/`getattr` calls
([#&#8203;20486](https://redirect.github.com/astral-sh/ruff/pull/20486))
- \[`ruff`] Replace `-nan` with `nan` when using the value to construct
a `Decimal` (`FURB164` )
([#&#8203;20391](https://redirect.github.com/astral-sh/ruff/pull/20391))

##### Documentation

- Add 'Finding ways to help' to CONTRIBUTING.md
([#&#8203;20567](https://redirect.github.com/astral-sh/ruff/pull/20567))
- Update import path to `ruff-wasm-web`
([#&#8203;20539](https://redirect.github.com/astral-sh/ruff/pull/20539))
- \[`flake8-bandit`] Clarify the supported hashing functions (`S324`)
([#&#8203;20534](https://redirect.github.com/astral-sh/ruff/pull/20534))

##### Other changes

- \[`playground`] Allow hover quick fixes to appear for overlapping
diagnostics
([#&#8203;20527](https://redirect.github.com/astral-sh/ruff/pull/20527))
- \[`playground`] Fix non‑BMP code point handling in quick fixes and
markers
([#&#8203;20526](https://redirect.github.com/astral-sh/ruff/pull/20526))

##### Contributors

- [@&#8203;BurntSushi](https://redirect.github.com/BurntSushi)
- [@&#8203;mtshiba](https://redirect.github.com/mtshiba)
- [@&#8203;second-ed](https://redirect.github.com/second-ed)
- [@&#8203;danparizher](https://redirect.github.com/danparizher)
- [@&#8203;ShikChen](https://redirect.github.com/ShikChen)
- [@&#8203;PieterCK](https://redirect.github.com/PieterCK)
- [@&#8203;GDYendell](https://redirect.github.com/GDYendell)
- [@&#8203;RazerM](https://redirect.github.com/RazerM)
- [@&#8203;TaKO8Ki](https://redirect.github.com/TaKO8Ki)
- [@&#8203;amyreese](https://redirect.github.com/amyreese)
- [@&#8203;ntbre](https://redirect.github.com/ntBre)
- [@&#8203;MichaReiser](https://redirect.github.com/MichaReiser)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMzEuOSIsInVwZGF0ZWRJblZlciI6IjQxLjEzMS45IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 08:59:50 +02:00
Takayuki Maeda
666f53331f [ruff] Fix minor typos in doc comments (#20623) 2025-09-29 08:56:23 +02:00
renovate[bot]
65e805de62 Update dependency PyYAML to v6.0.3 (#20621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 08:55:39 +02:00
renovate[bot]
256b520a52 Update cargo-bins/cargo-binstall action to v1.15.6 (#20620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 08:55:13 +02:00
Rahul Sahoo
4e33501115 Fixed documentation for try_consider_else (#20587)
<!--
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

This PR addresses #20570 . In the example, the correct usage had a
bug/issue where in the except block after logging exception, None was
getting returned, which made the linters flag out the code. So adding an
empty raise solves the issue.

## Test Plan

Tested it by building the doc locally.
2025-09-27 13:50:51 +00:00
Alex Waygood
6b3c493cff [ty] Use Top materializations for TypeIs special form (#20591) 2025-09-26 17:24:43 +00:00
Alex Waygood
e4de179cdd [ty] Simplify Any | (Any & T) to Any (#20593) 2025-09-26 17:00:10 +01:00
Dylan
57e1ff8294 [pyflakes] Handle some common submodule import situations for unused-import (F401) (#20200)
# Summary

The PR under review attempts to make progress towards the age-old
problem of submodule imports, specifically with regards to their
treatment by the rule [`unused-import`
(`F401`)](https://docs.astral.sh/ruff/rules/unused-import/).

Some related issues:
- https://github.com/astral-sh/ruff/issues/60
- https://github.com/astral-sh/ruff/issues/4656

Prior art:
- https://github.com/astral-sh/ruff/pull/13965
- https://github.com/astral-sh/ruff/pull/5010
- https://github.com/astral-sh/ruff/pull/5011
- https://github.com/astral-sh/ruff/pull/666

See the PR summary for a detailed description.
2025-09-26 08:22:26 -05:00
David Peter
3932f7c849 [ty] Fix subtyping for dynamic specializations (#20592)
## Summary

Fixes a bug observed by @AlexWaygood where `C[Any] <: C[object]` should
hold for a class that is covariant in its type parameter (and similar
subtyping relations involving dynamic types for other variance
configurations).

## Test Plan

New and updated Markdown tests
2025-09-26 15:05:03 +02:00
Alex Waygood
2af8c53110 [ty] Add more tests for subtyping/assignability between two protocol types (#20573) 2025-09-26 12:07:57 +01:00
Dan Parizher
0bae7e613d Use Annotation::tags instead of hardcoded rule matching in ruff server (#20565)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-26 09:06:26 +02:00
Douglas Creager
02ebb2ee61 [ty] Change to BDD representation for constraint sets (#20533)
While working on #20093, I kept running into test failures due to
constraint sets not simplifying as much as they could, and therefore not
being easily testable against "always true" and "always false".

This PR updates our constraint set representation to use BDDs. Because
BDDs are reduced and ordered, they are canonical — equivalent boolean
formulas are represented by the same interned BDD node.

That said, there is a wrinkle, in that the "variables" that we use in
these BDDs — the individual constraints like `Lower ≤ T ≤ Upper` are not
always independent of each other.

As an example, given types `A ≤ B ≤ C ≤ D` and a typevar `T`, the
constraints `A ≤ T ≤ C` and `B ≤ T ≤ D` "overlap" — their intersection
is non-empty. So we should be able to simplify

```
(A ≤ T ≤ C) ∧ (B ≤ T ≤ D) == (B ≤ T ≤ C)
```

That's not a simplification that the BDD structure can perform itself,
since those three constraints are modeled as separate BDD variables, and
are therefore "opaque" to the BDD algorithms.

That means we need to perform this kind of simplification ourselves. We
look at pairs of constraints that appear in a BDD and see if they can be
simplified relative to each other, and if so, replace the pair with the
simplification. A large part of the toil of getting this PR to work was
identifying all of those patterns and getting that substitution logic
correct.

With this new representation, all existing tests pass, as well as some
new ones that represent test failures that were occuring on #20093.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-25 21:55:35 -04:00
Francesco Giacometti
e66a872c14 [ty] Coalesce allocations for parameter info in ArgumentMatcher (#20586)
<!--
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

Follow up on #20495. The improvement suggested by @AlexWaygood cannot be
applied as-is since the `argument_matches` vector is indexed by argument
number, while the two boolean vectors are indexed by parameter number.
Still coalescing the latter two saves one allocation.
2025-09-25 20:56:59 -04:00
Dan Parizher
589a674a8d [isort] Fix infinite loop when checking equivalent imports (I002, PLR0402) (#20381)
## Summary

Fixes #20380

The fix exempts required imports from `PLR0402`
2025-09-25 16:08:15 -05:00
Brent Westbrook
e4ac9e9041 Replace two more uses of unsafe with const Option::unwrap (#20584)
I guess I missed these in #20007, but I found them today while grepping
for something else. `Option::unwrap` has been const since 1.83, so we
can use it here and avoid some unsafe code.
2025-09-25 15:35:13 -04:00
Dylan
f2b7c82534 Handle t-string prefixes in SimpleTokenizer (#20578)
The simple tokenizer is meant to skip strings, but it was recording a
`Name` token for t-strings (from the `t`). This PR fixes that.
2025-09-25 14:33:37 -05:00
Bhuminjay Soni
cfc64d1707 [syntax-errors]: future-feature-not-defined (F407) (#20554)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

This PR implements
https://docs.astral.sh/ruff/rules/future-feature-not-defined/ (F407) as
a semantic syntax error.

## Test Plan

<!-- How was it tested? -->

I have written inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
2025-09-25 13:52:24 -04:00
Brent Westbrook
6b7a9dc2f2 [isort] Clarify dependency between order-by-type and case-sensitive settings (#20559)
Summary
--

Fixes #20536 by linking between the isort options `case-sensitive` and
`order-by-type`. The latter takes precedence over the former, so it
seems good to clarify this somewhere.

I tweaked the wording slightly, but this is otherwise based on the patch
from @SkylerWittman in
https://github.com/astral-sh/ruff/issues/20536#issuecomment-3326097324
(thank you!)

Test Plan
--

N/a

---------

Co-authored-by: Skyler Wittman <skyler.wittman@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-25 16:25:12 +00:00
Brent Westbrook
9903104328 [pylint] Fix missing max-nested-blocks in settings display (#20574)
Summary
--

This fixes a bug pointed out in #20560 where one of the `pylint`
settings wasn't used in its `Display` implementation.

Test Plan
--

Existing tests with updated snapshots
2025-09-25 12:14:28 -04:00
Giovani Moutinho
beec2f2dbb [flake8-simplify] Improve help message clarity (SIM105) (#20548)
## Summary

Improve the SIM105 rule message to prevent user confusion about how to
properly use `contextlib.suppress`.

The previous message "Replace with `contextlib.suppress(ValueError)`"
was ambiguous and led users to incorrectly use
`contextlib.suppress(ValueError)` as a statement inside except blocks
instead of replacing the entire try-except-pass block with `with
contextlib.suppress(ValueError):`.

This change makes the message more explicit:
- **Before**: `"Use \`contextlib.suppress({exception})\` instead of
\`try\`-\`except\`-\`pass\`"`
- **After**: `"Replace \`try\`-\`except\`-\`pass\` block with \`with
contextlib.suppress({exception})\`"`

The fix title is also updated to be more specific:
- **Before**: `"Replace with \`contextlib.suppress({exception})\`"`  
- **After**: `"Replace \`try\`-\`except\`-\`pass\` with \`with
contextlib.suppress({exception})\`"`

Fixes #20462

## Test Plan

-  All existing SIM105 tests pass with updated snapshots
-  Cargo clippy passes without warnings  
-  Full test suite passes
-  The new messages clearly indicate that the entire try-except-pass
block should be replaced with a `with` statement, preventing the misuse
described in the issue

---------

Co-authored-by: Giovani Moutinho <e@mgiovani.dev>
2025-09-25 11:19:26 -04:00
Micha Reiser
c256c7943c [ty] Update salsa to fix hang when cycle head panics (#20577) 2025-09-25 17:13:07 +02:00
Dhruv Manilawala
35ed55ec8c [ty] Filter overloads using variadic parameters (#20547)
## Summary

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

This PR adds support for step 4 of the overload call evaluation
algorithm which states that:

> If the argument list is compatible with two or more overloads,
determine whether one or more of the overloads has a variadic parameter
(either `*args` or `**kwargs`) that maps to a corresponding argument
that supplies an indeterminate number of positional or keyword
arguments. If so, eliminate overloads that do not have a variadic
parameter.

And, with that, the overload call evaluation algorithm has been
implemented completely end to end as stated in the typing spec.

## Test Plan

Expand the overload call test suite.
2025-09-25 14:58:00 +00:00
Brent Westbrook
b0bdf0334e Bump 0.13.2 (#20576) 2025-09-25 10:37:46 -04:00
Brent Westbrook
7331d393c5 Update rooster to 0.1.0 (#20575)
Summary
--

This reduces the page size of GraphQL queries
(https://github.com/zanieb/rooster/pull/85), hopefully helping with some
of the 502s we've been hitting.

Test Plan
--

I ran the release script, and it succeeded after failing several times
on the old rooster version.
2025-09-25 10:11:21 -04:00
David Peter
529e5fa6c2 [ty] Ecosystem analyzer: timing report (#20571)
## Summary

Generate a timing diff across the whole ecosystem and deploy it to
CloudFlare pages. The timing information is collected already, we just
need to create and upload the HTML report.

The timing results are just based on a single run. No statistical
analysis across multiple runs or similar is performed. This means that
results can be noisy, as can be seen on this PR, where we see slowdowns
up to 1.26× and speedups down to 0.89×, even though the change should be
neutral. Across all projects, these random events cancel out and we see
an average factor of 1.01×. So I think this feature can still be
interesting, given that it comes "for free". We just need to keep in
mind that it will be noisy, and shouldn't read too much into these
results.

## Test Plan

CI run on this PR (see the new *timing results* link).
2025-09-25 14:14:20 +02:00
David Peter
efbb80f747 [ty] Remove hack in protocol satisfiability check (#20568)
## Summary

This removes a hack in the protocol satisfiability check that was
previously needed to work around missing assignability-modeling of
inferable type variables. Assignability of type variables is not
implemented fully, but some recent changes allow us to remove that hack
with limited impact on the ecosystem (and the test suite). The change in
the typing conformance test is favorable.

## Test Plan

* Adapted Markdown tests
* Made sure that this change works in combination with
https://github.com/astral-sh/ruff/pull/20517
2025-09-25 13:35:47 +02:00
Micha Reiser
9f3cffc65c Add 'Finding ways to help' to CONTRIBUTING.md (#20567)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-25 12:46:41 +02:00
Alex Waygood
21be94ac33 [ty] Explicitly test assignability/subtyping between unions of nominal types and protocols with method members (#20557) 2025-09-25 09:21:29 +00:00
Alex Waygood
b7d5dc98c1 [ty] Add tests for interactions of @classmethod, @staticmethod, and protocol method members (#20555) 2025-09-25 10:14:53 +01:00
Dhruv Manilawala
e1bb74b25a [ty] Match variadic argument to variadic parameter (#20511)
## Summary

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

This PR fixes a bug where the variadic argument wouldn't match against
the variadic parameter in certain scenarios.

This was happening because I didn't realize that the `all_elements`
iterator wouldn't keep on returning the variable element (which is
correct, I just didn't realize it back then).

I don't think we can use the `resize` method here because we don't know
how many parameters this variadic argument is matching against as this
is where the actual parameter matching occurs.

## Test Plan

Expand test cases to consider a few more combinations of arguments and
parameters which are variadic.
2025-09-25 07:51:56 +00:00
Aria Desires
edeb45804e [ty] fallback to resolve_real_module in file_to_module (#20461)
This is a naive(?) implementation of the approach @MichaReiser
originally suggested to me in https://github.com/astral-sh/ty/issues/869

Fixes https://github.com/astral-sh/ty/issues/869
Fixes https://github.com/astral-sh/ty/issues/1195
2025-09-24 21:15:35 -04:00
Ibraheem Ahmed
bea92c8229 [ty] More precise type inference for dictionary literals (#20523)
## Summary

Extends https://github.com/astral-sh/ruff/pull/20360 to dictionary
literals. This also improves our `TypeDict` support by passing through
nested type context.
2025-09-24 18:12:00 -04:00
Ed Cuss
f2cc2f604f [flake8-pyi] Avoid syntax error from conflict with PIE790 (PYI021) (#20010)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->
First contribution so please let me know if I've made a mistake
anywhere. This was aimed to fix #19982, it adds the isolation level to
PYI021 to in the same style as the PIE790 rule.

fixes: #19982

## Test Plan

<!-- How was it tested? -->
I added a case to the PYI021.pyi file where the two rules are present as
there wasn't a case with them both interacting, using the minimal
reproducible example that @ntBre created on the issue (I think I got the
`# ERROR` markings wrong, so please let me know how to fix that if I
did).

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-24 21:26:59 +00:00
Dan Parizher
c361e2f759 [flake8-bandit] Clarify the supported hashing functions (S324) (#20534)
## Summary

Fixes #16572

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-24 20:10:23 +00:00
Alex Waygood
0e83af0b80 Bump mypy_primer pin (#20558) 2025-09-24 19:45:47 +00:00
Bhuminjay Soni
e6073d0cca [syntax-errors]: multiple-starred-expressions (F622) (#20243)
<!--
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

This PR implements
https://docs.astral.sh/ruff/rules/multiple-starred-expressions/ as a
semantic syntax error

## Test Plan

 I have added inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-24 19:32:55 +00:00
Brent Westbrook
73b4b1ed17 [ty] Make FileResolver::path return a full path (#20550)
## Summary

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

From finding references with the LSP, `FileResolver::path` is only
called once, in `UnifiedFile::path`, so I went through those references,
and it looked safe to make this change in every case. Most of the
references are in the various output formats, where we inherited the
absolute vs relative path decision from Ruff. Two other uses are as
fallbacks if converting a relativized path to a string fails. Finally,
we use the path for sorting and in `UnifiedFile::relative_path`.

## Test Plan

Existing tests, with snapshots updated to show absolute paths (in the
`TestDb` this just added a `/` in front of the file names). I also
updated the GitLab CLI test to set the `CI_PROJECT_DIR` environment
variable and ran a test in GitLab CI:

<img width="613" height="114" alt="image"
src="https://github.com/user-attachments/assets/8ab81dba-54fd-4a24-9110-77ef89293cff"
/>
2025-09-24 13:16:51 -04:00
Amethyst Reese
83f80effec include .pyw files by default when linting and formatting (#20458)
- Adds test cases exercising file selection by extension with
`--preview` enabled and disabled.
- Adds `INCLUDE_PREVIEW` with file patterns including `*.pyw`.
- In global preview mode, default configuration selects patterns from
`INCLUDE_PREVIEW`.
- Manually tested ruff server with local vscode for both formatting and
linting of a `.pyw` file.

Closes https://github.com/astral-sh/ruff/issues/13246
2025-09-24 08:39:30 -07:00
David Peter
fcc76bb7b2 [ty] Todo-types for os.fdopen, NamedTemporaryFile, and Path.open (#20549)
## Summary

This applies the trick that we use for `builtins.open` to similar
functions that have the same problem. The reason is that the problem
would otherwise become even more pronounced once we add understanding of
the implicit type of `self` parameters, because then something like
`(base_path / "test.bin").open("rb")` also leads to a wrong return type
and can result in false positives.

## Test Plan

New Markdown tests
2025-09-24 15:43:58 +02:00
David Peter
eea87e24e3 [ty] Update ecosystem-analyzer version for weekly report (#20546)
## Summary

This needs to be updated so we don't fail on "missing" projects
(https://github.com/astral-sh/ruff/actions/runs/17966905581)

## Test plan

Workflow run: https://github.com/astral-sh/ruff/actions/runs/17973053536
2025-09-24 12:02:58 +02:00
Dan Parizher
3e1e02e9b6 Fix non‑BMP code point handling in quick‑fixes and markers (#20526)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-24 10:08:00 +02:00
Alex Waygood
09f570af92 Bump mypy_primer pin (#20540) 2025-09-23 19:35:49 +00:00
Shunsuke Shibayama
722f1a7d7a [ty] fix stack overflow when comparing recursive NamedTuple types with is_disjoint_from (#20538)
## Summary

I found this bug while working on #20528.
The minimum reproducible code is:

```python
from __future__ import annotations

from typing import NamedTuple
from ty_extensions import is_disjoint_from, static_assert

class Path(NamedTuple):
    prev: Path | None
    key: str

static_assert(not is_disjoint_from(Path, Path))
```

A stack overflow occurs when a nominal instance type inherits from
`NamedTuple` and is defined recursively.
This PR fixes this bug.

## Test Plan

mdtest updated
2025-09-23 19:29:03 +02:00
ShikChen
dbc5983503 Update import path to ruff-wasm-web (#20539) 2025-09-23 16:57:26 +00:00
Dan Parizher
46decd4feb [pyupgrade] Fix UP008 to not apply when __class__ is a local variable (UP008) (#20497)
## Summary

Fixes #20491
2025-09-23 10:56:39 -04:00
Renkai Ge
bf38e69870 [ty] Rename "possibly unbound" diagnostics to "possibly missing" (#20492)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-09-23 14:26:55 +00:00
fgiacome
4ed8c65d29 [ty] Add positional-only-parameter-as-kwarg error (#20495) 2025-09-23 15:10:45 +01:00
Dan Parizher
2c916562ba [playground] Allow hover quick fixes to appear for overlapping diagnostics (#20527) 2025-09-23 15:15:31 +02:00
Pieter Cardillo Kwok
edb920b4d5 [flake8-async] Implement blocking-path-method (ASYNC240) (#20264)
## Summary
Adds a new rule to find and report use of `os.path` or `pathlib.Path` in
async functions.

Issue: #8451

## Test Plan

Using `cargo insta test`
2025-09-23 08:30:47 -04:00
Dan Parizher
346842f003 [pyflakes] Fix false positives for __annotate__ (Py3.14+) and __warningregistry__ (F821) (#20154)
## Summary

Fixes #19970

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-09-23 08:16:00 -04:00
David Peter
742f8a4ee6 [ty] Use C[T] instead of C[Unknown] for the upper bound of Self (#20479)
### Summary

This PR includes two changes, both of which are necessary to resolve
https://github.com/astral-sh/ty/issues/1196:

* For a generic class `C[T]`, we previously used `C[Unknown]` as the
upper bound of the `Self` type variable. There were two problems with
this. For one, when `Self` appeared in contravariant position, we would
materialize its upper bound to `Bottom[C[Unknown]]` (which might
simplify to `C[Never]` if `C` is covariant in `T`) when accessing
methods on `Top[C[Unknown]]`. This would result in `invalid-argument`
errors on the `self` parameter. Also, using an upper bound of
`C[Unknown]` would mean that inside methods, references to `T` would be
treated as `Unknown`. This could lead to false negatives. To fix this,
we now use `C[T]` (with a "nested" typevar) as the upper bound for
`Self` on `C[T]`.
* In order to make this work, we needed to allow assignability/subtyping
of inferable typevars to other types, since we now check assignability
of e.g. `C[int]` to `C[T]` (when checking assignability to the upper
bound of `Self`) when calling an instance-method on `C[int]` whose
`self` parameter is annotated as `self: Self` (or implicitly `Self`,
following https://github.com/astral-sh/ruff/pull/18007).

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


### Test Plan

Regression tests for both issues.
2025-09-23 14:02:25 +02:00
Matthew Mckee
fd5c48c539 [ty] Add support for inlay hints on attribute assignment (#20485) 2025-09-23 13:14:46 +02:00
justin
ef4df34652 [ty] implement auto() for StrEnum (#20524)
## Summary
see discussion here:
https://github.com/astral-sh/ty/issues/876#issuecomment-3310130167

https://docs.python.org/3/library/enum.html#enum.StrEnum

> Note Using
[auto](https://docs.python.org/3/library/enum.html#enum.auto) with
[StrEnum](https://docs.python.org/3/library/enum.html#enum.StrEnum)
results in the lower-cased member name as the value.

## Test Plan
- new mdtest
- also, added a test to assert the (already correct) behavior for
`IntEnum`

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-23 12:22:59 +02:00
Manuel Mendez
036f3616a1 [ty] Add PYTHONPATH to EnvVars and fix on Windows (#20490)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-23 08:27:05 +00:00
Matthew Mckee
68ae9c8a15 [ty] Fix class literal subtyping with object fallback (#20521)
## Summary

@ibraheemdev notes this example failed

```py
from typing import Callable

class X:
    ...

def f(callable: Callable[[], X]) -> X:
    return callable()

x = f(X)
```

Resolves https://github.com/astral-sh/ty/issues/1210

The issue was that we set the `Self` to the class type instead of the
instance type of the class.

## Test Plan

Fix tests in `is_subtype_of.md`
2025-09-22 17:26:25 -07:00
Dan Parizher
094bf70a60 [flake8-bultins] Detect class-scope builtin shadowing in decorators, default args, and attribute initializers (A003) (#20178)
## Summary
Fix #20171

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-22 18:12:45 -04:00
Ibraheem Ahmed
32d00cd569 update get-size2 to 0.7.0 2025-09-22 17:37:46 -04:00
David Peter
00a9e65d00 Fix 'cargo shear' runs (#20514)
## Summary

Previous error:

```
▶ cargo shear
Analyzing /home/shark/ruff

ruff_diagnostics -- crates/ruff_diagnostics/Cargo.toml:
  get-size2

ruff_index -- crates/ruff_index/Cargo.toml:
  get-size2

ruff_source_file -- crates/ruff_source_file/Cargo.toml:
  get-size2

ruff_text_size -- crates/ruff_text_size/Cargo.toml:
  get-size2

ty_ide -- crates/ty_ide/Cargo.toml:
  get-size2

ty_project -- crates/ty_project/Cargo.toml:
  get-size2


cargo-shear may have detected unused dependencies incorrectly due to its limitations.
They can be ignored by adding the crate name to the package's Cargo.toml:

[package.metadata.cargo-shear]
ignored = ["crate-name"]

or in the workspace Cargo.toml:

[workspace.metadata.cargo-shear]
ignored = ["crate-name"]
```
2025-09-22 16:04:56 +02:00
Micha Reiser
0c7cfd2a8d Update transitive dependencies (#20513) 2025-09-22 12:50:53 +02:00
renovate[bot]
61bb2a8245 Update Rust crate anyhow to v1.0.100 (#20499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-22 09:51:52 +02:00
Alex Waygood
f1aacd0f2c [ty] The runtime object typing.Protocol is an instance of _ProtocolMeta (#20488)
## Summary

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

This bug doesn't currently cause us any real-world issues, because we
don't yet understand the signatures typeshed gives us for `isinstance()`
and `issubclass()` (typeshed's annotations there use PEP-613 type
aliases). #20107 demonstrates that this will start causing us issues as
soon as we add support for PEP-613 aliases, however, so it makes sense
to fix it now.

## Test Plan

Added mdtests
2025-09-22 08:29:03 +01:00
renovate[bot]
3033d1e5a5 Update Rust crate wasm-bindgen-test to v0.3.53 (#20506)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[wasm-bindgen-test](https://redirect.github.com/wasm-bindgen/wasm-bindgen)
| workspace.dependencies | patch | `0.3.51` -> `0.3.53` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:06:31 +02:00
renovate[bot]
d96d40ef42 Update Rust crate toml to v0.9.7 (#20505)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [toml](https://redirect.github.com/toml-rs/toml) |
workspace.dependencies | patch | `0.9.5` -> `0.9.7` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>toml-rs/toml (toml)</summary>

###
[`v0.9.7`](https://redirect.github.com/toml-rs/toml/compare/toml-v0.9.6...toml-v0.9.7)

[Compare
Source](https://redirect.github.com/toml-rs/toml/compare/toml-v0.9.6...toml-v0.9.7)

###
[`v0.9.6`](https://redirect.github.com/toml-rs/toml/compare/toml-v0.9.5...toml-v0.9.6)

[Compare
Source](https://redirect.github.com/toml-rs/toml/compare/toml-v0.9.5...toml-v0.9.6)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [x] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:04:58 +02:00
renovate[bot]
79224cc53d Update Rust crate clap to v4.5.48 (#20500)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [clap](https://redirect.github.com/clap-rs/clap) |
workspace.dependencies | patch | `4.5.47` -> `4.5.48` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>clap-rs/clap (clap)</summary>

###
[`v4.5.48`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4548---2025-09-19)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.47...v4.5.48)

##### Documentation

- Add a new CLI Concepts document as another way of framing clap
- Expand the `typed_derive` cookbook entry

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:03:53 +02:00
renovate[bot]
fe01a5e032 Update Rust crate serde to v1.0.226 (#20503)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) |
workspace.dependencies | patch | `1.0.223` -> `1.0.226` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>serde-rs/serde (serde)</summary>

###
[`v1.0.226`](https://redirect.github.com/serde-rs/serde/releases/tag/v1.0.226)

[Compare
Source](https://redirect.github.com/serde-rs/serde/compare/v1.0.225...v1.0.226)

- Deduplicate variant matching logic inside generated Deserialize impl
for adjacently tagged enums
([#&#8203;2935](https://redirect.github.com/serde-rs/serde/issues/2935),
thanks [@&#8203;Mingun](https://redirect.github.com/Mingun))

###
[`v1.0.225`](https://redirect.github.com/serde-rs/serde/releases/tag/v1.0.225)

[Compare
Source](https://redirect.github.com/serde-rs/serde/compare/v1.0.224...v1.0.225)

- Avoid triggering a deprecation warning in derived Serialize and
Deserialize impls for a data structure that contains its own
deprecations
([#&#8203;2879](https://redirect.github.com/serde-rs/serde/issues/2879),
thanks [@&#8203;rcrisanti](https://redirect.github.com/rcrisanti))

###
[`v1.0.224`](https://redirect.github.com/serde-rs/serde/releases/tag/v1.0.224)

[Compare
Source](https://redirect.github.com/serde-rs/serde/compare/v1.0.223...v1.0.224)

- Remove private types being suggested in rustc diagnostics
([#&#8203;2979](https://redirect.github.com/serde-rs/serde/issues/2979))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:03:28 +02:00
renovate[bot]
740425d39d Update Rust crate serde_with to v3.14.1 (#20504)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde_with](https://redirect.github.com/jonasbb/serde_with) |
workspace.dependencies | patch | `3.14.0` -> `3.14.1` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>jonasbb/serde_with (serde_with)</summary>

###
[`v3.14.1`](https://redirect.github.com/jonasbb/serde_with/releases/tag/v3.14.1):
serde_with v3.14.1

[Compare
Source](https://redirect.github.com/jonasbb/serde_with/compare/v3.14.0...v3.14.1)

##### Fixed

- Show macro expansion in the docs.rs generated rustdoc.
Since macros are used to generate trait implementations, this is useful
to understand the exact generated code.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:03:10 +02:00
renovate[bot]
4fddd373aa Update Rust crate ordermap to v0.5.12 (#20502)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ordermap](https://redirect.github.com/indexmap-rs/ordermap) |
workspace.dependencies | patch | `0.5.10` -> `0.5.12` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>indexmap-rs/ordermap (ordermap)</summary>

###
[`v0.5.12`](https://redirect.github.com/indexmap-rs/ordermap/blob/HEAD/RELEASES.md#0512-2025-09-15)

[Compare
Source](https://redirect.github.com/indexmap-rs/ordermap/compare/0.5.11...0.5.12)

- Make the minimum `serde` version only apply when "serde" is enabled.

###
[`v0.5.11`](https://redirect.github.com/indexmap-rs/ordermap/blob/HEAD/RELEASES.md#0511-2025-09-15)

[Compare
Source](https://redirect.github.com/indexmap-rs/ordermap/compare/0.5.10...0.5.11)

- Switched the "serde" feature to depend on `serde_core`, improving
build
parallelism in cases where other dependents have enabled "serde/derive".

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:02:58 +02:00
renovate[bot]
0b1e12f086 Update Rust crate indexmap to v2.11.4 (#20501) 2025-09-22 09:02:43 +02:00
renovate[bot]
d12324f06e Update dependency ruff to v0.13.1 (#20498) 2025-09-22 09:02:05 +02:00
Manuel Mendez
2c6c3e78f6 [ty] Search PYTHONPATH to find modules (#20441)
Co-authored-by: Nate Lust <natelust@linux.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-20 13:40:10 +02:00
Micha Reiser
3ffe56d19d [ty] Remove unnecessary FileScopeId to ScopeId conversion (#20481) 2025-09-20 11:20:10 +00:00
GF
eb354608d2 [ty] Add LSP debug information command (#20379)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-20 11:15:13 +00:00
Ibraheem Ahmed
12086dfa69 re-infer RHS of annotated assignments in isolation for assignability diagnostics 2025-09-19 17:00:37 -04:00
Ibraheem Ahmed
5f294f9f2e use type context for inference of generic function calls 2025-09-19 17:00:37 -04:00
Gary Yendell
44fc87f491 [ruff] Add logging-eager-conversion (RUF065) (#19942)
<!--
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

Fixes #12734

I have started with simply checking if any arguments that are providing
extra values to the log message are calls to `str` or `repr`, as
suggested in the linked issue. There was a concern that this could cause
false positives and the check should be more explicit. I am happy to
look into that if I have some further examples to work with.

If this is the accepted solution then there are more cases to add to the
test and it should possibly also do test for the same behavior via the
`extra` keyword.

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

I have added a new test case and python file to flake8_logging_format
with examples of this anti-pattern.

<!-- How was it tested? -->
2025-09-19 16:43:44 -04:00
Takayuki Maeda
43cda2dfe9 [ruff] Fix B004 to skip invalid hasattr/getattr calls (#20486)
## Summary

Fixes #20440

Fix B004 to skip invalid hasattr/getattr calls

- Add argument validation for `hasattr` and `getattr`
- Skip B004 rule when function calls have invalid argument patterns
2025-09-19 13:44:42 -05:00
Takayuki Maeda
bd5b3e4f6e Deduplicate input paths (#20105)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #20035, fixes #19395

This is for deduplicating input paths to avoid processing the same file
multiple times.

This is my first contribution, so I'm sorry if I miss something. Please
tell me if this is needed for this feature.

## Test Plan

<!-- How was it tested? -->

I just added a test `find_python_files_deduplicated` in
eee1020e32/crates/ruff_workspace/src/resolver.rs (L1017)
. This pull request adds changes to `WalkPythonFilesState::finish`,
which is used in `python_files_in_path`, so they affect some commands
such as `analyze`, `format`, `check` and so on. I will add snapshot
tests for them if necessary.

I’ve already confirmed that the same thing happens with ruff check as
well.

```
$ echo "x   = 1" > example/foo.py
$ uvx ruff check example example/foo.py
I002 [*] Missing required import: `from __future__ import annotations`
--> /path/to/example/foo.py:1:1
help: Insert required import: `from __future__ import annotations`

I002 [*] Missing required import: `from __future__ import annotations`
--> /path/to/example/foo.py:1:1
help: Insert required import: `from __future__ import annotations`

Found 2 errors.
[*] 2 fixable with the `--fix` option.
```
2025-09-19 14:40:23 -04:00
Andrew Gallant
3bf4dae452 [ty] Make auto-import work in the playground
It turned out that we weren't quite funneling the new completion data
all the way through.

I followed the docs for [`CompletionItem`] for the Monaco editor. It's
similar, but not identical, to the LSP protocol specification.

[`CompletionItem`]: https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.CompletionItem.html
2025-09-19 14:35:51 -04:00
Takayuki Maeda
8eeca023d6 [ruff] FURB164 Replace -nan with nan when using the value to construct Decimal (#20391)
## Summary

Fixes #19699

Normalize `Decimal(float("-nan"))` to `Decimal("nan")`.

The same handling is implemented in:

c3e873dd82/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs (L165)
2025-09-19 13:04:29 -05:00
Dan Parizher
c94ddb590f [flake8-bugbear] Add B912: map() without an explicit strict= parameter (#20429)
## Summary

Implements new rule `B912` that requires the `strict=` argument for
`map(...)` calls with two or more iterables on Python 3.14+, following
the same pattern as `B905` for `zip()`.

Closes #20057

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-09-19 12:54:44 -05:00
renovate[bot]
bae8ddfb8a Update Rust crate hashbrown to 0.16.0 (#20399)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [hashbrown](https://redirect.github.com/rust-lang/hashbrown) |
workspace.dependencies | minor | `0.15.0` -> `0.16.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>rust-lang/hashbrown (hashbrown)</summary>

###
[`v0.16.0`](https://redirect.github.com/rust-lang/hashbrown/blob/HEAD/CHANGELOG.md#0160---2025-08-28)

[Compare
Source](https://redirect.github.com/rust-lang/hashbrown/compare/v0.15.5...v0.16.0)

##### Changed

- Bump foldhash, the default hasher, to 0.2.0.
- Replaced `DefaultHashBuilder` with a newtype wrapper around `foldhash`
instead
  of re-exporting it directly.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: David Peter <mail@david-peter.de>
Co-authored-by: Ibraheem Ahmed <ibraheem@ibraheem.ca>
2025-09-19 13:24:45 -04:00
Dan Parizher
c0fb235a70 [flake8-comprehensions] Preserve trailing commas for single-element lists (C409) (#19571)
## Summary

Fixes #19568
2025-09-19 09:27:14 -04:00
Andrew Gallant
b5a3503a58 [ty] Enable auto-import for completions in WASM builds by default
Now that imports are actually inserted, this should give us some
valuable dog-fooding experience.

Note that we don't currently do any ranking on completions, so until
that is improved, even in-scope completions could suffer. With that
said, this shouldn't have any impact at all in several scenarios (like
completions for attributes on objects).
2025-09-19 08:11:59 -04:00
Andrew Gallant
d45209f425 [ty] Add some tricky test cases for the auto-import importer
We don't attempt to fix these yet. I think there are bigger fish to fry.

I came up with these based on this discussion:
https://github.com/astral-sh/ruff/pull/20439#discussion_r2357769518

Here's one example:

```
if ...:
    from foo import MAGIC
else:
    from bar import MAGIC

MAG<CURSOR>
```

Now in this example, completions will include `MAGIC` from the local
scope. That is, auto-import is involved with that completion. But at
present, auto-import will suggest importing `foo` and `bar` because we
haven't de-duplicated completions yet. Which is fine.

Here's another example:

```
if ...:
    import foo as fubar
else:
    import bar as fubar

MAG<CURSOR>
```

Now here, there is no `MAGIC` symbol in scope. So auto-import is in
play. Let's assume that the user selects `MAGIC` from `foo` in this
example. (`bar` also has `MAGIC`.)

Since we currently ignore the declaration site for symbols with
multiple possible bindings, the importer today doesn't know that
`fubar` _could_ contain `MAGIC`. But even if it did, what would we do
with that information? Should we do this?

```
if ...:
    import foo as fubar
    from foo import MAGIC
else:
    import bar as fubar

MAGIC
```

Or could we reason that `bar` also has `MAGIC`?

```
if ...:
    import foo as fubar
else:
    import bar as fubar

fubar.MAGIC
```

But if we did that, we're making an assumption of user intent, since
they *selected* `foo.MAGIC` but not `bar.MAGIC`.

Anyway, I don't think we need to settle on an answer today, but I
wanted to capture some of these tricky cases in tests at the very
least.
2025-09-19 07:54:07 -04:00
Micha Reiser
5d1cd85662 Shard instrumented benchmark (#20437) 2025-09-19 10:25:04 +02:00
Dhruv Manilawala
902b0b4ce9 [ty] Add support for **kwargs (#20430)
## Summary

This PR adds support for unpacking `**kwargs` argument.

This can be matched against any standard (positional or keyword),
keyword-only, or keyword variadic parameter that haven't been matched
yet.

This PR also takes care of special casing `TypedDict` because the key
names and the corresponding value type is known, so we can be more
precise in our matching and type checking step. In the future, this
special casing would be extended to include `ParamSpec` as well.

Part of astral-sh/ty#247

## Test Plan

Add test cases for various scenarios.
2025-09-19 05:00:30 +00:00
Amethyst Reese
6f2b60708e Exclude snapshots from vscode search results (#20457)
Makes ⌘-T file search ignore snapshot files, so you can actually fuzzy
match "ruff cache" to "ruff/src/cache.rs" without looking/scrolling past
dozens of snapshot files in the search results.
2025-09-18 21:10:42 -07:00
Frazer McLean
bc89d0394c [flake8-simplify] Fix incorrect fix for positive maxsplit without separator (SIM905) (#20056)
<!--
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

Resolves #20033

## Test Plan

unit tests added to the new split function, existing snapshot test
updated.

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-18 20:56:34 +00:00
Dylan
706be0a6e7 Add pyproject.toml to rooster config version_files and bump to 0.13.1 (#20475)
It looks like the new `rooster` does not automatically bump
`pyproject.toml`.

This should fix the following failure for the release action:


https://github.com/astral-sh/ruff/actions/runs/17839256763/job/50724254795
2025-09-18 14:37:29 -05:00
Dylan
7b40428b6a Bump 0.13.1 (#20473) 2025-09-18 19:25:17 +00:00
Dylan
b9b5755368 Upgrade to the latest rooster version and include contributors in CHANGELOG (#20472)
What it says on the tin!

Manually tested the release script and it appears to produce the right
thing.
2025-09-18 13:43:39 -05:00
Takayuki Maeda
b4b5d67a4a [flynt] Use triple quotes for joined raw strings with newlines (FLY002) (#20197)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #19887

- flynt(FLY002): When joining only string constants, upgrade raw
single-quoted strings to raw triple-quoted if the resulting
content contains a newline.
- Choose a safe triple-quote delimiter by switching to the opposite
quote style if the preferred triple appears inside the
content.
- Update FLY002 snapshot to include the `\n'.join([r'line1','line2'])`
case.

## Test Plan

I've added one test case to FLY002.py.

<!-- How was it tested? -->

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-18 13:18:29 -04:00
Micha Reiser
0b60584b7e Bump MSRV to Rust 1.88 (#20470) 2025-09-18 17:52:37 +02:00
Takayuki Maeda
821b2f8b2e [refurb] Mark single-item-membership-test fix as always unsafe (FURB171) (#20279)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #20255

Mark single-item-membership-test fixes as always unsafe

- Always set `Applicability::Unsafe` for FURB171 fixes
- Update “Fix safety” docs to reflect always-unsafe behavior
- Expand tests (not in, nested set/frozenset, commented args)

## Test Plan

<!-- How was it tested? -->

I have added new test cases to
`crates/ruff_linter/resources/test/fixtures/refurb/FURB171_0.py` and
`crates/ruff_linter/resources/test/fixtures/refurb/FURB171_1.py`.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-18 15:24:15 +00:00
Micha Reiser
1758f26d94 Update rust toolchain to 1.90 (#20469) 2025-09-18 16:54:49 +02:00
Eric Mark Martin
2502ff7638 [ty] Make TypeIs invariant in its type argument (#20428)
## Summary

What it says on the tin. See the [typing
spec](https://docs.python.org/3/library/typing.html#typing.TypeIs) for
justification.

## Test Plan

Add more tests to PEP 695 `variance.md` suite.
2025-09-18 07:53:13 -07:00
chiri
144373fb3c [flake8-use-pathlib] Fix PTH101, PTH104, PTH105, PTH121 fixes (#20143)
## Summary
Fixes https://github.com/astral-sh/ruff/issues/20134

## Test Plan
`cargo nextest run flake8_use_pathlib`

---------

Co-authored-by: Dan Parizher <danparizher@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-18 14:17:54 +00:00
Dan Parizher
91995aa516 [pyupgrade] Fix false positive when class name is shadowed by local variable (UP008) (#20427)
## Summary

Fixes #20422

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-18 14:05:05 +00:00
Andrew Gallant
1ebbe73a1d [ty] Swap detail and description fields for CompletionItemLabelDetails
This seems to be more consistent with how other LSPs work (like
`rust-analyzer`), and also I think is more consistent with how
`CompletionItem.detail` is itself rendered. Namely, in VS Code, it
is right-aligned. And it's also where we put the type signature.
But `CompletionItemLabelDetails.detail` is left-aligned where as
`CompletionItemLabelDetails.description` is right-aligned. So let's
swap them such that type signatures go in the latter and not the
former.

This also adds a space before the module name and contextualizes
it with `(import <name>)` to help aide the end user in figuring out
selecting the completion will do.

Fixes #1200
2025-09-18 09:11:17 -04:00
Shahar Naveh
48ada2d359 Generator preferred quote style (#20434) 2025-09-18 12:57:21 +02:00
David Peter
50bd3943da [ty] Faster iteration on mdtests (#20465)
## Summary

This change reduces MD test compilation time from 6s to 3s on my laptop.
We don't need to build the unit tests and the corpus tests when we're
only interested in Markdown-based tests.

## Test Plan

local benchmarks
2025-09-18 10:48:52 +00:00
Dan Parizher
5707958dad [flake8-simplify] Fix diagnostic to show correct method name for rsplit calls (SIM905) (#20459)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-18 07:52:08 +00:00
Nikolas Hearp
c4d359306b Add fixes to output-format=sarif (#20300)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-18 09:37:04 +02:00
Ibraheem Ahmed
e84d523bcf [ty] Infer more precise types for collection literals (#20360)
## Summary

Part of https://github.com/astral-sh/ty/issues/168. Infer more precise types for collection literals (currently, only `list` and `set`). For example,

```py
x = [1, 2, 3] # revealed: list[Unknown | int]
y: list[int] = [1, 2, 3] # revealed: list[int]
```

This could easily be extended to `dict` literals, but I am intentionally limiting scope for now.
2025-09-17 18:51:50 -04:00
chiri
bfb0902446 [flake8-use-pathlib] Add fix for PTH123 (#20169)
## Summary
Part of https://github.com/astral-sh/ruff/issues/2331

## Test Plan
`cargo nextest run flake8_use_pathlib`

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-17 15:47:29 -04:00
Shaygan Hooshyari
05622ae757 [ty] Bind Self typevar to method context (#20366)
Fixes: https://github.com/astral-sh/ty/issues/1173

<!--
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

This PR will change the logic of binding Self type variables to bind
self to the immediate function that it's used on.
Since we are binding `self` to methods and not the class itself we need
to ensure that we bind self consistently.

The fix is to traverse scopes containing the self and find the first
function inside a class and use that function to bind the typevar for
self.

If no such scope is found we fallback to the normal behavior. Using Self
outside of a class scope is not legal anyway.

## Test Plan

Added a new mdtest.

Checked the diagnostics that are not emitted anymore in [primer
results](https://github.com/astral-sh/ruff/pull/20366#issuecomment-3289411424).
It looks good altough I don't completely understand what was wrong
before.

---------

Co-authored-by: Douglas Creager <dcreager@dcreager.net>
2025-09-17 14:58:54 -04:00
Andrew Gallant
3fcbe8bde6 [ty] Remove TODO about using a non-panicking lookup method
Ref https://github.com/astral-sh/ruff/pull/20439#discussion_r2355082049

Ref https://github.com/astral-sh/ruff/pull/18455#discussion_r2126833137
2025-09-17 13:59:28 -04:00
Andrew Gallant
64a4e2889e [ty] Add new completion data to wasm bridge 2025-09-17 13:59:28 -04:00
Andrew Gallant
5816985ecd [ruff] Remove Locator from Importer
It seems like we'd like to remove `Locator` since it's a bit
awkward in how it works:
https://github.com/astral-sh/ruff/pull/20439#discussion_r2354683797

It looked pretty easy to rip it out of the `Importer`, so that's
one less thing using it.
2025-09-17 13:59:28 -04:00
Andrew Gallant
b6a29592e7 [ty] Add a new abstraction for adding imports to a module
This is somewhat inspired by a similar abstraction in
`ruff_linter`. The main idea is to create an importer once
for a module that you want to add imports to. And then call
`import` to generate an edit for each symbol you want to
add.

I haven't done any performance profiling here yet. I don't
know if it will be a bottleneck. In particular, I do expect
`Importer::import` (but not `Importer::new`) to get called
many times for a single completion request when auto-import
is enabled. Particularly in projects with a lot of unimported
symbols. Because I don't know the perf impact, I didn't do
any premature optimization here. But there are surely some
low hanging fruit if this does prove to be a problem.

New tests make up a big portion of the diff here. I tried to
think of a bunch of different cases, although I'm sure there
are more.
2025-09-17 13:59:28 -04:00
Andrew Gallant
bcc8d6910b [ty] Refactor to handle unimported completions
This rejiggers some stuff in the main completions entrypoint
in `ty_ide`. A more refined `Completion` type is defined
with more information. In particular, to support auto-import,
we now include a module name and an "edit" for inserting an
import.

This also rolls the old "detailed completion" into the new
completion type. Previously, we were relying on the completion
type for `ty_python_semantic`. But `ty_ide` is really the code
that owns completions.

Note that this code doesn't build as-is. The next commit will
add the importer used here in `add_unimported_completions`.
2025-09-17 13:59:28 -04:00
Andrew Gallant
02ee22db78 [ty] Add module to result returned by "all symbols" API
Based on how this API is currently implemented, this doesn't
really cost us anything. But it gives us access to more
information about where the symbol is defined.
2025-09-17 13:59:28 -04:00
Andrew Gallant
0a2325c5fe [ty] Move CompletionKind to ty_ide
I think this is a better home for it. This way, `ty_ide`
more clearly owns how the "kind" of a completion is computed.
In particular, it is computed differently for things where
we know its type versus unimported symbols.
2025-09-17 13:59:28 -04:00
Andrew Gallant
6c3c963f8a [ty] Include definition site for "members of" API
In the course of writing the "add an import" implementation,
I realized that we needed to know which symbols were in scope
and how they were defined. This was necessary to be able to
determine how to add a new import in a way that (minimally)
does not conflict with existing symbols.

I'm not sure that this is fully correct (especially for
symbol bindings) and it's unclear to me in which cases a
definition site will be missing. But this seems to work for
some of the basic cases that I tried.
2025-09-17 13:59:28 -04:00
Andrew Gallant
6ec52991cb [ty] Fix a bug with "all_submodule_names_for_package" API
The names of the submodules returned should be *complete*. This
is the contract of `Module::name`. However, we were previously
only returning the basename of the submodule.
2025-09-17 13:59:28 -04:00
Andrew Gallant
cf16fc4aa4 [ty] Export some stuff from ty_python_semantic
We're going to want to use this outside of `ty_python_semantic`.
Specifically, in `ty_ide`.
2025-09-17 13:59:28 -04:00
Andrew Gallant
61a49c89eb [ruff] Add TextRange::to_std_range
This can already be accomplished via a `From` impl (and indeed,
that's how this is implemented). But in a generic context, the
turbo-fishing that needs to be applied is quite annoying.
2025-09-17 13:59:28 -04:00
Andrew Gallant
da5eb85087 [ruff] Add API for splicing into an existing import statement
Basically, given a `from module import name1, name2, ...` statement,
we'd like to be able to insert another name in that list.

This new `Insertion::existing_import` API provides such
functionality. There isn't much to it, although we are careful
to try and avoid inserting nonsense for import statements
that are already invalid.
2025-09-17 13:59:28 -04:00
Andrew Gallant
a47a50e6e2 [ruff] Provide a way to get an owned Stylist
This makes it easier to test with in some cases and generally shouldn't
cost anything.
2025-09-17 13:59:28 -04:00
Andrew Gallant
880a867696 [ruff] Move Insertion abstraction out of ruff_linter
This refactors the importer abstraction to use a shared
`Insertion`. This is mostly just moving some code around
with some slight tweaks.

The plan here is to keep the rest of the importing code
in `ruff_linter` and then write something ty-specific on
top of `Insertion`. This ends up sharing some code, but
not as much as would be ideal. In particular, the
`ruff_linter` imported is pretty tightly coupled with
ruff's semantic model. So to share the code, we'd need to
abstract over that.
2025-09-17 13:59:28 -04:00
Andrew Gallant
ec2720c814 [ruff] Small tweak to import
As I was playing around in this file, it was much nicer
to just use `cst::` everywhere, similar to what we do with
`ruff_python_ast`.
2025-09-17 13:59:28 -04:00
chiri
cb3c3ba94d [flake8-use-pathlib] A bit clean up PTH100 (#20452)
## Summary

Part of https://github.com/astral-sh/ruff/pull/20215

## Test Plan
2025-09-17 12:11:30 -04:00
chiri
c585c9f6d4 [flake8-use-pathlib] Make PTH111 fix unsafe because it can change behavior (#20215)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/20214

## Test Plan
2025-09-17 11:23:55 -04:00
Brent Westbrook
ac5488086f [ty] Add GitHub output format (#20358)
## Summary

This PR wires up the GitHub output format moved to `ruff_db` in #20320
to the ty CLI.

It's a bit smaller than the GitLab version (#20155) because some of the
helpers were already in place, but I did factor out a few
`DisplayDiagnosticConfig` constructor calls in Ruff. I also exposed the
`GithubRenderer` and a wrapper `DisplayGithubDiagnostics` type because
we needed a way to configure the program name displayed in the GitHub
diagnostics. This was previously hard-coded to `Ruff`:

<img width="675" height="247" alt="image"
src="https://github.com/user-attachments/assets/592da860-d2f5-4abd-bc5a-66071d742509"
/>

Another option would be to drop the program name in the output format,
but I think it can be helpful in workflows with multiple programs
emitting annotations (such as Ruff and ty!)

## Test Plan

New CLI test, and a manual test with `--config 'terminal.output-format =
"github"'`
2025-09-17 09:50:25 -04:00
Carl Meyer
7e464b8150 [ty] move graphql-core to good.txt (#20447)
## Summary

With https://github.com/astral-sh/ruff/pull/20446, graphql-core now
checks without error; we can move it to `good.txt`.

## Test Plan

CI
2025-09-17 10:09:32 +02:00
David Peter
ffd650e5fd [ty] Update mypy_primer (#20433)
## Summary

Revert the materialize-changes, see
https://github.com/hauntsaninja/mypy_primer/pull/208

## Test Plan

CI
2025-09-17 09:51:48 +02:00
Carl Meyer
99ec4d2c69 [ty] detect cycles in binary comparison inference (#20446)
## Summary

Catch infinite recursion in binary-compare inference.

Fixes the stack overflow in `graphql-core` in mypy-primer.

## Test Plan

Added two tests that stack-overflowed before this PR.
2025-09-17 09:45:25 +02:00
justin
9f0b942b9e [ty] infer name and value for enum members (#20311)
## summary
- this pr implements the following attributes for `Enum` members:
  - `name`
  - `_name_`
  - `value`
  - `_value_`
- adds a TODO test for `my_enum_class_instance.name`
- only implements if the instance is a subclass of `Enum` re: this
[comment](https://github.com/astral-sh/ruff/pull/19481#issuecomment-3103460307)
and existing
[test](c34449ed7c/crates/ty_python_semantic/resources/mdtest/enums.md (L625))

### pointers
- https://github.com/astral-sh/ty/issues/876
- https://typing.python.org/en/latest/spec/enums.html#enum-definition
- https://github.com/astral-sh/ruff/pull/19481#issuecomment-3103460307

## test plan
- mdtests
- triaged conformance diffs here:
https://diffswarm.dev/d-01k531ag4nee3xmdeq4f3j66pb
- triaged mypy primer diffs here for django-stubs:
https://diffswarm.dev/d-01k5331n13k9yx8tvnxnkeawp3
  - added a TODO test for overriding `.value`
- discord diff seems reasonable

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-17 09:36:27 +02:00
Carl Meyer
c2fa449954 [ty] support type aliases in binary compares (#20445)
## Summary

Add missing `Type::TypeAlias` clauses to `infer_binary_type_comparison`.

## Test Plan

Added mdtests that failed before.
2025-09-17 09:33:26 +02:00
Carl Meyer
681ad2fd92 [ty] move primer projects to good.txt (#20444)
## Summary

After https://github.com/astral-sh/ruff/pull/20359 we can move all but
three remaining projects over to `good.txt`.

## Test Plan

CI
2025-09-17 09:31:27 +02:00
Takayuki Maeda
98071b49c2 [playground] Enable inline noqa for multiline strings in playground (#20442) 2025-09-17 09:29:40 +02:00
Carl Meyer
d121a76aef [ty] no more diverging query cycles in type expressions (#20359)
## Summary

Use `Type::Divergent` to short-circuit diverging types in type
expressions. This avoids panicking in a wide variety of cases of
recursive type expressions.

Avoids many panics (but not yet all -- I'll be tracking down the rest)
from https://github.com/astral-sh/ty/issues/256 by falling back to
Divergent. For many of these recursive type aliases, we'd like to
support them properly (i.e. really understand the recursive nature of
the type, not just fall back to Divergent) but that will be future work.

This switches `Type::has_divergent_type` from using `any_over_type` to a
custom set of visit methods, because `any_over_type` visits more than we
need to visit, and exercises some lazy attributes of type, causing
significantly more work. This change means this diff doesn't regress
perf; it even reclaims some of the perf regression from
https://github.com/astral-sh/ruff/pull/20333.

## Test Plan

Added mdtest for recursive type alias that panics on main.

Verified that we can now type-check `packaging` (and projects depending
on it) without panic; this will allow moving a number of mypy-primer
projects from `bad.txt` to `good.txt` in a subsequent PR.
2025-09-16 16:44:11 -07:00
Bhuminjay Soni
c3f2187fda [syntax-errors]: import from * only allowed at module scope (F406) (#20166)
<!--
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

This PR implements F406
https://docs.astral.sh/ruff/rules/undefined-local-with-nested-import-star-usage/
as a semantic syntax error

## Test Plan

I have written inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
2025-09-16 15:53:28 -04:00
Amethyst Reese
8a027b0d74 [ruff] Treat panics as fatal diagnostics, sort panics last (#20258)
- Convert panics to diagnostics with id `Panic`, severity `Fatal`, and
the error as the diagnostic message, annotated with a `Span` with empty
code block and no range.
- Updates the post-linting message diagnostic handling to track the
maximum severity seen, and then prints the "report a bug in ruff"
message only if the max severity was `Fatal`

This depends on the sorting changes since it creates diagnostics with no
range specified.
2025-09-16 11:33:37 -07:00
Dan Parizher
aa63c24b8f [pycodestyle] Fix E301 to only trigger for functions immediately within a class (#19768)
## Summary

Fixes #19752

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-16 11:00:07 -04:00
Douglas Creager
1f46c18921 [ty] More constraint set simplifications via simpler constraint representation (#20423)
Previously, we used a very fine-grained representation for individual
constraints: each constraint was _either_ a range constraint, a
not-equivalent constraint, or an incomparable constraint. These three
pieces are enough to represent all of the "real" constraints we need to
create — range constraints and their negation.

However, it meant that we weren't picking up as many chances to simplify
constraint sets as we could. Our simplification logic depends on being
able to look at _pairs_ of constraints or clauses to see if they
simplify relative to each other. With our fine-grained representation,
we could easily encounter situations that we should have been able to
simplify, but that would require looking at three or more individual
constraints.

For instance, negating a range constraint would produce:

```
¬(Base ≤ T ≤ Super) = ((T ≤ Base) ∧ (T ≠ Base)) ∨ (T ≁ Base) ∨
                      ((Super ≤ T) ∧ (T ≠ Super)) ∨ (T ≁ Super)
```

That is, `T` must be (strictly) less than `Base`, (strictly) greater
than `Super`, or incomparable to either.

If we tried to union those back together, we should get `always`, since
`x ∨ ¬x` should always be true, no matter what `x` is. But instead we
would get:

```
(Base ≤ T ≤ Super) ∨ ((T ≤ Base) ∧ (T ≠ Base)) ∨ (T ≁ Base) ∨ ((Super ≤ T) ∧ (T ≠
 Super)) ∨ (T ≁ Super)
```

Nothing would simplify relative to each other, because we'd have to look
at all five union elements to see that together they do in fact combine
to `always`.

The fine-grained representation was nice, because it made it easier to
[work out the math](https://dcreager.net/theory/constraints/) for
intersections and unions of each kind of constraint. But being able to
simplify is more important, since the example above comes up immediately
in #20093 when trying to handle constrained typevars.

The fix in this PR is to go back to a more coarse-grained
representation, where each individual constraint consists of a positive
range (which might be `always` / `Never ≤ T ≤ object`), and zero or more
negative ranges. The intuition is to think of a constraint as a region
of the type space (representable as a range) with zero or more "holes"
removed from it.

With this representation, negating a range constraint produces:

```
¬(Base ≤ T ≤ Super) = (always ∧ ¬(Base ≤ T ≤ Super))
```

(That looks trivial, because it is! We just move the positive range to
the negative side.)

The math is not that much harder than before, because there are only
three combinations to consider (each for intersection and union) —
though the fact that there can be multiple holes in a constraint does
require some nested loops. But the mdtest suite gives me confidence that
this is not introducing any new issues, and it definitely removes a
troublesome TODO.

(As an aside, this change also means that we are back to having each
clause contain no more than one individual constraint for any typevar.
This turned out to be important, because part of our simplification
logic was also depending on that!)

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-16 10:05:01 -04:00
Takayuki Maeda
0d424d8e78 [ruff] Add analyze.string-imports-min-dots to settings documentation (#20375) 2025-09-16 13:19:34 +02:00
Alex Waygood
e6b321eccc Disable flamegraph uploads for walltime benchmarks (#20419) 2025-09-16 09:08:22 +00:00
David Peter
2a6dde4acb [ty] Remove Self from generic context when binding Self (#20364)
## Summary

This mainly removes an internal inconsistency, where we didn't remove
the `Self` type variable when eagerly binding `Self` to an instance
type. It has no observable effect, apparently.

builds on top of https://github.com/astral-sh/ruff/pull/20328

## Test Plan

None
2025-09-15 17:40:10 +02:00
David Peter
25cbf38a47 [ty] Patch Self for fallback-methods on NamedTuples and TypedDicts (#20328)
## Summary

We use classes like
[`_typeshed._type_checker_internals.NamedTupleFallback`](d9c76e1d9f/stdlib/_typeshed/_type_checker_internals.pyi (L54-L75))
to tack on additional attributes/methods to instances of user-defined
`NamedTuple`s (or `TypedDict`s), even though these classes are not
present in the MRO of those types.

The problem is that those classes use implicit and explicit `Self`
annotations which refer to `NamedTupleFallback` itself, instead of to
the actual type that we're adding those methods to:
```py
class NamedTupleFallback(tuple[Any, ...]):
    # […]
    def _replace(self, **kwargs: Any) -> typing_extensions.Self: ...
```

In effect, when we access `_replace` on an instance of a custom
`NamedTuple` instance, its `self` parameter and return type refer to the
wrong `Self`. This leads to incorrect *"Argument to bound method
`_replace` is incorrect: Argument type `Person` does not satisfy upper
bound `NamedTupleFallback` of type variable `Self`"* errors on #18007.
It would also lead to similar errors on `TypedDict`s, if they would
already implement assignability properly.


## Test Plan

I applied the following patch to typeshed and verified that no errors
appear anymore.

<details>

```diff
diff --git a/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi b/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
index feb22aae00..8e41034f19 100644
--- a/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
+++ b/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
@@ -29,27 +29,27 @@ class TypedDictFallback(Mapping[str, object], metaclass=ABCMeta):
         __readonly_keys__: ClassVar[frozenset[str]]
         __mutable_keys__: ClassVar[frozenset[str]]
 
-    def copy(self) -> typing_extensions.Self: ...
+    def copy(self: typing_extensions.Self) -> typing_extensions.Self: ...
     # Using Never so that only calls using mypy plugin hook that specialize the signature
     # can go through.
-    def setdefault(self, k: Never, default: object) -> object: ...
+    def setdefault(self: typing_extensions.Self, k: Never, default: object) -> object: ...
     # Mypy plugin hook for 'pop' expects that 'default' has a type variable type.
-    def pop(self, k: Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
-    def update(self, m: typing_extensions.Self, /) -> None: ...
-    def __delitem__(self, k: Never) -> None: ...
-    def items(self) -> dict_items[str, object]: ...
-    def keys(self) -> dict_keys[str, object]: ...
-    def values(self) -> dict_values[str, object]: ...
+    def pop(self: typing_extensions.Self, k: Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
+    def update(self: typing_extensions.Self, m: typing_extensions.Self, /) -> None: ...
+    def __delitem__(self: typing_extensions.Self, k: Never) -> None: ...
+    def items(self: typing_extensions.Self) -> dict_items[str, object]: ...
+    def keys(self: typing_extensions.Self) -> dict_keys[str, object]: ...
+    def values(self: typing_extensions.Self) -> dict_values[str, object]: ...
     @overload
-    def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    def __or__(self: typing_extensions.Self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
     @overload
-    def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    def __or__(self: typing_extensions.Self, value: dict[str, Any], /) -> dict[str, object]: ...
     @overload
-    def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    def __ror__(self: typing_extensions.Self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
     @overload
-    def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    def __ror__(self: typing_extensions.Self, value: dict[str, Any], /) -> dict[str, object]: ...
     # supposedly incompatible definitions of __or__ and __ior__
-    def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
+    def __ior__(self: typing_extensions.Self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
 
 # Fallback type providing methods and attributes that appear on all `NamedTuple` types.
 class NamedTupleFallback(tuple[Any, ...]):
@@ -61,18 +61,18 @@ class NamedTupleFallback(tuple[Any, ...]):
         __orig_bases__: ClassVar[tuple[Any, ...]]
 
     @overload
-    def __init__(self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ...
+    def __init__(self: typing_extensions.Self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ...
     @overload
     @typing_extensions.deprecated(
         "Creating a typing.NamedTuple using keyword arguments is deprecated and support will be removed in Python 3.15"
     )
-    def __init__(self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ...
+    def __init__(self: typing_extensions.Self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ...
     @classmethod
     def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ...
-    def _asdict(self) -> dict[str, Any]: ...
-    def _replace(self, **kwargs: Any) -> typing_extensions.Self: ...
+    def _asdict(self: typing_extensions.Self) -> dict[str, Any]: ...
+    def _replace(self: typing_extensions.Self, **kwargs: Any) -> typing_extensions.Self: ...
     if sys.version_info >= (3, 13):
-        def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ...
+        def __replace__(self: typing_extensions.Self, **kwargs: Any) -> typing_extensions.Self: ...
 
 # Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter.
 _S = TypeVar("_S")
```

</details>
2025-09-15 16:21:53 +02:00
Salaheddine EL FARISSI
9a9ebc316c [docs] Update README.md with Albumentations new repository URL (#20415)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-15 13:59:43 +00:00
Alex Waygood
e8b4450125 [ty] Remove unneeded disjoint-base special casing for builtins.tuple (#20414) 2025-09-15 13:12:20 +01:00
renovate[bot]
eb71536dce Update dependency monaco-editor to ^0.53.0 (#20395)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 13:47:51 +02:00
Alex Waygood
8341da7f63 [ty] Allow annotation expressions to be ast::Attribute nodes (#20413)
Fixes https://github.com/astral-sh/ty/issues/1187
2025-09-15 12:06:48 +01:00
renovate[bot]
1f1365a0fa Update dependency vite to v7.0.7 (#20323)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:57:06 +02:00
renovate[bot]
25c13ea91c Update CodSpeedHQ/action action to v4 (#20409)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-15 11:44:52 +02:00
renovate[bot]
6581f9bf2a Update actions/github-script action to v8 (#20406)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:28:18 +02:00
renovate[bot]
c4b0d10438 Update actions/download-artifact action to v5 (#20405)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:26:47 +02:00
renovate[bot]
3adb478c6b Update actions/setup-python action to v6 (#20408)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:25:04 +02:00
renovate[bot]
ac8ac2c677 Update actions/setup-node action to v5 (#20407)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:24:05 +02:00
renovate[bot]
2c8aa6e9e3 Update dependency node to v22 (#20410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:23:37 +02:00
Takayuki Maeda
093fa72656 [ty] Include NamedTupleFallback members in NamedTuple instance completions (#20356)
## Summary

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

Include `NamedTupleFallback` members in `NamedTuple` instance
completions.

- Augment instance attribute completions when completing on NamedTuple
instances by merging members from
`_typeshed._type_checker_internals.NamedTupleFallback`

## Test Plan

Adds a minimal completion test `namedtuple_fallback_instance_methods`

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-15 11:00:03 +02:00
David Peter
02c58f1beb [ty] Remove 'materialize' from the ecosystem projects (#20412)
## Summary

This project was [recently removed from
mypy_primer](https://github.com/astral-sh/ruff/pull/20378), so we need
to remove it from `good.txt` in order for ecosystem-analyzer to work
correctly.

## Test Plan

Run mypy_primer and ecosystem-analyzer on this branch.
2025-09-15 10:42:35 +02:00
github-actions[bot]
276ee1bb1e [ty] Sync vendored typeshed stubs (#20394)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
Co-authored-by: David Peter <mail@david-peter.de>
2025-09-15 09:30:28 +02:00
renovate[bot]
9e4acd8bdd Update actions/checkout action to v5 (#20404) 2025-09-14 22:51:05 -04:00
renovate[bot]
e061c39119 Update taiki-e/install-action action to v2.61.3 (#20403) 2025-09-14 22:50:58 -04:00
renovate[bot]
9299bb42ca Update Rust crate ctrlc to v3.5.0 (#20398) 2025-09-14 22:50:52 -04:00
renovate[bot]
876d800205 Update Rust crate rayon to v1.11.0 (#20400) 2025-09-15 01:54:11 +00:00
renovate[bot]
89d83282b9 Update Rust crate tempfile to v3.22.0 (#20401) 2025-09-15 01:53:05 +00:00
renovate[bot]
97ebb1492f Update Rust crate uuid to v1.18.1 (#20402) 2025-09-15 01:51:31 +00:00
renovate[bot]
afc207ec0f Update Rust crate camino to v1.2.0 (#20397) 2025-09-15 01:50:11 +00:00
renovate[bot]
326c878adb Update dependency ruff to v0.13.0 (#20396) 2025-09-14 21:44:23 -04:00
renovate[bot]
b9a96535bc Update astral-sh/setup-uv action to v6.7.0 (#20389) 2025-09-14 21:29:47 -04:00
renovate[bot]
59330be95d Update Rust crate serde_json to v1.0.145 (#20387) 2025-09-14 21:29:41 -04:00
renovate[bot]
963974146d Update Rust crate serde to v1.0.223 (#20386) 2025-09-14 21:29:36 -04:00
renovate[bot]
4dc28a483e Update Rust crate pyproject-toml to v0.13.6 (#20385) 2025-09-14 21:29:29 -04:00
renovate[bot]
850eefc0c4 Update Rust crate ordermap to v0.5.10 (#20384) 2025-09-14 21:29:16 -04:00
renovate[bot]
fc3e571804 Update Rust crate indexmap to v2.11.1 (#20383) 2025-09-14 21:29:10 -04:00
renovate[bot]
0a36b1e4d7 Update cargo-bins/cargo-binstall action to v1.15.5 (#20382) 2025-09-14 21:28:58 -04:00
renovate[bot]
72e6698550 Update Rust crate unicode-ident to v1.0.19 (#20388) 2025-09-14 21:27:18 -04:00
Alex Waygood
9edbeb44dd bump mypy_primer pin (#20378)
Pulls in
06849fda40
2025-09-14 19:16:35 +01:00
William Woodruff
1fa64a24b8 Revert "[ruff]: Build loongarch64 binaries in CI (#20361)" (#20372) 2025-09-12 17:21:04 -04:00
Alex Waygood
1745554809 [ty] Temporary hack to reduce false positives around builtins.open() (#20367)
## Summary

https://github.com/astral-sh/ruff/pull/20165 added a lot of false
positives around calls to `builtins.open()`, because our missing support
for PEP-613 type aliases means that we don't understand typeshed's
overloads for `builtins.open()` at all yet, and therefore always select
the first overload. This didn't use to matter very much, but now that we
have a much stricter implementation of protocol assignability/subtyping
it matters a lot, because most of the stdlib functions dealing with I/O
(`pickle`, `marshal`, `io`, `json`, etc.) are annotated in typeshed as
taking in protocols of some kind.

In lieu of full PEP-613 support, which is blocked on various things and
might not land in time for our next alpha release, this PR adds some
temporary special-casing for `builtins.open()` to avoid the false
positives. We just infer `Todo` for anything that isn't meant to match
typeshed's first `open()` overload. This should be easy to rip out again
once we have proper support for PEP-613 type aliases, which hopefully
should be pretty soon!

## Test Plan

Added an mdtest
2025-09-12 22:20:38 +01:00
Alex Waygood
98708976e4 [ty] Fix subtyping/assignability of function- and class-literal types to callback protocols (#20363)
## Summary

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

We were treating any function as being assignable to any callback
protocol, because we were trying to figure out a type's `Callable`
supertype by looking up the `__call__` attribute on the type's
meta-type. But a function-literal's meta-type is `types.FunctionType`,
and `types.FunctionType.__call__` is `(...) -> Any`, which is not very
helpful!

While working on this PR, I also realised that assignability between
class-literals and callback protocols was somewhat broken too, so I
fixed that at the same time.

## Test Plan

Added mdtests
2025-09-12 22:20:09 +01:00
Igor Drokin
c7f6b85fb3 [ruff] Allow dataclass attribute value instantiation from nested frozen dataclass (RUF009) (#20352)
## Summary
Resolves #20266

Definition of the frozen dataclass attribute can be instantiation of a
nested frozen dataclass as well as a non-nested one.

### Problem explanation
The `function_call_in_dataclass_default` function is invoked during the
"defined scope" stage, after all scopes have been processed. At this
point, the semantic references the top-level scope. When
`SemanticModel::lookup_attribute` executes, it searches for bindings in
the top-level module scope rather than the class scope, resulting in an
error.

To solve this issue, the lookup should be evaluated through the class
scope.

## Test Plan
- Added test case from issue

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
2025-09-12 16:46:49 -04:00
Takayuki Maeda
151ba49b36 [pyupgrade] Prevent infinite loop with I002 and UP026 (#20327)
## Summary

Fixes #19842

Prevent infinite loop with I002 and UP026

- Implement isort-aware handling for UP026 (deprecated mock import):
- Add CLI integration tests in crates/ruff/tests/lint.rs:

## Test Plan

I have added two integration tests
`pyupgrade_up026_respects_isort_required_import_fix` and
`pyupgrade_up026_respects_isort_required_import_from_fix` in
`crates/ruff/tests/lint.rs`.
2025-09-12 15:45:26 -05:00
Carl Meyer
82796df9b5 [ty] add cycle handling to BoundMethodType::into_callable_type() (#20369)
## Summary

This looks like it should fix the errors that we've been seeing in sympy
in recent mypy-primer runs.

## Test Plan

I wasn't able to reproduce the sympy failures locally; it looks like
there is probably a dependency on the order in which files are checked.
So I don't have a minimal reproducible example, and wasn't able to add a
test :/ Obviously I would be happier if we could commit a regression
test here, but since the change is straightforward and clearly
desirable, I'm not sure how many hours it's worth trying to track it
down.

Mypy-primer is still failing in CI on this PR, because it fails on the
"old" ty commit already (i.e. on main). But it passes [on a no-op PR
stacked on top of this](https://github.com/astral-sh/ruff/pull/20370),
which strongly suggests this PR fixes the problem.
2025-09-12 13:39:38 -07:00
Igor Drokin
dfec94608c [flake8-bugbear] Mark the fix for unreliable-callable-check as always unsafe (B004) (#20318)
## Summary
Resolves #20282

Makes the rule fix always unsafe, because the replacement may not be
semantically equivalent to the original expression, potentially changing
the behavior of the code.

Updated docstring with examples.

## Test Plan
- Added two tests from issue and regenerated the snapshot

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-12 19:27:17 +00:00
Takayuki Maeda
ff677a96e4 [ruff] Recognize t-strings, generators, and lambdas in invalid-index-type (RUF016) (#20213)
## Summary

Fixes #20204

Recognize t-strings, generators, and lambdas in RUF016

- Accept boolean literals as valid index and slice bounds.
- Add TString, Generator, and Lambda to `CheckableExprType`.
- Expand RUF016.py fixture and update snapshots accordingly.
2025-09-12 13:37:02 -05:00
Dylan
b6bd32d9dc Track t-strings and f-strings for token-based rules and suppression comments (#20357)
Our token-based rules and `noqa` extraction used an `Indexer` that kept
track of f-string ranges but not t-strings. We've updated the `Indexer`
and downstream uses thereof to handle both f-strings and t-strings.

Most of the diff is renaming and adding tests.

Note that much of the "new" logic gets to be naive because the lexer has
already ensured that f and t-string "starts" are paired with their
respective "ends", even amidst nesting and so on.

Finally: one could imagine wanting to know if a given interpolated
string range corresponds to an f-string or a t-string, but I didn't find
a place where we actually needed this.

Closes #20310
2025-09-12 13:00:12 -05:00
SkyBird
ec863bcde7 [ruff]: Build loongarch64 binaries in CI (#20361)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

This PR adds support for building loongarch64 binaries in CI. As such
support has been merged in uv (astral-sh/uv#15387) it's time to consider
adding it to ruff.

Please note that as Ubuntu is not yet available for loongarch64, I have
elected to use a Debian Trixie container maintained by community
members. In addition, as Debian's pip does not allow installing modules
system-wide, I have modified the workflow to install additional modules
in a virtual environment.

Since the workflow is shared between all targets, the only way to handle
this difference (between Debian and Ubuntu) is just to install pip in a
venv for all targets. If there is a better (and less intrusive) way to
work around this, please let me know.

## Test Plan

Tests are included in CI and the loongarch64 artifacts built in [this
workflow](https://github.com/SkyBird233/ruff/actions/runs/17640270032/job/50125471548)
has been smoke tested.
2025-09-12 13:49:13 -04:00
Dan Parizher
7be11b496d [flake8-simplify] Detect unnecessary None default for additional key expression types (SIM910) (#20343)
## Summary

Fixes #20341

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-12 10:17:54 -04:00
Alex Waygood
33b3d44ebd [ty] Proper assignability/subtyping checks for protocols with method members (#20165) 2025-09-12 10:10:31 +00:00
Dhruv Manilawala
bb9be263c7 [ty] Retry parameter matching for argument type expansion (#20153)
## Summary

This PR addresses an issue for a variadic argument when involved in
argument type expansion of overload call evaluation.

The issue is that the expansion of the variadic argument could result in
argument list of different arity. For example, in `*args: tuple[int] |
tuple[int, str]`, the expansion would lead to the variadic argument
being unpacked into 1 and 2 element respectively. This means that the
parameter matching that was performed initially isn't sufficient and
each expanded argument list would need to redo the parameter matching
again.

This is currently done by redoing the parameter matching directly,
maintaining the state of argument forms (and the conflicting forms), and
updating the `Bindings` values if it changes.

Closes: astral-sh/ty#735

## Test Plan

Update existing mdtest.
2025-09-12 08:40:07 +00:00
Douglas Creager
1cd8ab3f26 [ty] Remove the Constraints trait (#20355)
This PR removes the `Constraints` trait. We removed the `bool`
implementation several weeks back, and are using `ConstraintSet`
everywhere. There have been discussions about trying to include the
reason for an assignability failure as part of the result, but that
there are no concrete plans to do so soon, and it's not clear that we'll
need the `Constraints` trait to do that. (We can ideally just update the
`ConstraintSet` type directly.)

In the meantime, this just complicates the code for no good reason.

This PR is a pure refactoring, and contains no behavioral changes.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-11 20:55:28 -04:00
Ibraheem Ahmed
36888198a6 [ty] Integrate type context for bidirectional inference (#20337)
## Summary

Adds the infrastructure necessary to perform bidirectional type
inference (https://github.com/astral-sh/ty/issues/168) without any
typing changes.
2025-09-11 15:19:12 -04:00
Carl Meyer
c4cd5c00fd [ty] guard against recursion in determining completion kind (#20354)
## Summary

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

## Test Plan

Added test.
2025-09-11 11:25:06 -07:00
Brent Westbrook
5bf6977ded Move GitHub rendering to ruff_db (#20320)
## Summary

This is the GitHub analog to #20117. This PR prepares to add a GitHub
output format to ty by moving the implementation from `ruff_linter` to
`ruff_db`. Hopefully this one is a bit easier to review
commit-by-commit. Almost all of the refactoring this time is in the
first commit, then the second commit adds the new `OutputFormat` variant
and moves the file into `ruff_db`. The third commit is just a small
touch up to use a private method that accommodates ty files so that we
can run the tests and update/move the snapshots.

I had to push a fourth commit to fix and test diagnostics without a
span/file.

## Test Plan

Existing tests
2025-09-11 13:11:15 -04:00
Douglas Creager
abb705aa4e [ty] Add dedicated variant for NominalInstance(object) (#20340)
Previously, `Type::object` would find the definition of the `object`
class in typeshed, load that in (to produce a `ClassLiteral` and
`ClassType`), and then create a `NominalInstance` of that class.

It's possible that we are using a typeshed that doesn't define `object`.
We will not be able to do much useful work with that kind of typeshed,
but it's still a possibility that we have to support at least without
panicking. Previously, we would handle this situation by falling back on
`Unknown`.

In most cases, that's a perfectly fine fallback! But `object` is also
our top type — the type of all values. `Unknown` is _not_ an acceptable
stand-in for the top type.

This PR adds a new `NominalInstance` variant for "instances of
`object`". Unlike other nominal instances, we do not need to load in
`object`'s `ClassType` to instantiate this variant. We will use this new
variant even when the current typeshed does not define an `object`
class, ensuring that we have a fully static representation of our top
type at all times.

There are several operations that need access to a nominal instance's
class, and for this new `object` variant we load it lazily only when
it's needed. That means this operation is now fallible, since this is
where the "typeshed doesn't define `object`" failure shows up.

This new approach also has the benefit of avoiding some salsa cycles
that were cropping up while I was debugging #20093, since the new
constraint set representation was trying to instantiate `Type::object`
while in the middle of processing its definition in typeshed. Cycle
handling was kicking in correctly and returning the `Unknown` fallback
mentioned above. But the constraint set implementation depends on
`Type::object` being a distinct and fully static type, highlighting that
this is a correctness fix, not just an optimization fix.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-11 13:02:58 -04:00
Alex Waygood
0e3697a643 [ty] Minor fixes to Protocol tests (#20347) 2025-09-11 14:42:13 +00:00
Carl Meyer
ffd4340dce [ty] use Type::Divergent to avoid panic in infinitely-nested-tuple implicit attribute (#20333)
## Summary

Use `Type::Divergent` to avoid "too many iterations" panic on an
infinitely-nested tuple in an implicit instance attribute.

The regression here is from checking all tuple elements to see if they
contain a Divergent type. It's 5% on one project, 1% on another, and
zero on the rest. I spent some time looking into eliminating this
regression by tracking a flag on inference results to note if they could
possibly contain any Divergent type, but this doesn't really work --
there are too many different ways a type containing a Divergent type
could enter an inference result. Still thinking about whether there are
other ways to reduce this. One option is if we see certain kinds of
non-atomic types that are commonly expensive to check for Divergent, we
could make `has_divergent_type` a Salsa query on those types.

## Test Plan

Added mdtest.

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-11 06:51:22 -07:00
Alex Waygood
89f17467ef [ty] Require that implementors of Constraints also implement Debug (#20348)
The debug representation isn't as useful as calling `.display(db)`, but
it's still kind-of annoying when `dbg!()` calls don't compile locally
due to the compiler not being able to guarantee that an object of type
`impl Constraints` implements `Debug`
2025-09-11 14:34:40 +01:00
David Peter
59c8fda3f8 [ty] Fix CallableTypeOf[…] for classmethods (#20345)
## Summary

See https://github.com/astral-sh/ruff/pull/20338#discussion_r2337731998

## Test Plan

Regression test.
2025-09-11 10:14:38 +02:00
Carl Meyer
c6b92b918e [ty] split up types/infer.rs (#20342) 2025-09-10 19:25:07 -07:00
Amethyst Reese
a3ec8ca9df Remove Diagnostic::expect_range and all consumers (#20322)
Replace usage with `range().unwrap_or_default()` or more appropriate
alternatives based on context.
2025-09-10 17:19:20 -07:00
Takayuki Maeda
12c337c948 [RUF102] Respect rule redirects in invalid rule code detection (#20245)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #20235

• Fix `RUF102` to properly handle rule redirects when validating noqa
codes
• Update `code_is_valid` to check redirect targets before determining
validity
• Add test case for rule redirects (TCH002 in this case)

## Test Plan

<!-- How was it tested? -->

I have added a test case for rule redirects to
`crates/ruff_linter/resources/test/fixtures/ruff/RUF102.py`.
2025-09-10 14:27:07 -07:00
Dan Parizher
4c64ba4ee1 [flake8-bandit] Fix truthiness: dict-only ** displays not truthy for shell (S602, S604, S609) (#20177)
## Summary
Fixes #19927
2025-09-10 17:06:33 -04:00
David Peter
cde5e4e343 [ty] Fix CallableTypeOf[…] for bound methods (#20338)
## Summary

`CallableTypeOf[bound_method]` would previously bind `self` to the
bound method type itself, instead of binding it to the instance type
stored inside the bound method type.

## Test Plan

Added regression test
2025-09-10 21:13:23 +02:00
Alex Waygood
8a0edf0da8 [ty] Ensure various special-cased builtin functions are understood as assignable to Callable (#20331) 2025-09-10 19:03:33 +00:00
Alex Waygood
d23cae870e [ty] Ensure various special-cased bound methods are understood as assignable to Callable (#20330) 2025-09-10 19:58:54 +01:00
Douglas Creager
2ac4147435 [ty] Add mdtests that exercise constraint sets (#20319)
This PR adds a new `ty_extensions.ConstraintSet` class, which is used to
expose constraint sets to our mdtest framework. This lets us write a
large collection of unit tests that exercise the invariants and rewrite
rules of our constraint set implementation.

As part of this, `is_assignable_to` and friends are updated to return a
`ConstraintSet` instead of a `bool`, and we implement
`ConstraintSet.__bool__` to return when a constraint set is always
satisfied. That lets us still use
`static_assert(is_assignable_to(...))`, since the assertion will coerce
the constraint set to a bool, and also lets us
`reveal_type(is_assignable_to(...))` to see more detail about
whether/when the two types are assignable. That lets us get rid of
`reveal_when_assignable_to` and friends, since they are now redundant
with the expanded capabilities of `is_assignable_to`.
2025-09-10 13:22:19 -04:00
Alex Waygood
ffead90410 [ty] Add more tests for special-cased builtin functions and methods (#20329) 2025-09-10 18:08:32 +01:00
Brent Westbrook
a1fdd66f10 Bump 0.13.0 (#20336) 2025-09-10 12:11:22 -04:00
Shunsuke Shibayama
8770b95509 [ty] introduce DivergentType (#20312)
## Summary

From #17371

In #17371, `DivergentType` was introduced to prevent type inference for
recursive functions from diverging and causing panics. This turned out
to be useful for other divergent type inferences
(https://github.com/astral-sh/ruff/pull/17371#discussion_r2329337965),
so I extracted the introduction part of `DivergentType` into this PR so
that we can use it without waiting for the merge of #17371.

Note that this PR only introduces `DivergentType` and does not actually
address divergent type inference yet. Please refer to
https://github.com/astral-sh/ruff/pull/17371/files#diff-20b910c6e20faa962bb1642e111db1cbad8e66ace089bdd966ac9d7f9fa99ff2R542-R622
etc. when implementing handling of divergence suppression using this
type.

## Test Plan

---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
2025-09-10 08:32:26 -07:00
David Peter
65982a1e14 [ty] Use 'unknown' specialization for upper bound on Self (#20325)
## Summary

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

## Test Plan

Added a regression test
2025-09-10 17:00:28 +02:00
David Peter
57d1f7132d [ty] Simplify unions of enum literals and subtypes thereof (#20324)
## Summary

When adding an enum literal `E = Literal[Color.RED]` to a union which
already contained a subtype of that enum literal(!), we were previously
not simplifying the union correctly. My assumption is that our property
tests didn't catch that earlier, because the only possible non-trivial
subytpe of an enum literal that I can think of is `Any & E`. And in
order for that to be detected by the property tests, it would have to
randomly generate `Any & E | E` and then also compare that with `E` on
the other side (in an equivalence test, or the subtyping-antisymmetry
test).

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

## Test Plan

* Added a regression test.
* I also ran the property tests for a while, but probably not for two
months worth of daily CI runs.
2025-09-10 15:54:06 +02:00
Loïc Riegel
7a75702237 Ignore deprecated rules unless selected by exact code (#20167)
<!--
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

Closes #18349

After this change:
- All deprecated rules are deselected by default
- They are only selected if the user specifically selects them by code,
e.g. `--select UP038`
- Thus, `--select ALL --select UP --select UP0` won't select the
deprecated rule UP038
- Documented the change in version policy. From now on, deprecating a
rule should increase the minor version

## Test Plan

Integration tests in "integration_tests.rs"

Also tested with a temporary test package:
```
~> ../../ruff/target/debug/ruff.exe check --select UP038
warning: Rule `UP038` is deprecated and will be removed in a future release.
warning: Detected debug build without --no-cache.
UP038 Use `X | Y` in `isinstance` call instead of `(X, Y)`
 --> main.py:2:11
  |
1 | def main():
2 |     print(isinstance(25, (str, int)))
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
help: Convert to `X | Y`

Found 1 error.
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).

~> ../../ruff/target/debug/ruff.exe check --select UP03
warning: Detected debug build without --no-cache.
All checks passed!

~> ../../ruff/target/debug/ruff.exe check --select UP0
warning: Detected debug build without --no-cache.
All checks passed!

~> ../../ruff/target/debug/ruff.exe check --select UP
warning: Detected debug build without --no-cache.
All checks passed!

~> ../../ruff/target/debug/ruff.exe check --select ALL
# warnings and errors, but because of other errors, UP038 was deselected
```
2025-09-10 09:00:27 -04:00
Dylan
9ca632c84f Stabilize adding future import via config option (#20277)
Introduced in #19100. Removed gating, updated tests, removed warning(s),
and updated documentation.
2025-09-10 09:00:27 -04:00
Dylan
64fe7d30a3 [flake8-errmsg] Stabilize extending raw-string-in-exception (EM101) to support byte strings (#20273)
Introduced in #18867. Removed gating, updated tests, updated docs.
2025-09-10 09:00:27 -04:00
Brent Westbrook
beeeb8d5c5 Stabilize the remaining Airflow rules (#20250)
- **Stabilize `airflow3-suggested-update` (`AIR311`)**
- **Stabilize `airflow3-suggested-to-move-to-provider` (`AIR312`)**
- **Stabilize `airflow3-removal` (`AIR301`)**
- **Stabilize `airflow3-moved-to-provider` (`AIR302`)**
- **Stabilize `airflow-dag-no-schedule-argument` (`AIR002`)**

I put this all in one PR to make it easier to double check with @Lee-W
before we merge this. I also made a few minor documentation changes and
updated one error message that I want to make sure are okay. But for the
most part this just moves the rules from `RuleGroup::Preview` to
`RuleGroup::Stable`!

Fixes #17749
2025-09-10 09:00:27 -04:00
Dylan
b6fca52855 [flake8-bugbear] Stabilize support for non-context-manager calls in assert-raises-exception (B017) (#20274)
Introduced in #19063. Removed gating, updated tests. Not documented so
docs are the same.
2025-09-10 09:00:27 -04:00
Dylan
ac7f882c78 [flake8-commas] Stabilize support for trailing comma checks in type parameter lists (COM812, COM819) (#20275)
Introduced in #19390. Removed gating, updated tests. No documentation to
update.
2025-09-10 09:00:27 -04:00
Dylan
aef0a107a8 [pygrep_hooks] Stabilize usingAsyncMock methods in invalid-mock-access (PGH005) (#20272)
Introduced in #18547. Removed gating, updated tests. Not documented so
documentation is the same.
2025-09-10 09:00:27 -04:00
Dylan
512395f4e6 Stabilize new strategy for classifying imports as first party (#20268)
This stabilizes the behavior introduced in #16565 which (roughly) tries
to match an import like `import a.b.c` to an actual directory path
`a/b/c` in order to label it as first-party, rather than simply looking
for a directory `a`.

Mainly this affects the sorting of imports in the presence of namespace
packages, but a few other rules are affected as well.
2025-09-10 09:00:27 -04:00
Dylan
5dec37fbaf [pylint] Stabilize ignoring __init__.py for useless-import-alias (PLC0414) (#20271)
Stabilizes change from #18400. Removed gating, updated docs, updated
tests.
2025-09-10 09:00:27 -04:00
Dylan
4bda9dad68 [pylint] Stabilize adding U+061C to bidirectional-unicode (PLE2502) (#20276)
Introduced in #20106. Removed gating. Updated tests. No documentation to
update.
2025-09-10 09:00:27 -04:00
Dylan
9d972d0583 [flake8-simplify] Stabilize fix safety of multiple-with-statements (SIM117) (#20270)
Introduced in #18208. Removed gating, updated tests and docs.
2025-09-10 09:00:27 -04:00
Brent Westbrook
1bbb553d6f Stabilize pytest-raises-ambiguous-pattern (RUF043) (#20253)
This one has been a bit contentious in the past. It usually uncovers
~700 ecosystem hits. See:

- https://github.com/astral-sh/ruff/pull/16657
- https://github.com/astral-sh/ruff/issues/16690

But I think there's consensus that it's okay to merge as-is. We'd love
an
autofix since it's so common, but we can't reliably tell what a user
meant. The
pattern is ambiguous after all 😆

This is the first rule that actually needed its test case relocated, but
the
docs looked good.
2025-09-10 09:00:27 -04:00
Brent Westbrook
bb4c51afb2 Stabilize f-string-number-format (FURB116) (#20247)
Tests and docs look good
2025-09-10 09:00:27 -04:00
हिमांशु
3dbdd2b883 [pyupgrade] Remove non-pep604-isinstance (UP038) (#19156)
## Summary
This PR Removes deprecated UP038 as per instructed in #18727 
closes #18727 
## Test Plan
I have run tests non of them failing 

One Question i have is do we have to document that UP038 is removed?

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-10 09:00:27 -04:00
हिमांशु
d8e43bf9f7 [pandas-vet] Remove pandas-df-variable-name (PD901) (#19223)
## Summary
closes #7710 

## Test Plan

It is is removal so i don't think we have to add tests otherwise i have
followed test plan mentioned in contributing.md

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-10 09:00:27 -04:00
हिमांशु
ee448eab2d Remove deprecated macOS config file discovery (#19210)
## Summary
In #11115 we moved from defaulting to $HOME/Library/Application Support
to $XDG_HOME on macOS and added a deprecation warning if we find files
in the old location. So this PR removes the warning
closes #19145 

## Test Plan
Ran `cargo test`
2025-09-10 09:00:27 -04:00
Brent Westbrook
307b7df027 Stabilize redundant-none-literal (PYI061) (#20236)
Tests and docs look good
2025-09-10 09:00:27 -04:00
Brent Westbrook
e139104aba Stabilize generic-not-last-base-class (PYI059) (#20246)
Tests and docs look good

We nearly stabilized this last time
(https://github.com/astral-sh/ruff/pull/18601), but it needed one more
bug fix and a documentation improvement
(https://github.com/astral-sh/ruff/pull/18611)
2025-09-10 09:00:27 -04:00
Brent Westbrook
9bb9b54168 Stabilize useless-class-metaclass-type (UP050) (#20230)
Tests and docs look good
2025-09-10 09:00:27 -04:00
Brent Westbrook
262f2767ca Stabilize os-symlink (PTH211) (#20229)
Summary
--

Rule and test/snapshot updated, the docs look good

My one hesitation here is that we could hold off stabilizing the rule
until its fix is also ready for stabilization, but this is also the only
preview PTH rule, so I think it's okay to stabilize the rule and later
(probably in the next minor release) stabilize the fixes together.
2025-09-10 09:00:27 -04:00
Brent Westbrook
1de9dac9d5 Stabilize unused-unpacked-variable (RUF059) (#20233)
The tests looked good. For the docs, I added a `## See also` section
pointing to
the closely-related F841 (unused-variable) and the corresponding section
to F841
pointing back to RUF059. It seems like you'd probably want both of these
active
or at least to know about the other when reading the docs.
2025-09-10 09:00:27 -04:00
Brent Westbrook
1cf6c2439f Stabilize long-sleep-not-forever (ASYNC116) (#20244)
Tests and docs look good
2025-09-10 09:00:27 -04:00
David Peter
2b51ec6531 [ty] Improve specialization-error diagnostics (#20326)
## Summary

Add information about the upper bound or the constraints of the type
variable to the `SpecializationError` diagnostics.
2025-09-10 14:01:23 +02:00
Alex Waygood
b85c995927 [ty] "foo".startswith is not an instance of types.MethodWrapperType (#20317) 2025-09-10 11:14:26 +00:00
Alex Waygood
fd7eb1e22f [ty] Allow protocols to participate in nominal subtyping as well as structural subtyping (#20314) 2025-09-10 11:05:50 +00:00
Alex Waygood
4de7d653bd [ty] Treat Hashable, and similar protocols, equivalently to object for subtyping/assignability (#20284) 2025-09-10 11:38:58 +01:00
Brent Westbrook
9cb37db510 Bump LibCST to 1.8.4 (#20321)
This should fix the fuzz build on `main`. They added support for
t-strings, which made one of our matches non-exhaustive.

https://github.com/Instagram/LibCST/releases/tag/v1.8.4
2025-09-09 17:34:33 -04:00
Douglas Creager
ed06fb5ce2 [ty] Use partial-order-friendly representation of typevar constraints (#20306)
The constraint representation that we added in #19997 was subtly wrong,
in that it didn't correctly model that type assignability is a _partial_
order — it's possible for two types to be incomparable, with neither a
subtype of the other. That means the negation of a constraint like `T ≤
t` (typevar `T` must be a subtype of `t`) is **_not_** `t < T`, but
rather `t < T ∨ T ≁ t` (using ≁ to mean "not comparable to").

That means we need to update our constraint representation to be an
enum, so that we can track both _range_ constraints (upper/lower bound
on the typevar), and these new _incomparable_ constraints.

Since we need an enum now, that also lets us simplify how we were
modeling range constraints. Before, we let the lower/upper bounds be
either open (<) or closed (≤). Now, range constraints are always closed,
and we add a third kind of constraint for _not equivalent_ (≠). We can
translate an open upper bound `T < t` into `T ≤ t ∧ T ≠ t`.

We already had the logic for doing adding _clauses_ to a _set_ by doing
a pairwise simplification. We copy that over to where we add
_constraints_ to a _clause_. To calculate the intersection or union of
two constraints, the new enum representation makes it easy to break down
all of the possibilities into a small number of cases: intersect range
with range, intersect range with not-equivalent, etc. I've done the math
[here](https://dcreager.net/theory/constraints/) to show that the
simplifications for each of these cases is correct.
2025-09-09 15:54:47 -04:00
Igor Drokin
54df73c9f7 [pyupgrade] Apply UP008 only when the __class__ cell exists (#19424)
## Summary

Resolves #19357 

Skip UP008 diagnostic for `builtins.super(P, self)` calls when
`__class__` is not referenced locally, preventing incorrect fixes.

**Note:** I haven't found concrete information about which cases
`__class__` will be loaded into the scope. Let me know if anyone has
references, it would be useful to enhance the implementation. I did a
lot of tests to determine when `__class__` is loaded. Considered
sources:
1. [Python doc
super](https://docs.python.org/3/library/functions.html#super)
2. [Python doc classes](https://docs.python.org/3/tutorial/classes.html)
3. [pep-3135](https://peps.python.org/pep-3135/#specification)

As I understand it, Python will inject at runtime into local scope a
`__class__` variable if it detects references to `super` or `__class__`.
This allows calling `super()` and passing appropriate parameters.
However, the compiler doesn't do the same for `builtins.super`, so we
need to somehow introduce `__class__` into the local scope.

I figured out `__class__` will be in scope with valid value when two
conditions are met:
1. `super` or `__class__` names have been loaded within function scope
4. `__class__` is not overridden.

I think my solution isn't elegant, so I would be appreciate a detailed
review.

## Test Plan

Added 19 test cases, updated snapshots.

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
2025-09-09 14:59:23 -04:00
Amethyst Reese
d7524ea6d4 Refactor diagnostic start|end location helpers (#20309)
- Renames functions to drop `expect_` from names.
- Make functions return `Option<LineColumn>` to appropriately signal
  when range is not available.
- Update existing consumers to use `unwrap_or_default()`. Uncertain if
  there are better fallback behaviors for individual consumers.
2025-09-09 11:39:31 -07:00
Alex Waygood
bf66178959 [ty] Add tests for protocols with generic method members (#20316) 2025-09-09 16:44:00 +00:00
Zanie Blue
9cdac2d6fb Add support for using uv as an alternative formatter backend (#19665)
This adds a new `backend: internal | uv` option to the LSP
`FormatOptions` allowing users to perform document and range formatting
operations though uv. The idea here is to prototype a solution for users
to transition to a `uv format` command without encountering version
mismatches (and consequently, formatting differences) between the LSP's
version of `ruff` and uv's version of `ruff`.

The primarily alternative to this would be to use uv to discover the
`ruff` version used to start the LSP in the first place. However, this
would increase the scope of a minimal `uv format` command beyond "run a
formatter", and raise larger questions about how uv should be used to
coordinate toolchain discovery. I think those are good things to
explore, but I'm hesitant to let them block a `uv format`
implementation. Another downside of using uv to discover `ruff`, is that
it needs to be implemented _outside_ the LSP; e.g., we'd need to change
the instructions on how to run the LSP and implement it in each editor
integration, like the VS Code plugin.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-09-09 20:39:53 +05:30
Igor Drokin
79706a2e26 [pyupgrade] Enable rule triggering for stub files (UP043) (#20027)
## Summary
Resolves #20011

Implemented alternative triggering condition for rule
[`UP043`](https://docs.astral.sh/ruff/rules/unnecessary-default-type-args/)
based on requirements outlined in [issue
#20011](https://github.com/astral-sh/ruff/issues/20011)
## Test Plan
Created .pyi file to ensure triggering the rule

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-09-09 12:57:26 +00:00
Andrew Gallant
25853e2377 Allow the if_not_else Clippy lint
Specifically, the [`if_not_else`] lint will sometimes flag
code to change the order of `if` and `else` bodies if this
would allow a `!` to be removed. While perhaps tasteful in
some cases, there are many cases in my experience where this
bows to other competing concerns that impact readability.
(Such as the relative sizes of the `if` and `else` bodies,
or perhaps an ordering that just makes the code flow in a
more natural way.)

[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#/if_not_else
2025-09-09 08:49:25 -04:00
Renkai Ge
61f906d8e7 [ty] equality narrowing on enums that don't override __eq__ or __ne__ (#20285)
Add equality narrowing for enums, if they don't override `__eq__` or `__ne__` in an unsafe way.

Follow-up to PR https://github.com/astral-sh/ruff/pull/20164

Fixes https://github.com/astral-sh/ty/issues/939
2025-09-08 16:56:28 -07:00
Shunsuke Shibayama
08a561fc05 [ty] more precise lazy scope place lookup (#19932)
## Summary

This is a follow-up to https://github.com/astral-sh/ruff/pull/19321.

Now lazy snapshots are updated to take into account new bindings on
every symbol reassignment.

```python
def outer(x: A | None):
    if x is None:
        x = A()

    reveal_type(x)  # revealed: A

    def inner() -> None:
        # lazy snapshot: {x: A}
        reveal_type(x)  # revealed: A
    inner()

def outer() -> None:
    x = None

    x = 1

    def inner() -> None:
        # lazy snapshot: {x: Literal[1]} -> {x: Literal[1, 2]}
        reveal_type(x)  # revealed: Literal[1, 2]
    inner()

    x = 2
```

Closes astral-sh/ty#559.

## Test Plan

Some TODOs in `public_types.md` now work properly.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-08 21:08:35 +00:00
Ibraheem Ahmed
aa5d665d52 [ty] Add support for generic PEP695 type aliases (#20219)
## Summary

Adds support for generic PEP695 type aliases, e.g.,
```python
type A[T] = T
reveal_type(A[int]) # A[int]
```

Resolves https://github.com/astral-sh/ty/issues/677.
2025-09-08 13:26:21 -07:00
David Peter
d55edb3d74 [ty] Support "legacy" typing.Self in combination with PEP 695 generic contexts (#20304)
## Summary

Support cases like the following, where we need the generic context to
include both `Self` and `T` (not just `T`):

```py
from typing import Self

class C:
    def method[T](self: Self, arg: T): ...

C().method(1)
```

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

## Test Plan

Added regression test
2025-09-08 16:57:09 +02:00
arielle
ab86ae1760 [pep8-naming] Fix formatting of __all__ (N816) (#20301)
<!--
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

Noticed this was not escaped when writing a project that parses the
result of `ruff rule --outputformat json`. This is visible here:
<https://docs.astral.sh/ruff/rules/mixed-case-variable-in-global-scope/#why-is-this-bad>

## Test Plan

documentation only

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-08 14:40:38 +00:00
David Peter
916968d0ff [ty] Fix signature of NamedTupleLike._make (#20302) 2025-09-08 14:53:17 +02:00
Alex Waygood
deb3d3d150 [ty] Fall back to object for attribute access on synthesized protocols (#20286) 2025-09-08 13:04:37 +01:00
Frank Dana
982a0a2a7c CI: Eliminate warning in fuzz build workflow (#20290)
## Summary

Pr #11919 changed the fuzz build from `taiki-e/install-action` to
`cargo-bins/cargo-binstall` for necessary reasons of version selection.
But it left the `with:` parameter, which the `binstall` action does not
support. As a result, all workflow runs are showing a warning:
> Unexpected input(s) `'tool'`, valid inputs are `['']`

Eliminate the warning by removing the `with` parameter.

## Test Plan

Run CI, determine that the "cargo fuzz build" step no longer includes an
Annotation showing the warning message (quoted above).
2025-09-08 11:33:38 +05:30
renovate[bot]
f893b19930 Update dependency ruff to v0.12.12 (#20293)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [ruff](https://docs.astral.sh/ruff)
([source](https://redirect.github.com/astral-sh/ruff),
[changelog](https://redirect.github.com/astral-sh/ruff/blob/main/CHANGELOG.md))
| `==0.12.11` -> `==0.12.12` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.12.12?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.12.11/0.12.12?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/ruff (ruff)</summary>

###
[`v0.12.12`](https://redirect.github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#01212)

[Compare
Source](https://redirect.github.com/astral-sh/ruff/compare/0.12.11...0.12.12)

##### Preview features

- Show fixes by default
([#&#8203;19919](https://redirect.github.com/astral-sh/ruff/pull/19919))
- \[`airflow`] Convert `DatasetOrTimeSchedule(datasets=...)` to
`AssetOrTimeSchedule(assets=...)` (`AIR311`)
([#&#8203;20202](https://redirect.github.com/astral-sh/ruff/pull/20202))
- \[`airflow`] Improve the `AIR002` error message
([#&#8203;20173](https://redirect.github.com/astral-sh/ruff/pull/20173))
- \[`airflow`] Move `airflow.operators.postgres_operator.Mapping` from
`AIR302` to `AIR301`
([#&#8203;20172](https://redirect.github.com/astral-sh/ruff/pull/20172))
- \[`flake8-async`] Implement `blocking-input` rule (`ASYNC250`)
([#&#8203;20122](https://redirect.github.com/astral-sh/ruff/pull/20122))
- \[`flake8-use-pathlib`] Make `PTH119` and `PTH120` fixes unsafe
because they can change behavior
([#&#8203;20118](https://redirect.github.com/astral-sh/ruff/pull/20118))
- \[`pylint`] Add U+061C to `PLE2502`
([#&#8203;20106](https://redirect.github.com/astral-sh/ruff/pull/20106))
- \[`ruff`] Fix false negative for empty f-strings in `deque` calls
(`RUF037`)
([#&#8203;20109](https://redirect.github.com/astral-sh/ruff/pull/20109))

##### Bug fixes

- Less confidently mark f-strings as empty when inferring truthiness
([#&#8203;20152](https://redirect.github.com/astral-sh/ruff/pull/20152))
- \[`fastapi`] Fix false positive for paths with spaces around
parameters (`FAST003`)
([#&#8203;20077](https://redirect.github.com/astral-sh/ruff/pull/20077))
- \[`flake8-comprehensions`] Skip `C417` when lambda contains
`yield`/`yield from`
([#&#8203;20201](https://redirect.github.com/astral-sh/ruff/pull/20201))
- \[`perflint`] Handle tuples in dictionary comprehensions (`PERF403`)
([#&#8203;19934](https://redirect.github.com/astral-sh/ruff/pull/19934))

##### Rule changes

- \[`pycodestyle`] Preserve return type annotation for `ParamSpec`
(`E731`)
([#&#8203;20108](https://redirect.github.com/astral-sh/ruff/pull/20108))

##### Documentation

- Add fix safety sections to docs
([#&#8203;17490](https://redirect.github.com/astral-sh/ruff/pull/17490),[#&#8203;17499](https://redirect.github.com/astral-sh/ruff/pull/17499))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 11:03:19 +05:30
renovate[bot]
c96ebe3936 Update cargo-bins/cargo-binstall action to v1.15.4 (#20292)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cargo-bins/cargo-binstall](https://redirect.github.com/cargo-bins/cargo-binstall)
| action | patch | `v1.15.3` -> `v1.15.4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>cargo-bins/cargo-binstall (cargo-bins/cargo-binstall)</summary>

###
[`v1.15.4`](https://redirect.github.com/cargo-bins/cargo-binstall/releases/tag/v1.15.4)

[Compare
Source](https://redirect.github.com/cargo-bins/cargo-binstall/compare/v1.15.3...v1.15.4)

*Binstall is a tool to fetch and install Rust-based executables as
binaries. It aims to be a drop-in replacement for `cargo install` in
most cases. Install it today with `cargo install cargo-binstall`, from
the binaries below, or if you already have it, upgrade with `cargo
binstall cargo-binstall`.*

##### In this release:

- clarify `--install-path` behavior when installing from source
([#&#8203;2259](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2259)
[#&#8203;2294](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2294))

##### Other changes:

- bump dependencies
([#&#8203;2295](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2295)
[#&#8203;2298](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2298)
[#&#8203;2299](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2299))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 11:03:07 +05:30
renovate[bot]
22ca5dd890 Update dependency mdformat-mkdocs to v4.4.1 (#20299)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[mdformat-mkdocs](https://redirect.github.com/kyleking/mdformat-mkdocs)
([changelog](https://redirect.github.com/kyleking/mdformat-mkdocs/releases))
| `==4.3.0` -> `==4.4.1` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mdformat-mkdocs/4.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mdformat-mkdocs/4.3.0/4.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>kyleking/mdformat-mkdocs (mdformat-mkdocs)</summary>

###
[`v4.4.1`](https://redirect.github.com/KyleKing/mdformat-mkdocs/releases/tag/v4.4.1)

[Compare
Source](https://redirect.github.com/kyleking/mdformat-mkdocs/compare/v4.4.0...v4.4.1)

##### What's Changed

-
fix([#&#8203;56](https://redirect.github.com/kyleking/mdformat-mkdocs/issues/56)):
narrowly scope escape\_deflist by
[@&#8203;KyleKing](https://redirect.github.com/KyleKing) in
[KyleKing#57](https://redirect.github.com/KyleKing/mdformat-mkdocs/pull/57)

**Full Changelog**:
<https://github.com/KyleKing/mdformat-mkdocs/compare/v4.4.0...v4.4.1>

###
[`v4.4.0`](https://redirect.github.com/KyleKing/mdformat-mkdocs/releases/tag/v4.4.0)

[Compare
Source](https://redirect.github.com/kyleking/mdformat-mkdocs/compare/v4.3.0...v4.4.0)

##### What's Changed

-
fix([#&#8203;54](https://redirect.github.com/kyleking/mdformat-mkdocs/issues/54)):
add 4-space indented deflists by
[@&#8203;KyleKing](https://redirect.github.com/KyleKing) in
[KyleKing#55](https://redirect.github.com/KyleKing/mdformat-mkdocs/pull/55)

**Full Changelog**:
<https://github.com/KyleKing/mdformat-mkdocs/compare/v4.3.0...v4.4.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 11:02:55 +05:30
renovate[bot]
f7995f4aef Update astral-sh/setup-uv action to v6.6.1 (#20291)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
action | patch | `v6.6.0` -> `v6.6.1` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/setup-uv (astral-sh/setup-uv)</summary>

###
[`v6.6.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v6.6.1):
🌈 Fix exclusions in cache-dependency-glob

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v6.6.0...v6.6.1)

##### Changes

Exclusions with a leading `!` in the
[cache-dependency-glob](https://redirect.github.com/astral-sh/setup-uv?tab=readme-ov-file#cache-dependency-glob)
did not work and got fixed with this release. Thank you
[@&#8203;KnisterPeter](https://redirect.github.com/KnisterPeter) for
raising this!

##### 🐛 Bug fixes

- Fix exclusions in cache-dependency-glob
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;546](https://redirect.github.com/astral-sh/setup-uv/issues/546))

##### 🧰 Maintenance

- Bump dependencies
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;547](https://redirect.github.com/astral-sh/setup-uv/issues/547))
- chore: update known versions for 0.8.14
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;543](https://redirect.github.com/astral-sh/setup-uv/issues/543))
- chore: update known versions for 0.8.13
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;536](https://redirect.github.com/astral-sh/setup-uv/issues/536))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 11:00:16 +05:30
renovate[bot]
480fb278d0 Update Rust crate bitflags to v2.9.4 (#20294)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [bitflags](https://redirect.github.com/bitflags/bitflags) |
workspace.dependencies | patch | `2.9.3` -> `2.9.4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>bitflags/bitflags (bitflags)</summary>

###
[`v2.9.4`](https://redirect.github.com/bitflags/bitflags/blob/HEAD/CHANGELOG.md#294)

[Compare
Source](https://redirect.github.com/bitflags/bitflags/compare/2.9.3...2.9.4)

#### What's Changed

- Add Cargo features to readme by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[#&#8203;460](https://redirect.github.com/bitflags/bitflags/pull/460)

**Full Changelog**:
<https://github.com/bitflags/bitflags/compare/2.9.3...2.9.4>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 10:59:59 +05:30
renovate[bot]
b2f364d9cb Update Rust crate clap to v4.5.47 (#20295)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [clap](https://redirect.github.com/clap-rs/clap) |
workspace.dependencies | patch | `4.5.46` -> `4.5.47` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>clap-rs/clap (clap)</summary>

###
[`v4.5.47`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4547---2025-09-02)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.46...v4.5.47)

##### Features

- Added `impl FromArgMatches for ()`
- Added `impl Args for ()`
- Added `impl Subcommand for ()`
- Added `impl FromArgMatches for Infallible`
- Added `impl Subcommand for Infallible`

##### Fixes

- *(derive)* Update runtime error text to match `clap`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 10:59:47 +05:30
renovate[bot]
aa82137d9f Update Rust crate wasm-bindgen-test to v0.3.51 (#20298)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[wasm-bindgen-test](https://redirect.github.com/wasm-bindgen/wasm-bindgen)
| workspace.dependencies | patch | `0.3.50` -> `0.3.51` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 10:59:29 +05:30
renovate[bot]
adfe2438e9 Update Rust crate log to v0.4.28 (#20297)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [log](https://redirect.github.com/rust-lang/log) |
workspace.dependencies | patch | `0.4.27` -> `0.4.28` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>rust-lang/log (log)</summary>

###
[`v0.4.28`](https://redirect.github.com/rust-lang/log/blob/HEAD/CHANGELOG.md#0428---2025-09-02)

[Compare
Source](https://redirect.github.com/rust-lang/log/compare/0.4.27...0.4.28)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 10:59:09 +05:30
renovate[bot]
3247991429 Update Rust crate insta to v1.43.2 (#20296)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [insta](https://insta.rs/)
([source](https://redirect.github.com/mitsuhiko/insta)) |
workspace.dependencies | patch | `1.43.1` -> `1.43.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>mitsuhiko/insta (insta)</summary>

###
[`v1.43.2`](https://redirect.github.com/mitsuhiko/insta/blob/HEAD/CHANGELOG.md#1432)

[Compare
Source](https://redirect.github.com/mitsuhiko/insta/compare/1.43.1...1.43.2)

- Fix panics when `cargo metadata` fails to execute or parse (e.g., when
cargo is not in PATH or returns invalid output). Now falls back to using
the manifest directory as the workspace root.
[#&#8203;798](https://redirect.github.com/mitsuhiko/insta/issues/798)
([@&#8203;adriangb](https://redirect.github.com/adriangb))
- Fix clippy `uninlined_format_args` lint warnings.
[#&#8203;801](https://redirect.github.com/mitsuhiko/insta/issues/801)
- Changed diff line numbers to 1-based indexing.
[#&#8203;799](https://redirect.github.com/mitsuhiko/insta/issues/799)
- Preserve snapshot names with `INSTA_GLOB_FILTER`.
[#&#8203;786](https://redirect.github.com/mitsuhiko/insta/issues/786)
- Bumped `libc` crate to `0.2.174`, fixing building on musl targets, and
increasing the MSRV of
`insta` to `1.64.0` (released Sept 2022).
[#&#8203;784](https://redirect.github.com/mitsuhiko/insta/issues/784)
- Fix clippy 1.88 errors.
[#&#8203;783](https://redirect.github.com/mitsuhiko/insta/issues/783)
- Fix source path in snapshots for non-child workspaces.
[#&#8203;778](https://redirect.github.com/mitsuhiko/insta/issues/778)
- Add lifetime to Selector in redaction iterator.
[#&#8203;779](https://redirect.github.com/mitsuhiko/insta/issues/779)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 10:58:52 +05:30
justin
08fcf7e106 [ty] initial support for slots=True in dataclasses (#20278) 2025-09-07 18:25:35 +01:00
Carl Meyer
2467c4352e [ty] propagate visitors and constraints through has_relation_in_invariant_position (#20259)
## Summary

The sub-checks for assignability and subtyping of materializations
performed in `has_relation_in_invariant_position` and
`is_subtype_in_invariant_position` need to propagate the
`HasRelationToVisitor`, or we can stack overflow.

A side effect of this change is that we also propagate the
`ConstraintSet` through, rather than using `C::from_bool`, which I think
may also become important for correctness in cases involving type
variables (though it isn't testable yet, since we aren't yet actually
creating constraints other than always-true and always-false.)

## Test Plan

Added mdtest (derived from code found in pydantic) which
stack-overflowed before this PR.

With this change incorporated, pydantic now checks successfully on my
draft PR for PEP 613 TypeAlias support.
2025-09-06 00:17:17 +00:00
Amethyst Reese
a27c64811e Add support for sorting diagnostics without a range (#20257)
- Update ruff ordering compare to work with optional ranges (treating
  them as starting from zero)
2025-09-05 15:23:30 -07:00
Alex Waygood
5d52902e18 [ty] Implement the legacy PEP-484 convention for indicating positional-only parameters (#20248)
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-05 17:56:06 +01:00
Eric Mark Martin
eb6154f792 [ty] add doc-comments for some variance stuff (#20189) 2025-09-05 15:03:10 +01:00
David Peter
fdfb51b595 [ty] Update mypy_primer, add egglog-python project (#20078)
Now that https://github.com/astral-sh/ruff/pull/20263 is merged, we can
update mypy_primer and add the new `egglog-python` project to
`good.txt`. The ecosystem-analyzer run shows that we now add 1,356
diagnostics (where we had over 5,000 previously, due to the unsupported
project layout).
2025-09-05 14:17:07 +02:00
David Peter
7ee863b6d7 [ty] Include python folder in environment.root if it exists (#20263)
## Summary

I felt it was safer to add the `python` folder *in addition* to a
possibly-existing `src` folder, even though the `src` folder only
contains Rust code for `maturin`-based projects. There might be
non-maturin projects where a `python` folder exists for other reasons,
next to a normal `src` layout.

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

## Test Plan

Tested locally on the egglog-python project.
2025-09-05 13:53:48 +02:00
David Peter
8ade6c4eaf [ty] Add backreferences to TypedDict items in diagnostics (#20262)
## Summary

Add backreferences to the original item declaration in TypedDict
diagnostics.

Thanks to @AlexWaygood for the suggestion.

## Test Plan

Updated snapshots
2025-09-05 12:38:37 +02:00
David Peter
9e45bfa9fd [ty] Cover full range of annotated assignments (#20261)
## Summary

An annotated assignment `name: annotation` without a right-hand side was
previously not covered by the range returned from
`DefinitionKind::full_range`, because we did expand the range to include
the right-hand side (if there was one), but failed to include the
annotation.

## Test Plan

Updated snapshot tests
2025-09-05 10:12:40 +02:00
David Peter
7509d376eb [ty] Minor: 'can not' => cannot (#20260) 2025-09-05 09:19:14 +02:00
David Peter
a24a4b55ee [ty] TypedDict: Add support for typing.ReadOnly (#20241)
## Summary

Add support for `typing.ReadOnly` as a type qualifier to mark
`TypedDict` fields as being read-only. If you try to mutate them, you
get a new diagnostic:

<img width="787" height="234" alt="image"
src="https://github.com/user-attachments/assets/f62fddf9-4961-4bcd-ad1c-747043ebe5ff"
/>


## Test Plan

* New Markdown tests
* The typing conformance changes are all correct. There are some false
negatives, but those are related to the missing support for the
functional form of `TypedDict`, or to overriding of fields via
inheritance. Both of these topics are tracked in
https://github.com/astral-sh/ty/issues/154
2025-09-04 15:37:42 -07:00
Alex Waygood
888a22e849 [ty] Reduce false positives for ParamSpecs and TypeVarTuples (#20239) 2025-09-04 23:34:37 +01:00
Jelle Zijlstra
08c1d3660c [ty] Narrow specialized generics using isinstance() (#20256)
Closes astral-sh/ty#456. Part of astral-sh/ty#994.

After all the foundational work, this is only a small change, but let's
see if it exposes any unresolved issues.
2025-09-04 15:28:33 -07:00
Takayuki Maeda
670fffef37 [ruff] Use helper function for empty f-string detection in in-empty-collection (RUF060) (#20249)
## Summary

Fixes #20238

Replace inline f-string emptiness check with `is_empty_f_string` helper
function
2025-09-04 20:20:59 +00:00
Jelle Zijlstra
de63f408b9 [ty] Attribute access on top/bottom materializations (#20221)
## Summary

Part of astral-sh/ty#994. The goal of this PR was to add correct
behavior for attribute access on the top and bottom materializations.
This is necessary for the end goal of using the top materialization for
narrowing generics (`isinstance(x, list)`): we want methods like
`x.append` to work correctly in that case.

It turned out to be convenient to represent materialization as a
TypeMapping, so it can be stashed in the `type_mappings` list of a
function object. This also allowed me to remove most concrete
`materialize` methods, since they usually just delegate to the subparts
of the type, the same as other type mappings. That is why the net effect
of this PR is to remove a few hundred lines.

## Test Plan

I added a few more tests. Much of this PR is refactoring and covered by
existing tests.

## Followups

Assigning to attributes of top materializations is not yet covered. This
seems less important so I'd like to defer it.

I noticed that the `materialize` implementation of `Parameters` was
wrong; it did the same for the top and bottom materializations. This PR
makes the bottom materialization slightly more reasonable, but
implementing this correctly will require extending the struct.
2025-09-04 12:01:44 -07:00
Alex Waygood
555b9f78d6 [ty] Minor cleanups (#20240)
## Summary

Two minor cleanups:
- Return `Option<ClassType>` rather than `Option<ClassLiteral>` from
`TypeInferenceBuilder::class_context_of_current_method`. Now that
`ClassType::is_protocol` exists as a method as well as
`ClassLiteral::is_protocol`, this simplifies most of the call-sites of
the `class_context_of_current_method()` method.
- Make more use of the `MethodDecorator::try_from_fn_type` method in
`class.rs`. Under the hood, this method uses the new methods
`FunctionType::is_classmethod()` and `FunctionType::is_staticmethod()`
that @sharkdp recently added, so it gets the semantics more precisely
correct than the code it's replacing in `infer.rs` (by accounting for
implicit staticmethods/classmethods as well as explicit ones). By using
these methods we can delete some code elsewhere (the
`FunctionDecorators::from_decorator_types()` constructor)

## Test Plan

Existing tests
2025-09-04 10:25:49 -07:00
Dylan
c6516e9b60 Bump 0.12.12 (#20242) 2025-09-04 11:35:56 -05:00
David Peter
1aaa0847ab [ty] More tests for TypedDict (#20205)
## Summary

A small set of additional tests for `TypedDict` that I wrote while going
through the spec. Note that this certainly doesn't make the test suite
exhaustive (see remaining open points in the updated list here:
https://github.com/astral-sh/ty/issues/154).
2025-09-04 15:55:42 +00:00
Charlie Marsh
b49aa35074 Split LICENSE addendum by derivation type (#20222)
## Summary

Per
https://github.com/astral-sh/ruff/issues/20191#issuecomment-3251131478,
this PR restructures the license file to draw a distinction between
projects from which we've (e.g.) drawn source code and projects whose
rules we've implemented but have otherwise not reused or adapted source
code from, which are credited in the README. While I was here, I also
sorted the list.
2025-09-03 21:35:33 -04:00
Samuel Rigaud
1e34f3f20a [ty] Fix small test typo (#20220)
Small typo in the comment of a test

Co-authored-by: Samuel Rigaud <rigaud@gmail.com>
2025-09-03 15:24:17 -07:00
Douglas Creager
77b2cee223 [ty] Add functions for revealing assignability/subtyping constraints (#20217)
This PR adds two new `ty_extensions` functions,
`reveal_when_assignable_to` and `reveal_when_subtype_of`. These are
closely related to the existing `is_assignable_to` and `is_subtype_of`,
but instead of returning when the property (always) holds, it produces a
diagnostic that describes _when_ the property holds. (This will let us
construct mdtests that print out constraints that are not always true or
always false — though we don't currently have any instances of those.)

I did not replace _every_ occurrence of the `is_property` variants in
the mdtest suite, instead focusing on the generics-related tests where
it will be important to see the full detail of the constraint sets.

As part of this, I also updated the mdtest harness to accept the shorter
`# revealed:` assertion format for more than just `reveal_type`, and
updated the existing uses of `reveal_protocol_interface` to take
advantage of this.
2025-09-03 16:44:35 -04:00
Dan Parizher
200349c6e8 [flake8-comprehensions] Skip C417 when lambda contains yield/yield from (#20201)
## Summary

Fixes #20198
2025-09-03 16:39:11 -04:00
David Peter
0d4f7dde99 [ty] Treat __new__ as a static method (#20212)
## Summary

Pull this out of https://github.com/astral-sh/ruff/pull/18473 as an
isolated change to make sure it has no adverse effects.

The wrong behavior is observable on `main` for something like
```py
class C:
    def __new__(cls) -> "C":
        cls.x = 1

C.x  # previously: Attribute `x` can only be accessed on instances
     # now:        Type `<class 'C'>` has no attribute `x`
```
where we currently treat `x` as an *instance* attribute (because we
consider `__new__` to be a normal function and `cls` to be the "self"
attribute). With this PR, we do not consider `x` to be an attribute,
neither on the class nor on instances of `C`. If this turns out to be an
important feature, we should add it intentionally, instead of
accidentally.

## Test Plan

Ecosystem checks.
2025-09-03 19:55:20 +02:00
Shahar Naveh
cb1ba0d4c2 Expose Indentation in ruff_python_codegen (#20216)
## Summary

I'm trying to reduce code complexity for
[RustPython](https://github.com/RustPython/RustPython), we have this
file:
056795eed4/compiler/codegen/src/unparse.rs
which can be replaced entirely by `ruff_python_codegen::Generator`.
Unfortunately we can not create an instance of `Generator` easily,
because `Indentation` is not exported at
cda376afe0/crates/ruff_python_codegen/src/lib.rs (L3)

I have managed to bypass this restriction by doing:
```rust
let contents = r"x = 1";
let module = ruff_python_parser::parse_module(contents).unwrap();
let stylist = ruff_python_codegen::Stylist::from_tokens(module.tokens(), contents);
stylist.indentation()
```

But ideally I'd rather use:
```rust
ruff_python_codegen::Indentation::default()
```
2025-09-03 13:32:31 -04:00
Renkai Ge
cda376afe0 [ty]eliminate definitely-impossible types from union in equality narrowing (#20164)
solves https://github.com/astral-sh/ty/issues/939

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-03 08:34:22 -07:00
renovate[bot]
b14fc96141 Update Rust crate tracing-subscriber to v0.3.20 (#20162)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tracing-subscriber](https://tokio.rs)
([source](https://redirect.github.com/tokio-rs/tracing)) |
workspace.dependencies | patch | `0.3.19` -> `0.3.20` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

### GitHub Vulnerability Alerts

####
[CVE-2025-58160](https://redirect.github.com/tokio-rs/tracing/security/advisories/GHSA-xwfj-jgwm-7wp5)

### Impact

Previous versions of tracing-subscriber were vulnerable to ANSI escape
sequence injection attacks. Untrusted user input containing ANSI escape
sequences could be injected into terminal output when logged,
potentially allowing attackers to:

- Manipulate terminal title bars
- Clear screens or modify terminal display
- Potentially mislead users through terminal manipulation

In isolation, impact is minimal, however security issues have been found
in terminal emulators that enabled an attacker to use ANSI escape
sequences via logs to exploit vulnerabilities in the terminal emulator.

### Patches

`tracing-subscriber` version 0.3.20 fixes this vulnerability by escaping
ANSI control characters in when writing events to destinations that may
be printed to the terminal.

### Workarounds

Avoid printing logs to terminal emulators without escaping ANSI control
sequences.

### References

https://www.packetlabs.net/posts/weaponizing-ansi-escape-sequences/

### Acknowledgments

We would like to thank [zefr0x](http://github.com/zefr0x) who
responsibly reported the issue at `security@tokio.rs`.

If you believe you have found a security vulnerability in any tokio-rs
project, please email us at `security@tokio.rs`.

---

### Release Notes

<details>
<summary>tokio-rs/tracing (tracing-subscriber)</summary>

###
[`v0.3.20`](https://redirect.github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.20):
tracing-subscriber 0.3.20

[Compare
Source](https://redirect.github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20)

**Security Fix**: ANSI Escape Sequence Injection (CVE-TBD)

#### Impact

Previous versions of tracing-subscriber were vulnerable to ANSI escape
sequence injection attacks. Untrusted user input containing ANSI escape
sequences could be injected into terminal output when logged,
potentially allowing attackers to:

- Manipulate terminal title bars
- Clear screens or modify terminal display
- Potentially mislead users through terminal manipulation

In isolation, impact is minimal, however security issues have been found
in terminal emulators that enabled an attacker to use ANSI escape
sequences via logs to exploit vulnerabilities in the terminal emulator.

#### Solution

Version 0.3.20 fixes this vulnerability by escaping ANSI control
characters in when writing events to destinations that may be printed to
the terminal.

#### Affected Versions

All versions of tracing-subscriber prior to 0.3.20 are affected by this
vulnerability.

#### Recommendations

Immediate Action Required: We recommend upgrading to tracing-subscriber
0.3.20 immediately, especially if your application:

- Logs user-provided input (form data, HTTP headers, query parameters,
etc.)
- Runs in environments where terminal output is displayed to users

#### Migration

This is a patch release with no breaking API changes. Simply update your
Cargo.toml:

```toml
[dependencies]
tracing-subscriber = "0.3.20"
```

#### Acknowledgments

We would like to thank [zefr0x](http://github.com/zefr0x) who
responsibly reported the issue at `security@tokio.rs`.

If you believe you have found a security vulnerability in any tokio-rs
project, please email us at `security@tokio.rs`.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiLCJzZWN1cml0eSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 10:48:38 -04:00
Wei Lee
c452a2cb79 [airflow] Move airflow.operators.postgres_operator.Mapping from AIR302 to AIR301 (#20172)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

### Why
Removal should be grouped into the same category. It doesn't matter
whether it's from a provider or not (and the only case we used to have
was not anyway).
`ProviderReplacement` is used to indicate that we have a replacement and
we might need to install an extra Python package to cater to it.

### What
Move `airflow.operators.postgres_operator.Mapping` from AIR302 to AIR301
and get rid of `ProviderReplace::None`

## Test Plan

<!-- How was it tested? -->

Update the test fixtures accordingly in the first commit and reorganize
them in the second commit
2025-09-03 10:18:17 -04:00
Bhuminjay Soni
4c3e1930f6 [syntax-errors] Detect yield from inside async function (#20051)
<!--
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

This PR implements
https://docs.astral.sh/ruff/rules/yield-from-in-async-function/ as a
syntax semantic error

## Test Plan

<!-- How was it tested? -->
I have written a simple inline test as directed in
[https://github.com/astral-sh/ruff/issues/17412](https://github.com/astral-sh/ruff/issues/17412)

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-03 10:13:05 -04:00
Wei Lee
5d7c17c20a [airflow] Convert DatasetOrTimeSchedule(datasets=...) to AssetOrTimeSchedule(assets=...) (AIR311) (#20202)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

update the argument `datasets` as `assets`

## Test Plan

<!-- How was it tested? -->

update fixture accordingly
2025-09-03 10:12:11 -04:00
Andrew Gallant
c402bf8ae2 [ty] Correct default value for experimental rename setting
Ref https://github.com/astral-sh/ruff/pull/20207#discussion_r2318336964
2025-09-03 09:57:26 -04:00
Andrew Gallant
6bc33a041f [ty] Pick up typed text as a query for unimported completions
It's almost certainly bad juju to show literally every single possible
symbol when completions are requested but there is nothing typed yet.
Moreover, since there are so many symbols, it is likely beneficial to
try and winnow them down before sending them to the client.

This change tries to extract text that has been typed and then uses
that as a query to listing all available symbols.
2025-09-03 09:57:26 -04:00
Andrew Gallant
0a0eaf5a9b [ty] Make auto-import completions opt-in via an experimental option
Instead of waiting to land auto-import until it is "ready
for users," it'd be nicer to get incremental progress merged
to `main`. By making it an experimental opt-in, we avoid making
the default completion experience worse but permit developers
and motivated users to try it.
2025-09-03 09:57:26 -04:00
Andrew Gallant
8e52027a88 [ty] Add naive implementation of completions for unimported symbols
This re-works the `all_symbols` based added previously to work across
all modules available, and not just what is directly in the workspace.

Note that we always pass an empty string as a query, which makes the
results always empty. We'll fix this in a subsequent commit.
2025-09-03 09:57:26 -04:00
Andrew Gallant
78db56e362 [ty] Add base for "all symbols" implementation
This just copies the existing "workspace symbols" implementation and
re-names some things.
2025-09-03 09:57:26 -04:00
Andrew Gallant
046893c186 [ty] Make Module::all_submodules return Module instead of Name
This is to facilitate recursive traversal of all modules in an
environment. This way, we can keep asking for submodules.

This also simplifies how this is used in completions, and probably makes
it faster. Namely, since we return the `Module` itself, callers don't
need to invoke the full module resolver just to get the module type.

Note that this doesn't include namespace packages. (Which were
previously not supported in `Module::all_submodules`.) Given how they
can be spread out across multiple search paths, they will likely require
special consideration here.
2025-09-03 09:57:26 -04:00
Andrew Gallant
9cea752934 [ty] Require that we can find a root when listing sub-modules
This is similar to a change made in the "list top-level modules"
implementation that had been masked by poor Salsa failure modes.
Basically, if we can't find a root here, it *must* be a bug. And if we
just silently skip over it, we risk voiding Salsa's purity contract,
leading to more difficult to debug panics.

This did cause one test to fail, but only because the test wasn't
properly setting up roots.
2025-09-03 09:57:26 -04:00
Wei Lee
3b913ce652 [airflow] Improve the AIR002 error message (#20173)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->


### What
Change the message from "DAG should have an explicit `schedule`
argument" to "`DAG` or `@dag` should have an explicit `schedule`
argument"

### Why
We're trying to get rid of the idea that DAG in airflow was Directed
acyclic graph. Thus, change it to refer to the class `DAG` or the
decorator `@dag` might help a bit.

## Test Plan

<!-- How was it tested? -->

update the test fixtures accordly
2025-09-03 09:22:56 -04:00
Brent Westbrook
aee9350df1 [ty] Add GitLab output format (#20155)
## Summary

This wires up the GitLab output format moved into `ruff_db` in
https://github.com/astral-sh/ruff/pull/20117 to the ty CLI.

While I was here, I made one unrelated change to the CLI docs. Clap was
rendering the escapes around the `\[default\]` brackets for the `full`
output, so I just switched those to parentheses:

```
--output-format <OUTPUT_FORMAT>
    The format to use for printing diagnostic messages

    Possible values:
    - full:    Print diagnostics verbosely, with context and helpful hints \[default\]
    - concise: Print diagnostics concisely, one per line
    - gitlab:  Print diagnostics in the JSON format expected by GitLab Code Quality reports
```

## Test Plan

New CLI test, and a manual test with `--config 'terminal.output-format =
"gitlab"'` to make sure this works as a configuration option too. I also
tried piping the output through jq to make sure it's at least valid JSON
2025-09-03 09:08:12 -04:00
David Peter
4e97b97a76 [ty] Run ecosystem-analyzer in release mode (#20210)
## Summary

We already have `mypy_primer` running in debug mode, so this should
provide some additional coverage, and allows us produce reasonable
timing results.

## Test Plan

CI run on this PR
2025-09-03 12:51:30 +02:00
David Peter
00214fc60c [ty] Update ecosystem-analyzer (#20209)
This pulls in some new changes, like the possibility to run on failing
projects (timeouts, abnormal exits).
2025-09-03 12:28:34 +02:00
Aria Desires
ec5584219e [ty] Make initializer calls GotoTargets (#20014)
This introduces `GotoTarget::Call` that represents the kind of
ambiguous/overloaded click of a callable-being-called:

```py
x = mymodule.MyClass(1, 2)
             ^^^^^^^
```

This is equivalent to `GotoTarget::Expression` for the same span but
enriched
with information about the actual callable implementation.

That is, if you click on `MyClass` in `MyClass()` it is *both* a
reference to the class and to the initializer of the class. Therefore
it would be ideal for goto-* and docstrings to be some intelligent
merging of both the class and the initializer.

In particular the callable-implementation (initializer) is prioritized
over the callable-itself (class) so when showing docstrings we will
preferentially show the docs of the initializer if it exists, and then
fallback to the docs of the class.

For goto-definition/goto-declaration we will yield both the class and
the initializer, requiring you to pick which you want (this is perhaps
needlessly pedantic but...).

Fixes https://github.com/astral-sh/ty/issues/898
Fixes https://github.com/astral-sh/ty/issues/1010
2025-09-02 14:49:14 -04:00
chiri
d5e48a0f80 [flake8-use-pathlib] Make PTH119 and PTH120 fixes unsafe because they can change behavior (#20118)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/20112

## Test Plan
`cargo nextest run flake8_use_pathlib`

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-09-02 10:55:24 -05:00
Aria Desires
f40a0b3800 [ty] add more lsp tests for overloads (#20148)
I decided to split out the addition of these tests from other PRs so
that it's easier to follow changes to the LSP's function call handling.
I'm not particularly concerned with whether the results produced by
these tests are "good" or "bad" in this PR, I'm just establishing a
baseline.
2025-09-02 10:38:26 -04:00
David Peter
bbfcf6e111 [ty] __class_getitem__ is a classmethod (#20192)
## Summary

`__class_getitem__` is [implicitly a
classmethod](https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__).

## Test Plan

Added regression test.
2025-09-01 11:22:19 +02:00
David Peter
5518c84ab3 [ty] Support __init_subclass__ (#20190)
## Summary

`__init_subclass__` is implicitly a classmethod.

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

## Test Plan

Regression test
2025-09-01 10:16:28 +02:00
renovate[bot]
c71ce006c4 Update dependency ruff to v0.12.11 (#20184)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [ruff](https://docs.astral.sh/ruff)
([source](https://redirect.github.com/astral-sh/ruff),
[changelog](https://redirect.github.com/astral-sh/ruff/blob/main/CHANGELOG.md))
| `==0.12.10` -> `==0.12.11` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.12.11?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.12.10/0.12.11?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/ruff (ruff)</summary>

###
[`v0.12.11`](https://redirect.github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#01211)

[Compare
Source](https://redirect.github.com/astral-sh/ruff/compare/0.12.10...0.12.11)

##### Preview features

- \[`airflow`] Extend `AIR311` and `AIR312` rules
([#&#8203;20082](https://redirect.github.com/astral-sh/ruff/pull/20082))
- \[`airflow`] Replace wrong path `airflow.io.storage` with
`airflow.io.store` (`AIR311`)
([#&#8203;20081](https://redirect.github.com/astral-sh/ruff/pull/20081))
- \[`flake8-async`] Implement
`blocking-http-call-httpx-in-async-function` (`ASYNC212`)
([#&#8203;20091](https://redirect.github.com/astral-sh/ruff/pull/20091))
- \[`flake8-logging-format`] Add auto-fix for f-string logging calls
(`G004`)
([#&#8203;19303](https://redirect.github.com/astral-sh/ruff/pull/19303))
- \[`flake8-use-pathlib`] Add autofix for `PTH211`
([#&#8203;20009](https://redirect.github.com/astral-sh/ruff/pull/20009))
- \[`flake8-use-pathlib`] Make `PTH100` fix unsafe because it can change
behavior
([#&#8203;20100](https://redirect.github.com/astral-sh/ruff/pull/20100))

##### Bug fixes

- \[`pyflakes`, `pylint`] Fix false positives caused by `__class__` cell
handling (`F841`, `PLE0117`)
([#&#8203;20048](https://redirect.github.com/astral-sh/ruff/pull/20048))
- \[`pyflakes`] Fix `allowed-unused-imports` matching for top-level
modules (`F401`)
([#&#8203;20115](https://redirect.github.com/astral-sh/ruff/pull/20115))
- \[`ruff`] Fix false positive for t-strings in `default-factory-kwarg`
(`RUF026`)
([#&#8203;20032](https://redirect.github.com/astral-sh/ruff/pull/20032))
- \[`ruff`] Preserve relative whitespace in multi-line expressions
(`RUF033`)
([#&#8203;19647](https://redirect.github.com/astral-sh/ruff/pull/19647))

##### Rule changes

- \[`ruff`] Handle empty t-strings in
`unnecessary-empty-iterable-within-deque-call` (`RUF037`)
([#&#8203;20045](https://redirect.github.com/astral-sh/ruff/pull/20045))

##### Documentation

- Fix incorrect `D413` links in docstrings convention FAQ
([#&#8203;20089](https://redirect.github.com/astral-sh/ruff/pull/20089))
- \[`flake8-use-pathlib`] Update links to the table showing the
correspondence between `os` and `pathlib`
([#&#8203;20103](https://redirect.github.com/astral-sh/ruff/pull/20103))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:51:28 +02:00
renovate[bot]
2f3bf03d82 Update rui314/setup-mold digest to 725a879 (#20181)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [rui314/setup-mold](https://redirect.github.com/rui314/setup-mold) |
action | digest | `7344740` -> `725a879` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:51:00 +02:00
renovate[bot]
24a6d247f1 Update CodSpeedHQ/action action to v3.8.1 (#20183)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [CodSpeedHQ/action](https://redirect.github.com/CodSpeedHQ/action) |
action | patch | `v3.8.0` -> `v3.8.1` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>CodSpeedHQ/action (CodSpeedHQ/action)</summary>

###
[`v3.8.1`](https://redirect.github.com/CodSpeedHQ/action/releases/tag/v3.8.1)

[Compare
Source](https://redirect.github.com/CodSpeedHQ/action/compare/v3.8.0...v3.8.1)

##### What's Changed

##### <!-- 1 -->🐛 Bug Fixes

- Don't show error when libpython is not found by
[@&#8203;not-matthias](https://redirect.github.com/not-matthias)

##### <!-- 2 -->🏗️ Refactor

- Improve conditional compilation in `get_pipe_open_options` by
[@&#8203;art049](https://redirect.github.com/art049) in
[#&#8203;100](https://redirect.github.com/CodSpeedHQ/runner/pull/100)

##### <!-- 7 -->⚙️ Internals

- Change log level to warn for venv\_compat error by
[@&#8203;not-matthias](https://redirect.github.com/not-matthias) in
[#&#8203;104](https://redirect.github.com/CodSpeedHQ/runner/pull/104)

**Full Changelog**:
<https://github.com/CodSpeedHQ/action/compare/v3.8.0...v3.8.1>
**Full Runner Changelog**:
<https://github.com/CodSpeedHQ/runner/blob/main/CHANGELOG.md>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:50:02 +02:00
renovate[bot]
88f7644551 Update cargo-bins/cargo-binstall action to v1.15.3 (#20182)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cargo-bins/cargo-binstall](https://redirect.github.com/cargo-bins/cargo-binstall)
| action | patch | `v1.15.1` -> `v1.15.3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>cargo-bins/cargo-binstall (cargo-bins/cargo-binstall)</summary>

###
[`v1.15.3`](https://redirect.github.com/cargo-bins/cargo-binstall/releases/tag/v1.15.3)

[Compare
Source](https://redirect.github.com/cargo-bins/cargo-binstall/compare/v1.15.2...v1.15.3)

*Binstall is a tool to fetch and install Rust-based executables as
binaries. It aims to be a drop-in replacement for `cargo install` in
most cases. Install it today with `cargo install cargo-binstall`, from
the binaries below, or if you already have it, upgrade with `cargo
binstall cargo-binstall`.*

##### In this release:

- Upgrade (de)compression libraries
([#&#8203;2289](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2289))
- Fix release pipeline: stop building for x86\_64h-apple-darwin since it
is broken and is a rarely used tier 3 target, plus GHA no longer offers
x86\_64 mac
([#&#8203;2292](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2292))

###
[`v1.15.2`](https://redirect.github.com/cargo-bins/cargo-binstall/releases/tag/v1.15.2)

[Compare
Source](https://redirect.github.com/cargo-bins/cargo-binstall/compare/v1.15.1...v1.15.2)

*Binstall is a tool to fetch and install Rust-based executables as
binaries. It aims to be a drop-in replacement for `cargo install` in
most cases. Install it today with `cargo install cargo-binstall`, from
the binaries below, or if you already have it, upgrade with `cargo
binstall cargo-binstall`.*

##### In this release:

- Fix updating telemetry config
([#&#8203;2279](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2279)
[#&#8203;2287](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2287))

##### Other changes:

- Upgrade dependencies
([#&#8203;2278](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2278)
[#&#8203;2286](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2286))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:49:31 +02:00
renovate[bot]
6be46033b9 Update Rust crate camino to v1.1.12 (#20185)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [camino](https://redirect.github.com/camino-rs/camino) |
workspace.dependencies | patch | `1.1.11` -> `1.1.12` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>camino-rs/camino (camino)</summary>

###
[`v1.1.12`](https://redirect.github.com/camino-rs/camino/blob/HEAD/CHANGELOG.md#1112---2025-08-26)

[Compare
Source](https://redirect.github.com/camino-rs/camino/compare/camino-1.1.11...camino-1.1.12)

##### Added

- `Utf8PathBuf::from_os_string` and `Utf8Path::from_os_str` conversions.
- `TryFrom<OsString> for Utf8PathBuf` and `TryFrom<&OsStr> for
&Utf8Path` conversions.

Thanks to [BenjaminBrienen](https://redirect.github.com/BenjaminBrienen)
for your first contribution!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:48:52 +02:00
renovate[bot]
9e292f1522 Update Rust crate clap to v4.5.46 (#20186)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [clap](https://redirect.github.com/clap-rs/clap) |
workspace.dependencies | patch | `4.5.45` -> `4.5.46` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>clap-rs/clap (clap)</summary>

###
[`v4.5.46`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4546---2025-08-26)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.45...v4.5.46)

##### Features

- Expose `StyledStr::push_str`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:48:35 +02:00
renovate[bot]
74b60212dc Update Rust crate mimalloc to v0.1.48 (#20187)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [mimalloc](https://redirect.github.com/purpleprotocol/mimalloc_rust) |
workspace.dependencies | patch | `0.1.47` -> `0.1.48` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>purpleprotocol/mimalloc_rust (mimalloc)</summary>

###
[`v0.1.48`](https://redirect.github.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.48):
Version 0.1.48

[Compare
Source](https://redirect.github.com/purpleprotocol/mimalloc_rust/compare/v0.1.47...v0.1.48)

##### Changes

- Mimalloc `v3` feature flag. (credits
[@&#8203;gschulze](https://redirect.github.com/gschulze)).

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-01 08:48:22 +02:00
github-actions[bot]
bfe501b802 [ty] Sync vendored typeshed stubs (#20188)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
2025-09-01 06:27:48 +01:00
Carl Meyer
6f2b874d6c [ty] improve cycle-detection coverage for apply_type_mapping (#20159)
## Summary

Thread visitors through the rest of `apply_type_mapping`: callable and
protocol types.

## Test Plan

Added mdtest that previously stack overflowed.
2025-08-29 16:20:07 -07:00
Carl Meyer
17dc2e4d80 [ty] don't assume that deferred type inference means deferred name resolution (#20160)
## Summary

We have the ability to defer type inference of some parts of
definitions, so as to allow us to create a type that may need to be
recursively referenced in those other parts of the definition.

We also have the ability to do type inference in a context where all
name resolution should be deferred (that is, names should be looked up
from all-reachable-definitions rather than from the location of use.)
This is used for all annotations in stubs, or if `from __future__ import
annotations` is active.

Previous to this PR, these two concepts were linked: deferred-inference
always implied deferred-name-resolution, though we also supported
deferred-name-resolution without deferred-inference, via
`DeferredExpressionState`.

For the upcoming `typing.TypeAlias` support, I will defer inference of
the entire RHS of the alias (so as to support cycles), but that doesn't
imply deferred name resolution; at runtime, the RHS of a name annotated
as `typing.TypeAlias` is executed eagerly.

So this PR fully de-couples the two concepts, instead explicitly setting
the `DeferredExpressionState` in those cases where we should defer name
resolution.

It also fixes a long-standing related bug, where we were deferring name
resolution of all names in class bases, if any of the class bases
contained a stringified annotation.

## Test Plan

Added test that failed before this PR.
2025-08-29 16:19:45 -07:00
Dylan
694e7ed52e Less confidently mark f-strings as empty when inferring truthiness (#20152)
When computing the boolean value of an f-string, we over-eagerly
interpreted some f-string interpolations as empty. In this PR we now
mark the truthiness of f-strings involving format specs, debug text, and
bytes literals as "unknown".

This will probably result in some false negatives, which may be further
refined (for example - there are probably many cases where
`is_not_empty_f_string` should be modified to return `true`), but for
now at least we should have fewer false positives.

Affected rules (may not be an exhaustive list):

- [unnecessary-empty-iterable-within-deque-call
(RUF037)](https://docs.astral.sh/ruff/rules/unnecessary-empty-iterable-within-deque-call/#unnecessary-empty-iterable-within-deque-call-ruf037)
- [falsy-dict-get-fallback
(RUF056)](https://docs.astral.sh/ruff/rules/falsy-dict-get-fallback/#falsy-dict-get-fallback-ruf056)
- [pytest-assert-always-false
(PT015)](https://docs.astral.sh/ruff/rules/pytest-assert-always-false/#pytest-assert-always-false-pt015)
- [expr-or-not-expr
(SIM221)](https://docs.astral.sh/ruff/rules/expr-or-not-expr/#expr-or-not-expr-sim221)
- [expr-or-true
(SIM222)](https://docs.astral.sh/ruff/rules/expr-or-true/#expr-or-true-sim222)
- [expr-and-false
(SIM223)](https://docs.astral.sh/ruff/rules/expr-and-false/#expr-and-false-sim223)

Closes #19935
2025-08-29 22:12:54 +00:00
Carl Meyer
fe953e5c5c [ty] skip a slow seed in fuzzer (#20161)
## Summary

Fuzzer seed 208 seems to be timing out all fuzzer runs on PRs today.
This has happened on multiple unrelated PRs, as well as on an initial
version of this PR that made a comment-only change in ty and didn't skip
any seeds, so the timeout appears to be consistent in CI, on ty main
branch, as of today, but it started happening due to some change in a
factor outside ty; not sure what.

I checked the code generated for seed 208 locally, and it takes about
30s to check on current ty main branch. This is slow for a fuzzer seed,
but shouldn't be slow enough to make it time out after 20min in CI (even
accounting for GH runners being slower than my laptop.)

I tried to bisect the slowness of checking that code locally, but I
didn't go back far enough to find the change that made it slow. In fact
it seems like it became significantly faster in the last few days (on an
older checkout I had to stop it after several minutes.) So whatever the
cause of the slowness, it's not a recent change in ty.

I don't want to rabbit-hole on this right now (fuzzer-discovered issues
are lower-priority than real-world-code issues), and need a working CI,
so skip this seed for now until we can investigate it.

## Test Plan

CI. This PR contains a no-op (comment) change in ty, so that the fuzz
test is triggered in CI and we can verify it now works (as well as
verify, on the previous commit, that the fuzzer job is timing out on
that seed, even with just a no-op change in ty.)
2025-08-29 13:59:16 -07:00
Alex Waygood
0bf5d2a204 Revert "[ty] Use invalid-assignment error code for invalid assignments to ClassVars" (#20158)
Reverts astral-sh/ruff#20156. As @sharkdp noted in his post-merge
review, there were several issues with that PR that I didn't spot before
merging — but I'm out for four days now, and would rather not leave
things in an inconsistent state for that long. I'll revisit this on
Wednesday.
2025-08-29 19:48:45 +01:00
Carl Meyer
8eb8d25565 [ty] add six ecosystem projects to good.txt (#20157)
## Summary

These projects all check successfully now.

(Pandas still takes 9s, as the comment in `bad.txt` said, but I don't
think this is slow enough to exclude it; mypy-primer overall still runs
in 4 minutes, faster than e.g. the test suite on Windows.)

## Test Plan

mypy-primer CI.
2025-08-29 11:37:29 -07:00
Alex Waygood
9b1b58a451 [ty] Use invalid-assignment error code for invalid assignments to ClassVars (#20156)
## Summary

This error is about assigning to attributes rather than reading
attributes, so I think `invalid-assignment` makes more sense than
`invalid-attribute-access`

## Test Plan

existing mdtests updated
2025-08-29 18:43:30 +01:00
Carl Meyer
fa7798ddd9 [ty] minor TypedDict fixes (#20146)
## Summary

In `is_disjoint_from_impl`, we should unpack type aliases before we
check `TypedDict`. This change probably doesn't have any visible effect
until we have a more discriminating implementation of disjointness for
`TypedDict`, but making the change now can avoid some confusion/bugs in
future.

In `type_ordering.rs`, we should order `TypedDict` near more similar
types, and leave Union/Intersection together at the end of the list.
This is not necessary for correctness, but it's more consistent and it
could have saved me some confusion trying to figure out why I was only
getting an unreachable panic when my code example included a `TypedDict`
type.

## Test Plan

None besides existing tests.
2025-08-29 09:46:48 -07:00
Carl Meyer
8223fea062 [ty] ensure union normalization really normalizes (#20147)
## Summary

Now that we have `Type::TypeAlias`, which can wrap a union, and the
possibility of unions including non-unpacked type aliases (which is
necessary to support recursive type aliases), we can no longer assume in
`UnionType::normalized_impl` that normalizing each element of an
existing union will result in a set of elements that we can order and
then place raw into `UnionType` to create a normalized union. It's now
possible for those elements to themselves include union types (unpacked
from an alias). So instead, we need to feed those elements into the full
`UnionBuilder` (with alias-unpacking turned on) to flatten/normalize
them, and then order them.

## Test Plan

Added mdtest.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-08-29 09:02:35 -07:00
Eric Jolibois
5a608f7366 [ty] typecheck dict methods for TypedDict (#19874)
## Summary

Typecheck `get()`, `setdefault()`, `pop()` for `TypedDict`

```py
from typing import TypedDict
from typing_extensions import NotRequired

class Employee(TypedDict):
    name: str
    department: NotRequired[str]

emp = Employee(name="Alice", department="Engineering")

emp.get("name")
emp.get("departmen", "Unknown")
emp.pop("department")
emp.pop("name")
```

<img width="838" height="529" alt="Screenshot 2025-08-12 at 11 42 12"
src="https://github.com/user-attachments/assets/77ce150a-223c-4931-b914-551095d8a3a6"
/>


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

## Test Plan

Updated Markdown tests

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-08-29 16:25:03 +02:00
Hans
c2d7c673ca [pyupgrade] Add fix safety section to docs (UP029) (#17490)
## Summary

Add `fix safety` section to `UP029: unnecessary_builtin_import.rs`, for
#15584
2025-08-29 13:55:19 +00:00
Brent Westbrook
8a6db4f257 Show fixes by default (#19919)
## Summary

This PR fixes #7352 by exposing the `show_fix_diff` option used in our
snapshot tests in the CLI. As the issue suggests, we plan to make this
the default output format in the future, so this is added to the `full`
output format in preview for now.

This turned out to be pretty straightforward. I just used our existing
`Applicability` settings to determine whether or not to print the diff.

The snapshot differences are because we now set
`Applicability::DisplayOnly` for our snapshot tests. This
`Applicability` is also used to determine whether or not the fix icon
(`[*]`) is rendered, so this is now shown for display-only fixes in our
snapshots. This was already the case previously, but we were only
setting `Applicability::Unsafe` in these tests and ignoring the
`Applicability` when rendering fix diffs. CLI users can't enable
display-only fixes, so this is only a test change for now, but this
should work smoothly if we decide to expose a `--display-only-fixes`
flag or similar in the future.

I also deleted the `PrinterFlags::SHOW_FIX_DIFF` flag. This was
completely unused before, and it seemed less confusing just to delete it
than to enable it in the right place and check it along with the
`OutputFormat` and `preview`.

## Test Plan

I only added one CLI test for now. I'm kind of assuming that we have
decent coverage of the cases where this shouldn't be firing, especially
the `output_format` CLI test, which shows that this definitely doesn't
affect non-preview `full` output. I'm happy to add more tests with
different combinations of options, if we're worried about any in
particular. I did try `--diff` and `--preview` and a few other
combinations manually.

And here's a screenshot using our trusty UP049 example from the design
discussion confirming that all the colors and other formatting still
look as expected:

<img width="786" height="629" alt="image"
src="https://github.com/user-attachments/assets/94e408bc-af7b-4573-b546-a5ceac2620f2"
/>

And one with an unsafe fix to see the footer:

<img width="782" height="367" alt="image"
src="https://github.com/user-attachments/assets/bbb29e47-310b-4293-b2c2-cc7aee3baff4"
/>


## Related issues and PR
- https://github.com/astral-sh/ruff/issues/7352
- https://github.com/astral-sh/ruff/pull/12595
- https://github.com/astral-sh/ruff/issues/12598
- https://github.com/astral-sh/ruff/issues/12599
- https://github.com/astral-sh/ruff/issues/12600

I think we could probably close all of these issues now. I think we've
either resolved or avoided most of them, and if we encounter them again
with the new output format, it would probably make sense to open new
ones anyway.
2025-08-29 09:53:05 -04:00
Hans
ffcdd4ea42 [refurb] Add fix safety section (FURB105) (#17499)
## Summary

This PR add the `fix safety` section for rule `FURB105` in
`print_empty_string.rs` for #15584

Before:
```
def get_sep():
    print("side effect")
    return ""
    
print("", sep=get_sep())
```

After:
```
def get_sep():
    print("side effect")
    return ""
    
print()
```

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-08-29 09:41:06 -04:00
Dan Parizher
0ff0c70302 [fastapi] Fix false positive for paths with spaces around parameters (FAST003) (#20077)
## Summary

Fixes #20060
2025-08-29 13:40:25 +00:00
Alex Waygood
f77315776c [ty] Better error message for attempting to assign to a read-only property (#20150) 2025-08-29 13:22:23 +00:00
Alex Waygood
04dc223710 [ty] Improve disambiguation of types via fully qualified names (#20141) 2025-08-29 08:44:18 +00:00
Alex Waygood
0d7ed32494 [ty] Enforce that an attribute on a class X must be callable in order to satisfy a member on a protocol P (#20142)
## Summary

Small, incremental progress towards checking the types of method
members.

## Test Plan

Added an mdtest
2025-08-29 08:31:26 +01:00
Dhruv Manilawala
4ca38b2974 [ty] Unpack variadic argument type in specialization (#20130)
## Summary

This PR fixes various TODOs around overload call when a variadic
argument is used.

The reason this bug existed is because the specialization wouldn't
account for unpacking the type of the variadic argument.

This is fixed by expanding `MatchedArgument` to contain the type of that
argument _only_ when it is a variadic argument. The reason is that
there's a split for when the argument type is inferred -- the
non-variadic arguments are inferred using `infer_argument_types` _after_
parameter matching while the variadic argument type is inferred _during_
the parameter matching. And, the `MatchedArgument` is populated _during_
parameter matching which means the unpacking would need to happen during
parameter matching.

This split seems a bit inconsistent but I don't want to spend a lot of
time on trying to merge them such that all argument type inference
happens in a single place. I might look into it while adding support for
`**kwargs`.

## Test Plan

Update existing tests by resolving the todos.

The ecosystem changes looks correct to me except for the `slice` call
but it seems that it's unrelated to this PR as we infer `slice[Any, Any,
Any]` for a `slice(1, 2, 3)` call on `main` as well
([playground](https://play.ty.dev/9eacce00-c7d5-4dd5-a932-4265cb2bb4f6)).
2025-08-29 04:27:28 +00:00
Douglas Creager
a8039f80f0 [ty] Add constraint set implementation (#19997)
This PR adds an implementation of constraint sets.

An individual constraint restricts the specialization of a single
typevar to be within a particular lower and upper bound: the typevar can
only specialize to types that are a supertype of the lower bound, and a
subtype of the upper bound. (Note that lower and upper bounds are fully
static; we take the bottom and top materializations of the bounds to
remove any gradual forms if needed.) Either bound can be “closed” (where
the bound is a valid specialization), or “open” (where it is not).

You can then build up more complex constraint sets using union,
intersection, and negation operations. We use a disjunctive normal form
(DNF) representation, just like we do for types: a _constraint set_ is
the union of zero or more _clauses_, each of which is the intersection
of zero or more individual constraints. Note that the constraint set
that contains no clauses is never satisfiable (`⋃ {} = 0`); and the
constraint set that contains a single clause, which contains no
constraints, is always satisfiable (`⋃ {⋂ {}} = 1`).

One thing to note is that this PR does not change the logic of the
actual assignability checks, and in particular, we still aren't ever
trying to create an "individual constraint" that constrains a typevar.
Technically we're still operating only on `bool`s, since we only ever
instantiate `C::always_satisfiable` (i.e., `true`) and
`C::unsatisfiable` (i.e., `false`) in the `has_relation_to` methods. So
if you thought that #19838 introduced an unnecessarily complex stand-in
for `bool`, well here you go, this one is worse! (But still seemingly
not yielding a performance regression!) The next PR in this series,
#20093, is where we will actually create some non-trivial constraint
sets and use them in anger.

That said, the PR does go ahead and update the assignability checks to
use the new `ConstraintSet` type instead of `bool`. That part is fairly
straightforward since we had already updated the assignability checks to
use the `Constraints` trait; we just have to actively choose a different
impl type. (For the `is_whatever` variants, which still return a `bool`,
we have to convert the constraint set, but the explicit
`is_always_satisfiable` calls serve as nice documentation of our
intent.)

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-08-28 20:04:29 -04:00
Lior Weissman
5c2d4d8d8f [perflint] Handle tuples in dictionary comprehensions (PERF403) (#19934)
This pull request fixes the bug described in issue
[#19153](https://github.com/astral-sh/ruff/issues/19153).

The issue occurred when `PERF403` incorrectly flagged cases involving
tuple unpacking in a for loop. For example:

```python
def f():
    v = {}
    for (o, p), x in [("op", "x")]:
        v[x] = o, p
```

This code was wrongly suggested to be rewritten into a dictionary
comprehension, which changes the semantics.

Changes in this PR:

Updated the `PERF403` rule to correctly handle tuple unpacking in loop
targets.

Added regression tests to ensure this case (and similar ones) are no
longer flagged incorrectly.

Why:
This ensures that `PERF403` only triggers when a dictionary
comprehension is semantically equivalent to the original loop,
preventing false positives.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-08-28 21:37:40 +00:00
Dan Parizher
26082e8ec1 [ruff] Fix false negative for empty f-strings in deque calls (RUF037) (#20109)
## Summary

Fixes #20050
2025-08-28 16:58:39 -04:00
Kot
b6522cb534 [pylint] Add U+061C to PLE2502 (#20106)
Resolves #20058
2025-08-28 16:35:48 -04:00
Dan Parizher
637a2b1170 [pycodestyle] Preserve return type annotation for ParamSpec (E731) (#20108)
## Summary

Fixes #20097

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-08-28 20:31:45 +00:00
Jelle Zijlstra
3927b0c931 [ty] Simplify materialization of specialized generics (#20121)
This is a variant of #20076 that moves some complexity out of
`apply_type_mapping_impl` in `generics.rs`. The tradeoff is that now
every place that applies `TypeMapping::Specialization` must take care to
call `.materialize()` afterwards. (A previous version of this didn't
work because I had missed a spot where I had to call `.materialize()`.)

@carljm as asked in
https://github.com/astral-sh/ruff/pull/20076#discussion_r2305385298 .
2025-08-28 11:35:00 -07:00
Amethyst Reese
ca1f66a657 [flake8-async] Implement blocking-input rule (ASYNC250) (#20122)
## Summary

Adds new rule to catch use of builtins `input()` in async functions.

Issue #8451

## Test Plan

New snapshosts in `ASYNC250.py` with `cargo insta test`.
2025-08-28 11:04:24 -07:00
Matthew Mckee
166b63ad4d Fix mdtest ignore python code blocks (#20139)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1103

## Test Plan

Edited `crates/ty_python_semantic/resources/mdtest/literal/boolean.md`
with:

```
# Boolean literals

```python
reveal_type(True)  # revealed: Literal[False]
reveal_type(False)  # revealed: Literal[False]
```

Ran `cargo test -p ty_python_semantic --test mdtest -- mdtest__literal_boolean`

And we get a test failure:

```
running 1 test
test mdtest__literal_boolean ... FAILED

failures:

---- mdtest__literal_boolean stdout ----

boolean.md - Boolean literals (c336e1af3d538acd)

crates/ty_python_semantic/resources/mdtest/literal/boolean.md:4
unmatched assertion: revealed: Literal[False]
crates/ty_python_semantic/resources/mdtest/literal/boolean.md:4
unexpected error: 13 [revealed-type] "Revealed type: `Literal[True]`"

To rerun this specific test, set the environment variable:
MDTEST_TEST_FILTER='boolean.md - Boolean literals (c336e1af3d538acd)'
MDTEST_TEST_FILTER='boolean.md - Boolean literals (c336e1af3d538acd)'
cargo test -p ty_python_semantic --test mdtest --
mdtest__literal_boolean

--------------------------------------------------


thread 'mdtest__literal_boolean' panicked at
crates/ty_test/src/lib.rs:138:5:
Some tests failed.
note: run with `RUST_BACKTRACE=1` environment variable to display a
backtrace


failures:
    mdtest__literal_boolean

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 263
filtered out; finished in 0.18s

error: test failed, to rerun pass `-p ty_python_semantic --test mdtest`
```

As expected.

And when i checkout main and keep the same mdtest file all tests pass (as the repro).
2025-08-28 09:59:03 -07:00
Carl Meyer
9363eeca26 [ty] add support for cyclic legacy generic protocols (#20125)
## Summary

Just add the necessary Salsa cycle handling.

## Test Plan

Added mdtest.
2025-08-28 16:58:01 +00:00
Carl Meyer
f4362b95d7 [ty] add cycle detection for find_legacy_typevars (#20124)
## Summary

Add cycle detection to the `find_legacy_typevars` type method.

## Test Plan

Added mdtest that stack overflowed without this.
2025-08-28 09:55:08 -07:00
Brent Westbrook
f703536977 Use new diff rendering format in tests (#20101)
## Summary

I spun this off from #19919 to separate the rendering code change and
snapshot updates from the (much smaller) changes to expose this in the
CLI. I grouped all of the `ruff_linter` snapshot changes in the final
commit in an effort to make this easier to review. The code changes are
in [this
range](619395eb41).

I went through all of the snapshots, albeit fairly quickly, and they all
looked correct to me. In the last few commits I was trying to resolve an
existing issue in the alignment of the line number separator:


73720c73be/crates/ruff_linter/src/rules/flake8_comprehensions/snapshots/ruff_linter__rules__flake8_comprehensions__tests__C409_C409.py.snap (L87-L89)

In the snapshot above on `main`, you can see that a double-digit line
number at the end of the context lines for a snippet was causing a
misalignment with the other separators. That's now resolved. The one
downside is that this can lead to a mismatch with the diagnostic above:

```
C409 [*] Unnecessary list literal passed to `tuple()` (rewrite as a tuple literal)
 --> C409.py:4:6
  |
2 |   t2 = tuple([1, 2])
3 |   t3 = tuple((1, 2))
4 |   t4 = tuple([
  |  ______^
5 | |     1,
6 | |     2
7 | | ])
  | |__^
8 |   t5 = tuple(
9 |       (1, 2)
  |
help: Rewrite as a tuple literal
1  | t1 = tuple([])
2  | t2 = tuple([1, 2])
3  | t3 = tuple((1, 2))
   - t4 = tuple([
4  + t4 = (
5  |     1,
6  |     2
   - ])
7  + )
8  | t5 = tuple(
9  |     (1, 2)
10 | )
note: This is an unsafe fix and may remove comments or change runtime behavior
```

But I don't think we can avoid that without really reworking this
rendering to make the diagnostic and diff rendering aware of each other.
Anyway, this should only happen in relatively rare cases where the
diagnostic is near a digit boundary and also near a context boundary.
Most of our diagnostics line up nicely.

Another potential downside of the new rendering format is its handling
of long stretches of `+` or `-` lines:

```
help: Replace with `Literal[...] | None`
21 |     ...
22 |
23 |
   - def func6(arg1: Literal[
   -     "hello",
   -     None  # Comment 1
   -     , "world"
   -     ]):
24 + def func6(arg1: Literal["hello", "world"] | None):
25 |     ...
26 |
27 |
note: This is an unsafe fix and may remove comments or change runtime behavior
```

To me it just seems a little hard to tell what's going on with just a
long streak of `-`-prefixed lines. I saw an even more exaggerated
example at some point, but I think this is also fairly rare. Most of the
snapshots seem more like the examples we looked at on Discord with
plenty of `|` lines and pairs of `+` and `-` lines.

## Test Plan

Existing tests plus one new test in `ruff_db` to isolate a line
separator alignment issue
2025-08-28 10:56:58 -04:00
David Peter
1842cfe333 [ty] Fix 'too many cycle iterations' for unions of literals (#20137)
## Summary

Decrease the maximum number of literals in a union before we collapse to
the supertype. The better fix for this will be
https://github.com/astral-sh/ty/issues/957, but it is very tempting to
solve this for now by simply decreasing the limit by one, to get below
the salsa limit of 200.

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

## Test Plan

Added a regression test that would previously lead to a "too many cycle
iterations" panic.
2025-08-28 16:46:37 +02:00
David Peter
b3c4005289 [ty] No boundness analysis for implicit instance attributes (#20128)
## Summary

With this PR, we stop performing boundness analysis for implicit
instance attributes:

```py
class C:
    def __init__(self):
        if False:   
            self.x = 1

C().x  # would previously show an error, with this PR we pretend the attribute exists
```

This PR is potentially just a temporary measure until we find a better
fix. But I have already invested a lot of time trying to find the root
cause of https://github.com/astral-sh/ty/issues/758 (and [this
example](https://github.com/astral-sh/ty/issues/758#issuecomment-3206108262),
which I'm not entirely sure is related) and I still don't understand
what is going on. This PR fixes the performance problems in both of
these problems (in a rather crude way).

The impact of the proposed change on the ecosystem is small, and the
three new diagnostics are arguably true positives (previously hidden
because we considered the code unreachable, based on e.g. `assert`ions
that depended on implicit instance attributes). So this seems like a
reasonable fix for now.

Note that we still support cases like these:

```py
class D:
    if False:  # or any other expression that statically evaluates to `False`
        x: int = 1

D().x  # still an error


class E:
    if False:  # or any other expression that statically evaluates to `False`
        def f(self):
            self.x = 1

E().x  # still an error
```

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

## Test Plan

Updated tests, benchmark results
2025-08-28 16:25:07 +02:00
Brent Westbrook
c2bc15bc15 Bump 0.12.11 (#20136) 2025-08-28 09:45:01 -04:00
David Peter
e586f6dcc4 [ty] Benchmarks for problematic implicit instance attributes cases (#20133)
## Summary

Add regression benchmarks for the problematic cases in
https://github.com/astral-sh/ty/issues/758. I'd like to merge this
before https://github.com/astral-sh/ruff/pull/20128 to measure the
impact (local tests show that this will "solve" both cases).
2025-08-28 15:25:25 +02:00
Takayuki Maeda
76a6b7e3e2 [pyflakes] Fix allowed-unused-imports matching for top-level modules (F401) (#20115)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #19664

Fix allowed unused imports matching for top-level modules.

I've simply replaced `from_dotted_name` with `user_defined`. Since
QualifiedName for imports is created in
crates/ruff_python_semantic/src/imports.rs, I guess it's acceptable to
use `user_defined` here. Please tell me if there is better way.


0c5089ed9e/crates/ruff_python_semantic/src/imports.rs (L62)

## Test Plan

<!-- How was it tested? -->

I've added a snapshot test
`f401_allowed_unused_imports_top_level_module`.
2025-08-28 13:02:50 +00:00
Brent Westbrook
1ce65714c0 Move GitLab output rendering to ruff_db (#20117)
## Summary

This PR is a first step toward adding a GitLab output format to ty. It
converts the `GitlabEmitter` from `ruff_linter` to a `GitlabRenderer` in
`ruff_db` and updates its implementation to handle non-Ruff files and
diagnostics without primary spans. I tried to break up the changes here
so that they're easy to review commit-by-commit, or at least in groups
of commits:
- [preparatory changes in-place in `ruff_linter` and a `ruff_db`
skeleton](0761b73a61)
- [moving the code over with no implementation changes mixed
in](0761b73a61..8f909ea0bb)
- [tidying up the code now in
`ruff_db`](9f047c4f9f..e5e217fcd6)

This wasn't strictly necessary, but I also added some `Serialize`
structs instead of calling `json!` to make it a little clearer that we
weren't modifying the schema (e4c4bee35d).

I plan to follow this up with a separate PR exposing this output format
in the ty CLI, which should be quite straightforward.

## Test Plan

Existing tests, especially the two that show up in the diff as renamed
nearly without changes
2025-08-28 08:56:33 -04:00
Shaygan Hooshyari
d9aaacd01f [ty] Evaluate reachability of non-definitely-bound to Ambiguous (#19579)
## Summary

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

If the expression (or any child expressions) is not definitely bound the
reachability constraint evaluation is determined as ambiguous.

This fixes the infinite cycles panic in the following code:

```py
from typing import Literal

class Toggle:
    def __init__(self: "Toggle"):
        if not self.x:
            self.x: Literal[True] = True
```

Credit of this solution is for David.

## Test Plan

- Added a test case with too many cycle iterations panic.
- Previous tests.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-08-28 14:34:49 +02:00
Jelle Zijlstra
18eaa659c1 [ty] Introduce a representation for the top/bottom materialization of an invariant generic (#20076)
Part of #994. This adds a new field to the Specialization struct to
record when we're dealing with the top or bottom materialization of an
invariant generic. It also implements subtyping and assignability for
these objects.

Next planned steps after this is done are to implement other operations
on top/bottom materializations; probably attribute access is an
important one.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-08-27 17:53:57 -07:00
Amethyst Reese
af259faed5 [flake8-async] Implement blocking-http-call-httpx (ASYNC212) (#20091)
## Summary

Adds new rule to find and report use of `httpx.Client` in synchronous
functions.

See issue #8451

## Test Plan

New snapshots for `ASYNC212.py` with `cargo insta test`.
2025-08-27 15:19:01 -07:00
Leandro Braga
d75ef3823c [ty] print diagnostics with fully qualified name to disambiguate some cases (#19850)
There are some situations that we have a confusing diagnostics due to
identical class names.

## Class with same name from different modules

```python
import pandas
import polars

df: pandas.DataFrame = polars.DataFrame()
```

This yields the following error:

**Actual:**
error: [invalid-assignment] "Object of type `DataFrame` is not
assignable to `DataFrame`"
**Expected**:
error: [invalid-assignment] "Object of type `polars.DataFrame` is not
assignable to `pandas.DataFrame`"

## Nested classes

```python
from enum import Enum

class A:
    class B(Enum):
        ACTIVE = "active"
        INACTIVE = "inactive"

class C:
    class B(Enum):
        ACTIVE = "active"
        INACTIVE = "inactive"
```

**Actual**:
error: [invalid-assignment] "Object of type `Literal[B.ACTIVE]` is not
assignable to `B`"
**Expected**:
error: [invalid-assignment] "Object of type
`Literal[my_module.C.B.ACTIVE]` is not assignable to `my_module.A.B`"

## Solution

In this MR we added an heuristics to detect when to use a fully
qualified name:
- There is an invalid assignment and;
- They are two different classes and;
- They have the same name

The fully qualified name always includes:
- module name
- nested classes name
- actual class name

There was no `QualifiedDisplay` so I had to implement it from scratch.
I'm very new to the codebase, so I might have done things inefficiently,
so I appreciate feedback.

Should we pre-compute the fully qualified name or do it on demand? 

## Not implemented

### Function-local classes

Should we approach this in a different PR?

**Example**:
```python 
# t.py
from __future__ import annotations


def function() -> A:
    class A:
        pass

    return A()


class A:
    pass


a: A = function()
```

#### mypy

```console
t.py:8: error: Incompatible return value type (got "t.A@5", expected "t.A")  [return-value]
```

From my testing the 5 in `A@5` comes from the like number. 

#### ty

```console
error[invalid-return-type]: Return type does not match returned value
 --> t.py:4:19
  |
4 | def function() -> A:
  |                   - Expected `A` because of return type
5 |     class A:
6 |         pass
7 |
8 |     return A()
  |            ^^^ expected `A`, found `A`
  |
info: rule `invalid-return-type` is enabled by default
```

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

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-08-27 20:46:07 +00:00
Dan Parizher
89ca493fd9 [ruff] Preserve relative whitespace in multi-line expressions (RUF033) (#19647)
## Summary

Fixes #19581

I decided to add in a `indent_first_line` function into
[`textwrap.rs`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_python_trivia/src/textwrap.rs),
as it solely focuses on text manipulation utilities. It follows the same
design as `indent()`, and there may be situations in the future where it
can be reused as well.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-08-27 19:15:44 +00:00
David Peter
4b80f5fa4f [ty] Optimize TDD atom ordering (#20098)
## Summary

While looking at some logging output that I added to
`ReachabilityConstraintBuilder::add_and_constraint` in order to debug
https://github.com/astral-sh/ty/issues/1091, I noticed that it seemed to
suggest that the TDD was built in an imbalanced way for code like the
following, where we have a sequence of non-nested `if` conditions:

```py
def f(t1, t2, t3, t4, …):
    x = 0
    if t1:
        x = 1
    if t2:
        x = 2
    if t3:
        x = 3
    if t4:
        x = 4
    …
```

To understand this a bit better, I added some code to the
`ReachabilityConstraintBuilder` to render the resulting TDD. On `main`,
we get a tree that looks like the following, where you can see a pattern
of N sub-trees that grow linearly with N (number of `if` statements).
This results in an overall tree structure that has N² nodes (see graph
below):

<img alt="normal order"
src="https://github.com/user-attachments/assets/aab40ce9-e82a-4fcd-823a-811f05f15f66"
/>

If we zoom in to one of these subgraphs, we can see what the problem is.
When we add new constraints that represent combinations like `t1 AND ~t2
AND ~t3 AND t4 AND …`, they start with the evaluation of "early"
conditions (`t1`, `t2`, …). This means that we have to create new
subgraphs for each new `if` condition because there is little sharing
with the previous structure. We evaluate the Boolean condition in a
right-associative way: `t1 AND (~t2 AND (~t3 AND t4)))`:

<img width="500" align="center"
src="https://github.com/user-attachments/assets/31ea7182-9e00-4975-83df-d980464f545d"
/>

If we change the ordering of TDD atoms, we can change that to a
left-associative evaluation: `(((t1 AND ~t2) AND ~t3) AND t4) …`. This
means that we can re-use previous subgraphs `(t1 AND ~t2)`, which
results in a much more compact graph structure overall (note how "late"
conditions are now at the top, and "early" conditions are further down
in the graph):

<img alt="reverse order"
src="https://github.com/user-attachments/assets/96a6b7c1-3d35-4192-a917-0b2d24c6b144"
/>

If we count the number of TDD nodes for a growing number if `if`
statements, we can see that this change results in a slower growth. It's
worth noting that the growth is still superlinear, though:

<img width="800" height="600" alt="plot"
src="https://github.com/user-attachments/assets/22e8394f-e74e-4a9e-9687-0d41f94f2303"
/>

On the actual code from the referenced ticket (the `t_main.py` file
reduced to its main function, with the main function limited to 2000
lines instead of 11000 to allow the version on `main` to run to
completion), the effect is much more dramatic. Instead of 26 million TDD
nodes (`main`), we now only create 250 thousand (this branch), which is
slightly less than 1%.

The change in this PR allows us to build the semantic index and
type-check the problematic `t_main.py` file in
https://github.com/astral-sh/ty/issues/1091 in 9 seconds. This is still
not great, but an obvious improvement compared to running out of memory
after *minutes* of execution.

An open question remains whether this change is beneficial for all kinds
of code patterns, or just this linear sequence of `if` statements. It
does not seem unreasonable to think that referring to "earlier"
conditions is generally a good idea, but I learned from Doug that it's
generally not possible to find a TDD-construction heuristic that is
non-pathological for all kinds of inputs. Fortunately, it seems like
this change here results in performance improvements across *all of our
benchmarks*, which should increase the confidence in this change:

| Benchmark           | Improvement |
|---------------------|-------------------------|
| hydra-zen           | +13%                    |
| DateType            | +5%                     |
| sympy (walltime)    | +4%                     |
| attrs               | +4%                     |
| pydantic (walltime) | +2%                     |
| pandas (walltime)   | +2%                     |
| altair (walltime)   | +2%                     |
| static-frame        | +2%                     |
| anyio               | +1%                     |
| freqtrade           | +1%                     |
| colour-science      | +1%                     |
| tanjun              | +1%                     |

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

---------

Co-authored-by: Douglas Creager <dcreager@dcreager.net>
2025-08-27 20:42:09 +02:00
Wei Lee
5663426d73 [airflow] Extend AIR311 and AIR312 rules (#20082)
<!--
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

<!-- What's the purpose of the change? What does it do, and why? -->

Extend the following rules.

### AIR311
* `airflow.sensors.base.BaseSensorOperator` →
airflow.sdk.bases.sensor.BaseSensorOperator`
* `airflow.sensors.base.PokeReturnValue` →
airflow.sdk.bases.sensor.PokeReturnValue`
* `airflow.sensors.base.poke_mode_only` →
airflow.sdk.bases.sensor.poke_mode_only`
* `airflow.decorators.base.DecoratedOperator` →
airflow.sdk.bases.decorator.DecoratedOperator`
* `airflow.models.param.Param` → airflow.sdk.definitions.param.Param`
* `airflow.decorators.base.DecoratedMappedOperator` →
`airflow.sdk.bases.decorator.DecoratedMappedOperator`
* `airflow.decorators.base.DecoratedOperator` →
`airflow.sdk.bases.decorator.DecoratedOperator`
* `airflow.decorators.base.TaskDecorator` →
`airflow.sdk.bases.decorator.TaskDecorator`
* `airflow.decorators.base.get_unique_task_id` →
`airflow.sdk.bases.decorator.get_unique_task_id`
* `airflow.decorators.base.task_decorator_factory` →
`airflow.sdk.bases.decorator.task_decorator_factory`


### AIR312
* `airflow.sensors.bash.BashSensor` →
`airflow.providers.standard.sensor.bash.BashSensor`
* `airflow.sensors.python.PythonSensor` →
`airflow.providers.standard.sensors.python.PythonSensor`



## Test Plan

<!-- How was it tested? -->

update the test fixture accordingly in the second commit and reorg in
the third
2025-08-27 14:11:22 -04:00
David Peter
0b3548755c [ty] Preserve qualifiers when accessing attributes on unions/intersections (#20114)
## Summary

Properly preserve type qualifiers when accessing attributes on unions
and intersections. This is a prerequisite for
https://github.com/astral-sh/ruff/pull/19579.

Also fix a completely wrong implementation of
`map_with_boundness_and_qualifiers`. It now closely follows
`map_with_boundness` (just above).

## Test Plan

I thought about it, but didn't find any easy way to test this. This only
affected `Type::member`. Things like validation of attribute writes
(where type qualifiers like `ClassVar` and `Final` are important) were
already handling things correctly.
2025-08-27 20:01:45 +02:00
Alex Waygood
ce1dc21e7e [ty] Fix the inferred interface of specialized generic protocols (#19866) 2025-08-27 18:16:15 +01:00
Alex Waygood
7d0c8e045c [ty] Infer slightly more precise types for comprehensions (#20111) 2025-08-27 13:21:47 +01:00
Alex Waygood
d71518b369 [ty] Add more tests for protocols (#20095)
Co-authored-by: Shunsuke Shibayama <sbym1346@gmail.com>
2025-08-27 12:56:14 +01:00
Carl Meyer
9ab276b345 [ty] don't eagerly unpack aliases in user-authored unions (#20055)
## Summary

Add a subtly different test case for recursive PEP 695 type aliases,
which does require that we relax our union simplification, so we don't
eagerly unpack aliases from user-provided union annotations.

## Test Plan

Added mdtest.
2025-08-26 16:29:45 -07:00
chiri
a60fb3f2c8 [flake8-use-pathlib] Update links to the table showing the correspondence between os and pathlib (#20103)
<!--
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

Part of https://github.com/astral-sh/ruff/pull/20100 |
https://github.com/astral-sh/ruff/pull/20100#issuecomment-3225349156
2025-08-26 17:33:33 -04:00
chiri
f558bf721c [flake8-use-pathlib] Make PTH100 fix unsafe because it can change behavior (#20100)
<!--
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
Fixes https://github.com/astral-sh/ruff/issues/20088
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

`cargo nextest run flake8_use_pathlib`

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-08-26 18:59:12 +00:00
chiri
ea1c080881 [flake8-use-pathlib] Delete unused Rule::OsSymlink enabled check (#20099)
<!--
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
Part of #20009 (i forgot to delete it in this PR)
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
2025-08-26 12:05:52 -04:00
Renkai Ge
73720c73be [ty] Add search paths info to unresolved import diagnostics (#20040)
Fixes https://github.com/astral-sh/ty/issues/457

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-08-26 11:01:16 -04:00
Hamir Mahal
136abace92 [flake8-logging-format] Add auto-fix for f-string logging calls (G004) (#19303)
Closes #19302

<!--
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
This adds an auto-fix for `Logging statement uses f-string` Ruff G004,
so users don't have to resolve it manually.
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
I ran the auto-fixes on a Python file locally and and it worked as
expected.
<!-- How was it tested? -->

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-08-26 10:51:24 -04:00
Brent Westbrook
bc7274d148 Add a ScopeKind for the __class__ cell (#20048)
Summary
--

This PR aims to resolve (or help to resolve) #18442 and #19357 by
encoding the CPython semantics around the `__class__` cell in our
semantic model. Namely,

> `__class__` is an implicit closure reference created by the compiler
if any methods in a class body refer to either `__class__` or super.

from the Python
[docs](https://docs.python.org/3/reference/datamodel.html#creating-the-class-object).

As noted in the variant docs by @AlexWaygood, we don't fully model this
behavior, opting always to create the `__class__` cell binding in a new
`ScopeKind::DunderClassCell` around each method definition, without
checking if any method in the class body actually refers to `__class__`
or `super`.

As such, this PR fixes #18442 but not #19357.

Test Plan
--

Existing tests, plus the tests from #19783, which now pass without any
rule-specific code.

Note that we opted not to alter the behavior of F841 here because
flagging `__class__` in these cases still seems helpful. See the
discussion in
https://github.com/astral-sh/ruff/pull/20048#discussion_r2296252395 and
in the test comments for more information.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Mikko Leppänen <mleppan23@gmail.com>
2025-08-26 09:49:08 -04:00
Avasam
911d5cc973 Fix incorrect D413 links in docstrings convention FAQ (#20089)
## Summary

D413 in this section was incorrectly linking to D410.

I haven't checked if this issue happens anywhere else in the docs.

## Test Plan

Look at docs
2025-08-26 10:24:58 +05:30
Matthew Mckee
8d6dc7d3a3 [ty] Refactor inlay hints structure to use separate parts (#20052)
## Summary

Our internal inlay hints structure (`ty_ide::InlayHint`) now more
closely resembles `lsp_types::InlayHint`.

This mainly allows us to convert to `lsp_types::InlayHint` with less
hassle, but it also allows us to manage the different parts of the inlay
hint better, which in the future will allow us to implement features
like goto on the type part of the type inlay hint.

It also really isn't important to store a specific `Type` instance in
the `InlayHintContent`. So we remove this and use `InlayHintLabel`
instead which just shows the representation of the type (along with
other information).

We see a similar structure used in rust-analyzer too.
2025-08-26 10:21:31 +05:30
Dylan
ef4897f9f3 [ty] Add support for PEP 750 t-strings (#20085)
This PR attempts to adds support for inferring`string.templatelib.Template` for t-string literals.
2025-08-25 18:49:49 +00:00
Alex Waygood
ecf3c4ca11 [ty] Add support for PEP 800 (#20084) 2025-08-25 19:39:05 +01:00
Carl Meyer
33c5f6f4f8 [ty] don't mark entire type-alias scopes as Deferred (#20086)
## Summary

This has been here for awhile (since our initial PEP 695 type alias
support) but isn't really correct. The right-hand-side of a PEP 695 type
alias is a distinct scope, and we don't mark it as an "eager" nested
scope, so it automatically gets "deferred" resolution of names from
outer scopes (just like a nested function). Thus it's
redundant/unnecessary for us to use `DeferredExpressionState::Deferred`
for resolving that RHS expression -- that's for deferring resolution of
individual names within a scope. Using it here causes us to wrongly
ignore applicable outer-scope narrowing.

## Test Plan

Added mdtest that failed before this PR (the second snippet -- the first
snippet always passed.)
2025-08-25 11:32:18 -07:00
github-actions[bot]
ba47010150 [ty] Sync vendored typeshed stubs (#20083)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-08-25 17:01:51 +00:00
Wei Lee
db423ee978 [airflow] replace wrong path airflow.io.stroage as airflow.io.store (AIR311) (#20081)
## Summary

`airflow.io.storage` is not the correct path. it should be
`airflow.io.store` instead
2025-08-25 10:15:34 -05:00
Alex Waygood
a04823cfad [ty] Completely ignore typeshed's stub for Any (#20079) 2025-08-25 15:27:55 +01:00
Brent Westbrook
d0bcf56bd9 Improve diff rendering for notebooks (#20036)
## Summary

As noted in a code TODO, our `Diff` rendering code previously didn't
have any
special handling for notebooks. This was particularly obvious when the
diffs
were rendered right next to the corresponding diagnostic because the
diagnostic
used cell-based line numbers, while the diff was still using line
numbers from
the concatenated source. This PR updates the diff rendering to handle
notebooks
too.

The main improvements shown in the example below are:

- Line numbers are now remapped to be relative to their cell
- Context lines from other cells are suppressed

```
error[unused-import][*]: `math` imported but unused                             
 --> notebook.ipynb:cell 2:2:8                                                  
  |                                                                             
1 | # cell 2                                                                    
2 | import math                                                                 
  |        ^^^^                                                                 
3 |                                                                             
4 | print('hello world')                                                        
  |                                                                             
help: Remove unused import: `math`                                              
                                                                                
ℹ Safe fix                                                                      
1 1 | # cell 2                                                                  
2   |-import math                                                               
3 2 |                                                                           
4 3 | print('hello world')                                                      
```

I tried a few different approaches here before finally just splitting
the notebook into separate text ranges by cell and diffing each one
separately. It seems to work and passes all of our tests, but I don't
know if it's actually enforced anywhere that a single edit doesn't span
cells. Such an edit would silently be dropped right now since it would
fail the `contains_range` check. I also feel like I may have overlooked
an existing way to partition a file into cells like this.

## Test Plan

Existing notebook tests, plus a new one in `ruff_db`
2025-08-25 09:20:42 -04:00
Eric Jolibois
f9bbee33f6 [ty] validate constructor call of TypedDict (#19810)
## Summary
Implement validation for `TypedDict` constructor calls and dictionary
literal assignments, including support for `total=False` and proper
field management.
Also add support for `Required` and `NotRequired` type qualifiers in
`TypedDict` classes, along with proper inheritance behavior and the
`total=` parameter.
Support both constructor calls and dict literal syntax

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

### Basic Required Field Validation
```py
class Person(TypedDict):
    name: str
    age: int | None

# Error: Missing required field 'name' in TypedDict `Person` constructor
incomplete = Person(age=25)

# Error: Invalid argument to key "name" with declared type `str` on TypedDict `Person`
wrong_type = Person(name=123, age=25)

# Error: Invalid key access on TypedDict `Person`: Unknown key "extra"
extra_field = Person(name="Bob", age=25, extra=True)
```
<img width="773" height="191" alt="Screenshot 2025-08-07 at 17 59 22"
src="https://github.com/user-attachments/assets/79076d98-e85f-4495-93d6-a731aa72a5c9"
/>

### Support for `total=False`
```py
class OptionalPerson(TypedDict, total=False):
    name: str
    age: int | None

# All valid - all fields are optional with total=False
charlie = OptionalPerson()
david = OptionalPerson(name="David")
emily = OptionalPerson(age=30)
frank = OptionalPerson(name="Frank", age=25)

# But type validation and extra fields still apply
invalid_type = OptionalPerson(name=123)  # Error: Invalid argument type
invalid_extra = OptionalPerson(extra=True)  # Error: Invalid key access
```

### Dictionary Literal Validation
```py
# Type checking works for both constructors and dict literals
person: Person = {"name": "Alice", "age": 30}

reveal_type(person["name"])  # revealed: str
reveal_type(person["age"])   # revealed: int | None

# Error: Invalid key access on TypedDict `Person`: Unknown key "non_existing"
reveal_type(person["non_existing"])  # revealed: Unknown
```

### `Required`, `NotRequired`, `total`
```python
from typing import TypedDict
from typing_extensions import Required, NotRequired

class PartialUser(TypedDict, total=False):
    name: Required[str]      # Required despite total=False
    age: int                 # Optional due to total=False
    email: NotRequired[str]  # Explicitly optional (redundant)

class User(TypedDict):
    name: Required[str]      # Explicitly required (redundant)
    age: int                 # Required due to total=True
    bio: NotRequired[str]    # Optional despite total=True

# Valid constructions
partial = PartialUser(name="Alice")  # name required, age optional
full = User(name="Bob", age=25)      # name and age required, bio optional

# Inheritance maintains original field requirements
class Employee(PartialUser):
    department: str                  # Required (new field)
    # name: still Required (inherited)
    # age: still optional (inherited)

emp = Employee(name="Charlie", department="Engineering")  # 
Employee(department="Engineering")  # 
e: Employee = {"age": 1}  # 
```

<img width="898" height="683" alt="Screenshot 2025-08-11 at 22 02 57"
src="https://github.com/user-attachments/assets/4c1b18cd-cb2e-493a-a948-51589d121738"
/>

## Implementation
The implementation reuses existing validation logic done in
https://github.com/astral-sh/ruff/pull/19782

### ℹ️ Why I did NOT synthesize an `__init__` for `TypedDict`:

`TypedDict` inherits `dict.__init__(self, *args, **kwargs)` that accepts
all arguments.
The type resolution system finds this inherited signature **before**
looking for synthesized members.
So `own_synthesized_member()` is never called because a signature
already exists.

To force synthesis, you'd have to override Python’s inheritance
mechanism, which would break compatibility with the existing ecosystem.

This is why I went with ad-hoc validation. IMO it's the only viable
approach that respects Python’s
inheritance semantics while providing the required validation.

### Refacto of `Field`

**Before:**
```rust
struct Field<'db> {
    declared_ty: Type<'db>,
    default_ty: Option<Type<'db>>,     // NamedTuple and dataclass only
    init_only: bool,                   // dataclass only  
    init: bool,                        // dataclass only
    is_required: Option<bool>,         // TypedDict only
}
```

**After:**
```rust
struct Field<'db> {
    declared_ty: Type<'db>,
    kind: FieldKind<'db>,
}

enum FieldKind<'db> {
    NamedTuple { default_ty: Option<Type<'db>> },
    Dataclass { default_ty: Option<Type<'db>>, init_only: bool, init: bool },
    TypedDict { is_required: bool },
}
```

## Test Plan
Updated Markdown tests

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-08-25 14:45:52 +02:00
Dhruv Manilawala
376e3ff395 [ty] Limit argument expansion size for overload call evaluation (#20041)
## Summary

This PR limits the argument type expansion size for an overload call
evaluation to 512.

The limit chosen is arbitrary but I've taken the 256 limit from Pyright
into account and bumped it x2 to start with.

Initially, I actually started out by trying to refactor the entire
argument type expansion to be lazy. Currently, expanding a single
argument at any position eagerly creates the combination (argument
lists) and returns that (`Vec<CallArguments>`) but I thought we could
make it lazier by converting the return type of `expand` from
`Iterator<Item = Vec<CallArguments>>` to `Iterator<Item = Iterator<Item
= CallArguments>>` but that's proving to be difficult to implement
mainly because we **need** to maintain the previous expansion to
generate the next expansion which is the main reason to use
`std::iter::successors` in the first place.

Another approach would be to eagerly expand all the argument types and
then use the `combinations` from `itertools` to generate the
combinations but we would need to find the "boundary" between arguments
lists produced from expanding argument at position 1 and position 2
because that's important for the algorithm.

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

## Test Plan

Add test case to demonstrate the limit along with the diagnostic
snapshot stating that the limit has been reached.
2025-08-25 09:43:04 +00:00
renovate[bot]
b57cc5be33 Update actions/checkout digest to ff7abcd (#20061)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| actions/checkout | action | digest | `08c6903` -> `ff7abcd` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-08-25 11:04:07 +05:30
renovate[bot]
3c1fe12259 Update Rust crate regex-automata to v0.4.10 (#20068)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[regex-automata](https://redirect.github.com/rust-lang/regex/tree/master/regex-automata)
([source](https://redirect.github.com/rust-lang/regex)) |
workspace.dependencies | patch | `0.4.9` -> `0.4.10` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>rust-lang/regex (regex-automata)</summary>

###
[`v0.4.10`](https://redirect.github.com/rust-lang/regex/compare/regex-automata-0.4.9...regex-automata-0.4.10)

[Compare
Source](https://redirect.github.com/rust-lang/regex/compare/regex-automata-0.4.9...regex-automata-0.4.10)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:44:59 +05:30
renovate[bot]
e4289deb5a Update Rust crate regex to v1.11.2 (#20067)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [regex](https://redirect.github.com/rust-lang/regex) |
workspace.dependencies | patch | `1.11.1` -> `1.11.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>rust-lang/regex (regex)</summary>

###
[`v1.11.2`](https://redirect.github.com/rust-lang/regex/blob/HEAD/CHANGELOG.md#1112-2025-08-24)

[Compare
Source](https://redirect.github.com/rust-lang/regex/compare/1.11.1...1.11.2)

\===================
This is a new patch release of `regex` with some minor fixes. A larger
number
of typo or lint fix patches were merged. Also, we now finally recommend
using
`std::sync::LazyLock`.

Improvements:

- [BUG
#&#8203;1217](https://redirect.github.com/rust-lang/regex/issues/1217):
  Switch recommendation from `once_cell` to `std::sync::LazyLock`.
- [BUG
#&#8203;1225](https://redirect.github.com/rust-lang/regex/issues/1225):
  Add `DFA::set_prefilter` to `regex-automata`.

Bug fixes:

- [BUG
#&#8203;1165](https://redirect.github.com/rust-lang/regex/pull/1150):
Remove `std` dependency from `perf-literal-multisubstring` crate
feature.
- [BUG
#&#8203;1165](https://redirect.github.com/rust-lang/regex/pull/1165):
  Clarify the meaning of `(?R)$` in the documentation.
- [BUG
#&#8203;1281](https://redirect.github.com/rust-lang/regex/pull/1281):
Remove `fuzz/` and `record/` directories from published crate on
crates.io.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:44:47 +05:30
renovate[bot]
dbbcb7f452 Update cargo-bins/cargo-binstall action to v1.15.1 (#20075)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cargo-bins/cargo-binstall](https://redirect.github.com/cargo-bins/cargo-binstall)
| action | minor | `v1.14.4` -> `v1.15.1` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>cargo-bins/cargo-binstall (cargo-bins/cargo-binstall)</summary>

###
[`v1.15.1`](https://redirect.github.com/cargo-bins/cargo-binstall/releases/tag/v1.15.1)

[Compare
Source](https://redirect.github.com/cargo-bins/cargo-binstall/compare/v1.15.0...v1.15.1)

*Binstall is a tool to fetch and install Rust-based executables as
binaries. It aims to be a drop-in replacement for `cargo install` in
most cases. Install it today with `cargo install cargo-binstall`, from
the binaries below, or if you already have it, upgrade with `cargo
binstall cargo-binstall`.*

##### In this release:

- fix failure to create settings manifest file
([#&#8203;2268](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2268)
[#&#8203;2271](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2271))
- fix race condition in creating, loading and writing of settings
manifest file
([#&#8203;2272](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2272))
- fix infinite hang on `--self-install` due to the quickinstall consent
([#&#8203;2269](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2269)
[#&#8203;2273](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2273))

###
[`v1.15.0`](https://redirect.github.com/cargo-bins/cargo-binstall/releases/tag/v1.15.0)

[Compare
Source](https://redirect.github.com/cargo-bins/cargo-binstall/compare/v1.14.4...v1.15.0)

*Binstall is a tool to fetch and install Rust-based executables as
binaries. It aims to be a drop-in replacement for `cargo install` in
most cases. Install it today with `cargo install cargo-binstall`, from
the binaries below, or if you already have it, upgrade with `cargo
binstall cargo-binstall`.*

##### In this release:

- confirm on the first time using quickinstall
([#&#8203;2223](https://redirect.github.com/cargo-bins/cargo-binstall/issues/2223))

##### Other changes:

- upgrade dependencies

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:43:27 +05:30
renovate[bot]
c6dfdb1d39 Update Rust crate url to v2.5.7 (#20073)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [url](https://redirect.github.com/servo/rust-url) |
workspace.dependencies | patch | `2.5.4` -> `2.5.7` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>servo/rust-url (url)</summary>

###
[`v2.5.5`](https://redirect.github.com/servo/rust-url/releases/tag/v2.5.5)

[Compare
Source](https://redirect.github.com/servo/rust-url/compare/v2.5.4...v2.5.5)

##### What's Changed

- ci: downgrade crates when building for Rust 1.67.0 by
[@&#8203;mxinden](https://redirect.github.com/mxinden) in
[https://github.com/servo/rust-url/pull/1003](https://redirect.github.com/servo/rust-url/pull/1003)
- ci: run unit tests with sanitizers by
[@&#8203;mxinden](https://redirect.github.com/mxinden) in
[https://github.com/servo/rust-url/pull/1002](https://redirect.github.com/servo/rust-url/pull/1002)
- fix small typo by [@&#8203;hkBst](https://redirect.github.com/hkBst)
in
[https://github.com/servo/rust-url/pull/1011](https://redirect.github.com/servo/rust-url/pull/1011)
- chore: fix clippy errors on main by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1019](https://redirect.github.com/servo/rust-url/pull/1019)
- perf: remove heap allocation in parse\_query by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1020](https://redirect.github.com/servo/rust-url/pull/1020)
- perf: slightly improve parsing a port by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1022](https://redirect.github.com/servo/rust-url/pull/1022)
- perf: improve to\_file\_path() by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1018](https://redirect.github.com/servo/rust-url/pull/1018)
- perf: make parse\_scheme slightly faster by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1025](https://redirect.github.com/servo/rust-url/pull/1025)
- update LICENSE-MIT by
[@&#8203;wmjae](https://redirect.github.com/wmjae) in
[https://github.com/servo/rust-url/pull/1029](https://redirect.github.com/servo/rust-url/pull/1029)
- perf: url encode path segments in longer string slices by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1026](https://redirect.github.com/servo/rust-url/pull/1026)
- Disable the default features on serde by
[@&#8203;rilipco](https://redirect.github.com/rilipco) in
[https://github.com/servo/rust-url/pull/1033](https://redirect.github.com/servo/rust-url/pull/1033)
- docs: base url relative join by
[@&#8203;tisonkun](https://redirect.github.com/tisonkun) in
[https://github.com/servo/rust-url/pull/1013](https://redirect.github.com/servo/rust-url/pull/1013)
- perf: remove heap allocation in parse\_host by
[@&#8203;dsherret](https://redirect.github.com/dsherret) in
[https://github.com/servo/rust-url/pull/1021](https://redirect.github.com/servo/rust-url/pull/1021)
- Update tests to Unicode 16.0 by
[@&#8203;hsivonen](https://redirect.github.com/hsivonen) in
[https://github.com/servo/rust-url/pull/1045](https://redirect.github.com/servo/rust-url/pull/1045)
- Add some some basic functions to `Mime` by
[@&#8203;mrobinson](https://redirect.github.com/mrobinson) in
[https://github.com/servo/rust-url/pull/1047](https://redirect.github.com/servo/rust-url/pull/1047)
- ran `cargo clippy --fix -- -Wclippy::use_self` by
[@&#8203;mrobinson](https://redirect.github.com/mrobinson) in
[https://github.com/servo/rust-url/pull/1048](https://redirect.github.com/servo/rust-url/pull/1048)
- Fix MSRV and clippy CI by
[@&#8203;Manishearth](https://redirect.github.com/Manishearth) in
[https://github.com/servo/rust-url/pull/1058](https://redirect.github.com/servo/rust-url/pull/1058)
- Update `Url::domain` docs to show that it includes subdomain by
[@&#8203;supercoolspy](https://redirect.github.com/supercoolspy) in
[https://github.com/servo/rust-url/pull/1057](https://redirect.github.com/servo/rust-url/pull/1057)
- set\_hostname should error when encountering colon ':' by
[@&#8203;edgul](https://redirect.github.com/edgul) in
[https://github.com/servo/rust-url/pull/1060](https://redirect.github.com/servo/rust-url/pull/1060)
- version bump to 2.5.5 by
[@&#8203;edgul](https://redirect.github.com/edgul) in
[https://github.com/servo/rust-url/pull/1061](https://redirect.github.com/servo/rust-url/pull/1061)

##### New Contributors

- [@&#8203;mxinden](https://redirect.github.com/mxinden) made their
first contribution in
[https://github.com/servo/rust-url/pull/1003](https://redirect.github.com/servo/rust-url/pull/1003)
- [@&#8203;hkBst](https://redirect.github.com/hkBst) made their first
contribution in
[https://github.com/servo/rust-url/pull/1011](https://redirect.github.com/servo/rust-url/pull/1011)
- [@&#8203;wmjae](https://redirect.github.com/wmjae) made their first
contribution in
[https://github.com/servo/rust-url/pull/1029](https://redirect.github.com/servo/rust-url/pull/1029)
- [@&#8203;rilipco](https://redirect.github.com/rilipco) made their
first contribution in
[https://github.com/servo/rust-url/pull/1033](https://redirect.github.com/servo/rust-url/pull/1033)
- [@&#8203;tisonkun](https://redirect.github.com/tisonkun) made their
first contribution in
[https://github.com/servo/rust-url/pull/1013](https://redirect.github.com/servo/rust-url/pull/1013)
- [@&#8203;supercoolspy](https://redirect.github.com/supercoolspy) made
their first contribution in
[https://github.com/servo/rust-url/pull/1057](https://redirect.github.com/servo/rust-url/pull/1057)

**Full Changelog**:
https://github.com/servo/rust-url/compare/v2.5.4...v2.5.5

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:42:38 +05:30
renovate[bot]
c65029f9a5 Update Rust crate syn to v2.0.106 (#20070)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [syn](https://redirect.github.com/dtolnay/syn) |
workspace.dependencies | patch | `2.0.104` -> `2.0.106` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>dtolnay/syn (syn)</summary>

###
[`v2.0.106`](https://redirect.github.com/dtolnay/syn/releases/tag/2.0.106)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.105...2.0.106)

- Replace `~const` syntax with `[const]` conditionally const syntax in
trait bounds
([#&#8203;1896](https://redirect.github.com/dtolnay/syn/issues/1896),
[rust-lang/rust#139858](https://redirect.github.com/rust-lang/rust/pull/139858))
- Support conditionally const impl Trait types
([#&#8203;1897](https://redirect.github.com/dtolnay/syn/issues/1897))
- Reject polarity modifier and lifetime binder used in the same trait
bound
([#&#8203;1899](https://redirect.github.com/dtolnay/syn/issues/1899),
[rust-lang/rust#127054](https://redirect.github.com/rust-lang/rust/pull/127054))
- Parse const trait bounds with bound lifetimes
([#&#8203;1902](https://redirect.github.com/dtolnay/syn/issues/1902))
- Parse bound lifetimes with lifetime bounds
([#&#8203;1903](https://redirect.github.com/dtolnay/syn/issues/1903))
- Allow type parameters and const parameters in trait bounds and generic
closures
([#&#8203;1904](https://redirect.github.com/dtolnay/syn/issues/1904),
[#&#8203;1907](https://redirect.github.com/dtolnay/syn/issues/1907),
[#&#8203;1908](https://redirect.github.com/dtolnay/syn/issues/1908),
[#&#8203;1909](https://redirect.github.com/dtolnay/syn/issues/1909))

###
[`v2.0.105`](https://redirect.github.com/dtolnay/syn/releases/tag/2.0.105)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.104...2.0.105)

- Disallow "negative" inherent impls like `impl !T {}`
([#&#8203;1881](https://redirect.github.com/dtolnay/syn/issues/1881),
[rust-lang/rust#144386](https://redirect.github.com/rust-lang/rust/pull/144386))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:41:48 +05:30
renovate[bot]
a0bba718f6 Update Rust crate tracing-indicatif to v0.3.13 (#20072)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[tracing-indicatif](https://redirect.github.com/emersonford/tracing-indicatif)
| workspace.dependencies | patch | `0.3.12` -> `0.3.13` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>emersonford/tracing-indicatif (tracing-indicatif)</summary>

###
[`v0.3.13`](https://redirect.github.com/emersonford/tracing-indicatif/blob/HEAD/CHANGELOG.md#0313---2025-08-15)

[Compare
Source](https://redirect.github.com/emersonford/tracing-indicatif/compare/0.3.12...0.3.13)

- eliminate panics on internal lock poison

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:41:33 +05:30
renovate[bot]
1eab4dbd95 Update Rust crate thiserror to v2.0.16 (#20071)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [thiserror](https://redirect.github.com/dtolnay/thiserror) |
workspace.dependencies | patch | `2.0.12` -> `2.0.16` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>dtolnay/thiserror (thiserror)</summary>

###
[`v2.0.16`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.16)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.15...2.0.16)

- Add to "no-std" crates.io category
([#&#8203;429](https://redirect.github.com/dtolnay/thiserror/issues/429))

###
[`v2.0.15`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.15)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.14...2.0.15)

- Prevent `Error::provide` API becoming unavailable from a future new
compiler lint
([#&#8203;427](https://redirect.github.com/dtolnay/thiserror/issues/427))

###
[`v2.0.14`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.14)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.13...2.0.14)

- Allow build-script cleanup failure with NFSv3 output directory to be
non-fatal
([#&#8203;426](https://redirect.github.com/dtolnay/thiserror/issues/426))

###
[`v2.0.13`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.13)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.12...2.0.13)

- Documentation improvements

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:41:18 +05:30
renovate[bot]
3eb3c3572b Update astral-sh/setup-uv action to v6.6.0 (#20074)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
action | minor | `v6.4.3` -> `v6.6.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>astral-sh/setup-uv (astral-sh/setup-uv)</summary>

###
[`v6.6.0`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v6.6.0):
🌈 Support for .tools-versions

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v6.5.0...v6.6.0)

##### Changes

This release adds support for [asdf](https://asdf-vm.com/)
`.tool-versions` in the `version-file` input

##### 🐛 Bug fixes

- Add log message before long API calls to GitHub
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;530](https://redirect.github.com/astral-sh/setup-uv/issues/530))

##### 🚀 Enhancements

- Add support for .tools-versions
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;531](https://redirect.github.com/astral-sh/setup-uv/issues/531))

##### 🧰 Maintenance

- Bump dependencies
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;532](https://redirect.github.com/astral-sh/setup-uv/issues/532))
- chore: update known versions for 0.8.12
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;529](https://redirect.github.com/astral-sh/setup-uv/issues/529))
- chore: update known versions for 0.8.11
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;526](https://redirect.github.com/astral-sh/setup-uv/issues/526))
- chore: update known versions for 0.8.10
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;525](https://redirect.github.com/astral-sh/setup-uv/issues/525))

###
[`v6.5.0`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v6.5.0):
🌈 Better error messages, bug fixes and copilot agent settings

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v6.4.3...v6.5.0)

##### Changes

This release brings better error messages in case the GitHub API is
impacted, fixes a few bugs and allows to disable [problem
matchers](https://redirect.github.com/actions/toolkit/blob/main/docs/problem-matchers.md)
for better use in Copilot Agent workspaces.

##### 🐛 Bug fixes

- Improve error messages on GitHub API errors
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;518](https://redirect.github.com/astral-sh/setup-uv/issues/518))
- Ignore backslashes and whitespace in requirements
[@&#8203;axm2](https://redirect.github.com/axm2)
([#&#8203;501](https://redirect.github.com/astral-sh/setup-uv/issues/501))

##### 🚀 Enhancements

- Add input add-problem-matchers
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;517](https://redirect.github.com/astral-sh/setup-uv/issues/517))

##### 🧰 Maintenance

- chore: update known versions for 0.8.9
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;512](https://redirect.github.com/astral-sh/setup-uv/issues/512))
- chore: update known versions for 0.8.6-0.8.8
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;510](https://redirect.github.com/astral-sh/setup-uv/issues/510))
- chore: update known versions for 0.8.5
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;509](https://redirect.github.com/astral-sh/setup-uv/issues/509))
- chore: update known versions for 0.8.4
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;505](https://redirect.github.com/astral-sh/setup-uv/issues/505))
- chore: update known versions for 0.8.3
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;502](https://redirect.github.com/astral-sh/setup-uv/issues/502))

##### 📚 Documentation

- add note on caching to read disable-cache-pruning
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;506](https://redirect.github.com/astral-sh/setup-uv/issues/506))

##### ⬆️ Dependency updates

- Bump actions/checkout from 4 to 5
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;514](https://redirect.github.com/astral-sh/setup-uv/issues/514))
- bump dependencies
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;516](https://redirect.github.com/astral-sh/setup-uv/issues/516))
- Bump biome to v2
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;515](https://redirect.github.com/astral-sh/setup-uv/issues/515))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi43IiwidXBkYXRlZEluVmVyIjoiNDEuODIuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 10:40:46 +05:30
renovate[bot]
41bb24a87e Update Rust crate serde_json to v1.0.143 (#20069) 2025-08-24 22:20:39 -04:00
renovate[bot]
87f0da139a Update Rust crate proc-macro2 to v1.0.101 (#20066) 2025-08-24 22:20:24 -04:00
renovate[bot]
f9bfc9ab5b Update Rust crate ordermap to v0.5.9 (#20065) 2025-08-24 22:20:17 -04:00
renovate[bot]
59f7102606 Update Rust crate bitflags to v2.9.3 (#20063) 2025-08-24 22:19:54 -04:00
renovate[bot]
862d2d0687 Update dependency ruff to v0.12.10 (#20062) 2025-08-24 22:19:44 -04:00
renovate[bot]
48edf46f3b Update Rust crate filetime to v0.2.26 (#20064) 2025-08-24 22:19:34 -04:00
Jelle Zijlstra
ec86a4e960 [ty] Add Top[] and Bottom[] special forms, replacing top_materialization_of() function (#20054)
Part of astral-sh/ty#994

## Summary

Add new special forms to `ty_extensions`, `Top[T]` and `Bottom[T]`.
Remove `ty_extensions.top_materialization` and
`ty_extensions.bottom_materialization`.

## Test Plan

Converted the existing `materialization.md` mdtest to the new syntax.
Added some tests for invalid use of the new special form.
2025-08-23 11:20:56 -07:00
Andrew Gallant
e7237652a9 [ty] Lightly refactor document symbols AST visitor
This makes use of early continue/return to keep rightward drift under
control. (I also find it easier to read.)
2025-08-23 12:53:41 -04:00
Andrew Gallant
205eae14d2 [ty] Rejigger workspace symbols for more efficient caching
In effect, we make the Salsa query aspect keyed only on whether we want
global symbols. We move everything else (hierarchical and querying) to
an aggregate step *after* the query.

This was a somewhat involved change since we want to return a flattened
list from visiting the source while also preserving enough information
to reform the symbols into a hierarchical structure that the LSP
expects. But I think overall the API has gotten simpler and we encode
more invariants into the type system. (For example, previously you got a
runtime assertion if you tried to provide a query string while enabling
hierarchical mode. But now that's prevented by construction.)
2025-08-23 12:53:41 -04:00
Andrew Gallant
f407f12f4c [ty] Parallelize workspace symbols
This is a pretty naive approach, but it makes cold times for listing
workspace symbols in home-assistant under 1s on my machine.

Courtesy of Micha:
https://github.com/astral-sh/ruff/pull/20030#discussion_r2292924171
2025-08-23 12:53:41 -04:00
Andrew Gallant
fb2d0af18c [ty] Optimize "workspace symbols" retrieval
Basically, this splits the implementation into two pieces:
the first piece does the traversal and finds *all* symbols
across the workspace. The second piece does filtering based
on a user provided query string. Only the first piece is
cached by Salsa.

This brings warm "workspace symbols" requests down from
500-600ms to 100-200ms.
2025-08-23 12:53:41 -04:00
Andrew Gallant
8ead02e0b1 [ty] Optimize query string matching
While this doesn't typically matter, when ty returns a very
large list of symbols, this can have an impact. Specifically,
when searching `async` in home-assistant, this gets times
closer to 500ms versus closer to 600ms before this change.
It looks like an overall ~50ms improvement (so around 10%),
but variance is all over the place and I didn't do any
statistical tests.

But this does make intuitive sense. Previously, we were
allocating intermediate strings, doing UTF-8 decoding and
consulting Unicode casing tables. Now we're just doing what
is likely a single DFA scan. In effect, we front load all
of the Unicode junk into regex compilation.
2025-08-23 12:53:41 -04:00
Andrew Gallant
330bb4efbf [ty] Add some unit tests for "query matches symbol"
There is a small amount of subtlety to this matching routine,
and it could be implemented in a faster way. So let's right some
tests for what we have to ensure we don't break anything when
we optimize it.
2025-08-23 12:53:41 -04:00
Andrew Gallant
ad8c98117a [ty] Move query filtering outside of symbol visitor
This is prep work for turning this into a Salsa query.
Specifically, we don't want the Salsa query to be
dependent upon the query string.
2025-08-23 12:53:41 -04:00
Andrew Gallant
06dbec8479 [ty] Add debug trace for workspace symbol elapsed time
Useful for ad hoc debugging, but it's also useful to have permanently to
enable serendipitous discovery of performance problems.
2025-08-23 12:53:41 -04:00
Andrew Gallant
85931ab594 [ty] Add a TODO for linting on todo! 2025-08-23 12:53:41 -04:00
Ibraheem Ahmed
b3cc733f06 [ty] Remove duplicate global lint registry (#20053)
## Summary

Looks like an oversight at some point that led to two identical globals,
the one in `ty_project` just calls `ty_python_semantic::register_lints`.
2025-08-22 19:43:12 -04:00
Ibraheem Ahmed
7abc41727b [ty] Shrink size of AstNodeRef (#20028)
## Summary

Removes the `module_ptr` field from `AstNodeRef` in release mode, and
change `NodeIndex` to a `NonZeroU32` to reduce the size of
`Option<AstNodeRef<_>>` fields.

I believe CI runs in debug mode, so this won't show up in the memory
report, but this reduces memory by ~2% in release mode.
2025-08-22 17:03:22 -04:00
chiri
886c4e4773 [flake8-use-pathlib] Fix PTH211 autofix (#20049)
## Summary
Part of #20009
2025-08-22 13:35:08 -05:00
Alex Waygood
bc6ea68733 [ty] Add precise iteration and unpacking inference for string literals and bytes literals (#20023)
## Summary

Previously we held off from doing this because we weren't sure that it
was worth the added complexity cost. But our code has changed in the
months since we made that initial decision, and I think the structure of
the code is such that it no longer really leads to much added complexity
to add precise inference when unpacking a string literal or a bytes
literal.

The improved inference we gain from this has real benefits to users (see
the mypy_primer report), and this PR doesn't appear to have a
performance impact.

## Test plan

mdtests
2025-08-22 19:33:08 +01:00
Micha Reiser
796819e7a0 [ty] Disallow std::env and io methods in most ty crates (#20046)
## Summary

We use the `System` abstraction in ty to abstract away the host/system
on which ty runs.
This has a few benefits:

* Tests can run in full isolation using a memory system (that uses an
in-memory file system)
* The LSP has a custom implementation where `read_to_string` returns the
content as seen by the editor (e.g. unsaved changes) instead of always
returning the content as it is stored on disk
* We don't require any file system polyfills for wasm in the browser


However, it does require extra care that we don't accidentally use
`std::fs` or `std::env` (etc.) methods in ty's code base (which is very
easy).

This PR sets up Clippy and disallows the most common methods, instead
pointing users towards the corresponding `System` methods.

The setup is a bit awkward because clippy doesn't support inheriting
configurations. That means, a crate can only override the entire
workspace configuration or not at all.
The approach taken in this PR is:

* Configure the disallowed methods at the workspace level
* Allow `disallowed_methods` at the workspace level
* Enable the lint at the crate level using the warn attribute (in code)


The obvious downside is that it won't work if we ever want to disallow
other methods, but we can figure that out once we reach that point.

What about false positives: Just add an `allow` and move on with your
life :) This isn't something that we have to enforce strictly; the goal
is to catch accidental misuse.

## Test Plan

Clippy found a place where we incorrectly used `std::fs::read_to_string`
2025-08-22 11:13:47 -07:00
Vivek Dasari
5508e8e528 Add testing helper to compare stable vs preview snapshots (#19715)
## Summary
This PR implements a diff test helper `assert_diagnostics_diff` as
described in #19351. The diff file includes both the settings ( e.g.
`+linter.preview = enabled`) and the snapshot data itself.

The current implementation looks for each old diagnostic in the new
snapshot. This works when the preview behavior adds/removes a couple
diagnostics. This implementation does not work well when every
diagnostic is modified (e.g. a "fix" is added).
https://github.com/astral-sh/ruff/pull/19715#discussion_r2259410763 has
ideas for future improvements to this implementation.

The example usage in this PR writes the diff to `preview_diff` file
instead of `preview` file, which might be a useful convention to keep.


## Test Plan
- Included a unit test at:
https://github.com/astral-sh/ruff/pull/19715/files#diff-d49487fe3e8a8585529f62c2df2a2b0a4c44267a1f93d1e859dff1d9f8771d36R523
- Example usage of this new test helper:
https://github.com/astral-sh/ruff/pull/19715/files#diff-2a33ac11146d1794c01a29549a6041d3af6fb6f9b423a31ade12a88d1951b0c2R1
2025-08-22 12:49:34 -05:00
chiri
0be3e1fbbf [flake8-use-pathlib] Add autofix for PTH211 (#20009)
## Summary
Part of https://github.com/astral-sh/ruff/issues/2331
2025-08-22 12:38:37 -05:00
Micha Reiser
5d217b7f46 [ty] Add type as detail to completion items (#20047)
## Summary

@BurntSushi was so kind as to find me an easy task to do some coding
before I'm off to PTO.

This PR adds the type to completion items (see the gray little text at
the end of a completion item).



https://github.com/user-attachments/assets/c0a86061-fa12-47b4-b43c-3c646771a69d
2025-08-22 12:32:53 -04:00
Dylan
0b6ce1c788 [ruff] Handle empty t-strings in unnecessary-empty-iterable-within-deque-call (RUF037) (#20045)
Adds a method to `TStringValue` to detect whether the t-string is empty
_as an iterable_. Note the subtlety here that, unlike f-strings, an
empty t-string is still truthy (i.e. `bool(t"")==True`).

Closes #19951
2025-08-22 10:23:49 -05:00
Matthew Mckee
0e9d77e43a Fix incorrect lsp inlay hint type (#20044) 2025-08-22 17:12:49 +02:00
Carl Meyer
8b827c3c6c [ty] rename BareTypeAliasType to ManualPEP695TypeAliasType (#20037)
## Summary

Rename `TypeAliasType::Bare` to `TypeAliasType::ManualPEP695`, and
`BareTypeAliasType` to `ManualPEP695TypeAliasType`.

Why?

Both existing variants of `TypeAliasType` are specific to features added
in PEP 695 (which introduced both the `type` statement and
`types.TypeAliasType`), so it doesn't make sense to name one with the
name `PEP695` and not the other.

A "bare" type alias, in my mind, is a legacy type alias like `IntOrStr =
int | str`, which is "bare" in that there is nothing at all
distinguishing it as a type alias. I will want to use the "bare" name
for this variant, in a future PR.

The renamed variant here describes a type alias created with `IntOrStr =
types.TypeAliasType("IntOrStr", int | str)`, which is not "bare", it's
just "manually" instantiated instead of using the `type` statement
syntax sugar. (This is useful when using the `typing_extensions`
backport of `TypeAliasType` on older Python versions.)

## Test Plan

Pure rename, existing tests pass.
2025-08-22 07:40:29 -07:00
Max Mynter
c22395dbc6 [ruff] Fix false positive for t-strings in default-factory-kwarg (RUF026) (#20032)
Closes #19993

## Summary
Recognize t strings as never being callable to avoid false positives on
RUF026.
2025-08-22 09:29:42 -05:00
Micha Reiser
11f521c768 [ty] Close signature help after ) (#20017) 2025-08-22 16:09:22 +02:00
Micha Reiser
c5e05df966 [ty] Cancel background tasks when shutdown is requested (#20039) 2025-08-22 10:20:13 +02:00
github-actions[bot]
7a44ea680e [ty] Sync vendored typeshed stubs (#20031)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-08-21 21:32:48 +00:00
Alex Waygood
f82025d919 [ty] Improve diagnostics for bad calls to functions (#20022) 2025-08-21 22:00:44 +01:00
Micha Reiser
365f521c37 [ty] Fix incorrect docstring in call signature completion (#20021)
## Summary

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

The core issue is that `CallableType` is a salsa interned but
`Signature` (which `CallableType` stores) ignores the `Definition` in
its `Eq` and `Hash` implementation.

This PR tries to simplest fix by removing the custom `Eq` and `Hash`
implementation. The main downside of this fix is that it can increase
memory usage because `CallableType`s that are equal except for their
`Definition` are now interned separately.

The alternative is to remove `Definition` from `CallableType` and
instead, call `bindings` directly on the callee (call_expression.func).
However, this would require
addressing the TODO 

here
39ee71c2a5/crates/ty_python_semantic/src/types.rs (L4582-L4586)

This might probably be worth addressing anyway, but is the more involved
fix. That's why I opted for removing the custom `Eq` implementation.

We already "ignore" the definition during normalization, thank's to
Alex's work in https://github.com/astral-sh/ruff/pull/19615

## Test Plan



https://github.com/user-attachments/assets/248d1cb1-12fd-4441-adab-b7e0866d23eb
2025-08-21 16:36:40 -04:00
Aria Desires
fc5321e000 [ty] fix GotoTargets for keyword args in nested function calls (#20013)
While implementing similar logic for initializers I noticed that this
code appeared to be walking the ancestors in the wrong direction, and so
if you have nested function calls it would always grab the outermost one
instead of the closest-ancestor.

The four copies of the test are because there's something really evil in
our caching that can't seem to be demonstrated in our cursor testing
framework, which I'm filing a followup for.
2025-08-21 20:19:52 +00:00
3965 changed files with 242281 additions and 153789 deletions

View File

@@ -8,7 +8,3 @@ benchmark = "bench -p ruff_benchmark --bench linter --bench formatter --"
# See: https://github.com/astral-sh/ruff/issues/11503
[target.'cfg(all(target_env="msvc", target_os = "windows"))']
rustflags = ["-C", "target-feature=+crt-static"]
[target.'wasm32-unknown-unknown']
# See https://docs.rs/getrandom/latest/getrandom/#webassembly-support
rustflags = ["--cfg", 'getrandom_backend="wasm_js"']

View File

@@ -1,3 +1,16 @@
# Define serial test group for running tests sequentially.
[test-groups]
serial = { max-threads = 1 }
# Run ty file watching tests sequentially to avoid race conditions.
[[profile.default.overrides]]
filter = 'binary(file_watching)'
test-group = 'serial'
[[profile.default.overrides]]
filter = 'binary(e2e)'
test-group = 'serial'
[profile.ci]
# Print out output for failing tests as soon as they fail, and also at the end
# of the run (for easy scrollability).

View File

@@ -10,7 +10,7 @@ indent_style = space
insert_final_newline = true
indent_size = 2
[*.{rs,py,pyi}]
[*.{rs,py,pyi,toml}]
indent_size = 4
[*.snap]
@@ -18,6 +18,3 @@ trim_trailing_whitespace = false
[*.md]
max_line_length = 100
[*.toml]
indent_size = 4

View File

@@ -39,11 +39,11 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
@@ -68,11 +68,11 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: macos-14
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -110,11 +110,11 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: macos-14
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: arm64
@@ -166,11 +166,11 @@ jobs:
- target: aarch64-pc-windows-msvc
arch: x64
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.platform.arch }}
@@ -219,11 +219,11 @@ jobs:
- x86_64-unknown-linux-gnu
- i686-unknown-linux-gnu
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -296,11 +296,11 @@ jobs:
arch: riscv64
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
@@ -361,11 +361,11 @@ jobs:
- x86_64-unknown-linux-musl
- i686-unknown-linux-musl
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -427,11 +427,11 @@ jobs:
arch: armv7
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"

View File

@@ -33,14 +33,14 @@ jobs:
- linux/amd64
- linux/arm64
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
persist-credentials: false
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -113,7 +113,7 @@ jobs:
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
- name: Download digests
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: /tmp/digests
pattern: digests-*
@@ -131,7 +131,7 @@ jobs:
type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }}
type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }}
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -169,7 +169,7 @@ jobs:
steps:
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -256,7 +256,7 @@ jobs:
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
- name: Download digests
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: /tmp/digests
pattern: digests-*
@@ -276,7 +276,7 @@ jobs:
type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }}
type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }}
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}

View File

@@ -12,13 +12,18 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
defaults:
run:
shell: bash
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
PACKAGE_NAME: ruff
PYTHON_VERSION: "3.13"
PYTHON_VERSION: "3.14"
NEXTEST_PROFILE: ci
jobs:
determine_changes:
@@ -43,7 +48,7 @@ jobs:
# Flag that is set to "true" when code related to the playground changes.
playground: ${{ steps.check_playground.outputs.changed }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -88,7 +93,6 @@ jobs:
':!crates/ruff_python_formatter/**' \
':!crates/ruff_formatter/**' \
':!crates/ruff_dev/**' \
':!crates/ruff_db/**' \
':scripts/*' \
':python/**' \
':.github/workflows/ci.yaml' \
@@ -143,7 +147,7 @@ jobs:
env:
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
run: |
if git diff --quiet "${MERGE_BASE}...HEAD" -- 'python/py_fuzzer/**' \
if git diff --quiet "${MERGE_BASE}...HEAD" -- 'python/py-fuzzer/**' \
; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
@@ -209,7 +213,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: "Install Rust toolchain"
@@ -223,10 +227,12 @@ jobs:
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: |
rustup component add clippy
@@ -238,27 +244,34 @@ jobs:
cargo-test-linux:
name: "cargo test (linux)"
runs-on: depot-ubuntu-22.04-16
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
needs: determine_changes
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
shared-key: ruff-linux-debug
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-insta
- name: "Install uv"
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
enable-cache: "true"
- name: ty mdtests (GitHub annotations)
if: ${{ needs.determine_changes.outputs.ty == 'true' }}
env:
@@ -268,11 +281,9 @@ jobs:
# This step is just to get nice GitHub annotations on the PR diff in the files-changed tab.
run: cargo test -p ty_python_semantic --test mdtest || true
- name: "Run tests"
shell: bash
env:
NEXTEST_PROFILE: "ci"
run: cargo insta test --all-features --unreferenced reject --test-runner nextest
- name: Dogfood ty on py-fuzzer
run: uv run --project=./python/py-fuzzer cargo run -p ty check --project=./python/py-fuzzer
# Check for broken links in the documentation.
- run: cargo doc --all --no-deps
env:
@@ -285,67 +296,70 @@ jobs:
env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ruff
path: target/debug/ruff
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ty
path: target/debug/ty
cargo-test-linux-release:
name: "cargo test (linux, release)"
# release builds timeout on GitHub runners, so this job is just skipped on forks in the `if` check
runs-on: depot-ubuntu-22.04-16
needs: determine_changes
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
if: |
github.repository == 'astral-sh/ruff' &&
!contains(github.event.pull_request.labels.*.name, 'no-test') &&
(needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main')
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
- name: "Install uv"
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
tool: cargo-insta
enable-cache: "true"
- name: "Run tests"
shell: bash
env:
NEXTEST_PROFILE: "ci"
run: cargo insta test --release --all-features --unreferenced reject --test-runner nextest
run: cargo nextest run --cargo-profile profiling --all-features
- name: "Run doctests"
run: cargo test --doc --profile profiling --all-features
cargo-test-windows:
name: "cargo test (windows)"
runs-on: depot-windows-2022-16
cargo-test-other:
strategy:
matrix:
platform:
- ${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}
- macos-latest
name: "cargo test (${{ matrix.platform }})"
runs-on: ${{ matrix.platform }}
needs: determine_changes
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo nextest"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-nextest
- name: "Install uv"
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
enable-cache: "true"
- name: "Run tests"
shell: bash
env:
NEXTEST_PROFILE: "ci"
# Workaround for <https://github.com/nextest-rs/nextest/issues/1493>.
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
run: |
cargo nextest run --all-features --profile ci
cargo test --all-features --doc
@@ -357,15 +371,17 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 20
node-version: 22
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-pack-action@0d096b08b4e5a7de8c28de67e11e945404e9eefa # v0.4.0
@@ -380,31 +396,14 @@ jobs:
cd crates/ty_wasm
wasm-pack test --node
cargo-build-release:
name: "cargo build (release)"
runs-on: macos-latest
if: ${{ github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1
- name: "Build"
run: cargo build --release --locked
cargo-build-msrv:
name: "cargo build (msrv)"
runs-on: depot-ubuntu-latest-8
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-latest-8' || 'ubuntu-latest' }}
needs: determine_changes
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: SebRollen/toml-action@b1b3628f55fc3a28208d4203ada8b737e9687876 # v1.2.0
@@ -412,15 +411,16 @@ jobs:
with:
file: "Cargo.toml"
field: "workspace.package.rust-version"
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
env:
MSRV: ${{ steps.msrv.outputs.value }}
run: rustup default "${MSRV}"
- name: "Install mold"
uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Build tests"
shell: bash
env:
MSRV: ${{ steps.msrv.outputs.value }}
run: cargo "+${MSRV}" test --no-run --all-features
@@ -432,18 +432,19 @@ jobs:
if: ${{ github.ref == 'refs/heads/main' || needs.determine_changes.outputs.fuzz == 'true' || needs.determine_changes.outputs.code == 'true' }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "fuzz -> target"
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Install cargo-binstall"
uses: cargo-bins/cargo-binstall@79e4beb1e02f733a26129a6bf26c37dab4ab3307 # v1.14.4
with:
tool: cargo-fuzz@0.11.2
uses: cargo-bins/cargo-binstall@ae04fb5e853ae6cd3ad7de4a1d554a8b646d12aa # v1.15.11
- name: "Install cargo-fuzz"
# Download the latest version from quick install and not the github releases because github releases only has MUSL targets.
run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm
@@ -452,37 +453,33 @@ jobs:
fuzz-parser:
name: "fuzz parser"
runs-on: ubuntu-latest
needs:
- cargo-test-linux
- determine_changes
needs: determine_changes
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.parser == 'true' || needs.determine_changes.outputs.py-fuzzer == 'true') }}
timeout-minutes: 20
env:
FORCE_COLOR: 1
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
name: Download Ruff binary to test
id: download-cached-binary
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
name: ruff
path: ruff-to-test
shared-key: ruff-linux-debug
save-if: false
- name: "Install Rust toolchain"
run: rustup show
- name: Build Ruff binary
run: cargo build --bin ruff
- name: Fuzz
env:
DOWNLOAD_PATH: ${{ steps.download-cached-binary.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x "${DOWNLOAD_PATH}/ruff"
(
uvx \
uv run \
--python="${PYTHON_VERSION}" \
--from=./python/py-fuzzer \
--project=./python/py-fuzzer \
--locked \
fuzz \
--test-executable="${DOWNLOAD_PATH}/ruff" \
--test-executable=target/debug/ruff \
--bin=ruff \
0-500
)
@@ -494,10 +491,13 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 5
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: "Install Rust toolchain"
run: rustup component add rustfmt
# Run all code generation scripts, and verify that the current output is
@@ -512,56 +512,66 @@ jobs:
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
./scripts/add_rule.py --name FirstRule --prefix TST --code 001 --linter test
- run: cargo check
# Lint/format/type-check py-fuzzer
# (dogfooding with ty is done in a separate job)
- run: uv run --directory=./python/py-fuzzer mypy
- run: uv run --directory=./python/py-fuzzer ruff format --check
- run: uv run --directory=./python/py-fuzzer ruff check
ecosystem:
name: "ecosystem"
runs-on: depot-ubuntu-latest-8
needs:
- cargo-test-linux
- determine_changes
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-latest-8' || 'ubuntu-latest' }}
needs: determine_changes
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
# Ecosystem check needs linter and/or formatter changes.
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && needs.determine_changes.outputs.code == 'true' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.base.ref }}
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
python-version: ${{ env.PYTHON_VERSION }}
activate-environment: true
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
name: Download comparison Ruff binary
id: ruff-target
with:
name: ruff
path: target/debug
- name: "Install Rust toolchain"
run: rustup show
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download baseline Ruff binary
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
name: ruff
branch: ${{ github.event.pull_request.base.ref }}
workflow: "ci.yaml"
check_artifacts: true
shared-key: ruff-linux-debug
save-if: false
- name: Build baseline version
run: |
cargo build --bin ruff
mv target/debug/ruff target/debug/ruff-baseline
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
clean: false
- name: Build comparison version
run: cargo build --bin ruff
- name: Install ruff-ecosystem
run: |
pip install ./python/ruff-ecosystem
uv pip install ./python/ruff-ecosystem
- name: Run `ruff check` stable ecosystem check
if: ${{ needs.determine_changes.outputs.linter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem check ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown | tee ecosystem-result-check-stable
ruff-ecosystem check ./target/debug/ruff-baseline ./target/debug/ruff --cache ./checkouts --output-format markdown | tee ecosystem-result-check-stable
cat ecosystem-result-check-stable > "$GITHUB_STEP_SUMMARY"
echo "### Linter (stable)" > ecosystem-result
@@ -570,16 +580,11 @@ jobs:
- name: Run `ruff check` preview ecosystem check
if: ${{ needs.determine_changes.outputs.linter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem check ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-check-preview
ruff-ecosystem check ./target/debug/ruff-baseline ./target/debug/ruff --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-check-preview
cat ecosystem-result-check-preview > "$GITHUB_STEP_SUMMARY"
echo "### Linter (preview)" >> ecosystem-result
@@ -588,16 +593,11 @@ jobs:
- name: Run `ruff format` stable ecosystem check
if: ${{ needs.determine_changes.outputs.formatter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem format ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown | tee ecosystem-result-format-stable
ruff-ecosystem format ./target/debug/ruff-baseline ./target/debug/ruff --cache ./checkouts --output-format markdown | tee ecosystem-result-format-stable
cat ecosystem-result-format-stable > "$GITHUB_STEP_SUMMARY"
echo "### Formatter (stable)" >> ecosystem-result
@@ -606,32 +606,19 @@ jobs:
- name: Run `ruff format` preview ecosystem check
if: ${{ needs.determine_changes.outputs.formatter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem format ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-format-preview
ruff-ecosystem format ./target/debug/ruff-baseline ./target/debug/ruff --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-format-preview
cat ecosystem-result-format-preview > "$GITHUB_STEP_SUMMARY"
echo "### Formatter (preview)" >> ecosystem-result
cat ecosystem-result-format-preview >> ecosystem-result
echo "" >> ecosystem-result
- name: Export pull request number
run: |
echo ${{ github.event.number }} > pr-number
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
name: Upload PR Number
with:
name: pr-number
path: pr-number
# NOTE: astral-sh-bot uses this artifact to post comments on PRs.
# Make sure to update the bot if you rename the artifact.
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
name: Upload Results
with:
@@ -640,49 +627,52 @@ jobs:
fuzz-ty:
name: "Fuzz for new ty panics"
runs-on: depot-ubuntu-22.04-16
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
needs:
- cargo-test-linux
- determine_changes
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && (needs.determine_changes.outputs.ty == 'true' || needs.determine_changes.outputs.py-fuzzer == 'true') }}
timeout-minutes: 20
timeout-minutes: ${{ github.repository == 'astral-sh/ruff' && 10 || 20 }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
name: Download new ty binary
id: ty-new
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
name: ty
path: target/debug
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download baseline ty binary
with:
name: ty
branch: ${{ github.event.pull_request.base.ref }}
workflow: "ci.yaml"
check_artifacts: true
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: Fuzz
env:
FORCE_COLOR: 1
NEW_TY: ${{ steps.ty-new.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x "${PWD}/ty" "${NEW_TY}/ty"
echo "new commit"
git rev-list --format=%s --max-count=1 "$GITHUB_SHA"
cargo build --profile=profiling --bin=ty
mv target/profiling/ty ty-new
MERGE_BASE="$(git merge-base "$GITHUB_SHA" "origin/$GITHUB_BASE_REF")"
git checkout -b old_commit "$MERGE_BASE"
echo "old commit (merge base)"
git rev-list --format=%s --max-count=1 old_commit
cargo build --profile=profiling --bin=ty
mv target/profiling/ty ty-old
(
uvx \
uv run \
--python="${PYTHON_VERSION}" \
--from=./python/py-fuzzer \
--project=./python/py-fuzzer \
--locked \
fuzz \
--test-executable="${NEW_TY}/ty" \
--baseline-executable="${PWD}/ty" \
--test-executable=ty-new \
--baseline-executable=ty-old \
--only-new-bugs \
--bin=ty \
0-500
0-1000
)
cargo-shear:
@@ -691,27 +681,51 @@ jobs:
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@79e4beb1e02f733a26129a6bf26c37dab4ab3307 # v1.14.4
- uses: cargo-bins/cargo-binstall@ae04fb5e853ae6cd3ad7de4a1d554a8b646d12aa # v1.15.11
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear
ty-completion-evaluation:
name: "ty completion evaluation"
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
needs: determine_changes
if: ${{ needs.determine_changes.outputs.ty == 'true' || github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Run ty completion evaluation"
run: cargo run --profile profiling --package ty_completion_eval -- all --threshold 0.4 --tasks /tmp/completion-evaluation-tasks.csv
- name: "Ensure there are no changes"
run: diff ./crates/ty_completion_eval/completion-evaluation-tasks.csv /tmp/completion-evaluation-tasks.csv
python-package:
name: "python package"
runs-on: ubuntu-latest
timeout-minutes: 20
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
@@ -728,19 +742,21 @@ jobs:
pre-commit:
name: "pre-commit"
runs-on: depot-ubuntu-22.04-16
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
- name: "Cache pre-commit"
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
@@ -762,13 +778,12 @@ jobs:
env:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
python-version: "3.13"
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
@@ -777,13 +792,16 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
python-version: 3.13
activate-environment: true
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: uv pip install -r docs/requirements-insiders.txt --system
run: uv pip install -r docs/requirements-insiders.txt
- name: "Install dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: uv pip install -r docs/requirements.txt --system
run: uv pip install -r docs/requirements.txt
- name: "Update README File"
run: python scripts/transform_readme.py --target mkdocs
- name: "Generate docs"
@@ -804,10 +822,12 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.formatter == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: "Install Rust toolchain"
run: rustup show
- name: "Run checks"
@@ -821,47 +841,54 @@ jobs:
name: "test ruff-lsp"
runs-on: ubuntu-latest
timeout-minutes: 5
needs:
- cargo-test-linux
- determine_changes
needs: determine_changes
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
steps:
- uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
name: "Download ruff-lsp source"
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
name: "Checkout ruff source"
with:
persist-credentials: false
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
shared-key: ruff-linux-debug
save-if: false
- name: "Install Rust toolchain"
run: rustup show
- name: Build Ruff binary
run: cargo build -p ruff --bin ruff
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
name: "Checkout ruff-lsp source"
with:
persist-credentials: false
repository: "astral-sh/ruff-lsp"
path: ruff-lsp
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
# installation fails on 3.13 and newer
python-version: "3.12"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
name: Download development ruff binary
id: ruff-target
with:
name: ruff
path: target/debug
- name: Install ruff-lsp dependencies
run: |
cd ruff-lsp
just install
- name: Run ruff-lsp tests
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Setup development binary
pip uninstall --yes ruff
chmod +x "${DOWNLOAD_PATH}/ruff"
export PATH="${DOWNLOAD_PATH}:${PATH}"
export PATH="${PWD}/target/debug:${PATH}"
ruff version
cd ruff-lsp
just test
check-playground:
@@ -872,13 +899,15 @@ jobs:
- determine_changes
if: ${{ (needs.determine_changes.outputs.playground == 'true') }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
cache: "npm"
@@ -897,66 +926,128 @@ jobs:
run: npm run fmt:check
working-directory: playground
benchmarks-instrumented:
benchmarks-instrumented-ruff:
name: "benchmarks instrumented (ruff)"
runs-on: ubuntu-24.04
needs: determine_changes
if: ${{ github.repository == 'astral-sh/ruff' && !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
if: |
github.repository == 'astral-sh/ruff' &&
(
github.ref == 'refs/heads/main' ||
needs.determine_changes.outputs.formatter == 'true' ||
needs.determine_changes.outputs.linter == 'true'
)
timeout-minutes: 20
steps:
- name: "Checkout Branch"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-codspeed
- name: "Build benchmarks"
run: cargo codspeed build --features "codspeed,instrumented" --no-default-features -p ruff_benchmark
run: cargo codspeed build --features "codspeed,instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench formatter --bench lexer --bench linter --bench parser
- name: "Run benchmarks"
uses: CodSpeedHQ/action@0b6e7a3d96c9d2a6057e7bcea6b45aaf2f7ce60b # v3.8.0
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
with:
mode: instrumentation
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
benchmarks-instrumented-ty:
name: "benchmarks instrumented (ty)"
runs-on: ubuntu-24.04
needs: determine_changes
if: |
github.repository == 'astral-sh/ruff' &&
(
github.ref == 'refs/heads/main' ||
needs.determine_changes.outputs.ty == 'true'
)
timeout-minutes: 20
steps:
- name: "Checkout Branch"
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-codspeed
- name: "Build benchmarks"
run: cargo codspeed build --features "codspeed,instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench ty
- name: "Run benchmarks"
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
with:
mode: instrumentation
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
benchmarks-walltime:
name: "benchmarks walltime (${{ matrix.benchmarks }})"
runs-on: codspeed-macro
needs: determine_changes
if: ${{ github.repository == 'astral-sh/ruff' && !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.ty == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
env:
TY_LOG: ruff_benchmark=debug
strategy:
matrix:
benchmarks:
- "medium|multithreaded"
- "small|large"
steps:
- name: "Checkout Branch"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-codspeed
- name: "Build benchmarks"
run: cargo codspeed build --features "codspeed,walltime" --no-default-features -p ruff_benchmark
run: cargo codspeed build --features "codspeed,walltime" --profile profiling --no-default-features -p ruff_benchmark
- name: "Run benchmarks"
uses: CodSpeedHQ/action@0b6e7a3d96c9d2a6057e7bcea6b45aaf2f7ce60b # v3.8.0
uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4
env:
# enabling walltime flamegraphs adds ~6 minutes to the CI time, and they don't
# appear to provide much useful insight for our walltime benchmarks right now
# (see https://github.com/astral-sh/ruff/pull/20419)
CODSPEED_PERF_ENABLED: false
with:
run: cargo codspeed run
mode: walltime
run: cargo codspeed run --bench ty_walltime "${{ matrix.benchmarks }}"
token: ${{ secrets.CODSPEED_TOKEN }}

View File

@@ -31,15 +31,15 @@ jobs:
# Don't run the cron job on forks:
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@7344740a9418dcdcb481c7df83d9fbd1d5072d7d # v1
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- name: Build ruff
# A debug build means the script runs slower once it gets started,
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
@@ -48,9 +48,10 @@ jobs:
run: |
# shellcheck disable=SC2046
(
uvx \
--python=3.12 \
--from=./python/py-fuzzer \
uv run \
--python=3.14 \
--project=./python/py-fuzzer \
--locked \
fuzz \
--test-executable=target/debug/ruff \
--bin=ruff \
@@ -65,7 +66,7 @@ jobs:
permissions:
issues: write
steps:
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

View File

@@ -19,6 +19,10 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
defaults:
run:
shell: bash
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
@@ -29,19 +33,19 @@ env:
jobs:
mypy_primer:
name: Run mypy_primer
runs-on: depot-ubuntu-22.04-32
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
@@ -49,42 +53,37 @@ jobs:
run: rustup show
- name: Run mypy_primer
shell: bash
env:
PRIMER_SELECTOR: crates/ty_python_semantic/resources/primer/good.txt
CLICOLOR_FORCE: "1"
DIFF_FILE: mypy_primer.diff
run: |
cd ruff
scripts/mypy_primer.sh
echo ${{ github.event.number }} > ../pr-number
# NOTE: astral-sh-bot uses this artifact to post comments on PRs.
# Make sure to update the bot if you rename the artifact.
- name: Upload diff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: mypy_primer_diff
path: mypy_primer.diff
- name: Upload pr-number
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pr-number
path: pr-number
memory_usage:
name: Run memory statistics
runs-on: depot-ubuntu-22.04-32
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
@@ -92,7 +91,6 @@ jobs:
run: rustup show
- name: Run mypy_primer
shell: bash
env:
TY_MAX_PARALLELISM: 1 # for deterministic memory numbers
TY_MEMORY_REPORT: mypy_primer

View File

@@ -1,122 +0,0 @@
name: PR comment (mypy_primer)
on: # zizmor: ignore[dangerous-triggers]
workflow_run:
workflows: [Run mypy_primer]
types: [completed]
workflow_dispatch:
inputs:
workflow_run_id:
description: The mypy_primer workflow that triggers the workflow run
required: true
jobs:
comment:
runs-on: ubuntu-24.04
permissions:
pull-requests: write
steps:
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download PR number
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- name: Parse pull request number
id: pr-number
run: |
if [[ -f pr-number ]]
then
echo "pr-number=$(<pr-number)" >> "$GITHUB_OUTPUT"
fi
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: "Download mypy_primer results"
id: download-mypy_primer_diff
if: steps.pr-number.outputs.pr-number
with:
name: mypy_primer_diff
workflow: mypy_primer.yaml
pr: ${{ steps.pr-number.outputs.pr-number }}
path: pr/mypy_primer_diff
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: "Download mypy_primer memory results"
id: download-mypy_primer_memory_diff
if: steps.pr-number.outputs.pr-number
with:
name: mypy_primer_memory_diff
workflow: mypy_primer.yaml
pr: ${{ steps.pr-number.outputs.pr-number }}
path: pr/mypy_primer_memory_diff
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- name: Generate comment content
id: generate-comment
if: ${{ steps.download-mypy_primer_diff.outputs.found_artifact == 'true' && steps.download-mypy_primer_memory_diff.outputs.found_artifact == 'true' }}
run: |
# Guard against malicious mypy_primer results that symlink to a secret
# file on this runner
if [[ -L pr/mypy_primer_diff/mypy_primer.diff ]] || [[ -L pr/mypy_primer_memory_diff/mypy_primer_memory.diff ]]
then
echo "Error: mypy_primer.diff and mypy_primer_memory.diff cannot be a symlink"
exit 1
fi
# Note this identifier is used to find the comment to update on
# subsequent runs
echo '<!-- generated-comment mypy_primer -->' >> comment.txt
echo '## `mypy_primer` results' >> comment.txt
if [ -s "pr/mypy_primer_diff/mypy_primer.diff" ]; then
echo '<details>' >> comment.txt
echo '<summary>Changes were detected when running on open source projects</summary>' >> comment.txt
echo '' >> comment.txt
echo '```diff' >> comment.txt
cat pr/mypy_primer_diff/mypy_primer.diff >> comment.txt
echo '```' >> comment.txt
echo '</details>' >> comment.txt
else
echo 'No ecosystem changes detected ✅' >> comment.txt
fi
if [ -s "pr/mypy_primer_memory_diff/mypy_primer_memory.diff" ]; then
echo '<details>' >> comment.txt
echo '<summary>Memory usage changes were detected when running on open source projects</summary>' >> comment.txt
echo '' >> comment.txt
echo '```diff' >> comment.txt
cat pr/mypy_primer_memory_diff/mypy_primer_memory.diff >> comment.txt
echo '```' >> comment.txt
echo '</details>' >> comment.txt
else
echo 'No memory usage changes detected ✅' >> comment.txt
fi
echo 'comment<<EOF' >> "$GITHUB_OUTPUT"
cat comment.txt >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: Find existing comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
if: steps.generate-comment.outcome == 'success'
id: find-comment
with:
issue-number: ${{ steps.pr-number.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "<!-- generated-comment mypy_primer -->"
- name: Create or update comment
if: steps.find-comment.outcome == 'success'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-number.outputs.pr-number }}
body-path: comment.txt
edit-mode: replace

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Update pre-commit mirror"
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.RUFF_PRE_COMMIT_PAT }}
script: |

View File

@@ -1,88 +0,0 @@
name: Ecosystem check comment
on:
workflow_run:
workflows: [CI]
types: [completed]
workflow_dispatch:
inputs:
workflow_run_id:
description: The ecosystem workflow that triggers the workflow run
required: true
jobs:
comment:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download pull request number
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- name: Parse pull request number
id: pr-number
run: |
if [[ -f pr-number ]]
then
echo "pr-number=$(<pr-number)" >> "$GITHUB_OUTPUT"
fi
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: "Download ecosystem results"
id: download-ecosystem-result
if: steps.pr-number.outputs.pr-number
with:
name: ecosystem-result
workflow: ci.yaml
pr: ${{ steps.pr-number.outputs.pr-number }}
path: pr/ecosystem
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- name: Generate comment content
id: generate-comment
if: steps.download-ecosystem-result.outputs.found_artifact == 'true'
run: |
# Guard against malicious ecosystem results that symlink to a secret
# file on this runner
if [[ -L pr/ecosystem/ecosystem-result ]]
then
echo "Error: ecosystem-result cannot be a symlink"
exit 1
fi
# Note this identifier is used to find the comment to update on
# subsequent runs
echo '<!-- generated-comment ecosystem -->' >> comment.txt
echo '## `ruff-ecosystem` results' >> comment.txt
cat pr/ecosystem/ecosystem-result >> comment.txt
echo "" >> comment.txt
echo 'comment<<EOF' >> "$GITHUB_OUTPUT"
cat comment.txt >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: Find existing comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
if: steps.generate-comment.outcome == 'success'
id: find-comment
with:
issue-number: ${{ steps.pr-number.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "<!-- generated-comment ecosystem -->"
- name: Create or update comment
if: steps.find-comment.outcome == 'success'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-number.outputs.pr-number }}
body-path: comment.txt
edit-mode: replace

View File

@@ -23,12 +23,12 @@ jobs:
env:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ inputs.ref }}
persist-credentials: true
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: 3.12
@@ -68,7 +68,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}

View File

@@ -18,22 +18,23 @@ env:
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
cache: "npm"
cache-dependency-path: playground/package-lock.json
package-manager-cache: false
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
- name: "Install Node dependencies"
run: npm ci

View File

@@ -22,8 +22,8 @@ jobs:
id-token: write
steps:
- name: "Install uv"
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
pattern: wheels-*
path: wheels

View File

@@ -30,14 +30,15 @@ jobs:
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
package-manager-cache: false
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
- name: "Install Node dependencies"
run: npm ci

View File

@@ -29,7 +29,7 @@ jobs:
target: [web, bundler, nodejs]
fail-fast: false
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: "Install Rust toolchain"
@@ -45,9 +45,9 @@ jobs:
jq '.name="@astral-sh/ruff-wasm-${{ matrix.target }}"' crates/ruff_wasm/pkg/package.json > /tmp/package.json
mv /tmp/package.json crates/ruff_wasm/pkg
- run: cp LICENSE crates/ruff_wasm/pkg # wasm-pack does not put the LICENSE file in the pkg
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 20
node-version: 22
registry-url: "https://registry.npmjs.org"
- name: "Publish (dry-run)"
if: ${{ inputs.plan == '' || fromJson(inputs.plan).announcement_tag_is_implicit }}

View File

@@ -1,7 +1,6 @@
# This file was autogenerated by dist: https://github.com/astral-sh/cargo-dist
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
#
# Copyright 2022-2024, axodotdev
# Copyright 2025 Astral Software Inc.
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
@@ -61,7 +60,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
with:
persist-credentials: false
submodules: recursive
@@ -69,9 +68,9 @@ jobs:
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5-prerelease.1/cargo-dist-installer.sh | sh"
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.2/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
@@ -87,7 +86,7 @@ jobs:
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
@@ -124,19 +123,19 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
pattern: artifacts-*
path: target/distrib/
@@ -154,7 +153,7 @@ jobs:
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
with:
name: artifacts-build-global
path: |
@@ -167,27 +166,27 @@ jobs:
- custom-build-binaries
- custom-build-docker
- build-global-artifacts
# Only run if we're "publishing", and only if local and global didn't fail (skipped is fine)
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }}
# Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine)
if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: "depot-ubuntu-latest-4"
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
pattern: artifacts-*
path: target/distrib/
@@ -201,7 +200,7 @@ jobs:
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
@@ -251,13 +250,13 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
with:
persist-credentials: false
submodules: recursive
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
pattern: artifacts-*
path: artifacts

View File

@@ -16,8 +16,10 @@ name: Sync typeshed
# 3. Once the Windows worker is done, a MacOS worker:
# a. Checks out the branch created by the Linux worker
# b. Syncs all docstrings available on MacOS that are not available on Linux or Windows
# c. Commits the changes and pushes them to the same upstream branch
# d. Creates a PR against the `main` branch using the branch all three workers have pushed to
# c. Attempts to update any snapshots that might have changed
# (this sub-step is allowed to fail)
# d. Commits the changes and pushes them to the same upstream branch
# e. Creates a PR against the `main` branch using the branch all three workers have pushed to
# 4. If any of steps 1-3 failed, an issue is created in the `astral-sh/ruff` repository
on:
@@ -26,8 +28,18 @@ on:
# Run on the 1st and the 15th of every month:
- cron: "0 0 1,15 * *"
defaults:
run:
shell: bash
env:
FORCE_COLOR: 1
# Don't set this flag globally for the workflow: it does strange things
# to the snapshots in the `cargo insta test --accept` step in the MacOS job.
#
# FORCE_COLOR: 1
CARGO_TERM_COLOR: always
NEXTEST_PROFILE: "ci"
GH_TOKEN: ${{ github.token }}
# The name of the upstream branch that the first worker creates,
@@ -50,12 +62,12 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
name: Checkout Ruff
with:
path: ruff
persist-credentials: true
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
name: Checkout typeshed
with:
repository: python/typeshed
@@ -65,7 +77,7 @@ jobs:
run: |
git config --global user.name typeshedbot
git config --global user.email '<>'
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: Sync typeshed stubs
run: |
rm -rf "ruff/${VENDORED_TYPESHED}"
@@ -86,6 +98,8 @@ jobs:
git commit -m "Sync typeshed. Source commit: https://github.com/python/typeshed/commit/$(git -C ../typeshed rev-parse HEAD)" --allow-empty
- name: Sync Linux docstrings
if: ${{ success() }}
env:
FORCE_COLOR: 1
run: |
cd ruff
./scripts/codemod_docstrings.sh
@@ -112,19 +126,20 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
name: Checkout Ruff
with:
persist-credentials: true
ref: ${{ env.UPSTREAM_BRANCH}}
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: Setup git
run: |
git config --global user.name typeshedbot
git config --global user.email '<>'
- name: Sync Windows docstrings
id: docstrings
shell: bash
env:
FORCE_COLOR: 1
run: ./scripts/codemod_docstrings.sh
- name: Commit the changes
if: ${{ steps.docstrings.outcome == 'success' }}
@@ -150,37 +165,79 @@ jobs:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
name: Checkout Ruff
with:
persist-credentials: true
ref: ${{ env.UPSTREAM_BRANCH}}
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
- uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
- name: Setup git
run: |
git config --global user.name typeshedbot
git config --global user.email '<>'
- name: Sync macOS docstrings
run: ./scripts/codemod_docstrings.sh
- name: Commit and push the changes
if: ${{ success() }}
env:
FORCE_COLOR: 1
run: |
./scripts/codemod_docstrings.sh
git commit -am "Sync macOS docstrings" --allow-empty
- name: Format the changes
if: ${{ success() }}
env:
FORCE_COLOR: 1
run: |
# Here we just reformat the codemodded stubs so that they are
# consistent with the other typeshed stubs around them.
# Typeshed formats code using black in their CI, so we just invoke
# black on the stubs the same way that typeshed does.
uvx black "${VENDORED_TYPESHED}/stdlib" --config "${VENDORED_TYPESHED}/pyproject.toml" || true
git commit -am "Format codemodded docstrings" --allow-empty
rm "${VENDORED_TYPESHED}/pyproject.toml"
git commit -am "Remove pyproject.toml file"
git push
- name: Create a PR
- name: Remove typeshed pyproject.toml file
if: ${{ success() }}
run: |
rm "${VENDORED_TYPESHED}/pyproject.toml"
git commit -am "Remove pyproject.toml file"
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- name: "Install Rust toolchain"
if: ${{ success() }}
run: rustup show
- name: "Install mold"
if: ${{ success() }}
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Install cargo nextest"
if: ${{ success() }}
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-nextest
- name: "Install cargo insta"
if: ${{ success() }}
uses: taiki-e/install-action@537c30d2b45cc3aa3fb35e2bbcfb61ef93fd6f02 # v2.62.52
with:
tool: cargo-insta
- name: Update snapshots
if: ${{ success() }}
run: |
cargo r \
--profile=profiling \
-p ty_completion_eval \
-- all --tasks ./crates/ty_completion_eval/completion-evaluation-tasks.csv
# The `cargo insta` docs indicate that `--unreferenced=delete` might be a good option,
# but from local testing it appears to just revert all changes made by `cargo insta test --accept`.
#
# If there were only snapshot-related failures, `cargo insta test --accept` will have exit code 0,
# but if there were also other mdtest failures (for example), it will return a nonzero exit code.
# We don't care about other tests failing here, we just want snapshots updated where possible,
# so we use `|| true` here to ignore the exit code.
cargo insta test --accept --color=always --all-features --test-runner=nextest || true
- name: Commit snapshot changes
if: ${{ success() }}
run: git commit -am "Update snapshots" || echo "No snapshot changes to commit"
- name: Push changes upstream and create a PR
if: ${{ success() }}
run: |
git push
gh pr list --repo "${GITHUB_REPOSITORY}" --head "${UPSTREAM_BRANCH}" --json id --jq length | grep 1 && exit 0 # exit if there is existing pr
gh pr create --title "[ty] Sync vendored typeshed stubs" --body "Close and reopen this PR to trigger CI" --label "ty"
@@ -192,7 +249,7 @@ jobs:
permissions:
issues: write
steps:
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

View File

@@ -22,22 +22,25 @@ env:
jobs:
ty-ecosystem-analyzer:
name: Compute diagnostic diff
runs-on: depot-ubuntu-22.04-32
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
timeout-minutes: 20
if: contains(github.event.label.name, 'ecosystem-analyzer')
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- name: Install Rust toolchain
run: rustup show
@@ -64,11 +67,12 @@ jobs:
cd ..
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@27dd66d9e397d986ef9c631119ee09556eab8af9"
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@e26ebfb78d372b8b091e1cb1d6fc522e135474c1"
ecosystem-analyzer \
--repository ruff \
diff \
--profile=profiling \
--projects-old ruff/projects_old.txt \
--projects-new ruff/projects_new.txt \
--old old_commit \
@@ -94,14 +98,20 @@ jobs:
--new-name "$REF_NAME" \
--output diff-statistics.md
ecosystem-analyzer \
generate-timing-diff \
diagnostics-old.json \
diagnostics-new.json \
--old-name "main (merge base)" \
--new-name "$REF_NAME" \
--output-html dist/timing.html
echo '## `ecosystem-analyzer` results' > comment.md
echo >> comment.md
cat diff-statistics.md >> comment.md
cat diff-statistics.md >> "$GITHUB_STEP_SUMMARY"
echo ${{ github.event.number }} > pr-number
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
id: deploy
@@ -117,22 +127,24 @@ jobs:
DEPLOYMENT_URL: ${{ steps.deploy.outputs.pages-deployment-alias-url }}
run: |
echo >> comment.md
echo "**[Full report with detailed diff]($DEPLOYMENT_URL/diff)**" >> comment.md
echo "**[Full report with detailed diff]($DEPLOYMENT_URL/diff)** ([timing results]($DEPLOYMENT_URL/timing))" >> comment.md
# NOTE: astral-sh-bot uses this artifact to post comments on PRs.
# Make sure to update the bot if you rename the artifact.
- name: Upload comment
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: comment.md
path: comment.md
- name: Upload pr-number
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pr-number
path: pr-number
- name: Upload diagnostics diff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: diff.html
path: dist/diff.html
- name: Upload timing diff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: timing.html
path: dist/timing.html

View File

@@ -1,85 +0,0 @@
name: PR comment (ty ecosystem-analyzer)
on: # zizmor: ignore[dangerous-triggers]
workflow_run:
workflows: [ty ecosystem-analyzer]
types: [completed]
workflow_dispatch:
inputs:
workflow_run_id:
description: The ty ecosystem-analyzer workflow that triggers the workflow run
required: true
jobs:
comment:
runs-on: ubuntu-24.04
permissions:
pull-requests: write
steps:
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download PR number
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- name: Parse pull request number
id: pr-number
run: |
if [[ -f pr-number ]]
then
echo "pr-number=$(<pr-number)" >> "$GITHUB_OUTPUT"
fi
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: "Download comment.md"
id: download-comment
if: steps.pr-number.outputs.pr-number
with:
name: comment.md
workflow: ty-ecosystem-analyzer.yaml
pr: ${{ steps.pr-number.outputs.pr-number }}
path: pr/comment
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- name: Generate comment content
id: generate-comment
if: ${{ steps.download-comment.outputs.found_artifact == 'true' }}
run: |
# Guard against malicious ty ecosystem-analyzer results that symlink to a secret
# file on this runner
if [[ -L pr/comment/comment.md ]]
then
echo "Error: comment.md cannot be a symlink"
exit 1
fi
# Note: this identifier is used to find the comment to update on subsequent runs
echo '<!-- generated-comment ty ecosystem-analyzer -->' > comment.md
echo >> comment.md
cat pr/comment/comment.md >> comment.md
echo 'comment<<EOF' >> "$GITHUB_OUTPUT"
cat comment.md >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: Find existing comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
if: steps.generate-comment.outcome == 'success'
id: find-comment
with:
issue-number: ${{ steps.pr-number.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "<!-- generated-comment ty ecosystem-analyzer -->"
- name: Create or update comment
if: steps.find-comment.outcome == 'success'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-number.outputs.pr-number }}
body-path: comment.md
edit-mode: replace

View File

@@ -19,21 +19,24 @@ env:
jobs:
ty-ecosystem-report:
name: Create ecosystem report
runs-on: depot-ubuntu-22.04-32
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3
with:
enable-cache: true # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
lookup-only: false # zizmor: ignore[cache-poisoning] acceptable risk for CloudFlare pages artifact
- name: Install Rust toolchain
run: rustup show
@@ -49,12 +52,13 @@ jobs:
cd ..
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@27dd66d9e397d986ef9c631119ee09556eab8af9"
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@e26ebfb78d372b8b091e1cb1d6fc522e135474c1"
ecosystem-analyzer \
--verbose \
--repository ruff \
analyze \
--profile=profiling \
--projects ruff/crates/ty_python_semantic/resources/primer/good.txt \
--output ecosystem-diagnostics.json
@@ -62,7 +66,7 @@ jobs:
ecosystem-analyzer \
generate-report \
--max-diagnostics-per-project=1200 \
--max-diagnostics-per-project=1000 \
ecosystem-diagnostics.json \
--output dist/index.html

View File

@@ -24,28 +24,28 @@ env:
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: 1
CONFORMANCE_SUITE_COMMIT: d4f39b27a4a47aac8b6d4019e1b0b5b3156fabdc
CONFORMANCE_SUITE_COMMIT: 9f6d8ced7cd1c8d92687a4e9c96d7716452e471e
jobs:
typing_conformance:
name: Compute diagnostic diff
runs-on: depot-ubuntu-22.04-32
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: python/typing
ref: ${{ env.CONFORMANCE_SUITE_COMMIT }}
path: typing
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
@@ -94,21 +94,18 @@ jobs:
touch typing_conformance_diagnostics.diff
fi
echo ${{ github.event.number }} > pr-number
echo "${CONFORMANCE_SUITE_COMMIT}" > conformance-suite-commit
# NOTE: astral-sh-bot uses this artifact to post comments on PRs.
# Make sure to update the bot if you rename the artifact.
- name: Upload diff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: typing_conformance_diagnostics_diff
path: typing_conformance_diagnostics.diff
- name: Upload pr-number
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pr-number
path: pr-number
# NOTE: astral-sh-bot uses this artifact to post comments on PRs.
# Make sure to update the bot if you rename the artifact.
- name: Upload conformance suite commit
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:

View File

@@ -1,112 +0,0 @@
name: PR comment (typing_conformance)
on: # zizmor: ignore[dangerous-triggers]
workflow_run:
workflows: [Run typing conformance]
types: [completed]
workflow_dispatch:
inputs:
workflow_run_id:
description: The typing_conformance workflow that triggers the workflow run
required: true
jobs:
comment:
runs-on: ubuntu-24.04
permissions:
pull-requests: write
steps:
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download PR number
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- name: Parse pull request number
id: pr-number
run: |
if [[ -f pr-number ]]
then
echo "pr-number=$(<pr-number)" >> "$GITHUB_OUTPUT"
fi
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: Download typing conformance suite commit
with:
name: conformance-suite-commit
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
name: "Download typing_conformance results"
id: download-typing_conformance_diff
if: steps.pr-number.outputs.pr-number
with:
name: typing_conformance_diagnostics_diff
workflow: typing_conformance.yaml
pr: ${{ steps.pr-number.outputs.pr-number }}
path: pr/typing_conformance_diagnostics_diff
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- name: Generate comment content
id: generate-comment
if: ${{ steps.download-typing_conformance_diff.outputs.found_artifact == 'true' }}
run: |
# Guard against malicious typing_conformance results that symlink to a secret
# file on this runner
if [[ -L pr/typing_conformance_diagnostics_diff/typing_conformance_diagnostics.diff ]]
then
echo "Error: typing_conformance_diagnostics.diff cannot be a symlink"
exit 1
fi
# Note this identifier is used to find the comment to update on
# subsequent runs
echo '<!-- generated-comment typing_conformance_diagnostics_diff -->' >> comment.txt
if [[ -f conformance-suite-commit ]]
then
echo "## Diagnostic diff on [typing conformance tests](https://github.com/python/typing/tree/$(<conformance-suite-commit)/conformance)" >> comment.txt
else
echo "conformance-suite-commit file not found"
echo "## Diagnostic diff on typing conformance tests" >> comment.txt
fi
if [ -s "pr/typing_conformance_diagnostics_diff/typing_conformance_diagnostics.diff" ]; then
echo '<details>' >> comment.txt
echo '<summary>Changes were detected when running ty on typing conformance tests</summary>' >> comment.txt
echo '' >> comment.txt
echo '```diff' >> comment.txt
cat pr/typing_conformance_diagnostics_diff/typing_conformance_diagnostics.diff >> comment.txt
echo '```' >> comment.txt
echo '</details>' >> comment.txt
else
echo 'No changes detected when running ty on typing conformance tests ✅' >> comment.txt
fi
echo 'comment<<EOF' >> "$GITHUB_OUTPUT"
cat comment.txt >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: Find existing comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
if: steps.generate-comment.outcome == 'success'
id: find-comment
with:
issue-number: ${{ steps.pr-number.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "<!-- generated-comment typing_conformance_diagnostics_diff -->"
- name: Create or update comment
if: steps.find-comment.outcome == 'success'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-number.outputs.pr-number }}
body-path: comment.txt
edit-mode: replace

18
.github/zizmor.yml vendored
View File

@@ -1,21 +1,23 @@
# Configuration for the zizmor static analysis tool, run via pre-commit in CI
# https://woodruffw.github.io/zizmor/configuration/
# https://docs.zizmor.sh/configuration/
#
# TODO: can we remove the ignores here so that our workflows are more secure?
rules:
dangerous-triggers:
ignore:
- pr-comment.yaml
cache-poisoning:
ignore:
- build-docker.yml
- publish-playground.yml
- ty-ecosystem-analyzer.yaml
- ty-ecosystem-report.yaml
excessive-permissions:
# it's hard to test what the impact of removing these ignores would be
# without actually running the release workflow...
ignore:
- build-docker.yml
- publish-playground.yml
- publish-docs.yml
secrets-inherit:
# `cargo dist` makes extensive use of `secrets: inherit`,
# and we can't easily fix that until an upstream release changes that.
disable: true
template-injection:
ignore:
# like with `secrets-inherit`, `cargo dist` introduces some
# template injections. We've manually audited these usages for safety.
- release.yml

View File

@@ -16,7 +16,8 @@ exclude: |
crates/ruff_python_formatter/resources/.*|
crates/ruff_python_formatter/tests/snapshots/.*|
crates/ruff_python_resolver/resources/.*|
crates/ruff_python_resolver/tests/snapshots/.*
crates/ruff_python_resolver/tests/snapshots/.*|
crates/ty_completion_eval/truth/.*
)$
repos:
@@ -34,6 +35,7 @@ repos:
rev: 0.7.22
hooks:
- id: mdformat
language: python # means renovate will also update `additional_dependencies`
additional_dependencies:
- mdformat-mkdocs==4.0.0
- mdformat-footnote==0.1.1
@@ -57,6 +59,7 @@ repos:
rev: 1.19.1
hooks:
- id: blacken-docs
language: python # means renovate will also update `additional_dependencies`
args: ["--pyi", "--line-length", "130"]
files: '^crates/.*/resources/mdtest/.*\.md'
exclude: |
@@ -98,8 +101,8 @@ repos:
# zizmor detects security vulnerabilities in GitHub Actions workflows.
# Additional configuration for the tool is found in `.github/zizmor.yml`
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.11.0
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.16.0
hooks:
- id: zizmor

View File

@@ -3,4 +3,7 @@
"--all-features"
],
"rust-analyzer.check.command": "clippy",
}
"search.exclude": {
"**/*.snap": true
}
}

View File

@@ -1,5 +1,60 @@
# Breaking Changes
## 0.14.0
- **Default to Python 3.10**
Ruff now defaults to Python 3.10 instead of 3.9 if no explicit Python
version is configured using [`ruff.target-version`](https://docs.astral.sh/ruff/settings/#target-version)
or [`project.requires-python`](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#python-requires)
([#20725](https://github.com/astral-sh/ruff/pull/20725))
- **Default to Python 3.14 for syntax errors**
Ruff will default to the _latest_ supported Python version (3.14) when
checking for syntax errors without a Python version configured. The default
in all other cases, like applying lint rules, remains at the minimum
supported Python version (3.10).
## 0.13.0
- **Several rules can now add `from __future__ import annotations` automatically**
`TC001`, `TC002`, `TC003`, `RUF013`, and `UP037` now add `from __future__ import annotations` as part of their fixes when the
`lint.future-annotations` setting is enabled. This allows the rules to move
more imports into `TYPE_CHECKING` blocks (`TC001`, `TC002`, and `TC003`),
use PEP 604 union syntax on Python versions before 3.10 (`RUF013`), and
unquote more annotations (`UP037`).
- **Full module paths are now used to verify first-party modules**
Ruff now checks that the full path to a module exists on disk before
categorizing it as a first-party import. This change makes first-party
import detection more accurate, helping to avoid false positives on local
directories with the same name as a third-party dependency, for example. See
the [FAQ
section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) on import categorization for more details.
- **Deprecated rules must now be selected by exact rule code**
Ruff will no longer activate deprecated rules selected by their group name
or prefix. As noted below, the two remaining deprecated rules were also
removed in this release, so this won't affect any current rules, but it will
still affect any deprecations in the future.
- **The deprecated macOS configuration directory fallback has been removed**
Ruff will no longer look for a user-level configuration file at
`~/Library/Application Support/ruff/ruff.toml` on macOS. This feature was
deprecated in v0.5 in favor of using the [XDG
specification](https://specifications.freedesktop.org/basedir-spec/latest/)
(usually resolving to `~/.config/ruff/ruff.toml`), like on Linux. The
fallback and accompanying deprecation warning have now been removed.
- **[`pandas-df-variable-name`](https://docs.astral.sh/ruff/rules/pandas-df-variable-name) (`PD901`) has been removed**
- **[`non-pep604-isinstance`](https://docs.astral.sh/ruff/rules/non-pep604-isinstance) (`UP038`) has been removed**
## 0.12.0
- **Detection of more syntax errors**

View File

@@ -1,490 +1,352 @@
# Changelog
## 0.12.10
## 0.14.6
Released on 2025-11-21.
### Preview features
- \[`flake8-simplify`\] Implement fix for `maxsplit` without separator (`SIM905`) ([#19851](https://github.com/astral-sh/ruff/pull/19851))
- \[`flake8-use-pathlib`\] Add fixes for `PTH102` and `PTH103` ([#19514](https://github.com/astral-sh/ruff/pull/19514))
- \[`flake8-bandit`\] Support new PySNMP API paths (`S508`, `S509`) ([#21374](https://github.com/astral-sh/ruff/pull/21374))
### Bug fixes
- \[`isort`\] Handle multiple continuation lines after module docstring (`I002`) ([#19818](https://github.com/astral-sh/ruff/pull/19818))
- \[`pyupgrade`\] Avoid reporting `__future__` features as unnecessary when they are used (`UP010`) ([#19769](https://github.com/astral-sh/ruff/pull/19769))
- \[`pyupgrade`\] Handle nested `Optional`s (`UP045`) ([#19770](https://github.com/astral-sh/ruff/pull/19770))
- Adjust own-line comment placement between branches ([#21185](https://github.com/astral-sh/ruff/pull/21185))
- Avoid syntax error when formatting attribute expressions with outer parentheses, parenthesized value, and trailing comment on value ([#20418](https://github.com/astral-sh/ruff/pull/20418))
- Fix panic when formatting comments in unary expressions ([#21501](https://github.com/astral-sh/ruff/pull/21501))
- Respect `fmt: skip` for compound statements on a single line ([#20633](https://github.com/astral-sh/ruff/pull/20633))
- \[`refurb`\] Fix `FURB103` autofix ([#21454](https://github.com/astral-sh/ruff/pull/21454))
- \[`ruff`\] Fix false positive for complex conversion specifiers in `logging-eager-conversion` (`RUF065`) ([#21464](https://github.com/astral-sh/ruff/pull/21464))
### Rule changes
- \[`pycodestyle`\] Make `E731` fix unsafe instead of display-only for class assignments ([#19700](https://github.com/astral-sh/ruff/pull/19700))
- \[`pyflakes`\] Add secondary annotation showing previous definition (`F811`) ([#19900](https://github.com/astral-sh/ruff/pull/19900))
- \[`ruff`\] Avoid false positive on `ClassVar` reassignment (`RUF012`) ([#21478](https://github.com/astral-sh/ruff/pull/21478))
### CLI
- Render hyperlinks for lint errors ([#21514](https://github.com/astral-sh/ruff/pull/21514))
- Add a `ruff analyze` option to skip over imports in `TYPE_CHECKING` blocks ([#21472](https://github.com/astral-sh/ruff/pull/21472))
### Documentation
- Fix description of global config file discovery strategy ([#19188](https://github.com/astral-sh/ruff/pull/19188))
- Update outdated links to <https://typing.python.org/en/latest/source/stubs.html> ([#19992](https://github.com/astral-sh/ruff/pull/19992))
- \[`flake8-annotations`\] Remove unused import in example (`ANN401`) ([#20000](https://github.com/astral-sh/ruff/pull/20000))
- Limit `eglot-format` hook to eglot-managed Python buffers ([#21459](https://github.com/astral-sh/ruff/pull/21459))
- Mention `force-exclude` in "Configuration > Python file discovery" ([#21500](https://github.com/astral-sh/ruff/pull/21500))
## 0.12.9
### Contributors
- [@ntBre](https://github.com/ntBre)
- [@dylwil3](https://github.com/dylwil3)
- [@gauthsvenkat](https://github.com/gauthsvenkat)
- [@MichaReiser](https://github.com/MichaReiser)
- [@thamer](https://github.com/thamer)
- [@Ruchir28](https://github.com/Ruchir28)
- [@thejcannon](https://github.com/thejcannon)
- [@danparizher](https://github.com/danparizher)
- [@chirizxc](https://github.com/chirizxc)
## 0.14.5
Released on 2025-11-13.
### Preview features
- \[`airflow`\] Add check for `airflow.secrets.cache.SecretCache` (`AIR301`) ([#17707](https://github.com/astral-sh/ruff/pull/17707))
- \[`ruff`\] Offer a safe fix for multi-digit zeros (`RUF064`) ([#19847](https://github.com/astral-sh/ruff/pull/19847))
- \[`flake8-simplify`\] Apply `SIM113` when index variable is of type `int` ([#21395](https://github.com/astral-sh/ruff/pull/21395))
- \[`pydoclint`\] Fix false positive when Sphinx directives follow a "Raises" section (`DOC502`) ([#20535](https://github.com/astral-sh/ruff/pull/20535))
- \[`pydoclint`\] Support NumPy-style comma-separated parameters (`DOC102`) ([#20972](https://github.com/astral-sh/ruff/pull/20972))
- \[`refurb`\] Auto-fix annotated assignments (`FURB101`) ([#21278](https://github.com/astral-sh/ruff/pull/21278))
- \[`ruff`\] Ignore `str()` when not used for simple conversion (`RUF065`) ([#21330](https://github.com/astral-sh/ruff/pull/21330))
### Bug fixes
- \[`flake8-blind-except`\] Fix `BLE001` false-positive on `raise ... from None` ([#19755](https://github.com/astral-sh/ruff/pull/19755))
- \[`flake8-comprehensions`\] Fix false positive for `C420` with attribute, subscript, or slice assignment targets ([#19513](https://github.com/astral-sh/ruff/pull/19513))
- \[`flake8-simplify`\] Fix handling of U+001C..U+001F whitespace (`SIM905`) ([#19849](https://github.com/astral-sh/ruff/pull/19849))
- Fix syntax error false positive on alternative `match` patterns ([#21362](https://github.com/astral-sh/ruff/pull/21362))
- \[`flake8-simplify`\] Fix false positive for iterable initializers with generator arguments (`SIM222`) ([#21187](https://github.com/astral-sh/ruff/pull/21187))
- \[`pyupgrade`\] Fix false positive on relative imports from local `.builtins` module (`UP029`) ([#21309](https://github.com/astral-sh/ruff/pull/21309))
- \[`pyupgrade`\] Consistently set the deprecated tag (`UP035`) ([#21396](https://github.com/astral-sh/ruff/pull/21396))
### Rule changes
- \[`pylint`\] Use lowercase hex characters to match the formatter (`PLE2513`) ([#19808](https://github.com/astral-sh/ruff/pull/19808))
- \[`refurb`\] Detect empty f-strings (`FURB105`) ([#21348](https://github.com/astral-sh/ruff/pull/21348))
### CLI
- Add option to provide a reason to `--add-noqa` ([#21294](https://github.com/astral-sh/ruff/pull/21294))
- Add upstream linter URL to `ruff linter --output-format=json` ([#21316](https://github.com/astral-sh/ruff/pull/21316))
- Add color to `--help` ([#21337](https://github.com/astral-sh/ruff/pull/21337))
### Documentation
- Fix `lint.future-annotations` link ([#19876](https://github.com/astral-sh/ruff/pull/19876))
- Add a new "Opening a PR" section to the contribution guide ([#21298](https://github.com/astral-sh/ruff/pull/21298))
- Added the PyScripter IDE to the list of "Who is using Ruff?" ([#21402](https://github.com/astral-sh/ruff/pull/21402))
- Update PyCharm setup instructions ([#21409](https://github.com/astral-sh/ruff/pull/21409))
- \[`flake8-annotations`\] Add link to `allow-star-arg-any` option (`ANN401`) ([#21326](https://github.com/astral-sh/ruff/pull/21326))
### Other changes
- Build `riscv64` binaries for release ([#19819](https://github.com/astral-sh/ruff/pull/19819))
- \[`configuration`\] Improve error message when `line-length` exceeds `u16::MAX` ([#21329](https://github.com/astral-sh/ruff/pull/21329))
- Add rule code to error description in GitLab output ([#19896](https://github.com/astral-sh/ruff/pull/19896))
### Contributors
- Improve rendering of the `full` output format ([#19415](https://github.com/astral-sh/ruff/pull/19415))
- [@njhearp](https://github.com/njhearp)
- [@11happy](https://github.com/11happy)
- [@hugovk](https://github.com/hugovk)
- [@Gankra](https://github.com/Gankra)
- [@ntBre](https://github.com/ntBre)
- [@pyscripter](https://github.com/pyscripter)
- [@danparizher](https://github.com/danparizher)
- [@MichaReiser](https://github.com/MichaReiser)
- [@henryiii](https://github.com/henryiii)
- [@charliecloudberry](https://github.com/charliecloudberry)
Below is an example diff for [`F401`](https://docs.astral.sh/ruff/rules/unused-import/):
## 0.14.4
```diff
-unused.py:8:19: F401 [*] `pathlib` imported but unused
+F401 [*] `pathlib` imported but unused
+ --> unused.py:8:19
|
7 | # Unused, _not_ marked as required (due to the alias).
8 | import pathlib as non_alias
- | ^^^^^^^^^ F401
+ | ^^^^^^^^^
9 |
10 | # Unused, marked as required.
|
- = help: Remove unused import: `pathlib`
+help: Remove unused import: `pathlib`
```
For now, the primary difference is the movement of the filename, line number, and column information to a second line in the header. This new representation will allow us to make further additions to Ruff's diagnostics, such as adding sub-diagnostics and multiple annotations to the same snippet.
## 0.12.8
Released on 2025-11-06.
### Preview features
- \[`flake8-use-pathlib`\] Expand `PTH201` to check all `PurePath` subclasses ([#19440](https://github.com/astral-sh/ruff/pull/19440))
- [formatter] Allow newlines after function headers without docstrings ([#21110](https://github.com/astral-sh/ruff/pull/21110))
- [formatter] Avoid extra parentheses for long `match` patterns with `as` captures ([#21176](https://github.com/astral-sh/ruff/pull/21176))
- \[`refurb`\] Expand fix safety for keyword arguments and `Decimal`s (`FURB164`) ([#21259](https://github.com/astral-sh/ruff/pull/21259))
- \[`refurb`\] Preserve argument ordering in autofix (`FURB103`) ([#20790](https://github.com/astral-sh/ruff/pull/20790))
### Bug fixes
- \[`flake8-blind-except`\] Change `BLE001` to correctly parse exception tuples ([#19747](https://github.com/astral-sh/ruff/pull/19747))
- \[`flake8-errmsg`\] Exclude `typing.cast` from `EM101` ([#19656](https://github.com/astral-sh/ruff/pull/19656))
- \[`flake8-simplify`\] Fix raw string handling in `SIM905` for embedded quotes ([#19591](https://github.com/astral-sh/ruff/pull/19591))
- \[`flake8-import-conventions`\] Avoid false positives for NFKC-normalized `__debug__` import aliases in `ICN001` ([#19411](https://github.com/astral-sh/ruff/pull/19411))
- \[`isort`\] Fix syntax error after docstring ending with backslash (`I002`) ([#19505](https://github.com/astral-sh/ruff/pull/19505))
- \[`pylint`\] Mark `PLC0207` fixes as unsafe when `*args` unpacking is present ([#19679](https://github.com/astral-sh/ruff/pull/19679))
- \[`pyupgrade`\] Prevent infinite loop with `I002` (`UP010`, `UP035`) ([#19413](https://github.com/astral-sh/ruff/pull/19413))
- \[`ruff`\] Parenthesize generator expressions in f-strings (`RUF010`) ([#19434](https://github.com/astral-sh/ruff/pull/19434))
- [server] Fix missing diagnostics for notebooks ([#21156](https://github.com/astral-sh/ruff/pull/21156))
- \[`flake8-bugbear`\] Ignore non-NFKC attribute names in `B009` and `B010` ([#21131](https://github.com/astral-sh/ruff/pull/21131))
- \[`refurb`\] Fix false negative for underscores before sign in `Decimal` constructor (`FURB157`) ([#21190](https://github.com/astral-sh/ruff/pull/21190))
- \[`ruff`\] Fix false positives on starred arguments (`RUF057`) ([#21256](https://github.com/astral-sh/ruff/pull/21256))
### Rule changes
- \[`eradicate`\] Don't flag `pyrefly` pragmas as unused code (`ERA001`) ([#19731](https://github.com/astral-sh/ruff/pull/19731))
- \[`airflow`\] extend deprecated argument `concurrency` in `airflow..DAG` (`AIR301`) ([#21220](https://github.com/astral-sh/ruff/pull/21220))
### Documentation
- Replace "associative" with "commutative" in docs for `RUF036` ([#19706](https://github.com/astral-sh/ruff/pull/19706))
- Fix copy and line separator colors in dark mode ([#19630](https://github.com/astral-sh/ruff/pull/19630))
- Fix link to `typing` documentation ([#19648](https://github.com/astral-sh/ruff/pull/19648))
- \[`refurb`\] Make more examples error out-of-the-box ([#19695](https://github.com/astral-sh/ruff/pull/19695),[#19673](https://github.com/astral-sh/ruff/pull/19673),[#19672](https://github.com/astral-sh/ruff/pull/19672))
- Improve `extend` docs ([#21135](https://github.com/astral-sh/ruff/pull/21135))
- \[`flake8-comprehensions`\] Fix typo in `C416` documentation ([#21184](https://github.com/astral-sh/ruff/pull/21184))
- Revise Ruff setup instructions for Zed editor ([#20935](https://github.com/astral-sh/ruff/pull/20935))
### Other changes
- Include column numbers in GitLab output format ([#19708](https://github.com/astral-sh/ruff/pull/19708))
- Always expand tabs to four spaces in diagnostics ([#19618](https://github.com/astral-sh/ruff/pull/19618))
- Update pre-commit's `ruff` id ([#19654](https://github.com/astral-sh/ruff/pull/19654))
- Make `ruff analyze graph` work with jupyter notebooks ([#21161](https://github.com/astral-sh/ruff/pull/21161))
## 0.12.7
### Contributors
This is a follow-up release to 0.12.6. Because of an issue in the package metadata, 0.12.6 failed to publish fully to PyPI and has been yanked. Similarly, there is no GitHub release or Git tag for 0.12.6. The contents of the 0.12.7 release are identical to 0.12.6, except for the updated metadata.
- [@chirizxc](https://github.com/chirizxc)
- [@Lee-W](https://github.com/Lee-W)
- [@musicinmybrain](https://github.com/musicinmybrain)
- [@MichaReiser](https://github.com/MichaReiser)
- [@tjkuson](https://github.com/tjkuson)
- [@danparizher](https://github.com/danparizher)
- [@renovate](https://github.com/renovate)
- [@ntBre](https://github.com/ntBre)
- [@gauthsvenkat](https://github.com/gauthsvenkat)
- [@LoicRiegel](https://github.com/LoicRiegel)
## 0.12.6
## 0.14.3
Released on 2025-10-30.
### Preview features
- \[`flake8-commas`\] Add support for trailing comma checks in type parameter lists (`COM812`, `COM819`) ([#19390](https://github.com/astral-sh/ruff/pull/19390))
- \[`pylint`\] Implement auto-fix for `missing-maxsplit-arg` (`PLC0207`) ([#19387](https://github.com/astral-sh/ruff/pull/19387))
- \[`ruff`\] Offer fixes for `RUF039` in more cases ([#19065](https://github.com/astral-sh/ruff/pull/19065))
- Respect `--output-format` with `--watch` ([#21097](https://github.com/astral-sh/ruff/pull/21097))
- \[`pydoclint`\] Fix false positive on explicit exception re-raising (`DOC501`, `DOC502`) ([#21011](https://github.com/astral-sh/ruff/pull/21011))
- \[`pyflakes`\] Revert to stable behavior if imports for module lie in alternate branches for `F401` ([#20878](https://github.com/astral-sh/ruff/pull/20878))
- \[`pylint`\] Implement `stop-iteration-return` (`PLR1708`) ([#20733](https://github.com/astral-sh/ruff/pull/20733))
- \[`ruff`\] Add support for additional eager conversion patterns (`RUF065`) ([#20657](https://github.com/astral-sh/ruff/pull/20657))
### Bug fixes
- Support `.pyi` files in ruff analyze graph ([#19611](https://github.com/astral-sh/ruff/pull/19611))
- \[`flake8-pyi`\] Preserve inline comment in ellipsis removal (`PYI013`) ([#19399](https://github.com/astral-sh/ruff/pull/19399))
- \[`perflint`\] Ignore rule if target is `global` or `nonlocal` (`PERF401`) ([#19539](https://github.com/astral-sh/ruff/pull/19539))
- \[`pyupgrade`\] Fix `UP030` to avoid modifying double curly braces in format strings ([#19378](https://github.com/astral-sh/ruff/pull/19378))
- \[`refurb`\] Ignore decorated functions for `FURB118` ([#19339](https://github.com/astral-sh/ruff/pull/19339))
- \[`refurb`\] Mark `int` and `bool` cases for `Decimal.from_float` as safe fixes (`FURB164`) ([#19468](https://github.com/astral-sh/ruff/pull/19468))
- \[`ruff`\] Fix `RUF033` for named default expressions ([#19115](https://github.com/astral-sh/ruff/pull/19115))
- Fix finding keyword range for clause header after statement ending with semicolon ([#21067](https://github.com/astral-sh/ruff/pull/21067))
- Fix syntax error false positive on nested alternative patterns ([#21104](https://github.com/astral-sh/ruff/pull/21104))
- \[`ISC001`\] Fix panic when string literals are unclosed ([#21034](https://github.com/astral-sh/ruff/pull/21034))
- \[`flake8-django`\] Apply `DJ001` to annotated fields ([#20907](https://github.com/astral-sh/ruff/pull/20907))
- \[`flake8-pyi`\] Fix `PYI034` to not trigger on metaclasses (`PYI034`) ([#20881](https://github.com/astral-sh/ruff/pull/20881))
- \[`flake8-type-checking`\] Fix `TC003` false positive with `future-annotations` ([#21125](https://github.com/astral-sh/ruff/pull/21125))
- \[`pyflakes`\] Fix false positive for `__class__` in lambda expressions within class definitions (`F821`) ([#20564](https://github.com/astral-sh/ruff/pull/20564))
- \[`pyupgrade`\] Fix false positive for `TypeVar` with default on Python \<3.13 (`UP046`,`UP047`) ([#21045](https://github.com/astral-sh/ruff/pull/21045))
### Rule changes
- \[`flake8-blind-except`\] Change `BLE001` to permit `logging.critical(..., exc_info=True)` ([#19520](https://github.com/astral-sh/ruff/pull/19520))
### Performance
- Add support for specifying minimum dots in detected string imports ([#19538](https://github.com/astral-sh/ruff/pull/19538))
## 0.12.5
### Preview features
- \[`flake8-use-pathlib`\] Add autofix for `PTH101`, `PTH104`, `PTH105`, `PTH121` ([#19404](https://github.com/astral-sh/ruff/pull/19404))
- \[`ruff`\] Support byte strings (`RUF055`) ([#18926](https://github.com/astral-sh/ruff/pull/18926))
### Bug fixes
- Fix `unreachable` panic in parser ([#19183](https://github.com/astral-sh/ruff/pull/19183))
- \[`flake8-pyi`\] Skip fix if all `Union` members are `None` (`PYI016`) ([#19416](https://github.com/astral-sh/ruff/pull/19416))
- \[`perflint`\] Parenthesize generator expressions (`PERF401`) ([#19325](https://github.com/astral-sh/ruff/pull/19325))
- \[`pylint`\] Handle empty comments after line continuation (`PLR2044`) ([#19405](https://github.com/astral-sh/ruff/pull/19405))
### Rule changes
- \[`pep8-naming`\] Fix `N802` false positives for `CGIHTTPRequestHandler` and `SimpleHTTPRequestHandler` ([#19432](https://github.com/astral-sh/ruff/pull/19432))
## 0.12.4
### Preview features
- \[`flake8-type-checking`, `pyupgrade`, `ruff`\] Add `from __future__ import annotations` when it would allow new fixes (`TC001`, `TC002`, `TC003`, `UP037`, `RUF013`) ([#19100](https://github.com/astral-sh/ruff/pull/19100))
- \[`flake8-use-pathlib`\] Add autofix for `PTH109` ([#19245](https://github.com/astral-sh/ruff/pull/19245))
- \[`pylint`\] Detect indirect `pathlib.Path` usages for `unspecified-encoding` (`PLW1514`) ([#19304](https://github.com/astral-sh/ruff/pull/19304))
### Bug fixes
- \[`flake8-bugbear`\] Fix `B017` false negatives for keyword exception arguments ([#19217](https://github.com/astral-sh/ruff/pull/19217))
- \[`flake8-use-pathlib`\] Fix false negative on direct `Path()` instantiation (`PTH210`) ([#19388](https://github.com/astral-sh/ruff/pull/19388))
- \[`flake8-django`\] Fix `DJ008` false positive for abstract models with type-annotated `abstract` field ([#19221](https://github.com/astral-sh/ruff/pull/19221))
- \[`isort`\] Fix `I002` import insertion after docstring with multiple string statements ([#19222](https://github.com/astral-sh/ruff/pull/19222))
- \[`isort`\] Treat form feed as valid whitespace before a semicolon ([#19343](https://github.com/astral-sh/ruff/pull/19343))
- \[`pydoclint`\] Fix `SyntaxError` from fixes with line continuations (`D201`, `D202`) ([#19246](https://github.com/astral-sh/ruff/pull/19246))
- \[`refurb`\] `FURB164` fix should validate arguments and should usually be marked unsafe ([#19136](https://github.com/astral-sh/ruff/pull/19136))
### Rule changes
- \[`flake8-use-pathlib`\] Skip single dots for `invalid-pathlib-with-suffix` (`PTH210`) on versions >= 3.14 ([#19331](https://github.com/astral-sh/ruff/pull/19331))
- \[`pep8_naming`\] Avoid false positives on standard library functions with uppercase names (`N802`) ([#18907](https://github.com/astral-sh/ruff/pull/18907))
- \[`pycodestyle`\] Handle brace escapes for t-strings in logical lines ([#19358](https://github.com/astral-sh/ruff/pull/19358))
- \[`pylint`\] Extend invalid string character rules to include t-strings ([#19355](https://github.com/astral-sh/ruff/pull/19355))
- \[`ruff`\] Allow `strict` kwarg when checking for `starmap-zip` (`RUF058`) in Python 3.14+ ([#19333](https://github.com/astral-sh/ruff/pull/19333))
### Documentation
- \[`flake8-type-checking`\] Make `TC010` docs example more realistic ([#19356](https://github.com/astral-sh/ruff/pull/19356))
- Make more documentation examples error out-of-the-box ([#19288](https://github.com/astral-sh/ruff/pull/19288),[#19272](https://github.com/astral-sh/ruff/pull/19272),[#19291](https://github.com/astral-sh/ruff/pull/19291),[#19296](https://github.com/astral-sh/ruff/pull/19296),[#19292](https://github.com/astral-sh/ruff/pull/19292),[#19295](https://github.com/astral-sh/ruff/pull/19295),[#19297](https://github.com/astral-sh/ruff/pull/19297),[#19309](https://github.com/astral-sh/ruff/pull/19309))
## 0.12.3
### Preview features
- \[`flake8-bugbear`\] Support non-context-manager calls in `B017` ([#19063](https://github.com/astral-sh/ruff/pull/19063))
- \[`flake8-use-pathlib`\] Add autofixes for `PTH100`, `PTH106`, `PTH107`, `PTH108`, `PTH110`, `PTH111`, `PTH112`, `PTH113`, `PTH114`, `PTH115`, `PTH117`, `PTH119`, `PTH120` ([#19213](https://github.com/astral-sh/ruff/pull/19213))
- \[`flake8-use-pathlib`\] Add autofixes for `PTH203`, `PTH204`, `PTH205` ([#18922](https://github.com/astral-sh/ruff/pull/18922))
### Bug fixes
- \[`flake8-return`\] Fix false-positive for variables used inside nested functions in `RET504` ([#18433](https://github.com/astral-sh/ruff/pull/18433))
- Treat form feed as valid whitespace before a line continuation ([#19220](https://github.com/astral-sh/ruff/pull/19220))
- \[`flake8-type-checking`\] Fix syntax error introduced by fix (`TC008`) ([#19150](https://github.com/astral-sh/ruff/pull/19150))
- \[`pyupgrade`\] Keyword arguments in `super` should suppress the `UP008` fix ([#19131](https://github.com/astral-sh/ruff/pull/19131))
### Documentation
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI007`, `PYI008`) ([#19103](https://github.com/astral-sh/ruff/pull/19103))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM116`) ([#19111](https://github.com/astral-sh/ruff/pull/19111))
- \[`flake8-type-checking`\] Make example error out-of-the-box (`TC001`) ([#19151](https://github.com/astral-sh/ruff/pull/19151))
- \[`flake8-use-pathlib`\] Make example error out-of-the-box (`PTH210`) ([#19189](https://github.com/astral-sh/ruff/pull/19189))
- \[`pycodestyle`\] Make example error out-of-the-box (`E272`) ([#19191](https://github.com/astral-sh/ruff/pull/19191))
- \[`pycodestyle`\] Make example not raise unnecessary `SyntaxError` (`E114`) ([#19190](https://github.com/astral-sh/ruff/pull/19190))
- \[`pydoclint`\] Make example error out-of-the-box (`DOC501`) ([#19218](https://github.com/astral-sh/ruff/pull/19218))
- \[`pylint`, `pyupgrade`\] Fix syntax errors in examples (`PLW1501`, `UP028`) ([#19127](https://github.com/astral-sh/ruff/pull/19127))
- \[`pylint`\] Update `missing-maxsplit-arg` docs and error to suggest proper usage (`PLC0207`) ([#18949](https://github.com/astral-sh/ruff/pull/18949))
- \[`flake8-bandit`\] Make example error out-of-the-box (`S412`) ([#19241](https://github.com/astral-sh/ruff/pull/19241))
## 0.12.2
### Preview features
- \[`flake8-pyi`\] Expand `Optional[A]` to `A | None` (`PYI016`) ([#18572](https://github.com/astral-sh/ruff/pull/18572))
- \[`pyupgrade`\] Mark `UP008` fix safe if no comments are in range ([#18683](https://github.com/astral-sh/ruff/pull/18683))
### Bug fixes
- \[`flake8-comprehensions`\] Fix `C420` to prepend whitespace when needed ([#18616](https://github.com/astral-sh/ruff/pull/18616))
- \[`perflint`\] Fix `PERF403` panic on attribute or subscription loop variable ([#19042](https://github.com/astral-sh/ruff/pull/19042))
- \[`pydocstyle`\] Fix `D413` infinite loop for parenthesized docstring ([#18930](https://github.com/astral-sh/ruff/pull/18930))
- \[`pylint`\] Fix `PLW0108` autofix introducing a syntax error when the lambda's body contains an assignment expression ([#18678](https://github.com/astral-sh/ruff/pull/18678))
- \[`refurb`\] Fix false positive on empty tuples (`FURB168`) ([#19058](https://github.com/astral-sh/ruff/pull/19058))
- \[`ruff`\] Allow more `field` calls from `attrs` (`RUF009`) ([#19021](https://github.com/astral-sh/ruff/pull/19021))
- \[`ruff`\] Fix syntax error introduced for an empty string followed by a u-prefixed string (`UP025`) ([#18899](https://github.com/astral-sh/ruff/pull/18899))
### Rule changes
- \[`flake8-executable`\] Allow `uvx` in shebang line (`EXE003`) ([#18967](https://github.com/astral-sh/ruff/pull/18967))
- \[`pandas`\] Avoid flagging `PD002` if `pandas` is not imported ([#18963](https://github.com/astral-sh/ruff/pull/18963))
- \[`pyupgrade`\] Avoid PEP-604 unions with `typing.NamedTuple` (`UP007`, `UP045`) ([#18682](https://github.com/astral-sh/ruff/pull/18682))
### Documentation
- Document link between `import-outside-top-level (PLC0415)` and `lint.flake8-tidy-imports.banned-module-level-imports` ([#18733](https://github.com/astral-sh/ruff/pull/18733))
- Fix description of the `format.skip-magic-trailing-comma` example ([#19095](https://github.com/astral-sh/ruff/pull/19095))
- \[`airflow`\] Make `AIR302` example error out-of-the-box ([#18988](https://github.com/astral-sh/ruff/pull/18988))
- \[`airflow`\] Make `AIR312` example error out-of-the-box ([#18989](https://github.com/astral-sh/ruff/pull/18989))
- \[`flake8-annotations`\] Make `ANN401` example error out-of-the-box ([#18974](https://github.com/astral-sh/ruff/pull/18974))
- \[`flake8-async`\] Make `ASYNC100` example error out-of-the-box ([#18993](https://github.com/astral-sh/ruff/pull/18993))
- \[`flake8-async`\] Make `ASYNC105` example error out-of-the-box ([#19002](https://github.com/astral-sh/ruff/pull/19002))
- \[`flake8-async`\] Make `ASYNC110` example error out-of-the-box ([#18975](https://github.com/astral-sh/ruff/pull/18975))
- \[`flake8-async`\] Make `ASYNC210` example error out-of-the-box ([#18977](https://github.com/astral-sh/ruff/pull/18977))
- \[`flake8-async`\] Make `ASYNC220`, `ASYNC221`, and `ASYNC222` examples error out-of-the-box ([#18978](https://github.com/astral-sh/ruff/pull/18978))
- \[`flake8-async`\] Make `ASYNC251` example error out-of-the-box ([#18990](https://github.com/astral-sh/ruff/pull/18990))
- \[`flake8-bandit`\] Make `S201` example error out-of-the-box ([#19017](https://github.com/astral-sh/ruff/pull/19017))
- \[`flake8-bandit`\] Make `S604` and `S609` examples error out-of-the-box ([#19049](https://github.com/astral-sh/ruff/pull/19049))
- \[`flake8-bugbear`\] Make `B028` example error out-of-the-box ([#19054](https://github.com/astral-sh/ruff/pull/19054))
- \[`flake8-bugbear`\] Make `B911` example error out-of-the-box ([#19051](https://github.com/astral-sh/ruff/pull/19051))
- \[`flake8-datetimez`\] Make `DTZ011` example error out-of-the-box ([#19055](https://github.com/astral-sh/ruff/pull/19055))
- \[`flake8-datetimez`\] Make `DTZ901` example error out-of-the-box ([#19056](https://github.com/astral-sh/ruff/pull/19056))
- \[`flake8-pyi`\] Make `PYI032` example error out-of-the-box ([#19061](https://github.com/astral-sh/ruff/pull/19061))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI014`, `PYI015`) ([#19097](https://github.com/astral-sh/ruff/pull/19097))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI042`) ([#19101](https://github.com/astral-sh/ruff/pull/19101))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI059`) ([#19080](https://github.com/astral-sh/ruff/pull/19080))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI062`) ([#19079](https://github.com/astral-sh/ruff/pull/19079))
- \[`flake8-pytest-style`\] Make example error out-of-the-box (`PT023`) ([#19104](https://github.com/astral-sh/ruff/pull/19104))
- \[`flake8-pytest-style`\] Make example error out-of-the-box (`PT030`) ([#19105](https://github.com/astral-sh/ruff/pull/19105))
- \[`flake8-quotes`\] Make example error out-of-the-box (`Q003`) ([#19106](https://github.com/astral-sh/ruff/pull/19106))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM110`) ([#19113](https://github.com/astral-sh/ruff/pull/19113))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM113`) ([#19109](https://github.com/astral-sh/ruff/pull/19109))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM401`) ([#19110](https://github.com/astral-sh/ruff/pull/19110))
- \[`pyflakes`\] Fix backslash in docs (`F621`) ([#19098](https://github.com/astral-sh/ruff/pull/19098))
- \[`pylint`\] Fix `PLC0415` example ([#18970](https://github.com/astral-sh/ruff/pull/18970))
## 0.12.1
### Preview features
- \[`flake8-errmsg`\] Extend `EM101` to support byte strings ([#18867](https://github.com/astral-sh/ruff/pull/18867))
- \[`flake8-use-pathlib`\] Add autofix for `PTH202` ([#18763](https://github.com/astral-sh/ruff/pull/18763))
- \[`pygrep-hooks`\] Add `AsyncMock` methods to `invalid-mock-access` (`PGH005`) ([#18547](https://github.com/astral-sh/ruff/pull/18547))
- \[`pylint`\] Ignore `__init__.py` files in (`PLC0414`) ([#18400](https://github.com/astral-sh/ruff/pull/18400))
- \[`ruff`\] Trigger `RUF037` for empty string and byte strings ([#18862](https://github.com/astral-sh/ruff/pull/18862))
- [formatter] Fix missing blank lines before decorated classes in `.pyi` files ([#18888](https://github.com/astral-sh/ruff/pull/18888))
### Bug fixes
- Avoid generating diagnostics with per-file ignores ([#18801](https://github.com/astral-sh/ruff/pull/18801))
- Handle parenthesized arguments in `remove_argument` ([#18805](https://github.com/astral-sh/ruff/pull/18805))
- \[`flake8-logging`\] Avoid false positive for `exc_info=True` outside `logger.exception` (`LOG014`) ([#18737](https://github.com/astral-sh/ruff/pull/18737))
- \[`flake8-pytest-style`\] Enforce `pytest` import for decorators ([#18779](https://github.com/astral-sh/ruff/pull/18779))
- \[`flake8-pytest-style`\] Mark autofix for `PT001` and `PT023` as unsafe if there's comments in the decorator ([#18792](https://github.com/astral-sh/ruff/pull/18792))
- \[`flake8-pytest-style`\] `PT001`/`PT023` fix makes syntax error on parenthesized decorator ([#18782](https://github.com/astral-sh/ruff/pull/18782))
- \[`flake8-raise`\] Make fix unsafe if it deletes comments (`RSE102`) ([#18788](https://github.com/astral-sh/ruff/pull/18788))
- \[`flake8-simplify`\] Fix `SIM911` autofix creating a syntax error ([#18793](https://github.com/astral-sh/ruff/pull/18793))
- \[`flake8-simplify`\] Fix false negatives for shadowed bindings (`SIM910`, `SIM911`) ([#18794](https://github.com/astral-sh/ruff/pull/18794))
- \[`flake8-simplify`\] Preserve original behavior for `except ()` and bare `except` (`SIM105`) ([#18213](https://github.com/astral-sh/ruff/pull/18213))
- \[`flake8-pyi`\] Fix `PYI041`'s fix causing `TypeError` with `None | None | ...` ([#18637](https://github.com/astral-sh/ruff/pull/18637))
- \[`perflint`\] Fix `PERF101` autofix creating a syntax error and mark autofix as unsafe if there are comments in the `list` call expr ([#18803](https://github.com/astral-sh/ruff/pull/18803))
- \[`perflint`\] Fix false negative in `PERF401` ([#18866](https://github.com/astral-sh/ruff/pull/18866))
- \[`pylint`\] Avoid flattening nested `min`/`max` when outer call has single argument (`PLW3301`) ([#16885](https://github.com/astral-sh/ruff/pull/16885))
- \[`pylint`\] Fix `PLC2801` autofix creating a syntax error ([#18857](https://github.com/astral-sh/ruff/pull/18857))
- \[`pylint`\] Mark `PLE0241` autofix as unsafe if there's comments in the base classes ([#18832](https://github.com/astral-sh/ruff/pull/18832))
- \[`pylint`\] Suppress `PLE2510`/`PLE2512`/`PLE2513`/`PLE2514`/`PLE2515` autofix if the text contains an odd number of backslashes ([#18856](https://github.com/astral-sh/ruff/pull/18856))
- \[`refurb`\] Detect more exotic float literals in `FURB164` ([#18925](https://github.com/astral-sh/ruff/pull/18925))
- \[`refurb`\] Fix `FURB163` autofix creating a syntax error for `yield` expressions ([#18756](https://github.com/astral-sh/ruff/pull/18756))
- \[`refurb`\] Mark `FURB129` autofix as unsafe if there's comments in the `readlines` call ([#18858](https://github.com/astral-sh/ruff/pull/18858))
- \[`ruff`\] Fix false positives and negatives in `RUF010` ([#18690](https://github.com/astral-sh/ruff/pull/18690))
- Fix casing of `analyze.direction` variant names ([#18892](https://github.com/astral-sh/ruff/pull/18892))
### Rule changes
- Fix f-string interpolation escaping in generated fixes ([#18882](https://github.com/astral-sh/ruff/pull/18882))
- \[`flake8-return`\] Mark `RET501` fix unsafe if comments are inside ([#18780](https://github.com/astral-sh/ruff/pull/18780))
- \[`flake8-async`\] Fix detection for large integer sleep durations in `ASYNC116` rule ([#18767](https://github.com/astral-sh/ruff/pull/18767))
- \[`flake8-async`\] Mark autofix for `ASYNC115` as unsafe if the call expression contains comments ([#18753](https://github.com/astral-sh/ruff/pull/18753))
- \[`flake8-bugbear`\] Mark autofix for `B004` as unsafe if the `hasattr` call expr contains comments ([#18755](https://github.com/astral-sh/ruff/pull/18755))
- \[`flake8-comprehension`\] Mark autofix for `C420` as unsafe if there's comments inside the dict comprehension ([#18768](https://github.com/astral-sh/ruff/pull/18768))
- \[`flake8-comprehensions`\] Handle template strings for comprehension fixes ([#18710](https://github.com/astral-sh/ruff/pull/18710))
- \[`flake8-future-annotations`\] Add autofix (`FA100`) ([#18903](https://github.com/astral-sh/ruff/pull/18903))
- \[`pyflakes`\] Mark `F504`/`F522`/`F523` autofix as unsafe if there's a call with side effect ([#18839](https://github.com/astral-sh/ruff/pull/18839))
- \[`pylint`\] Allow fix with comments and document performance implications (`PLW3301`) ([#18936](https://github.com/astral-sh/ruff/pull/18936))
- \[`pylint`\] Detect more exotic `NaN` literals in `PLW0177` ([#18630](https://github.com/astral-sh/ruff/pull/18630))
- \[`pylint`\] Fix `PLC1802` autofix creating a syntax error and mark autofix as unsafe if there's comments in the `len` call ([#18836](https://github.com/astral-sh/ruff/pull/18836))
- \[`pyupgrade`\] Extend version detection to include `sys.version_info.major` (`UP036`) ([#18633](https://github.com/astral-sh/ruff/pull/18633))
- \[`ruff`\] Add lint rule `RUF064` for calling `chmod` with non-octal integers ([#18541](https://github.com/astral-sh/ruff/pull/18541))
- \[`ruff`\] Added `cls.__dict__.get('__annotations__')` check (`RUF063`) ([#18233](https://github.com/astral-sh/ruff/pull/18233))
- \[`ruff`\] Frozen `dataclass` default should be valid (`RUF009`) ([#18735](https://github.com/astral-sh/ruff/pull/18735))
- Add missing docstring sections to the numpy list ([#20931](https://github.com/astral-sh/ruff/pull/20931))
- \[`airflow`\] Extend `airflow.models..Param` check (`AIR311`) ([#21043](https://github.com/astral-sh/ruff/pull/21043))
- \[`airflow`\] Warn that `airflow....DAG.create_dagrun` has been removed (`AIR301`) ([#21093](https://github.com/astral-sh/ruff/pull/21093))
- \[`refurb`\] Preserve digit separators in `Decimal` constructor (`FURB157`) ([#20588](https://github.com/astral-sh/ruff/pull/20588))
### Server
- Consider virtual path for various server actions ([#18910](https://github.com/astral-sh/ruff/pull/18910))
- Avoid sending an unnecessary "clear diagnostics" message for clients supporting pull diagnostics ([#21105](https://github.com/astral-sh/ruff/pull/21105))
### Documentation
- Add fix safety sections ([#18940](https://github.com/astral-sh/ruff/pull/18940),[#18841](https://github.com/astral-sh/ruff/pull/18841),[#18802](https://github.com/astral-sh/ruff/pull/18802),[#18837](https://github.com/astral-sh/ruff/pull/18837),[#18800](https://github.com/astral-sh/ruff/pull/18800),[#18415](https://github.com/astral-sh/ruff/pull/18415),[#18853](https://github.com/astral-sh/ruff/pull/18853),[#18842](https://github.com/astral-sh/ruff/pull/18842))
- Use updated pre-commit id ([#18718](https://github.com/astral-sh/ruff/pull/18718))
- \[`perflint`\] Small docs improvement to `PERF401` ([#18786](https://github.com/astral-sh/ruff/pull/18786))
- \[`pyupgrade`\]: Use `super()`, not `__super__` in error messages (`UP008`) ([#18743](https://github.com/astral-sh/ruff/pull/18743))
- \[`flake8-pie`\] Small docs fix to `PIE794` ([#18829](https://github.com/astral-sh/ruff/pull/18829))
- \[`flake8-pyi`\] Correct `collections-named-tuple` example to use PascalCase assignment ([#16884](https://github.com/astral-sh/ruff/pull/16884))
- \[`flake8-pie`\] Add note on type checking benefits to `unnecessary-dict-kwargs` (`PIE804`) ([#18666](https://github.com/astral-sh/ruff/pull/18666))
- \[`pycodestyle`\] Clarify PEP 8 relationship to `whitespace-around-operator` rules ([#18870](https://github.com/astral-sh/ruff/pull/18870))
- \[`flake8-bandit`\] Fix correct example for `S308` ([#21128](https://github.com/astral-sh/ruff/pull/21128))
### Other changes
- Disallow newlines in format specifiers of single quoted f- or t-strings ([#18708](https://github.com/astral-sh/ruff/pull/18708))
- \[`flake8-logging`\] Add fix safety section to `LOG002` ([#18840](https://github.com/astral-sh/ruff/pull/18840))
- \[`pyupgrade`\] Add fix safety section to `UP010` ([#18838](https://github.com/astral-sh/ruff/pull/18838))
- Clearer error message when `line-length` goes beyond threshold ([#21072](https://github.com/astral-sh/ruff/pull/21072))
## 0.12.0
### Contributors
Check out the [blog post](https://astral.sh/blog/ruff-v0.12.0) for a migration
guide and overview of the changes!
- [@danparizher](https://github.com/danparizher)
- [@jvacek](https://github.com/jvacek)
- [@ntBre](https://github.com/ntBre)
- [@augustelalande](https://github.com/augustelalande)
- [@prakhar1144](https://github.com/prakhar1144)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@dylwil3](https://github.com/dylwil3)
- [@fatelei](https://github.com/fatelei)
- [@ShaharNaveh](https://github.com/ShaharNaveh)
- [@Lee-W](https://github.com/Lee-W)
## 0.14.2
Released on 2025-10-23.
### Preview features
- \[`flake8-gettext`\] Resolve qualified names and built-in bindings (`INT001`, `INT002`, `INT003`) ([#19045](https://github.com/astral-sh/ruff/pull/19045))
### Bug fixes
- Avoid reusing nested, interpolated quotes before Python 3.12 ([#20930](https://github.com/astral-sh/ruff/pull/20930))
- Catch syntax errors in nested interpolations before Python 3.12 ([#20949](https://github.com/astral-sh/ruff/pull/20949))
- \[`fastapi`\] Handle ellipsis defaults in `FAST002` autofix ([#20810](https://github.com/astral-sh/ruff/pull/20810))
- \[`flake8-simplify`\] Skip `SIM911` when unknown arguments are present ([#20697](https://github.com/astral-sh/ruff/pull/20697))
- \[`pyupgrade`\] Always parenthesize assignment expressions in fix for `f-string` (`UP032`) ([#21003](https://github.com/astral-sh/ruff/pull/21003))
- \[`pyupgrade`\] Fix `UP032` conversion for decimal ints with underscores ([#21022](https://github.com/astral-sh/ruff/pull/21022))
- \[`fastapi`\] Skip autofix for keyword and `__debug__` path params (`FAST003`) ([#20960](https://github.com/astral-sh/ruff/pull/20960))
### Rule changes
- \[`flake8-bugbear`\] Skip `B905` and `B912` for fewer than two iterables and no starred arguments ([#20998](https://github.com/astral-sh/ruff/pull/20998))
- \[`ruff`\] Use `DiagnosticTag` for more `pyflakes` and `pandas` rules ([#20801](https://github.com/astral-sh/ruff/pull/20801))
### CLI
- Improve JSON output from `ruff rule` ([#20168](https://github.com/astral-sh/ruff/pull/20168))
### Documentation
- Add source to testimonial ([#20971](https://github.com/astral-sh/ruff/pull/20971))
- Document when a rule was added ([#21035](https://github.com/astral-sh/ruff/pull/21035))
### Other changes
- [syntax-errors] Name is parameter and global ([#20426](https://github.com/astral-sh/ruff/pull/20426))
- [syntax-errors] Alternative `match` patterns bind different names ([#20682](https://github.com/astral-sh/ruff/pull/20682))
### Contributors
- [@hengky-kurniawan-1](https://github.com/hengky-kurniawan-1)
- [@ShalokShalom](https://github.com/ShalokShalom)
- [@robsdedude](https://github.com/robsdedude)
- [@LoicRiegel](https://github.com/LoicRiegel)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@dylwil3](https://github.com/dylwil3)
- [@11happy](https://github.com/11happy)
- [@ntBre](https://github.com/ntBre)
## 0.14.1
Released on 2025-10-16.
### Preview features
- [formatter] Remove parentheses around multiple exception types on Python 3.14+ ([#20768](https://github.com/astral-sh/ruff/pull/20768))
- \[`flake8-bugbear`\] Omit annotation in preview fix for `B006` ([#20877](https://github.com/astral-sh/ruff/pull/20877))
- \[`flake8-logging-format`\] Avoid dropping implicitly concatenated pieces in the `G004` fix ([#20793](https://github.com/astral-sh/ruff/pull/20793))
- \[`pydoclint`\] Implement `docstring-extraneous-parameter` (`DOC102`) ([#20376](https://github.com/astral-sh/ruff/pull/20376))
- \[`pyupgrade`\] Extend `UP019` to detect `typing_extensions.Text` (`UP019`) ([#20825](https://github.com/astral-sh/ruff/pull/20825))
- \[`pyupgrade`\] Fix false negative for `TypeVar` with default argument in `non-pep695-generic-class` (`UP046`) ([#20660](https://github.com/astral-sh/ruff/pull/20660))
### Bug fixes
- Fix false negatives in `Truthiness::from_expr` for lambdas, generators, and f-strings ([#20704](https://github.com/astral-sh/ruff/pull/20704))
- Fix syntax error false positives for escapes and quotes in f-strings ([#20867](https://github.com/astral-sh/ruff/pull/20867))
- Fix syntax error false positives on parenthesized context managers ([#20846](https://github.com/astral-sh/ruff/pull/20846))
- \[`fastapi`\] Fix false positives for path parameters that FastAPI doesn't recognize (`FAST003`) ([#20687](https://github.com/astral-sh/ruff/pull/20687))
- \[`flake8-pyi`\] Fix operator precedence by adding parentheses when needed (`PYI061`) ([#20508](https://github.com/astral-sh/ruff/pull/20508))
- \[`ruff`\] Suppress diagnostic for f-string interpolations with debug text (`RUF010`) ([#20525](https://github.com/astral-sh/ruff/pull/20525))
### Rule changes
- \[`airflow`\] Add warning to `airflow.datasets.DatasetEvent` usage (`AIR301`) ([#20551](https://github.com/astral-sh/ruff/pull/20551))
- \[`flake8-bugbear`\] Mark `B905` and `B912` fixes as unsafe ([#20695](https://github.com/astral-sh/ruff/pull/20695))
- Use `DiagnosticTag` for more rules - changes display in editors ([#20758](https://github.com/astral-sh/ruff/pull/20758),[#20734](https://github.com/astral-sh/ruff/pull/20734))
### Documentation
- Update Python compatibility from 3.13 to 3.14 in README.md ([#20852](https://github.com/astral-sh/ruff/pull/20852))
- Update `lint.flake8-type-checking.quoted-annotations` docs ([#20765](https://github.com/astral-sh/ruff/pull/20765))
- Update setup instructions for Zed 0.208.0+ ([#20902](https://github.com/astral-sh/ruff/pull/20902))
- \[`flake8-datetimez`\] Clarify docs for several rules ([#20778](https://github.com/astral-sh/ruff/pull/20778))
- Fix typo in `RUF015` description ([#20873](https://github.com/astral-sh/ruff/pull/20873))
### Other changes
- Reduce binary size ([#20863](https://github.com/astral-sh/ruff/pull/20863))
- Improved error recovery for unclosed strings (including f- and t-strings) ([#20848](https://github.com/astral-sh/ruff/pull/20848))
### Contributors
- [@ntBre](https://github.com/ntBre)
- [@Paillat-dev](https://github.com/Paillat-dev)
- [@terror](https://github.com/terror)
- [@pieterh-oai](https://github.com/pieterh-oai)
- [@MichaReiser](https://github.com/MichaReiser)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@ageorgou](https://github.com/ageorgou)
- [@danparizher](https://github.com/danparizher)
- [@mgaitan](https://github.com/mgaitan)
- [@augustelalande](https://github.com/augustelalande)
- [@dylwil3](https://github.com/dylwil3)
- [@Lee-W](https://github.com/Lee-W)
- [@injust](https://github.com/injust)
- [@CarrotManMatt](https://github.com/CarrotManMatt)
## 0.14.0
Released on 2025-10-07.
### Breaking changes
- **Detection of more syntax errors**
Ruff now detects version-related syntax errors, such as the use of the `match`
statement on Python versions before 3.10, and syntax errors emitted by
CPython's compiler, such as irrefutable `match` patterns before the final
`case` arm.
- **New default Python version handling for syntax errors**
Ruff will default to the *latest* supported Python version (3.13) when
checking for the version-related syntax errors mentioned above to prevent
false positives in projects without a Python version configured. The default
in all other cases, like applying lint rules, is unchanged and remains at the
minimum supported Python version (3.9).
- **Updated f-string formatting**
Ruff now formats multi-line f-strings with format specifiers to avoid adding a
line break after the format specifier. This addresses a change to the Python
grammar in version 3.13.4 that made such a line break a syntax error.
- **`rust-toolchain.toml` is no longer included in source distributions**
The `rust-toolchain.toml` is used to specify a higher Rust version than Ruff's
minimum supported Rust version (MSRV) for development and building release
artifacts. However, when present in source distributions, it would also cause
downstream package maintainers to pull in the same Rust toolchain, even if
their available toolchain was MSRV-compatible.
### Removed Rules
The following rules have been removed:
- [`suspicious-xmle-tree-usage`](https://docs.astral.sh/ruff/rules/suspicious-xmle-tree-usage/)
(`S320`)
### Deprecated Rules
The following rules have been deprecated:
- [`pandas-df-variable-name`](https://docs.astral.sh/ruff/rules/pandas-df-variable-name/)
### Stabilization
The following rules have been stabilized and are no longer in preview:
- [`for-loop-writes`](https://docs.astral.sh/ruff/rules/for-loop-writes) (`FURB122`)
- [`check-and-remove-from-set`](https://docs.astral.sh/ruff/rules/check-and-remove-from-set) (`FURB132`)
- [`verbose-decimal-constructor`](https://docs.astral.sh/ruff/rules/verbose-decimal-constructor) (`FURB157`)
- [`fromisoformat-replace-z`](https://docs.astral.sh/ruff/rules/fromisoformat-replace-z) (`FURB162`)
- [`int-on-sliced-str`](https://docs.astral.sh/ruff/rules/int-on-sliced-str) (`FURB166`)
- [`exc-info-outside-except-handler`](https://docs.astral.sh/ruff/rules/exc-info-outside-except-handler) (`LOG014`)
- [`import-outside-top-level`](https://docs.astral.sh/ruff/rules/import-outside-top-level) (`PLC0415`)
- [`unnecessary-dict-index-lookup`](https://docs.astral.sh/ruff/rules/unnecessary-dict-index-lookup) (`PLR1733`)
- [`nan-comparison`](https://docs.astral.sh/ruff/rules/nan-comparison) (`PLW0177`)
- [`eq-without-hash`](https://docs.astral.sh/ruff/rules/eq-without-hash) (`PLW1641`)
- [`pytest-parameter-with-default-argument`](https://docs.astral.sh/ruff/rules/pytest-parameter-with-default-argument) (`PT028`)
- [`pytest-warns-too-broad`](https://docs.astral.sh/ruff/rules/pytest-warns-too-broad) (`PT030`)
- [`pytest-warns-with-multiple-statements`](https://docs.astral.sh/ruff/rules/pytest-warns-with-multiple-statements) (`PT031`)
- [`invalid-formatter-suppression-comment`](https://docs.astral.sh/ruff/rules/invalid-formatter-suppression-comment) (`RUF028`)
- [`dataclass-enum`](https://docs.astral.sh/ruff/rules/dataclass-enum) (`RUF049`)
- [`class-with-mixed-type-vars`](https://docs.astral.sh/ruff/rules/class-with-mixed-type-vars) (`RUF053`)
- [`unnecessary-round`](https://docs.astral.sh/ruff/rules/unnecessary-round) (`RUF057`)
- [`starmap-zip`](https://docs.astral.sh/ruff/rules/starmap-zip) (`RUF058`)
- [`non-pep604-annotation-optional`] (`UP045`)
- [`non-pep695-generic-class`](https://docs.astral.sh/ruff/rules/non-pep695-generic-class) (`UP046`)
- [`non-pep695-generic-function`](https://docs.astral.sh/ruff/rules/non-pep695-generic-function) (`UP047`)
- [`private-type-parameter`](https://docs.astral.sh/ruff/rules/private-type-parameter) (`UP049`)
The following behaviors have been stabilized:
- [`collection-literal-concatenation`] (`RUF005`) now recognizes slices, in
addition to list literals and variables.
- The fix for [`readlines-in-for`] (`FURB129`) is now marked as always safe.
- [`if-else-block-instead-of-if-exp`] (`SIM108`) will now further simplify
expressions to use `or` instead of an `if` expression, where possible.
- [`unused-noqa`] (`RUF100`) now checks for file-level `noqa` comments as well
as inline comments.
- [`subprocess-without-shell-equals-true`] (`S603`) now accepts literal strings,
as well as lists and tuples of literal strings, as trusted input.
- [`boolean-type-hint-positional-argument`] (`FBT001`) now applies to types that
include `bool`, like `bool | int` or `typing.Optional[bool]`, in addition to
plain `bool` annotations.
- [`non-pep604-annotation-union`] (`UP007`) has now been split into two rules.
`UP007` now applies only to `typing.Union`, while
[`non-pep604-annotation-optional`] (`UP045`) checks for use of
`typing.Optional`. `UP045` has also been stabilized in this release, but you
may need to update existing `include`, `ignore`, or `noqa` settings to
accommodate this change.
- Update default and latest Python versions for 3.14 ([#20725](https://github.com/astral-sh/ruff/pull/20725))
### Preview features
- \[`ruff`\] Check for non-context-manager use of `pytest.raises`, `pytest.warns`, and `pytest.deprecated_call` (`RUF061`) ([#17368](https://github.com/astral-sh/ruff/pull/17368))
- [syntax-errors] Raise unsupported syntax error for template strings prior to Python 3.14 ([#18664](https://github.com/astral-sh/ruff/pull/18664))
- \[`flake8-bugbear`\] Include certain guaranteed-mutable expressions: tuples, generators, and assignment expressions (`B006`) ([#20024](https://github.com/astral-sh/ruff/pull/20024))
- \[`refurb`\] Add fixes for `FURB101` and `FURB103` ([#20520](https://github.com/astral-sh/ruff/pull/20520))
- \[`ruff`\] Extend `FA102` with listed PEP585-compatible APIs ([#20659](https://github.com/astral-sh/ruff/pull/20659))
### Bug fixes
- Add syntax error when conversion flag does not immediately follow exclamation mark ([#18706](https://github.com/astral-sh/ruff/pull/18706))
- Add trailing space around `readlines` ([#18542](https://github.com/astral-sh/ruff/pull/18542))
- Fix `\r` and `\r\n` handling in t- and f-string debug texts ([#18673](https://github.com/astral-sh/ruff/pull/18673))
- Hug closing `}` when f-string expression has a format specifier ([#18704](https://github.com/astral-sh/ruff/pull/18704))
- \[`flake8-pyi`\] Avoid syntax error in the case of starred and keyword arguments (`PYI059`) ([#18611](https://github.com/astral-sh/ruff/pull/18611))
- \[`flake8-return`\] Fix `RET504` autofix generating a syntax error ([#18428](https://github.com/astral-sh/ruff/pull/18428))
- \[`pep8-naming`\] Suppress fix for `N804` and `N805` if the recommended name is already used ([#18472](https://github.com/astral-sh/ruff/pull/18472))
- \[`pycodestyle`\] Avoid causing a syntax error in expressions spanning multiple lines (`E731`) ([#18479](https://github.com/astral-sh/ruff/pull/18479))
- \[`pyupgrade`\] Suppress `UP008` if `super` is shadowed ([#18688](https://github.com/astral-sh/ruff/pull/18688))
- \[`refurb`\] Parenthesize lambda and ternary expressions (`FURB122`, `FURB142`) ([#18592](https://github.com/astral-sh/ruff/pull/18592))
- \[`ruff`\] Handle extra arguments to `deque` (`RUF037`) ([#18614](https://github.com/astral-sh/ruff/pull/18614))
- \[`ruff`\] Preserve parentheses around `deque` in fix for `unnecessary-empty-iterable-within-deque-call` (`RUF037`) ([#18598](https://github.com/astral-sh/ruff/pull/18598))
- \[`ruff`\] Validate arguments before offering a fix (`RUF056`) ([#18631](https://github.com/astral-sh/ruff/pull/18631))
- \[`ruff`\] Skip fix for `RUF059` if dummy name is already bound ([#18509](https://github.com/astral-sh/ruff/pull/18509))
- \[`pylint`\] Fix `PLW0128` to check assignment targets in square brackets and after asterisks ([#18665](https://github.com/astral-sh/ruff/pull/18665))
### Rule changes
- Fix false positive on mutations in `return` statements (`B909`) ([#18408](https://github.com/astral-sh/ruff/pull/18408))
- Treat `ty:` comments as pragma comments ([#18532](https://github.com/astral-sh/ruff/pull/18532))
- \[`flake8-pyi`\] Apply `custom-typevar-for-self` to string annotations (`PYI019`) ([#18311](https://github.com/astral-sh/ruff/pull/18311))
- \[`pyupgrade`\] Don't offer a fix for `Optional[None]` (`UP007`, `UP045)` ([#18545](https://github.com/astral-sh/ruff/pull/18545))
- \[`pyupgrade`\] Fix `super(__class__, self)` detection (`UP008`) ([#18478](https://github.com/astral-sh/ruff/pull/18478))
- \[`refurb`\] Make the fix for `FURB163` unsafe for `log2`, `log10`, `*args`, and deleted comments ([#18645](https://github.com/astral-sh/ruff/pull/18645))
### Server
- Support cancellation requests ([#18627](https://github.com/astral-sh/ruff/pull/18627))
- \[`flake8-annotations`\] Fix return type annotations to handle shadowed builtin symbols (`ANN201`, `ANN202`, `ANN204`, `ANN205`, `ANN206`) ([#20612](https://github.com/astral-sh/ruff/pull/20612))
- \[`flynt`\] Fix f-string quoting for mixed quote joiners (`FLY002`) ([#20662](https://github.com/astral-sh/ruff/pull/20662))
- \[`isort`\] Fix inserting required imports before future imports (`I002`) ([#20676](https://github.com/astral-sh/ruff/pull/20676))
- \[`ruff`\] Handle argfile expansion errors gracefully ([#20691](https://github.com/astral-sh/ruff/pull/20691))
- \[`ruff`\] Skip `RUF051` if `else`/`elif` block is present ([#20705](https://github.com/astral-sh/ruff/pull/20705))
- \[`ruff`\] Improve handling of intermixed comments inside from-imports ([#20561](https://github.com/astral-sh/ruff/pull/20561))
### Documentation
- Drop confusing second `*` from glob pattern example for `per-file-target-version` ([#18709](https://github.com/astral-sh/ruff/pull/18709))
- Update Neovim configuration examples ([#18491](https://github.com/astral-sh/ruff/pull/18491))
- \[`pylint`\] De-emphasize `__hash__ = Parent.__hash__` (`PLW1641`) ([#18613](https://github.com/astral-sh/ruff/pull/18613))
- \[`refurb`\] Add a note about float literal handling (`FURB157`) ([#18615](https://github.com/astral-sh/ruff/pull/18615))
- \[`flake8-comprehensions`\] Clarify fix safety documentation (`C413`) ([#20640](https://github.com/astral-sh/ruff/pull/20640))
### Contributors
- [@danparizher](https://github.com/danparizher)
- [@terror](https://github.com/terror)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@ntBre](https://github.com/ntBre)
- [@njhearp](https://github.com/njhearp)
- [@amyreese](https://github.com/amyreese)
- [@IDrokin117](https://github.com/IDrokin117)
- [@chirizxc](https://github.com/chirizxc)
## 0.13.x
See [changelogs/0.13.x](./changelogs/0.13.x.md)
## 0.12.x
See [changelogs/0.12.x](./changelogs/0.12.x.md)
## 0.11.x
@@ -529,12 +391,3 @@ See [changelogs/0.2.x](./changelogs/0.2.x.md)
## 0.1.x
See [changelogs/0.1.x](./changelogs/0.1.x.md)
[`boolean-type-hint-positional-argument`]: https://docs.astral.sh/ruff/rules/boolean-type-hint-positional-argument
[`collection-literal-concatenation`]: https://docs.astral.sh/ruff/rules/collection-literal-concatenation
[`if-else-block-instead-of-if-exp`]: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp
[`non-pep604-annotation-optional`]: https://docs.astral.sh/ruff/rules/non-pep604-annotation-optional
[`non-pep604-annotation-union`]: https://docs.astral.sh/ruff/rules/non-pep604-annotation-union
[`readlines-in-for`]: https://docs.astral.sh/ruff/rules/readlines-in-for
[`subprocess-without-shell-equals-true`]: https://docs.astral.sh/ruff/rules/subprocess-without-shell-equals-true
[`unused-noqa`]: https://docs.astral.sh/ruff/rules/unused-noqa

View File

@@ -7,22 +7,38 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
> This guide is for Ruff. If you're looking to contribute to ty, please see [the ty contributing
> guide](https://github.com/astral-sh/ruff/blob/main/crates/ty/CONTRIBUTING.md).
## Finding ways to help
We label issues that would be good for a first time contributor as
[`good first issue`](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
These usually do not require significant experience with Rust or the Ruff code base.
We label issues that we think are a good opportunity for subsequent contributions as
[`help wanted`](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
These require varying levels of experience with Rust and Ruff. Often, we want to accomplish these
tasks but do not have the resources to do so ourselves.
You don't need our permission to start on an issue we have labeled as appropriate for community
contribution as described above. However, it's a good idea to indicate that you are going to work on
an issue to avoid concurrent attempts to solve the same problem.
Please check in with us before starting work on an issue that has not been labeled as appropriate
for community contribution. We're happy to receive contributions for other issues, but it's
important to make sure we have consensus on the solution to the problem first.
Outside of issues with the labels above, issues labeled as
[`bug`](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3A%22bug%22) are the
best candidates for contribution. In contrast, issues labeled with `needs-decision` or
`needs-design` are _not_ good candidates for contribution. Please do not open pull requests for
issues with these labels.
Please do not open pull requests for new features without prior discussion. While we appreciate
exploration of new features, we will often close these pull requests immediately. Adding a
new feature to ruff creates a long-term maintenance burden and requires strong consensus from the ruff
team before it is appropriate to begin work on an implementation.
## The Basics
Ruff welcomes contributions in the form of pull requests.
For small changes (e.g., bug fixes), feel free to submit a PR.
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
creating an [**issue**](https://github.com/astral-sh/ruff/issues) outlining your proposed change.
You can also join us on [Discord](https://discord.com/invite/astral-sh) to discuss your idea with the
community. We've labeled [beginner-friendly tasks](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
in the issue tracker, along with [bugs](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
and [improvements](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Aaccepted)
that are ready for contributions.
If you have suggestions on how we might improve the contributing documentation, [let us know](https://github.com/astral-sh/ruff/discussions/5693)!
### Prerequisites
Ruff is written in Rust. You'll need to install the
@@ -264,6 +280,55 @@ Note that plugin-specific configuration options are defined in their own modules
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
### Opening a PR
After you finish your changes, the next step is to open a PR. By default, two
sections will be filled into the PR body: the summary and the test plan.
#### The summary
The summary is intended to give us as maintainers information about your PR.
This should typically include a link to the relevant issue(s) you're addressing
in your PR, as well as a summary of the issue and your approach to fixing it. If
you have any questions about your approach or design, or if you considered
alternative approaches, that can also be helpful to include.
AI can be helpful in generating both the code and summary of your PR, but a
successful contribution should still be carefully reviewed by you and the
summary editorialized before submitting a PR. A great summary is thorough but
also succinct and gives us the context we need to review your PR.
You can find examples of excellent issues and PRs by searching for the
[`great writeup`](https://github.com/astral-sh/ruff/issues?q=label%3A%22great%20writeup%22)
label.
#### The test plan
The test plan is likely to be shorter than the summary and can be as simple as
"Added new snapshot tests for `RUF123`," at least for rule bugs. For LSP or some
types of CLI changes, in particular, it can also be helpful to include
screenshots or recordings of your change in action.
#### Ecosystem report
After opening the PR, an ecosystem report will be run as part of CI. This shows
a diff of linter and formatter behavior before and after the changes in your PR.
Going through these changes and reporting your findings in the PR summary or an
additional comment help us to review your PR more efficiently. It's also a great
way to find new test cases to incorporate into your PR if you identify any
issues.
#### PR status
To help us know when your PR is ready for review again, please either move your
PR back to a draft while working on it (marking it ready for review afterwards
will ping the previous reviewers) or explicitly re-request a review. This helps
us to avoid re-reviewing a PR while you're still working on it and also to
prioritize PRs that are definitely ready for review.
You can also thumbs-up or mark as resolved any comments we leave to let us know
you addressed them.
## MkDocs
> [!NOTE]
@@ -305,10 +370,16 @@ them to [PyPI](https://pypi.org/project/ruff/).
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
### Creating a new release
### Installing tools
1. Install `uv`: `curl -LsSf https://astral.sh/uv/install.sh | sh`
1. Install `npm`: `brew install npm` or similar
### Creating a new release
Commit each step of this process separately for easier review.
1. Run `./scripts/release.sh`; this command will:
- Generate a temporary virtual environment with `rooster`
@@ -321,6 +392,7 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
- Often labels will be missing from pull requests they will need to be manually organized into the proper section
- Changes should be edited to be user-facing descriptions, avoiding internal details
- Square brackets (eg, `[ruff]` project name) will be automatically escaped by `pre-commit`
Additionally, for minor releases:
@@ -360,13 +432,13 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
1. Verify the GitHub release:
1. The Changelog should match the content of `CHANGELOG.md`
1. Append the contributors from the `scripts/release.sh` script
1. The changelog should match the content of `CHANGELOG.md`
1. If needed, [update the schemastore](https://github.com/astral-sh/ruff/blob/main/scripts/update_schemastore.py).
1. One can determine if an update is needed when
`git diff old-version-tag new-version-tag -- ruff.schema.json` returns a non-empty diff.
1. Run `uv run --only-dev --no-sync scripts/update_schemastore.py --proto <https|ssh>`
1. Once run successfully, you should follow the link in the output to create a PR.
1. If needed, update the [`ruff-lsp`](https://github.com/astral-sh/ruff-lsp) and

1215
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ resolver = "2"
[workspace.package]
# Please update rustfmt.toml when bumping the Rust edition
edition = "2024"
rust-version = "1.87"
rust-version = "1.89"
homepage = "https://docs.astral.sh/ruff"
documentation = "https://docs.astral.sh/ruff"
repository = "https://github.com/astral-sh/ruff"
@@ -29,6 +29,7 @@ ruff_options_metadata = { path = "crates/ruff_options_metadata" }
ruff_python_ast = { path = "crates/ruff_python_ast" }
ruff_python_codegen = { path = "crates/ruff_python_codegen" }
ruff_python_formatter = { path = "crates/ruff_python_formatter" }
ruff_python_importer = { path = "crates/ruff_python_importer" }
ruff_python_index = { path = "crates/ruff_python_index" }
ruff_python_literal = { path = "crates/ruff_python_literal" }
ruff_python_parser = { path = "crates/ruff_python_parser" }
@@ -42,6 +43,7 @@ ruff_workspace = { path = "crates/ruff_workspace" }
ty = { path = "crates/ty" }
ty_combine = { path = "crates/ty_combine" }
ty_completion_eval = { path = "crates/ty_completion_eval" }
ty_ide = { path = "crates/ty_ide" }
ty_project = { path = "crates/ty_project", default-features = false }
ty_python_semantic = { path = "crates/ty_python_semantic" }
@@ -68,8 +70,9 @@ camino = { version = "1.1.7" }
clap = { version = "4.5.3", features = ["derive"] }
clap_complete_command = { version = "0.6.0" }
clearscreen = { version = "4.0.0" }
divan = { package = "codspeed-divan-compat", version = "3.0.2" }
codspeed-criterion-compat = { version = "3.0.2", default-features = false }
csv = { version = "1.3.1" }
divan = { package = "codspeed-divan-compat", version = "4.0.4" }
codspeed-criterion-compat = { version = "4.0.4", default-features = false }
colored = { version = "3.0.0" }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "1.0.0" }
@@ -81,11 +84,11 @@ dashmap = { version = "6.0.1" }
dir-test = { version = "0.4.0" }
dunce = { version = "1.0.5" }
drop_bomb = { version = "0.1.5" }
etcetera = { version = "0.10.0" }
etcetera = { version = "0.11.0" }
fern = { version = "0.7.0" }
filetime = { version = "0.2.23" }
getrandom = { version = "0.3.1" }
get-size2 = { version = "0.6.2", features = [
get-size2 = { version = "0.7.0", features = [
"derive",
"smallvec",
"hashbrown",
@@ -94,13 +97,13 @@ get-size2 = { version = "0.6.2", features = [
glob = { version = "0.3.1" }
globset = { version = "0.4.14" }
globwalk = { version = "0.9.1" }
hashbrown = { version = "0.15.0", default-features = false, features = [
hashbrown = { version = "0.16.0", default-features = false, features = [
"raw-entry",
"equivalent",
"inline-more",
] }
heck = "0.5.0"
ignore = { version = "0.4.22" }
ignore = { version = "0.4.24" }
imara-diff = { version = "0.1.5" }
imperative = { version = "1.0.4" }
indexmap = { version = "2.6.0" }
@@ -115,13 +118,13 @@ jiff = { version = "0.2.0" }
js-sys = { version = "0.3.69" }
jod-thread = { version = "1.0.0" }
libc = { version = "0.2.153" }
libcst = { version = "1.1.0", default-features = false }
libcst = { version = "1.8.4", default-features = false }
log = { version = "0.4.17" }
lsp-server = { version = "0.7.6" }
lsp-types = { git = "https://github.com/astral-sh/lsp-types.git", rev = "3512a9f", features = [
"proposed",
] }
matchit = { version = "0.8.1" }
matchit = { version = "0.9.0" }
memchr = { version = "2.7.1" }
mimalloc = { version = "0.1.39" }
natord = { version = "1.0.9" }
@@ -143,13 +146,13 @@ regex-automata = { version = "0.4.9" }
rustc-hash = { version = "2.0.0" }
rustc-stable-hash = { version = "0.1.2" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a3ffa22cb26756473d56f867aedec3fd907c4dd9", default-features = false, features = [
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "17bc55d699565e5a1cb1bd42363b905af2f9f3e7", default-features = false, features = [
"compact_str",
"macros",
"salsa_unstable",
"inventory",
] }
schemars = { version = "0.8.16" }
schemars = { version = "1.0.4" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }
serde-wasm-bindgen = { version = "0.6.4" }
@@ -170,6 +173,7 @@ snapbox = { version = "0.6.0", features = [
static_assertions = "1.1.0"
strum = { version = "0.27.0", features = ["strum_macros"] }
strum_macros = { version = "0.27.0" }
supports-hyperlinks = { version = "3.1.0" }
syn = { version = "2.0.55" }
tempfile = { version = "3.9.0" }
test-case = { version = "3.3.1" }
@@ -202,7 +206,7 @@ wild = { version = "2" }
zip = { version = "0.6.6", default-features = false }
[workspace.metadata.cargo-shear]
ignored = ["getrandom", "ruff_options_metadata", "uuid"]
ignored = ["getrandom", "ruff_options_metadata", "uuid", "get-size2", "ty_completion_eval"]
[workspace.lints.rust]
@@ -215,6 +219,8 @@ unexpected_cfgs = { level = "warn", check-cfg = [
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -2 }
# Enabled at the crate level
disallowed_methods = "allow"
# Allowed pedantic lints
char_lit_as_u8 = "allow"
collapsible_else_if = "allow"
@@ -249,17 +255,21 @@ rest_pat_in_fully_bound_structs = "warn"
redundant_clone = "warn"
debug_assert_with_mut_call = "warn"
unused_peekable = "warn"
# This lint sometimes flags code whose `if` and `else`
# bodies could be flipped when a `!` operator is removed.
# While perhaps sometimes a good idea, it is also often
# not a good idea due to other factors impacting
# readability. For example, if flipping the bodies results
# in the `if` being an order of magnitude bigger than the
# `else`, then some might consider that harder to read.
if_not_else = "allow"
# Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved.
large_stack_arrays = "allow"
[profile.release]
# Note that we set these explicitly, and these values
# were chosen based on a trade-off between compile times
# and runtime performance[1].
#
# [1]: https://github.com/astral-sh/ruff/pull/9031
lto = "thin"
lto = "fat"
codegen-units = 16
# Some crates don't change as much but benefit more from
@@ -269,6 +279,8 @@ codegen-units = 16
codegen-units = 1
[profile.release.package.ruff_python_ast]
codegen-units = 1
[profile.release.package.salsa]
codegen-units = 1
[profile.dev.package.insta]
opt-level = 3
@@ -284,11 +296,30 @@ opt-level = 3
[profile.dev.package.ruff_python_parser]
opt-level = 1
# This profile is meant to mimic the `release` profile as closely as
# possible, but using settings that are more beneficial for iterative
# development. That is, the `release` profile is intended for actually
# building the release, where as `profiling` is meant for building ty/ruff
# for running benchmarks.
#
# The main differences here are to avoid stripping debug information
# and disabling fat lto. This does result in a mismatch between our release
# configuration and our benchmarking configuration, which is unfortunate.
# But compile times with `lto = fat` are completely untenable.
#
# This setup does risk that we are measuring something in benchmarks
# that we aren't shipping, but in order to make those two the same, we'd
# either need to make compile times way worse for development, or take
# a hit to binary size and a slight hit to runtime performance in our
# release builds.
#
# Use the `--profile profiling` flag to show symbols in release mode.
# e.g. `cargo build --profile profiling`
[profile.profiling]
inherits = "release"
debug = 1
strip = false
debug = "full"
lto = false
# The profile that 'cargo dist' will build with.
[profile.dist]

1160
LICENSE

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ An extremely fast Python linter and code formatter, written in Rust.
- ⚡️ 10-100x faster than existing linters (like Flake8) and formatters (like Black)
- 🐍 Installable via `pip`
- 🛠️ `pyproject.toml` support
- 🤝 Python 3.13 compatibility
- 🤝 Python 3.14 compatibility
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruffs-linter-compare-to-flake8), isort, and [Black](https://docs.astral.sh/ruff/faq/#how-does-ruffs-formatter-compare-to-black)
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
@@ -89,8 +89,7 @@ creator of [isort](https://github.com/PyCQA/isort):
> Just switched my first project to Ruff. Only one downside so far: it's so fast I couldn't believe
> it was working till I intentionally introduced some errors.
[**Tim Abbott**](https://github.com/astral-sh/ruff/issues/465#issuecomment-1317400028), lead
developer of [Zulip](https://github.com/zulip/zulip):
[**Tim Abbott**](https://github.com/zulip/zulip/pull/23431#issuecomment-1302557034), lead developer of [Zulip](https://github.com/zulip/zulip) (also [here](https://github.com/astral-sh/ruff/issues/465#issuecomment-1317400028)):
> This is just ridiculously fast... `ruff` is amazing.
@@ -148,8 +147,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
# For a specific version.
curl -LsSf https://astral.sh/ruff/0.12.10/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.12.10/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.14.6/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.14.6/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -182,7 +181,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.12.10
rev: v0.14.6
hooks:
# Run the linter.
- id: ruff-check
@@ -421,7 +420,7 @@ Ruff is released under the MIT license.
Ruff is used by a number of major open-source projects and companies, including:
- [Albumentations](https://github.com/albumentations-team/albumentations)
- [Albumentations](https://github.com/albumentations-team/AlbumentationsX)
- Amazon ([AWS SAM](https://github.com/aws/serverless-application-model))
- [Anki](https://apps.ankiweb.net/)
- Anthropic ([Python SDK](https://github.com/anthropics/anthropic-sdk-python))
@@ -492,6 +491,7 @@ Ruff is used by a number of major open-source projects and companies, including:
- [PyTorch](https://github.com/pytorch/pytorch)
- [Pydantic](https://github.com/pydantic/pydantic)
- [Pylint](https://github.com/PyCQA/pylint)
- [PyScripter](https://github.com/pyscripter/pyscripter)
- [PyVista](https://github.com/pyvista/pyvista)
- [Reflex](https://github.com/reflex-dev/reflex)
- [River](https://github.com/online-ml/river)

View File

@@ -31,6 +31,7 @@ extend-ignore-re = [
"typ",
# TODO: Remove this once the `TYP` redirects are removed from `rule_redirects.rs`
"TYP",
"ntBre"
]
[default.extend-identifiers]

551
changelogs/0.12.x.md Normal file
View File

@@ -0,0 +1,551 @@
# Changelog 0.12.x
## 0.12.0
Check out the [blog post](https://astral.sh/blog/ruff-v0.12.0) for a migration
guide and overview of the changes!
### Breaking changes
- **Detection of more syntax errors**
Ruff now detects version-related syntax errors, such as the use of the `match`
statement on Python versions before 3.10, and syntax errors emitted by
CPython's compiler, such as irrefutable `match` patterns before the final
`case` arm.
- **New default Python version handling for syntax errors**
Ruff will default to the *latest* supported Python version (3.13) when
checking for the version-related syntax errors mentioned above to prevent
false positives in projects without a Python version configured. The default
in all other cases, like applying lint rules, is unchanged and remains at the
minimum supported Python version (3.9).
- **Updated f-string formatting**
Ruff now formats multi-line f-strings with format specifiers to avoid adding a
line break after the format specifier. This addresses a change to the Python
grammar in version 3.13.4 that made such a line break a syntax error.
- **`rust-toolchain.toml` is no longer included in source distributions**
The `rust-toolchain.toml` is used to specify a higher Rust version than Ruff's
minimum supported Rust version (MSRV) for development and building release
artifacts. However, when present in source distributions, it would also cause
downstream package maintainers to pull in the same Rust toolchain, even if
their available toolchain was MSRV-compatible.
### Removed Rules
The following rules have been removed:
- [`suspicious-xmle-tree-usage`](https://docs.astral.sh/ruff/rules/suspicious-xmle-tree-usage/)
(`S320`)
### Deprecated Rules
The following rules have been deprecated:
- [`pandas-df-variable-name`](https://docs.astral.sh/ruff/rules/pandas-df-variable-name/)
### Stabilization
The following rules have been stabilized and are no longer in preview:
- [`for-loop-writes`](https://docs.astral.sh/ruff/rules/for-loop-writes) (`FURB122`)
- [`check-and-remove-from-set`](https://docs.astral.sh/ruff/rules/check-and-remove-from-set) (`FURB132`)
- [`verbose-decimal-constructor`](https://docs.astral.sh/ruff/rules/verbose-decimal-constructor) (`FURB157`)
- [`fromisoformat-replace-z`](https://docs.astral.sh/ruff/rules/fromisoformat-replace-z) (`FURB162`)
- [`int-on-sliced-str`](https://docs.astral.sh/ruff/rules/int-on-sliced-str) (`FURB166`)
- [`exc-info-outside-except-handler`](https://docs.astral.sh/ruff/rules/exc-info-outside-except-handler) (`LOG014`)
- [`import-outside-top-level`](https://docs.astral.sh/ruff/rules/import-outside-top-level) (`PLC0415`)
- [`unnecessary-dict-index-lookup`](https://docs.astral.sh/ruff/rules/unnecessary-dict-index-lookup) (`PLR1733`)
- [`nan-comparison`](https://docs.astral.sh/ruff/rules/nan-comparison) (`PLW0177`)
- [`eq-without-hash`](https://docs.astral.sh/ruff/rules/eq-without-hash) (`PLW1641`)
- [`pytest-parameter-with-default-argument`](https://docs.astral.sh/ruff/rules/pytest-parameter-with-default-argument) (`PT028`)
- [`pytest-warns-too-broad`](https://docs.astral.sh/ruff/rules/pytest-warns-too-broad) (`PT030`)
- [`pytest-warns-with-multiple-statements`](https://docs.astral.sh/ruff/rules/pytest-warns-with-multiple-statements) (`PT031`)
- [`invalid-formatter-suppression-comment`](https://docs.astral.sh/ruff/rules/invalid-formatter-suppression-comment) (`RUF028`)
- [`dataclass-enum`](https://docs.astral.sh/ruff/rules/dataclass-enum) (`RUF049`)
- [`class-with-mixed-type-vars`](https://docs.astral.sh/ruff/rules/class-with-mixed-type-vars) (`RUF053`)
- [`unnecessary-round`](https://docs.astral.sh/ruff/rules/unnecessary-round) (`RUF057`)
- [`starmap-zip`](https://docs.astral.sh/ruff/rules/starmap-zip) (`RUF058`)
- [`non-pep604-annotation-optional`] (`UP045`)
- [`non-pep695-generic-class`](https://docs.astral.sh/ruff/rules/non-pep695-generic-class) (`UP046`)
- [`non-pep695-generic-function`](https://docs.astral.sh/ruff/rules/non-pep695-generic-function) (`UP047`)
- [`private-type-parameter`](https://docs.astral.sh/ruff/rules/private-type-parameter) (`UP049`)
The following behaviors have been stabilized:
- [`collection-literal-concatenation`] (`RUF005`) now recognizes slices, in
addition to list literals and variables.
- The fix for [`readlines-in-for`] (`FURB129`) is now marked as always safe.
- [`if-else-block-instead-of-if-exp`] (`SIM108`) will now further simplify
expressions to use `or` instead of an `if` expression, where possible.
- [`unused-noqa`] (`RUF100`) now checks for file-level `noqa` comments as well
as inline comments.
- [`subprocess-without-shell-equals-true`] (`S603`) now accepts literal strings,
as well as lists and tuples of literal strings, as trusted input.
- [`boolean-type-hint-positional-argument`] (`FBT001`) now applies to types that
include `bool`, like `bool | int` or `typing.Optional[bool]`, in addition to
plain `bool` annotations.
- [`non-pep604-annotation-union`] (`UP007`) has now been split into two rules.
`UP007` now applies only to `typing.Union`, while
[`non-pep604-annotation-optional`] (`UP045`) checks for use of
`typing.Optional`. `UP045` has also been stabilized in this release, but you
may need to update existing `include`, `ignore`, or `noqa` settings to
accommodate this change.
### Preview features
- \[`ruff`\] Check for non-context-manager use of `pytest.raises`, `pytest.warns`, and `pytest.deprecated_call` (`RUF061`) ([#17368](https://github.com/astral-sh/ruff/pull/17368))
- [syntax-errors] Raise unsupported syntax error for template strings prior to Python 3.14 ([#18664](https://github.com/astral-sh/ruff/pull/18664))
### Bug fixes
- Add syntax error when conversion flag does not immediately follow exclamation mark ([#18706](https://github.com/astral-sh/ruff/pull/18706))
- Add trailing space around `readlines` ([#18542](https://github.com/astral-sh/ruff/pull/18542))
- Fix `\r` and `\r\n` handling in t- and f-string debug texts ([#18673](https://github.com/astral-sh/ruff/pull/18673))
- Hug closing `}` when f-string expression has a format specifier ([#18704](https://github.com/astral-sh/ruff/pull/18704))
- \[`flake8-pyi`\] Avoid syntax error in the case of starred and keyword arguments (`PYI059`) ([#18611](https://github.com/astral-sh/ruff/pull/18611))
- \[`flake8-return`\] Fix `RET504` autofix generating a syntax error ([#18428](https://github.com/astral-sh/ruff/pull/18428))
- \[`pep8-naming`\] Suppress fix for `N804` and `N805` if the recommended name is already used ([#18472](https://github.com/astral-sh/ruff/pull/18472))
- \[`pycodestyle`\] Avoid causing a syntax error in expressions spanning multiple lines (`E731`) ([#18479](https://github.com/astral-sh/ruff/pull/18479))
- \[`pyupgrade`\] Suppress `UP008` if `super` is shadowed ([#18688](https://github.com/astral-sh/ruff/pull/18688))
- \[`refurb`\] Parenthesize lambda and ternary expressions (`FURB122`, `FURB142`) ([#18592](https://github.com/astral-sh/ruff/pull/18592))
- \[`ruff`\] Handle extra arguments to `deque` (`RUF037`) ([#18614](https://github.com/astral-sh/ruff/pull/18614))
- \[`ruff`\] Preserve parentheses around `deque` in fix for `unnecessary-empty-iterable-within-deque-call` (`RUF037`) ([#18598](https://github.com/astral-sh/ruff/pull/18598))
- \[`ruff`\] Validate arguments before offering a fix (`RUF056`) ([#18631](https://github.com/astral-sh/ruff/pull/18631))
- \[`ruff`\] Skip fix for `RUF059` if dummy name is already bound ([#18509](https://github.com/astral-sh/ruff/pull/18509))
- \[`pylint`\] Fix `PLW0128` to check assignment targets in square brackets and after asterisks ([#18665](https://github.com/astral-sh/ruff/pull/18665))
### Rule changes
- Fix false positive on mutations in `return` statements (`B909`) ([#18408](https://github.com/astral-sh/ruff/pull/18408))
- Treat `ty:` comments as pragma comments ([#18532](https://github.com/astral-sh/ruff/pull/18532))
- \[`flake8-pyi`\] Apply `custom-typevar-for-self` to string annotations (`PYI019`) ([#18311](https://github.com/astral-sh/ruff/pull/18311))
- \[`pyupgrade`\] Don't offer a fix for `Optional[None]` (`UP007`, `UP045)` ([#18545](https://github.com/astral-sh/ruff/pull/18545))
- \[`pyupgrade`\] Fix `super(__class__, self)` detection (`UP008`) ([#18478](https://github.com/astral-sh/ruff/pull/18478))
- \[`refurb`\] Make the fix for `FURB163` unsafe for `log2`, `log10`, `*args`, and deleted comments ([#18645](https://github.com/astral-sh/ruff/pull/18645))
### Server
- Support cancellation requests ([#18627](https://github.com/astral-sh/ruff/pull/18627))
### Documentation
- Drop confusing second `*` from glob pattern example for `per-file-target-version` ([#18709](https://github.com/astral-sh/ruff/pull/18709))
- Update Neovim configuration examples ([#18491](https://github.com/astral-sh/ruff/pull/18491))
- \[`pylint`\] De-emphasize `__hash__ = Parent.__hash__` (`PLW1641`) ([#18613](https://github.com/astral-sh/ruff/pull/18613))
- \[`refurb`\] Add a note about float literal handling (`FURB157`) ([#18615](https://github.com/astral-sh/ruff/pull/18615))
## 0.12.1
### Preview features
- \[`flake8-errmsg`\] Extend `EM101` to support byte strings ([#18867](https://github.com/astral-sh/ruff/pull/18867))
- \[`flake8-use-pathlib`\] Add autofix for `PTH202` ([#18763](https://github.com/astral-sh/ruff/pull/18763))
- \[`pygrep-hooks`\] Add `AsyncMock` methods to `invalid-mock-access` (`PGH005`) ([#18547](https://github.com/astral-sh/ruff/pull/18547))
- \[`pylint`\] Ignore `__init__.py` files in (`PLC0414`) ([#18400](https://github.com/astral-sh/ruff/pull/18400))
- \[`ruff`\] Trigger `RUF037` for empty string and byte strings ([#18862](https://github.com/astral-sh/ruff/pull/18862))
- [formatter] Fix missing blank lines before decorated classes in `.pyi` files ([#18888](https://github.com/astral-sh/ruff/pull/18888))
### Bug fixes
- Avoid generating diagnostics with per-file ignores ([#18801](https://github.com/astral-sh/ruff/pull/18801))
- Handle parenthesized arguments in `remove_argument` ([#18805](https://github.com/astral-sh/ruff/pull/18805))
- \[`flake8-logging`\] Avoid false positive for `exc_info=True` outside `logger.exception` (`LOG014`) ([#18737](https://github.com/astral-sh/ruff/pull/18737))
- \[`flake8-pytest-style`\] Enforce `pytest` import for decorators ([#18779](https://github.com/astral-sh/ruff/pull/18779))
- \[`flake8-pytest-style`\] Mark autofix for `PT001` and `PT023` as unsafe if there's comments in the decorator ([#18792](https://github.com/astral-sh/ruff/pull/18792))
- \[`flake8-pytest-style`\] `PT001`/`PT023` fix makes syntax error on parenthesized decorator ([#18782](https://github.com/astral-sh/ruff/pull/18782))
- \[`flake8-raise`\] Make fix unsafe if it deletes comments (`RSE102`) ([#18788](https://github.com/astral-sh/ruff/pull/18788))
- \[`flake8-simplify`\] Fix `SIM911` autofix creating a syntax error ([#18793](https://github.com/astral-sh/ruff/pull/18793))
- \[`flake8-simplify`\] Fix false negatives for shadowed bindings (`SIM910`, `SIM911`) ([#18794](https://github.com/astral-sh/ruff/pull/18794))
- \[`flake8-simplify`\] Preserve original behavior for `except ()` and bare `except` (`SIM105`) ([#18213](https://github.com/astral-sh/ruff/pull/18213))
- \[`flake8-pyi`\] Fix `PYI041`'s fix causing `TypeError` with `None | None | ...` ([#18637](https://github.com/astral-sh/ruff/pull/18637))
- \[`perflint`\] Fix `PERF101` autofix creating a syntax error and mark autofix as unsafe if there are comments in the `list` call expr ([#18803](https://github.com/astral-sh/ruff/pull/18803))
- \[`perflint`\] Fix false negative in `PERF401` ([#18866](https://github.com/astral-sh/ruff/pull/18866))
- \[`pylint`\] Avoid flattening nested `min`/`max` when outer call has single argument (`PLW3301`) ([#16885](https://github.com/astral-sh/ruff/pull/16885))
- \[`pylint`\] Fix `PLC2801` autofix creating a syntax error ([#18857](https://github.com/astral-sh/ruff/pull/18857))
- \[`pylint`\] Mark `PLE0241` autofix as unsafe if there's comments in the base classes ([#18832](https://github.com/astral-sh/ruff/pull/18832))
- \[`pylint`\] Suppress `PLE2510`/`PLE2512`/`PLE2513`/`PLE2514`/`PLE2515` autofix if the text contains an odd number of backslashes ([#18856](https://github.com/astral-sh/ruff/pull/18856))
- \[`refurb`\] Detect more exotic float literals in `FURB164` ([#18925](https://github.com/astral-sh/ruff/pull/18925))
- \[`refurb`\] Fix `FURB163` autofix creating a syntax error for `yield` expressions ([#18756](https://github.com/astral-sh/ruff/pull/18756))
- \[`refurb`\] Mark `FURB129` autofix as unsafe if there's comments in the `readlines` call ([#18858](https://github.com/astral-sh/ruff/pull/18858))
- \[`ruff`\] Fix false positives and negatives in `RUF010` ([#18690](https://github.com/astral-sh/ruff/pull/18690))
- Fix casing of `analyze.direction` variant names ([#18892](https://github.com/astral-sh/ruff/pull/18892))
### Rule changes
- Fix f-string interpolation escaping in generated fixes ([#18882](https://github.com/astral-sh/ruff/pull/18882))
- \[`flake8-return`\] Mark `RET501` fix unsafe if comments are inside ([#18780](https://github.com/astral-sh/ruff/pull/18780))
- \[`flake8-async`\] Fix detection for large integer sleep durations in `ASYNC116` rule ([#18767](https://github.com/astral-sh/ruff/pull/18767))
- \[`flake8-async`\] Mark autofix for `ASYNC115` as unsafe if the call expression contains comments ([#18753](https://github.com/astral-sh/ruff/pull/18753))
- \[`flake8-bugbear`\] Mark autofix for `B004` as unsafe if the `hasattr` call expr contains comments ([#18755](https://github.com/astral-sh/ruff/pull/18755))
- \[`flake8-comprehension`\] Mark autofix for `C420` as unsafe if there's comments inside the dict comprehension ([#18768](https://github.com/astral-sh/ruff/pull/18768))
- \[`flake8-comprehensions`\] Handle template strings for comprehension fixes ([#18710](https://github.com/astral-sh/ruff/pull/18710))
- \[`flake8-future-annotations`\] Add autofix (`FA100`) ([#18903](https://github.com/astral-sh/ruff/pull/18903))
- \[`pyflakes`\] Mark `F504`/`F522`/`F523` autofix as unsafe if there's a call with side effect ([#18839](https://github.com/astral-sh/ruff/pull/18839))
- \[`pylint`\] Allow fix with comments and document performance implications (`PLW3301`) ([#18936](https://github.com/astral-sh/ruff/pull/18936))
- \[`pylint`\] Detect more exotic `NaN` literals in `PLW0177` ([#18630](https://github.com/astral-sh/ruff/pull/18630))
- \[`pylint`\] Fix `PLC1802` autofix creating a syntax error and mark autofix as unsafe if there's comments in the `len` call ([#18836](https://github.com/astral-sh/ruff/pull/18836))
- \[`pyupgrade`\] Extend version detection to include `sys.version_info.major` (`UP036`) ([#18633](https://github.com/astral-sh/ruff/pull/18633))
- \[`ruff`\] Add lint rule `RUF064` for calling `chmod` with non-octal integers ([#18541](https://github.com/astral-sh/ruff/pull/18541))
- \[`ruff`\] Added `cls.__dict__.get('__annotations__')` check (`RUF063`) ([#18233](https://github.com/astral-sh/ruff/pull/18233))
- \[`ruff`\] Frozen `dataclass` default should be valid (`RUF009`) ([#18735](https://github.com/astral-sh/ruff/pull/18735))
### Server
- Consider virtual path for various server actions ([#18910](https://github.com/astral-sh/ruff/pull/18910))
### Documentation
- Add fix safety sections ([#18940](https://github.com/astral-sh/ruff/pull/18940),[#18841](https://github.com/astral-sh/ruff/pull/18841),[#18802](https://github.com/astral-sh/ruff/pull/18802),[#18837](https://github.com/astral-sh/ruff/pull/18837),[#18800](https://github.com/astral-sh/ruff/pull/18800),[#18415](https://github.com/astral-sh/ruff/pull/18415),[#18853](https://github.com/astral-sh/ruff/pull/18853),[#18842](https://github.com/astral-sh/ruff/pull/18842))
- Use updated pre-commit id ([#18718](https://github.com/astral-sh/ruff/pull/18718))
- \[`perflint`\] Small docs improvement to `PERF401` ([#18786](https://github.com/astral-sh/ruff/pull/18786))
- \[`pyupgrade`\]: Use `super()`, not `__super__` in error messages (`UP008`) ([#18743](https://github.com/astral-sh/ruff/pull/18743))
- \[`flake8-pie`\] Small docs fix to `PIE794` ([#18829](https://github.com/astral-sh/ruff/pull/18829))
- \[`flake8-pyi`\] Correct `collections-named-tuple` example to use PascalCase assignment ([#16884](https://github.com/astral-sh/ruff/pull/16884))
- \[`flake8-pie`\] Add note on type checking benefits to `unnecessary-dict-kwargs` (`PIE804`) ([#18666](https://github.com/astral-sh/ruff/pull/18666))
- \[`pycodestyle`\] Clarify PEP 8 relationship to `whitespace-around-operator` rules ([#18870](https://github.com/astral-sh/ruff/pull/18870))
### Other changes
- Disallow newlines in format specifiers of single quoted f- or t-strings ([#18708](https://github.com/astral-sh/ruff/pull/18708))
- \[`flake8-logging`\] Add fix safety section to `LOG002` ([#18840](https://github.com/astral-sh/ruff/pull/18840))
- \[`pyupgrade`\] Add fix safety section to `UP010` ([#18838](https://github.com/astral-sh/ruff/pull/18838))
## 0.12.2
### Preview features
- \[`flake8-pyi`\] Expand `Optional[A]` to `A | None` (`PYI016`) ([#18572](https://github.com/astral-sh/ruff/pull/18572))
- \[`pyupgrade`\] Mark `UP008` fix safe if no comments are in range ([#18683](https://github.com/astral-sh/ruff/pull/18683))
### Bug fixes
- \[`flake8-comprehensions`\] Fix `C420` to prepend whitespace when needed ([#18616](https://github.com/astral-sh/ruff/pull/18616))
- \[`perflint`\] Fix `PERF403` panic on attribute or subscription loop variable ([#19042](https://github.com/astral-sh/ruff/pull/19042))
- \[`pydocstyle`\] Fix `D413` infinite loop for parenthesized docstring ([#18930](https://github.com/astral-sh/ruff/pull/18930))
- \[`pylint`\] Fix `PLW0108` autofix introducing a syntax error when the lambda's body contains an assignment expression ([#18678](https://github.com/astral-sh/ruff/pull/18678))
- \[`refurb`\] Fix false positive on empty tuples (`FURB168`) ([#19058](https://github.com/astral-sh/ruff/pull/19058))
- \[`ruff`\] Allow more `field` calls from `attrs` (`RUF009`) ([#19021](https://github.com/astral-sh/ruff/pull/19021))
- \[`ruff`\] Fix syntax error introduced for an empty string followed by a u-prefixed string (`UP025`) ([#18899](https://github.com/astral-sh/ruff/pull/18899))
### Rule changes
- \[`flake8-executable`\] Allow `uvx` in shebang line (`EXE003`) ([#18967](https://github.com/astral-sh/ruff/pull/18967))
- \[`pandas`\] Avoid flagging `PD002` if `pandas` is not imported ([#18963](https://github.com/astral-sh/ruff/pull/18963))
- \[`pyupgrade`\] Avoid PEP-604 unions with `typing.NamedTuple` (`UP007`, `UP045`) ([#18682](https://github.com/astral-sh/ruff/pull/18682))
### Documentation
- Document link between `import-outside-top-level (PLC0415)` and `lint.flake8-tidy-imports.banned-module-level-imports` ([#18733](https://github.com/astral-sh/ruff/pull/18733))
- Fix description of the `format.skip-magic-trailing-comma` example ([#19095](https://github.com/astral-sh/ruff/pull/19095))
- \[`airflow`\] Make `AIR302` example error out-of-the-box ([#18988](https://github.com/astral-sh/ruff/pull/18988))
- \[`airflow`\] Make `AIR312` example error out-of-the-box ([#18989](https://github.com/astral-sh/ruff/pull/18989))
- \[`flake8-annotations`\] Make `ANN401` example error out-of-the-box ([#18974](https://github.com/astral-sh/ruff/pull/18974))
- \[`flake8-async`\] Make `ASYNC100` example error out-of-the-box ([#18993](https://github.com/astral-sh/ruff/pull/18993))
- \[`flake8-async`\] Make `ASYNC105` example error out-of-the-box ([#19002](https://github.com/astral-sh/ruff/pull/19002))
- \[`flake8-async`\] Make `ASYNC110` example error out-of-the-box ([#18975](https://github.com/astral-sh/ruff/pull/18975))
- \[`flake8-async`\] Make `ASYNC210` example error out-of-the-box ([#18977](https://github.com/astral-sh/ruff/pull/18977))
- \[`flake8-async`\] Make `ASYNC220`, `ASYNC221`, and `ASYNC222` examples error out-of-the-box ([#18978](https://github.com/astral-sh/ruff/pull/18978))
- \[`flake8-async`\] Make `ASYNC251` example error out-of-the-box ([#18990](https://github.com/astral-sh/ruff/pull/18990))
- \[`flake8-bandit`\] Make `S201` example error out-of-the-box ([#19017](https://github.com/astral-sh/ruff/pull/19017))
- \[`flake8-bandit`\] Make `S604` and `S609` examples error out-of-the-box ([#19049](https://github.com/astral-sh/ruff/pull/19049))
- \[`flake8-bugbear`\] Make `B028` example error out-of-the-box ([#19054](https://github.com/astral-sh/ruff/pull/19054))
- \[`flake8-bugbear`\] Make `B911` example error out-of-the-box ([#19051](https://github.com/astral-sh/ruff/pull/19051))
- \[`flake8-datetimez`\] Make `DTZ011` example error out-of-the-box ([#19055](https://github.com/astral-sh/ruff/pull/19055))
- \[`flake8-datetimez`\] Make `DTZ901` example error out-of-the-box ([#19056](https://github.com/astral-sh/ruff/pull/19056))
- \[`flake8-pyi`\] Make `PYI032` example error out-of-the-box ([#19061](https://github.com/astral-sh/ruff/pull/19061))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI014`, `PYI015`) ([#19097](https://github.com/astral-sh/ruff/pull/19097))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI042`) ([#19101](https://github.com/astral-sh/ruff/pull/19101))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI059`) ([#19080](https://github.com/astral-sh/ruff/pull/19080))
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI062`) ([#19079](https://github.com/astral-sh/ruff/pull/19079))
- \[`flake8-pytest-style`\] Make example error out-of-the-box (`PT023`) ([#19104](https://github.com/astral-sh/ruff/pull/19104))
- \[`flake8-pytest-style`\] Make example error out-of-the-box (`PT030`) ([#19105](https://github.com/astral-sh/ruff/pull/19105))
- \[`flake8-quotes`\] Make example error out-of-the-box (`Q003`) ([#19106](https://github.com/astral-sh/ruff/pull/19106))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM110`) ([#19113](https://github.com/astral-sh/ruff/pull/19113))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM113`) ([#19109](https://github.com/astral-sh/ruff/pull/19109))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM401`) ([#19110](https://github.com/astral-sh/ruff/pull/19110))
- \[`pyflakes`\] Fix backslash in docs (`F621`) ([#19098](https://github.com/astral-sh/ruff/pull/19098))
- \[`pylint`\] Fix `PLC0415` example ([#18970](https://github.com/astral-sh/ruff/pull/18970))
## 0.12.3
### Preview features
- \[`flake8-bugbear`\] Support non-context-manager calls in `B017` ([#19063](https://github.com/astral-sh/ruff/pull/19063))
- \[`flake8-use-pathlib`\] Add autofixes for `PTH100`, `PTH106`, `PTH107`, `PTH108`, `PTH110`, `PTH111`, `PTH112`, `PTH113`, `PTH114`, `PTH115`, `PTH117`, `PTH119`, `PTH120` ([#19213](https://github.com/astral-sh/ruff/pull/19213))
- \[`flake8-use-pathlib`\] Add autofixes for `PTH203`, `PTH204`, `PTH205` ([#18922](https://github.com/astral-sh/ruff/pull/18922))
### Bug fixes
- \[`flake8-return`\] Fix false-positive for variables used inside nested functions in `RET504` ([#18433](https://github.com/astral-sh/ruff/pull/18433))
- Treat form feed as valid whitespace before a line continuation ([#19220](https://github.com/astral-sh/ruff/pull/19220))
- \[`flake8-type-checking`\] Fix syntax error introduced by fix (`TC008`) ([#19150](https://github.com/astral-sh/ruff/pull/19150))
- \[`pyupgrade`\] Keyword arguments in `super` should suppress the `UP008` fix ([#19131](https://github.com/astral-sh/ruff/pull/19131))
### Documentation
- \[`flake8-pyi`\] Make example error out-of-the-box (`PYI007`, `PYI008`) ([#19103](https://github.com/astral-sh/ruff/pull/19103))
- \[`flake8-simplify`\] Make example error out-of-the-box (`SIM116`) ([#19111](https://github.com/astral-sh/ruff/pull/19111))
- \[`flake8-type-checking`\] Make example error out-of-the-box (`TC001`) ([#19151](https://github.com/astral-sh/ruff/pull/19151))
- \[`flake8-use-pathlib`\] Make example error out-of-the-box (`PTH210`) ([#19189](https://github.com/astral-sh/ruff/pull/19189))
- \[`pycodestyle`\] Make example error out-of-the-box (`E272`) ([#19191](https://github.com/astral-sh/ruff/pull/19191))
- \[`pycodestyle`\] Make example not raise unnecessary `SyntaxError` (`E114`) ([#19190](https://github.com/astral-sh/ruff/pull/19190))
- \[`pydoclint`\] Make example error out-of-the-box (`DOC501`) ([#19218](https://github.com/astral-sh/ruff/pull/19218))
- \[`pylint`, `pyupgrade`\] Fix syntax errors in examples (`PLW1501`, `UP028`) ([#19127](https://github.com/astral-sh/ruff/pull/19127))
- \[`pylint`\] Update `missing-maxsplit-arg` docs and error to suggest proper usage (`PLC0207`) ([#18949](https://github.com/astral-sh/ruff/pull/18949))
- \[`flake8-bandit`\] Make example error out-of-the-box (`S412`) ([#19241](https://github.com/astral-sh/ruff/pull/19241))
## 0.12.4
### Preview features
- \[`flake8-type-checking`, `pyupgrade`, `ruff`\] Add `from __future__ import annotations` when it would allow new fixes (`TC001`, `TC002`, `TC003`, `UP037`, `RUF013`) ([#19100](https://github.com/astral-sh/ruff/pull/19100))
- \[`flake8-use-pathlib`\] Add autofix for `PTH109` ([#19245](https://github.com/astral-sh/ruff/pull/19245))
- \[`pylint`\] Detect indirect `pathlib.Path` usages for `unspecified-encoding` (`PLW1514`) ([#19304](https://github.com/astral-sh/ruff/pull/19304))
### Bug fixes
- \[`flake8-bugbear`\] Fix `B017` false negatives for keyword exception arguments ([#19217](https://github.com/astral-sh/ruff/pull/19217))
- \[`flake8-use-pathlib`\] Fix false negative on direct `Path()` instantiation (`PTH210`) ([#19388](https://github.com/astral-sh/ruff/pull/19388))
- \[`flake8-django`\] Fix `DJ008` false positive for abstract models with type-annotated `abstract` field ([#19221](https://github.com/astral-sh/ruff/pull/19221))
- \[`isort`\] Fix `I002` import insertion after docstring with multiple string statements ([#19222](https://github.com/astral-sh/ruff/pull/19222))
- \[`isort`\] Treat form feed as valid whitespace before a semicolon ([#19343](https://github.com/astral-sh/ruff/pull/19343))
- \[`pydoclint`\] Fix `SyntaxError` from fixes with line continuations (`D201`, `D202`) ([#19246](https://github.com/astral-sh/ruff/pull/19246))
- \[`refurb`\] `FURB164` fix should validate arguments and should usually be marked unsafe ([#19136](https://github.com/astral-sh/ruff/pull/19136))
### Rule changes
- \[`flake8-use-pathlib`\] Skip single dots for `invalid-pathlib-with-suffix` (`PTH210`) on versions >= 3.14 ([#19331](https://github.com/astral-sh/ruff/pull/19331))
- \[`pep8_naming`\] Avoid false positives on standard library functions with uppercase names (`N802`) ([#18907](https://github.com/astral-sh/ruff/pull/18907))
- \[`pycodestyle`\] Handle brace escapes for t-strings in logical lines ([#19358](https://github.com/astral-sh/ruff/pull/19358))
- \[`pylint`\] Extend invalid string character rules to include t-strings ([#19355](https://github.com/astral-sh/ruff/pull/19355))
- \[`ruff`\] Allow `strict` kwarg when checking for `starmap-zip` (`RUF058`) in Python 3.14+ ([#19333](https://github.com/astral-sh/ruff/pull/19333))
### Documentation
- \[`flake8-type-checking`\] Make `TC010` docs example more realistic ([#19356](https://github.com/astral-sh/ruff/pull/19356))
- Make more documentation examples error out-of-the-box ([#19288](https://github.com/astral-sh/ruff/pull/19288),[#19272](https://github.com/astral-sh/ruff/pull/19272),[#19291](https://github.com/astral-sh/ruff/pull/19291),[#19296](https://github.com/astral-sh/ruff/pull/19296),[#19292](https://github.com/astral-sh/ruff/pull/19292),[#19295](https://github.com/astral-sh/ruff/pull/19295),[#19297](https://github.com/astral-sh/ruff/pull/19297),[#19309](https://github.com/astral-sh/ruff/pull/19309))
## 0.12.5
### Preview features
- \[`flake8-use-pathlib`\] Add autofix for `PTH101`, `PTH104`, `PTH105`, `PTH121` ([#19404](https://github.com/astral-sh/ruff/pull/19404))
- \[`ruff`\] Support byte strings (`RUF055`) ([#18926](https://github.com/astral-sh/ruff/pull/18926))
### Bug fixes
- Fix `unreachable` panic in parser ([#19183](https://github.com/astral-sh/ruff/pull/19183))
- \[`flake8-pyi`\] Skip fix if all `Union` members are `None` (`PYI016`) ([#19416](https://github.com/astral-sh/ruff/pull/19416))
- \[`perflint`\] Parenthesize generator expressions (`PERF401`) ([#19325](https://github.com/astral-sh/ruff/pull/19325))
- \[`pylint`\] Handle empty comments after line continuation (`PLR2044`) ([#19405](https://github.com/astral-sh/ruff/pull/19405))
### Rule changes
- \[`pep8-naming`\] Fix `N802` false positives for `CGIHTTPRequestHandler` and `SimpleHTTPRequestHandler` ([#19432](https://github.com/astral-sh/ruff/pull/19432))
## 0.12.6
### Preview features
- \[`flake8-commas`\] Add support for trailing comma checks in type parameter lists (`COM812`, `COM819`) ([#19390](https://github.com/astral-sh/ruff/pull/19390))
- \[`pylint`\] Implement auto-fix for `missing-maxsplit-arg` (`PLC0207`) ([#19387](https://github.com/astral-sh/ruff/pull/19387))
- \[`ruff`\] Offer fixes for `RUF039` in more cases ([#19065](https://github.com/astral-sh/ruff/pull/19065))
### Bug fixes
- Support `.pyi` files in ruff analyze graph ([#19611](https://github.com/astral-sh/ruff/pull/19611))
- \[`flake8-pyi`\] Preserve inline comment in ellipsis removal (`PYI013`) ([#19399](https://github.com/astral-sh/ruff/pull/19399))
- \[`perflint`\] Ignore rule if target is `global` or `nonlocal` (`PERF401`) ([#19539](https://github.com/astral-sh/ruff/pull/19539))
- \[`pyupgrade`\] Fix `UP030` to avoid modifying double curly braces in format strings ([#19378](https://github.com/astral-sh/ruff/pull/19378))
- \[`refurb`\] Ignore decorated functions for `FURB118` ([#19339](https://github.com/astral-sh/ruff/pull/19339))
- \[`refurb`\] Mark `int` and `bool` cases for `Decimal.from_float` as safe fixes (`FURB164`) ([#19468](https://github.com/astral-sh/ruff/pull/19468))
- \[`ruff`\] Fix `RUF033` for named default expressions ([#19115](https://github.com/astral-sh/ruff/pull/19115))
### Rule changes
- \[`flake8-blind-except`\] Change `BLE001` to permit `logging.critical(..., exc_info=True)` ([#19520](https://github.com/astral-sh/ruff/pull/19520))
### Performance
- Add support for specifying minimum dots in detected string imports ([#19538](https://github.com/astral-sh/ruff/pull/19538))
## 0.12.7
This is a follow-up release to 0.12.6. Because of an issue in the package metadata, 0.12.6 failed to publish fully to PyPI and has been yanked. Similarly, there is no GitHub release or Git tag for 0.12.6. The contents of the 0.12.7 release are identical to 0.12.6, except for the updated metadata.
## 0.12.8
### Preview features
- \[`flake8-use-pathlib`\] Expand `PTH201` to check all `PurePath` subclasses ([#19440](https://github.com/astral-sh/ruff/pull/19440))
### Bug fixes
- \[`flake8-blind-except`\] Change `BLE001` to correctly parse exception tuples ([#19747](https://github.com/astral-sh/ruff/pull/19747))
- \[`flake8-errmsg`\] Exclude `typing.cast` from `EM101` ([#19656](https://github.com/astral-sh/ruff/pull/19656))
- \[`flake8-simplify`\] Fix raw string handling in `SIM905` for embedded quotes ([#19591](https://github.com/astral-sh/ruff/pull/19591))
- \[`flake8-import-conventions`\] Avoid false positives for NFKC-normalized `__debug__` import aliases in `ICN001` ([#19411](https://github.com/astral-sh/ruff/pull/19411))
- \[`isort`\] Fix syntax error after docstring ending with backslash (`I002`) ([#19505](https://github.com/astral-sh/ruff/pull/19505))
- \[`pylint`\] Mark `PLC0207` fixes as unsafe when `*args` unpacking is present ([#19679](https://github.com/astral-sh/ruff/pull/19679))
- \[`pyupgrade`\] Prevent infinite loop with `I002` (`UP010`, `UP035`) ([#19413](https://github.com/astral-sh/ruff/pull/19413))
- \[`ruff`\] Parenthesize generator expressions in f-strings (`RUF010`) ([#19434](https://github.com/astral-sh/ruff/pull/19434))
### Rule changes
- \[`eradicate`\] Don't flag `pyrefly` pragmas as unused code (`ERA001`) ([#19731](https://github.com/astral-sh/ruff/pull/19731))
### Documentation
- Replace "associative" with "commutative" in docs for `RUF036` ([#19706](https://github.com/astral-sh/ruff/pull/19706))
- Fix copy and line separator colors in dark mode ([#19630](https://github.com/astral-sh/ruff/pull/19630))
- Fix link to `typing` documentation ([#19648](https://github.com/astral-sh/ruff/pull/19648))
- \[`refurb`\] Make more examples error out-of-the-box ([#19695](https://github.com/astral-sh/ruff/pull/19695),[#19673](https://github.com/astral-sh/ruff/pull/19673),[#19672](https://github.com/astral-sh/ruff/pull/19672))
### Other changes
- Include column numbers in GitLab output format ([#19708](https://github.com/astral-sh/ruff/pull/19708))
- Always expand tabs to four spaces in diagnostics ([#19618](https://github.com/astral-sh/ruff/pull/19618))
- Update pre-commit's `ruff` id ([#19654](https://github.com/astral-sh/ruff/pull/19654))
## 0.12.9
### Preview features
- \[`airflow`\] Add check for `airflow.secrets.cache.SecretCache` (`AIR301`) ([#17707](https://github.com/astral-sh/ruff/pull/17707))
- \[`ruff`\] Offer a safe fix for multi-digit zeros (`RUF064`) ([#19847](https://github.com/astral-sh/ruff/pull/19847))
### Bug fixes
- \[`flake8-blind-except`\] Fix `BLE001` false-positive on `raise ... from None` ([#19755](https://github.com/astral-sh/ruff/pull/19755))
- \[`flake8-comprehensions`\] Fix false positive for `C420` with attribute, subscript, or slice assignment targets ([#19513](https://github.com/astral-sh/ruff/pull/19513))
- \[`flake8-simplify`\] Fix handling of U+001C..U+001F whitespace (`SIM905`) ([#19849](https://github.com/astral-sh/ruff/pull/19849))
### Rule changes
- \[`pylint`\] Use lowercase hex characters to match the formatter (`PLE2513`) ([#19808](https://github.com/astral-sh/ruff/pull/19808))
### Documentation
- Fix `lint.future-annotations` link ([#19876](https://github.com/astral-sh/ruff/pull/19876))
### Other changes
- Build `riscv64` binaries for release ([#19819](https://github.com/astral-sh/ruff/pull/19819))
- Add rule code to error description in GitLab output ([#19896](https://github.com/astral-sh/ruff/pull/19896))
- Improve rendering of the `full` output format ([#19415](https://github.com/astral-sh/ruff/pull/19415))
Below is an example diff for [`F401`](https://docs.astral.sh/ruff/rules/unused-import/):
```diff
-unused.py:8:19: F401 [*] `pathlib` imported but unused
+F401 [*] `pathlib` imported but unused
+ --> unused.py:8:19
|
7 | # Unused, _not_ marked as required (due to the alias).
8 | import pathlib as non_alias
- | ^^^^^^^^^ F401
+ | ^^^^^^^^^
9 |
10 | # Unused, marked as required.
|
- = help: Remove unused import: `pathlib`
+help: Remove unused import: `pathlib`
```
For now, the primary difference is the movement of the filename, line number, and column information to a second line in the header. This new representation will allow us to make further additions to Ruff's diagnostics, such as adding sub-diagnostics and multiple annotations to the same snippet.
## 0.12.10
### Preview features
- \[`flake8-simplify`\] Implement fix for `maxsplit` without separator (`SIM905`) ([#19851](https://github.com/astral-sh/ruff/pull/19851))
- \[`flake8-use-pathlib`\] Add fixes for `PTH102` and `PTH103` ([#19514](https://github.com/astral-sh/ruff/pull/19514))
### Bug fixes
- \[`isort`\] Handle multiple continuation lines after module docstring (`I002`) ([#19818](https://github.com/astral-sh/ruff/pull/19818))
- \[`pyupgrade`\] Avoid reporting `__future__` features as unnecessary when they are used (`UP010`) ([#19769](https://github.com/astral-sh/ruff/pull/19769))
- \[`pyupgrade`\] Handle nested `Optional`s (`UP045`) ([#19770](https://github.com/astral-sh/ruff/pull/19770))
### Rule changes
- \[`pycodestyle`\] Make `E731` fix unsafe instead of display-only for class assignments ([#19700](https://github.com/astral-sh/ruff/pull/19700))
- \[`pyflakes`\] Add secondary annotation showing previous definition (`F811`) ([#19900](https://github.com/astral-sh/ruff/pull/19900))
### Documentation
- Fix description of global config file discovery strategy ([#19188](https://github.com/astral-sh/ruff/pull/19188))
- Update outdated links to <https://typing.python.org/en/latest/source/stubs.html> ([#19992](https://github.com/astral-sh/ruff/pull/19992))
- \[`flake8-annotations`\] Remove unused import in example (`ANN401`) ([#20000](https://github.com/astral-sh/ruff/pull/20000))
## 0.12.11
### Preview features
- \[`airflow`\] Extend `AIR311` and `AIR312` rules ([#20082](https://github.com/astral-sh/ruff/pull/20082))
- \[`airflow`\] Replace wrong path `airflow.io.storage` with `airflow.io.store` (`AIR311`) ([#20081](https://github.com/astral-sh/ruff/pull/20081))
- \[`flake8-async`\] Implement `blocking-http-call-httpx-in-async-function` (`ASYNC212`) ([#20091](https://github.com/astral-sh/ruff/pull/20091))
- \[`flake8-logging-format`\] Add auto-fix for f-string logging calls (`G004`) ([#19303](https://github.com/astral-sh/ruff/pull/19303))
- \[`flake8-use-pathlib`\] Add autofix for `PTH211` ([#20009](https://github.com/astral-sh/ruff/pull/20009))
- \[`flake8-use-pathlib`\] Make `PTH100` fix unsafe because it can change behavior ([#20100](https://github.com/astral-sh/ruff/pull/20100))
### Bug fixes
- \[`pyflakes`, `pylint`\] Fix false positives caused by `__class__` cell handling (`F841`, `PLE0117`) ([#20048](https://github.com/astral-sh/ruff/pull/20048))
- \[`pyflakes`\] Fix `allowed-unused-imports` matching for top-level modules (`F401`) ([#20115](https://github.com/astral-sh/ruff/pull/20115))
- \[`ruff`\] Fix false positive for t-strings in `default-factory-kwarg` (`RUF026`) ([#20032](https://github.com/astral-sh/ruff/pull/20032))
- \[`ruff`\] Preserve relative whitespace in multi-line expressions (`RUF033`) ([#19647](https://github.com/astral-sh/ruff/pull/19647))
### Rule changes
- \[`ruff`\] Handle empty t-strings in `unnecessary-empty-iterable-within-deque-call` (`RUF037`) ([#20045](https://github.com/astral-sh/ruff/pull/20045))
### Documentation
- Fix incorrect `D413` links in docstrings convention FAQ ([#20089](https://github.com/astral-sh/ruff/pull/20089))
- \[`flake8-use-pathlib`\] Update links to the table showing the correspondence between `os` and `pathlib` ([#20103](https://github.com/astral-sh/ruff/pull/20103))
## 0.12.12
### Preview features
- Show fixes by default ([#19919](https://github.com/astral-sh/ruff/pull/19919))
- \[`airflow`\] Convert `DatasetOrTimeSchedule(datasets=...)` to `AssetOrTimeSchedule(assets=...)` (`AIR311`) ([#20202](https://github.com/astral-sh/ruff/pull/20202))
- \[`airflow`\] Improve the `AIR002` error message ([#20173](https://github.com/astral-sh/ruff/pull/20173))
- \[`airflow`\] Move `airflow.operators.postgres_operator.Mapping` from `AIR302` to `AIR301` ([#20172](https://github.com/astral-sh/ruff/pull/20172))
- \[`flake8-async`\] Implement `blocking-input` rule (`ASYNC250`) ([#20122](https://github.com/astral-sh/ruff/pull/20122))
- \[`flake8-use-pathlib`\] Make `PTH119` and `PTH120` fixes unsafe because they can change behavior ([#20118](https://github.com/astral-sh/ruff/pull/20118))
- \[`pylint`\] Add U+061C to `PLE2502` ([#20106](https://github.com/astral-sh/ruff/pull/20106))
- \[`ruff`\] Fix false negative for empty f-strings in `deque` calls (`RUF037`) ([#20109](https://github.com/astral-sh/ruff/pull/20109))
### Bug fixes
- Less confidently mark f-strings as empty when inferring truthiness ([#20152](https://github.com/astral-sh/ruff/pull/20152))
- \[`fastapi`\] Fix false positive for paths with spaces around parameters (`FAST003`) ([#20077](https://github.com/astral-sh/ruff/pull/20077))
- \[`flake8-comprehensions`\] Skip `C417` when lambda contains `yield`/`yield from` ([#20201](https://github.com/astral-sh/ruff/pull/20201))
- \[`perflint`\] Handle tuples in dictionary comprehensions (`PERF403`) ([#19934](https://github.com/astral-sh/ruff/pull/19934))
### Rule changes
- \[`pycodestyle`\] Preserve return type annotation for `ParamSpec` (`E731`) ([#20108](https://github.com/astral-sh/ruff/pull/20108))
### Documentation
- Add fix safety sections to docs ([#17490](https://github.com/astral-sh/ruff/pull/17490),[#17499](https://github.com/astral-sh/ruff/pull/17499))
[`boolean-type-hint-positional-argument`]: https://docs.astral.sh/ruff/rules/boolean-type-hint-positional-argument
[`collection-literal-concatenation`]: https://docs.astral.sh/ruff/rules/collection-literal-concatenation
[`if-else-block-instead-of-if-exp`]: https://docs.astral.sh/ruff/rules/if-else-block-instead-of-if-exp
[`non-pep604-annotation-optional`]: https://docs.astral.sh/ruff/rules/non-pep604-annotation-optional
[`non-pep604-annotation-union`]: https://docs.astral.sh/ruff/rules/non-pep604-annotation-union
[`readlines-in-for`]: https://docs.astral.sh/ruff/rules/readlines-in-for
[`subprocess-without-shell-equals-true`]: https://docs.astral.sh/ruff/rules/subprocess-without-shell-equals-true
[`unused-noqa`]: https://docs.astral.sh/ruff/rules/unused-noqa

263
changelogs/0.13.x.md Normal file
View File

@@ -0,0 +1,263 @@
# Changelog 0.13.x
## 0.13.0
Check out the [blog post](https://astral.sh/blog/ruff-v0.13.0) for a migration
guide and overview of the changes!
### Breaking changes
- **Several rules can now add `from __future__ import annotations` automatically**
`TC001`, `TC002`, `TC003`, `RUF013`, and `UP037` now add `from __future__ import annotations` as part of their fixes when the
`lint.future-annotations` setting is enabled. This allows the rules to move
more imports into `TYPE_CHECKING` blocks (`TC001`, `TC002`, and `TC003`),
use PEP 604 union syntax on Python versions before 3.10 (`RUF013`), and
unquote more annotations (`UP037`).
- **Full module paths are now used to verify first-party modules**
Ruff now checks that the full path to a module exists on disk before
categorizing it as a first-party import. This change makes first-party
import detection more accurate, helping to avoid false positives on local
directories with the same name as a third-party dependency, for example. See
the [FAQ
section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) on import categorization for more details.
- **Deprecated rules must now be selected by exact rule code**
Ruff will no longer activate deprecated rules selected by their group name
or prefix. As noted below, the two remaining deprecated rules were also
removed in this release, so this won't affect any current rules, but it will
still affect any deprecations in the future.
- **The deprecated macOS configuration directory fallback has been removed**
Ruff will no longer look for a user-level configuration file at
`~/Library/Application Support/ruff/ruff.toml` on macOS. This feature was
deprecated in v0.5 in favor of using the [XDG
specification](https://specifications.freedesktop.org/basedir-spec/latest/)
(usually resolving to `~/.config/ruff/ruff.toml`), like on Linux. The
fallback and accompanying deprecation warning have now been removed.
### Removed Rules
The following rules have been removed:
- [`pandas-df-variable-name`](https://docs.astral.sh/ruff/rules/pandas-df-variable-name) (`PD901`)
- [`non-pep604-isinstance`](https://docs.astral.sh/ruff/rules/non-pep604-isinstance) (`UP038`)
### Stabilization
The following rules have been stabilized and are no longer in preview:
- [`airflow-dag-no-schedule-argument`](https://docs.astral.sh/ruff/rules/airflow-dag-no-schedule-argument)
(`AIR002`)
- [`airflow3-removal`](https://docs.astral.sh/ruff/rules/airflow3-removal) (`AIR301`)
- [`airflow3-moved-to-provider`](https://docs.astral.sh/ruff/rules/airflow3-moved-to-provider)
(`AIR302`)
- [`airflow3-suggested-update`](https://docs.astral.sh/ruff/rules/airflow3-suggested-update)
(`AIR311`)
- [`airflow3-suggested-to-move-to-provider`](https://docs.astral.sh/ruff/rules/airflow3-suggested-to-move-to-provider)
(`AIR312`)
- [`long-sleep-not-forever`](https://docs.astral.sh/ruff/rules/long-sleep-not-forever) (`ASYNC116`)
- [`f-string-number-format`](https://docs.astral.sh/ruff/rules/f-string-number-format) (`FURB116`)
- [`os-symlink`](https://docs.astral.sh/ruff/rules/os-symlink) (`PTH211`)
- [`generic-not-last-base-class`](https://docs.astral.sh/ruff/rules/generic-not-last-base-class)
(`PYI059`)
- [`redundant-none-literal`](https://docs.astral.sh/ruff/rules/redundant-none-literal) (`PYI061`)
- [`pytest-raises-ambiguous-pattern`](https://docs.astral.sh/ruff/rules/pytest-raises-ambiguous-pattern)
(`RUF043`)
- [`unused-unpacked-variable`](https://docs.astral.sh/ruff/rules/unused-unpacked-variable)
(`RUF059`)
- [`useless-class-metaclass-type`](https://docs.astral.sh/ruff/rules/useless-class-metaclass-type)
(`UP050`)
The following behaviors have been stabilized:
- [`assert-raises-exception`](https://docs.astral.sh/ruff/rules/assert-raises-exception) (`B017`)
now checks for direct calls to `unittest.TestCase.assert_raises` and `pytest.raises` instead of
only the context manager forms.
- [`missing-trailing-comma`](https://docs.astral.sh/ruff/rules/missing-trailing-comma) (`COM812`)
and [`prohibited-trailing-comma`](https://docs.astral.sh/ruff/rules/prohibited-trailing-comma)
(`COM819`) now check for trailing commas in PEP 695 type parameter lists.
- [`raw-string-in-exception`](https://docs.astral.sh/ruff/rules/raw-string-in-exception) (`EM101`)
now also checks for byte strings in exception messages.
- [`invalid-mock-access`](https://docs.astral.sh/ruff/rules/invalid-mock-access) (`PGH005`) now
checks for `AsyncMock` methods like `not_awaited` in addition to the synchronous variants.
- [`useless-import-alias`](https://docs.astral.sh/ruff/rules/useless-import-alias) (`PLC0414`) no
longer applies to `__init__.py` files, where it conflicted with one of the suggested fixes for
[`unused-import`](https://docs.astral.sh/ruff/rules/unused-import) (`F401`).
- [`bidirectional-unicode`](https://docs.astral.sh/ruff/rules/bidirectional-unicode) (`PLE2502`) now
also checks for U+061C (Arabic Letter Mark).
- The fix for
[`multiple-with-statements`](https://docs.astral.sh/ruff/rules/multiple-with-statements)
(`SIM117`) is now marked as always safe.
### Preview features
- \[`pyupgrade`\] Enable `UP043` in stub files ([#20027](https://github.com/astral-sh/ruff/pull/20027))
### Bug fixes
- \[`pyupgrade`\] Apply `UP008` only when the `__class__` cell exists ([#19424](https://github.com/astral-sh/ruff/pull/19424))
- \[`ruff`\] Fix empty f-string detection in `in-empty-collection` (`RUF060`) ([#20249](https://github.com/astral-sh/ruff/pull/20249))
### Server
- Add support for using uv as an alternative formatter backend ([#19665](https://github.com/astral-sh/ruff/pull/19665))
### Documentation
- \[`pep8-naming`\] Fix formatting of `__all__` (`N816`) ([#20301](https://github.com/astral-sh/ruff/pull/20301))
## 0.13.1
Released on 2025-09-18.
### Preview features
- \[`flake8-simplify`\] Detect unnecessary `None` default for additional key expression types (`SIM910`) ([#20343](https://github.com/astral-sh/ruff/pull/20343))
- \[`flake8-use-pathlib`\] Add fix for `PTH123` ([#20169](https://github.com/astral-sh/ruff/pull/20169))
- \[`flake8-use-pathlib`\] Fix `PTH101`, `PTH104`, `PTH105`, `PTH121` fixes ([#20143](https://github.com/astral-sh/ruff/pull/20143))
- \[`flake8-use-pathlib`\] Make `PTH111` fix unsafe because it can change behavior ([#20215](https://github.com/astral-sh/ruff/pull/20215))
- \[`pycodestyle`\] Fix `E301` to only trigger for functions immediately within a class ([#19768](https://github.com/astral-sh/ruff/pull/19768))
- \[`refurb`\] Mark `single-item-membership-test` fix as always unsafe (`FURB171`) ([#20279](https://github.com/astral-sh/ruff/pull/20279))
### Bug fixes
- Handle t-strings for token-based rules and suppression comments ([#20357](https://github.com/astral-sh/ruff/pull/20357))
- \[`flake8-bandit`\] Fix truthiness: dict-only `**` displays not truthy for `shell` (`S602`, `S604`, `S609`) ([#20177](https://github.com/astral-sh/ruff/pull/20177))
- \[`flake8-simplify`\] Fix diagnostic to show correct method name for `str.rsplit` calls (`SIM905`) ([#20459](https://github.com/astral-sh/ruff/pull/20459))
- \[`flynt`\] Use triple quotes for joined raw strings with newlines (`FLY002`) ([#20197](https://github.com/astral-sh/ruff/pull/20197))
- \[`pyupgrade`\] Fix false positive when class name is shadowed by local variable (`UP008`) ([#20427](https://github.com/astral-sh/ruff/pull/20427))
- \[`pyupgrade`\] Prevent infinite loop with `I002` and `UP026` ([#20327](https://github.com/astral-sh/ruff/pull/20327))
- \[`ruff`\] Recognize t-strings, generators, and lambdas in `invalid-index-type` (`RUF016`) ([#20213](https://github.com/astral-sh/ruff/pull/20213))
### Rule changes
- \[`RUF102`\] Respect rule redirects in invalid rule code detection ([#20245](https://github.com/astral-sh/ruff/pull/20245))
- \[`flake8-bugbear`\] Mark the fix for `unreliable-callable-check` as always unsafe (`B004`) ([#20318](https://github.com/astral-sh/ruff/pull/20318))
- \[`ruff`\] Allow dataclass attribute value instantiation from nested frozen dataclass (`RUF009`) ([#20352](https://github.com/astral-sh/ruff/pull/20352))
### CLI
- Add fixes to `output-format=sarif` ([#20300](https://github.com/astral-sh/ruff/pull/20300))
- Treat panics as fatal diagnostics, sort panics last ([#20258](https://github.com/astral-sh/ruff/pull/20258))
### Documentation
- \[`ruff`\] Add `analyze.string-imports-min-dots` to settings ([#20375](https://github.com/astral-sh/ruff/pull/20375))
- Update README.md with Albumentations new repository URL ([#20415](https://github.com/astral-sh/ruff/pull/20415))
### Other changes
- Bump MSRV to Rust 1.88 ([#20470](https://github.com/astral-sh/ruff/pull/20470))
- Enable inline noqa for multiline strings in playground ([#20442](https://github.com/astral-sh/ruff/pull/20442))
### Contributors
- [@chirizxc](https://github.com/chirizxc)
- [@danparizher](https://github.com/danparizher)
- [@IDrokin117](https://github.com/IDrokin117)
- [@amyreese](https://github.com/amyreese)
- [@AlexWaygood](https://github.com/AlexWaygood)
- [@dylwil3](https://github.com/dylwil3)
- [@njhearp](https://github.com/njhearp)
- [@woodruffw](https://github.com/woodruffw)
- [@dcreager](https://github.com/dcreager)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@BurntSushi](https://github.com/BurntSushi)
- [@salahelfarissi](https://github.com/salahelfarissi)
- [@MichaReiser](https://github.com/MichaReiser)
## 0.13.2
Released on 2025-09-25.
### Preview features
- \[`flake8-async`\] Implement `blocking-path-method` (`ASYNC240`) ([#20264](https://github.com/astral-sh/ruff/pull/20264))
- \[`flake8-bugbear`\] Implement `map-without-explicit-strict` (`B912`) ([#20429](https://github.com/astral-sh/ruff/pull/20429))
- \[`flake8-bultins`\] Detect class-scope builtin shadowing in decorators, default args, and attribute initializers (`A003`) ([#20178](https://github.com/astral-sh/ruff/pull/20178))
- \[`ruff`\] Implement `logging-eager-conversion` (`RUF065`) ([#19942](https://github.com/astral-sh/ruff/pull/19942))
- Include `.pyw` files by default when linting and formatting ([#20458](https://github.com/astral-sh/ruff/pull/20458))
### Bug fixes
- Deduplicate input paths ([#20105](https://github.com/astral-sh/ruff/pull/20105))
- \[`flake8-comprehensions`\] Preserve trailing commas for single-element lists (`C409`) ([#19571](https://github.com/astral-sh/ruff/pull/19571))
- \[`flake8-pyi`\] Avoid syntax error from conflict with `PIE790` (`PYI021`) ([#20010](https://github.com/astral-sh/ruff/pull/20010))
- \[`flake8-simplify`\] Correct fix for positive `maxsplit` without separator (`SIM905`) ([#20056](https://github.com/astral-sh/ruff/pull/20056))
- \[`pyupgrade`\] Fix `UP008` not to apply when `__class__` is a local variable ([#20497](https://github.com/astral-sh/ruff/pull/20497))
- \[`ruff`\] Fix `B004` to skip invalid `hasattr`/`getattr` calls ([#20486](https://github.com/astral-sh/ruff/pull/20486))
- \[`ruff`\] Replace `-nan` with `nan` when using the value to construct a `Decimal` (`FURB164` ) ([#20391](https://github.com/astral-sh/ruff/pull/20391))
### Documentation
- Add 'Finding ways to help' to CONTRIBUTING.md ([#20567](https://github.com/astral-sh/ruff/pull/20567))
- Update import path to `ruff-wasm-web` ([#20539](https://github.com/astral-sh/ruff/pull/20539))
- \[`flake8-bandit`\] Clarify the supported hashing functions (`S324`) ([#20534](https://github.com/astral-sh/ruff/pull/20534))
### Other changes
- \[`playground`\] Allow hover quick fixes to appear for overlapping diagnostics ([#20527](https://github.com/astral-sh/ruff/pull/20527))
- \[`playground`\] Fix nonBMP code point handling in quick fixes and markers ([#20526](https://github.com/astral-sh/ruff/pull/20526))
### Contributors
- [@BurntSushi](https://github.com/BurntSushi)
- [@mtshiba](https://github.com/mtshiba)
- [@second-ed](https://github.com/second-ed)
- [@danparizher](https://github.com/danparizher)
- [@ShikChen](https://github.com/ShikChen)
- [@PieterCK](https://github.com/PieterCK)
- [@GDYendell](https://github.com/GDYendell)
- [@RazerM](https://github.com/RazerM)
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@amyreese](https://github.com/amyreese)
- [@ntbre](https://github.com/ntBre)
- [@MichaReiser](https://github.com/MichaReiser)
## 0.13.3
Released on 2025-10-02.
### Preview features
- Display diffs for `ruff format --check` and add support for different output formats ([#20443](https://github.com/astral-sh/ruff/pull/20443))
- \[`pyflakes`\] Handle some common submodule import situations for `unused-import` (`F401`) ([#20200](https://github.com/astral-sh/ruff/pull/20200))
- \[`ruff`\] Do not flag `%r` + `repr()` combinations (`RUF065`) ([#20600](https://github.com/astral-sh/ruff/pull/20600))
### Bug fixes
- \[`cli`\] Add conflict between `--add-noqa` and `--diff` options ([#20642](https://github.com/astral-sh/ruff/pull/20642))
- \[`pylint`\] Exempt required imports from `PLR0402` ([#20381](https://github.com/astral-sh/ruff/pull/20381))
- \[`pylint`\] Fix missing `max-nested-blocks` in settings display ([#20574](https://github.com/astral-sh/ruff/pull/20574))
- \[`pyupgrade`\] Prevent infinite loop with `I002` and `UP026` ([#20634](https://github.com/astral-sh/ruff/pull/20634))
### Rule changes
- \[`flake8-simplify`\] Improve help message clarity (`SIM105`) ([#20548](https://github.com/astral-sh/ruff/pull/20548))
### Documentation
- Add the *The Basics* title back to CONTRIBUTING.md ([#20624](https://github.com/astral-sh/ruff/pull/20624))
- Fixed documentation for try_consider_else ([#20587](https://github.com/astral-sh/ruff/pull/20587))
- \[`isort`\] Clarify dependency between `order-by-type` and `case-sensitive` settings ([#20559](https://github.com/astral-sh/ruff/pull/20559))
- \[`pylint`\] Clarify fix safety to include left-hand hashability (`PLR6201`) ([#20518](https://github.com/astral-sh/ruff/pull/20518))
### Other changes
- \[`playground`\] Fix quick fixes for empty ranges in playground ([#20599](https://github.com/astral-sh/ruff/pull/20599))
### Contributors
- [@TaKO8Ki](https://github.com/TaKO8Ki)
- [@ntBre](https://github.com/ntBre)
- [@dylwil3](https://github.com/dylwil3)
- [@MichaReiser](https://github.com/MichaReiser)
- [@danparizher](https://github.com/danparizher)
- [@LilMonk](https://github.com/LilMonk)
- [@mgiovani](https://github.com/mgiovani)
- [@IDrokin117](https://github.com/IDrokin117)

View File

@@ -24,3 +24,20 @@ ignore-interior-mutability = [
# The expression is read-only.
"ruff_python_ast::hashable::HashableExpr",
]
disallowed-methods = [
{ path = "std::env::var", reason = "Use System::env_var instead in ty crates" },
{ path = "std::env::current_dir", reason = "Use System::current_directory instead in ty crates" },
{ path = "std::fs::read_to_string", reason = "Use System::read_to_string instead in ty crates" },
{ path = "std::fs::metadata", reason = "Use System::path_metadata instead in ty crates" },
{ path = "std::fs::canonicalize", reason = "Use System::canonicalize_path instead in ty crates" },
{ path = "dunce::canonicalize", reason = "Use System::canonicalize_path instead in ty crates" },
{ path = "std::fs::read_dir", reason = "Use System::read_directory instead in ty crates" },
{ path = "std::fs::write", reason = "Use WritableSystem::write_file instead in ty crates" },
{ path = "std::fs::create_dir_all", reason = "Use WritableSystem::create_directory_all instead in ty crates" },
{ path = "std::fs::File::create_new", reason = "Use WritableSystem::create_new_file instead in ty crates" },
# Path methods that have System trait equivalents
{ path = "std::path::Path::exists", reason = "Use System::path_exists instead in ty crates" },
{ path = "std::path::Path::is_dir", reason = "Use System::is_directory instead in ty crates" },
{ path = "std::path::Path::is_file", reason = "Use System::is_file instead in ty crates" },
]

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.12.10"
version = "0.14.6"
publish = true
authors = { workspace = true }
edition = { workspace = true }
@@ -72,6 +72,7 @@ dunce = { workspace = true }
indoc = { workspace = true }
insta = { workspace = true, features = ["filters", "json"] }
insta-cmd = { workspace = true }
ruff_python_trivia = { workspace = true }
tempfile = { workspace = true }
test-case = { workspace = true }

View File

@@ -7,6 +7,8 @@ use std::sync::Arc;
use crate::commands::completions::config::{OptionString, OptionStringParser};
use anyhow::bail;
use clap::builder::Styles;
use clap::builder::styling::{AnsiColor, Effects};
use clap::builder::{TypedValueParser, ValueParserFactory};
use clap::{Parser, Subcommand, command};
use colored::Colorize;
@@ -78,6 +80,13 @@ impl GlobalConfigArgs {
}
}
// Configures Clap v3-style help menu colors
const STYLES: Styles = Styles::styled()
.header(AnsiColor::Green.on_default().effects(Effects::BOLD))
.usage(AnsiColor::Green.on_default().effects(Effects::BOLD))
.literal(AnsiColor::Cyan.on_default().effects(Effects::BOLD))
.placeholder(AnsiColor::Cyan.on_default());
#[derive(Debug, Parser)]
#[command(
author,
@@ -86,6 +95,7 @@ impl GlobalConfigArgs {
after_help = "For help with a specific command, see: `ruff help <command>`."
)]
#[command(version)]
#[command(styles = STYLES)]
pub struct Args {
#[command(subcommand)]
pub(crate) command: Command,
@@ -157,6 +167,7 @@ pub enum AnalyzeCommand {
}
#[derive(Clone, Debug, clap::Parser)]
#[expect(clippy::struct_excessive_bools)]
pub struct AnalyzeGraphCommand {
/// List of files or directories to include.
#[clap(help = "List of files or directories to include [default: .]")]
@@ -183,6 +194,12 @@ pub struct AnalyzeGraphCommand {
/// Path to a virtual environment to use for resolving additional dependencies
#[arg(long)]
python: Option<PathBuf>,
/// Include imports that are only used for type checking (i.e., imports within `if TYPE_CHECKING:` blocks).
/// Use `--no-type-checking-imports` to exclude imports that are only used for type checking.
#[arg(long, overrides_with("no_type_checking_imports"))]
type_checking_imports: bool,
#[arg(long, overrides_with("type_checking_imports"), hide = true)]
no_type_checking_imports: bool,
}
// The `Parser` derive is for ruff_dev, for ruff `Args` would be sufficient
@@ -405,8 +422,13 @@ pub struct CheckCommand {
)]
pub statistics: bool,
/// Enable automatic additions of `noqa` directives to failing lines.
/// Optionally provide a reason to append after the codes.
#[arg(
long,
value_name = "REASON",
default_missing_value = "",
num_args = 0..=1,
require_equals = true,
// conflicts_with = "add_noqa",
conflicts_with = "show_files",
conflicts_with = "show_settings",
@@ -416,8 +438,9 @@ pub struct CheckCommand {
conflicts_with = "stdin_filename",
conflicts_with = "watch",
conflicts_with = "fix",
conflicts_with = "diff",
)]
pub add_noqa: bool,
pub add_noqa: Option<String>,
/// See the files Ruff will be run against with the current settings.
#[arg(
long,
@@ -537,6 +560,14 @@ pub struct FormatCommand {
/// Exit with a non-zero status code if any files were modified via format, even if all files were formatted successfully.
#[arg(long, help_heading = "Miscellaneous", alias = "exit-non-zero-on-fix")]
pub exit_non_zero_on_format: bool,
/// Output serialization format for violations, when used with `--check`.
/// The default serialization format is "full".
///
/// Note that this option is currently only respected in preview mode. A warning will be emitted
/// if this flag is used on stable.
#[arg(long, value_enum, env = "RUFF_OUTPUT_FORMAT")]
pub output_format: Option<OutputFormat>,
}
#[derive(Copy, Clone, Debug, clap::Parser)]
@@ -784,6 +815,7 @@ impl FormatCommand {
target_version: self.target_version.map(ast::PythonVersion::from),
cache_dir: self.cache_dir,
extension: self.extension,
output_format: self.output_format,
..ExplicitConfigOverrides::default()
};
@@ -814,6 +846,10 @@ impl AnalyzeGraphCommand {
string_imports_min_dots: self.min_dots,
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
target_version: self.target_version.map(ast::PythonVersion::from),
type_checking_imports: resolve_bool_arg(
self.type_checking_imports,
self.no_type_checking_imports,
),
..ExplicitConfigOverrides::default()
};
@@ -1037,7 +1073,7 @@ Possible choices:
/// etc.).
#[expect(clippy::struct_excessive_bools)]
pub struct CheckArguments {
pub add_noqa: bool,
pub add_noqa: Option<String>,
pub diff: bool,
pub exit_non_zero_on_fix: bool,
pub exit_zero: bool,
@@ -1310,6 +1346,7 @@ struct ExplicitConfigOverrides {
extension: Option<Vec<ExtensionPair>>,
detect_string_imports: Option<bool>,
string_imports_min_dots: Option<usize>,
type_checking_imports: Option<bool>,
}
impl ConfigurationTransformer for ExplicitConfigOverrides {
@@ -1400,6 +1437,9 @@ impl ConfigurationTransformer for ExplicitConfigOverrides {
if let Some(string_imports_min_dots) = &self.string_imports_min_dots {
config.analyze.string_imports_min_dots = Some(*string_imports_min_dots);
}
if let Some(type_checking_imports) = &self.type_checking_imports {
config.analyze.type_checking_imports = Some(*type_checking_imports);
}
config
}

View File

@@ -21,6 +21,7 @@ pub(crate) fn add_noqa(
files: &[PathBuf],
pyproject_config: &PyprojectConfig,
config_arguments: &ConfigArguments,
reason: Option<&str>,
) -> Result<usize> {
// Collect all the files to check.
let start = Instant::now();
@@ -76,7 +77,14 @@ pub(crate) fn add_noqa(
return None;
}
};
match add_noqa_to_path(path, package, &source_kind, source_type, &settings.linter) {
match add_noqa_to_path(
path,
package,
&source_kind,
source_type,
&settings.linter,
reason,
) {
Ok(count) => Some(count),
Err(e) => {
error!("Failed to add noqa to {}: {e}", path.display());

View File

@@ -7,6 +7,7 @@ use path_absolutize::CWD;
use ruff_db::system::{SystemPath, SystemPathBuf};
use ruff_graph::{Direction, ImportMap, ModuleDb, ModuleImports};
use ruff_linter::package::PackageRoot;
use ruff_linter::source_kind::SourceKind;
use ruff_linter::{warn_user, warn_user_once};
use ruff_python_ast::{PySourceType, SourceType};
use ruff_workspace::resolver::{ResolvedFile, match_exclusion, python_files_in_path};
@@ -104,6 +105,7 @@ pub(crate) fn analyze_graph(
let settings = resolver.resolve(path);
let string_imports = settings.analyze.string_imports;
let include_dependencies = settings.analyze.include_dependencies.get(path).cloned();
let type_checking_imports = settings.analyze.type_checking_imports;
// Skip excluded files.
if (settings.file_resolver.force_exclude || !resolved_file.is_root())
@@ -127,10 +129,6 @@ pub(crate) fn analyze_graph(
},
Some(language) => PySourceType::from(language),
};
if matches!(source_type, PySourceType::Ipynb) {
debug!("Ignoring Jupyter notebook: {}", path.display());
continue;
}
// Convert to system paths.
let Ok(package) = package.map(SystemPathBuf::from_path_buf).transpose() else {
@@ -147,13 +145,35 @@ pub(crate) fn analyze_graph(
let root = root.clone();
let result = inner_result.clone();
scope.spawn(move |_| {
// Extract source code (handles both .py and .ipynb files)
let source_kind = match SourceKind::from_path(path.as_std_path(), source_type) {
Ok(Some(source_kind)) => source_kind,
Ok(None) => {
debug!("Skipping non-Python notebook: {path}");
return;
}
Err(err) => {
warn!("Failed to read source for {path}: {err}");
return;
}
};
let source_code = source_kind.source_code();
// Identify any imports via static analysis.
let mut imports =
ModuleImports::detect(&db, &path, package.as_deref(), string_imports)
.unwrap_or_else(|err| {
warn!("Failed to generate import map for {path}: {err}");
ModuleImports::default()
});
let mut imports = ModuleImports::detect(
&db,
source_code,
source_type,
&path,
package.as_deref(),
string_imports,
type_checking_imports,
)
.unwrap_or_else(|err| {
warn!("Failed to generate import map for {path}: {err}");
ModuleImports::default()
});
debug!("Discovered {} imports for {}", imports.len(), path);

View File

@@ -6,9 +6,10 @@ use std::time::Instant;
use anyhow::Result;
use colored::Colorize;
use ignore::Error;
use log::{debug, error, warn};
use log::{debug, warn};
#[cfg(not(target_family = "wasm"))]
use rayon::prelude::*;
use ruff_linter::message::create_panic_diagnostic;
use rustc_hash::FxHashMap;
use ruff_db::diagnostic::Diagnostic;
@@ -193,21 +194,8 @@ fn lint_path(
match result {
Ok(inner) => inner,
Err(error) => {
let message = r"This indicates a bug in Ruff. If you could open an issue at:
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
...with the relevant file contents, the `pyproject.toml` settings, and the following stack trace, we'd be very appreciative!
";
error!(
"{}{}{} {message}\n{error}",
"Panicked while linting ".bold(),
fs::relativize_path(path).bold(),
":".bold()
);
Ok(Diagnostics::default())
let diagnostic = create_panic_diagnostic(&error, Some(path));
Ok(Diagnostics::new(vec![diagnostic], FxHashMap::default()))
}
}
}
@@ -222,7 +210,8 @@ mod test {
use rustc_hash::FxHashMap;
use tempfile::TempDir;
use ruff_linter::message::{Emitter, EmitterContext, TextEmitter};
use ruff_db::diagnostic::{DiagnosticFormat, DisplayDiagnosticConfig, DisplayDiagnostics};
use ruff_linter::message::EmitterContext;
use ruff_linter::registry::Rule;
use ruff_linter::settings::types::UnsafeFixes;
use ruff_linter::settings::{LinterSettings, flags};
@@ -275,19 +264,16 @@ mod test {
UnsafeFixes::Enabled,
)
.unwrap();
let mut output = Vec::new();
TextEmitter::default()
.with_show_fix_status(true)
.with_color(false)
.emit(
&mut output,
&diagnostics.inner,
&EmitterContext::new(&FxHashMap::default()),
)
.unwrap();
let messages = String::from_utf8(output).unwrap();
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::Concise)
.hide_severity(true);
let messages = DisplayDiagnostics::new(
&EmitterContext::new(&FxHashMap::default()),
&config,
&diagnostics.inner,
)
.to_string();
insta::with_settings!({
omit_expression => true,

View File

@@ -11,13 +11,19 @@ use itertools::Itertools;
use log::{error, warn};
use rayon::iter::Either::{Left, Right};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use ruff_db::diagnostic::{
Annotation, Diagnostic, DiagnosticId, DisplayDiagnosticConfig, Severity, Span,
};
use ruff_linter::message::{EmitterContext, create_panic_diagnostic, render_diagnostics};
use ruff_linter::settings::types::OutputFormat;
use ruff_notebook::NotebookIndex;
use ruff_python_parser::ParseError;
use rustc_hash::FxHashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use thiserror::Error;
use tracing::debug;
use ruff_db::panic::{PanicError, catch_unwind};
use ruff_diagnostics::SourceMap;
use ruff_diagnostics::{Edit, Fix, SourceMap};
use ruff_linter::fs;
use ruff_linter::logging::{DisplayParseError, LogLevel};
use ruff_linter::package::PackageRoot;
@@ -27,14 +33,15 @@ use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_linter::warn_user_once;
use ruff_python_ast::{PySourceType, SourceType};
use ruff_python_formatter::{FormatModuleError, QuoteStyle, format_module_source, format_range};
use ruff_source_file::LineIndex;
use ruff_source_file::{LineIndex, LineRanges, OneIndexed, SourceFileBuilder};
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_workspace::FormatterSettings;
use ruff_workspace::resolver::{ResolvedFile, Resolver, match_exclusion, python_files_in_path};
use ruff_workspace::resolver::{
PyprojectConfig, ResolvedFile, Resolver, match_exclusion, python_files_in_path,
};
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
use crate::cache::{Cache, FileCacheKey, PackageCacheMap, PackageCaches};
use crate::resolve::resolve;
use crate::{ExitStatus, resolve_default_files};
#[derive(Debug, Copy, Clone, is_macro::Is)]
@@ -63,11 +70,14 @@ impl FormatMode {
pub(crate) fn format(
cli: FormatArguments,
config_arguments: &ConfigArguments,
pyproject_config: &PyprojectConfig,
) -> Result<ExitStatus> {
let pyproject_config = resolve(config_arguments, cli.stdin_filename.as_deref())?;
let mode = FormatMode::from_cli(&cli);
let files = resolve_default_files(cli.files, false);
let (paths, resolver) = python_files_in_path(&files, &pyproject_config, config_arguments)?;
let (paths, resolver) = python_files_in_path(&files, pyproject_config, config_arguments)?;
let output_format = pyproject_config.settings.output_format;
let preview = pyproject_config.settings.formatter.preview;
if paths.is_empty() {
warn_user_once!("No Python files found under the given path(s)");
@@ -184,17 +194,26 @@ pub(crate) fn format(
caches.persist()?;
// Report on any errors.
errors.sort_unstable_by(|a, b| a.path().cmp(&b.path()));
//
// We only convert errors to `Diagnostic`s in `Check` mode with preview enabled, otherwise we
// fall back on printing simple messages.
if !(preview.is_enabled() && mode.is_check()) {
errors.sort_unstable_by(|a, b| a.path().cmp(&b.path()));
for error in &errors {
error!("{error}");
for error in &errors {
error!("{error}");
}
}
let results = FormatResults::new(results.as_slice(), mode);
match mode {
FormatMode::Write => {}
FormatMode::Check => {
results.write_changed(&mut stdout().lock())?;
if preview.is_enabled() {
results.write_changed_preview(&mut stdout().lock(), output_format, &errors)?;
} else {
results.write_changed(&mut stdout().lock())?;
}
}
FormatMode::Diff => {
results.write_diff(&mut stdout().lock())?;
@@ -206,7 +225,7 @@ pub(crate) fn format(
if mode.is_diff() {
// Allow piping the diff to e.g. a file by writing the summary to stderr
results.write_summary(&mut stderr().lock())?;
} else {
} else if !preview.is_enabled() || output_format.is_human_readable() {
results.write_summary(&mut stdout().lock())?;
}
}
@@ -295,8 +314,7 @@ pub(crate) fn format_path(
FormatResult::Formatted
}
FormatMode::Check => FormatResult::Formatted,
FormatMode::Diff => FormatResult::Diff {
FormatMode::Check | FormatMode::Diff => FormatResult::Diff {
unformatted,
formatted,
},
@@ -329,7 +347,7 @@ pub(crate) enum FormattedSource {
impl From<FormattedSource> for FormatResult {
fn from(value: FormattedSource) -> Self {
match value {
FormattedSource::Formatted(_) => FormatResult::Formatted,
FormattedSource::Formatted { .. } => FormatResult::Formatted,
FormattedSource::Unchanged => FormatResult::Unchanged,
}
}
@@ -352,7 +370,7 @@ pub(crate) fn format_source(
let line_index = LineIndex::from_source_text(unformatted);
let byte_range = range.to_text_range(unformatted, &line_index);
format_range(unformatted, byte_range, options).map(|formatted_range| {
let mut formatted = unformatted.to_string();
let mut formatted = unformatted.clone();
formatted.replace_range(
std::ops::Range::<usize>::from(formatted_range.source_range()),
formatted_range.as_code(),
@@ -477,10 +495,10 @@ pub(crate) fn format_source(
/// The result of an individual formatting operation.
#[derive(Debug, Clone, is_macro::Is)]
pub(crate) enum FormatResult {
/// The file was formatted.
/// The file was formatted and written back to disk.
Formatted,
/// The file was formatted, [`SourceKind`] contains the formatted code
/// The file needs to be formatted, as the `formatted` and `unformatted` contents differ.
Diff {
unformatted: SourceKind,
formatted: SourceKind,
@@ -552,7 +570,7 @@ impl<'a> FormatResults<'a> {
.results
.iter()
.filter_map(|result| {
if result.result.is_formatted() {
if result.result.is_diff() {
Some(result.path.as_path())
} else {
None
@@ -566,6 +584,30 @@ impl<'a> FormatResults<'a> {
Ok(())
}
/// Write a list of the files that would be changed and any errors to the given writer.
fn write_changed_preview(
&self,
f: &mut impl Write,
output_format: OutputFormat,
errors: &[FormatCommandError],
) -> io::Result<()> {
let mut notebook_index = FxHashMap::default();
let diagnostics: Vec<_> = errors
.iter()
.map(Diagnostic::from)
.chain(self.to_diagnostics(&mut notebook_index))
.sorted_unstable_by(Diagnostic::ruff_start_ordering)
.collect();
let context = EmitterContext::new(&notebook_index);
let config = DisplayDiagnosticConfig::default()
.hide_severity(true)
.show_fix_diff(true)
.color(!cfg!(test) && colored::control::SHOULD_COLORIZE.should_colorize());
render_diagnostics(f, output_format, config, &context, &diagnostics)
}
/// Write a summary of the formatting results to the given writer.
fn write_summary(&self, f: &mut impl Write) -> io::Result<()> {
// Compute the number of changed and unchanged files.
@@ -628,6 +670,155 @@ impl<'a> FormatResults<'a> {
Ok(())
}
}
/// Convert formatted files into [`Diagnostic`]s.
fn to_diagnostics(
&self,
notebook_index: &mut FxHashMap<String, NotebookIndex>,
) -> impl Iterator<Item = Diagnostic> {
/// The number of unmodified context lines rendered in diffs.
///
/// Note that this should be kept in sync with the argument to `TextDiff::grouped_ops` in
/// the diff rendering in `ruff_db` (currently 3). The `similar` crate uses two times that
/// argument as a cutoff for rendering unmodified lines.
const CONTEXT_LINES: u32 = 6;
self.results.iter().filter_map(|result| {
let (unformatted, formatted) = match &result.result {
FormatResult::Skipped | FormatResult::Unchanged => return None,
FormatResult::Diff {
unformatted,
formatted,
} => (unformatted, formatted),
FormatResult::Formatted => {
debug_assert!(
false,
"Expected `FormatResult::Diff` for changed files in check mode"
);
return None;
}
};
let mut diagnostic = Diagnostic::new(
DiagnosticId::Unformatted,
Severity::Error,
"File would be reformatted",
);
// Locate the first and last characters that differ to use as the diagnostic
// range and to narrow the `Edit` range.
let modified_range = ModifiedRange::new(unformatted, formatted);
let path = result.path.to_string_lossy();
// For scripts, this is a single `Edit` using the `ModifiedRange` above, but notebook
// edits must be split by cell in order to render them as diffs.
//
// We also attempt to estimate the line number width for aligning the
// annotate-snippets header. This is only an estimate because we don't actually know
// if the maximum line number present in the document will be rendered as part of
// the diff, either as a changed line or as an unchanged context line. For
// notebooks, we refine our estimate by checking the number of lines in each cell
// individually, otherwise we could use `formatted.source_code().count_lines(...)`
// in both cases.
let (fix, line_count) = if let SourceKind::IpyNotebook(formatted) = formatted
&& let SourceKind::IpyNotebook(unformatted) = unformatted
{
notebook_index.insert(path.to_string(), unformatted.index().clone());
let mut edits = formatted
.cell_offsets()
.ranges()
.zip(unformatted.cell_offsets().ranges())
.filter_map(|(formatted_range, unformatted_range)| {
// Filter out cells that weren't modified. We use `intersect` instead of
// `contains_range` because the full modified range might start or end in
// the middle of a cell:
//
// ```
// | cell 1 | cell 2 | cell 3 |
// |----------------| modified range
// ```
//
// The intersection will be `Some` for all three cells in this case.
if modified_range
.unformatted
.intersect(unformatted_range)
.is_some()
{
let formatted = &formatted.source_code()[formatted_range];
let edit = if formatted.is_empty() {
Edit::range_deletion(unformatted_range)
} else {
Edit::range_replacement(formatted.to_string(), unformatted_range)
};
Some(edit)
} else {
None
}
});
let fix = Fix::safe_edits(
edits
.next()
.expect("Formatted files must have at least one edit"),
edits,
);
let source = formatted.source_code();
let line_count = formatted
.cell_offsets()
.ranges()
.filter_map(|range| {
if modified_range.formatted.contains_range(range) {
Some(source.count_lines(range))
} else {
None
}
})
.max()
.unwrap_or_default();
(fix, line_count)
} else {
let formatted_code = &formatted.source_code()[modified_range.formatted];
let edit = if formatted_code.is_empty() {
Edit::range_deletion(modified_range.unformatted)
} else {
Edit::range_replacement(formatted_code.to_string(), modified_range.unformatted)
};
let fix = Fix::safe_edit(edit);
let line_count = formatted
.source_code()
.count_lines(TextRange::up_to(modified_range.formatted.end()));
(fix, line_count)
};
let source_file = SourceFileBuilder::new(path, unformatted.source_code()).finish();
let span = Span::from(source_file).with_range(modified_range.unformatted);
let mut annotation = Annotation::primary(span);
annotation.hide_snippet(true);
diagnostic.annotate(annotation);
diagnostic.set_fix(fix);
// TODO(brent) this offset is a hack to get the header of the diagnostic message, which
// is rendered by our fork of `annotate-snippets`, to align with our manually-rendered
// diff. `annotate-snippets` computes the alignment of the arrow in the header based on
// the maximum line number width in its rendered snippet. However, we don't have a
// reasonable range to underline in an annotation, so we don't send `annotate-snippets`
// a snippet to measure. If we commit to staying on our fork, a more robust way of
// handling this would be to move the diff rendering in
// `ruff_db::diagnostic::render::full` into `annotate-snippets`, likely as another
// `DisplayLine` variant and update the `lineno_width` calculation in
// `DisplayList::fmt`. That would handle this offset "automatically."
let line_count = (line_count + CONTEXT_LINES).min(
formatted
.source_code()
.count_lines(TextRange::up_to(formatted.source_code().text_len())),
);
let lines = OneIndexed::new(line_count as usize).unwrap_or_default();
diagnostic.set_header_offset(lines.digits().get());
Some(diagnostic)
})
}
}
/// An error that can occur while formatting a set of files.
@@ -639,7 +830,6 @@ pub(crate) enum FormatCommandError {
Read(Option<PathBuf>, SourceError),
Format(Option<PathBuf>, FormatModuleError),
Write(Option<PathBuf>, SourceError),
Diff(Option<PathBuf>, io::Error),
RangeFormatNotebook(Option<PathBuf>),
}
@@ -658,12 +848,53 @@ impl FormatCommandError {
| Self::Read(path, _)
| Self::Format(path, _)
| Self::Write(path, _)
| Self::Diff(path, _)
| Self::RangeFormatNotebook(path) => path.as_deref(),
}
}
}
impl From<&FormatCommandError> for Diagnostic {
fn from(error: &FormatCommandError) -> Self {
let annotation = error.path().map(|path| {
let file = SourceFileBuilder::new(path.to_string_lossy(), "").finish();
let span = Span::from(file);
let mut annotation = Annotation::primary(span);
annotation.hide_snippet(true);
annotation
});
let mut diagnostic = match error {
FormatCommandError::Ignore(error) => {
Diagnostic::new(DiagnosticId::Io, Severity::Error, error)
}
FormatCommandError::Parse(display_parse_error) => Diagnostic::new(
DiagnosticId::InvalidSyntax,
Severity::Error,
&display_parse_error.error().error,
),
FormatCommandError::Panic(path, panic_error) => {
return create_panic_diagnostic(panic_error, path.as_deref());
}
FormatCommandError::Read(_, source_error)
| FormatCommandError::Write(_, source_error) => {
Diagnostic::new(DiagnosticId::Io, Severity::Error, source_error)
}
FormatCommandError::Format(_, format_module_error) => format_module_error.into(),
FormatCommandError::RangeFormatNotebook(_) => Diagnostic::new(
DiagnosticId::InvalidCliOption,
Severity::Error,
"Range formatting isn't supported for notebooks.",
),
};
if let Some(annotation) = annotation {
diagnostic.annotate(annotation);
}
diagnostic
}
}
impl Display for FormatCommandError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
@@ -731,23 +962,6 @@ impl Display for FormatCommandError {
write!(f, "{header} {err}", header = "Failed to format:".bold())
}
}
Self::Diff(path, err) => {
if let Some(path) = path {
write!(
f,
"{}{}{} {err}",
"Failed to generate diff for ".bold(),
fs::relativize_path(path).bold(),
":".bold()
)
} else {
write!(
f,
"{header} {err}",
header = "Failed to generate diff:".bold(),
)
}
}
Self::RangeFormatNotebook(path) => {
if let Some(path) = path {
write!(
@@ -792,6 +1006,54 @@ impl Display for FormatCommandError {
}
}
#[derive(Debug)]
struct ModifiedRange {
unformatted: TextRange,
formatted: TextRange,
}
impl ModifiedRange {
/// Determine the range that differs between `unformatted` and `formatted`.
///
/// If the two inputs are equal, the returned ranges will be empty.
fn new(unformatted: &SourceKind, formatted: &SourceKind) -> Self {
let unformatted = unformatted.source_code();
let formatted = formatted.source_code();
let mut prefix_length = TextSize::ZERO;
for (unformatted, formatted) in unformatted.chars().zip(formatted.chars()) {
if unformatted != formatted {
break;
}
prefix_length += unformatted.text_len();
}
// For the ends of the ranges, track the length of the common suffix and then subtract that
// from each total text length. Unlike for `start`, the character offsets are very unlikely
// to be equal, so they need to be treated separately.
let mut suffix_length = TextSize::ZERO;
for (old, new) in unformatted[prefix_length.to_usize()..]
.chars()
.rev()
.zip(formatted[prefix_length.to_usize()..].chars().rev())
{
if old != new {
break;
}
suffix_length += old.text_len();
}
let unformatted_range =
TextRange::new(prefix_length, unformatted.text_len() - suffix_length);
let formatted_range = TextRange::new(prefix_length, formatted.text_len() - suffix_length);
Self {
unformatted: unformatted_range,
formatted: formatted_range,
}
}
}
pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
// First, collect all rules that are incompatible regardless of the linter-specific settings.
let mut incompatible_rules = FxHashSet::default();
@@ -963,3 +1225,144 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
}
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::ops::Range;
use std::path::PathBuf;
use ignore::Error;
use insta::assert_snapshot;
use ruff_db::panic::catch_unwind;
use ruff_linter::logging::DisplayParseError;
use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_python_formatter::FormatModuleError;
use ruff_python_parser::{ParseError, ParseErrorType};
use ruff_text_size::{TextRange, TextSize};
use test_case::test_case;
use crate::commands::format::{FormatCommandError, FormatMode, FormatResults, ModifiedRange};
#[test]
fn error_diagnostics() -> anyhow::Result<()> {
let path = PathBuf::from("test.py");
let source_kind = SourceKind::Python("1".to_string());
let panic_error = catch_unwind(|| {
panic!("Test panic for FormatCommandError");
})
.unwrap_err();
let errors = [
FormatCommandError::Ignore(Error::WithPath {
path: path.clone(),
err: Box::new(Error::Io(io::Error::new(
io::ErrorKind::PermissionDenied,
"Permission denied",
))),
}),
FormatCommandError::Parse(DisplayParseError::from_source_kind(
ParseError {
error: ParseErrorType::UnexpectedIndentation,
location: TextRange::default(),
},
Some(path.clone()),
&source_kind,
)),
FormatCommandError::Panic(Some(path.clone()), Box::new(panic_error)),
FormatCommandError::Read(
Some(path.clone()),
SourceError::Io(io::Error::new(io::ErrorKind::NotFound, "File not found")),
),
FormatCommandError::Format(
Some(path.clone()),
FormatModuleError::ParseError(ParseError {
error: ParseErrorType::EmptySlice,
location: TextRange::default(),
}),
),
FormatCommandError::Write(
Some(path.clone()),
SourceError::Io(io::Error::new(
io::ErrorKind::PermissionDenied,
"Cannot write to file",
)),
),
FormatCommandError::RangeFormatNotebook(Some(path)),
];
let results = FormatResults::new(&[], FormatMode::Check);
let mut buf = Vec::new();
results.write_changed_preview(
&mut buf,
ruff_linter::settings::types::OutputFormat::Full,
&errors,
)?;
let mut settings = insta::Settings::clone_current();
settings.add_filter(r"(Panicked at) [^:]+:\d+:\d+", "$1 <location>");
let _s = settings.bind_to_scope();
assert_snapshot!(str::from_utf8(&buf)?, @r"
io: test.py: Permission denied
--> test.py:1:1
invalid-syntax: Unexpected indentation
--> test.py:1:1
io: File not found
--> test.py:1:1
internal-error: Expected index or slice expression
--> test.py:1:1
io: Cannot write to file
--> test.py:1:1
invalid-cli-option: Range formatting isn't supported for notebooks.
--> test.py:1:1
panic: Panicked at <location> when checking `test.py`: `Test panic for FormatCommandError`
--> test.py:1:1
info: This indicates a bug in Ruff.
info: If you could open an issue at https://github.com/astral-sh/ruff/issues/new?title=%5Bpanic%5D, we'd be very appreciative!
info: run with `RUST_BACKTRACE=1` environment variable to show the full backtrace information
");
Ok(())
}
#[test_case("abcdef", "abcXYdef", 3..3, 3..5; "insertion")]
#[test_case("abcXYdef", "abcdef", 3..5, 3..3; "deletion")]
#[test_case("abcXdef", "abcYdef", 3..4, 3..4; "modification")]
#[test_case("abc", "abcX", 3..3, 3..4; "strict_prefix")]
#[test_case("", "", 0..0, 0..0; "empty")]
#[test_case("abc", "abc", 3..3, 3..3; "equal")]
fn modified_range(
unformatted: &str,
formatted: &str,
expect_unformatted: Range<u32>,
expect_formatted: Range<u32>,
) {
let mr = ModifiedRange::new(
&SourceKind::Python(unformatted.to_string()),
&SourceKind::Python(formatted.to_string()),
);
assert_eq!(
mr.unformatted,
TextRange::new(
TextSize::new(expect_unformatted.start),
TextSize::new(expect_unformatted.end)
)
);
assert_eq!(
mr.formatted,
TextRange::new(
TextSize::new(expect_formatted.start),
TextSize::new(expect_formatted.end)
)
);
}
}

View File

@@ -4,10 +4,10 @@ use std::path::Path;
use anyhow::Result;
use log::error;
use ruff_linter::source_kind::SourceKind;
use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_python_ast::{PySourceType, SourceType};
use ruff_workspace::FormatterSettings;
use ruff_workspace::resolver::{Resolver, match_exclusion, python_file_at_path};
use ruff_workspace::resolver::{PyprojectConfig, Resolver, match_exclusion, python_file_at_path};
use crate::ExitStatus;
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
@@ -15,17 +15,15 @@ use crate::commands::format::{
FormatCommandError, FormatMode, FormatResult, FormattedSource, format_source,
warn_incompatible_formatter_settings,
};
use crate::resolve::resolve;
use crate::stdin::{parrot_stdin, read_from_stdin};
/// Run the formatter over a single file, read from `stdin`.
pub(crate) fn format_stdin(
cli: &FormatArguments,
config_arguments: &ConfigArguments,
pyproject_config: &PyprojectConfig,
) -> Result<ExitStatus> {
let pyproject_config = resolve(config_arguments, cli.stdin_filename.as_deref())?;
let mut resolver = Resolver::new(&pyproject_config);
let mut resolver = Resolver::new(pyproject_config);
warn_incompatible_formatter_settings(&resolver);
let mode = FormatMode::from_cli(cli);
@@ -124,7 +122,9 @@ fn format_source_code(
"{}",
source_kind.diff(formatted, path).unwrap()
)
.map_err(|err| FormatCommandError::Diff(path.map(Path::to_path_buf), err))?;
.map_err(|err| {
FormatCommandError::Write(path.map(Path::to_path_buf), SourceError::Io(err))
})?;
}
},
FormattedSource::Unchanged => {

View File

@@ -16,6 +16,8 @@ struct LinterInfo {
prefix: &'static str,
name: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
url: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
categories: Option<Vec<LinterCategoryInfo>>,
}
@@ -50,6 +52,7 @@ pub(crate) fn linter(format: HelpFormat) -> Result<()> {
.map(|linter_info| LinterInfo {
prefix: linter_info.common_prefix(),
name: linter_info.name(),
url: linter_info.url(),
categories: linter_info.upstream_categories().map(|cats| {
cats.iter()
.map(|c| LinterCategoryInfo {

View File

@@ -7,6 +7,7 @@ use serde::{Serialize, Serializer};
use strum::IntoEnumIterator;
use ruff_linter::FixAvailability;
use ruff_linter::codes::RuleGroup;
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
use crate::args::HelpFormat;
@@ -19,9 +20,12 @@ struct Explanation<'a> {
summary: &'a str,
message_formats: &'a [&'a str],
fix: String,
fix_availability: FixAvailability,
#[expect(clippy::struct_field_names)]
explanation: Option<&'a str>,
preview: bool,
status: RuleGroup,
source_location: SourceLocation,
}
impl<'a> Explanation<'a> {
@@ -36,8 +40,14 @@ impl<'a> Explanation<'a> {
summary: rule.message_formats()[0],
message_formats: rule.message_formats(),
fix,
fix_availability: rule.fixable(),
explanation: rule.explanation(),
preview: rule.is_preview(),
status: rule.group(),
source_location: SourceLocation {
file: rule.file(),
line: rule.line(),
},
}
}
}
@@ -122,3 +132,14 @@ pub(crate) fn rules(format: HelpFormat) -> Result<()> {
}
Ok(())
}
/// The location of the rule's implementation in the Ruff source tree, relative to the repository
/// root.
///
/// For most rules this will point to the `#[derive(ViolationMetadata)]` line above the rule's
/// struct.
#[derive(Serialize)]
struct SourceLocation {
file: &'static str,
line: u32,
}

View File

@@ -13,7 +13,6 @@ use log::{debug, warn};
use ruff_db::diagnostic::Diagnostic;
use ruff_linter::codes::Rule;
use ruff_linter::linter::{FixTable, FixerResult, LinterResult, ParseSource, lint_fix, lint_only};
use ruff_linter::message::create_syntax_error_diagnostic;
use ruff_linter::package::PackageRoot;
use ruff_linter::pyproject_toml::lint_pyproject_toml;
use ruff_linter::settings::types::UnsafeFixes;
@@ -103,11 +102,7 @@ impl Diagnostics {
let name = path.map_or_else(|| "-".into(), Path::to_string_lossy);
let dummy = SourceFileBuilder::new(name, "").finish();
Self::new(
vec![create_syntax_error_diagnostic(
dummy,
err,
TextRange::default(),
)],
vec![Diagnostic::invalid_syntax(dummy, err, TextRange::default())],
FxHashMap::default(),
)
}

View File

@@ -9,10 +9,11 @@ use std::sync::mpsc::channel;
use anyhow::Result;
use clap::CommandFactory;
use colored::Colorize;
use log::warn;
use log::{error, warn};
use notify::{RecursiveMode, Watcher, recommended_watcher};
use args::{GlobalConfigArgs, ServerCommand};
use ruff_db::diagnostic::{Diagnostic, Severity};
use ruff_linter::logging::{LogLevel, set_up_logging};
use ruff_linter::settings::flags::FixMode;
use ruff_linter::settings::types::OutputFormat;
@@ -204,12 +205,18 @@ pub fn run(
}
fn format(args: FormatCommand, global_options: GlobalConfigArgs) -> Result<ExitStatus> {
let cli_output_format_set = args.output_format.is_some();
let (cli, config_arguments) = args.partition(global_options)?;
let pyproject_config = resolve::resolve(&config_arguments, cli.stdin_filename.as_deref())?;
if cli_output_format_set && !pyproject_config.settings.formatter.preview.is_enabled() {
warn_user_once!(
"The --output-format flag for the formatter is unstable and requires preview mode to use."
);
}
if is_stdin(&cli.files, cli.stdin_filename.as_deref()) {
commands::format_stdin::format_stdin(&cli, &config_arguments)
commands::format_stdin::format_stdin(&cli, &config_arguments, &pyproject_config)
} else {
commands::format::format(cli, &config_arguments)
commands::format::format(cli, &config_arguments, &pyproject_config)
}
}
@@ -312,12 +319,20 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
warn_user!("Detected debug build without --no-cache.");
}
if cli.add_noqa {
if let Some(reason) = &cli.add_noqa {
if !fix_mode.is_generate() {
warn_user!("--fix is incompatible with --add-noqa.");
}
if reason.contains(['\n', '\r']) {
return Err(anyhow::anyhow!(
"--add-noqa <reason> cannot contain newline characters"
));
}
let reason_opt = (!reason.is_empty()).then_some(reason.as_str());
let modifications =
commands::add_noqa::add_noqa(&files, &pyproject_config, &config_arguments)?;
commands::add_noqa::add_noqa(&files, &pyproject_config, &config_arguments, reason_opt)?;
if modifications > 0 && config_arguments.log_level >= LogLevel::Default {
let s = if modifications == 1 { "" } else { "s" };
#[expect(clippy::print_stderr)]
@@ -444,6 +459,27 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
}
if !cli.exit_zero {
let max_severity = diagnostics
.inner
.iter()
.map(Diagnostic::severity)
.max()
.unwrap_or(Severity::Info);
if max_severity.is_fatal() {
// When a panic/fatal error is reported, prompt the user to open an issue on github.
// Diagnostics with severity `fatal` will be sorted to the bottom, and printing the
// message here instead of attaching it to the diagnostic ensures that we only print
// it once instead of repeating it for each diagnostic. Prints to stderr to prevent
// the message from being captured by tools parsing the normal output.
let message = "Panic during linting indicates a bug in Ruff. If you could open an issue at:
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
...with the relevant file contents, the `pyproject.toml` settings, and the stack trace above, we'd be very appreciative!
";
error!("{message}");
return Ok(ExitStatus::Error);
}
if cli.diff {
// If we're printing a diff, we always want to exit non-zero if there are
// any fixable violations (since we've printed the diff, but not applied the

View File

@@ -1,6 +1,7 @@
use std::io::Write;
use std::process::ExitCode;
use anyhow::Context;
use clap::Parser;
use colored::Colorize;
@@ -39,39 +40,46 @@ pub fn main() -> ExitCode {
}
let args = wild::args_os();
let args = argfile::expand_args_from(args, argfile::parse_fromfile, argfile::PREFIX).unwrap();
let args = match argfile::expand_args_from(args, argfile::parse_fromfile, argfile::PREFIX)
.context("Failed to read CLI arguments from files")
{
Ok(args) => args,
Err(err) => return report_error(&err),
};
let args = Args::parse_from(args);
match run(args) {
Ok(code) => code.into(),
Err(err) => {
{
// Exit "gracefully" on broken pipe errors.
//
// See: https://github.com/BurntSushi/ripgrep/blob/bf63fe8f258afc09bae6caa48f0ae35eaf115005/crates/core/main.rs#L47C1-L61C14
for cause in err.chain() {
if let Some(ioerr) = cause.downcast_ref::<std::io::Error>() {
if ioerr.kind() == std::io::ErrorKind::BrokenPipe {
return ExitCode::from(0);
}
}
}
// Use `writeln` instead of `eprintln` to avoid panicking when the stderr pipe is broken.
let mut stderr = std::io::stderr().lock();
// This communicates that this isn't a linter error but ruff itself hard-errored for
// some reason (e.g. failed to resolve the configuration)
writeln!(stderr, "{}", "ruff failed".red().bold()).ok();
// Currently we generally only see one error, but e.g. with io errors when resolving
// the configuration it is help to chain errors ("resolving configuration failed" ->
// "failed to read file: subdir/pyproject.toml")
for cause in err.chain() {
writeln!(stderr, " {} {cause}", "Cause:".bold()).ok();
}
}
ExitStatus::Error.into()
}
Err(err) => report_error(&err),
}
}
fn report_error(err: &anyhow::Error) -> ExitCode {
{
// Exit "gracefully" on broken pipe errors.
//
// See: https://github.com/BurntSushi/ripgrep/blob/bf63fe8f258afc09bae6caa48f0ae35eaf115005/crates/core/main.rs#L47C1-L61C14
for cause in err.chain() {
if let Some(ioerr) = cause.downcast_ref::<std::io::Error>() {
if ioerr.kind() == std::io::ErrorKind::BrokenPipe {
return ExitCode::from(0);
}
}
}
// Use `writeln` instead of `eprintln` to avoid panicking when the stderr pipe is broken.
let mut stderr = std::io::stderr().lock();
// This communicates that this isn't a linter error but ruff itself hard-errored for
// some reason (e.g. failed to resolve the configuration)
writeln!(stderr, "{}", "ruff failed".red().bold()).ok();
// Currently we generally only see one error, but e.g. with io errors when resolving
// the configuration it is help to chain errors ("resolving configuration failed" ->
// "failed to read file: subdir/pyproject.toml")
for cause in err.chain() {
writeln!(stderr, " {} {cause}", "Cause:".bold()).ok();
}
}
ExitStatus::Error.into()
}

View File

@@ -9,15 +9,10 @@ use itertools::{Itertools, iterate};
use ruff_linter::linter::FixTable;
use serde::Serialize;
use ruff_db::diagnostic::{
Diagnostic, DiagnosticFormat, DisplayDiagnosticConfig, DisplayDiagnostics, SecondaryCode,
};
use ruff_db::diagnostic::{Diagnostic, DisplayDiagnosticConfig, SecondaryCode};
use ruff_linter::fs::relativize_path;
use ruff_linter::logging::LogLevel;
use ruff_linter::message::{
Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter, SarifEmitter,
TextEmitter,
};
use ruff_linter::message::{EmitterContext, render_diagnostics};
use ruff_linter::notify_user;
use ruff_linter::settings::flags::{self};
use ruff_linter::settings::types::{OutputFormat, UnsafeFixes};
@@ -31,8 +26,6 @@ bitflags! {
const SHOW_VIOLATIONS = 1 << 0;
/// Whether to show a summary of the fixed violations when emitting diagnostics.
const SHOW_FIX_SUMMARY = 1 << 1;
/// Whether to show a diff of each fixed violation when emitting diagnostics.
const SHOW_FIX_DIFF = 1 << 2;
}
}
@@ -229,92 +222,28 @@ impl Printer {
let context = EmitterContext::new(&diagnostics.notebook_indexes);
let fixables = FixableStatistics::try_from(diagnostics, self.unsafe_fixes);
match self.format {
OutputFormat::Json => {
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::Json)
.preview(preview);
let value = DisplayDiagnostics::new(&context, &config, &diagnostics.inner);
write!(writer, "{value}")?;
}
OutputFormat::Rdjson => {
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::Rdjson)
.preview(preview);
let value = DisplayDiagnostics::new(&context, &config, &diagnostics.inner);
write!(writer, "{value}")?;
}
OutputFormat::JsonLines => {
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::JsonLines)
.preview(preview);
let value = DisplayDiagnostics::new(&context, &config, &diagnostics.inner);
write!(writer, "{value}")?;
}
OutputFormat::Junit => {
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::Junit)
.preview(preview);
let value = DisplayDiagnostics::new(&context, &config, &diagnostics.inner);
write!(writer, "{value}")?;
}
OutputFormat::Concise | OutputFormat::Full => {
TextEmitter::default()
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
.with_show_fix_diff(self.flags.intersects(Flags::SHOW_FIX_DIFF))
.with_show_source(self.format == OutputFormat::Full)
.with_unsafe_fixes(self.unsafe_fixes)
.with_preview(preview)
.emit(writer, &diagnostics.inner, &context)?;
let config = DisplayDiagnosticConfig::default()
.preview(preview)
.hide_severity(true)
.color(!cfg!(test) && colored::control::SHOULD_COLORIZE.should_colorize())
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
.with_fix_applicability(self.unsafe_fixes.required_applicability())
.show_fix_diff(preview);
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
if !diagnostics.fixed.is_empty() {
writeln!(writer)?;
print_fix_summary(writer, &diagnostics.fixed)?;
writeln!(writer)?;
}
render_diagnostics(writer, self.format, config, &context, &diagnostics.inner)?;
if matches!(
self.format,
OutputFormat::Full | OutputFormat::Concise | OutputFormat::Grouped
) {
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
if !diagnostics.fixed.is_empty() {
writeln!(writer)?;
print_fix_summary(writer, &diagnostics.fixed)?;
writeln!(writer)?;
}
self.write_summary_text(writer, diagnostics)?;
}
OutputFormat::Grouped => {
GroupedEmitter::default()
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
.with_unsafe_fixes(self.unsafe_fixes)
.emit(writer, &diagnostics.inner, &context)?;
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
if !diagnostics.fixed.is_empty() {
writeln!(writer)?;
print_fix_summary(writer, &diagnostics.fixed)?;
writeln!(writer)?;
}
}
self.write_summary_text(writer, diagnostics)?;
}
OutputFormat::Github => {
GithubEmitter.emit(writer, &diagnostics.inner, &context)?;
}
OutputFormat::Gitlab => {
GitlabEmitter::default().emit(writer, &diagnostics.inner, &context)?;
}
OutputFormat::Pylint => {
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::Pylint)
.preview(preview);
let value = DisplayDiagnostics::new(&context, &config, &diagnostics.inner);
write!(writer, "{value}")?;
}
OutputFormat::Azure => {
let config = DisplayDiagnosticConfig::default()
.format(DiagnosticFormat::Azure)
.preview(preview);
let value = DisplayDiagnostics::new(&context, &config, &diagnostics.inner);
write!(writer, "{value}")?;
}
OutputFormat::Sarif => {
SarifEmitter.emit(writer, &diagnostics.inner, &context)?;
}
self.write_summary_text(writer, diagnostics)?;
}
writer.flush()?;
@@ -458,11 +387,19 @@ impl Printer {
}
let context = EmitterContext::new(&diagnostics.notebook_indexes);
TextEmitter::default()
let format = if preview {
self.format
} else {
OutputFormat::Concise
};
let config = DisplayDiagnosticConfig::default()
.preview(preview)
.hide_severity(true)
.color(!cfg!(test) && colored::control::SHOULD_COLORIZE.should_colorize())
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
.with_show_source(preview)
.with_unsafe_fixes(self.unsafe_fixes)
.emit(writer, &diagnostics.inner, &context)?;
.with_fix_applicability(self.unsafe_fixes.required_applicability())
.show_fix_diff(preview);
render_diagnostics(writer, format, config, &context, &diagnostics.inner)?;
}
writer.flush()?;

View File

@@ -246,6 +246,59 @@ fn string_detection() -> Result<()> {
Ok(())
}
#[test]
fn string_detection_from_config() -> Result<()> {
let tempdir = TempDir::new()?;
let root = ChildPath::new(tempdir.path());
// Configure string import detection with a lower min-dots via ruff.toml
root.child("ruff.toml").write_str(indoc::indoc! {r#"
[analyze]
detect-string-imports = true
string-imports-min-dots = 1
"#})?;
root.child("ruff").child("__init__.py").write_str("")?;
root.child("ruff")
.child("a.py")
.write_str(indoc::indoc! {r#"
import ruff.b
"#})?;
root.child("ruff")
.child("b.py")
.write_str(indoc::indoc! {r#"
import importlib
importlib.import_module("ruff.c")
"#})?;
root.child("ruff").child("c.py").write_str("")?;
insta::with_settings!({
filters => INSTA_FILTERS.to_vec(),
}, {
assert_cmd_snapshot!(command().current_dir(&root), @r#"
success: true
exit_code: 0
----- stdout -----
{
"ruff/__init__.py": [],
"ruff/a.py": [
"ruff/b.py"
],
"ruff/b.py": [
"ruff/c.py"
],
"ruff/c.py": []
}
----- stderr -----
"#);
});
Ok(())
}
#[test]
fn globs() -> Result<()> {
let tempdir = TempDir::new()?;
@@ -600,3 +653,133 @@ fn venv() -> Result<()> {
Ok(())
}
#[test]
fn notebook_basic() -> Result<()> {
let tempdir = TempDir::new()?;
let root = ChildPath::new(tempdir.path());
root.child("ruff").child("__init__.py").write_str("")?;
root.child("ruff")
.child("a.py")
.write_str(indoc::indoc! {r#"
def helper():
pass
"#})?;
// Create a basic notebook with a simple import
root.child("notebook.ipynb").write_str(indoc::indoc! {r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ruff.a import helper"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#})?;
insta::with_settings!({
filters => INSTA_FILTERS.to_vec(),
}, {
assert_cmd_snapshot!(command().current_dir(&root), @r###"
success: true
exit_code: 0
----- stdout -----
{
"notebook.ipynb": [
"ruff/a.py"
],
"ruff/__init__.py": [],
"ruff/a.py": []
}
----- stderr -----
"###);
});
Ok(())
}
#[test]
fn notebook_with_magic() -> Result<()> {
let tempdir = TempDir::new()?;
let root = ChildPath::new(tempdir.path());
root.child("ruff").child("__init__.py").write_str("")?;
root.child("ruff")
.child("a.py")
.write_str(indoc::indoc! {r#"
def helper():
pass
"#})?;
// Create a notebook with IPython magic commands and imports
root.child("notebook.ipynb").write_str(indoc::indoc! {r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ruff.a import helper"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#})?;
insta::with_settings!({
filters => INSTA_FILTERS.to_vec(),
}, {
assert_cmd_snapshot!(command().current_dir(&root), @r###"
success: true
exit_code: 0
----- stdout -----
{
"notebook.ipynb": [
"ruff/a.py"
],
"ruff/__init__.py": [],
"ruff/a.py": []
}
----- stderr -----
"###);
});
Ok(())
}

View File

@@ -0,0 +1,193 @@
use std::process::Command;
use insta_cmd::assert_cmd_snapshot;
use crate::CliTest;
#[test]
fn type_checking_imports() -> anyhow::Result<()> {
let test = AnalyzeTest::with_files([
("ruff/__init__.py", ""),
(
"ruff/a.py",
r#"
from typing import TYPE_CHECKING
import ruff.b
if TYPE_CHECKING:
import ruff.c
"#,
),
(
"ruff/b.py",
r#"
if TYPE_CHECKING:
from ruff import c
"#,
),
("ruff/c.py", ""),
])?;
assert_cmd_snapshot!(test.command(), @r###"
success: true
exit_code: 0
----- stdout -----
{
"ruff/__init__.py": [],
"ruff/a.py": [
"ruff/b.py",
"ruff/c.py"
],
"ruff/b.py": [
"ruff/c.py"
],
"ruff/c.py": []
}
----- stderr -----
"###);
assert_cmd_snapshot!(
test.command()
.arg("--no-type-checking-imports"),
@r###"
success: true
exit_code: 0
----- stdout -----
{
"ruff/__init__.py": [],
"ruff/a.py": [
"ruff/b.py"
],
"ruff/b.py": [],
"ruff/c.py": []
}
----- stderr -----
"###
);
Ok(())
}
#[test]
fn type_checking_imports_from_config() -> anyhow::Result<()> {
let test = AnalyzeTest::with_files([
("ruff/__init__.py", ""),
(
"ruff/a.py",
r#"
from typing import TYPE_CHECKING
import ruff.b
if TYPE_CHECKING:
import ruff.c
"#,
),
(
"ruff/b.py",
r#"
if TYPE_CHECKING:
from ruff import c
"#,
),
("ruff/c.py", ""),
(
"ruff.toml",
r#"
[analyze]
type-checking-imports = false
"#,
),
])?;
assert_cmd_snapshot!(test.command(), @r###"
success: true
exit_code: 0
----- stdout -----
{
"ruff/__init__.py": [],
"ruff/a.py": [
"ruff/b.py"
],
"ruff/b.py": [],
"ruff/c.py": []
}
----- stderr -----
"###);
test.write_file(
"ruff.toml",
r#"
[analyze]
type-checking-imports = true
"#,
)?;
assert_cmd_snapshot!(test.command(), @r###"
success: true
exit_code: 0
----- stdout -----
{
"ruff/__init__.py": [],
"ruff/a.py": [
"ruff/b.py",
"ruff/c.py"
],
"ruff/b.py": [
"ruff/c.py"
],
"ruff/c.py": []
}
----- stderr -----
"###
);
Ok(())
}
struct AnalyzeTest {
cli_test: CliTest,
}
impl AnalyzeTest {
pub(crate) fn new() -> anyhow::Result<Self> {
Ok(Self {
cli_test: CliTest::with_settings(|_, mut settings| {
settings.add_filter(r#"\\\\"#, "/");
settings
})?,
})
}
fn with_files<'a>(files: impl IntoIterator<Item = (&'a str, &'a str)>) -> anyhow::Result<Self> {
let case = Self::new()?;
case.write_files(files)?;
Ok(case)
}
#[expect(unused)]
fn with_file(path: impl AsRef<std::path::Path>, content: &str) -> anyhow::Result<Self> {
let fixture = Self::new()?;
fixture.write_file(path, content)?;
Ok(fixture)
}
fn command(&self) -> Command {
let mut command = self.cli_test.command();
command.arg("analyze").arg("graph").arg("--preview");
command
}
}
impl std::ops::Deref for AnalyzeTest {
type Target = CliTest;
fn deref(&self) -> &Self::Target {
&self.cli_test
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
//! Test fixture utilities for ruff CLI tests
//!
//! The core concept is borrowed from ty/tests/cli/main.rs and can be extended
//! with more functionality from there in the future if needed.
#![cfg(not(target_family = "wasm"))]
use anyhow::{Context as _, Result};
use insta::internals::SettingsBindDropGuard;
use insta_cmd::get_cargo_bin;
use std::{
fs,
path::{Path, PathBuf},
process::Command,
};
use tempfile::TempDir;
mod analyze_graph;
mod format;
mod lint;
const BIN_NAME: &str = "ruff";
/// Creates a regex filter for replacing temporary directory paths in snapshots
pub(crate) fn tempdir_filter(path: impl AsRef<str>) -> String {
format!(r"{}[\\/]?", regex::escape(path.as_ref()))
}
/// A test fixture for running ruff CLI tests with temporary directories and files.
///
/// This fixture provides:
/// - Temporary directory management
/// - File creation utilities
/// - Proper snapshot filtering for cross-platform compatibility
/// - Pre-configured ruff command creation
///
/// # Example
///
/// ```rust,no_run
/// use crate::common::RuffTestFixture;
///
/// let fixture = RuffTestFixture::with_file("ruff.toml", "select = ['E']")?;
/// let output = fixture.command().args(["check", "."]).output()?;
/// ```
pub(crate) struct CliTest {
_temp_dir: TempDir,
_settings_scope: SettingsBindDropGuard,
project_dir: PathBuf,
}
impl CliTest {
/// Creates a new test fixture with an empty temporary directory.
///
/// This sets up:
/// - A temporary directory that's automatically cleaned up
/// - Insta snapshot filters for cross-platform path compatibility
/// - Environment isolation for consistent test behavior
pub(crate) fn new() -> Result<Self> {
Self::with_settings(|_, settings| settings)
}
pub(crate) fn with_files<'a>(
files: impl IntoIterator<Item = (&'a str, &'a str)>,
) -> anyhow::Result<Self> {
let case = Self::new()?;
case.write_files(files)?;
Ok(case)
}
pub(crate) fn with_settings(
setup_settings: impl FnOnce(&Path, insta::Settings) -> insta::Settings,
) -> Result<Self> {
let temp_dir = TempDir::new()?;
// Canonicalize the tempdir path because macOS uses symlinks for tempdirs
// and that doesn't play well with our snapshot filtering.
// Simplify with dunce because otherwise we get UNC paths on Windows.
let project_dir = dunce::simplified(
&temp_dir
.path()
.canonicalize()
.context("Failed to canonicalize project path")?,
)
.to_path_buf();
let mut settings = setup_settings(&project_dir, insta::Settings::clone_current());
settings.add_filter(&tempdir_filter(project_dir.to_str().unwrap()), "[TMP]/");
settings.add_filter(r#"\\([\w&&[^nr"]]\w|\s|\.)"#, "/$1");
settings.add_filter(r"(Panicked at) [^:]+:\d+:\d+", "$1 <location>");
settings.add_filter(ruff_linter::VERSION, "[VERSION]");
settings.add_filter(
r#"The system cannot find the file specified."#,
"No such file or directory",
);
let settings_scope = settings.bind_to_scope();
Ok(Self {
project_dir,
_temp_dir: temp_dir,
_settings_scope: settings_scope,
})
}
/// Creates a test fixture with a single file.
///
/// # Arguments
///
/// * `path` - The relative path for the file
/// * `content` - The content to write to the file
///
/// # Example
///
/// ```rust,no_run
/// let fixture = RuffTestFixture::with_file("ruff.toml", "select = ['E']")?;
/// ```
pub(crate) fn with_file(path: impl AsRef<Path>, content: &str) -> Result<Self> {
let fixture = Self::new()?;
fixture.write_file(path, content)?;
Ok(fixture)
}
/// Ensures that the parent directory of a path exists.
fn ensure_parent_directory(path: &Path) -> Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create directory `{}`", parent.display()))?;
}
Ok(())
}
/// Writes a file to the test directory.
///
/// Parent directories are created automatically if they don't exist.
/// Content is dedented to remove common leading whitespace for cleaner test code.
///
/// # Arguments
///
/// * `path` - The relative path for the file
/// * `content` - The content to write to the file
pub(crate) fn write_file(&self, path: impl AsRef<Path>, content: &str) -> Result<()> {
let path = path.as_ref();
let file_path = self.project_dir.join(path);
Self::ensure_parent_directory(&file_path)?;
let content = ruff_python_trivia::textwrap::dedent(content);
fs::write(&file_path, content.as_ref())
.with_context(|| format!("Failed to write file `{}`", file_path.display()))?;
Ok(())
}
pub(crate) fn write_files<'a>(
&self,
files: impl IntoIterator<Item = (&'a str, &'a str)>,
) -> Result<()> {
for file in files {
self.write_file(file.0, file.1)?;
}
Ok(())
}
/// Returns the path to the test directory root.
pub(crate) fn root(&self) -> &Path {
&self.project_dir
}
/// Creates a pre-configured ruff command for testing.
///
/// The command is set up with:
/// - The correct ruff binary path
/// - Working directory set to the test directory
/// - Clean environment variables for consistent behavior
///
/// You can chain additional arguments and options as needed.
///
/// # Example
///
/// ```rust,no_run
/// let output = fixture
/// .command()
/// .args(["check", "--select", "E"])
/// .arg(".")
/// .output()?;
/// ```
pub(crate) fn command(&self) -> Command {
let mut command = Command::new(get_cargo_bin(BIN_NAME));
command.current_dir(&self.project_dir);
// Unset all environment variables because they can affect test behavior.
command.env_clear();
command
}
pub(crate) fn format_command(&self) -> Command {
let mut command = self.command();
command.args(["format", "--no-cache"]);
command
}
}

View File

@@ -0,0 +1,19 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- azure
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
##vso[task.logissue type=error;sourcepath=[TMP]/input.py;linenumber=1;columnnumber=1;code=unformatted;]File would be reformatted
----- stderr -----

View File

@@ -0,0 +1,20 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- concise
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
input.py:1:1: unformatted: File would be reformatted
1 file would be reformatted
----- stderr -----

View File

@@ -0,0 +1,26 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- full
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
unformatted: File would be reformatted
--> input.py:1:1
-
1 | from test import say_hy
2 |
3 | if __name__ == "__main__":
1 file would be reformatted
----- stderr -----

View File

@@ -0,0 +1,19 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- github
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
::error title=Ruff (unformatted),file=[TMP]/input.py,line=1,col=1,endLine=2,endColumn=1::input.py:1:1: unformatted: File would be reformatted
----- stderr -----

View File

@@ -0,0 +1,38 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- gitlab
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
[
{
"check_name": "unformatted",
"description": "unformatted: File would be reformatted",
"severity": "major",
"fingerprint": "d868d7da11a65fcf",
"location": {
"path": "input.py",
"positions": {
"begin": {
"line": 1,
"column": 1
},
"end": {
"line": 2,
"column": 1
}
}
}
}
]
----- stderr -----

View File

@@ -0,0 +1,22 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- grouped
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
input.py:
1:1 unformatted: File would be reformatted
1 file would be reformatted
----- stderr -----

View File

@@ -0,0 +1,19 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- json-lines
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
{"cell":null,"code":"unformatted","end_location":{"column":1,"row":2},"filename":"[TMP]/input.py","fix":{"applicability":"safe","edits":[{"content":"","end_location":{"column":1,"row":2},"location":{"column":1,"row":1}}],"message":null},"location":{"column":1,"row":1},"message":"File would be reformatted","noqa_row":null,"url":null}
----- stderr -----

View File

@@ -0,0 +1,52 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- json
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
[
{
"cell": null,
"code": "unformatted",
"end_location": {
"column": 1,
"row": 2
},
"filename": "[TMP]/input.py",
"fix": {
"applicability": "safe",
"edits": [
{
"content": "",
"end_location": {
"column": 1,
"row": 2
},
"location": {
"column": 1,
"row": 1
}
}
],
"message": null
},
"location": {
"column": 1,
"row": 1
},
"message": "File would be reformatted",
"noqa_row": null,
"url": null
}
]
----- stderr -----

View File

@@ -0,0 +1,26 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- junit
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="ruff" tests="1" failures="1" errors="0">
<testsuite name="[TMP]/input.py" tests="1" disabled="0" errors="0" failures="1" package="org.ruff">
<testcase name="org.ruff.unformatted" classname="[TMP]/input" line="1" column="1">
<failure message="File would be reformatted">line 1, col 1, File would be reformatted</failure>
</testcase>
</testsuite>
</testsuites>
----- stderr -----

View File

@@ -0,0 +1,19 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- pylint
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
input.py:1: [unformatted] File would be reformatted
----- stderr -----

View File

@@ -0,0 +1,60 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- rdjson
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
{
"diagnostics": [
{
"code": {
"value": "unformatted"
},
"location": {
"path": "[TMP]/input.py",
"range": {
"end": {
"column": 1,
"line": 2
},
"start": {
"column": 1,
"line": 1
}
}
},
"message": "File would be reformatted",
"suggestions": [
{
"range": {
"end": {
"column": 1,
"line": 2
},
"start": {
"column": 1,
"line": 1
}
},
"text": ""
}
]
}
],
"severity": "WARNING",
"source": {
"name": "ruff",
"url": "https://docs.astral.sh/ruff"
}
}
----- stderr -----

View File

@@ -0,0 +1,81 @@
---
source: crates/ruff/tests/cli/format.rs
info:
program: ruff
args:
- format
- "--no-cache"
- "--output-format"
- sarif
- "--preview"
- "--check"
- input.py
---
success: false
exit_code: 1
----- stdout -----
{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [
{
"results": [
{
"fixes": [
{
"artifactChanges": [
{
"artifactLocation": {
"uri": "[TMP]/input.py"
},
"replacements": [
{
"deletedRegion": {
"endColumn": 1,
"endLine": 2,
"startColumn": 1,
"startLine": 1
}
}
]
}
],
"description": {
"text": null
}
}
],
"level": "error",
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "[TMP]/input.py"
},
"region": {
"endColumn": 1,
"endLine": 2,
"startColumn": 1,
"startLine": 1
}
}
}
],
"message": {
"text": "File would be reformatted"
},
"ruleId": "unformatted"
}
],
"tool": {
"driver": {
"informationUri": "https://github.com/astral-sh/ruff",
"name": "ruff",
"rules": [],
"version": "[VERSION]"
}
}
}
],
"version": "2.1.0"
}
----- stderr -----

View File

@@ -20,59 +20,59 @@ exit_code: 1
{
"check_name": "F401",
"description": "F401: `os` imported but unused",
"severity": "major",
"fingerprint": "4dbad37161e65c72",
"location": {
"path": "input.py",
"positions": {
"begin": {
"column": 8,
"line": 1
"line": 1,
"column": 8
},
"end": {
"column": 10,
"line": 1
"line": 1,
"column": 10
}
}
},
"severity": "major"
}
},
{
"check_name": "F821",
"description": "F821: Undefined name `y`",
"severity": "major",
"fingerprint": "7af59862a085230",
"location": {
"path": "input.py",
"positions": {
"begin": {
"column": 5,
"line": 2
"line": 2,
"column": 5
},
"end": {
"column": 6,
"line": 2
"line": 2,
"column": 6
}
}
},
"severity": "major"
}
},
{
"check_name": "invalid-syntax",
"description": "invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)",
"severity": "major",
"fingerprint": "e558cec859bb66e8",
"location": {
"path": "input.py",
"positions": {
"begin": {
"column": 1,
"line": 3
"line": 3,
"column": 1
},
"end": {
"column": 6,
"line": 3
"line": 3,
"column": 6
}
}
},
"severity": "major"
}
}
]
----- stderr -----

View File

@@ -1,5 +1,5 @@
---
source: crates/ruff/tests/lint.rs
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
@@ -12,6 +12,7 @@ info:
- "--target-version"
- py39
- input.py
snapshot_kind: text
---
success: false
exit_code: 1
@@ -22,6 +23,30 @@ exit_code: 1
{
"results": [
{
"fixes": [
{
"artifactChanges": [
{
"artifactLocation": {
"uri": "[TMP]/input.py"
},
"replacements": [
{
"deletedRegion": {
"endColumn": 1,
"endLine": 2,
"startColumn": 1,
"startLine": 1
}
}
]
}
],
"description": {
"text": "Remove unused import: `os`"
}
}
],
"level": "error",
"locations": [
{
@@ -95,7 +120,7 @@ exit_code: 1
"rules": [
{
"fullDescription": {
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Preview\nWhen [preview] is enabled (and certain simplifying assumptions\nare met), we analyze all import statements for a given module\nwhen determining whether an import is used, rather than simply\nthe last of these statements. This can result in both different and\nmore import statements being marked as unused.\n\nFor example, if a module consists of\n\n```python\nimport a\nimport a.b\n```\n\nthen both statements are marked as unused under [preview], whereas\nonly the second is marked as unused under stable behavior.\n\nAs another example, if a module consists of\n\n```python\nimport a.b\nimport a\n\na.b.foo()\n```\n\nthen a diagnostic will only be emitted for the first line under [preview],\nwhereas a diagnostic would only be emitted for the second line under\nstable behavior.\n\nNote that this behavior is somewhat subjective and is designed\nto conform to the developer's intuition rather than Python's actual\nexecution. To wit, the statement `import a.b` automatically executes\n`import a`, so in some sense `import a` is _always_ redundant\nin the presence of `import a.b`.\n\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\nSee [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)\nfor more details on how Ruff\ndetermines whether an import is first or third-party.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n\n[preview]: https://docs.astral.sh/ruff/preview/\n"
},
"help": {
"text": "`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability"

View File

@@ -0,0 +1,26 @@
---
source: crates/ruff/tests/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--select"
- F401
- "--fix"
- "--show-fixes"
- input.py
---
success: true
exit_code: 0
----- stdout -----
Fixed 1 error:
- input.py:
1 × F401 (unused-import)
Found 1 error (1 fixed, 0 remaining).
----- stderr -----

View File

@@ -0,0 +1,26 @@
---
source: crates/ruff/tests/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- full
- "--select"
- F401
- "--fix"
- "--show-fixes"
- input.py
---
success: true
exit_code: 0
----- stdout -----
Fixed 1 error:
- input.py:
1 × F401 (unused-import)
Found 1 error (1 fixed, 0 remaining).
----- stderr -----

View File

@@ -0,0 +1,26 @@
---
source: crates/ruff/tests/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- grouped
- "--select"
- F401
- "--fix"
- "--show-fixes"
- input.py
---
success: true
exit_code: 0
----- stdout -----
Fixed 1 error:
- input.py:
1 × F401 (unused-import)
Found 1 error (1 fixed, 0 remaining).
----- stderr -----

View File

@@ -0,0 +1,288 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- test.py
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/test.py"
Settings path: "[TMP]/ruff.toml"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.10
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.10
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.10
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,290 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- "--select"
- UP007
- test.py
- "-"
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/test.py"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.11
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.11
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.11
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,292 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--preview"
- "--show-settings"
- "--select"
- UP007
- test.py
- "-"
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/test.py"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.pyw",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.11
linter.per_file_target_version = {}
linter.preview = enabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.11
formatter.per_file_target_version = {}
formatter.preview = enabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = enabled
analyze.target_version = 3.11
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,292 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- "--select"
- UP007
- "--target-version"
- py310
- test.py
- "-"
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/test.py"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.10
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.10
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.10
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,289 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- "--select"
- UP007
- foo/test.py
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/foo/test.py"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.11
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.11
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.11
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,289 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- "--select"
- UP007
- foo/test.py
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/foo/test.py"
# General Settings
cache_dir = "[TMP]/foo/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/foo"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/foo"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.10
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/foo",
"[TMP]/foo/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.10
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.10
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,288 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- test.py
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/foo/test.py"
Settings path: "[TMP]/ruff.toml"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = none
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.10
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.10
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,288 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- "--show-settings"
- foo/test.py
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/foo/test.py"
Settings path: "[TMP]/ruff.toml"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = none
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.10
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.10
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -0,0 +1,288 @@
---
source: crates/ruff/tests/cli/lint.rs
info:
program: ruff
args:
- check
- "--no-cache"
- "--output-format"
- concise
- test.py
- "--show-settings"
---
success: true
exit_code: 0
----- stdout -----
Resolved settings for: "[TMP]/test.py"
Settings path: "[TMP]/ruff.toml"
# General Settings
cache_dir = "[TMP]/.ruff_cache"
fix = false
fix_only = false
output_format = concise
show_fixes = false
unsafe_fixes = hint
# File Resolver Settings
file_resolver.exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"dist",
"node_modules",
"site-packages",
"venv",
]
file_resolver.extend_exclude = []
file_resolver.force_exclude = false
file_resolver.include = [
"*.py",
"*.pyi",
"*.ipynb",
"**/pyproject.toml",
]
file_resolver.extend_include = []
file_resolver.respect_gitignore = true
file_resolver.project_root = "[TMP]/"
# Linter Settings
linter.exclude = []
linter.project_root = "[TMP]/"
linter.rules.enabled = [
non-pep604-annotation-union (UP007),
]
linter.rules.should_fix = [
non-pep604-annotation-union (UP007),
]
linter.per_file_ignores = {}
linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = []
linter.unresolved_target_version = 3.11
linter.per_file_target_version = {}
linter.preview = disabled
linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({})
linter.allowed_confusables = []
linter.builtins = []
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
linter.external = []
linter.ignore_init_module_imports = true
linter.logger_objects = []
linter.namespace_packages = []
linter.src = [
"[TMP]/",
"[TMP]/src",
]
linter.tab_size = 4
linter.line_length = 88
linter.task_tags = [
TODO,
FIXME,
XXX,
]
linter.typing_modules = []
linter.typing_extensions = true
# Linter Plugins
linter.flake8_annotations.mypy_init_return = false
linter.flake8_annotations.suppress_dummy_args = false
linter.flake8_annotations.suppress_none_returning = false
linter.flake8_annotations.allow_star_arg_any = false
linter.flake8_annotations.ignore_fully_untyped = false
linter.flake8_bandit.hardcoded_tmp_directory = [
/tmp,
/var/tmp,
/dev/shm,
]
linter.flake8_bandit.check_typed_exception = false
linter.flake8_bandit.extend_markup_names = []
linter.flake8_bandit.allowed_markup_calls = []
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.allowed_modules = []
linter.flake8_builtins.ignorelist = []
linter.flake8_builtins.strict_checking = false
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0
linter.flake8_gettext.functions_names = [
_,
gettext,
ngettext,
]
linter.flake8_implicit_str_concat.allow_multiline = true
linter.flake8_import_conventions.aliases = {
altair = alt,
holoviews = hv,
matplotlib = mpl,
matplotlib.pyplot = plt,
networkx = nx,
numpy = np,
numpy.typing = npt,
pandas = pd,
panel = pn,
plotly.express = px,
polars = pl,
pyarrow = pa,
seaborn = sns,
tensorflow = tf,
tkinter = tk,
xml.etree.ElementTree = ET,
}
linter.flake8_import_conventions.banned_aliases = {}
linter.flake8_import_conventions.banned_from = []
linter.flake8_pytest_style.fixture_parentheses = false
linter.flake8_pytest_style.parametrize_names_type = tuple
linter.flake8_pytest_style.parametrize_values_type = list
linter.flake8_pytest_style.parametrize_values_row_type = tuple
linter.flake8_pytest_style.raises_require_match_for = [
BaseException,
Exception,
ValueError,
OSError,
IOError,
EnvironmentError,
socket.error,
]
linter.flake8_pytest_style.raises_extend_require_match_for = []
linter.flake8_pytest_style.mark_parentheses = false
linter.flake8_quotes.inline_quotes = double
linter.flake8_quotes.multiline_quotes = double
linter.flake8_quotes.docstring_quotes = double
linter.flake8_quotes.avoid_escape = true
linter.flake8_self.ignore_names = [
_make,
_asdict,
_replace,
_fields,
_field_defaults,
_name_,
_value_,
]
linter.flake8_tidy_imports.ban_relative_imports = "parents"
linter.flake8_tidy_imports.banned_api = {}
linter.flake8_tidy_imports.banned_module_level_imports = []
linter.flake8_type_checking.strict = false
linter.flake8_type_checking.exempt_modules = [
typing,
typing_extensions,
]
linter.flake8_type_checking.runtime_required_base_classes = []
linter.flake8_type_checking.runtime_required_decorators = []
linter.flake8_type_checking.quote_annotations = false
linter.flake8_unused_arguments.ignore_variadic_names = false
linter.isort.required_imports = []
linter.isort.combine_as_imports = false
linter.isort.force_single_line = false
linter.isort.force_sort_within_sections = false
linter.isort.detect_same_package = true
linter.isort.case_sensitive = false
linter.isort.force_wrap_aliases = false
linter.isort.force_to_top = []
linter.isort.known_modules = {}
linter.isort.order_by_type = true
linter.isort.relative_imports_order = furthest_to_closest
linter.isort.single_line_exclusions = []
linter.isort.split_on_trailing_comma = true
linter.isort.classes = []
linter.isort.constants = []
linter.isort.variables = []
linter.isort.no_lines_before = []
linter.isort.lines_after_imports = -1
linter.isort.lines_between_types = 0
linter.isort.forced_separate = []
linter.isort.section_order = [
known { type = future },
known { type = standard_library },
known { type = third_party },
known { type = first_party },
known { type = local_folder },
]
linter.isort.default_section = known { type = third_party }
linter.isort.no_sections = false
linter.isort.from_first = false
linter.isort.length_sort = false
linter.isort.length_sort_straight = false
linter.mccabe.max_complexity = 10
linter.pep8_naming.ignore_names = [
setUp,
tearDown,
setUpClass,
tearDownClass,
setUpModule,
tearDownModule,
asyncSetUp,
asyncTearDown,
setUpTestData,
failureException,
longMessage,
maxDiff,
]
linter.pep8_naming.classmethod_decorators = []
linter.pep8_naming.staticmethod_decorators = []
linter.pycodestyle.max_line_length = 88
linter.pycodestyle.max_doc_length = none
linter.pycodestyle.ignore_overlong_task_comments = false
linter.pyflakes.extend_generics = []
linter.pyflakes.allowed_unused_imports = []
linter.pylint.allow_magic_value_types = [
str,
bytes,
]
linter.pylint.allow_dunder_method_names = []
linter.pylint.max_args = 5
linter.pylint.max_positional_args = 5
linter.pylint.max_returns = 6
linter.pylint.max_bool_expr = 5
linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
# Formatter Settings
formatter.exclude = []
formatter.unresolved_target_version = 3.11
formatter.per_file_target_version = {}
formatter.preview = disabled
formatter.line_width = 88
formatter.line_ending = auto
formatter.indent_style = space
formatter.indent_width = 4
formatter.quote_style = double
formatter.magic_trailing_comma = respect
formatter.docstring_code_format = disabled
formatter.docstring_code_line_width = dynamic
# Analyze Settings
analyze.exclude = []
analyze.preview = disabled
analyze.target_version = 3.11
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

View File

@@ -951,6 +951,20 @@ fn rule_f401() {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "F401"]));
}
#[test]
fn rule_f401_output_json() {
insta::with_settings!({filters => vec![
(r#"("file": ")[^"]+(",)"#, "$1<FILE>$2"),
]}, {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "F401", "--output-format", "json"]));
});
}
#[test]
fn rule_f401_output_text() {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "F401", "--output-format", "text"]));
}
#[test]
fn rule_invalid_rule_name() {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "RUF404"]), @r"
@@ -965,6 +979,34 @@ fn rule_invalid_rule_name() {
");
}
#[test]
fn rule_invalid_rule_name_output_json() {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "RUF404", "--output-format", "json"]), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'RUF404' for '[RULE]'
For more information, try '--help'.
");
}
#[test]
fn rule_invalid_rule_name_output_text() {
assert_cmd_snapshot!(ruff_cmd().args(["rule", "RUF404", "--output-format", "text"]), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'RUF404' for '[RULE]'
For more information, try '--help'.
");
}
#[test]
fn show_statistics() {
let mut cmd = RuffCheck::default()
@@ -1489,6 +1531,8 @@ fn deprecated_direct() {
#[test]
fn deprecated_multiple_direct() {
// Multiple deprecated rules selected by exact code should be included
// but a warning should be displayed
let mut cmd = RuffCheck::default()
.args(["--select", "RUF920", "--select", "RUF921"])
.build();
@@ -1516,16 +1560,10 @@ fn deprecated_indirect() {
// since it is not a "direct" selection
let mut cmd = RuffCheck::default().args(["--select", "RUF92"]).build();
assert_cmd_snapshot!(cmd, @r"
success: false
exit_code: 1
success: true
exit_code: 0
----- stdout -----
RUF920 Hey this is a deprecated test rule.
--> -:1:1
RUF921 Hey this is another deprecated test rule.
--> -:1:1
Found 2 errors.
All checks passed!
----- stderr -----
");
@@ -1692,6 +1730,27 @@ fn check_input_from_argfile() -> Result<()> {
Ok(())
}
#[test]
// Regression test for https://github.com/astral-sh/ruff/issues/20655
fn missing_argfile_reports_error() {
let mut cmd = RuffCheck::default().filename("@!.txt").build();
insta::with_settings!({filters => vec![
("The system cannot find the file specified.", "No such file or directory")
]}, {
assert_cmd_snapshot!(cmd, @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to read CLI arguments from files
Cause: failed to open file `!.txt`
Cause: No such file or directory (os error 2)
");
});
}
#[test]
fn check_hints_hidden_unsafe_fixes() {
let mut cmd = RuffCheck::default()
@@ -2155,16 +2214,10 @@ extend-safe-fixes = ["RUF9"]
RUF903 Hey this is a stable test rule with a display only fix.
--> -:1:1
RUF920 Hey this is a deprecated test rule.
--> -:1:1
RUF921 Hey this is another deprecated test rule.
--> -:1:1
RUF950 Hey this is a test rule that was redirected from another.
--> -:1:1
Found 7 errors.
Found 5 errors.
[*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
----- stderr -----

File diff suppressed because it is too large Load Diff

View File

@@ -44,6 +44,43 @@ import some_module
__all__ = ["some_module"]
```
## Preview
When [preview] is enabled (and certain simplifying assumptions
are met), we analyze all import statements for a given module
when determining whether an import is used, rather than simply
the last of these statements. This can result in both different and
more import statements being marked as unused.
For example, if a module consists of
```python
import a
import a.b
```
then both statements are marked as unused under [preview], whereas
only the second is marked as unused under stable behavior.
As another example, if a module consists of
```python
import a.b
import a
a.b.foo()
```
then a diagnostic will only be emitted for the first line under [preview],
whereas a diagnostic would only be emitted for the second line under
stable behavior.
Note that this behavior is somewhat subjective and is designed
to conform to the developer's intuition rather than Python's actual
execution. To wit, the statement `import a.b` automatically executes
`import a`, so in some sense `import a` is _always_ redundant
in the presence of `import a.b`.
## Fix safety
Fixes to remove unused imports are safe, except in `__init__.py` files.
@@ -55,6 +92,10 @@ either a redundant alias or, if already present in the file, an `__all__` entry.
to remove third-party and standard library imports -- the fix is unsafe because the module's
interface changes.
See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)
for more details on how Ruff
determines whether an import is first or third-party.
## Example
```python
@@ -83,11 +124,6 @@ else:
print("numpy is not installed")
```
## Preview
When [preview](https://docs.astral.sh/ruff/preview/) is enabled,
the criterion for determining whether an import is first-party
is stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.
## Options
- `lint.ignore-init-module-imports`
- `lint.pyflakes.allowed-unused-imports`
@@ -97,4 +133,6 @@ is stricter, which could affect the suggested fix. See [this FAQ section](https:
- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)
- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)
[preview]: https://docs.astral.sh/ruff/preview/
----- stderr -----

View File

@@ -0,0 +1,38 @@
---
source: crates/ruff/tests/integration_test.rs
info:
program: ruff
args:
- rule
- F401
- "--output-format"
- json
---
success: true
exit_code: 0
----- stdout -----
{
"name": "unused-import",
"code": "F401",
"linter": "Pyflakes",
"summary": "`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability",
"message_formats": [
"`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability",
"`{name}` imported but unused; consider removing, adding to `__all__`, or using a redundant alias",
"`{name}` imported but unused"
],
"fix": "Fix is sometimes available.",
"fix_availability": "Sometimes",
"explanation": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Preview\nWhen [preview] is enabled (and certain simplifying assumptions\nare met), we analyze all import statements for a given module\nwhen determining whether an import is used, rather than simply\nthe last of these statements. This can result in both different and\nmore import statements being marked as unused.\n\nFor example, if a module consists of\n\n```python\nimport a\nimport a.b\n```\n\nthen both statements are marked as unused under [preview], whereas\nonly the second is marked as unused under stable behavior.\n\nAs another example, if a module consists of\n\n```python\nimport a.b\nimport a\n\na.b.foo()\n```\n\nthen a diagnostic will only be emitted for the first line under [preview],\nwhereas a diagnostic would only be emitted for the second line under\nstable behavior.\n\nNote that this behavior is somewhat subjective and is designed\nto conform to the developer's intuition rather than Python's actual\nexecution. To wit, the statement `import a.b` automatically executes\n`import a`, so in some sense `import a` is _always_ redundant\nin the presence of `import a.b`.\n\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\nSee [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)\nfor more details on how Ruff\ndetermines whether an import is first or third-party.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n\n[preview]: https://docs.astral.sh/ruff/preview/\n",
"preview": false,
"status": {
"Stable": {
"since": "v0.0.18"
}
},
"source_location": {
"file": "<FILE>",
"line": 145
}
}
----- stderr -----

View File

@@ -0,0 +1,140 @@
---
source: crates/ruff/tests/integration_test.rs
info:
program: ruff
args:
- rule
- F401
- "--output-format"
- text
---
success: true
exit_code: 0
----- stdout -----
# unused-import (F401)
Derived from the **Pyflakes** linter.
Fix is sometimes available.
## What it does
Checks for unused imports.
## Why is this bad?
Unused imports add a performance overhead at runtime, and risk creating
import cycles. They also increase the cognitive load of reading the code.
If an import statement is used to check for the availability or existence
of a module, consider using `importlib.util.find_spec` instead.
If an import statement is used to re-export a symbol as part of a module's
public interface, consider using a "redundant" import alias, which
instructs Ruff (and other tools) to respect the re-export, and avoid
marking it as unused, as in:
```python
from module import member as member
```
Alternatively, you can use `__all__` to declare a symbol as part of the module's
interface, as in:
```python
# __init__.py
import some_module
__all__ = ["some_module"]
```
## Preview
When [preview] is enabled (and certain simplifying assumptions
are met), we analyze all import statements for a given module
when determining whether an import is used, rather than simply
the last of these statements. This can result in both different and
more import statements being marked as unused.
For example, if a module consists of
```python
import a
import a.b
```
then both statements are marked as unused under [preview], whereas
only the second is marked as unused under stable behavior.
As another example, if a module consists of
```python
import a.b
import a
a.b.foo()
```
then a diagnostic will only be emitted for the first line under [preview],
whereas a diagnostic would only be emitted for the second line under
stable behavior.
Note that this behavior is somewhat subjective and is designed
to conform to the developer's intuition rather than Python's actual
execution. To wit, the statement `import a.b` automatically executes
`import a`, so in some sense `import a` is _always_ redundant
in the presence of `import a.b`.
## Fix safety
Fixes to remove unused imports are safe, except in `__init__.py` files.
Applying fixes to `__init__.py` files is currently in preview. The fix offered depends on the
type of the unused import. Ruff will suggest a safe fix to export first-party imports with
either a redundant alias or, if already present in the file, an `__all__` entry. If multiple
`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix
to remove third-party and standard library imports -- the fix is unsafe because the module's
interface changes.
See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)
for more details on how Ruff
determines whether an import is first or third-party.
## Example
```python
import numpy as np # unused import
def area(radius):
return 3.14 * radius**2
```
Use instead:
```python
def area(radius):
return 3.14 * radius**2
```
To check the availability of a module, use `importlib.util.find_spec`:
```python
from importlib.util import find_spec
if find_spec("numpy") is not None:
print("numpy is installed")
else:
print("numpy is not installed")
```
## Options
- `lint.ignore-init-module-imports`
- `lint.pyflakes.allowed-unused-imports`
## References
- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)
- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)
- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)
[preview]: https://docs.astral.sh/ruff/preview/
----- stderr -----

View File

@@ -371,6 +371,7 @@ linter.pylint.max_branches = 12
linter.pylint.max_statements = 50
linter.pylint.max_public_methods = 20
linter.pylint.max_locals = 15
linter.pylint.max_nested_blocks = 5
linter.pyupgrade.keep_runtime_typing = false
linter.ruff.parenthesize_tuple_in_subscript = false
@@ -395,5 +396,6 @@ analyze.target_version = 3.7
analyze.string_imports = disabled
analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {}
analyze.type_checking_imports = true
----- stderr -----

Some files were not shown because too many files have changed in this diff Show More