Compare commits

...

72 Commits

Author SHA1 Message Date
David Peter
b6954063c1 [ty] Do not function-like property for Callable type relations 2025-10-13 13:15:36 +02: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
384 changed files with 18294 additions and 11929 deletions

View File

@@ -40,7 +40,7 @@ jobs:
- 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 }}
@@ -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 }}
@@ -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

@@ -225,7 +225,7 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: |
rustup component add clippy
@@ -245,21 +245,21 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-insta
- name: "Install uv"
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
enable-cache: "true"
- name: ty mdtests (GitHub annotations)
@@ -307,21 +307,21 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-insta
- name: "Install uv"
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
enable-cache: "true"
- name: "Run tests"
@@ -340,15 +340,15 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo nextest"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-nextest
- name: "Install uv"
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
enable-cache: "true"
- name: "Run tests"
@@ -371,7 +371,7 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
@@ -400,7 +400,7 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
@@ -423,7 +423,7 @@ 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
- name: "Install Rust toolchain"
env:
MSRV: ${{ steps.msrv.outputs.value }}
@@ -446,13 +446,13 @@ jobs:
- 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"
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo-binstall"
uses: cargo-bins/cargo-binstall@38e8f5e4c386b611d51e8aa997b9a06a3c8eb67a # v1.15.6
uses: cargo-bins/cargo-binstall@a66119fbb1c952daba62640c2609111fe0803621 # v1.15.7
- 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
@@ -472,7 +472,7 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
name: Download Ruff binary to test
id: download-cached-binary
@@ -506,7 +506,7 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup component add rustfmt
# Run all code generation scripts, and verify that the current output is
@@ -673,7 +673,7 @@ jobs:
branch: ${{ github.event.pull_request.base.ref }}
workflow: "ci.yaml"
check_artifacts: true
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: Fuzz
env:
FORCE_COLOR: 1
@@ -703,10 +703,28 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@38e8f5e4c386b611d51e8aa997b9a06a3c8eb67a # v1.15.6
- uses: cargo-bins/cargo-binstall@a66119fbb1c952daba62640c2609111fe0803621 # v1.15.7
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear
ty-completion-evaluation:
name: "ty completion evaluation"
runs-on: depot-ubuntu-22.04-16
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@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- name: "Install Rust toolchain"
run: rustup show
- name: "Run ty completion evaluation"
run: cargo run --release --package ty_completion_eval -- all --threshold 0.1 --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
@@ -720,7 +738,7 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
@@ -743,13 +761,13 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.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') }}
@@ -777,7 +795,7 @@ jobs:
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: "3.13"
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
@@ -786,7 +804,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: uv pip install -r docs/requirements-insiders.txt --system
@@ -816,7 +834,7 @@ jobs:
- 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
- name: "Install Rust toolchain"
run: rustup show
- name: "Run checks"
@@ -886,7 +904,7 @@ jobs:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22
@@ -911,7 +929,7 @@ jobs:
runs-on: ubuntu-24.04
needs: determine_changes
if: |
github.ref == 'refs/heads/main' ||
github.ref == 'refs/heads/main' ||
(needs.determine_changes.outputs.formatter == 'true' || needs.determine_changes.outputs.linter == 'true')
timeout-minutes: 20
steps:
@@ -920,14 +938,14 @@ jobs:
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-codspeed
@@ -935,7 +953,7 @@ jobs:
run: cargo codspeed build --features "codspeed,instrumented" --no-default-features -p ruff_benchmark --bench formatter --bench lexer --bench linter --bench parser
- name: "Run benchmarks"
uses: CodSpeedHQ/action@653fdc30e6c40ffd9739e40c8a0576f4f4523ca1 # v4.0.1
uses: CodSpeedHQ/action@6b43a0cd438f6ca5ad26f9ed03ed159ed2df7da9 # v4.1.1
with:
mode: instrumentation
run: cargo codspeed run
@@ -946,7 +964,7 @@ jobs:
runs-on: ubuntu-24.04
needs: determine_changes
if: |
github.ref == 'refs/heads/main' ||
github.ref == 'refs/heads/main' ||
needs.determine_changes.outputs.ty == 'true'
timeout-minutes: 20
steps:
@@ -955,14 +973,14 @@ jobs:
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-codspeed
@@ -970,33 +988,37 @@ jobs:
run: cargo codspeed build --features "codspeed,instrumented" --no-default-features -p ruff_benchmark --bench ty
- name: "Run benchmarks"
uses: CodSpeedHQ/action@653fdc30e6c40ffd9739e40c8a0576f4f4523ca1 # v4.0.1
uses: CodSpeedHQ/action@6b43a0cd438f6ca5ad26f9ed03ed159ed2df7da9 # v4.1.1
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@67cc679904bee382389bf22082124fa963c6f6bd # v2.61.3
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
with:
tool: cargo-codspeed
@@ -1004,7 +1026,7 @@ jobs:
run: cargo codspeed build --features "codspeed,walltime" --no-default-features -p ruff_benchmark
- name: "Run benchmarks"
uses: CodSpeedHQ/action@653fdc30e6c40ffd9739e40c8a0576f4f4523ca1 # v4.0.1
uses: CodSpeedHQ/action@6b43a0cd438f6ca5ad26f9ed03ed159ed2df7da9 # v4.1.1
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
@@ -1012,5 +1034,5 @@ jobs:
CODSPEED_PERF_ENABLED: false
with:
mode: walltime
run: cargo codspeed run
run: cargo codspeed run --bench ty_walltime "${{ matrix.benchmarks }}"
token: ${{ secrets.CODSPEED_TOKEN }}

View File

@@ -34,12 +34,12 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- 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

View File

@@ -39,9 +39,9 @@ jobs:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"
@@ -82,9 +82,9 @@ jobs:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"

View File

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

@@ -22,7 +22,7 @@ jobs:
id-token: write
steps:
- name: "Install uv"
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
pattern: wheels-*

View File

@@ -65,7 +65,7 @@ jobs:
run: |
git config --global user.name typeshedbot
git config --global user.email '<>'
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: Sync typeshed stubs
run: |
rm -rf "ruff/${VENDORED_TYPESHED}"
@@ -117,7 +117,7 @@ jobs:
with:
persist-credentials: true
ref: ${{ env.UPSTREAM_BRANCH}}
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: Setup git
run: |
git config --global user.name typeshedbot
@@ -155,7 +155,7 @@ jobs:
with:
persist-credentials: true
ref: ${{ env.UPSTREAM_BRANCH}}
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: Setup git
run: |
git config --global user.name typeshedbot

View File

@@ -33,9 +33,9 @@ jobs:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"

View File

@@ -29,9 +29,9 @@ jobs:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"

View File

@@ -45,7 +45,7 @@ jobs:
path: typing
persist-credentials: false
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
workspaces: "ruff"

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:

View File

@@ -1,5 +1,21 @@
# 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**

View File

@@ -1,266 +1,46 @@
# Changelog
## 0.13.3
## 0.14.0
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)
## 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.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.0
Check out the [blog post](https://astral.sh/blog/ruff-v0.13.0) for a migration
guide and overview of the changes!
Released on 2025-10-07.
### 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.
- Update default and latest Python versions for 3.14 ([#20725](https://github.com/astral-sh/ruff/pull/20725))
### Preview features
- \[`pyupgrade`\] Enable `UP043` in stub files ([#20027](https://github.com/astral-sh/ruff/pull/20027))
- \[`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
- \[`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))
- \[`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
- \[`pep8-naming`\] Fix formatting of `__all__` (`N816`) ([#20301](https://github.com/astral-sh/ruff/pull/20301))
- \[`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

View File

@@ -321,10 +321,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`
@@ -337,6 +343,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:
@@ -376,13 +383,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

195
Cargo.lock generated
View File

@@ -50,9 +50,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.20"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -65,9 +65,9 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.11"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-lossy"
@@ -214,15 +214,6 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bincode"
version = "2.0.1"
@@ -243,6 +234,26 @@ dependencies = [
"virtue",
]
[[package]]
name = "bindgen"
version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags 2.9.4",
"cexpr",
"clang-sys",
"itertools 0.10.5",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -316,9 +327,9 @@ dependencies = [
[[package]]
name = "camino"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603"
checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
dependencies = [
"serde_core",
]
@@ -350,6 +361,15 @@ dependencies = [
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.3"
@@ -400,6 +420,17 @@ dependencies = [
"half",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "4.5.48"
@@ -486,16 +517,17 @@ dependencies = [
[[package]]
name = "codspeed"
version = "3.0.5"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35584c5fcba8059780748866387fb97c5a203bcfc563fc3d0790af406727a117"
checksum = "d0f62ea8934802f8b374bf691eea524c3aa444d7014f604dd4182a3667b69510"
dependencies = [
"anyhow",
"bincode 1.3.3",
"bindgen",
"cc",
"colored 2.2.0",
"glob",
"libc",
"nix 0.29.0",
"nix 0.30.1",
"serde",
"serde_json",
"statrs",
@@ -504,20 +536,22 @@ dependencies = [
[[package]]
name = "codspeed-criterion-compat"
version = "3.0.5"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78f6c1c6bed5fd84d319e8b0889da051daa361c79b7709c9394dfe1a882bba67"
checksum = "d87efbc015fc0ff1b2001cd87df01c442824de677e01a77230bf091534687abb"
dependencies = [
"clap",
"codspeed",
"codspeed-criterion-compat-walltime",
"colored 2.2.0",
"regex",
]
[[package]]
name = "codspeed-criterion-compat-walltime"
version = "3.0.5"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c989289ce6b1cbde72ed560496cb8fbf5aa14d5ef5666f168e7f87751038352e"
checksum = "ae5713ace440123bb4f1f78dd068d46872cb8548bfe61f752e7b2ad2c06d7f00"
dependencies = [
"anes",
"cast",
@@ -540,20 +574,22 @@ dependencies = [
[[package]]
name = "codspeed-divan-compat"
version = "3.0.5"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adf64eda57508448d59efd940bad62ede7c50b0d451a150b8d6a0eca642792a6"
checksum = "95b4214b974f8f5206497153e89db90274e623f06b00bf4b9143eeb7735d975d"
dependencies = [
"clap",
"codspeed",
"codspeed-divan-compat-macros",
"codspeed-divan-compat-walltime",
"regex",
]
[[package]]
name = "codspeed-divan-compat-macros"
version = "3.0.5"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058167258e819b16a4ba601fdfe270349ef191154758dbce122c62a698f70ba8"
checksum = "a53f34a16cb70ce4fd9ad57e1db016f0718e434f34179ca652006443b9a39967"
dependencies = [
"divan-macros",
"itertools 0.14.0",
@@ -565,9 +601,9 @@ dependencies = [
[[package]]
name = "codspeed-divan-compat-walltime"
version = "3.0.5"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f9866ee3a4ef9d2868823ea5811886763af244f2df584ca247f49281c43f1f"
checksum = "e8a5099050c8948dce488b8eaa2e68dc5cf571cb8f9fce99aaaecbdddb940bcd"
dependencies = [
"cfg-if",
"clap",
@@ -818,6 +854,27 @@ dependencies = [
"typenum",
]
[[package]]
name = "csv"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
dependencies = [
"memchr",
]
[[package]]
name = "ctrlc"
version = "3.5.0"
@@ -1506,7 +1563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown 0.16.0",
"hashbrown 0.15.5",
"serde",
"serde_core",
]
@@ -1780,15 +1837,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.175"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libcst"
version = "1.8.4"
version = "1.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052ef5d9fc958a51aeebdf3713573b36c6fd6eed0bf0e60e204d2c0f8cf19b9f"
checksum = "9d56bcd52d9b5e5f43e7fba20eb1f423ccb18c84cdf1cb506b8c1b95776b0b49"
dependencies = [
"annotate-snippets",
"libcst_derive",
@@ -1801,14 +1858,24 @@ dependencies = [
[[package]]
name = "libcst_derive"
version = "1.8.4"
version = "1.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91a751afee92cbdd59d4bc6754c7672712eec2d30a308f23de4e3287b2929cb"
checksum = "3fcf5a725c4db703660124fe0edb98285f1605d0b87b7ee8684b699764a4f01a"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "libloading"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link 0.2.0",
]
[[package]]
name = "libmimalloc-sys"
version = "0.1.44"
@@ -1950,9 +2017,9 @@ checksum = "2f926ade0c4e170215ae43342bf13b9310a437609c81f29f86c5df6657582ef9"
[[package]]
name = "memchr"
version = "2.7.5"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "memoffset"
@@ -2461,6 +2528,16 @@ dependencies = [
"yansi",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
@@ -2492,9 +2569,9 @@ dependencies = [
[[package]]
name = "pyproject-toml"
version = "0.13.6"
version = "0.13.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec768e063102b426e8962989758115e8659485124de9207bc365fab524125d65"
checksum = "f6d755483ad14b49e76713b52285235461a5b4f73f17612353e11a5de36a5fd2"
dependencies = [
"indexmap",
"pep440_rs",
@@ -2692,9 +2769,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.2"
version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
dependencies = [
"aho-corasick",
"memchr",
@@ -2704,9 +2781,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.10"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
dependencies = [
"aho-corasick",
"memchr",
@@ -2738,12 +2815,12 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.13.3"
version = "0.14.0"
dependencies = [
"anyhow",
"argfile",
"assert_fs",
"bincode 2.0.1",
"bincode",
"bitflags 2.9.4",
"cachedir",
"clap",
@@ -2777,6 +2854,7 @@ dependencies = [
"ruff_python_ast",
"ruff_python_formatter",
"ruff_python_parser",
"ruff_python_trivia",
"ruff_server",
"ruff_source_file",
"ruff_text_size",
@@ -2994,7 +3072,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.13.3"
version = "0.14.0"
dependencies = [
"aho-corasick",
"anyhow",
@@ -3348,7 +3426,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.13.3"
version = "0.14.0"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -4227,6 +4305,26 @@ dependencies = [
"ty_python_semantic",
]
[[package]]
name = "ty_completion_eval"
version = "0.0.0"
dependencies = [
"anyhow",
"bstr",
"clap",
"csv",
"regex",
"ruff_db",
"ruff_text_size",
"serde",
"tempfile",
"toml",
"ty_ide",
"ty_project",
"ty_python_semantic",
"walkdir",
]
[[package]]
name = "ty_ide"
version = "0.0.0"
@@ -4401,6 +4499,7 @@ dependencies = [
"colored 3.0.0",
"insta",
"memchr",
"path-slash",
"regex",
"ruff_db",
"ruff_index",

View File

@@ -43,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" }
@@ -69,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" }
@@ -203,7 +205,7 @@ wild = { version = "2" }
zip = { version = "0.6.6", default-features = false }
[workspace.metadata.cargo-shear]
ignored = ["getrandom", "ruff_options_metadata", "uuid", "get-size2"]
ignored = ["getrandom", "ruff_options_metadata", "uuid", "get-size2", "ty_completion_eval"]
[workspace.lints.rust]

View File

@@ -148,8 +148,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.13.3/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.13.3/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.14.0/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.14.0/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 +182,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.13.3
rev: v0.14.0
hooks:
# Run the linter.
- id: ruff-check

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

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.13.3"
version = "0.14.0"
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 }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
//! 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 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_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(())
}
/// 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
}
}

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

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
snapshot_kind: text
---
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 = {}
----- 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
- "-"
snapshot_kind: text
---
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 = {}
----- 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
- "-"
snapshot_kind: text
---
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 = {}
----- 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
- "-"
snapshot_kind: text
---
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 = {}
----- 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
snapshot_kind: text
---
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 = {}
----- 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
snapshot_kind: text
---
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 = {}
----- stderr -----

View File

@@ -0,0 +1,287 @@
---
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 = {}
----- stderr -----

View File

@@ -0,0 +1,287 @@
---
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 = {}
----- 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"
snapshot_kind: text
---
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 = {}
----- stderr -----

File diff suppressed because it is too large Load Diff

View File

@@ -599,7 +599,7 @@ impl<'a> ProjectBenchmark<'a> {
self.project
.check_paths()
.iter()
.map(|path| path.to_path_buf())
.map(|path| SystemPathBuf::from(*path))
.collect(),
);
@@ -645,8 +645,8 @@ fn hydra(criterion: &mut Criterion) {
name: "hydra-zen",
repository: "https://github.com/mit-ll-responsible-ai/hydra-zen",
commit: "dd2b50a9614c6f8c46c5866f283c8f7e7a960aa8",
paths: vec![SystemPath::new("src")],
dependencies: vec!["pydantic", "beartype", "hydra-core"],
paths: &["src"],
dependencies: &["pydantic", "beartype", "hydra-core"],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
@@ -662,8 +662,8 @@ fn attrs(criterion: &mut Criterion) {
name: "attrs",
repository: "https://github.com/python-attrs/attrs",
commit: "a6ae894aad9bc09edc7cdad8c416898784ceec9b",
paths: vec![SystemPath::new("src")],
dependencies: vec![],
paths: &["src"],
dependencies: &[],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
@@ -679,8 +679,8 @@ fn anyio(criterion: &mut Criterion) {
name: "anyio",
repository: "https://github.com/agronholm/anyio",
commit: "561d81270a12f7c6bbafb5bc5fad99a2a13f96be",
paths: vec![SystemPath::new("src")],
dependencies: vec![],
paths: &["src"],
dependencies: &[],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY313,
},
@@ -696,8 +696,8 @@ fn datetype(criterion: &mut Criterion) {
name: "DateType",
repository: "https://github.com/glyph/DateType",
commit: "57c9c93cf2468069f72945fc04bf27b64100dad8",
paths: vec![SystemPath::new("src")],
dependencies: vec![],
paths: &["src"],
dependencies: &[],
max_dep_date: "2025-07-04",
python_version: PythonVersion::PY313,
},

View File

@@ -1,6 +1,5 @@
use std::fmt::{Display, Formatter};
use divan::{Bencher, bench};
use std::fmt::{Display, Formatter};
use rayon::ThreadPoolBuilder;
use ruff_benchmark::real_world_projects::{InstalledProject, RealWorldProject};
@@ -13,29 +12,39 @@ use ty_project::metadata::value::{RangedValue, RelativePathBuf};
use ty_project::{Db, ProjectDatabase, ProjectMetadata};
struct Benchmark<'a> {
project: InstalledProject<'a>,
project: RealWorldProject<'a>,
installed_project: std::sync::OnceLock<InstalledProject<'a>>,
max_diagnostics: usize,
}
impl<'a> Benchmark<'a> {
fn new(project: RealWorldProject<'a>, max_diagnostics: usize) -> Self {
let setup_project = project.setup().expect("Failed to setup project");
const fn new(project: RealWorldProject<'a>, max_diagnostics: usize) -> Self {
Self {
project: setup_project,
project,
installed_project: std::sync::OnceLock::new(),
max_diagnostics,
}
}
fn installed_project(&self) -> &InstalledProject<'a> {
self.installed_project.get_or_init(|| {
self.project
.clone()
.setup()
.expect("Failed to setup project")
})
}
fn setup_iteration(&self) -> ProjectDatabase {
let root = SystemPathBuf::from_path_buf(self.project.path.clone()).unwrap();
let installed_project = self.installed_project();
let root = SystemPathBuf::from_path_buf(installed_project.path.clone()).unwrap();
let system = OsSystem::new(&root);
let mut metadata = ProjectMetadata::discover(&root, &system).unwrap();
metadata.apply_options(Options {
environment: Some(EnvironmentOptions {
python_version: Some(RangedValue::cli(self.project.config.python_version)),
python_version: Some(RangedValue::cli(installed_project.config.python_version)),
python: Some(RelativePathBuf::cli(SystemPath::new(".venv"))),
..EnvironmentOptions::default()
}),
@@ -46,7 +55,7 @@ impl<'a> Benchmark<'a> {
db.project().set_included_paths(
&mut db,
self.project
installed_project
.check_paths()
.iter()
.map(|path| SystemPath::absolute(path, &root))
@@ -58,7 +67,7 @@ impl<'a> Benchmark<'a> {
impl Display for Benchmark<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.project.config.name)
self.project.name.fmt(f)
}
}
@@ -75,166 +84,150 @@ fn check_project(db: &ProjectDatabase, max_diagnostics: usize) {
);
}
static ALTAIR: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "altair",
repository: "https://github.com/vega/altair",
commit: "d1f4a1ef89006e5f6752ef1f6df4b7a509336fba",
paths: vec![SystemPath::new("altair")],
dependencies: vec![
"jinja2",
"narwhals",
"numpy",
"packaging",
"pandas-stubs",
"pyarrow-stubs",
"pytest",
"scipy-stubs",
"types-jsonschema",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
1000,
)
});
static ALTAIR: Benchmark = Benchmark::new(
RealWorldProject {
name: "altair",
repository: "https://github.com/vega/altair",
commit: "d1f4a1ef89006e5f6752ef1f6df4b7a509336fba",
paths: &["altair"],
dependencies: &[
"jinja2",
"narwhals",
"numpy",
"packaging",
"pandas-stubs",
"pyarrow-stubs",
"pytest",
"scipy-stubs",
"types-jsonschema",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
1000,
);
static COLOUR_SCIENCE: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "colour-science",
repository: "https://github.com/colour-science/colour",
commit: "a17e2335c29e7b6f08080aa4c93cfa9b61f84757",
paths: vec![SystemPath::new("colour")],
dependencies: vec![
"matplotlib",
"numpy",
"pandas-stubs",
"pytest",
"scipy-stubs",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY310,
},
600,
)
});
static COLOUR_SCIENCE: Benchmark = Benchmark::new(
RealWorldProject {
name: "colour-science",
repository: "https://github.com/colour-science/colour",
commit: "a17e2335c29e7b6f08080aa4c93cfa9b61f84757",
paths: &["colour"],
dependencies: &[
"matplotlib",
"numpy",
"pandas-stubs",
"pytest",
"scipy-stubs",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY310,
},
600,
);
static FREQTRADE: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "freqtrade",
repository: "https://github.com/freqtrade/freqtrade",
commit: "2d842ea129e56575852ee0c45383c8c3f706be19",
paths: vec![SystemPath::new("freqtrade")],
dependencies: vec![
"numpy",
"pandas-stubs",
"pydantic",
"sqlalchemy",
"types-cachetools",
"types-filelock",
"types-python-dateutil",
"types-requests",
"types-tabulate",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
400,
)
});
static FREQTRADE: Benchmark = Benchmark::new(
RealWorldProject {
name: "freqtrade",
repository: "https://github.com/freqtrade/freqtrade",
commit: "2d842ea129e56575852ee0c45383c8c3f706be19",
paths: &["freqtrade"],
dependencies: &[
"numpy",
"pandas-stubs",
"pydantic",
"sqlalchemy",
"types-cachetools",
"types-filelock",
"types-python-dateutil",
"types-requests",
"types-tabulate",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
400,
);
static PANDAS: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "pandas",
repository: "https://github.com/pandas-dev/pandas",
commit: "5909621e2267eb67943a95ef5e895e8484c53432",
paths: vec![SystemPath::new("pandas")],
dependencies: vec![
"numpy",
"types-python-dateutil",
"types-pytz",
"types-PyMySQL",
"types-setuptools",
"pytest",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
3000,
)
});
static PANDAS: Benchmark = Benchmark::new(
RealWorldProject {
name: "pandas",
repository: "https://github.com/pandas-dev/pandas",
commit: "5909621e2267eb67943a95ef5e895e8484c53432",
paths: &["pandas"],
dependencies: &[
"numpy",
"types-python-dateutil",
"types-pytz",
"types-PyMySQL",
"types-setuptools",
"pytest",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
3000,
);
static PYDANTIC: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "pydantic",
repository: "https://github.com/pydantic/pydantic",
commit: "0c4a22b64b23dfad27387750cf07487efc45eb05",
paths: vec![SystemPath::new("pydantic")],
dependencies: vec![
"annotated-types",
"pydantic-core",
"typing-extensions",
"typing-inspection",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY39,
},
1000,
)
});
static PYDANTIC: Benchmark = Benchmark::new(
RealWorldProject {
name: "pydantic",
repository: "https://github.com/pydantic/pydantic",
commit: "0c4a22b64b23dfad27387750cf07487efc45eb05",
paths: &["pydantic"],
dependencies: &[
"annotated-types",
"pydantic-core",
"typing-extensions",
"typing-inspection",
],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY39,
},
1000,
);
static SYMPY: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "sympy",
repository: "https://github.com/sympy/sympy",
commit: "22fc107a94eaabc4f6eb31470b39db65abb7a394",
paths: vec![SystemPath::new("sympy")],
dependencies: vec!["mpmath"],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
13000,
)
});
static SYMPY: Benchmark = Benchmark::new(
RealWorldProject {
name: "sympy",
repository: "https://github.com/sympy/sympy",
commit: "22fc107a94eaabc4f6eb31470b39db65abb7a394",
paths: &["sympy"],
dependencies: &["mpmath"],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
13000,
);
static TANJUN: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "tanjun",
repository: "https://github.com/FasterSpeeding/Tanjun",
commit: "69f40db188196bc59516b6c69849c2d85fbc2f4a",
paths: vec![SystemPath::new("tanjun")],
dependencies: vec!["hikari", "alluka"],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
100,
)
});
static TANJUN: Benchmark = Benchmark::new(
RealWorldProject {
name: "tanjun",
repository: "https://github.com/FasterSpeeding/Tanjun",
commit: "69f40db188196bc59516b6c69849c2d85fbc2f4a",
paths: &["tanjun"],
dependencies: &["hikari", "alluka"],
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
100,
);
static STATIC_FRAME: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
Benchmark::new(
RealWorldProject {
name: "static-frame",
repository: "https://github.com/static-frame/static-frame",
commit: "34962b41baca5e7f98f5a758d530bff02748a421",
paths: vec![SystemPath::new("static_frame")],
// N.B. `arraykit` is installed as a dependency during mypy_primer runs,
// but it takes much longer to be installed in a Codspeed run than it does in a mypy_primer run
// (seems to be built from source on the Codspeed CI runners for some reason).
dependencies: vec!["numpy"],
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
600,
)
});
static STATIC_FRAME: Benchmark = Benchmark::new(
RealWorldProject {
name: "static-frame",
repository: "https://github.com/static-frame/static-frame",
commit: "34962b41baca5e7f98f5a758d530bff02748a421",
paths: &["static_frame"],
// N.B. `arraykit` is installed as a dependency during mypy_primer runs,
// but it takes much longer to be installed in a Codspeed run than it does in a mypy_primer run
// (seems to be built from source on the Codspeed CI runners for some reason).
dependencies: &["numpy"],
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
630,
);
#[track_caller]
fn run_single_threaded(bencher: Bencher, benchmark: &Benchmark) {
@@ -245,22 +238,22 @@ fn run_single_threaded(bencher: Bencher, benchmark: &Benchmark) {
});
}
#[bench(args=[&*ALTAIR, &*FREQTRADE, &*PYDANTIC, &*TANJUN], sample_size=2, sample_count=3)]
#[bench(args=[&ALTAIR, &FREQTRADE, &PYDANTIC, &TANJUN], sample_size=2, sample_count=3)]
fn small(bencher: Bencher, benchmark: &Benchmark) {
run_single_threaded(bencher, benchmark);
}
#[bench(args=[&*COLOUR_SCIENCE, &*PANDAS, &*STATIC_FRAME], sample_size=1, sample_count=3)]
#[bench(args=[&COLOUR_SCIENCE, &PANDAS, &STATIC_FRAME], sample_size=1, sample_count=3)]
fn medium(bencher: Bencher, benchmark: &Benchmark) {
run_single_threaded(bencher, benchmark);
}
#[bench(args=[&*SYMPY], sample_size=1, sample_count=2)]
#[bench(args=[&SYMPY], sample_size=1, sample_count=2)]
fn large(bencher: Bencher, benchmark: &Benchmark) {
run_single_threaded(bencher, benchmark);
}
#[bench(args=[&*PYDANTIC], sample_size=3, sample_count=8)]
#[bench(args=[&PYDANTIC], sample_size=3, sample_count=8)]
fn multithreaded(bencher: Bencher, benchmark: &Benchmark) {
let thread_pool = ThreadPoolBuilder::new().build().unwrap();

View File

@@ -30,9 +30,9 @@ pub struct RealWorldProject<'a> {
/// Specific commit hash to checkout
pub commit: &'a str,
/// List of paths within the project to check (`ty check <paths>`)
pub paths: Vec<&'a SystemPath>,
pub paths: &'a [&'a str],
/// Dependencies to install via uv
pub dependencies: Vec<&'a str>,
pub dependencies: &'a [&'a str],
/// Limit candidate packages to those that were uploaded prior to a given point in time (ISO 8601 format).
/// Maps to uv's `exclude-newer`.
pub max_dep_date: &'a str,
@@ -125,9 +125,9 @@ impl<'a> InstalledProject<'a> {
&self.config
}
/// Get the benchmark paths as `SystemPathBuf`
pub fn check_paths(&self) -> &[&SystemPath] {
&self.config.paths
/// Get the benchmark paths
pub fn check_paths(&self) -> &[&str] {
self.config.paths
}
/// Get the virtual environment path
@@ -297,7 +297,7 @@ fn install_dependencies(checkout: &Checkout) -> Result<()> {
"--exclude-newer",
checkout.project().max_dep_date,
])
.args(&checkout.project().dependencies);
.args(checkout.project().dependencies);
let output = cmd
.output()

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
version = "0.13.3"
version = "0.14.0"
publish = false
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -227,3 +227,32 @@ async def read_thing(query: str):
@app.get("/things/{ thing_id : str }")
async def read_thing(query: str):
return {"query": query}
# https://github.com/astral-sh/ruff/issues/20680
# These should NOT trigger FAST003 because FastAPI doesn't recognize them as path parameters
# Non-ASCII characters in parameter name
@app.get("/f1/{用户身份}")
async def f1():
return locals()
# Space in parameter name
@app.get("/f2/{x: str}")
async def f2():
return locals()
# Non-ASCII converter
@app.get("/f3/{complex_number:}")
async def f3():
return locals()
# Mixed non-ASCII characters
@app.get("/f4/{用户_id}")
async def f4():
return locals()
# Space in parameter name with converter
@app.get("/f5/{param: int}")
async def f5():
return locals()

View File

@@ -0,0 +1,8 @@
import logging
variablename = "value"
log = logging.getLogger(__name__)
log.info(f"a" f"b {variablename}")
log.info("a " f"b {variablename}")
log.info("prefix " f"middle {variablename}" f" suffix")

View File

@@ -0,0 +1,3 @@
"a docstring"
from __future__ import annotations
# EOF

View File

@@ -0,0 +1,2 @@
from __future__ import annotations
# EOF

View File

@@ -14,6 +14,6 @@ class Bar:
]
# OK: Allow named expressions in annotations.
# This is no longer allowed on Python 3.14+
x: (y := 1)
print(y)

View File

@@ -13,16 +13,16 @@ CStr2: TypeAlias = Union["C", str] # always okay
# References to a class from inside the class:
class C:
other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file
other: C = ... # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations
other2: "C" = ... # always okay
def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file
def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations
def from_str2(self, s: str) -> "C": ... # always okay
# Circular references:
class A:
foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file
foo: B # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations
foo2: "B" # always okay
bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file
bar: dict[str, B] # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations
bar2: dict[str, "A"] # always okay
class B:

View File

@@ -56,3 +56,11 @@ f"{str(object=3)}"
f"{str(x for x in [])}"
f"{str((x for x in []))}"
# Debug text cases - should not trigger RUF010
f"{str(1)=}"
f"{ascii(1)=}"
f"{repr(1)=}"
f"{str('hello')=}"
f"{ascii('hello')=}"
f"{repr('hello')=}"

View File

@@ -128,3 +128,15 @@ if f"0" in d: # f-string
if k in a.d: # Attribute dict
del a.d[k]
if k in d: # else statement
del d[k]
else:
pass
if k in d: # elif and else statements
del d[k]
elif 0 in d:
del d[0]
else:
pass

View File

@@ -67,17 +67,25 @@ impl<'a> Importer<'a> {
/// Add an import statement to import the given module.
///
/// If there are no existing imports, the new import will be added at the top
/// of the file. Otherwise, it will be added after the most recent top-level
/// import statement.
/// of the file. If there are future imports, the new import will be added
/// after the last future import. Otherwise, it will be added after the most
/// recent top-level import statement.
pub(crate) fn add_import(&self, import: &NameImport, at: TextSize) -> Edit {
let required_import = import.to_string();
if let Some(stmt) = self.preceding_import(at) {
// Insert after the last top-level import.
Insertion::end_of_statement(stmt, self.source, self.stylist).into_edit(&required_import)
} else {
// Insert at the start of the file.
Insertion::start_of_file(self.python_ast, self.source, self.stylist)
.into_edit(&required_import)
// Check if there are any future imports that we need to respect
if let Some(last_future_import) = self.find_last_future_import() {
// Insert after the last future import
Insertion::end_of_statement(last_future_import, self.source, self.stylist)
.into_edit(&required_import)
} else {
// Insert at the start of the file.
Insertion::start_of_file(self.python_ast, self.source, self.stylist)
.into_edit(&required_import)
}
}
}
@@ -524,6 +532,18 @@ impl<'a> Importer<'a> {
}
}
/// Find the last `from __future__` import statement in the AST.
fn find_last_future_import(&self) -> Option<&'a Stmt> {
let mut body = self.python_ast.iter().peekable();
let _docstring = body.next_if(|stmt| ast::helpers::is_docstring_stmt(stmt));
body.take_while(|stmt| {
stmt.as_import_from_stmt()
.is_some_and(|import_from| import_from.module.as_deref() == Some("__future__"))
})
.last()
}
/// Add a `from __future__ import annotations` import.
pub(crate) fn add_future_import(&self) -> Edit {
let import = &NameImport::ImportFrom(MemberNameImport::member(

View File

@@ -27,14 +27,13 @@ use crate::fix::{FixResult, fix_file};
use crate::message::create_syntax_error_diagnostic;
use crate::noqa::add_noqa;
use crate::package::PackageRoot;
use crate::preview::is_py314_support_enabled;
use crate::registry::Rule;
#[cfg(any(feature = "test-rules", test))]
use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule};
use crate::settings::types::UnsafeFixes;
use crate::settings::{LinterSettings, TargetVersion, flags};
use crate::source_kind::SourceKind;
use crate::{Locator, directives, fs, warn_user_once};
use crate::{Locator, directives, fs};
pub(crate) mod float;
@@ -442,14 +441,6 @@ pub fn lint_only(
) -> LinterResult {
let target_version = settings.resolve_target_version(path);
if matches!(target_version, TargetVersion(Some(PythonVersion::PY314)))
&& !is_py314_support_enabled(settings)
{
warn_user_once!(
"Support for Python 3.14 is in preview and may undergo breaking changes. Enable `preview` to remove this warning."
);
}
let parsed = source.into_parsed(source_kind, source_type, target_version.parser_version());
// Map row and column locations to byte slices (lazily).
@@ -551,14 +542,6 @@ pub fn lint_fix<'a>(
let target_version = settings.resolve_target_version(path);
if matches!(target_version, TargetVersion(Some(PythonVersion::PY314)))
&& !is_py314_support_enabled(settings)
{
warn_user_once!(
"Support for Python 3.14 is in preview and may undergo breaking changes. Enable `preview` to remove this warning."
);
}
// Continuously fix until the source code stabilizes.
loop {
// Parse once.

View File

@@ -7,10 +7,6 @@
use crate::settings::LinterSettings;
pub(crate) const fn is_py314_support_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled()
}
// Rule-specific behavior
// https://github.com/astral-sh/ruff/pull/15541
@@ -259,3 +255,13 @@ pub(crate) const fn is_b006_unsafe_fix_preserve_assignment_expr_enabled(
) -> bool {
settings.preview.is_enabled()
}
// https://github.com/astral-sh/ruff/pull/20520
pub(crate) const fn is_fix_read_whole_file_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled()
}
// https://github.com/astral-sh/ruff/pull/20520
pub(crate) const fn is_fix_write_whole_file_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled()
}

View File

@@ -8,9 +8,12 @@ mod tests {
use anyhow::Result;
use test_case::test_case;
use ruff_python_ast::PythonVersion;
use crate::registry::Rule;
use crate::settings::LinterSettings;
use crate::test::test_path;
use crate::{assert_diagnostics, settings};
use crate::{assert_diagnostics, assert_diagnostics_diff};
#[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))]
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))]
@@ -20,12 +23,35 @@ mod tests {
let snapshot = format!("{}_{}", rule_code.name(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("fastapi").join(path).as_path(),
&settings::LinterSettings::for_rule(rule_code),
&LinterSettings::for_rule(rule_code),
)?;
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}
#[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))]
#[test_case(Rule::FastApiUnusedPathParameter, Path::new("FAST003.py"))]
fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"deferred_annotations_diff_{}_{}",
rule_code.name(),
path.to_string_lossy()
);
assert_diagnostics_diff!(
snapshot,
Path::new("fastapi").join(path).as_path(),
&LinterSettings {
unresolved_target_version: PythonVersion::PY313.into(),
..LinterSettings::for_rule(rule_code)
},
&LinterSettings {
unresolved_target_version: PythonVersion::PY314.into(),
..LinterSettings::for_rule(rule_code)
},
);
Ok(())
}
// FAST002 autofixes use `typing_extensions` on Python 3.8,
// since `typing.Annotated` was added in Python 3.9
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))]
@@ -34,9 +60,9 @@ mod tests {
let snapshot = format!("{}_{}_py38", rule_code.name(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("fastapi").join(path).as_path(),
&settings::LinterSettings {
unresolved_target_version: ruff_python_ast::PythonVersion::PY38.into(),
..settings::LinterSettings::for_rule(rule_code)
&LinterSettings {
unresolved_target_version: PythonVersion::PY38.into(),
..LinterSettings::for_rule(rule_code)
},
)?;
assert_diagnostics!(snapshot, diagnostics);

View File

@@ -1,12 +1,12 @@
use std::iter::Peekable;
use std::ops::Range;
use std::str::CharIndices;
use std::sync::LazyLock;
use regex::{CaptureMatches, Regex};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_python_ast::{Arguments, Expr, ExprCall, ExprSubscript, Parameter, ParameterWithDefault};
use ruff_python_semantic::{BindingKind, Modules, ScopeKind, SemanticModel};
use ruff_python_stdlib::identifiers::is_identifier;
use ruff_text_size::{Ranged, TextSize};
use crate::Fix;
@@ -165,11 +165,6 @@ pub(crate) fn fastapi_unused_path_parameter(
// Check if any of the path parameters are not in the function signature.
for (path_param, range) in path_params {
// Ignore invalid identifiers (e.g., `user-id`, as opposed to `user_id`)
if !is_identifier(path_param) {
continue;
}
// If the path parameter is already in the function or the dependency signature,
// we don't need to do anything.
if named_args.contains(&path_param) {
@@ -461,15 +456,19 @@ fn parameter_alias<'a>(parameter: &'a Parameter, semantic: &SemanticModel) -> Op
/// the parameter name. For example, `/{x}` is a valid parameter, but `/{ x }` is treated literally.
#[derive(Debug)]
struct PathParamIterator<'a> {
input: &'a str,
chars: Peekable<CharIndices<'a>>,
inner: CaptureMatches<'a, 'a>,
}
impl<'a> PathParamIterator<'a> {
fn new(input: &'a str) -> Self {
PathParamIterator {
input,
chars: input.char_indices().peekable(),
/// Matches the Starlette pattern for path parameters with optional converters from
/// <https://github.com/Kludex/starlette/blob/e18637c68e36d112b1983bc0c8b663681e6a4c50/starlette/routing.py#L121>
static FASTAPI_PATH_PARAM_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_]*)(?::[a-zA-Z_][a-zA-Z0-9_]*)?\}").unwrap()
});
Self {
inner: FASTAPI_PATH_PARAM_REGEX.captures_iter(input),
}
}
}
@@ -478,19 +477,10 @@ impl<'a> Iterator for PathParamIterator<'a> {
type Item = (&'a str, Range<usize>);
fn next(&mut self) -> Option<Self::Item> {
while let Some((start, c)) = self.chars.next() {
if c == '{' {
if let Some((end, _)) = self.chars.by_ref().find(|&(_, ch)| ch == '}') {
let param_content = &self.input[start + 1..end];
// We ignore text after a colon, since those are path converters
// See also: https://fastapi.tiangolo.com/tutorial/path-params/?h=path#path-convertor
let param_name_end = param_content.find(':').unwrap_or(param_content.len());
let param_name = &param_content[..param_name_end];
return Some((param_name, start..end + 1));
}
}
}
None
self.inner
.next()
// Extract the first capture group (the path parameter), but return the range of the
// whole match (everything in braces and including the braces themselves).
.and_then(|capture| Some((capture.get(1)?.as_str(), capture.get(0)?.range())))
}
}

View File

@@ -0,0 +1,213 @@
---
source: crates/ruff_linter/src/rules/fastapi/mod.rs
---
--- Linter settings ---
-linter.unresolved_target_version = 3.13
+linter.unresolved_target_version = 3.14
--- Summary ---
Removed: 10
Added: 0
--- Removed ---
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:17:22
|
17 | @app.post("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
18 | async def create_item(item: Item) -> Item:
19 | return item
|
help: Remove argument
14 | # Errors
15 |
16 |
- @app.post("/items/", response_model=Item)
17 + @app.post("/items/")
18 | async def create_item(item: Item) -> Item:
19 | return item
20 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:22:22
|
22 | @app.post("/items/", response_model=list[Item])
| ^^^^^^^^^^^^^^^^^^^^^^^^^
23 | async def create_item(item: Item) -> list[Item]:
24 | return item
|
help: Remove argument
19 | return item
20 |
21 |
- @app.post("/items/", response_model=list[Item])
22 + @app.post("/items/")
23 | async def create_item(item: Item) -> list[Item]:
24 | return item
25 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:27:22
|
27 | @app.post("/items/", response_model=List[Item])
| ^^^^^^^^^^^^^^^^^^^^^^^^^
28 | async def create_item(item: Item) -> List[Item]:
29 | return item
|
help: Remove argument
24 | return item
25 |
26 |
- @app.post("/items/", response_model=List[Item])
27 + @app.post("/items/")
28 | async def create_item(item: Item) -> List[Item]:
29 | return item
30 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:32:22
|
32 | @app.post("/items/", response_model=Dict[str, Item])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33 | async def create_item(item: Item) -> Dict[str, Item]:
34 | return item
|
help: Remove argument
29 | return item
30 |
31 |
- @app.post("/items/", response_model=Dict[str, Item])
32 + @app.post("/items/")
33 | async def create_item(item: Item) -> Dict[str, Item]:
34 | return item
35 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:37:22
|
37 | @app.post("/items/", response_model=str)
| ^^^^^^^^^^^^^^^^^^
38 | async def create_item(item: Item) -> str:
39 | return item
|
help: Remove argument
34 | return item
35 |
36 |
- @app.post("/items/", response_model=str)
37 + @app.post("/items/")
38 | async def create_item(item: Item) -> str:
39 | return item
40 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:42:21
|
42 | @app.get("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
43 | async def create_item(item: Item) -> Item:
44 | return item
|
help: Remove argument
39 | return item
40 |
41 |
- @app.get("/items/", response_model=Item)
42 + @app.get("/items/")
43 | async def create_item(item: Item) -> Item:
44 | return item
45 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:47:21
|
47 | @app.get("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
48 | @app.post("/items/", response_model=Item)
49 | async def create_item(item: Item) -> Item:
|
help: Remove argument
44 | return item
45 |
46 |
- @app.get("/items/", response_model=Item)
47 + @app.get("/items/")
48 | @app.post("/items/", response_model=Item)
49 | async def create_item(item: Item) -> Item:
50 | return item
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:48:22
|
47 | @app.get("/items/", response_model=Item)
48 | @app.post("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
49 | async def create_item(item: Item) -> Item:
50 | return item
|
help: Remove argument
45 |
46 |
47 | @app.get("/items/", response_model=Item)
- @app.post("/items/", response_model=Item)
48 + @app.post("/items/")
49 | async def create_item(item: Item) -> Item:
50 | return item
51 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:53:24
|
53 | @router.get("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
54 | async def create_item(item: Item) -> Item:
55 | return item
|
help: Remove argument
50 | return item
51 |
52 |
- @router.get("/items/", response_model=Item)
53 + @router.get("/items/")
54 | async def create_item(item: Item) -> Item:
55 | return item
56 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:118:23
|
116 | def setup_app(app_arg: FastAPI, non_app: str) -> None:
117 | # Error
118 | @app_arg.get("/", response_model=str)
| ^^^^^^^^^^^^^^^^^^
119 | async def get_root() -> str:
120 | return "Hello World!"
|
help: Remove argument
115 |
116 | def setup_app(app_arg: FastAPI, non_app: str) -> None:
117 | # Error
- @app_arg.get("/", response_model=str)
118 + @app_arg.get("/")
119 | async def get_root() -> str:
120 | return "Hello World!"
121 |
note: This is an unsafe fix and may change runtime behavior

View File

@@ -0,0 +1,74 @@
---
source: crates/ruff_linter/src/rules/fastapi/mod.rs
---
--- Linter settings ---
-linter.unresolved_target_version = 3.13
+linter.unresolved_target_version = 3.14
--- Summary ---
Removed: 3
Added: 0
--- Removed ---
FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature
--> FAST003.py:158:19
|
157 | ### Errors
158 | @app.get("/things/{thing_id}")
| ^^^^^^^^^^
159 | async def single(other: Annotated[str, Depends(something_else)]): ...
160 | @app.get("/things/{thing_id}")
|
help: Add `thing_id` to function signature
156 |
157 | ### Errors
158 | @app.get("/things/{thing_id}")
- async def single(other: Annotated[str, Depends(something_else)]): ...
159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ...
160 | @app.get("/things/{thing_id}")
161 | async def default(other: str = Depends(something_else)): ...
162 |
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature
--> FAST003.py:197:12
|
196 | # Errors
197 | @app.get("/{id}")
| ^^^^
198 | async def get_id_pydantic_full(
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
|
help: Add `id` to function signature
196 | # Errors
197 | @app.get("/{id}")
198 | async def get_id_pydantic_full(
- params: Annotated[PydanticParams, Depends(PydanticParams)],
199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id,
200 | ): ...
201 | @app.get("/{id}")
202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature
--> FAST003.py:201:12
|
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
200 | ): ...
201 | @app.get("/{id}")
| ^^^^
202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
203 | @app.get("/{id}")
|
help: Add `id` to function signature
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
200 | ): ...
201 | @app.get("/{id}")
- async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ...
203 | @app.get("/{id}")
204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ...
205 |
note: This is an unsafe fix and may change runtime behavior

View File

@@ -1,195 +1,4 @@
---
source: crates/ruff_linter/src/rules/fastapi/mod.rs
---
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:17:22
|
17 | @app.post("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
18 | async def create_item(item: Item) -> Item:
19 | return item
|
help: Remove argument
14 | # Errors
15 |
16 |
- @app.post("/items/", response_model=Item)
17 + @app.post("/items/")
18 | async def create_item(item: Item) -> Item:
19 | return item
20 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:22:22
|
22 | @app.post("/items/", response_model=list[Item])
| ^^^^^^^^^^^^^^^^^^^^^^^^^
23 | async def create_item(item: Item) -> list[Item]:
24 | return item
|
help: Remove argument
19 | return item
20 |
21 |
- @app.post("/items/", response_model=list[Item])
22 + @app.post("/items/")
23 | async def create_item(item: Item) -> list[Item]:
24 | return item
25 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:27:22
|
27 | @app.post("/items/", response_model=List[Item])
| ^^^^^^^^^^^^^^^^^^^^^^^^^
28 | async def create_item(item: Item) -> List[Item]:
29 | return item
|
help: Remove argument
24 | return item
25 |
26 |
- @app.post("/items/", response_model=List[Item])
27 + @app.post("/items/")
28 | async def create_item(item: Item) -> List[Item]:
29 | return item
30 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:32:22
|
32 | @app.post("/items/", response_model=Dict[str, Item])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33 | async def create_item(item: Item) -> Dict[str, Item]:
34 | return item
|
help: Remove argument
29 | return item
30 |
31 |
- @app.post("/items/", response_model=Dict[str, Item])
32 + @app.post("/items/")
33 | async def create_item(item: Item) -> Dict[str, Item]:
34 | return item
35 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:37:22
|
37 | @app.post("/items/", response_model=str)
| ^^^^^^^^^^^^^^^^^^
38 | async def create_item(item: Item) -> str:
39 | return item
|
help: Remove argument
34 | return item
35 |
36 |
- @app.post("/items/", response_model=str)
37 + @app.post("/items/")
38 | async def create_item(item: Item) -> str:
39 | return item
40 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:42:21
|
42 | @app.get("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
43 | async def create_item(item: Item) -> Item:
44 | return item
|
help: Remove argument
39 | return item
40 |
41 |
- @app.get("/items/", response_model=Item)
42 + @app.get("/items/")
43 | async def create_item(item: Item) -> Item:
44 | return item
45 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:47:21
|
47 | @app.get("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
48 | @app.post("/items/", response_model=Item)
49 | async def create_item(item: Item) -> Item:
|
help: Remove argument
44 | return item
45 |
46 |
- @app.get("/items/", response_model=Item)
47 + @app.get("/items/")
48 | @app.post("/items/", response_model=Item)
49 | async def create_item(item: Item) -> Item:
50 | return item
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:48:22
|
47 | @app.get("/items/", response_model=Item)
48 | @app.post("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
49 | async def create_item(item: Item) -> Item:
50 | return item
|
help: Remove argument
45 |
46 |
47 | @app.get("/items/", response_model=Item)
- @app.post("/items/", response_model=Item)
48 + @app.post("/items/")
49 | async def create_item(item: Item) -> Item:
50 | return item
51 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:53:24
|
53 | @router.get("/items/", response_model=Item)
| ^^^^^^^^^^^^^^^^^^^
54 | async def create_item(item: Item) -> Item:
55 | return item
|
help: Remove argument
50 | return item
51 |
52 |
- @router.get("/items/", response_model=Item)
53 + @router.get("/items/")
54 | async def create_item(item: Item) -> Item:
55 | return item
56 |
note: This is an unsafe fix and may change runtime behavior
FAST001 [*] FastAPI route with redundant `response_model` argument
--> FAST001.py:118:23
|
116 | def setup_app(app_arg: FastAPI, non_app: str) -> None:
117 | # Error
118 | @app_arg.get("/", response_model=str)
| ^^^^^^^^^^^^^^^^^^
119 | async def get_root() -> str:
120 | return "Hello World!"
|
help: Remove argument
115 |
116 | def setup_app(app_arg: FastAPI, non_app: str) -> None:
117 | # Error
- @app_arg.get("/", response_model=str)
118 + @app_arg.get("/")
119 | async def get_root() -> str:
120 | return "Hello World!"
121 |
note: This is an unsafe fix and may change runtime behavior

View File

@@ -324,26 +324,6 @@ help: Add `name` to function signature
91 |
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature
--> FAST003.py:158:19
|
157 | ### Errors
158 | @app.get("/things/{thing_id}")
| ^^^^^^^^^^
159 | async def single(other: Annotated[str, Depends(something_else)]): ...
160 | @app.get("/things/{thing_id}")
|
help: Add `thing_id` to function signature
156 |
157 | ### Errors
158 | @app.get("/things/{thing_id}")
- async def single(other: Annotated[str, Depends(something_else)]): ...
159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ...
160 | @app.get("/things/{thing_id}")
161 | async def default(other: str = Depends(something_else)): ...
162 |
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `thing_id` appears in route path, but not in `default` signature
--> FAST003.py:160:19
|
@@ -364,47 +344,6 @@ help: Add `thing_id` to function signature
164 | ### No errors
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature
--> FAST003.py:197:12
|
196 | # Errors
197 | @app.get("/{id}")
| ^^^^
198 | async def get_id_pydantic_full(
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
|
help: Add `id` to function signature
196 | # Errors
197 | @app.get("/{id}")
198 | async def get_id_pydantic_full(
- params: Annotated[PydanticParams, Depends(PydanticParams)],
199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id,
200 | ): ...
201 | @app.get("/{id}")
202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature
--> FAST003.py:201:12
|
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
200 | ): ...
201 | @app.get("/{id}")
| ^^^^
202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
203 | @app.get("/{id}")
|
help: Add `id` to function signature
199 | params: Annotated[PydanticParams, Depends(PydanticParams)],
200 | ): ...
201 | @app.get("/{id}")
- async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ...
203 | @app.get("/{id}")
204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ...
205 |
note: This is an unsafe fix and may change runtime behavior
FAST003 [*] Parameter `id` appears in route path, but not in `get_id_init_not_annotated` signature
--> FAST003.py:203:12
|

View File

@@ -1091,9 +1091,12 @@ fn suspicious_function(
] => checker.report_diagnostic_if_enabled(SuspiciousInsecureCipherModeUsage, range),
// Mktemp
["tempfile", "mktemp"] => {
checker.report_diagnostic_if_enabled(SuspiciousMktempUsage, range)
}
["tempfile", "mktemp"] => checker
.report_diagnostic_if_enabled(SuspiciousMktempUsage, range)
.map(|mut diagnostic| {
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
diagnostic
}),
// Eval
["" | "builtins", "eval"] => {

View File

@@ -34,9 +34,10 @@ use crate::{AlwaysFixableViolation, Applicability, Fix};
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe for `map` calls that contain
/// `**kwargs`, as adding a `strict` keyword argument to such a call may lead
/// to a duplicate keyword argument error.
/// This rule's fix is marked as unsafe. While adding `strict=False` preserves
/// the runtime behavior, it can obscure situations where the iterables are of
/// unequal length. Ruff prefers to alert users so they can choose the intended
/// behavior themselves.
///
/// ## References
/// - [Python documentation: `map`](https://docs.python.org/3/library/functions.html#map)
@@ -73,17 +74,7 @@ pub(crate) fn map_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
checker.comment_ranges(),
checker.locator().contents(),
),
// If the function call contains `**kwargs`, mark the fix as unsafe.
if call
.arguments
.keywords
.iter()
.any(|keyword| keyword.arg.is_none())
{
Applicability::Unsafe
} else {
Applicability::Safe
},
Applicability::Unsafe,
));
}
}

View File

@@ -31,9 +31,10 @@ use crate::{AlwaysFixableViolation, Applicability, Fix};
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe for `zip` calls that contain
/// `**kwargs`, as adding a `strict` keyword argument to such a call may lead
/// to a duplicate keyword argument error.
/// This rule's fix is marked as unsafe. While adding `strict=False` preserves
/// the runtime behavior, it can obscure situations where the iterables are of
/// unequal length. Ruff prefers to alert users so they can choose the intended
/// behavior themselves.
///
/// ## References
/// - [Python documentation: `zip`](https://docs.python.org/3/library/functions.html#zip)
@@ -68,17 +69,7 @@ pub(crate) fn zip_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
checker.comment_ranges(),
checker.locator().contents(),
),
// If the function call contains `**kwargs`, mark the fix as unsafe.
if call
.arguments
.keywords
.iter()
.any(|keyword| keyword.arg.is_none())
{
Applicability::Unsafe
} else {
Applicability::Safe
},
Applicability::Unsafe,
));
}
}

View File

@@ -1,5 +1,6 @@
---
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
assertion_line: 156
---
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:4:1
@@ -19,6 +20,7 @@ help: Add explicit value for parameter `strict=`
5 | zip(range(3))
6 | zip("a", "b")
7 | zip("a", "b", *zip("c"))
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:5:1
@@ -39,6 +41,7 @@ help: Add explicit value for parameter `strict=`
6 | zip("a", "b")
7 | zip("a", "b", *zip("c"))
8 | zip(zip("a"), strict=False)
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:6:1
@@ -59,6 +62,7 @@ help: Add explicit value for parameter `strict=`
7 | zip("a", "b", *zip("c"))
8 | zip(zip("a"), strict=False)
9 | zip(zip("a", strict=True))
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:7:1
@@ -79,6 +83,7 @@ help: Add explicit value for parameter `strict=`
8 | zip(zip("a"), strict=False)
9 | zip(zip("a", strict=True))
10 |
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:7:16
@@ -99,6 +104,7 @@ help: Add explicit value for parameter `strict=`
8 | zip(zip("a"), strict=False)
9 | zip(zip("a", strict=True))
10 |
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:8:5
@@ -118,6 +124,7 @@ help: Add explicit value for parameter `strict=`
9 | zip(zip("a", strict=True))
10 |
11 | # OK
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:9:1
@@ -138,6 +145,7 @@ help: Add explicit value for parameter `strict=`
10 |
11 | # OK
12 | zip(range(3), strict=True)
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:24:1
@@ -156,6 +164,7 @@ help: Add explicit value for parameter `strict=`
25 | zip([1, 2, 3], repeat(1, times=4))
26 |
27 | import builtins
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:25:1
@@ -176,6 +185,7 @@ help: Add explicit value for parameter `strict=`
26 |
27 | import builtins
28 | # Still an error even though it uses the qualified name
note: This is an unsafe fix and may change runtime behavior
B905 [*] `zip()` without an explicit `strict=` parameter
--> B905.py:29:1
@@ -191,3 +201,4 @@ help: Add explicit value for parameter `strict=`
28 | # Still an error even though it uses the qualified name
- builtins.zip([1, 2, 3])
29 + builtins.zip([1, 2, 3], strict=False)
note: This is an unsafe fix and may change runtime behavior

View File

@@ -1,5 +1,6 @@
---
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
assertion_line: 112
---
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:5:1
@@ -20,6 +21,7 @@ help: Add explicit value for parameter `strict=`
6 | map(lambda x, y, z: x + y + z, [1, 2, 3], [4, 5, 6], [7, 8, 9])
7 | map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6], *map(lambda x: x, [7, 8, 9]))
8 | map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6], *map(lambda x: x, [7, 8, 9]), strict=False)
note: This is an unsafe fix and may change runtime behavior
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:6:1
@@ -40,6 +42,7 @@ help: Add explicit value for parameter `strict=`
7 | map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6], *map(lambda x: x, [7, 8, 9]))
8 | map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6], *map(lambda x: x, [7, 8, 9]), strict=False)
9 | map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6], *map(lambda x: x, [7, 8, 9], strict=True))
note: This is an unsafe fix and may change runtime behavior
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:7:1
@@ -61,6 +64,7 @@ help: Add explicit value for parameter `strict=`
9 | map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6], *map(lambda x: x, [7, 8, 9], strict=True))
10 |
11 | # Errors (limited iterators).
note: This is an unsafe fix and may change runtime behavior
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:9:1
@@ -81,6 +85,7 @@ help: Add explicit value for parameter `strict=`
10 |
11 | # Errors (limited iterators).
12 | map(lambda x, y: x + y, [1, 2, 3], repeat(1, 1))
note: This is an unsafe fix and may change runtime behavior
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:12:1
@@ -99,6 +104,7 @@ help: Add explicit value for parameter `strict=`
13 | map(lambda x, y: x + y, [1, 2, 3], repeat(1, times=4))
14 |
15 | import builtins
note: This is an unsafe fix and may change runtime behavior
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:13:1
@@ -119,6 +125,7 @@ help: Add explicit value for parameter `strict=`
14 |
15 | import builtins
16 | # Still an error even though it uses the qualified name
note: This is an unsafe fix and may change runtime behavior
B912 [*] `map()` without an explicit `strict=` parameter
--> B912.py:17:1
@@ -139,3 +146,4 @@ help: Add explicit value for parameter `strict=`
18 |
19 | # OK
20 | map(lambda x: x, [1, 2, 3], strict=True)
note: This is an unsafe fix and may change runtime behavior

View File

@@ -10,12 +10,12 @@ mod tests {
use anyhow::Result;
use test_case::test_case;
use crate::assert_diagnostics;
use crate::registry::Rule;
use crate::rules::flake8_builtins;
use crate::settings::LinterSettings;
use crate::settings::types::PreviewMode;
use crate::test::{test_path, test_resource_path};
use crate::{assert_diagnostics, assert_diagnostics_diff};
use ruff_python_ast::PythonVersion;
#[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))]
@@ -64,6 +64,28 @@ mod tests {
Ok(())
}
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"deferred_annotations_diff_{}_{}",
rule_code.name(),
path.to_string_lossy()
);
assert_diagnostics_diff!(
snapshot,
Path::new("flake8_builtins").join(path).as_path(),
&LinterSettings {
unresolved_target_version: PythonVersion::PY313.into(),
..LinterSettings::for_rule(rule_code)
},
&LinterSettings {
unresolved_target_version: PythonVersion::PY314.into(),
..LinterSettings::for_rule(rule_code)
},
);
Ok(())
}
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(

View File

@@ -1,22 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A003 Python builtin is shadowed by method `str` from line 14
--> A003.py:17:31
|
15 | pass
16 |
17 | def method_usage(self) -> str:
| ^^^
18 | pass
|
A003 Python builtin is shadowed by class attribute `id` from line 3
--> A003.py:20:34
|
18 | pass
19 |
20 | def attribute_usage(self) -> id:
| ^^
21 | pass
|

View File

@@ -1,12 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A003 Python builtin is shadowed by method `str` from line 14
--> A003.py:17:31
|
15 | pass
16 |
17 | def method_usage(self) -> str:
| ^^^
18 | pass
|

View File

@@ -0,0 +1,32 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
--- Linter settings ---
-linter.unresolved_target_version = 3.13
+linter.unresolved_target_version = 3.14
--- Summary ---
Removed: 2
Added: 0
--- Removed ---
A003 Python builtin is shadowed by method `str` from line 14
--> A003.py:17:31
|
15 | pass
16 |
17 | def method_usage(self) -> str:
| ^^^
18 | pass
|
A003 Python builtin is shadowed by class attribute `id` from line 3
--> A003.py:20:34
|
18 | pass
19 |
20 | def attribute_usage(self) -> id:
| ^^
21 | pass
|

View File

@@ -1,26 +1,6 @@
---
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
---
A003 Python builtin is shadowed by method `str` from line 14
--> A003.py:17:31
|
15 | pass
16 |
17 | def method_usage(self) -> str:
| ^^^
18 | pass
|
A003 Python builtin is shadowed by class attribute `id` from line 3
--> A003.py:20:34
|
18 | pass
19 |
20 | def attribute_usage(self) -> id:
| ^^
21 | pass
|
A003 Python builtin is shadowed by method `property` from line 26
--> A003.py:31:7
|

View File

@@ -11,15 +11,15 @@ use crate::checkers::ast::Checker;
/// Checks for usage of `datetime.date.fromtimestamp()`.
///
/// ## Why is this bad?
/// Python datetime objects can be naive or timezone-aware. While an aware
/// Python date objects are naive, that is, not timezone-aware. While an aware
/// object represents a specific moment in time, a naive object does not
/// contain enough information to unambiguously locate itself relative to other
/// datetime objects. Since this can lead to errors, it is recommended to
/// always use timezone-aware objects.
///
/// `datetime.date.fromtimestamp(ts)` returns a naive datetime object.
/// Instead, use `datetime.datetime.fromtimestamp(ts, tz=...)` to create a
/// timezone-aware object.
/// `datetime.date.fromtimestamp(ts)` returns a naive date object.
/// Instead, use `datetime.datetime.fromtimestamp(ts, tz=...).date()` to
/// create a timezone-aware datetime object and retrieve its date component.
///
/// ## Example
/// ```python
@@ -32,14 +32,14 @@ use crate::checkers::ast::Checker;
/// ```python
/// import datetime
///
/// datetime.datetime.fromtimestamp(946684800, tz=datetime.timezone.utc)
/// datetime.datetime.fromtimestamp(946684800, tz=datetime.timezone.utc).date()
/// ```
///
/// Or, for Python 3.11 and later:
/// ```python
/// import datetime
///
/// datetime.datetime.fromtimestamp(946684800, tz=datetime.UTC)
/// datetime.datetime.fromtimestamp(946684800, tz=datetime.UTC).date()
/// ```
///
/// ## References

View File

@@ -11,14 +11,15 @@ use crate::checkers::ast::Checker;
/// Checks for usage of `datetime.date.today()`.
///
/// ## Why is this bad?
/// Python datetime objects can be naive or timezone-aware. While an aware
/// Python date objects are naive, that is, not timezone-aware. While an aware
/// object represents a specific moment in time, a naive object does not
/// contain enough information to unambiguously locate itself relative to other
/// datetime objects. Since this can lead to errors, it is recommended to
/// always use timezone-aware objects.
///
/// `datetime.date.today` returns a naive datetime object. Instead, use
/// `datetime.datetime.now(tz=...).date()` to create a timezone-aware object.
/// `datetime.date.today` returns a naive date object without taking timezones
/// into account. Instead, use `datetime.datetime.now(tz=...).date()` to
/// create a timezone-aware object and retrieve its date component.
///
/// ## Example
/// ```python

View File

@@ -42,6 +42,9 @@ use crate::rules::flake8_datetimez::helpers;
///
/// datetime.datetime.now(tz=datetime.UTC)
/// ```
///
/// ## References
/// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects)
#[derive(ViolationMetadata)]
pub(crate) struct CallDatetimeToday;

View File

@@ -41,6 +41,9 @@ use crate::rules::flake8_datetimez::helpers::{self, DatetimeModuleAntipattern};
///
/// datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.UTC)
/// ```
///
/// ## References
/// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects)
#[derive(ViolationMetadata)]
pub(crate) struct CallDatetimeWithoutTzinfo(DatetimeModuleAntipattern);

View File

@@ -38,6 +38,9 @@ use crate::checkers::ast::Checker;
///
/// datetime.datetime.max.replace(tzinfo=datetime.UTC)
/// ```
///
/// ## References
/// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects)
#[derive(ViolationMetadata)]
pub(crate) struct DatetimeMinMax {
min_max: MinMax,

View File

@@ -23,6 +23,7 @@ mod tests {
#[test_case(Path::new("G003.py"))]
#[test_case(Path::new("G004.py"))]
#[test_case(Path::new("G004_arg_order.py"))]
#[test_case(Path::new("G004_implicit_concat.py"))]
#[test_case(Path::new("G010.py"))]
#[test_case(Path::new("G101_1.py"))]
#[test_case(Path::new("G101_2.py"))]
@@ -52,6 +53,7 @@ mod tests {
#[test_case(Rule::LoggingFString, Path::new("G004.py"))]
#[test_case(Rule::LoggingFString, Path::new("G004_arg_order.py"))]
#[test_case(Rule::LoggingFString, Path::new("G004_implicit_concat.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"preview__{}_{}",

View File

@@ -42,38 +42,52 @@ fn logging_f_string(
// Default to double quotes if we can't determine it.
let quote_str = f_string
.value
.f_strings()
.iter()
.map(|part| match part {
ast::FStringPart::Literal(literal) => literal.flags.quote_str(),
ast::FStringPart::FString(f) => f.flags.quote_str(),
})
.next()
.map(|f| f.flags.quote_str())
.unwrap_or("\"");
for f in f_string.value.f_strings() {
for element in &f.elements {
match element {
InterpolatedStringElement::Literal(lit) => {
// If the literal text contains a '%' placeholder, bail out: mixing
// f-string interpolation with '%' placeholders is ambiguous for our
// automatic conversion, so don't offer a fix for this case.
if lit.value.as_ref().contains('%') {
return;
}
format_string.push_str(lit.value.as_ref());
for part in &f_string.value {
match part {
ast::FStringPart::Literal(literal) => {
let literal_text = literal.as_str();
if literal_text.contains('%') {
return;
}
InterpolatedStringElement::Interpolation(interpolated) => {
if interpolated.format_spec.is_some()
|| !matches!(
interpolated.conversion,
ruff_python_ast::ConversionFlag::None
)
{
return;
}
match interpolated.expression.as_ref() {
Expr::Name(name) => {
format_string.push_str("%s");
args.push(name.id.as_str());
format_string.push_str(literal_text);
}
ast::FStringPart::FString(f) => {
for element in &f.elements {
match element {
InterpolatedStringElement::Literal(lit) => {
// If the literal text contains a '%' placeholder, bail out: mixing
// f-string interpolation with '%' placeholders is ambiguous for our
// automatic conversion, so don't offer a fix for this case.
if lit.value.as_ref().contains('%') {
return;
}
format_string.push_str(lit.value.as_ref());
}
InterpolatedStringElement::Interpolation(interpolated) => {
if interpolated.format_spec.is_some()
|| !matches!(
interpolated.conversion,
ruff_python_ast::ConversionFlag::None
)
{
return;
}
match interpolated.expression.as_ref() {
Expr::Name(name) => {
format_string.push_str("%s");
args.push(name.id.as_str());
}
_ => return,
}
}
_ => return,
}
}
}

View File

@@ -0,0 +1,35 @@
---
source: crates/ruff_linter/src/rules/flake8_logging_format/mod.rs
assertion_line: 50
---
G004 Logging statement uses f-string
--> G004_implicit_concat.py:6:10
|
5 | log = logging.getLogger(__name__)
6 | log.info(f"a" f"b {variablename}")
| ^^^^^^^^^^^^^^^^^^^^^^^^
7 | log.info("a " f"b {variablename}")
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
help: Convert to lazy `%` formatting
G004 Logging statement uses f-string
--> G004_implicit_concat.py:7:10
|
5 | log = logging.getLogger(__name__)
6 | log.info(f"a" f"b {variablename}")
7 | log.info("a " f"b {variablename}")
| ^^^^^^^^^^^^^^^^^^^^^^^^
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
help: Convert to lazy `%` formatting
G004 Logging statement uses f-string
--> G004_implicit_concat.py:8:10
|
6 | log.info(f"a" f"b {variablename}")
7 | log.info("a " f"b {variablename}")
8 | log.info("prefix " f"middle {variablename}" f" suffix")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Convert to lazy `%` formatting

View File

@@ -0,0 +1,53 @@
---
source: crates/ruff_linter/src/rules/flake8_logging_format/mod.rs
assertion_line: 71
---
G004 [*] Logging statement uses f-string
--> G004_implicit_concat.py:6:10
|
5 | log = logging.getLogger(__name__)
6 | log.info(f"a" f"b {variablename}")
| ^^^^^^^^^^^^^^^^^^^^^^^^
7 | log.info("a " f"b {variablename}")
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
help: Convert to lazy `%` formatting
3 | variablename = "value"
4 |
5 | log = logging.getLogger(__name__)
- log.info(f"a" f"b {variablename}")
6 + log.info("ab %s", variablename)
7 | log.info("a " f"b {variablename}")
8 | log.info("prefix " f"middle {variablename}" f" suffix")
G004 [*] Logging statement uses f-string
--> G004_implicit_concat.py:7:10
|
5 | log = logging.getLogger(__name__)
6 | log.info(f"a" f"b {variablename}")
7 | log.info("a " f"b {variablename}")
| ^^^^^^^^^^^^^^^^^^^^^^^^
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
help: Convert to lazy `%` formatting
4 |
5 | log = logging.getLogger(__name__)
6 | log.info(f"a" f"b {variablename}")
- log.info("a " f"b {variablename}")
7 + log.info("a b %s", variablename)
8 | log.info("prefix " f"middle {variablename}" f" suffix")
G004 [*] Logging statement uses f-string
--> G004_implicit_concat.py:8:10
|
6 | log.info(f"a" f"b {variablename}")
7 | log.info("a " f"b {variablename}")
8 | log.info("prefix " f"middle {variablename}" f" suffix")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Convert to lazy `%` formatting
5 | log = logging.getLogger(__name__)
6 | log.info(f"a" f"b {variablename}")
7 | log.info("a " f"b {variablename}")
- log.info("prefix " f"middle {variablename}" f" suffix")
8 + log.info("prefix middle %s suffix", variablename)

View File

@@ -74,7 +74,8 @@ pub(crate) fn bytestring_attribute(checker: &Checker, attribute: &Expr) {
["collections", "abc", "ByteString"] => ByteStringOrigin::CollectionsAbc,
_ => return,
};
checker.report_diagnostic(ByteStringUsage { origin }, attribute.range());
let mut diagnostic = checker.report_diagnostic(ByteStringUsage { origin }, attribute.range());
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
}
/// PYI057
@@ -94,7 +95,9 @@ pub(crate) fn bytestring_import(checker: &Checker, import_from: &ast::StmtImport
for name in names {
if name.name.as_str() == "ByteString" {
checker.report_diagnostic(ByteStringUsage { origin }, name.range());
let mut diagnostic =
checker.report_diagnostic(ByteStringUsage { origin }, name.range());
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
}
}
}

View File

@@ -1,6 +1,196 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
--> PYI055.py:4:4
|
2 | from typing import Union
3 |
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 | t: type[int] | type[str] | type[float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
|
help: Combine multiple `type` members
1 | import builtins
2 | from typing import Union
3 |
- s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
4 + s: type[int | str | complex]
5 | t: type[int] | type[str] | type[float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
--> PYI055.py:5:4
|
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
5 | t: type[int] | type[str] | type[float]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
|
help: Combine multiple `type` members
2 | from typing import Union
3 |
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
- t: type[int] | type[str] | type[float]
5 + t: type[int | str | float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
--> PYI055.py:6:4
|
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
5 | t: type[int] | type[str] | type[float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
|
help: Combine multiple `type` members
3 |
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
5 | t: type[int] | type[str] | type[float]
- u: builtins.type[int] | type[str] | builtins.type[complex]
6 + u: type[int | str | complex]
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
--> PYI055.py:7:4
|
5 | t: type[int] | type[str] | type[float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
|
help: Combine multiple `type` members
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
5 | t: type[int] | type[str] | type[float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
- v: Union[type[float], type[complex]]
7 + v: type[Union[float, complex]]
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float | int, complex]]`.
--> PYI055.py:8:4
|
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
|
help: Combine multiple `type` members
5 | t: type[int] | type[str] | type[float]
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
- w: Union[type[float | int], type[complex]]
8 + w: type[Union[float | int, complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[Union[float, int], complex]]`.
--> PYI055.py:9:4
|
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
|
help: Combine multiple `type` members
6 | u: builtins.type[int] | type[str] | builtins.type[complex]
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
- x: Union[Union[type[Union[float, int]], type[complex]]]
9 + x: type[Union[Union[float, int], complex]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
12 |
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float | int, complex]]`.
--> PYI055.py:10:4
|
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
|
help: Combine multiple `type` members
7 | v: Union[type[float], type[complex]]
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
- y: Union[Union[Union[type[float | int], type[complex]]]]
10 + y: type[Union[float | int, complex]]
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
12 |
13 |
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[complex, Union[float, int]]]`.
--> PYI055.py:11:4
|
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Combine multiple `type` members
8 | w: Union[type[float | int], type[complex]]
9 | x: Union[Union[type[Union[float, int]], type[complex]]]
10 | y: Union[Union[Union[type[float | int], type[complex]]]]
- z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
11 + z: type[Union[complex, Union[float, int]]]
12 |
13 |
14 | def func(arg: type[int] | str | type[float]) -> None:
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
--> PYI055.py:14:15
|
14 | def func(arg: type[int] | str | type[float]) -> None:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15 | ...
|
help: Combine multiple `type` members
11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]]
12 |
13 |
- def func(arg: type[int] | str | type[float]) -> None:
14 + def func(arg: type[int | float] | str) -> None:
15 | ...
16 |
17 |
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
--> PYI055.py:29:7
|
28 | # OK
29 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Combine multiple `type` members
26 |
27 |
28 | # OK
- item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
29 + item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker
30 |
31 |
32 | def func():
PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty | str]`.
--> PYI055.py:34:8
|

View File

@@ -898,7 +898,9 @@ fn check_test_function_args(checker: &Checker, parameters: &Parameters, decorato
/// PT020
fn check_fixture_decorator_name(checker: &Checker, decorator: &Decorator) {
if is_pytest_yield_fixture(decorator, checker.semantic()) {
checker.report_diagnostic(PytestDeprecatedYieldFixture, decorator.range());
let mut diagnostic =
checker.report_diagnostic(PytestDeprecatedYieldFixture, decorator.range());
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
}
}

View File

@@ -11,18 +11,17 @@ TC003 [*] Move standard library import `collections.Counter` into a type-checkin
|
help: Move into type-checking block
- from collections import Counter
1 + from __future__ import annotations
2 |
3 | from elsewhere import third_party
4 |
5 | from . import first_party
6 + from typing import TYPE_CHECKING
7 +
8 + if TYPE_CHECKING:
9 + from collections import Counter
1 |
2 | from elsewhere import third_party
3 |
4 | from . import first_party
5 + from typing import TYPE_CHECKING
6 +
7 + if TYPE_CHECKING:
8 + from collections import Counter
9 |
10 |
11 |
12 | def f(x: first_party.foo): ...
11 | def f(x: first_party.foo): ...
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `elsewhere.third_party` into a type-checking block
@@ -36,19 +35,18 @@ TC002 [*] Move third-party import `elsewhere.third_party` into a type-checking b
5 | from . import first_party
|
help: Move into type-checking block
1 + from __future__ import annotations
2 | from collections import Counter
3 |
1 | from collections import Counter
2 |
- from elsewhere import third_party
4 |
5 | from . import first_party
6 + from typing import TYPE_CHECKING
7 +
8 + if TYPE_CHECKING:
9 + from elsewhere import third_party
3 |
4 | from . import first_party
5 + from typing import TYPE_CHECKING
6 +
7 + if TYPE_CHECKING:
8 + from elsewhere import third_party
9 |
10 |
11 |
12 | def f(x: first_party.foo): ...
11 | def f(x: first_party.foo): ...
note: This is an unsafe fix and may change runtime behavior
TC001 [*] Move application import `.first_party` into a type-checking block
@@ -60,17 +58,15 @@ TC001 [*] Move application import `.first_party` into a type-checking block
| ^^^^^^^^^^^
|
help: Move into type-checking block
1 + from __future__ import annotations
2 | from collections import Counter
3 |
4 | from elsewhere import third_party
5 |
2 |
3 | from elsewhere import third_party
4 |
- from . import first_party
6 + from typing import TYPE_CHECKING
7 +
8 + if TYPE_CHECKING:
9 + from . import first_party
5 + from typing import TYPE_CHECKING
6 +
7 + if TYPE_CHECKING:
8 + from . import first_party
9 |
10 |
11 |
12 | def f(x: first_party.foo): ...
11 | def f(x: first_party.foo): ...
note: This is an unsafe fix and may change runtime behavior

View File

@@ -12,15 +12,14 @@ TC001 [*] Move application import `.first_party` into a type-checking block
|
help: Move into type-checking block
- def f():
1 + from __future__ import annotations
2 + from typing import TYPE_CHECKING
3 +
4 + if TYPE_CHECKING:
5 | from . import first_party
6 + def f():
7 |
8 | def f(x: first_party.foo): ...
9 |
1 + from typing import TYPE_CHECKING
2 +
3 + if TYPE_CHECKING:
4 | from . import first_party
5 + def f():
6 |
7 | def f(x: first_party.foo): ...
8 |
note: This is an unsafe fix and may change runtime behavior
TC001 [*] Move application import `.foo` into a type-checking block
@@ -33,24 +32,19 @@ TC001 [*] Move application import `.foo` into a type-checking block
59 | def f(x: Union[foo.Ty, int]): ...
|
help: Move into type-checking block
1 + from __future__ import annotations
2 | def f():
3 | from . import first_party
4 |
--------------------------------------------------------------------------------
50 |
51 |
52 |
53 | # unions
52 | # unions
- from typing import Union
54 + from typing import Union, TYPE_CHECKING
55 |
56 + if TYPE_CHECKING:
57 + from . import foo
58 +
59 |
60 | def n():
53 + from typing import Union, TYPE_CHECKING
54 +
55 + if TYPE_CHECKING:
56 + from . import foo
57 |
58 |
59 | def n():
- from . import foo
61 |
62 | def f(x: Union[foo.Ty, int]): ...
63 | def g(x: foo.Ty | int): ...
60 |
61 | def f(x: Union[foo.Ty, int]): ...
62 | def g(x: foo.Ty | int): ...
note: This is an unsafe fix and may change runtime behavior

View File

@@ -220,32 +220,3 @@ help: Move into type-checking block
52 | x = dict["pd.DataFrame", "pd.DataFrame"]
53 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `module.Member` into a type-checking block
--> TC002.py:172:24
|
170 | global Member
171 |
172 | from module import Member
| ^^^^^^
173 |
174 | x: Member = 1
|
help: Move into type-checking block
1 | """Tests to determine accurate detection of typing-only imports."""
2 + from typing import TYPE_CHECKING
3 +
4 + if TYPE_CHECKING:
5 + from module import Member
6 |
7 |
8 | def f():
--------------------------------------------------------------------------------
173 | def f():
174 | global Member
175 |
- from module import Member
176 |
177 | x: Member = 1
178 |
note: This is an unsafe fix and may change runtime behavior

View File

@@ -1,26 +1,6 @@
---
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
---
TC004 [*] Quote references to `pandas.DataFrame`. Import is in a type-checking block.
--> quote.py:57:28
|
56 | if TYPE_CHECKING:
57 | from pandas import DataFrame
| ^^^^^^^^^
58 |
59 | def func(value: DataFrame):
|
help: Quote references
56 | if TYPE_CHECKING:
57 | from pandas import DataFrame
58 |
- def func(value: DataFrame):
59 + def func(value: "DataFrame"):
60 | ...
61 |
62 |
note: This is an unsafe fix and may change runtime behavior
TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting.
--> quote.py:110:28
|

View File

@@ -11,18 +11,15 @@ TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
4 | def baz() -> DataFrame:
|
help: Move into type-checking block
- def f():
1 + from typing import TYPE_CHECKING
2 +
3 + if TYPE_CHECKING:
4 | from pandas import DataFrame
5 + def f():
6 |
- def baz() -> DataFrame:
7 + def baz() -> "DataFrame":
8 | ...
9 |
10 |
- def f():
1 + from typing import TYPE_CHECKING
2 +
3 + if TYPE_CHECKING:
4 | from pandas import DataFrame
5 + def f():
6 |
7 | def baz() -> DataFrame:
8 | ...
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
@@ -48,11 +45,8 @@ help: Move into type-checking block
12 | def f():
- from pandas import DataFrame
13 |
- def baz() -> DataFrame[int]:
14 + def baz() -> "DataFrame[int]":
14 | def baz() -> DataFrame[int]:
15 | ...
16 |
17 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas` into a type-checking block
@@ -78,11 +72,8 @@ help: Move into type-checking block
19 | def f():
- import pandas as pd
20 |
- def baz() -> pd.DataFrame:
21 + def baz() -> "pd.DataFrame":
21 | def baz() -> pd.DataFrame:
22 | ...
23 |
24 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas` into a type-checking block
@@ -108,11 +99,8 @@ help: Move into type-checking block
26 | def f():
- import pandas as pd
27 |
- def baz() -> pd.DataFrame.Extra:
28 + def baz() -> "pd.DataFrame.Extra":
28 | def baz() -> pd.DataFrame.Extra:
29 | ...
30 |
31 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas` into a type-checking block
@@ -138,11 +126,8 @@ help: Move into type-checking block
33 | def f():
- import pandas as pd
34 |
- def baz() -> pd.DataFrame | int:
35 + def baz() -> "pd.DataFrame | int":
35 | def baz() -> pd.DataFrame | int:
36 | ...
37 |
38 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
@@ -168,11 +153,8 @@ help: Move into type-checking block
41 | def f():
- from pandas import DataFrame
42 |
- def baz() -> DataFrame():
43 + def baz() -> "DataFrame()":
43 | def baz() -> DataFrame():
44 | ...
45 |
46 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
@@ -199,11 +181,8 @@ help: Move into type-checking block
50 |
- from pandas import DataFrame
51 |
- def baz() -> DataFrame[Literal["int"]]:
52 + def baz() -> "DataFrame[Literal['int']]":
52 | def baz() -> DataFrame[Literal["int"]]:
53 | ...
54 |
55 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
@@ -229,11 +208,8 @@ help: Move into type-checking block
67 | def f():
- from pandas import DataFrame, Series
68 |
- def baz() -> DataFrame | Series:
69 + def baz() -> "DataFrame | Series":
69 | def baz() -> DataFrame | Series:
70 | ...
71 |
72 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.Series` into a type-checking block
@@ -259,11 +235,8 @@ help: Move into type-checking block
67 | def f():
- from pandas import DataFrame, Series
68 |
- def baz() -> DataFrame | Series:
69 + def baz() -> "DataFrame | Series":
69 | def baz() -> DataFrame | Series:
70 | ...
71 |
72 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
@@ -290,23 +263,7 @@ help: Move into type-checking block
- from pandas import DataFrame, Series
75 |
76 | def baz() -> (
- DataFrame |
- Series
77 + "DataFrame | Series"
78 | ):
79 | ...
80 |
81 | class C:
- x: DataFrame[
- int
- ] = 1
82 + x: "DataFrame[int]" = 1
83 |
- def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]:
84 + def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]":
85 | ...
86 |
87 |
77 | DataFrame |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.Series` into a type-checking block
@@ -333,23 +290,7 @@ help: Move into type-checking block
- from pandas import DataFrame, Series
75 |
76 | def baz() -> (
- DataFrame |
- Series
77 + "DataFrame | Series"
78 | ):
79 | ...
80 |
81 | class C:
- x: DataFrame[
- int
- ] = 1
82 + x: "DataFrame[int]" = 1
83 |
- def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]:
84 + def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]":
85 | ...
86 |
87 |
77 | DataFrame |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block
@@ -375,11 +316,8 @@ help: Move into type-checking block
92 | def f():
- from pandas import DataFrame, Series
93 |
- def func(self) -> DataFrame | list[Series]:
94 + def func(self) -> "DataFrame | list[Series]":
94 | def func(self) -> DataFrame | list[Series]:
95 | pass
96 |
97 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `pandas.Series` into a type-checking block
@@ -405,9 +343,6 @@ help: Move into type-checking block
92 | def f():
- from pandas import DataFrame, Series
93 |
- def func(self) -> DataFrame | list[Series]:
94 + def func(self) -> "DataFrame | list[Series]":
94 | def func(self) -> DataFrame | list[Series]:
95 | pass
96 |
97 |
note: This is an unsafe fix and may change runtime behavior

View File

@@ -11,18 +11,15 @@ TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser`
4 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]):
|
help: Move into type-checking block
- def f():
1 + from typing import TYPE_CHECKING
2 +
3 + if TYPE_CHECKING:
4 | from django.contrib.auth.models import AbstractBaseUser
5 + def f():
6 |
- def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]):
7 + def test_remove_inner_quotes_double(self, user: "AbstractBaseUser[int]"):
8 | pass
9 |
10 |
- def f():
1 + from typing import TYPE_CHECKING
2 +
3 + if TYPE_CHECKING:
4 | from django.contrib.auth.models import AbstractBaseUser
5 + def f():
6 |
7 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]):
8 | pass
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -48,11 +45,8 @@ help: Move into type-checking block
12 | def f():
- from django.contrib.auth.models import AbstractBaseUser
13 |
- def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']):
14 + def test_remove_inner_quotes_single(self, user: "AbstractBaseUser[int]"):
14 | def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']):
15 | pass
16 |
17 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -78,11 +72,8 @@ help: Move into type-checking block
19 | def f():
- from django.contrib.auth.models import AbstractBaseUser
20 |
- def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]):
21 + def test_remove_inner_quotes_mixed(self, user: "AbstractBaseUser[int, str]"):
21 | def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]):
22 | pass
23 |
24 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -109,11 +100,8 @@ help: Move into type-checking block
28 |
- from django.contrib.auth.models import AbstractBaseUser
29 |
- def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]):
30 + def test_literal_annotation_args_contain_quotes(self, type1: "AbstractBaseUser[Literal['user', 'admin'], Annotated[int, '1', 2]]"):
30 | def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]):
31 | pass
32 |
33 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -140,11 +128,8 @@ help: Move into type-checking block
37 |
- from django.contrib.auth.models import AbstractBaseUser
38 |
- def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]):
39 + def test_union_contain_inner_quotes(self, type1: "AbstractBaseUser[int | Literal['int']]"):
39 | def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]):
40 | pass
41 |
42 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -171,11 +156,8 @@ help: Move into type-checking block
46 |
- from django.contrib.auth.models import AbstractBaseUser
47 |
- def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]):
48 + def test_inner_literal_mixed_quotes(user: "AbstractBaseUser[Literal['user', 'admin']]"):
48 | def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]):
49 | pass
50 |
51 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -202,11 +184,8 @@ help: Move into type-checking block
55 |
- from django.contrib.auth.models import AbstractBaseUser
56 |
- def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]):
57 + def test_inner_literal_single_quote(user: "AbstractBaseUser[Literal['int'], str]"):
57 | def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]):
58 | pass
59 |
60 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -233,11 +212,8 @@ help: Move into type-checking block
64 |
- from django.contrib.auth.models import AbstractBaseUser
65 |
- def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]):
66 + def test_mixed_quotes_literal(user: "AbstractBaseUser[Literal['user'], int]"):
66 | def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]):
67 | pass
68 |
69 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -264,9 +240,6 @@ help: Move into type-checking block
73 |
- from django.contrib.auth.models import AbstractBaseUser
74 |
- def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]):
75 + def test_annotated_literal_mixed_quotes(user: "AbstractBaseUser[Annotated[str, 'max_length=20', Literal['user', 'admin']]]"):
75 | def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]):
76 | pass
77 |
78 |
note: This is an unsafe fix and may change runtime behavior

View File

@@ -21,11 +21,8 @@ help: Move into type-checking block
7 |
- from django.contrib.auth.models import AbstractBaseUser
8 |
- def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]):
9 + def test_union_literal_mixed_quotes(user: 'AbstractBaseUser[Union[Literal["active", "inactive"], str]]'):
9 | def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]):
10 | pass
11 |
12 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -52,11 +49,8 @@ help: Move into type-checking block
16 |
- from django.contrib.auth.models import AbstractBaseUser
17 |
- def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]):
18 + def test_callable_literal_mixed_quotes(callable_fn: 'AbstractBaseUser[Callable[[int, Literal["admin", "user"]], bool]]'):
18 | def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]):
19 | pass
20 |
21 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block
@@ -83,11 +77,8 @@ help: Move into type-checking block
25 |
- from django.contrib.auth.models import AbstractBaseUser
26 |
- def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]):
27 + def test_callable_annotated_literal(callable_fn: 'AbstractBaseUser[Callable[[int, Annotated[str, Literal["active", "inactive"]]], bool]]'):
27 | def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]):
28 | pass
29 |
30 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block
@@ -114,11 +105,8 @@ help: Move into type-checking block
34 |
- from django.contrib.auth import models
35 |
- def test_attribute(arg: models.AbstractBaseUser["int"]):
36 + def test_attribute(arg: 'models.AbstractBaseUser[int]'):
36 | def test_attribute(arg: models.AbstractBaseUser["int"]):
37 | pass
38 |
39 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block
@@ -145,11 +133,8 @@ help: Move into type-checking block
43 |
- from django.contrib.auth import models
44 |
- def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]):
45 + def test_attribute_typing_literal(arg: 'models.AbstractBaseUser[Literal["admin"]]'):
45 | def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]):
46 | pass
47 |
48 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `third_party.Type` into a type-checking block
@@ -176,11 +161,8 @@ help: Move into type-checking block
62 | from typing import Literal
- from third_party import Type
63 |
- def test_string_contains_opposite_quote(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]):
64 + def test_string_contains_opposite_quote(self, type1: 'Type[Literal["\'"]]', type2: 'Type[Literal["\'"]]'):
64 | def test_string_contains_opposite_quote(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]):
65 | pass
66 |
67 |
note: This is an unsafe fix and may change runtime behavior
TC002 [*] Move third-party import `third_party.Type` into a type-checking block
@@ -207,7 +189,6 @@ help: Move into type-checking block
70 | from typing import Literal
- from third_party import Type
71 |
- def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]):
72 + def test_quote_contains_backslash(self, type1: 'Type[Literal["\\n"]]', type2: 'Type[Literal[\'"\']]'):
72 | def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]):
73 | pass
note: This is an unsafe fix and may change runtime behavior

View File

@@ -1,21 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs
---
TC004 [*] Move import `typing.Any` out of type-checking block. Import is used for more than type hinting.
--> TC004_4.py:4:24
|
3 | if TYPE_CHECKING:
4 | from typing import Any
| ^^^
|
help: Move out of type-checking block
1 | from typing import TYPE_CHECKING, Type
2 + from typing import Any
3 |
4 | if TYPE_CHECKING:
- from typing import Any
5 + pass
6 |
7 |
8 | def example(*args: Any, **kwargs: Any):
note: This is an unsafe fix and may change runtime behavior

Some files were not shown because too many files have changed in this diff Show More