Compare commits

...

139 Commits

Author SHA1 Message Date
David Peter
48a026bd2b [red-knot] Do not merge: mypy_primer panic test 2025-04-14 13:53:31 +02:00
David Peter
850360a0b4 [red-knot] Document limitations of diagnostics-silencing in unreachable code (#17387)
## Summary

Document the limitations of our current approach to silencing only a
subset of diagnostics in unreachable sections.
2025-04-14 12:55:14 +02:00
Shunsuke Shibayama
dfd8eaeb32 [red-knot] detect unreachable attribute assignments (#16852)
## Summary

This PR closes #15967.

Attribute assignments that are statically known to be unreachable are
excluded from consideration for implicit instance attribute type
inference. If none of the assignments are found to be reachable, an
`unresolved-attribute` error is reported.

## Test Plan

[A test
case](https://github.com/astral-sh/ruff/blob/main/crates/red_knot_python_semantic/resources/mdtest/attributes.md#attributes-defined-in-statically-known-to-be-false-branches)
marked as TODO now work as intended, and new test cases have been added.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-04-14 09:23:20 +02:00
Dhruv Manilawala
3aa3ee8b89 [red-knot] Use concise message for the server (#17367)
## Summary

This is mainly a follow-up from
https://github.com/astral-sh/ruff/pull/17357 to use the
`concise_message` method for the red-knot server which I noticed
recently while testing the overload implementation.
2025-04-12 04:49:28 +00:00
David Peter
f0c3abd198 [red-knot] Minor followup on str.startswith (#17362)
## Summary

Minor follow-up to https://github.com/astral-sh/ruff/pull/17351, thank
you @AlexWaygood.
2025-04-11 18:40:08 +00:00
Dhruv Manilawala
e5026c0877 Use concise message to show diagnostics in playground (#17357)
## Summary

This PR fixes the playground to use the new `concise_diagnostic` method.

## Test plan

**Before:**

<img width="1728" alt="Screenshot 2025-04-11 at 11 37 34 AM"
src="https://github.com/user-attachments/assets/cbfcbc52-2e70-4277-9363-ba197711390e"
/>

**After:**

<img width="1728" alt="Screenshot 2025-04-11 at 11 38 03 AM"
src="https://github.com/user-attachments/assets/356ec63c-50d9-49a8-8df4-84000b46fb6d"
/>
2025-04-11 22:44:24 +05:30
Brent Westbrook
da32a83c9f [syntax-errors] return outside function (#17300)
Summary
--

This PR reimplements [return-outside-function
(F706)](https://docs.astral.sh/ruff/rules/return-outside-function/) as a
semantic syntax error.

These changes are very similar to those in
https://github.com/astral-sh/ruff/pull/17298.

Test Plan
--

New linter tests, plus existing F706 tests.
2025-04-11 17:05:54 +00:00
Charlie Marsh
4bfdf54d1a Allow types.ruff.rs for red-knot playground (#17358) 2025-04-11 12:56:03 -04:00
Andrew Gallant
7d11ef1564 red_knot_python_semantic: make TextRange required for reporting a lint diagnostic
This commit shuffles the reporting API around a little bit such that a
range is required, up front, when reporting a lint diagnostic. This in
turn enables us to make suppression checking eager.

In order to avoid callers needing to provide the range twice, we create
a primary annotation *without* a message inside the `Diagnostic`
encapsulated by the guard. We do this instead of requiring the message
up front because we're concerned about API complexity and the effort
involved in creating the message.

In order to provide a means of attaching a message to the primary
annotation, we expose a convenience API on `LintDiagnosticGuard` for
setting the message. This isn't generally possible for a `Diagnostic`,
but since a `LintDiagnosticGuard` knows how the `Diagnostic` was
constructed, we can offer this API correctly.

It strikes me that it might be easy to forget to attach a primary
annotation message, btu I think this the "least" bad failure mode. And
in particular, it should be somewhat obvious that it's missing once one
adds a snapshot test for how the diagnostic renders.

Otherwise, this API gives us the ability to eagerly check whether a
diagnostic should be reported with nearly minimal information. It also
shouldn't have any footguns since it guarantees that the primary
annotation is tied to the file in the typing context. And it keeps
things pretty simple: callers only need to provide what is actually
strictly necessary to make a diagnostic.
2025-04-11 12:36:36 -04:00
Andrew Gallant
b79d43a852 ruff_db: add primary annotation message mutators on Diagnostic
This will enable us to provide an API on `LintDiagnosticGuard` for
setting the primary annotation message. It will require an `unwrap()`,
but due to how `LintDiagnosticGuard` will build a `Diagnostic`, this
`unwrap()` will be guaranteed to succeed. (And it won't bubble out to
every user of `LintDiagnosticGuard`.)
2025-04-11 12:36:36 -04:00
Andrew Gallant
a4d3c4bf8b red_knot_python_semantic: make the guards implement DerefMut
This is the payoff from removing a bit of indirection. The types still
exist, but now callers don't need to do builder -> reporter ->
diagnostic. They can just conceptually think of it as builder ->
diagnostic.
2025-04-11 12:36:36 -04:00
Andrew Gallant
f3cc287a40 red_knot_python_semantic: tweak naming
We're going to make the guards deref to `Diagnostic` in order to remove
a layer of indirection in the reporter API. (Well, technically the layer
is not removed since the types still exist, but in actual _usage_ the
layer will be removed. We'll see how it shakes out in the next commit.)
2025-04-11 12:36:36 -04:00
Andrew Gallant
bd5b8a9ec6 ruff_db: tweak APIs accepting a diagnostic message
This expands the set of types accepted for diagnostic messages. Instead
of only `std::fmt::Display` impls, we now *also* accept a concrete
`DiagnosticMessage`.

This will be useful to avoid unnecessary copies of every single
diagnostic message created via `InferContext::report_lint`. (I'll call
out how this helps in a subsequent commit.)
2025-04-11 12:36:36 -04:00
renovate[bot]
0caf09f5c3 Update dependency vite to v6.2.6 (#17354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-11 15:56:10 +00:00
David Peter
47956db567 [red-knot] Specialize str.startswith for string literals (#17351)
## Summary

Infer precise Boolean literal types for `str.startswith` calls where the
instance and the prefix are both string literals. This allows us to
understand `sys.platform.startswith(…)` branches.

## Test Plan

New Markdown tests
2025-04-11 16:26:45 +02:00
Brent Westbrook
ffef71d106 [syntax-errors] yield, yield from, and await outside functions (#17298)
Summary
--

This PR reimplements [yield-outside-function
(F704)](https://docs.astral.sh/ruff/rules/yield-outside-function/) as a
semantic syntax error. Despite the name, this rule covers `yield from`
and `await` in addition to `yield`.

Test Plan
--

New linter tests, along with the existing F704 test.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-04-11 10:16:23 -04:00
Micha Reiser
7e571791c0 [red-knot] Refresh diagnostics when changing related files (#17350) 2025-04-11 15:50:28 +02:00
Brent Westbrook
8e11c53310 Add Checker::import_from_typing (#17340)
Summary
--

This PR replaces uses of version-dependent imports from `typing` or
`typing_extensions` with a centralized `Checker::import_from_typing`
method.

The idea here is to make the fix for #9761 (whatever it ends up being)
applicable to all of the rules performing similar checks.

Test Plan
--

Existing tests for the affected rules.
2025-04-11 13:37:55 +00:00
Max Mynter
1aad180aae Don't add chaperone space after escaped quote in triple quote (#17216)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-11 10:21:47 +02:00
David Peter
1a3b73720c [red-knot] Silence errors in unreachable type annotations / class bases (#17342)
## Summary

For silencing `invalid-type-form` diagnostics in unreachable code, we
use the same approach that we use before and check the reachability that
we already record.

For silencing `invalid-bases`, we simply check if the type of the base
is `Never`. If so, we silence the diagnostic with the argument that the
class construction would never happen.

## Test Plan

Updated Markdown tests.
2025-04-10 22:47:47 +02:00
David Peter
8b2727cf67 [red-knot] Silence unresolved-import in unreachable code (#17336)
## Summary

Similar to what we did for `unresolved-reference` and
`unresolved-attribute`, we now also silence `unresolved-import`
diagnostics if the corresponding `import` statement is unreachable.

This addresses the (already closed) issue #17049.

## Test Plan

Adapted Markdown tests.
2025-04-10 21:13:28 +02:00
Andrew Gallant
410aa4b8fd red_knot_python_semantic: move TODO comment
It fits with reporting lint diagnostics much better than reporting
general diagnostics.
2025-04-10 13:21:00 -04:00
Andrew Gallant
bd58135b0d red_knot_python_semantic: rename lint() and report()
... to `report_lint()` and `report_diagnostic()`. And rename the old
`report_lint()` to `report_lint_old()`.
2025-04-10 13:21:00 -04:00
Andrew Gallant
81045758d3 ruff_db: use Annotation::get_message in more places
I added this accessor because tests want it, but we can also use it in
other places internally. It's a little nicer because it does the
`as_deref()` for you.
2025-04-10 13:21:00 -04:00
Andrew Gallant
0b2a5b5cd3 red_knot_python_semantic: tweak docs on building reporter builders 2025-04-10 13:21:00 -04:00
Andrew Gallant
7d958a9ee5 red_knot_python_semantic: remove the "old" secondary message type
This finally completes the deletion of all old diagnostic types.

We do this by migrating the second (and last) use of secondary
diagnostic messages: to highlight the return type of a function
definition when its return value is inconsistent with the type.

Like the last diagnostic, we do actually change the message here a bit.
We don't need a sub-diagnostic here, and we can instead just add a
secondary annotation to highlight the return type.
2025-04-10 13:21:00 -04:00
Andrew Gallant
7e2eb591bc red_knot_python_semantic: replace one use of "old" secondary diagnostic messages
This is the first use of the new `lint()` reporter.

I somewhat skipped a step here and also modified the actual diagnostic
message itself. The snapshots should tell the story.

We couldn't do this before because we had no way of differentiating
between "message for the diagnostic as a whole" and "message for a
specific code annotation." Now we can, so we can write more precise
messages based on the assumption that users are also seeing the code
snippet.

The downside here is that the actual message text can become quite vague
in the absence of the code snippet. This occurs, for example, with
concise diagnostic formatting. It's unclear if we should do anything
about it. I don't really see a way to make it better that doesn't
involve creating diagnostics with messages for each mode, which I think
would be a major PITA.

The upside is that this code gets a bit simpler, and we very
specifically avoid doing extra work if this specific lint is disabled.
2025-04-10 13:21:00 -04:00
Andrew Gallant
ba408f4231 red_knot_python_semantic: update revealed type snapshots
This required a bit of surgery in the diagnostic matching and more
faffing about using a "concise" message from a diagnostic instead of
only printing the "primary" message.
2025-04-10 13:21:00 -04:00
Andrew Gallant
28b64064f5 ruff_db: tweak how the revealed type diagnostic is rendered
In the new diagnostic data model, we really should have a main
diagnostic message *and* a primary span (with an optional message
attached to it) for every diagnostic.

In this commit, I try to make this true for the "revealed type"
diagnostic. Instead of the annotation saying both "revealed type is"
and also the revealed type itself, the annotation is now just the
revealed type and the main diagnostic message is "Revealed type."

I expect this may be controversial. I'm open to doing something
different. I tried to avoid redundancy, but maybe this is a special case
where we want the redundancy. I'm honestly not sure. I do *like* how it
looks with this commit, but I'm not working with Red Knot's type
checking daily, so my opinion doesn't count for much.

This did also require some tweaking to concise diagnostic formatting in
order to preserve the essential information.

This commit doesn't update every relevant snapshot. Just a few. I split
the rest out into the next commit.
2025-04-10 13:21:00 -04:00
Andrew Gallant
75b15ea2d0 red_knot: add explicit test for concise reveal_type diagnostic
This test reflects the status quo before we change things.
2025-04-10 13:21:00 -04:00
Andrew Gallant
e7e86b8584 red_knot_python_semantic: remove InferContext::report_diagnostic
... and replace it with use of `report()`.

Interestingly, this is the only instance of `report_diagnostic` used
directly, and thus anticipated to be the only instance of using
`report()`. If this ends up being a true single use method, we could
make it less generic and tailored specifically to "reveal type."

Two other things to note:

I left the "primary message" as empty. This avoids changing snapshots.
I address this in a subsequent commit.

The creation of a diagnostic here is a bit verbose/annoying. Certainly
more so than it was. This is somewhat expected since our diagnostic
model is more expressive and because we don't have a proc macro. I
avoided creating helpers for this case since there's only one use of
`report()`. But I expect to create helpers for the `lint()` case.
2025-04-10 13:21:00 -04:00
Andrew Gallant
f84bc07d56 red_knot_python_semantic: add "reporter" API
This is a surgical change that adds new `report()` and `lint()`
APIs to `InferContext`. These are intended to replace the existing
`report_*` APIs.

The comments should explain what these reporters are meant to do. For
the most part, this is "just" shuffling some code around. The actual
logic for determining whether a lint *should* be reported or not remains
unchanged and we don't make any changes to how a `Diagnostic` is
actually constructed (yet).

I initially tried to just use `LintReporter` and `DiagnosticReporter`
without the builder types, since I perceive the builder types to be an
annoying additional layer. But I found it also exceedingly annoying to
have to construct and provide the diagnostic message before you even
know if you are going to build the diagnostic. I also felt like this
could result in potentially unnecessary and costly querying in some
cases, although this is somewhat hand wavy. So I overall felt like the
builder route was the way to go. If the builders end up being super
annoying, we can probably add convenience APIs for common patterns to
paper over them.
2025-04-10 13:21:00 -04:00
Dylan
7186d5e9ad Bump 0.11.5 (#17337) 2025-04-10 11:57:44 -05:00
David Peter
5b6e94981d [red-knot] Silence unresolved-attribute in unreachable code (#17305)
## Summary

Basically just repeat the same thing that we did for
`unresolved-reference`, but now for attribute expressions.

We now also handle the case where the unresolved attribute (or the
unresolved reference) diagnostic originates from a stringified type
annotation.

And I made the evaluation of reachability constraints lazy (will only be
evaluated right before we are about to emit a diagnostic).

## Test Plan

New Markdown tests for stringified annotations.
2025-04-10 17:15:47 +02:00
Carl Meyer
ec74f2d522 Revert "[red-knot] Type narrowing for assertions (#17149)" (#17335)
I merged #17149 without checking the ecosystem results, and it still
caused a cycle panic in pybind11. Reverting for now until I fix that, so
we don't lose the ecosystem signal on other PRs.
2025-04-10 11:06:25 -04:00
Matthew Mckee
907b6ed7b5 [red-knot] Type narrowing for assertions (#17149)
## Summary

Fixes #17147 

## Test Plan

Add new narrow/assert.md test file

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-04-10 10:15:52 -04:00
Carl Meyer
fd9882a1f4 [red-knot] avoid unnecessary evaluation of visibility constraint on definitely-unbound symbol (#17326)
This causes spurious query cycles.

This PR also includes an update to Salsa, which gives us db events on
cycle iteration, so we can write tests asserting the absence of a cycle.
2025-04-10 13:59:38 +00:00
Aria Desires
66a33bfd32 update cargo-dist (#17325)
Putting this up to confirm that it does what it should:

* undirty the release.yml by including action-commits in the config
* add persist-credentials=false hardening
2025-04-10 09:43:13 -04:00
Micha Reiser
5b1d8350ff [red-knot] Fix double hovers/inlays in playground (#17334) 2025-04-10 12:40:41 +00:00
David Peter
4d50ee6f52 [red-knot] Track reachability of scopes (#17332)
## Summary

Track the reachability of nested scopes within their parent scopes. We
use this as an additional requirement for emitting
`unresolved-reference` diagnostics (and in the future,
`unresolved-attribute` and `unresolved-import`). This means that we only
emit `unresolved-reference` for a given use of a symbol if the use
itself is reachable (within its own scope), *and if the scope itself is
reachable*. For example, no diagnostic should be emitted for the use of
`x` here:

```py
if False:
    x = 1

    def f():
        print(x)  # this use of `x` is reachable inside the `f` scope,
                  # but the whole `f` scope is not reachable.
```

There are probably more fine-grained ways of solving this problem, but
they require a more sophisticated understanding of nested scopes (see
#15777, in particular
https://github.com/astral-sh/ruff/issues/15777#issuecomment-2788950267).
But it doesn't seem completely unreasonable to silence *this specific
kind of error* in unreachable scopes.

## Test Plan

Observed changes in reachability tests and ecosystem.
2025-04-10 11:56:40 +00:00
Aaron Gokaslan
06ffeb2e09 Add pre-commit hook to check for merge conflicts (#17279) 2025-04-10 11:22:54 +01:00
Matthew Mckee
10e44124e6 [red-knot] Add inlay type hints (#17214)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-10 09:21:40 +00:00
Micha Reiser
9f6913c488 [red-knot] Update salsa (#17320)
## Summary

Update Salsa to pull in https://github.com/salsa-rs/salsa/pull/788 which
fixes the, by now, famous *access to field whilst the value is being
initialized*.

This PR also re-enables all tests that previously triggered the panic.

## Test Plan

`cargo test`
2025-04-09 16:22:02 -04:00
David Peter
5fef4d4572 Use python.typing.org for typing documentation links (#17323)
## Summary

There is a new official URL for the typing documentation:
https://typing.python.org/

Change all https://typing.readthedocs.io/ links to use the new sub
domain, which is slightly shorter and looks more official.

## Test Plan

Tested to see if each and every new URL is accessible. I noticed that
some links go to https://typing.python.org/en/latest/source/stubs.html
which seems to be outdated, but that is a separate issue. The same page
shows up for the old URL.
2025-04-09 20:38:20 +02:00
Brent Westbrook
144484d46c Refactor semantic syntax error scope handling (#17314)
## Summary

Based on the discussion in
https://github.com/astral-sh/ruff/pull/17298#discussion_r2033975460, we
decided to move the scope handling out of the `SemanticSyntaxChecker`
and into the `SemanticSyntaxContext` trait. This PR implements that
refactor by:

- Reverting all of the `Checkpoint` and `in_async_context` code in the
`SemanticSyntaxChecker`
- Adding four new methods to the `SemanticSyntaxContext` trait
- `in_async_context`: matches `SemanticModel::in_async_context` and only
detects the nearest enclosing function
- `in_sync_comprehension`: uses the new `is_async` tracking on
`Generator` scopes to detect any enclosing sync comprehension
  - `in_module_scope`: reports whether we're at the top-level scope
  - `in_notebook`: reports whether we're in a Jupyter notebook
- In-lining the `TestContext` directly into the
`SemanticSyntaxCheckerVisitor`
- This allows modifying the context as the visitor traverses the AST,
which wasn't possible before

One potential question here is "why not add a single method returning a
`Scope` or `Scopes` to the context?" The main reason is that the `Scope`
type is defined in the `ruff_python_semantic` crate, which is not
currently a dependency of the parser. It also doesn't appear to be used
in red-knot. So it seemed best to use these more granular methods
instead of trying to access `Scope` in `ruff_python_parser` (and
red-knot).

## Test Plan

Existing parser and linter tests.
2025-04-09 14:23:29 -04:00
Wei Lee
c87e3ccb2f [airflow] Add missing AIR302 attribute check (#17115)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

attribute check was missing in the previous implementation

e.g.

```python
from airflow.api.auth.backend import basic_auth

basic_auth.auth_current_user
```

This PR adds this kind of check.

## Test Plan

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

The test case has been added to the button of the existing test
fixtures, confirmed to be correct and later reorgnaized
2025-04-09 13:58:41 -04:00
Alex Waygood
781b653511 [red-knot] Fix false positives on types.UnionType instances in type expressions (#17297) 2025-04-09 18:33:16 +01:00
Micha Reiser
484a8ed36d [red-knot] Update salsa (part 1) (#17321) 2025-04-09 19:15:43 +02:00
Brent Westbrook
2fbc4d577e [syntax-errors] Document behavior of global declarations in try nodes before 3.13 (#17285)
Summary
--

This PR extends the documentation of the `LoadBeforeGlobalDeclaration`
check to specify the behavior on versions of Python before 3.13. Namely,
on Python 3.12, the `else` clause of a `try` statement is visited before
the `except` handlers:

```pycon
Python 3.12.9 (main, Feb 12 2025, 14:50:50) [Clang 19.1.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 10
>>> def g():
...     try:
...         1 / 0
...     except:
...         a = 1
...     else:
...         global a
...
>>> def f():
...     try:
...         pass
...     except:
...         global a
...     else:
...         print(a)
...
  File "<stdin>", line 5
SyntaxError: name 'a' is used prior to global declaration

```

The order is swapped on 3.13 (see
[CPython#111123](https://github.com/python/cpython/issues/111123)):

```pycon
Python 3.13.2 (main, Feb  5 2025, 08:05:21) [GCC 14.2.1 20250128] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 10
... def g():
...     try:
...         1 / 0
...     except:
...         a = 1
...     else:
...         global a
...
  File "<python-input-0>", line 8
    global a
    ^^^^^^^^
SyntaxError: name 'a' is assigned to before global declaration
>>> def f():
...     try:
...         pass
...     except:
...         global a
...     else:
...         print(a)
...
>>>
```

The current implementation of PLE0118 is correct for 3.13 but not 3.12:
[playground](https://play.ruff.rs/d7467ea6-f546-4a76-828f-8e6b800694c9)
(it flags the first case regardless of Python version).

We decided to maintain this incorrect diagnostic for Python versions
before 3.13 because the pre-3.13 behavior is very unintuitive and
confirmed to be a bug, although the bug fix was not backported to
earlier versions. This can lead to false positives and false negatives
for pre-3.13 code, but we also expect that to be very rare, as
demonstrated by the ecosystem check (before the version-dependent check
was reverted here).

Test Plan
--

N/a
2025-04-09 12:54:21 -04:00
Alex Waygood
73399029b2 [red-knot] Optimise visibility constraints for *-import definitions (#17317) 2025-04-09 16:53:26 +00:00
Douglas Creager
ff376fc262 [red-knot] Allow explicit specialization of generic classes (#17023)
This PR lets you explicitly specialize a generic class using a subscript
expression. It introduces three new Rust types for representing classes:

- `NonGenericClass`
- `GenericClass` (not specialized)
- `GenericAlias` (specialized)

and two enum wrappers:

- `ClassType` (a non-generic class or generic alias, represents a class
_type_ at runtime)
- `ClassLiteralType` (a non-generic class or generic class, represents a
class body in the AST)

We also add internal support for specializing callables, in particular
function literals. (That is, the internal `Type` representation now
attaches an optional specialization to a function literal.) This is used
in this PR for the methods of a generic class, but should also give us
most of what we need for specializing generic _functions_ (which this PR
does not yet tackle).

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-04-09 11:18:46 -04:00
Wei Lee
7c81408c54 [airflow] Refactor AIR301 logic and fix typos (AIR301) (#17293)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

* Simplify match conditions in AIR301
* Fix
* `airflow.datasets.manager.DatasetManager` →
`airflow.assets.manager.AssetManager`
* `airflow.www.auth.has_access_dataset` →
`airflow.www.auth.has_access_dataset`

## Test Plan

<!-- How was it tested? -->
The test fixture has been updated accordingly
2025-04-09 10:46:17 -04:00
Wei Lee
7207c86971 [airflow] Extract AIR312 from AIR302 rules (AIR302, AIR312) (#17152)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

As discussed in
https://github.com/astral-sh/ruff/issues/14626#issuecomment-2766146129,
we're to separate suggested changes from required changes.

The following symbols has been moved to AIR312 from AIR302. They still
work in Airflow 3.0, but they're suggested to be changed as they're
expected to be removed in future version

```python
from airflow.hooks.filesystem import FSHook
from airflow.hooks.package_index import PackageIndexHook
from airflow.hooks.subprocess import (SubprocessHook, SubprocessResult, working_directory)
from airflow.operators.bash import BashOperator
from airflow.operators.datetime import BranchDateTimeOperator, target_times_as_dates
from airflow.operators.trigger_dagrun import TriggerDagRunLink, TriggerDagRunOperator
from airflow.operators.empty import EmptyOperator
from airflow.operators.latest_only import LatestOnlyOperator
from airflow.operators.python import (BranchPythonOperator, PythonOperator, PythonVirtualenvOperator, ShortCircuitOperator)
from airflow.operators.weekday import BranchDayOfWeekOperator
from airflow.sensors.date_time import DateTimeSensor, DateTimeSensorAsync
from airflow.sensors.external_task import ExternalTaskMarker, ExternalTaskSensor, ExternalTaskSensorLink
from airflow.sensors.filesystem import FileSensor
from airflow.sensors.time_sensor import TimeSensor, TimeSensorAsync
from airflow.sensors.time_delta import TimeDeltaSensor, TimeDeltaSensorAsync, WaitSensor
from airflow.sensors.weekday import DayOfWeekSensor
from airflow.triggers.external_task import DagStateTrigger, WorkflowTrigger
from airflow.triggers.file import FileTrigger
from airflow.triggers.temporal import DateTimeTrigger, TimeDeltaTrigger
```

## Test Plan

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

The test fixture has been updated acccordingly

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-04-09 10:43:07 -04:00
Alex Waygood
6ec4c6a97e [red-knot] Improve handling of visibility constraints in external modules when resolving * imports (#17286) 2025-04-09 14:36:52 +00:00
Alex Waygood
f1ba596f22 [red-knot] Add more tests for * imports (#17315) 2025-04-09 15:10:30 +01:00
Micha Reiser
8249a72412 [red-knot] Default python-platform to current platform (#17183)
## Summary

As discussed in https://github.com/astral-sh/ruff/issues/16983 and
"mitigate" said issue for the alpha.

This PR changes the default for `PythonPlatform` to be the current
platform rather than `all`.

I'm not sure if we should be as sophisticated as supporting `ios` and
`android` as defaults but it was easy...

## Test Plan

Updated Markdown tests.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-04-09 12:05:18 +02:00
David Peter
00e00b9ad6 [red-knot] Add new 'unreachable code' test case (#17306)
## Summary

This is a new test case that I don't know how to handle yet. It leads to
many false positives in `rich/tests/test_win32_console.py`, which does
something like:

```py
if sys.platform == "win32":
    from windows_only_module import some_symbol

    some_other_symbol = 1

    def some_test_case():
        use(some_symbol)  # Red Knot: unresolved-reference
        use(some_other_symbol)  # Red Knot: unresolved-reference
```

Also adds a test for using unreachable symbols in type annotations or as
class bases.
2025-04-09 11:45:42 +02:00
David Peter
9a5a86769b [red-knot] mypy_primer: Run on async-utils (#17303)
closes #17299
2025-04-09 09:43:22 +02:00
David Peter
2cee86d807 [red-knot] Add custom __setattr__ support (#16748)
## Summary

Add support for classes with a custom `__setattr__` method.

## Test Plan

New Markdown tests, ecosystem checks.
2025-04-09 08:04:11 +02:00
Mike Perlov
fab7d820bd [red-knot] Add __init__ arguments check when doing try_call on a class literal (#16512)
## Summary

* Addresses #16511 for simple cases where only `__init__` method is
bound on class or doesn't exist at all.
* fixes a bug with argument counting in bound method diagnostics

Caveats:
* No handling of `__new__` or modified `__call__` on metaclass.
* This leads to a couple of false positive errors in tests

## Test Plan

- A couple new cases in mdtests
- cargo nextest run -p red_knot_python_semantic --no-fail-fast

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-04-08 17:26:20 -04:00
Denys Kyslytsyn
ed14dbb1a2 [flake8-pie] Avoid false positive for multiple assignment with auto() (PIE796) (#17274)
This fix closes #16868 

I noticed the issue is assigned, but the assignee appears to be actively
working on another pull request. I hope that’s okay!

<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

As of Python 3.11.1, `enum.auto()` can be used in multiple assignments.
This pattern should not trigger non-unique-enums check.
Reference: [Python docs on
enum.auto()](https://docs.python.org/3/library/enum.html#enum.auto)

This fix updates the check logic to skip enum variant statements where
the right-hand side is a tuple containing a call to `enum.auto()`.

## Test Plan

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

The added test case uses the example from the original issue. It
previously triggered a false positive, but now passes successfully.
2025-04-08 15:53:27 -04:00
Brent Westbrook
058439d5d3 [syntax-errors] Async comprehension in sync comprehension (#17177)
Summary
--

Detect async comprehensions nested in sync comprehensions in async
functions before Python 3.11, when this was [changed].

The actual logic of this rule is very straightforward, but properly
tracking the async scopes took a bit of work. An alternative to the
current approach is to offload the `in_async_context` check into the
`SemanticSyntaxContext` trait, but that actually required much more
extensive changes to the `TestContext` and also to ruff's semantic
model, as you can see in the changes up to
31554b473507034735bd410760fde6341d54a050. This version has the benefit
of mostly centralizing the state tracking in `SemanticSyntaxChecker`,
although there was some subtlety around deferred function body traversal
that made the changes to `Checker` more intrusive too (hence the new
linter test).

The `Checkpoint` struct/system is obviously overkill for now since it's
only tracking a single `bool`, but I thought it might be more useful
later.

[changed]: https://github.com/python/cpython/issues/77527

Test Plan
--

New inline tests and a new linter integration test.
2025-04-08 12:50:52 -04:00
Wei Lee
dc02732d4d [airflow] Expand module path check to individual symbols (AIR302) (#17278)
## Summary

### Improvement
Expand the following moved module into individual symbols.

* airflow.triggers.temporal
* airflow.triggers.file
* airflow.triggers.external_task
* airflow.hooks.subprocess
* airflow.hooks.package_index
* airflow.hooks.filesystem
* airflow.sensors.weekday
* airflow.sensors.time_delta
* airflow.sensors.time_sensor
* airflow.sensors.date_time
* airflow.operators.weekday
* airflow.operators.datetime
* airflow.operators.bash 

This removes `Replacement::ImportPathMoved`.

## Fix
During the expansion, the following paths were also fixed

* airflow.sensors.s3_key_sensor.S3KeySensor →
airflow.providers.amazon.aws.sensors.S3KeySensor
* airflow.operators.sql.SQLThresholdCheckOperator →
airflow.providers.common.sql.operators.sql.SQLThresholdCheckOperator
* airflow.hooks.druid_hook.DruidDbApiHook →
airflow.providers.apache.druid.hooks.druid.DruidDbApiHook
* airflow.hooks.druid_hook.DruidHook →
airflow.providers.apache.druid.hooks.druid.DruidHook
* airflow.kubernetes.pod_generator.extend_object_field →
airflow.providers.cncf.kubernetes.pod_generator.extend_object_field
* airflow.kubernetes.pod_launcher.PodLauncher →
airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodLauncher
* airflow.kubernetes.pod_launcher.PodStatus →
airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodStatus
* airflow.kubernetes.pod_generator.PodDefaults →
airflow.providers.cncf.kubernetes.pod_generator.PodDefaults
* airflow.kubernetes.pod_launcher_deprecated.PodDefaults →
airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodDefaults

### Refactor
As many symbols are moved into the same module,
`SourceModuleMovedToProvider` is introduced for grouping similar logic

## Test Plan
2025-04-08 09:03:27 -04:00
Brent Westbrook
0891689d2f [syntax-errors] Check annotations in annotated assignments (#17283)
Summary
--

This PR extends the checks in #17101 and #17282 to annotated assignments
after Python 3.13.

Currently stacked on #17282 to include `await`.

Test Plan
--

New inline tests. These are simpler than the other cases because there's
no place to put generics.
2025-04-08 08:56:25 -04:00
Brent Westbrook
127a45622f [syntax-errors] Extend annotation checks to await (#17282)
Summary
--

This PR extends the changes in #17101 to include `await` in the same
positions.

I also renamed the `valid_annotation_function` test to include `_py313`
and explicitly passed a Python version to contrast it with the `_py314`
version.

Test Plan
--

New test cases added to existing files.
2025-04-08 08:55:43 -04:00
David Peter
b662c3ff7e [red-knot] Add support for assert_never (#17287)
## Summary

We already have partial "support" for `assert_never`, because it is
annotated as
```pyi
def assert_never(arg: Never, /) -> Never: ...
```
in typeshed. So we already emit a `invalid-argument-type` diagnostic if
the argument type to `assert_never` is not assignable to `Never`.

That is not enough, however. Gradual types like `Any`, `Unknown`,
`@Todo(…)` or `Any & int` can be assignable to `Never`. Which means that
we didn't issue any diagnostic in those cases.

Also, it seems like `assert_never` deserves a dedicated diagnostic
message, not just a generic "invalid argument type" error.

## Test Plan

New Markdown tests.
2025-04-08 09:31:49 +02:00
Denys Kyslytsyn
97dd6d120c [flake8-pytest-style] Avoid false positive for legacy form of pytest.raises (PT011) (#17231)
This fix closes #17026 

## Summary

The check for the `PytestRaisesTooBroad` rule is now skipped if there is
a second positional argument present, which means `pytest.raises` is
used as a function.

## Test Plan

Tested on the example from the issue, which now passes the check.
```Python3
pytest.raises(Exception, func, *func_args, **func_kwargs).match("error message")
```

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-08 09:24:47 +02:00
InSync
34e06f2d17 [red-knot] Do not show types for literal expressions on hover (#17290)
## Summary

Resolves #17289.

After this change, Red Knot will no longer show types on hover for
`None`, `...`, `True`, `False`, numbers, strings (but not f-strings),
and bytes literals.

## Test Plan

Unit tests.
2025-04-08 09:05:51 +02:00
David Peter
a388c73752 [red-knot] Fix dead-code clippy warning (#17291)
## Summary

Failed run on main:
https://github.com/astral-sh/ruff/actions/runs/14326812317
2025-04-08 08:56:59 +02:00
David Peter
60f2e67454 [red-knot] Reachability analysis (#17199)
## Summary

This implements a new approach to silencing `unresolved-reference`
diagnostics by keeping track of the reachability of each use of a
symbol. The changes merged in
https://github.com/astral-sh/ruff/pull/17169 are still needed for the
"Use of variable in nested function" test case, but that could also be
solved in another way eventually (see
https://github.com/astral-sh/ruff/issues/15777). We can use the same
technique to silence `unresolved-import` and `unresolved-attribute`
false-positives, but I think this could be merged in isolation.

## Test Plan

New Markdown tests, ecosystem tests
2025-04-08 08:37:20 +02:00
Micha Reiser
cb7f56fb20 [red-knot] Don't use latency-sensitive for handlers (#17227)
## Summary

The priority latency-sensitive is reserved for actions that need to run
immediately because they would otherwise block the user's action. An
example of this is a format request. VS code blocks the editor until the
save action is complete. That's why formatting a document is very
sensitive to delays and it's important that we always have a worker
thread available to run a format request *immediately*. Another example
are code completions, where it's important that they appear immediately
when the user types.

On the other hand, showing diagnostics, hover, or inlay hints has high
priority but users are used that the editor takes a few ms to compute
the overlay.
Computing this information can also be expensive (e.g. find all
references), blocking the worker for quiet some time (a few 100ms).
That's why it's important
that those requests don't clog the sensitive worker threads.
2025-04-08 08:33:30 +02:00
David Peter
761749cb50 [playground] New default program (#17277)
## Summary

This PR proposes to change the default example program in the
playground. I realize that this is somewhat underwhelming, but I found
it rather difficult to come up with something that circumvented missing
support for overloads/generics/self-type, while still looking like
(easy!) code that someone might actually write, and demonstrating some
Red Knot features. One thing that I wanted to capture was the experience
of adding type constraints to an untyped program. And I wanted something
that could be executed in the Playground once all errors are fixed.

Happy for any suggestions on what we could do instead. I had a lot of
different ideas, but always ran into one or another limitation. So I
guess we can also iterate on this as we add more features to Red Knot.

Try it here:
https://playknot.ruff.rs/8e3a96af-f35d-4488-840a-2abee6c0512d
```py
from typing import Literal

type Style = Literal["italic", "bold", "underline"]

# Add parameter annotations `line: str, word: str, style: Style` and a return
# type annotation `-> str` to see if you can find the mistakes in this program.

def with_style(line, word, style):
    if style == "italic":
        return line.replace(word, f"*{word}*")
    elif style == "bold":
        return line.replace(word, f"__{word}__")

    position = line.find(word)
    output = line + "\n"
    output += " " * position
    output += "-" * len(word)


print(with_style("Red Knot is a fast type checker for Python.", "fast", "underlined"))
```

closes https://github.com/astral-sh/ruff/issues/17267
2025-04-07 21:52:07 +02:00
David Peter
3657f798c9 [red-knot] Add --python-platform CLI option (#17284)
## Summary

Add a new `--python-platform` command-line option, in analogy to
`--python-version`.

## Test Plan

Added new integration test.
2025-04-07 21:04:44 +02:00
Matthew Mckee
4a4a376f02 [red-knot] Allow ellipsis default params in stub functions (#17243)
## Summary

Fixes #17234

## Test Plan

Add tests to functions/paremeters.md

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-04-07 17:34:59 +00:00
Micha Reiser
5e0f563ee4 [red-knot] Fix stale syntax errors in playground (#17280)
## Summary

React requires that `key`s are unique but the constructed key for
diagnostics wasn't guaranteed when two diagnostics had the same name and
location.

This PR fixes this by using a disambiguator map to disambiguate the key.

Fixes #17276

## Test Plan



https://github.com/user-attachments/assets/f3f9d10a-ecc4-4ffe-8676-3633a12e07ce
2025-04-07 18:39:21 +02:00
renovate[bot]
27ecf350d8 Update Rust crate clap to v4.5.35 (#17273)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [clap](https://redirect.github.com/clap-rs/clap) |
workspace.dependencies | patch | `4.5.34` -> `4.5.35` |

---

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

---

### Release Notes

<details>
<summary>clap-rs/clap (clap)</summary>

###
[`v4.5.35`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4535---2025-04-01)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.34...v4.5.35)

##### Fixes

- *(help)* Align positionals and flags when put in the same
`help_heading`
-   *(help)* Don't leave space for shorts if there are none

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-07 14:25:05 +00:00
Max Mynter
f1d4ba32cc Fix RUF100 to detect unused file-level noqa directives with specific codes (#17042) (#17061)
Closes #17042

## Summary
This PR fixes the issue outlined in #17042 where RUF100 (unused-noqa)
fails to detect unused file-level noqa directives (`# ruff: noqa` or `#
ruff: noqa: {code}`).

The issue stems from two underlying causes:

1. For blanket file-level directives (`# ruff: noqa`), there's a
circular dependency: the directive exempts all rules including RUF100
itself, which prevents checking for usage. This isn't changed by this
PR. I would argue it is intendend behavior - a blanket `# ruff: noqa`
directive should exempt all rules including RUF100 itself.

2. For code-specific file-level directives (e.g. `# ruff: noqa: F841`),
the handling was missing in the `check_noqa` function. This is added in
this PR.

## Notes
- For file-level directives, the `matches` array is pre-populated with
the specified codes during parsing, unlike line-level directives which
only populate their `matches` array when actually suppressing
diagnostics. This difference requires the somewhat clunky handling of
both cases. I would appreciate guidance on a cleaner design :)

- A more fundamental solution would be to change how file-level
directives initialize the `matches` array in
`FileNoqaDirectives::extract()`, but that requires more substantial
changes as it breaks existing functionality. I suspect discussions in
#16483 are relevant for this.

## Test Plan
- Local verification
- Added a test case and fixture
2025-04-07 09:21:52 -05:00
Micha Reiser
83a97235ae [ci] Fix pattern for code changes (#17275)
## Summary

`**/*` only matches files in a subdirectory whereas `**` matches any
file at an arbitrary depth

> A trailing "/**" matches everything inside. For example, "abc/**"
matches all files inside directory "abc", relative to the location of
the .gitignore file, with infinite depth.

> A leading "**" followed by a slash means match in all directories. For
example, "**/foo" matches file or directory "foo" anywhere, the same as
pattern "foo". "**/foo/bar" matches file or directory "bar" anywhere
that is directly under directory "foo".
2025-04-07 16:05:05 +02:00
Wei Lee
1e9e423362 [airflow] Update oudated AIR301, AIR302 rules (#17123)
## Summary

Some of the migration rules has been changed during Airflow 3
development. The following are new AIR302 rules. Corresponding AIR301
has also been removed.

* airflow.sensors.external_task_sensor.ExternalTaskMarker →
airflow.providers.standard.sensors.external_task.ExternalTaskMarker
* airflow.sensors.external_task_sensor.ExternalTaskSensor →
airflow.providers.standard.sensors.external_task.ExternalTaskSensor
* airflow.sensors.external_task_sensor.ExternalTaskSensorLink →
airflow.providers.standard.sensors.external_task.ExternalTaskSensorLink
* airflow.sensors.time_delta_sensor.TimeDeltaSensor →
airflow.providers.standard.sensors.time_delta.TimeDeltaSensor
* airflow.operators.dagrun_operator.TriggerDagRunLink →
airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunLink
* airflow.operators.dagrun_operator.TriggerDagRunOperator →
airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunOperator
* airflow.operators.python_operator.BranchPythonOperator →
airflow.providers.standard.operators.python.BranchPythonOperator
* airflow.operators.python_operator.PythonOperator →
airflow.providers.standard.operators.python.PythonOperator
* airflow.operators.python_operator.PythonVirtualenvOperator →
airflow.providers.standard.operators.python.PythonVirtualenvOperator
* airflow.operators.python_operator.ShortCircuitOperator →
airflow.providers.standard.operators.python.ShortCircuitOperator
* airflow.operators.latest_only_operator.LatestOnlyOperator →
airflow.providers.standard.operators.latest_only.LatestOnlyOperator
* airflow.sensors.date_time_sensor.DateTimeSensor →
airflow.providers.standard.sensors.DateTimeSensor
* airflow.operators.email_operator.EmailOperator →
airflow.providers.smtp.operators.smtp.EmailOperator
* airflow.operators.email.EmailOperator →
airflow.providers.smtp.operators.smtp.EmailOperator
* airflow.operators.bash.BashOperator →
airflow.providers.standard.operators.bash.BashOperator
* airflow.operators.EmptyOperator →
airflow.providers.standard.operators.empty.EmptyOperator

closes: https://github.com/astral-sh/ruff/issues/17103

## Test Plan

The test fixture has been updated and checked after each change and
later reorganized in the latest commit
2025-04-07 09:45:56 -04:00
Bruno Alla
708b84fb87 [docs] fix formatting of "See Style Guide" link (#17272)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Minor formatting tweak in the docs. Looks like the link is meant to be
italic (others "See XXXX" are), but the opening underscore isn't closed
so it's displayed in the rendered version:
https://docs.astral.sh/ruff/formatter/#philosophy


![image](https://github.com/user-attachments/assets/e5984a90-3ea6-4afc-a561-2c681519c78d)


## Test Plan

<!-- How was it tested? -->
2025-04-07 12:43:26 +00:00
Micha Reiser
6cc2d02dfa [red-knot] Support stub packages (#17204)
## Summary

This PR adds support for stub packages, except for partial stub packages
(a stub package is always considered non-partial).

I read the specification at
[typing.python.org/en/latest/spec/distributing.html#stub-only-packages](https://typing.python.org/en/latest/spec/distributing.html#stub-only-packages)
but I found it lacking some details, especially on how to handle
namespace packages or when the regular and stub packages disagree on
whether they're namespace packages. I tried to document my decisions in
the mdtests where the specification isn't clear and compared the
behavior to Pyright.

Mypy seems to only support stub packages in the venv folder. At least,
it never picked up my stub packages otherwise. I decided not to spend
too much time fighting mypyp, which is why I focused the comparison
around Pyright

Closes https://github.com/astral-sh/ruff/issues/16612

## Test plan

Added mdtests
2025-04-07 14:40:50 +02:00
Andrew Gallant
c12c76e9c8 ruff_annotate_snippets: address unused code warnings
Fixes #17230
2025-04-07 08:24:08 -04:00
Alex Waygood
81cf860dc8 [red-knot] Add a couple more tests for * imports (#17270)
## Summary

Some more edge cases that I thought of while working on integrating
knowledge of statically known branches into the `*`-import machinery

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-04-07 11:12:28 +00:00
Micha Reiser
3150812ac4 [red-knot] Add 'Format document' to playground (#17217)
## Summary
This is more "because we can" than something we need. 

But since we're already building an "almost IDE" 

## Test Plan



https://github.com/user-attachments/assets/3a4bdad1-ba32-455a-9909-cfeb8caa1b28
2025-04-07 09:26:03 +02:00
renovate[bot]
12d7fad4ef Update actions/setup-node action to v4.3.0 (#17259)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-node](https://redirect.github.com/actions/setup-node) |
action | minor | `v4` -> `v4.3.0` |

---

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

---

### Release Notes

<details>
<summary>actions/setup-node (actions/setup-node)</summary>

###
[`v4.3.0`](https://redirect.github.com/actions/setup-node/compare/v4.2.0...v4.3.0)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4.2.0...v4.3.0)

###
[`v4.2.0`](https://redirect.github.com/actions/setup-node/releases/tag/v4.2.0)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4.1.0...v4.2.0)

#### What's Changed

- Enhance workflows and upgrade publish-actions from 0.2.2 to 0.3.0 by
[@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y) in
[https://github.com/actions/setup-node/pull/1174](https://redirect.github.com/actions/setup-node/pull/1174)
- Add recommended permissions section to readme by
[@&#8203;benwells](https://redirect.github.com/benwells) in
[https://github.com/actions/setup-node/pull/1193](https://redirect.github.com/actions/setup-node/pull/1193)
- Configure Dependabot settings by
[@&#8203;HarithaVattikuti](https://redirect.github.com/HarithaVattikuti)
in
[https://github.com/actions/setup-node/pull/1192](https://redirect.github.com/actions/setup-node/pull/1192)
- Upgrade `@actions/cache` to `^4.0.0` by
[@&#8203;priyagupta108](https://redirect.github.com/priyagupta108) in
[https://github.com/actions/setup-node/pull/1191](https://redirect.github.com/actions/setup-node/pull/1191)
- Upgrade pnpm/action-setup from 2 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/1194](https://redirect.github.com/actions/setup-node/pull/1194)
- Upgrade actions/publish-immutable-action from 0.0.3 to 0.0.4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/1195](https://redirect.github.com/actions/setup-node/pull/1195)
- Upgrade semver from 7.6.0 to 7.6.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/1196](https://redirect.github.com/actions/setup-node/pull/1196)
- Upgrade [@&#8203;types/jest](https://redirect.github.com/types/jest)
from 29.5.12 to 29.5.14 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/1201](https://redirect.github.com/actions/setup-node/pull/1201)
- Upgrade undici from 5.28.4 to 5.28.5 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/1205](https://redirect.github.com/actions/setup-node/pull/1205)

#### New Contributors

- [@&#8203;benwells](https://redirect.github.com/benwells) made their
first contribution in
[https://github.com/actions/setup-node/pull/1193](https://redirect.github.com/actions/setup-node/pull/1193)

**Full Changelog**:
https://github.com/actions/setup-node/compare/v4...v4.2.0

###
[`v4.1.0`](https://redirect.github.com/actions/setup-node/releases/tag/v4.1.0)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4.0.4...v4.1.0)

#### What's Changed

- Resolve High Security Alerts by upgrading Dependencies by
[@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y) in
[https://github.com/actions/setup-node/pull/1132](https://redirect.github.com/actions/setup-node/pull/1132)
- Upgrade IA Publish by
[@&#8203;Jcambass](https://redirect.github.com/Jcambass) in
[https://github.com/actions/setup-node/pull/1134](https://redirect.github.com/actions/setup-node/pull/1134)
- Revise `isGhes` logic by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/setup-node/pull/1148](https://redirect.github.com/actions/setup-node/pull/1148)
- Add architecture to cache key by
[@&#8203;pengx17](https://redirect.github.com/pengx17) in
[https://github.com/actions/setup-node/pull/843](https://redirect.github.com/actions/setup-node/pull/843)
This addresses issues with caching by adding the architecture (arch) to
the cache key, ensuring that cache keys are accurate to prevent
conflicts.
Note: This change may break previous cache keys as they will no longer
be compatible with the new format.

#### New Contributors

- [@&#8203;jww3](https://redirect.github.com/jww3) made their first
contribution in
[https://github.com/actions/setup-node/pull/1148](https://redirect.github.com/actions/setup-node/pull/1148)
- [@&#8203;pengx17](https://redirect.github.com/pengx17) made their
first contribution in
[https://github.com/actions/setup-node/pull/843](https://redirect.github.com/actions/setup-node/pull/843)

**Full Changelog**:
https://github.com/actions/setup-node/compare/v4...v4.1.0

###
[`v4.0.4`](https://redirect.github.com/actions/setup-node/releases/tag/v4.0.4)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4.0.3...v4.0.4)

#### What's Changed

- Add workflow file for publishing releases to immutable action package
by [@&#8203;Jcambass](https://redirect.github.com/Jcambass) in
[https://github.com/actions/setup-node/pull/1125](https://redirect.github.com/actions/setup-node/pull/1125)
- Enhance Windows ARM64 Setup and Update micromatch Dependency by
[@&#8203;priyagupta108](https://redirect.github.com/priyagupta108) in
[https://github.com/actions/setup-node/pull/1126](https://redirect.github.com/actions/setup-node/pull/1126)

##### Documentation changes:

- Documentation update in the README file by
[@&#8203;suyashgaonkar](https://redirect.github.com/suyashgaonkar) in
[https://github.com/actions/setup-node/pull/1106](https://redirect.github.com/actions/setup-node/pull/1106)
- Correct invalid 'lts' version string reference by
[@&#8203;fulldecent](https://redirect.github.com/fulldecent) in
[https://github.com/actions/setup-node/pull/1124](https://redirect.github.com/actions/setup-node/pull/1124)

#### New Contributors

- [@&#8203;suyashgaonkar](https://redirect.github.com/suyashgaonkar)
made their first contribution in
[https://github.com/actions/setup-node/pull/1106](https://redirect.github.com/actions/setup-node/pull/1106)
- [@&#8203;priyagupta108](https://redirect.github.com/priyagupta108)
made their first contribution in
[https://github.com/actions/setup-node/pull/1126](https://redirect.github.com/actions/setup-node/pull/1126)
- [@&#8203;Jcambass](https://redirect.github.com/Jcambass) made their
first contribution in
[https://github.com/actions/setup-node/pull/1125](https://redirect.github.com/actions/setup-node/pull/1125)
- [@&#8203;fulldecent](https://redirect.github.com/fulldecent) made
their first contribution in
[https://github.com/actions/setup-node/pull/1124](https://redirect.github.com/actions/setup-node/pull/1124)

**Full Changelog**:
https://github.com/actions/setup-node/compare/v4...v4.0.4

###
[`v4.0.3`](https://redirect.github.com/actions/setup-node/releases/tag/v4.0.3)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4.0.2...v4.0.3)

##### What's Changed

##### Bug fixes:

- Fix macos latest check failures by
[@&#8203;HarithaVattikuti](https://redirect.github.com/HarithaVattikuti)
in
[https://github.com/actions/setup-node/pull/1041](https://redirect.github.com/actions/setup-node/pull/1041)

##### Documentation changes:

- Documentation update to update default Node version to 20 by
[@&#8203;bengreeley](https://redirect.github.com/bengreeley) in
[https://github.com/actions/setup-node/pull/949](https://redirect.github.com/actions/setup-node/pull/949)

##### Dependency  updates:

- Bump undici from 5.26.5 to 5.28.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/965](https://redirect.github.com/actions/setup-node/pull/965)
- Bump braces from 3.0.2 to 3.0.3 and other dependency updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-node/pull/1087](https://redirect.github.com/actions/setup-node/pull/1087)

##### New Contributors

- [@&#8203;bengreeley](https://redirect.github.com/bengreeley) made
their first contribution in
[https://github.com/actions/setup-node/pull/949](https://redirect.github.com/actions/setup-node/pull/949)
-
[@&#8203;HarithaVattikuti](https://redirect.github.com/HarithaVattikuti)
made their first contribution in
[https://github.com/actions/setup-node/pull/1041](https://redirect.github.com/actions/setup-node/pull/1041)

**Full Changelog**:
https://github.com/actions/setup-node/compare/v4...v4.0.3

###
[`v4.0.2`](https://redirect.github.com/actions/setup-node/releases/tag/v4.0.2)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4.0.1...v4.0.2)

##### What's Changed

- Add support for `volta.extends` by
[@&#8203;ThisIsManta](https://redirect.github.com/ThisIsManta) in
[https://github.com/actions/setup-node/pull/921](https://redirect.github.com/actions/setup-node/pull/921)
- Add support for arm64 Windows by
[@&#8203;dmitry-shibanov](https://redirect.github.com/dmitry-shibanov)
in
[https://github.com/actions/setup-node/pull/927](https://redirect.github.com/actions/setup-node/pull/927)

##### New Contributors

- [@&#8203;ThisIsManta](https://redirect.github.com/ThisIsManta) made
their first contribution in
[https://github.com/actions/setup-node/pull/921](https://redirect.github.com/actions/setup-node/pull/921)

**Full Changelog**:
https://github.com/actions/setup-node/compare/v4.0.1...v4.0.2

###
[`v4.0.1`](https://redirect.github.com/actions/setup-node/releases/tag/v4.0.1)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4...v4.0.1)

##### What's Changed

- Ignore engines in Yarn 1 e2e-cache tests by
[@&#8203;trivikr](https://redirect.github.com/trivikr) in
[https://github.com/actions/setup-node/pull/882](https://redirect.github.com/actions/setup-node/pull/882)
- Update setup-node references in the README.md file to setup-node@v4 by
[@&#8203;jwetzell](https://redirect.github.com/jwetzell) in
[https://github.com/actions/setup-node/pull/884](https://redirect.github.com/actions/setup-node/pull/884)
- Update reusable workflows to use Node.js v20 by
[@&#8203;MaksimZhukov](https://redirect.github.com/MaksimZhukov) in
[https://github.com/actions/setup-node/pull/889](https://redirect.github.com/actions/setup-node/pull/889)
- Add fix for cache to resolve slow post action step by
[@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y) in
[https://github.com/actions/setup-node/pull/917](https://redirect.github.com/actions/setup-node/pull/917)
- Fix README.md by
[@&#8203;takayamaki](https://redirect.github.com/takayamaki) in
[https://github.com/actions/setup-node/pull/898](https://redirect.github.com/actions/setup-node/pull/898)
- Add `package.json` to `node-version-file` list of examples. by
[@&#8203;TWiStErRob](https://redirect.github.com/TWiStErRob) in
[https://github.com/actions/setup-node/pull/879](https://redirect.github.com/actions/setup-node/pull/879)
- Fix node-version-file interprets entire package.json as a version by
[@&#8203;NullVoxPopuli](https://redirect.github.com/NullVoxPopuli) in
[https://github.com/actions/setup-node/pull/865](https://redirect.github.com/actions/setup-node/pull/865)

##### New Contributors

- [@&#8203;trivikr](https://redirect.github.com/trivikr) made their
first contribution in
[https://github.com/actions/setup-node/pull/882](https://redirect.github.com/actions/setup-node/pull/882)
- [@&#8203;jwetzell](https://redirect.github.com/jwetzell) made their
first contribution in
[https://github.com/actions/setup-node/pull/884](https://redirect.github.com/actions/setup-node/pull/884)
- [@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y)
made their first contribution in
[https://github.com/actions/setup-node/pull/917](https://redirect.github.com/actions/setup-node/pull/917)
- [@&#8203;takayamaki](https://redirect.github.com/takayamaki) made
their first contribution in
[https://github.com/actions/setup-node/pull/898](https://redirect.github.com/actions/setup-node/pull/898)
- [@&#8203;TWiStErRob](https://redirect.github.com/TWiStErRob) made
their first contribution in
[https://github.com/actions/setup-node/pull/879](https://redirect.github.com/actions/setup-node/pull/879)
- [@&#8203;NullVoxPopuli](https://redirect.github.com/NullVoxPopuli)
made their first contribution in
[https://github.com/actions/setup-node/pull/865](https://redirect.github.com/actions/setup-node/pull/865)

**Full Changelog**:
https://github.com/actions/setup-node/compare/v4...v4.0.1

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:54:32 +02:00
renovate[bot]
41fec5171f Update actions/upload-artifact action to v4.6.2 (#17261)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
| action | minor | `v4` -> `v4.6.2` |

---

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

---

### Release Notes

<details>
<summary>actions/upload-artifact (actions/upload-artifact)</summary>

###
[`v4.6.2`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.6.2)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.6.1...v4.6.2)

#### What's Changed

- Update to use artifact 2.3.2 package & prepare for new upload-artifact
release by [@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) in
[https://github.com/actions/upload-artifact/pull/685](https://redirect.github.com/actions/upload-artifact/pull/685)

#### New Contributors

- [@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) made their
first contribution in
[https://github.com/actions/upload-artifact/pull/685](https://redirect.github.com/actions/upload-artifact/pull/685)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.6.2

###
[`v4.6.1`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.6.1)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1)

#### What's Changed

- Update to use artifact 2.2.2 package by
[@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) in
[https://github.com/actions/upload-artifact/pull/673](https://redirect.github.com/actions/upload-artifact/pull/673)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.6.1

###
[`v4.6.0`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.6.0)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.5.0...v4.6.0)

##### What's Changed

- Expose env vars to control concurrency and timeout by
[@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) in
[https://github.com/actions/upload-artifact/pull/662](https://redirect.github.com/actions/upload-artifact/pull/662)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.6.0

###
[`v4.5.0`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.5.0)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.4.3...v4.5.0)

##### What's Changed

- fix: deprecated `Node.js` version in action by
[@&#8203;hamirmahal](https://redirect.github.com/hamirmahal) in
[https://github.com/actions/upload-artifact/pull/578](https://redirect.github.com/actions/upload-artifact/pull/578)
- Add new `artifact-digest` output by
[@&#8203;bdehamer](https://redirect.github.com/bdehamer) in
[https://github.com/actions/upload-artifact/pull/656](https://redirect.github.com/actions/upload-artifact/pull/656)

##### New Contributors

- [@&#8203;hamirmahal](https://redirect.github.com/hamirmahal) made
their first contribution in
[https://github.com/actions/upload-artifact/pull/578](https://redirect.github.com/actions/upload-artifact/pull/578)
- [@&#8203;bdehamer](https://redirect.github.com/bdehamer) made their
first contribution in
[https://github.com/actions/upload-artifact/pull/656](https://redirect.github.com/actions/upload-artifact/pull/656)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.4.3...v4.5.0

###
[`v4.4.3`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.4.3)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.4.2...v4.4.3)

#### What's Changed

- Undo indirect dependency updates from
[#&#8203;627](https://redirect.github.com/actions/upload-artifact/issues/627)
by [@&#8203;joshmgross](https://redirect.github.com/joshmgross) in
[https://github.com/actions/upload-artifact/pull/632](https://redirect.github.com/actions/upload-artifact/pull/632)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.4.2...v4.4.3

###
[`v4.4.2`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.4.2)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.4.1...v4.4.2)

#### What's Changed

- Bump `@actions/artifact` to 2.1.11 by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/627](https://redirect.github.com/actions/upload-artifact/pull/627)
    -   Includes fix for relative symlinks not resolving properly

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.4.1...v4.4.2

###
[`v4.4.1`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.4.1)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.4.0...v4.4.1)

#### What's Changed

- Add a section about hidden files by
[@&#8203;joshmgross](https://redirect.github.com/joshmgross) in
[https://github.com/actions/upload-artifact/pull/607](https://redirect.github.com/actions/upload-artifact/pull/607)
- Add workflow file for publishing releases to immutable action package
by [@&#8203;Jcambass](https://redirect.github.com/Jcambass) in
[https://github.com/actions/upload-artifact/pull/621](https://redirect.github.com/actions/upload-artifact/pull/621)
- Update
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
to latest version, includes symlink and timeout fixes by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/625](https://redirect.github.com/actions/upload-artifact/pull/625)

#### New Contributors

- [@&#8203;Jcambass](https://redirect.github.com/Jcambass) made their
first contribution in
[https://github.com/actions/upload-artifact/pull/621](https://redirect.github.com/actions/upload-artifact/pull/621)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.4.0...v4.4.1

###
[`v4.4.0`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.4.0)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.6...v4.4.0)

#### Notice: Breaking Changes ⚠️

We will no longer include hidden files and folders by default in the
`upload-artifact` action of this version. This reduces the risk that
credentials are accidentally uploaded into artifacts. Customers who need
to continue to upload these files can use a new option,
`include-hidden-files`, to continue to do so.

See ["Notice of upcoming deprecations and breaking changes in GitHub
Actions
runners"](https://github.blog/changelog/2024-08-19-notice-of-upcoming-deprecations-and-breaking-changes-in-github-actions-runners/)
changelog and [this
issue](https://redirect.github.com/actions/upload-artifact/issues/602)
for more details.

#### What's Changed

- Exclude hidden files by default by
[@&#8203;joshmgross](https://redirect.github.com/joshmgross) in
[https://github.com/actions/upload-artifact/pull/598](https://redirect.github.com/actions/upload-artifact/pull/598)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.3.6...v4.4.0

###
[`v4.3.6`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.6)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.5...v4.3.6)

#### What's Changed

- Revert to
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
2.1.8 by [@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/594](https://redirect.github.com/actions/upload-artifact/pull/594)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.3.6

###
[`v4.3.5`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.5)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.4...v4.3.5)

#### What's Changed

- Bump
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
to v2.1.9 by [@&#8203;robherley](https://redirect.github.com/robherley)
in
[https://github.com/actions/upload-artifact/pull/588](https://redirect.github.com/actions/upload-artifact/pull/588)
- Fixed artifact upload chunk timeout logic
[#&#8203;1774](https://redirect.github.com/actions/toolkit/pull/1774)
- Use lazy stream to prevent issues with open file limits
[#&#8203;1771](https://redirect.github.com/actions/toolkit/pull/1771)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.3.4...v4.3.5

###
[`v4.3.4`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.4)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.3...v4.3.4)

#### What's Changed

- Update
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
version, bump dependencies by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/584](https://redirect.github.com/actions/upload-artifact/pull/584)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.3.3...v4.3.4

###
[`v4.3.3`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.3)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.2...v4.3.3)

#### What's Changed

- updating `@actions/artifact` dependency to v2.1.6 by
[@&#8203;eggyhead](https://redirect.github.com/eggyhead) in
[https://github.com/actions/upload-artifact/pull/565](https://redirect.github.com/actions/upload-artifact/pull/565)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.3.2...v4.3.3

###
[`v4.3.2`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.2)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.1...v4.3.2)

#### What's Changed

- Update release-new-action-version.yml by
[@&#8203;konradpabjan](https://redirect.github.com/konradpabjan) in
[https://github.com/actions/upload-artifact/pull/516](https://redirect.github.com/actions/upload-artifact/pull/516)
- Minor fix to the migration readme by
[@&#8203;andrewakim](https://redirect.github.com/andrewakim) in
[https://github.com/actions/upload-artifact/pull/523](https://redirect.github.com/actions/upload-artifact/pull/523)
- Update readme with v3/v2/v1 deprecation notice by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/561](https://redirect.github.com/actions/upload-artifact/pull/561)
- updating `@actions/artifact` dependency to v2.1.5 and `@actions/core`
to v1.0.1 by [@&#8203;eggyhead](https://redirect.github.com/eggyhead) in
[https://github.com/actions/upload-artifact/pull/562](https://redirect.github.com/actions/upload-artifact/pull/562)

#### New Contributors

- [@&#8203;andrewakim](https://redirect.github.com/andrewakim) made
their first contribution in
[https://github.com/actions/upload-artifact/pull/523](https://redirect.github.com/actions/upload-artifact/pull/523)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4.3.1...v4.3.2

###
[`v4.3.1`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.1)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.3.0...v4.3.1)

- Bump
[@&#8203;actions/artifacts](https://redirect.github.com/actions/artifacts)
to latest version to include [updated GHES host
check](https://redirect.github.com/actions/toolkit/pull/1648)

###
[`v4.3.0`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.3.0)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.2.0...v4.3.0)

#### What's Changed

- Reorganize upload code in prep for merge logic & add more tests by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/504](https://redirect.github.com/actions/upload-artifact/pull/504)
- Add sub-action to merge artifacts by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/505](https://redirect.github.com/actions/upload-artifact/pull/505)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.3.0

###
[`v4.2.0`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.2.0)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4.1.0...v4.2.0)

#### What's Changed

- Ability to overwrite an Artifact by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/501](https://redirect.github.com/actions/upload-artifact/pull/501)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.2.0

###
[`v4.1.0`](https://redirect.github.com/actions/upload-artifact/releases/tag/v4.1.0)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v4...v4.1.0)

#### What's Changed

- Add migrations docs by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/482](https://redirect.github.com/actions/upload-artifact/pull/482)
- Update README.md by
[@&#8203;samuelwine](https://redirect.github.com/samuelwine) in
[https://github.com/actions/upload-artifact/pull/492](https://redirect.github.com/actions/upload-artifact/pull/492)
- Support artifact-url output by
[@&#8203;konradpabjan](https://redirect.github.com/konradpabjan) in
[https://github.com/actions/upload-artifact/pull/496](https://redirect.github.com/actions/upload-artifact/pull/496)
- Update readme to reflect new 500 artifact per job limit by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/upload-artifact/pull/497](https://redirect.github.com/actions/upload-artifact/pull/497)

#### New Contributors

- [@&#8203;samuelwine](https://redirect.github.com/samuelwine) made
their first contribution in
[https://github.com/actions/upload-artifact/pull/492](https://redirect.github.com/actions/upload-artifact/pull/492)

**Full Changelog**:
https://github.com/actions/upload-artifact/compare/v4...v4.1.0

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:49:44 +02:00
renovate[bot]
db69dfba45 Update actions/download-artifact action to v4.2.1 (#17258)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/download-artifact](https://redirect.github.com/actions/download-artifact)
| action | minor | `v4` -> `v4.2.1` |

---

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

---

### Release Notes

<details>
<summary>actions/download-artifact (actions/download-artifact)</summary>

###
[`v4.2.1`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.2.1)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.2.0...v4.2.1)

#### What's Changed

- Add unit tests by
[@&#8203;GhadimiR](https://redirect.github.com/GhadimiR) in
[https://github.com/actions/download-artifact/pull/392](https://redirect.github.com/actions/download-artifact/pull/392)
- Fix bug introduced in 4.2.0 by
[@&#8203;GhadimiR](https://redirect.github.com/GhadimiR) in
[https://github.com/actions/download-artifact/pull/391](https://redirect.github.com/actions/download-artifact/pull/391)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4.2.0...v4.2.1

###
[`v4.2.0`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.2.0)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.9...v4.2.0)

#### What's Changed

- Update README.md by
[@&#8203;lkfortuna](https://redirect.github.com/lkfortuna) in
[https://github.com/actions/download-artifact/pull/384](https://redirect.github.com/actions/download-artifact/pull/384)
- Bump artifact version, do digest check by
[@&#8203;GhadimiR](https://redirect.github.com/GhadimiR) in
[https://github.com/actions/download-artifact/pull/383](https://redirect.github.com/actions/download-artifact/pull/383)

#### New Contributors

- [@&#8203;lkfortuna](https://redirect.github.com/lkfortuna) made their
first contribution in
[https://github.com/actions/download-artifact/pull/384](https://redirect.github.com/actions/download-artifact/pull/384)
- [@&#8203;GhadimiR](https://redirect.github.com/GhadimiR) made their
first contribution in
[https://github.com/actions/download-artifact/pull/383](https://redirect.github.com/actions/download-artifact/pull/383)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4.1.9...v4.2.0

###
[`v4.1.9`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.9)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.8...v4.1.9)

#### What's Changed

- Add workflow file for publishing releases to immutable action package
by [@&#8203;Jcambass](https://redirect.github.com/Jcambass) in
[https://github.com/actions/download-artifact/pull/354](https://redirect.github.com/actions/download-artifact/pull/354)
- docs: small migration fix by
[@&#8203;froblesmartin](https://redirect.github.com/froblesmartin) in
[https://github.com/actions/download-artifact/pull/370](https://redirect.github.com/actions/download-artifact/pull/370)
- Update MIGRATION.md by
[@&#8203;andyfeller](https://redirect.github.com/andyfeller) in
[https://github.com/actions/download-artifact/pull/372](https://redirect.github.com/actions/download-artifact/pull/372)
- Update artifact package to 2.2.2 by
[@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) in
[https://github.com/actions/download-artifact/pull/380](https://redirect.github.com/actions/download-artifact/pull/380)

#### New Contributors

- [@&#8203;Jcambass](https://redirect.github.com/Jcambass) made their
first contribution in
[https://github.com/actions/download-artifact/pull/354](https://redirect.github.com/actions/download-artifact/pull/354)
- [@&#8203;froblesmartin](https://redirect.github.com/froblesmartin)
made their first contribution in
[https://github.com/actions/download-artifact/pull/370](https://redirect.github.com/actions/download-artifact/pull/370)
- [@&#8203;andyfeller](https://redirect.github.com/andyfeller) made
their first contribution in
[https://github.com/actions/download-artifact/pull/372](https://redirect.github.com/actions/download-artifact/pull/372)
- [@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) made their
first contribution in
[https://github.com/actions/download-artifact/pull/380](https://redirect.github.com/actions/download-artifact/pull/380)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4...v4.1.9

###
[`v4.1.8`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.8)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.7...v4.1.8)

#### What's Changed

- Update
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
version, bump dependencies by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/download-artifact/pull/341](https://redirect.github.com/actions/download-artifact/pull/341)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4...v4.1.8

###
[`v4.1.7`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.7)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.6...v4.1.7)

#### What's Changed

- Update
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
dependency by
[@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) in
[https://github.com/actions/download-artifact/pull/325](https://redirect.github.com/actions/download-artifact/pull/325)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4.1.6...v4.1.7

###
[`v4.1.6`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.6)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.5...v4.1.6)

#### What's Changed

- updating `@actions/artifact` dependency to v2.1.6 by
[@&#8203;eggyhead](https://redirect.github.com/eggyhead) in
[https://github.com/actions/download-artifact/pull/324](https://redirect.github.com/actions/download-artifact/pull/324)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4.1.5...v4.1.6

###
[`v4.1.5`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.5)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.4...v4.1.5)

#### What's Changed

- Update readme with v3/v2/v1 deprecation notice by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/download-artifact/pull/322](https://redirect.github.com/actions/download-artifact/pull/322)
- Update dependencies `@actions/core` to v1.10.1 and `@actions/artifact`
to v2.1.5

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4.1.4...v4.1.5

###
[`v4.1.4`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.4)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.3...v4.1.4)

#### What's Changed

- Update
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
by [@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) in
[https://github.com/actions/download-artifact/pull/307](https://redirect.github.com/actions/download-artifact/pull/307)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4...v4.1.4

###
[`v4.1.3`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.3)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.2...v4.1.3)

#### What's Changed

- Update release-new-action-version.yml by
[@&#8203;konradpabjan](https://redirect.github.com/konradpabjan) in
[https://github.com/actions/download-artifact/pull/292](https://redirect.github.com/actions/download-artifact/pull/292)
- Update toolkit dependency with updated unzip logic by
[@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) in
[https://github.com/actions/download-artifact/pull/299](https://redirect.github.com/actions/download-artifact/pull/299)
- Update
[@&#8203;actions/artifact](https://redirect.github.com/actions/artifact)
by [@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) in
[https://github.com/actions/download-artifact/pull/303](https://redirect.github.com/actions/download-artifact/pull/303)

#### New Contributors

- [@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) made
their first contribution in
[https://github.com/actions/download-artifact/pull/299](https://redirect.github.com/actions/download-artifact/pull/299)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4...v4.1.3

###
[`v4.1.2`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.2)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.1...v4.1.2)

- Bump
[@&#8203;actions/artifacts](https://redirect.github.com/actions/artifacts)
to latest version to include [updated GHES host
check](https://redirect.github.com/actions/toolkit/pull/1648)

###
[`v4.1.1`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.1)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4.1.0...v4.1.1)

- Fix transient request timeouts
[https://github.com/actions/download-artifact/issues/249](https://redirect.github.com/actions/download-artifact/issues/249)
-   Bump `@actions/artifacts` to latest version

###
[`v4.1.0`](https://redirect.github.com/actions/download-artifact/releases/tag/v4.1.0)

[Compare
Source](https://redirect.github.com/actions/download-artifact/compare/v4...v4.1.0)

#### What's Changed

- Some cleanup by
[@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/download-artifact/pull/247](https://redirect.github.com/actions/download-artifact/pull/247)
- Fix default for run-id by
[@&#8203;stchr](https://redirect.github.com/stchr) in
[https://github.com/actions/download-artifact/pull/252](https://redirect.github.com/actions/download-artifact/pull/252)
- Support pattern matching to filter artifacts & merge to same directory
by [@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/download-artifact/pull/259](https://redirect.github.com/actions/download-artifact/pull/259)

#### New Contributors

- [@&#8203;stchr](https://redirect.github.com/stchr) made their first
contribution in
[https://github.com/actions/download-artifact/pull/252](https://redirect.github.com/actions/download-artifact/pull/252)

**Full Changelog**:
https://github.com/actions/download-artifact/compare/v4...v4.1.0

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 06:49:17 +00:00
renovate[bot]
50eb3f539e Update actions/setup-python action to v5.5.0 (#17260)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/setup-python](https://redirect.github.com/actions/setup-python)
| action | minor | `v5` -> `v5.5.0` |

---

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

---

### Release Notes

<details>
<summary>actions/setup-python (actions/setup-python)</summary>

###
[`v5.5.0`](https://redirect.github.com/actions/setup-python/releases/tag/v5.5.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5.4.0...v5.5.0)

##### What's Changed

##### Enhancements:

- Support free threaded Python versions like '3.13t' by
[@&#8203;colesbury](https://redirect.github.com/colesbury) in
[https://github.com/actions/setup-python/pull/973](https://redirect.github.com/actions/setup-python/pull/973)
- Enhance Workflows: Include ubuntu-arm runners, Add e2e Testing for
free threaded and Upgrade
[@&#8203;action/cache](https://redirect.github.com/action/cache) from
4.0.0 to 4.0.3 by
[@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali) in
[https://github.com/actions/setup-python/pull/1056](https://redirect.github.com/actions/setup-python/pull/1056)
- Add support for .tool-versions file in setup-python by
[@&#8203;mahabaleshwars](https://redirect.github.com/mahabaleshwars) in
[https://github.com/actions/setup-python/pull/1043](https://redirect.github.com/actions/setup-python/pull/1043)

##### Bug fixes:

- Fix architecture for pypy on Linux ARM64 by
[@&#8203;mayeut](https://redirect.github.com/mayeut) in
[https://github.com/actions/setup-python/pull/1011](https://redirect.github.com/actions/setup-python/pull/1011)
This update maps arm64 to aarch64 for Linux ARM64 PyPy installations.

##### Dependency updates:

- Upgrade [@&#8203;vercel/ncc](https://redirect.github.com/vercel/ncc)
from 0.38.1 to 0.38.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/1016](https://redirect.github.com/actions/setup-python/pull/1016)
- Upgrade
[@&#8203;actions/glob](https://redirect.github.com/actions/glob) from
0.4.0 to 0.5.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/1015](https://redirect.github.com/actions/setup-python/pull/1015)

##### New Contributors

- [@&#8203;colesbury](https://redirect.github.com/colesbury) made their
first contribution in
[https://github.com/actions/setup-python/pull/973](https://redirect.github.com/actions/setup-python/pull/973)
- [@&#8203;mahabaleshwars](https://redirect.github.com/mahabaleshwars)
made their first contribution in
[https://github.com/actions/setup-python/pull/1043](https://redirect.github.com/actions/setup-python/pull/1043)

**Full Changelog**:
https://github.com/actions/setup-python/compare/v5...v5.5.0

###
[`v5.4.0`](https://redirect.github.com/actions/setup-python/releases/tag/v5.4.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5.3.0...v5.4.0)

#### What's Changed

##### Enhancements:

- Update cache error message by
[@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y) in
[https://github.com/actions/setup-python/pull/968](https://redirect.github.com/actions/setup-python/pull/968)
- Enhance Workflows: Add Ubuntu-24, Remove Python 3.8 by
[@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali) in
[https://github.com/actions/setup-python/pull/985](https://redirect.github.com/actions/setup-python/pull/985)
- Configure Dependabot settings by
[@&#8203;HarithaVattikuti](https://redirect.github.com/HarithaVattikuti)
in
[https://github.com/actions/setup-python/pull/1008](https://redirect.github.com/actions/setup-python/pull/1008)

##### Documentation changes:

- Readme update - recommended permissions by
[@&#8203;benwells](https://redirect.github.com/benwells) in
[https://github.com/actions/setup-python/pull/1009](https://redirect.github.com/actions/setup-python/pull/1009)
- Improve Advanced Usage examples by
[@&#8203;lrq3000](https://redirect.github.com/lrq3000) in
[https://github.com/actions/setup-python/pull/645](https://redirect.github.com/actions/setup-python/pull/645)

##### Dependency updates:

- Upgrade `undici` from 5.28.4 to 5.28.5 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/1012](https://redirect.github.com/actions/setup-python/pull/1012)
- Upgrade `urllib3` from 1.25.9 to 1.26.19 in /**tests**/data by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/895](https://redirect.github.com/actions/setup-python/pull/895)
- Upgrade `actions/publish-immutable-action` from 0.0.3 to 0.0.4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/1014](https://redirect.github.com/actions/setup-python/pull/1014)
- Upgrade `@actions/http-client` from 2.2.1 to 2.2.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/1020](https://redirect.github.com/actions/setup-python/pull/1020)
- Upgrade `requests` from 2.24.0 to 2.32.2 in /**tests**/data by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/1019](https://redirect.github.com/actions/setup-python/pull/1019)
- Upgrade `@actions/cache` to `^4.0.0` by
[@&#8203;priyagupta108](https://redirect.github.com/priyagupta108) in
[https://github.com/actions/setup-python/pull/1007](https://redirect.github.com/actions/setup-python/pull/1007)

#### New Contributors

- [@&#8203;benwells](https://redirect.github.com/benwells) made their
first contribution in
[https://github.com/actions/setup-python/pull/1009](https://redirect.github.com/actions/setup-python/pull/1009)
-
[@&#8203;HarithaVattikuti](https://redirect.github.com/HarithaVattikuti)
made their first contribution in
[https://github.com/actions/setup-python/pull/1008](https://redirect.github.com/actions/setup-python/pull/1008)
- [@&#8203;lrq3000](https://redirect.github.com/lrq3000) made their
first contribution in
[https://github.com/actions/setup-python/pull/645](https://redirect.github.com/actions/setup-python/pull/645)

**Full Changelog**:
https://github.com/actions/setup-python/compare/v5...v5.4.0

###
[`v5.3.0`](https://redirect.github.com/actions/setup-python/releases/tag/v5.3.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5.2.0...v5.3.0)

##### What's Changed

- Add workflow file for publishing releases to immutable action package
by [@&#8203;Jcambass](https://redirect.github.com/Jcambass) in
[https://github.com/actions/setup-python/pull/941](https://redirect.github.com/actions/setup-python/pull/941)
- Upgrade IA publish by
[@&#8203;Jcambass](https://redirect.github.com/Jcambass) in
[https://github.com/actions/setup-python/pull/943](https://redirect.github.com/actions/setup-python/pull/943)

##### Bug Fixes:

- Normalise Line Endings to Ensure Cross-Platform Consistency by
[@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali) in
[https://github.com/actions/setup-python/pull/938](https://redirect.github.com/actions/setup-python/pull/938)
- Revise `isGhes` logic by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/setup-python/pull/963](https://redirect.github.com/actions/setup-python/pull/963)
- Bump pillow from 7.2 to 10.2.0 by
[@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y) in
[https://github.com/actions/setup-python/pull/956](https://redirect.github.com/actions/setup-python/pull/956)

##### Enhancements:

- Enhance workflows and documentation updates by
[@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali) in
[https://github.com/actions/setup-python/pull/965](https://redirect.github.com/actions/setup-python/pull/965)
- Bump default versions to latest by
[@&#8203;jeffwidman](https://redirect.github.com/jeffwidman) in
[https://github.com/actions/setup-python/pull/905](https://redirect.github.com/actions/setup-python/pull/905)

##### New Contributors

- [@&#8203;Jcambass](https://redirect.github.com/Jcambass) made their
first contribution in
[https://github.com/actions/setup-python/pull/941](https://redirect.github.com/actions/setup-python/pull/941)
- [@&#8203;jww3](https://redirect.github.com/jww3) made their first
contribution in
[https://github.com/actions/setup-python/pull/963](https://redirect.github.com/actions/setup-python/pull/963)

**Full Changelog**:
https://github.com/actions/setup-python/compare/v5...v5.3.0

###
[`v5.2.0`](https://redirect.github.com/actions/setup-python/releases/tag/v5.2.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5.1.1...v5.2.0)

#### What's Changed

##### Bug fixes:

- Add `.zip` extension to Windows package downloads for `Expand-Archive`
Compatibility by
[@&#8203;priyagupta108](https://redirect.github.com/priyagupta108) in
[https://github.com/actions/setup-python/pull/916](https://redirect.github.com/actions/setup-python/pull/916)
This addresses compatibility issues on Windows self-hosted runners by
ensuring that the filenames for Python and PyPy package downloads
explicitly include the .zip extension, allowing the Expand-Archive
command to function correctly.
- Add arch to cache key by
[@&#8203;Zxilly](https://redirect.github.com/Zxilly) in
[https://github.com/actions/setup-python/pull/896](https://redirect.github.com/actions/setup-python/pull/896)
This addresses issues with caching by adding the architecture (arch) to
the cache key, ensuring that cache keys are accurate to prevent
conflicts.
Note: This change may break previous cache keys as they will no longer
be compatible with the new format.

##### Documentation changes:

- Fix display of emojis in contributors doc by
[@&#8203;sciencewhiz](https://redirect.github.com/sciencewhiz) in
[https://github.com/actions/setup-python/pull/899](https://redirect.github.com/actions/setup-python/pull/899)
- Documentation update for caching poetry dependencies by
[@&#8203;gowridurgad](https://redirect.github.com/gowridurgad) in
[https://github.com/actions/setup-python/pull/908](https://redirect.github.com/actions/setup-python/pull/908)

##### Dependency updates:

- Bump [@&#8203;iarna/toml](https://redirect.github.com/iarna/toml)
version from 2.2.5 to 3.0.0 by
[@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali) in
[https://github.com/actions/setup-python/pull/912](https://redirect.github.com/actions/setup-python/pull/912)
- Bump pyinstaller from 3.6 to 5.13.1 by
[@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y) in
[https://github.com/actions/setup-python/pull/923](https://redirect.github.com/actions/setup-python/pull/923)

#### New Contributors

- [@&#8203;sciencewhiz](https://redirect.github.com/sciencewhiz) made
their first contribution in
[https://github.com/actions/setup-python/pull/899](https://redirect.github.com/actions/setup-python/pull/899)
- [@&#8203;priyagupta108](https://redirect.github.com/priyagupta108)
made their first contribution in
[https://github.com/actions/setup-python/pull/916](https://redirect.github.com/actions/setup-python/pull/916)
- [@&#8203;Zxilly](https://redirect.github.com/Zxilly) made their first
contribution in
[https://github.com/actions/setup-python/pull/896](https://redirect.github.com/actions/setup-python/pull/896)
- [@&#8203;aparnajyothi-y](https://redirect.github.com/aparnajyothi-y)
made their first contribution in
[https://github.com/actions/setup-python/pull/923](https://redirect.github.com/actions/setup-python/pull/923)

**Full Changelog**:
https://github.com/actions/setup-python/compare/v5...v5.2.0

###
[`v5.1.1`](https://redirect.github.com/actions/setup-python/releases/tag/v5.1.1)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5.1.0...v5.1.1)

#### What's Changed

##### Bug fixes:

- fix(ci): update all failing workflows by
[@&#8203;mayeut](https://redirect.github.com/mayeut) in
[https://github.com/actions/setup-python/pull/863](https://redirect.github.com/actions/setup-python/pull/863)
This update ensures compatibility and optimal performance of workflows
on the latest macOS version.

##### Documentation changes:

- Documentation update for cache by
[@&#8203;gowridurgad](https://redirect.github.com/gowridurgad) in
[https://github.com/actions/setup-python/pull/873](https://redirect.github.com/actions/setup-python/pull/873)

##### Dependency updates:

- Bump braces from 3.0.2 to 3.0.3 and undici from 5.28.3 to 5.28.4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/setup-python/pull/893](https://redirect.github.com/actions/setup-python/pull/893)

#### New Contributors

- [@&#8203;gowridurgad](https://redirect.github.com/gowridurgad) made
their first contribution in
[https://github.com/actions/setup-python/pull/873](https://redirect.github.com/actions/setup-python/pull/873)

**Full Changelog**:
https://github.com/actions/setup-python/compare/v5...v5.1.1

###
[`v5.1.0`](https://redirect.github.com/actions/setup-python/releases/tag/v5.1.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5.0.0...v5.1.0)

#### What's Changed

- Leveraging the raw API to retrieve the version-manifest, as it does
not impose a rate limit and hence facilitates unrestricted consumption
without the need for a token for Github Enterprise Servers by
[@&#8203;Shegox](https://redirect.github.com/Shegox) in
[https://github.com/actions/setup-python/pull/766](https://redirect.github.com/actions/setup-python/pull/766).
- Dependency updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot) and
[@&#8203;HarithaVattikuti](https://redirect.github.com/HarithaVattikuti)
in
[https://github.com/actions/setup-python/pull/817](https://redirect.github.com/actions/setup-python/pull/817)
- Documentation changes for version in README by
[@&#8203;basnijholt](https://redirect.github.com/basnijholt) in
[https://github.com/actions/setup-python/pull/776](https://redirect.github.com/actions/setup-python/pull/776)
- Documentation changes for link in README by
[@&#8203;ukd1](https://redirect.github.com/ukd1) in
[https://github.com/actions/setup-python/pull/793](https://redirect.github.com/actions/setup-python/pull/793)
- Documentation changes for link in Advanced Usage by
[@&#8203;Jamim](https://redirect.github.com/Jamim) in
[https://github.com/actions/setup-python/pull/782](https://redirect.github.com/actions/setup-python/pull/782)
- Documentation changes for avoiding rate limit issues on GHES by
[@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali) in
[https://github.com/actions/setup-python/pull/835](https://redirect.github.com/actions/setup-python/pull/835)

#### New Contributors

- [@&#8203;basnijholt](https://redirect.github.com/basnijholt) made
their first contribution in
[https://github.com/actions/setup-python/pull/776](https://redirect.github.com/actions/setup-python/pull/776)
- [@&#8203;ukd1](https://redirect.github.com/ukd1) made their first
contribution in
[https://github.com/actions/setup-python/pull/793](https://redirect.github.com/actions/setup-python/pull/793)
- [@&#8203;Jamim](https://redirect.github.com/Jamim) made their first
contribution in
[https://github.com/actions/setup-python/pull/782](https://redirect.github.com/actions/setup-python/pull/782)
- [@&#8203;Shegox](https://redirect.github.com/Shegox) made their first
contribution in
[https://github.com/actions/setup-python/pull/766](https://redirect.github.com/actions/setup-python/pull/766)
- [@&#8203;priya-kinthali](https://redirect.github.com/priya-kinthali)
made their first contribution in
[https://github.com/actions/setup-python/pull/835](https://redirect.github.com/actions/setup-python/pull/835)

**Full Changelog**:
https://github.com/actions/setup-python/compare/v5.0.0...v5.1.0

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:48:03 +02:00
renovate[bot]
ec30f18d02 Update actions/cache action to v4.2.3 (#17256)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://redirect.github.com/actions/cache) | action |
minor | `v4` -> `v4.2.3` |

---

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

---

### Release Notes

<details>
<summary>actions/cache (actions/cache)</summary>

###
[`v4.2.3`](https://redirect.github.com/actions/cache/releases/tag/v4.2.3)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.2.2...v4.2.3)

##### What's Changed

- Update to use
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) 4.0.3
package & prepare for new release by
[@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) in
[https://github.com/actions/cache/pull/1577](https://redirect.github.com/actions/cache/pull/1577)
(SAS tokens for cache entries are now masked in debug logs)

##### New Contributors

- [@&#8203;salmanmkc](https://redirect.github.com/salmanmkc) made their
first contribution in
[https://github.com/actions/cache/pull/1577](https://redirect.github.com/actions/cache/pull/1577)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.2.2...v4.2.3

###
[`v4.2.2`](https://redirect.github.com/actions/cache/releases/tag/v4.2.2)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.2.1...v4.2.2)

##### What's Changed

> \[!IMPORTANT]
> As a reminder, there were important backend changes to release v4.2.0,
see [those release
notes](https://redirect.github.com/actions/cache/releases/tag/v4.2.0)
and [the
announcement](https://redirect.github.com/actions/cache/discussions/1510)
for more details.

- Bump
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) to
v4.0.2 by [@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/cache/pull/1560](https://redirect.github.com/actions/cache/pull/1560)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.2.1...v4.2.2

###
[`v4.2.1`](https://redirect.github.com/actions/cache/releases/tag/v4.2.1)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.2.0...v4.2.1)

##### What's Changed

> \[!IMPORTANT]
> As a reminder, there were important backend changes to release v4.2.0,
see [those release
notes](https://redirect.github.com/actions/cache/releases/tag/v4.2.0)
and [the
announcement](https://redirect.github.com/actions/cache/discussions/1510)
for more details.

- docs: GitHub is spelled incorrectly in caching-strategies.md by
[@&#8203;janco-absa](https://redirect.github.com/janco-absa) in
[https://github.com/actions/cache/pull/1526](https://redirect.github.com/actions/cache/pull/1526)
- docs: Make the "always save prime numbers" example more clear by
[@&#8203;Tobbe](https://redirect.github.com/Tobbe) in
[https://github.com/actions/cache/pull/1525](https://redirect.github.com/actions/cache/pull/1525)
- Update force deletion docs due a recent deprecation by
[@&#8203;sebbalex](https://redirect.github.com/sebbalex) in
[https://github.com/actions/cache/pull/1500](https://redirect.github.com/actions/cache/pull/1500)
- Bump
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) to
v4.0.1 by [@&#8203;robherley](https://redirect.github.com/robherley) in
[https://github.com/actions/cache/pull/1554](https://redirect.github.com/actions/cache/pull/1554)

##### New Contributors

- [@&#8203;janco-absa](https://redirect.github.com/janco-absa) made
their first contribution in
[https://github.com/actions/cache/pull/1526](https://redirect.github.com/actions/cache/pull/1526)
- [@&#8203;Tobbe](https://redirect.github.com/Tobbe) made their first
contribution in
[https://github.com/actions/cache/pull/1525](https://redirect.github.com/actions/cache/pull/1525)
- [@&#8203;sebbalex](https://redirect.github.com/sebbalex) made their
first contribution in
[https://github.com/actions/cache/pull/1500](https://redirect.github.com/actions/cache/pull/1500)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.2.0...v4.2.1

###
[`v4.2.0`](https://redirect.github.com/actions/cache/releases/tag/v4.2.0)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.1.2...v4.2.0)

##### ⚠️ Important Changes

The cache backend service has been rewritten from the ground up for
improved performance and reliability.
[actions/cache](https://redirect.github.com/actions/cache) now
integrates with the new cache service (v2) APIs.

The new service will gradually roll out as of **February 1st, 2025**.
The legacy service will also be sunset on the same date. Changes in
these release are **fully backward compatible**.

**We are deprecating some versions of this action**. We recommend
upgrading to version `v4` or `v3` as soon as possible before **February
1st, 2025.** (Upgrade instructions below).

If you are using pinned SHAs, please use the SHAs of versions `v4.2.0`
or `v3.4.0`

If you do not upgrade, all workflow runs using any of the deprecated
[actions/cache](https://redirect.github.com/actions/cache) will fail.

Upgrading to the recommended versions will not break your workflows.

Read more about the change & access the migration guide: [reference to
the
announcement](https://redirect.github.com/actions/cache/discussions/1510).

##### Minor changes

Minor and patch version updates for these dependencies:

- [@&#8203;actions/core](https://redirect.github.com/actions/core):
`1.11.1`
- [@&#8203;actions/io](https://redirect.github.com/actions/io): `1.1.3`
- [@&#8203;vercel/ncc](https://redirect.github.com/vercel/ncc): `0.38.3`

**Full Changelog**:
https://github.com/actions/cache/compare/v4.1.2...v4.2.0

###
[`v4.1.2`](https://redirect.github.com/actions/cache/releases/tag/v4.1.2)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.1.1...v4.1.2)

##### What's Changed

- Add Bun example by
[@&#8203;idleberg](https://redirect.github.com/idleberg) in
[https://github.com/actions/cache/pull/1456](https://redirect.github.com/actions/cache/pull/1456)
- Revise `isGhes` logic by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/cache/pull/1474](https://redirect.github.com/actions/cache/pull/1474)
- Bump braces from 3.0.2 to 3.0.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/cache/pull/1475](https://redirect.github.com/actions/cache/pull/1475)
- Add dependabot.yml to enable automatic dependency upgrades by
[@&#8203;Link-](https://redirect.github.com/Link-) in
[https://github.com/actions/cache/pull/1476](https://redirect.github.com/actions/cache/pull/1476)
- Bump actions/checkout from 3 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/cache/pull/1478](https://redirect.github.com/actions/cache/pull/1478)
- Bump actions/stale from 3 to 9 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/cache/pull/1479](https://redirect.github.com/actions/cache/pull/1479)
- Bump github/codeql-action from 2 to 3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/cache/pull/1483](https://redirect.github.com/actions/cache/pull/1483)
- Bump actions/setup-node from 3 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/cache/pull/1481](https://redirect.github.com/actions/cache/pull/1481)
- Prepare `4.1.2` release by
[@&#8203;Link-](https://redirect.github.com/Link-) in
[https://github.com/actions/cache/pull/1477](https://redirect.github.com/actions/cache/pull/1477)

##### New Contributors

- [@&#8203;idleberg](https://redirect.github.com/idleberg) made their
first contribution in
[https://github.com/actions/cache/pull/1456](https://redirect.github.com/actions/cache/pull/1456)
- [@&#8203;jww3](https://redirect.github.com/jww3) made their first
contribution in
[https://github.com/actions/cache/pull/1474](https://redirect.github.com/actions/cache/pull/1474)
- [@&#8203;Link-](https://redirect.github.com/Link-) made their first
contribution in
[https://github.com/actions/cache/pull/1476](https://redirect.github.com/actions/cache/pull/1476)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.1.1...v4.1.2

###
[`v4.1.1`](https://redirect.github.com/actions/cache/releases/tag/v4.1.1)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.1.0...v4.1.1)

##### What's Changed

- Restore original behavior of `cache-hit` output by
[@&#8203;joshmgross](https://redirect.github.com/joshmgross) in
[https://github.com/actions/cache/pull/1467](https://redirect.github.com/actions/cache/pull/1467)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.1.0...v4.1.1

###
[`v4.1.0`](https://redirect.github.com/actions/cache/releases/tag/v4.1.0)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.0.2...v4.1.0)

##### What's Changed

- Fix cache-hit output when cache missed by
[@&#8203;fchimpan](https://redirect.github.com/fchimpan) in
[https://github.com/actions/cache/pull/1404](https://redirect.github.com/actions/cache/pull/1404)
- Deprecate `save-always` input by
[@&#8203;joshmgross](https://redirect.github.com/joshmgross) in
[https://github.com/actions/cache/pull/1452](https://redirect.github.com/actions/cache/pull/1452)

##### New Contributors

- [@&#8203;ottlinger](https://redirect.github.com/ottlinger) made their
first contribution in
[https://github.com/actions/cache/pull/1437](https://redirect.github.com/actions/cache/pull/1437)
- [@&#8203;Olegt0rr](https://redirect.github.com/Olegt0rr) made their
first contribution in
[https://github.com/actions/cache/pull/1377](https://redirect.github.com/actions/cache/pull/1377)
- [@&#8203;fchimpan](https://redirect.github.com/fchimpan) made their
first contribution in
[https://github.com/actions/cache/pull/1404](https://redirect.github.com/actions/cache/pull/1404)
- [@&#8203;x612skm](https://redirect.github.com/x612skm) made their
first contribution in
[https://github.com/actions/cache/pull/1434](https://redirect.github.com/actions/cache/pull/1434)
- [@&#8203;todgru](https://redirect.github.com/todgru) made their first
contribution in
[https://github.com/actions/cache/pull/1311](https://redirect.github.com/actions/cache/pull/1311)
- [@&#8203;Jcambass](https://redirect.github.com/Jcambass) made their
first contribution in
[https://github.com/actions/cache/pull/1463](https://redirect.github.com/actions/cache/pull/1463)
- [@&#8203;mackey0225](https://redirect.github.com/mackey0225) made
their first contribution in
[https://github.com/actions/cache/pull/1462](https://redirect.github.com/actions/cache/pull/1462)
- [@&#8203;quatquatt](https://redirect.github.com/quatquatt) made their
first contribution in
[https://github.com/actions/cache/pull/1445](https://redirect.github.com/actions/cache/pull/1445)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.0.2...v4.1.0

###
[`v4.0.2`](https://redirect.github.com/actions/cache/releases/tag/v4.0.2)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.0.1...v4.0.2)

#### What's Changed

- Fix `fail-on-cache-miss` not working by
[@&#8203;cdce8p](https://redirect.github.com/cdce8p) in
[https://github.com/actions/cache/pull/1327](https://redirect.github.com/actions/cache/pull/1327)

**Full Changelog**:
https://github.com/actions/cache/compare/v4.0.1...v4.0.2

###
[`v4.0.1`](https://redirect.github.com/actions/cache/releases/tag/v4.0.1)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4.0.0...v4.0.1)

#### What's Changed

- Update README.md by
[@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) in
[https://github.com/actions/cache/pull/1304](https://redirect.github.com/actions/cache/pull/1304)
- Update examples by
[@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) in
[https://github.com/actions/cache/pull/1305](https://redirect.github.com/actions/cache/pull/1305)
- Update actions/cache publish flow by
[@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) in
[https://github.com/actions/cache/pull/1340](https://redirect.github.com/actions/cache/pull/1340)
- Update
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) by
[@&#8203;bethanyj28](https://redirect.github.com/bethanyj28) in
[https://github.com/actions/cache/pull/1341](https://redirect.github.com/actions/cache/pull/1341)

#### New Contributors

- [@&#8203;yacaovsnc](https://redirect.github.com/yacaovsnc) made their
first contribution in
[https://github.com/actions/cache/pull/1304](https://redirect.github.com/actions/cache/pull/1304)

**Full Changelog**: https://github.com/actions/cache/compare/v4...v4.0.1

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:47:35 +02:00
renovate[bot]
1120def16a Update Swatinem/rust-cache action to v2.7.8 (#17255)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [Swatinem/rust-cache](https://redirect.github.com/Swatinem/rust-cache)
| action | minor | `v2` -> `v2.7.8` |

---

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

---

### Release Notes

<details>
<summary>Swatinem/rust-cache (Swatinem/rust-cache)</summary>

###
[`v2.7.8`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.8)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.7...v2.7.8)

##### What's Changed

- Include CPU arch in the cache key for arm64 Linux runners by
[@&#8203;rhysd](https://redirect.github.com/rhysd) in
[https://github.com/Swatinem/rust-cache/pull/228](https://redirect.github.com/Swatinem/rust-cache/pull/228)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.7.7...v2.7.8

###
[`v2.7.7`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.7)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.6...v2.7.7)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.7.6...v2.7.7

###
[`v2.7.6`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.6)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.5...v2.7.6)

##### What's Changed

- Updated artifact upload action to v4 by
[@&#8203;guylamar2006](https://redirect.github.com/guylamar2006) in
[https://github.com/Swatinem/rust-cache/pull/212](https://redirect.github.com/Swatinem/rust-cache/pull/212)
- Adds an option to do lookup-only of the cache by
[@&#8203;danlec](https://redirect.github.com/danlec) in
[https://github.com/Swatinem/rust-cache/pull/217](https://redirect.github.com/Swatinem/rust-cache/pull/217)
- add runner OS in cache key by
[@&#8203;rnbguy](https://redirect.github.com/rnbguy) in
[https://github.com/Swatinem/rust-cache/pull/220](https://redirect.github.com/Swatinem/rust-cache/pull/220)
- Allow opting out of caching $CARGO_HOME/bin. by
[@&#8203;benjyw](https://redirect.github.com/benjyw) in
[https://github.com/Swatinem/rust-cache/pull/216](https://redirect.github.com/Swatinem/rust-cache/pull/216)

##### New Contributors

- [@&#8203;guylamar2006](https://redirect.github.com/guylamar2006) made
their first contribution in
[https://github.com/Swatinem/rust-cache/pull/212](https://redirect.github.com/Swatinem/rust-cache/pull/212)
- [@&#8203;danlec](https://redirect.github.com/danlec) made their first
contribution in
[https://github.com/Swatinem/rust-cache/pull/217](https://redirect.github.com/Swatinem/rust-cache/pull/217)
- [@&#8203;rnbguy](https://redirect.github.com/rnbguy) made their first
contribution in
[https://github.com/Swatinem/rust-cache/pull/220](https://redirect.github.com/Swatinem/rust-cache/pull/220)
- [@&#8203;benjyw](https://redirect.github.com/benjyw) made their first
contribution in
[https://github.com/Swatinem/rust-cache/pull/216](https://redirect.github.com/Swatinem/rust-cache/pull/216)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.7.5...v2.7.6

###
[`v2.7.5`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.5)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.3...v2.7.5)

##### What's Changed

- Upgrade checkout action from version 3 to 4 by
[@&#8203;carsten-wenderdel](https://redirect.github.com/carsten-wenderdel)
in
[https://github.com/Swatinem/rust-cache/pull/190](https://redirect.github.com/Swatinem/rust-cache/pull/190)
- fix: usage of `deprecated` version of `node` by
[@&#8203;hamirmahal](https://redirect.github.com/hamirmahal) in
[https://github.com/Swatinem/rust-cache/pull/197](https://redirect.github.com/Swatinem/rust-cache/pull/197)
- Only run macOsWorkaround() on macOS by
[@&#8203;heksesang](https://redirect.github.com/heksesang) in
[https://github.com/Swatinem/rust-cache/pull/206](https://redirect.github.com/Swatinem/rust-cache/pull/206)
- Support Cargo.lock format cargo-lock v4 by
[@&#8203;NobodyXu](https://redirect.github.com/NobodyXu) in
[https://github.com/Swatinem/rust-cache/pull/211](https://redirect.github.com/Swatinem/rust-cache/pull/211)

##### New Contributors

-
[@&#8203;carsten-wenderdel](https://redirect.github.com/carsten-wenderdel)
made their first contribution in
[https://github.com/Swatinem/rust-cache/pull/190](https://redirect.github.com/Swatinem/rust-cache/pull/190)
- [@&#8203;hamirmahal](https://redirect.github.com/hamirmahal) made
their first contribution in
[https://github.com/Swatinem/rust-cache/pull/197](https://redirect.github.com/Swatinem/rust-cache/pull/197)
- [@&#8203;heksesang](https://redirect.github.com/heksesang) made their
first contribution in
[https://github.com/Swatinem/rust-cache/pull/206](https://redirect.github.com/Swatinem/rust-cache/pull/206)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.7.3...v2.7.5

###
[`v2.7.3`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.3)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.2...v2.7.3)

- Work around upstream problem that causes cache saving to hang for
minutes.

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.7.2...v2.7.3

###
[`v2.7.2`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.2)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.1...v2.7.2)

##### What's Changed

- Update action runtime to `node20` by
[@&#8203;rhysd](https://redirect.github.com/rhysd) in
[https://github.com/Swatinem/rust-cache/pull/175](https://redirect.github.com/Swatinem/rust-cache/pull/175)
- Only key by `Cargo.toml` and `Cargo.lock` files of workspace members
by [@&#8203;max-heller](https://redirect.github.com/max-heller) in
[https://github.com/Swatinem/rust-cache/pull/180](https://redirect.github.com/Swatinem/rust-cache/pull/180)

##### New Contributors

- [@&#8203;rhysd](https://redirect.github.com/rhysd) made their first
contribution in
[https://github.com/Swatinem/rust-cache/pull/175](https://redirect.github.com/Swatinem/rust-cache/pull/175)
- [@&#8203;max-heller](https://redirect.github.com/max-heller) made
their first contribution in
[https://github.com/Swatinem/rust-cache/pull/180](https://redirect.github.com/Swatinem/rust-cache/pull/180)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.7.1...v2.7.2

###
[`v2.7.1`](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.0...v2.7.1)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.7.0...v2.7.1)

###
[`v2.7.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.7.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.6.2...v2.7.0)

##### What's Changed

- Fix save-if documentation in readme by
[@&#8203;rukai](https://redirect.github.com/rukai) in
[https://github.com/Swatinem/rust-cache/pull/166](https://redirect.github.com/Swatinem/rust-cache/pull/166)
- Support for `trybuild` and similar macro testing tools by
[@&#8203;neysofu](https://redirect.github.com/neysofu) in
[https://github.com/Swatinem/rust-cache/pull/168](https://redirect.github.com/Swatinem/rust-cache/pull/168)

##### New Contributors

- [@&#8203;rukai](https://redirect.github.com/rukai) made their first
contribution in
[https://github.com/Swatinem/rust-cache/pull/166](https://redirect.github.com/Swatinem/rust-cache/pull/166)
- [@&#8203;neysofu](https://redirect.github.com/neysofu) made their
first contribution in
[https://github.com/Swatinem/rust-cache/pull/168](https://redirect.github.com/Swatinem/rust-cache/pull/168)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.6.2...v2.7.0

###
[`v2.6.2`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.6.2)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.6.1...v2.6.2)

##### What's Changed

- dep: Use `smol-toml` instead of `toml` by
[@&#8203;NobodyXu](https://redirect.github.com/NobodyXu) in
[https://github.com/Swatinem/rust-cache/pull/164](https://redirect.github.com/Swatinem/rust-cache/pull/164)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2...v2.6.2

###
[`v2.6.1`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.6.1)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.6.0...v2.6.1)

-   Fix hash contributions of `Cargo.lock`/`Cargo.toml` files.

###
[`v2.6.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.6.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.5.1...v2.6.0)

##### What's Changed

- Add "buildjet" as a second `cache-provider` backend
[@&#8203;joroshiba](https://redirect.github.com/joroshiba) in
[https://github.com/Swatinem/rust-cache/pull/154](https://redirect.github.com/Swatinem/rust-cache/pull/154)
-   Clean up sparse registry index.
-   Do not clean up src of `-sys` crates.
-   Remove `.cargo/credentials.toml` before saving.

##### New Contributors

- [@&#8203;joroshiba](https://redirect.github.com/joroshiba) made their
first contribution in
[https://github.com/Swatinem/rust-cache/pull/154](https://redirect.github.com/Swatinem/rust-cache/pull/154)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.5.1...v2.6.0

###
[`v2.5.1`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.5.1)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.5.0...v2.5.1)

-   Fix hash contribution of `Cargo.lock`.

###
[`v2.5.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.5.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.4.0...v2.5.0)

##### What's Changed

- feat: Rm workspace crates version before caching by
[@&#8203;NobodyXu](https://redirect.github.com/NobodyXu) in
[https://github.com/Swatinem/rust-cache/pull/147](https://redirect.github.com/Swatinem/rust-cache/pull/147)
- feat: Add hash of `.cargo/config.toml` to key by
[@&#8203;NobodyXu](https://redirect.github.com/NobodyXu) in
[https://github.com/Swatinem/rust-cache/pull/149](https://redirect.github.com/Swatinem/rust-cache/pull/149)

##### New Contributors

- [@&#8203;NobodyXu](https://redirect.github.com/NobodyXu) made their
first contribution in
[https://github.com/Swatinem/rust-cache/pull/147](https://redirect.github.com/Swatinem/rust-cache/pull/147)

**Full Changelog**:
https://github.com/Swatinem/rust-cache/compare/v2.4.0...v2.5.0

###
[`v2.4.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.4.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.3.0...v2.4.0)

-   Fix cache key stability.
- Use 8 character hash components to reduce the key length, making it
more readable.

###
[`v2.3.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.3.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.2.1...v2.3.0)

- Add `cache-all-crates` option, which enables caching of crates
installed by workflows.
- Add installed packages to cache key, so changes to workflows that
install rust tools are detected and cached properly.
-   Fix cache restore failures due to upstream bug.
-   Fix `EISDIR` error due to globed directories.
- Update runtime `@actions/cache`, `@actions/io` and dev `typescript`
dependencies.
- Update `npm run prepare` so it creates distribution files with the
right line endings.

###
[`v2.2.1`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.2.1)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.2.0...v2.2.1)

- Update `@actions/cache` dependency to fix usage of `zstd` compression.

###
[`v2.2.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.2.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.1.0...v2.2.0)

- Add new `save-if` option to always restore, but only conditionally
save the cache.

###
[`v2.1.0`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.1.0)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.0.2...v2.1.0)

- Only hash `Cargo.{lock,toml}` files in the configured workspace
directories.

###
[`v2.0.2`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.0.2)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2.0.1...v2.0.2)

-   Avoid calling cargo metadata on pre-cleanup.
-   Added `prefix-key`, `cache-directories` and `cache-targets` options.

###
[`v2.0.1`](https://redirect.github.com/Swatinem/rust-cache/releases/tag/v2.0.1)

[Compare
Source](https://redirect.github.com/Swatinem/rust-cache/compare/v2...v2.0.1)

- Primarily just updating dependencies to fix GitHub deprecation
notices.

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:46:35 +02:00
renovate[bot]
796e7510c4 Update actions/checkout action to v4.2.2 (#17257)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | minor | `v4` -> `v4.2.2` |

---

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

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

###
[`v4.2.2`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v422)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.2.1...v4.2.2)

- `url-helper.ts` now leverages well-known environment variables by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/checkout/pull/1941](https://redirect.github.com/actions/checkout/pull/1941)
- Expand unit test coverage for `isGhes` by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/checkout/pull/1946](https://redirect.github.com/actions/checkout/pull/1946)

###
[`v4.2.1`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v421)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.2.0...v4.2.1)

- Check out other refs/\* by commit if provided, fall back to ref by
[@&#8203;orhantoy](https://redirect.github.com/orhantoy) in
[https://github.com/actions/checkout/pull/1924](https://redirect.github.com/actions/checkout/pull/1924)

###
[`v4.2.0`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v420)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.7...v4.2.0)

- Add Ref and Commit outputs by
[@&#8203;lucacome](https://redirect.github.com/lucacome) in
[https://github.com/actions/checkout/pull/1180](https://redirect.github.com/actions/checkout/pull/1180)
- Dependency updates by
[@&#8203;dependabot-](https://redirect.github.com/dependabot-)
[https://github.com/actions/checkout/pull/1777](https://redirect.github.com/actions/checkout/pull/1777),
[https://github.com/actions/checkout/pull/1872](https://redirect.github.com/actions/checkout/pull/1872)

###
[`v4.1.7`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v417)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.6...v4.1.7)

- Bump the minor-npm-dependencies group across 1 directory with 4
updates by [@&#8203;dependabot](https://redirect.github.com/dependabot)
in
[https://github.com/actions/checkout/pull/1739](https://redirect.github.com/actions/checkout/pull/1739)
- Bump actions/checkout from 3 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/checkout/pull/1697](https://redirect.github.com/actions/checkout/pull/1697)
- Check out other refs/\* by commit by
[@&#8203;orhantoy](https://redirect.github.com/orhantoy) in
[https://github.com/actions/checkout/pull/1774](https://redirect.github.com/actions/checkout/pull/1774)
- Pin actions/checkout's own workflows to a known, good, stable version.
by [@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/checkout/pull/1776](https://redirect.github.com/actions/checkout/pull/1776)

###
[`v4.1.6`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v416)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.5...v4.1.6)

- Check platform to set archive extension appropriately by
[@&#8203;cory-miller](https://redirect.github.com/cory-miller) in
[https://github.com/actions/checkout/pull/1732](https://redirect.github.com/actions/checkout/pull/1732)

###
[`v4.1.5`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v415)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.4...v4.1.5)

- Update NPM dependencies by
[@&#8203;cory-miller](https://redirect.github.com/cory-miller) in
[https://github.com/actions/checkout/pull/1703](https://redirect.github.com/actions/checkout/pull/1703)
- Bump github/codeql-action from 2 to 3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/checkout/pull/1694](https://redirect.github.com/actions/checkout/pull/1694)
- Bump actions/setup-node from 1 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/checkout/pull/1696](https://redirect.github.com/actions/checkout/pull/1696)
- Bump actions/upload-artifact from 2 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/checkout/pull/1695](https://redirect.github.com/actions/checkout/pull/1695)
- README: Suggest `user.email` to be
`41898282+github-actions[bot]@&#8203;users.noreply.github.com` by
[@&#8203;cory-miller](https://redirect.github.com/cory-miller) in
[https://github.com/actions/checkout/pull/1707](https://redirect.github.com/actions/checkout/pull/1707)

###
[`v4.1.4`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v414)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.3...v4.1.4)

- Disable `extensions.worktreeConfig` when disabling `sparse-checkout`
by [@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/checkout/pull/1692](https://redirect.github.com/actions/checkout/pull/1692)
- Add dependabot config by
[@&#8203;cory-miller](https://redirect.github.com/cory-miller) in
[https://github.com/actions/checkout/pull/1688](https://redirect.github.com/actions/checkout/pull/1688)
- Bump the minor-actions-dependencies group with 2 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/checkout/pull/1693](https://redirect.github.com/actions/checkout/pull/1693)
- Bump word-wrap from 1.2.3 to 1.2.5 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/actions/checkout/pull/1643](https://redirect.github.com/actions/checkout/pull/1643)

###
[`v4.1.3`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v413)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.2...v4.1.3)

- Check git version before attempting to disable `sparse-checkout` by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/checkout/pull/1656](https://redirect.github.com/actions/checkout/pull/1656)
- Add SSH user parameter by
[@&#8203;cory-miller](https://redirect.github.com/cory-miller) in
[https://github.com/actions/checkout/pull/1685](https://redirect.github.com/actions/checkout/pull/1685)
- Update `actions/checkout` version in `update-main-version.yml` by
[@&#8203;jww3](https://redirect.github.com/jww3) in
[https://github.com/actions/checkout/pull/1650](https://redirect.github.com/actions/checkout/pull/1650)

###
[`v4.1.2`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v412)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.1...v4.1.2)

- Fix: Disable sparse checkout whenever `sparse-checkout` option is not
present [@&#8203;dscho](https://redirect.github.com/dscho) in
[https://github.com/actions/checkout/pull/1598](https://redirect.github.com/actions/checkout/pull/1598)

###
[`v4.1.1`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v411)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.1.0...v4.1.1)

- Correct link to GitHub Docs by
[@&#8203;peterbe](https://redirect.github.com/peterbe) in
[https://github.com/actions/checkout/pull/1511](https://redirect.github.com/actions/checkout/pull/1511)
- Link to release page from what's new section by
[@&#8203;cory-miller](https://redirect.github.com/cory-miller) in
[https://github.com/actions/checkout/pull/1514](https://redirect.github.com/actions/checkout/pull/1514)

###
[`v4.1.0`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v410)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4.0.0...v4.1.0)

- [Add support for partial checkout
filters](https://redirect.github.com/actions/checkout/pull/1396)

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:45:47 +02:00
renovate[bot]
1f254ab17e Update astral-sh/setup-uv action to v5.4.1 (#17262)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
action | minor | `v5` -> `v5.4.1` |

---

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

---

### Release Notes

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

###
[`v5.4.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.4.1):
🌈 Add support for pep440 version specifiers

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.4.0...v5.4.1)

##### Changes

With this release you can also use [pep440 version
specifiers](https://peps.python.org/pep-0440/#version-specifiers) as
`required-version` in files`uv.toml`, `pyroject.toml` and in the
`version` input:

```yaml
- name: Install a pep440-specifier-satisfying version of uv
  uses: astral-sh/setup-uv@v5
  with:
    version: ">=0.4.25,<0.5"
```

##### 🐛 Bug fixes

- Add support for pep440 version identifiers
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;353](https://redirect.github.com/astral-sh/setup-uv/issues/353))

##### 🧰 Maintenance

- chore: update known checksums for 0.6.10
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;345](https://redirect.github.com/astral-sh/setup-uv/issues/345))

##### 📚 Documentation

- Add pep440 to docs header
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;355](https://redirect.github.com/astral-sh/setup-uv/issues/355))
- Fix glob syntax link
[@&#8203;flying-sheep](https://redirect.github.com/flying-sheep)
([#&#8203;349](https://redirect.github.com/astral-sh/setup-uv/issues/349))
- Add link to supported glob patterns
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;348](https://redirect.github.com/astral-sh/setup-uv/issues/348))

###
[`v5.4.0`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.4.0):
🌈 uv and uvx path as outputs

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.3.1...v5.4.0)

#### Changes

The absolute paths to the uv and uvx binaries can now be accessed via
the outputs `uv-path` and `uvx-path`.

`setup-uv` now also issues a warning if the working directory is empty.
This makes users aware of the common mistake to run `setup-uv` before
`actions/checkout`. You can remove the warning by setting
`ignore-empty-workdir: true`

#### 🚀 Enhancements

- Add uv-path and uvx-path output
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;341](https://redirect.github.com/astral-sh/setup-uv/issues/341))
- Warn when the workdir is empty
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;322](https://redirect.github.com/astral-sh/setup-uv/issues/322))

#### 🧰 Maintenance

- chore: update known checksums for 0.6.9
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;339](https://redirect.github.com/astral-sh/setup-uv/issues/339))
- Merge workflows and add all-tests-passed
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;331](https://redirect.github.com/astral-sh/setup-uv/issues/331))
- chore: update known checksums for 0.6.8
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;332](https://redirect.github.com/astral-sh/setup-uv/issues/332))
- chore: update known checksums for 0.6.7
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;330](https://redirect.github.com/astral-sh/setup-uv/issues/330))
- Set required workflow permissions
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;329](https://redirect.github.com/astral-sh/setup-uv/issues/329))
- Add workflow_dispatch triggers to every workflow
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;326](https://redirect.github.com/astral-sh/setup-uv/issues/326))
- Bump dependencies
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;324](https://redirect.github.com/astral-sh/setup-uv/issues/324))
- Inline action-update-semver
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;323](https://redirect.github.com/astral-sh/setup-uv/issues/323))
- chore: update known checksums for 0.6.6
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;318](https://redirect.github.com/astral-sh/setup-uv/issues/318))
- chore: update known checksums for 0.6.5
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;313](https://redirect.github.com/astral-sh/setup-uv/issues/313))

#### 📚 Documentation

- Fix wrong warning message in FAQ
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;337](https://redirect.github.com/astral-sh/setup-uv/issues/337))
- Warn when the workdir is empty
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;322](https://redirect.github.com/astral-sh/setup-uv/issues/322))
- Remove apk add python3 for musl test
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;319](https://redirect.github.com/astral-sh/setup-uv/issues/319))

#### ⬆️ Dependency updates

- Bump
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) from
4.0.2 to 4.0.3
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;334](https://redirect.github.com/astral-sh/setup-uv/issues/334))

###
[`v5.3.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.3.1):
🌈 - Fix issues with GHES and HTTP proxies

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.3.0...v5.3.1)

##### Changes

This release fixes some issues when this action was used behind a HTTP
proxy or with GHES.
If you have been seeing `ENOTFOUND` or timeout errors, this release
should fix that.

A huge thank you to everyone who helped investigating this and testing
the fixes:

-   [@&#8203;siryessuhr](https://redirect.github.com/siryessuhr)
-   [@&#8203;my1e5](https://redirect.github.com/my1e5)
-   [@&#8203;dennis-m-e](https://redirect.github.com/dennis-m-e)
-   [@&#8203;PaarthShah](https://redirect.github.com/PaarthShah)

##### 🐛 Bug fixes

- Always fall back to anonymous download
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;304](https://redirect.github.com/astral-sh/setup-uv/issues/304))

##### 🧰 Maintenance

- chore: update known checksums for 0.6.3
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;300](https://redirect.github.com/astral-sh/setup-uv/issues/300))

##### 📚 Documentation

- 📚 Document automatically enabled cache on GitHub-hosted runners
[@&#8203;jerr0328](https://redirect.github.com/jerr0328)
([#&#8203;302](https://redirect.github.com/astral-sh/setup-uv/issues/302))

##### ⬆️ Dependency updates

- bump dependencies
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;308](https://redirect.github.com/astral-sh/setup-uv/issues/308))
- Bump peter-evans/create-pull-request from 7.0.6 to 7.0.7
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;299](https://redirect.github.com/astral-sh/setup-uv/issues/299))

###
[`v5.3.0`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.3.0):
🌈 Support MUSL, s390x and powerpc

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.2.2...v5.3.0)

In this release we add support for MUSL based systems.
This is helpful if you are running your workflow inside a docker image
based on [alpine](https://hub.docker.com/\_/alpine).

> \[!TIP]
> Please be aware that you have to make sure a python interpreter is
already present (`apk add python3`), see also
https://docs.astral.sh/uv/concepts/python-versions/#cpython-distributions
and
[https://github.com/astral-sh/uv/issues/6890](https://redirect.github.com/astral-sh/uv/issues/6890)

[@&#8203;Zxilly](https://redirect.github.com/Zxilly) also added support
for running this action on self-hosted runners using s390x and powerpc
architectures. Thank you!

This release also includes more debug logs which makes tracking down
issues easier in the future.

##### 🐛 Bug fixes

- Add more debug logs
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;297](https://redirect.github.com/astral-sh/setup-uv/issues/297))

##### 🚀 Enhancements

- Support OS using musl
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;284](https://redirect.github.com/astral-sh/setup-uv/issues/284))
- feat: support s390x and powerpc
[@&#8203;Zxilly](https://redirect.github.com/Zxilly)
([#&#8203;289](https://redirect.github.com/astral-sh/setup-uv/issues/289))

##### 🧰 Maintenance

- chore: update known checksums for 0.6.2
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;295](https://redirect.github.com/astral-sh/setup-uv/issues/295))
- chore: update known checksums for 0.6.1
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;293](https://redirect.github.com/astral-sh/setup-uv/issues/293))
- chore: update known checksums for 0.6.0
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;288](https://redirect.github.com/astral-sh/setup-uv/issues/288))
- chore: update known checksums for 0.5.31
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;277](https://redirect.github.com/astral-sh/setup-uv/issues/277))
- Run update-known-checksums every night
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;273](https://redirect.github.com/astral-sh/setup-uv/issues/273))
- chore: update known checksums for 0.5.29
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;272](https://redirect.github.com/astral-sh/setup-uv/issues/272))
- chore: update known checksums for 0.5.28
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;270](https://redirect.github.com/astral-sh/setup-uv/issues/270))
- chore: update known checksums for 0.5.27
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;267](https://redirect.github.com/astral-sh/setup-uv/issues/267))
- chore: update known checksums for 0.5.26
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;263](https://redirect.github.com/astral-sh/setup-uv/issues/263))

##### 📚 Documentation

- Add FAQ on resolution strategy and cache not found warnings
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;296](https://redirect.github.com/astral-sh/setup-uv/issues/296))

###
[`v5.2.2`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.2.2):
🌈 Full support for GHES

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.2.1...v5.2.2)

##### Changes

This release fixes some issues that prevented use with GitHub Enterprise
Server instances.

##### 🐛 Bug fixes

- Do not expect GITHUB_TOKEN to be set or valid
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;262](https://redirect.github.com/astral-sh/setup-uv/issues/262))
- Fallback if toml file parsing failed
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;246](https://redirect.github.com/astral-sh/setup-uv/issues/246))

##### 🧰 Maintenance

- chore: update known checksums for 0.5.25
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;259](https://redirect.github.com/astral-sh/setup-uv/issues/259))
- chore: update known checksums for 0.5.24
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;256](https://redirect.github.com/astral-sh/setup-uv/issues/256))
- chore: update known checksums for 0.5.23
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;252](https://redirect.github.com/astral-sh/setup-uv/issues/252))
- chore: update known checksums for 0.5.22
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;250](https://redirect.github.com/astral-sh/setup-uv/issues/250))
- chore: update known checksums for 0.5.21
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;247](https://redirect.github.com/astral-sh/setup-uv/issues/247))

##### 📚 Documentation

- Fix TOC [@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;257](https://redirect.github.com/astral-sh/setup-uv/issues/257))

##### ⬆️ Dependency updates

- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.10 to 22.12.0
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;258](https://redirect.github.com/astral-sh/setup-uv/issues/258))
- Bump release-drafter/release-drafter from 6.0.0 to 6.1.0
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;249](https://redirect.github.com/astral-sh/setup-uv/issues/249))
- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.9 to 22.10.10
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;254](https://redirect.github.com/astral-sh/setup-uv/issues/254))
- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.7 to 22.10.9
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;253](https://redirect.github.com/astral-sh/setup-uv/issues/253))
- Bump
[@&#8203;actions/tool-cache](https://redirect.github.com/actions/tool-cache)
from 2.0.1 to 2.0.2
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;244](https://redirect.github.com/astral-sh/setup-uv/issues/244))
- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.6 to 22.10.7
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;243](https://redirect.github.com/astral-sh/setup-uv/issues/243))

###
[`v5.2.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.2.1):
🌈 Support toml spec 1.0.0

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.2.0...v5.2.1)

v5.2.0 introduced TOML parsing using
[@&#8203;iarna/toml](https://www.npmjs.com/package/@&#8203;iarna/toml)
because we already found out in `astral-sh/ruff-action` that
[toml](https://www.npmjs.com/package/toml) has missing features.

As it turns out
[@&#8203;iarna/toml](https://www.npmjs.com/package/@&#8203;iarna/toml)
also is not fully TOML spec (1.0.0) compliant.

We now use [smol-toml](https://www.npmjs.com/package/smol-toml)

##### 🐛 Bug fixes

- Support toml spec 1.0.0
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;245](https://redirect.github.com/astral-sh/setup-uv/issues/245))

###
[`v5.2.0`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.2.0):
🌈 Detect required-version from config file

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.1.0...v5.2.0)

This release adds support to derive the version of uv to be installed
from `pyproject.toml` and `uv.toml` files.
If no `version` input is defined the default is now to look for a
[required-version](https://docs.astral.sh/uv/reference/settings/#required-version)
in `uv.toml` and then `pyproject.toml` in the repository root. If it
cannot find any it falls back to `latest`.

If your files are at a different place you can use the new inputs
`uv-file` or `pyproject-file`.

##### 🐛 Bug fixes

- Add venv/bin as absolute path to PATH
[@&#8203;op](https://redirect.github.com/op)
([#&#8203;241](https://redirect.github.com/astral-sh/setup-uv/issues/241))
- fix: make sure VIRTUAL_ENV is an absolute path
[@&#8203;samypr100](https://redirect.github.com/samypr100)
([#&#8203;224](https://redirect.github.com/astral-sh/setup-uv/issues/224))

##### 🚀 Enhancements

- Detect required-version from config file
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;233](https://redirect.github.com/astral-sh/setup-uv/issues/233))

##### 🧰 Maintenance

- chore: update known checksums for 0.5.20
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;238](https://redirect.github.com/astral-sh/setup-uv/issues/238))
- chore: update known checksums for 0.5.19
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;237](https://redirect.github.com/astral-sh/setup-uv/issues/237))
- chore: update known checksums for 0.5.18
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;232](https://redirect.github.com/astral-sh/setup-uv/issues/232))
- chore: update known checksums for 0.5.17
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;231](https://redirect.github.com/astral-sh/setup-uv/issues/231))
- chore: update known checksums for 0.5.16
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;228](https://redirect.github.com/astral-sh/setup-uv/issues/228))
- chore: update known checksums for 0.5.15
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;225](https://redirect.github.com/astral-sh/setup-uv/issues/225))
- chore: update known checksums for 0.5.14
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;222](https://redirect.github.com/astral-sh/setup-uv/issues/222))
- chore: update known checksums for 0.5.12
[@&#8203;github-actions](https://redirect.github.com/github-actions)
([#&#8203;214](https://redirect.github.com/astral-sh/setup-uv/issues/214))

##### 📚 Documentation

- docs: bump `astral-sh/setup-uv` to `v5`
[@&#8203;njzjz](https://redirect.github.com/njzjz)
([#&#8203;205](https://redirect.github.com/astral-sh/setup-uv/issues/205))

##### ⬆️ Dependency updates

- Bump [@&#8203;octokit/rest](https://redirect.github.com/octokit/rest)
from 21.0.2 to 21.1.0
[@&#8203;dependabot](https://redirect.github.com/dependabot)
([#&#8203;229](https://redirect.github.com/astral-sh/setup-uv/issues/229))
- Bump typescript from 5.7.2 to 5.7.3
[@&#8203;dependabot](https://redirect.github.com/dependabot)
([#&#8203;230](https://redirect.github.com/astral-sh/setup-uv/issues/230))
- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.5 to 22.10.6
[@&#8203;dependabot](https://redirect.github.com/dependabot)
([#&#8203;236](https://redirect.github.com/astral-sh/setup-uv/issues/236))
- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.3 to 22.10.5
[@&#8203;dependabot](https://redirect.github.com/dependabot)
([#&#8203;223](https://redirect.github.com/astral-sh/setup-uv/issues/223))
- Bump [@&#8203;types/node](https://redirect.github.com/types/node) from
22.10.2 to 22.10.3
[@&#8203;dependabot](https://redirect.github.com/dependabot)
([#&#8203;220](https://redirect.github.com/astral-sh/setup-uv/issues/220))
- Bump peter-evans/create-pull-request from 7.0.5 to 7.0.6
[@&#8203;dependabot](https://redirect.github.com/dependabot)
([#&#8203;218](https://redirect.github.com/astral-sh/setup-uv/issues/218))

###
[`v5.1.0`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.1.0):
🌈 Fewer cache invalidations

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5.0.1...v5.1.0)

##### Changes

This release includes less frequently invalidated caches and a fix for
setting the correct `VIRTUAL_ENV`

##### 🐛 Bug fixes

- Set VIRTUAL_ENV to .venv instead of .venv/bin
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;210](https://redirect.github.com/astral-sh/setup-uv/issues/210))

##### 🚀 Enhancements

- Remove uv version from cache key
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;206](https://redirect.github.com/astral-sh/setup-uv/issues/206))

##### 📚 Documentation

- Align use of `actions/setup-python` with uv docu
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;207](https://redirect.github.com/astral-sh/setup-uv/issues/207))

###
[`v5.0.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v5.0.1):
🌈 The christmas elves overlooked something

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v5...v5.0.1)

##### Changes

With so many breaking changes so close to the end of the year we missed
something.

Thank you [@&#8203;ryanhiebert](https://redirect.github.com/ryanhiebert)
for quickly reporting that our new defaults fail the workflow if neither
a `uv.lock` nor a `requirements*.txt` can be found. This is now a
warning instead.

##### 🐛 Bug fixes

- Fix wrong cacheDependencyPathHash
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;201](https://redirect.github.com/astral-sh/setup-uv/issues/201))
- Warn instead of fail for no-dependency-glob
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;200](https://redirect.github.com/astral-sh/setup-uv/issues/200))

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 08:44:19 +02:00
Micha Reiser
10003bd4e5 Revert "Update Rust crate clap to v4.5.35" (#17265)
Reverts astral-sh/ruff#17245

This broke main. Let's revert and then land this upgrade properly
2025-04-07 08:33:44 +02:00
renovate[bot]
85f8116b2f Update Rust crate smallvec to v1.15.0 (#17254) 2025-04-07 02:06:59 +00:00
renovate[bot]
bc8c6a4810 Update taiki-e/install-action digest to d4635f2 (#17244) 2025-04-06 22:02:47 -04:00
renovate[bot]
22f9f1dd29 Update PyO3/maturin-action action to v1.48.1 (#17252) 2025-04-06 22:02:27 -04:00
renovate[bot]
6f11470298 Update pre-commit dependencies (#17251) 2025-04-06 22:02:07 -04:00
renovate[bot]
a038c2e662 Update dependency pyodide to v0.27.5 (#17250) 2025-04-06 22:01:57 -04:00
renovate[bot]
7dc6583f9f Update Rust crate ordermap to v0.5.7 (#17249) 2025-04-06 22:01:49 -04:00
renovate[bot]
8c51fdfeff Update Rust crate mimalloc to v0.1.45 (#17248) 2025-04-06 22:01:40 -04:00
renovate[bot]
861a9c5d6c Update Rust crate env_logger to v0.11.8 (#17247) 2025-04-06 22:01:32 -04:00
renovate[bot]
396a7162d6 Update Rust crate ctrlc to v3.4.6 (#17246) 2025-04-06 22:01:24 -04:00
renovate[bot]
040f80ab68 Update Rust crate clap to v4.5.35 (#17245) 2025-04-06 22:01:18 -04:00
Micha Reiser
67f8c30b53 [red-knot] Remove global dead_code from red-knot-server (#17229)
## Summary

Use more local `expect(dead_code)` suppressions instead of a global
`allow(dead_code)` in `lib.rs`.

Remove some methods that are either easy to add later, are less likely
to be needed for red knot, or it's unclear if we'd add it the same way
as in ruff.
2025-04-06 22:09:24 +01:00
Micha Reiser
e01d228a71 [playground] Provide fallback monospace-font (#17242)
<img width="1679" alt="Screenshot 2025-04-06 at 22 51 43"
src="https://github.com/user-attachments/assets/ccd9dd1a-67cd-4bc3-a22a-f3c530b8670d"
/>
2025-04-06 21:57:20 +01:00
Micha Reiser
51bdb87fd8 [red-knot] Fix loading color in dark mode (#17237)
## Summary

The loading indicator on dark mode isn't "visible" because it's black on
black. Use white as text color instead.
2025-04-06 18:30:21 +01:00
Alex Waygood
ac5d220d75 [red-knot] Fix python setting in mdtests, and rewrite a site-packages test as an mdtest (#17222)
## Summary

This PR does the following things:
- Fixes the `python` configuration setting for mdtest (added in
https://github.com/astral-sh/ruff/pull/17221) so that it expects a path
pointing to a venv's `sys.prefix` variable rather than the a path
pointing to the venv's `site-packages` subdirectory. This brings the
`python` setting in mdtest in sync with our CLI `--python` flag.
- Tweaks mdtest so that it automatically creates a valid `pyvenv.cfg`
file for you if you don't specify one. This makes it much more ergonomic
to write an mdtest with a custom `python` setting: red-knot will reject
a `python` setting that points to a directory that doesn't have a
`pyvenv.cfg` file in it
- Tweaks mdtest so that it doesn't check a custom `pyvenv.cfg` as Python
source code if you _do_ add a custom `pyvenv.cfg` file for your mock
virtual environment in an mdtest. (You get a lot of diagnostics about
Python syntax errors in the `pyvenv.cfg` file, otherwise!)
- Rewrites the test added in
https://github.com/astral-sh/ruff/pull/17178 as an mdtest, and deletes
the original test that was added in that PR

## Test Plan

I verified that the new mdtest fails if I revert the changes to
`resolver.rs` that were added in
https://github.com/astral-sh/ruff/pull/17178
2025-04-06 18:24:32 +01:00
Matthew Mckee
73a9974d8a Fix CallableTypeOf display signature (#17235) 2025-04-06 18:12:52 +01:00
Micha Reiser
3dd6d0a422 [playground] Add Reset button (#17236)
## Summary

Add a *Reset* button to both Ruff's and Red Knot's playground that
resets the playground to its initial state.

Closes https://github.com/astral-sh/ruff/issues/17195

## Test Plan



https://github.com/user-attachments/assets/753cca19-155a-44b1-89ba-76744487a55d


https://github.com/user-attachments/assets/7d19f04c-70f4-4d9e-b745-0486cb1d4993
2025-04-06 17:09:56 +00:00
Micha Reiser
4571c5f56f [red-knot] Simplify playground editor state (#17223)
## Summary
Reduce the number of `useRef`s in `Editor`
2025-04-05 19:49:08 +02:00
Micha Reiser
1c652e6b98 [red-knot] Allow setting python in mdtests (#17221)
## Summary

This PR extends the mdtest options to allow setting the
`environment.python` option.

## Test Plan

I let @AlexWaygood write a test and he'll tell me if it works 😆
2025-04-05 16:43:31 +02:00
renovate[bot]
172af7b4b0 Update dependency vite to v6.2.5 (#17211)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [vite](https://vite.dev)
([source](https://redirect.github.com/vitejs/vite/tree/HEAD/packages/vite))
| [`6.2.4` ->
`6.2.5`](https://renovatebot.com/diffs/npm/vite/6.2.4/6.2.5) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/vite/6.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vite/6.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vite/6.2.4/6.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite/6.2.4/6.2.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

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

### GitHub Vulnerability Alerts

####
[CVE-2025-31486](https://redirect.github.com/vitejs/vite/security/advisories/GHSA-xcj6-pq6g-qj4x)

### Summary

The contents of arbitrary files can be returned to the browser.

### Impact

Only apps explicitly exposing the Vite dev server to the network (using
--host or [server.host config
option](https://vitejs.dev/config/server-options.html#server-host)) are
affected..

### Details

#### `.svg`

Requests ending with `.svg` are loaded at this line.

037f801075/packages/vite/src/node/plugins/asset.ts (L285-L290)
By adding `?.svg` with `?.wasm?init` or with `sec-fetch-dest: script`
header, the restriction was able to bypass.

This bypass is only possible if the file is smaller than
[`build.assetsInlineLimit`](https://vite.dev/config/build-options.html#build-assetsinlinelimit)
(default: 4kB) and when using Vite 6.0+.

#### relative paths

The check was applied before the id normalization. This allowed requests
to bypass with relative paths (e.g. `../../`).

### PoC

```bash
npm create vite@latest
cd vite-project/
npm install
npm run dev
```

send request to read `etc/passwd`

```bash
curl 'http://127.0.0.1:5173/etc/passwd?.svg?.wasm?init'
```

```bash
curl 'http://127.0.0.1:5173/@&#8203;fs/x/x/x/vite-project/?/../../../../../etc/passwd?import&?raw'
```

---

### Release Notes

<details>
<summary>vitejs/vite (vite)</summary>

###
[`v6.2.5`](https://redirect.github.com/vitejs/vite/releases/tag/v6.2.5)

[Compare
Source](https://redirect.github.com/vitejs/vite/compare/v6.2.4...v6.2.5)

Please refer to
[CHANGELOG.md](https://redirect.github.com/vitejs/vite/blob/v6.2.5/packages/vite/CHANGELOG.md)
for details.

</details>

---

### Configuration

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

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

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

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

---

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

---

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

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMjcuMyIsInVwZGF0ZWRJblZlciI6IjM5LjIyNy4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCIsInNlY3VyaXR5Il19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 08:28:01 +01:00
cake-monotone
7e6d3838bd [red-knot] Add cycle handling to narrow constraints queries (#17209)
## Summary

This PR fixes the cycle issue that was causing problems in the `support
super` PR.

### Affected queries
- `all_narrowing_constraints_for_expression`
- `all_negative_narrowing_constraints_for_expression`


--

Additionally, `bidict` and `werkzeug` have been added to the
project-selection list in `mypy_primer`.
This PR also addresses the panics that occurred while analyzing those
packages:

- `bidict`: panic triggered by
`all_narrowing_constraints_for_expression`
- `werkzeug`: panic triggered by
`all_negative_narrowing_constraints_for_expression`

I think the mypy-primer results for this PR can serve as sufficient test
:)
2025-04-04 22:26:20 -07:00
David Peter
1a6a10b30f [red-knot] Empty tuple is always-falsy (#17213)
## Summary

Fix assignability of `tuple[()]` to `AlwaysFalsy`.

closes #17202 

## Test Plan

Ran the property tests for a while
2025-04-04 22:00:28 +02:00
Brent Westbrook
b3243b5e2a Run fuzzer with --preview (#17210)
Summary
--

Updates `fuzz.py` to run with `--preview`, which should allow it to
catch semantic syntax errors.

Test Plan
--

@AlexWaygood and I temporarily made any named expression a semantic
syntax error and checked that this led to fuzzing errors. We also tested
that reverting the `--preview` addition did not show any errors.

We also ran the fuzzer on 500 seeds on `main` but didn't find any
issues, (un)fortunately.
2025-04-04 15:13:59 -04:00
Brent Westbrook
95d6ed40cc Bump 0.11.4 (#17212) 2025-04-04 14:09:10 -04:00
Brent Westbrook
acc5662e8b [syntax-errors] Allow yield in base classes and annotations (#17206)
Summary
--

This PR fixes the issue pointed out by @JelleZijlstra in
https://github.com/astral-sh/ruff/pull/17101#issuecomment-2777480204.
Namely, I conflated two very different errors from CPython:

```pycon
>>> def m[T](x: (yield from 1)): ...
  File "<python-input-310>", line 1
    def m[T](x: (yield from 1)): ...
                 ^^^^^^^^^^^^
SyntaxError: yield expression cannot be used within the definition of a generic
>>> def m(x: (yield from 1)): ...
  File "<python-input-311>", line 1
    def m(x: (yield from 1)): ...
              ^^^^^^^^^^^^
SyntaxError: 'yield from' outside function
>>> def outer():
...     def m(x: (yield from 1)): ...
...
>>>
```

I thought the second error was the same as the first, but `yield` (and
`yield from`) is actually valid in this position when inside a function
scope. The same is true for base classes, as pointed out in the original
comment.

We don't currently raise an error for `yield` outside of a function, but
that should be handled separately.

On the upside, this had the benefit of removing the
`InvalidExpressionPosition::BaseClass` variant and the
`allow_named_expr` field from the visitor because they were both no
longer used.

Test Plan
--

Updated inline tests.
2025-04-04 13:48:28 -04:00
David Salvisberg
33a56f198b Don't skip visiting non-tuple slice in typing.Annotated subscripts (#17201)
Fixes: #17196

## Summary

Skipping these nodes for malformed type expressions would lead to
incorrect semantic state, which can in turn mean we emit false positives
for rules like `unused-variable`(`F841`)

## Test Plan

`cargo nextest run`
2025-04-04 18:47:40 +05:30
David Peter
5cee346744 [red-knot] mypy_primer: do not specify Python version (#17200)
## Summary

* Pull in latest changes from upstream (which includes an update to a
`pyproject.toml` layout and a `uv.lock` file, which should make CI more
deterministic)
* Do not specify `--python-version 3.13` in Red Knot runs


https://github.com/astral-sh/mypy_primer/compare/add-red-knot-support-v2...astral-sh:mypy_primer:add-red-knot-support-v3

## Test Plan

Tested locally
2025-04-04 13:55:40 +02:00
Micha Reiser
ffa824e037 [red-knot] Add Type.definition method (#17153)
## Summary

This is a follow up to the goto type definition PR. Specifically, that
we want to avoid exposing too many semantic model internals publicly.

I want to get some feedback on the approach taken. I think it goes into
the right direction but I'm not super happy with it.
The basic idea is that we add a `Type::definition` method which does the
"goto type definition". The parts that I think make it awkward:

* We can't directly return `Definition` because we don't create a
`Definition` for modules (but we could?). Although I think it makes
sense to possibly have a more public wrapper type anyway?
* It doesn't handle unions and intersections. Mainly because not all
elements in an intersection may have a definition and we only want to
show a navigation target for intersections if there's only a single
positive element (besides maybe `Unknown`).


An alternative design or an addition to this design is to introduce a
`SemanticAnalysis(Db)` struct that has methods like
`type_definition(&self, type)` which explicitly exposes the methods we
want. I don't feel comfortable design this API yet because it's unclear
how fine granular it has to be (and if it is very fine granular,
directly using `Type` might be better after all)


## Test Plan

`cargo test`
2025-04-04 10:56:20 +02:00
Max Mynter
98b95c9c38 Implement Invalid rule provided as rule RUF102 with --fix (#17138)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

Closes #17084

## Summary
This PR adds a new rule (RUF102) to detect and fix invalid rule codes in
`noqa` comments.
Invalid rule codes in `noqa` directives serve no purpose and may
indicate outdated code suppressions.

This extends the previous behaviour originating from
`crates/ruff_linter/src/noqa.rs` which would only emit a warnigs.
With this rule a `--fix` is available.

The rule:
1. Analyzes all `noqa` directives to identify invalid rule codes
2. Provides autofix functionality to:
   - Remove the entire comment if all codes are invalid
   - Remove only the invalid codes when mixed with valid codes
3. Preserves original comment formatting and whitespace where possible

Example cases:
- `# noqa: XYZ111` → Remove entire comment (keep empty line)
- `# noqa: XYZ222, XYZ333` → Remove entire comment (keep empty line)
-  `# noqa: F401, INVALID123` → Keep only valid codes (`# noqa: F401`)

## Test Plan
- Added tests in
`crates/ruff_linter/resources/test/fixtures/ruff/RUF102.py` covering
different example cases.

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

## Notes 
- This does not handle cases where parsing fails. E.g. `# noqa:
NON_EXISTENT, ANOTHER_INVALID` causes a `LexicalError` and the
diagnostic is not propagated and we cannot handle the diagnostic. I am
also unsure what proper `fix` handling would be and making the user
aware we don't understand the codes is probably the best bet.
- The rule is added to the Preview rule group as it's a new addition

## Questions
- Should we remove the warnings, now that we have a rule?
- Is the current fix behavior appropriate for all cases, particularly
the handling of whitespace and line deletions?
- I'm new to the codebase; let me know if there are rule utilities which
could have used but didn't.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-04 08:05:59 +00:00
Micha Reiser
a4ba10ff0a [red-knot] Add basic on-hover to playground and LSP (#17057)
## Summary

Implement a very basic hover in the playground and LSP.

It's basic, because it only shows the type on-hover. Most other LSPs
also show:

* The signature of the symbol beneath the cursor. E.g. `class
Test(a:int, b:int)` (we want something like
54f7da25f9/packages/pyright-internal/src/analyzer/typeEvaluator.ts (L21929-L22129))
* The symbols' documentation
* Do more fancy markdown rendering

I decided to defer these features for now because it requires new
semantic APIs (similar to *goto definition*), and investing in fancy
rendering only makes sense once we have the relevant data.

Closes [#16826](https://github.com/astral-sh/ruff/issues/16826)

## Test Plan



https://github.com/user-attachments/assets/044aeee4-58ad-4d4e-9e26-ac2a712026be


https://github.com/user-attachments/assets/4a1f4004-2982-4cf2-9dfd-cb8b84ff2ecb
2025-04-04 08:13:43 +02:00
Carl Meyer
bf0306887a [red-knot] don't remove negations when simplifying constrained typevars (#17189)
For two non-disjoint types `P` and `Q`, the simplification of `(P | Q) &
~Q` is not `P`, but `P & ~Q`. In other words, the non-empty set `P & Q`
is also excluded from the type.

The same applies for a constrained typevar `[T: (P, Q)]`: `T & ~Q`
should simplify to `P & ~Q`, not just `P`.

Implementing this is actually purely a matter of removing code from the
constrained typevar simplification logic; we just need to not bother
removing the negations. If the negations are actually redundant (because
the constraint types are disjoint), normal intersection simplification
will already eliminate them (as shown in the added test.)
2025-04-03 16:30:57 -07:00
Brent Westbrook
4f924bb975 [minor] Fix extra semicolon for clippy (#17188) 2025-04-03 18:17:00 -04:00
Brent Westbrook
c2b2e42ad3 [syntax-errors] Invalid syntax in annotations (#17101)
Summary
--

This PR detects the use of invalid syntax in annotation scopes,
including
`yield` and `yield from` expressions and named expressions. I combined a
few
different types of CPython errors here, but I think the resulting error
messages
still make sense and are even preferable to what CPython gives. For
example, we
report `yield expression cannot be used in a type annotation` for both
of these:

```pycon
>>> def f[T](x: (yield 1)): ...
  File "<python-input-26>", line 1
    def f[T](x: (yield 1)): ...
                 ^^^^^^^
SyntaxError: yield expression cannot be used within the definition of a generic
>>> def foo() -> (yield x): ...
  File "<python-input-28>", line 1
    def foo() -> (yield x): ...
                  ^^^^^^^
SyntaxError: 'yield' outside function
```

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

Test Plan
--

New inline tests, along with some updates to existing tests.
2025-04-03 17:56:55 -04:00
Brent Westbrook
24b1b1d52c [syntax-errors] Duplicate attributes in match class pattern (#17186)
Summary
--

Detects duplicate attributes in a `match` class pattern:

```python
match x:
    case Class(x=1, x=2): ...
```

which are more analogous to the similar check for mapping patterns than
to the
multiple assignments rule.

I also realized that both this and the mapping check would only work on
top-level patterns, despite the possibility that they can be nested
inside other
patterns:

```python
match x:
    case [{"x": 1, "x": 2}]: ...  # false negative in the old version
```

and moved these checks into the recursive pattern visitor instead.

I also tidied up some of the names like the `multiple_case_assignment`
function
and the `MultipleCaseAssignmentVisitor`, which are now doing more than
checking
for multiple assignments.

Test Plan
--

New inline tests for both classes and mappings.
2025-04-03 17:55:37 -04:00
Brent Westbrook
6a07dd227d [syntax-errors] Fix multiple assignment for class keyword argument (#17184)
Summary
--

Fixes #17181. The cases being tested with multiple *keys* being equal
are actually a slightly different error, more like the error for
`MatchMapping` than like the other multiple assignment errors:

```pycon
>>> match x:
...     case Class(x=x, x=x): ...
...
  File "<python-input-249>", line 2
    case Class(x=x, x=x): ...
                      ^
SyntaxError: attribute name repeated in class pattern: x
>>> match x:
...     case {"x": 1, "x": 2}: ...
...
  File "<python-input-251>", line 2
    case {"x": 1, "x": 2}: ...
         ^^^^^^^^^^^^^^^^
SyntaxError: mapping pattern checks duplicate key ('x')
>>> match x:
...     case [x, x]: ...
...
  File "<python-input-252>", line 2
    case [x, x]: ...
             ^
SyntaxError: multiple assignments to name 'x' in pattern
```

This PR just stops the false positive reported in the issue, but I will
quickly follow it up with a new rule (or possibly combined with the
mapping rule) catching the repeated attributes separately.

Test Plan
--

New inline `test_ok` and updating the `test_err` cases to have duplicate
values instead of keys.
2025-04-03 17:32:39 -04:00
Aria Desires
8aa5686e63 use astral-sh/cargo-dist instead (#17187) 2025-04-03 17:25:12 -04:00
Micha Reiser
e7684d3493 Enable overindented docs lint (#17182)
## Summary

It turns out that `a.` isn't a list format supported by rustdoc. I
changed the documentation to use `1.`, `2.` instead.

## Test Plan

`cargo clippy`
2025-04-03 20:27:11 +01:00
Douglas Creager
64e7e1aa64 [red-knot] Add Type::TypeVar variant (#17102)
This adds a new `Type` variant for holding an instance of a typevar
inside of a generic function or class. We don't handle specializing the
typevars yet, but this should implement most of the typing rules for
inside the generic function/class, where we don't know yet which
specific type the typevar will be specialized to.

This PR does _not_ yet handle the constraint that multiple occurrences
of the typevar must be specialized to the _same_ time. (There is an
existing test case for this in `generics/functions.md` which is still
marked as TODO.)

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-04-03 14:36:29 -04:00
Carl Meyer
45c43735e0 [red-knot] update to latest Salsa with fixpoint caching fix (#17179)
With this PR, we no longer "hang" (not actually a hang, just an
explosion in execution time) when checking pylint.
2025-04-03 09:05:09 -07:00
Micha Reiser
8a4158c5f8 Upgrade to Rust 1.86 and bump MSRV to 1.84 (#17171)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

I decided to disable the new
[`needless_continue`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue)
rule because I often found the explicit `continue` more readable over an
empty block or having to invert the condition of an other branch.


## Test Plan

`cargo test`

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-04-03 15:59:44 +00:00
David Peter
fedd982fd5 [red-knot] Avoid unresolved-reference in unreachable code (#17169)
## Summary

This PR changes the inferred type for symbols in unreachable sections of
code to `Never` (instead of reporting them as unbound), in order to
silence false positive diagnostics. See the lengthy comment in the code
for further details.

## Test Plan

- Updated Markdown tests.
- Manually verified a couple of ecosystem diagnostic changes.
2025-04-03 16:52:11 +02:00
Alex Waygood
a1eb834a5f Fix relative import resolution in site-packages packages when the site-packages search path is a subdirectory of the first-party search path (#17178)
## Summary

If a package in `site-packages` had this directory structure:

```py
# bar/__init__.py
from .a import A

# bar/a.py
class A: ...
```

then we would fail to resolve the `from .a import A` import _if_ (as is
usually the case!) the `site-packages` search path was located inside a
`.venv` directory that was a subdirectory of the project's first-party
search path. The reason for this is a bug in `file_to_module` in the
module resolver. In this loop, we would identify that
`/project_root/.venv/lib/python3.13/site-packages/foo/__init__.py` can
be turned into a path relative to the first-party search path
(`/project_root`):


6e2b8f9696/crates/red_knot_python_semantic/src/module_resolver/resolver.rs (L101-L110)

but we'd then try to turn the relative path
(.venv/lib/python3.13/site-packages/foo/__init__.py`) into a module
path, realise that it wasn't a valid module path... and therefore
immediately `break` out of the loop before trying any other search paths
(such as the `site-packages` search path).

This bug was originally reported on Discord by @MatthewMckee4.

## Test Plan

I added a unit test for `file_to_module` in `resolver.rs`, and an
integration test that shows we can now resolve the import correctly in
`infer.rs`.
2025-04-03 15:48:05 +01:00
Carl Meyer
c1f93a702c [DO NOT LAND] bump Salsa version (#17176)
Update to latest Salsa main branch, so as to get a baseline for
measuring the perf effect of https://github.com/salsa-rs/salsa/pull/786
on red-knot in isolation from other recent changes in Salsa main branch.
2025-04-03 14:31:41 +00:00
Brent Westbrook
6e2b8f9696 [syntax-errors] Detect duplicate keys in match mapping patterns (#17129)
Summary
--

Detects duplicate literals in `match` mapping keys.

This PR also adds a `source` method to `SemanticSyntaxContext` to
display the duplicated key in the error message by slicing out its
range.

Test Plan
--

New inline tests.
2025-04-03 10:22:37 -04:00
Alex Waygood
ca0cce3f9c [red-knot] Fix more [redundant-cast] false positives (#17170)
Fixes #17164. Simply checking whether one type is gradually equivalent
to another is too simplistic here: `Any` is gradually equivalent to
`Todo`, but we should permit users to cast from `Todo` or `Unknown` to
`Any` without complaining about it. This changes our logic so that we
only complain about redundant casts if:
- the two types are exactly equal (when normalized) OR they are
equivalent (we'll still complain about `Any -> Any` casts, and about
`Any | str | int` -> `str | int | Any` casts, since their normalized
forms are exactly equal, even though the type is not fully static -- and
therefore does not participate in equivalence relations)
- AND the casted type does not contain `Todo`
2025-04-03 15:00:00 +01:00
David Peter
3f00010a7a [red-knot] Three-argument type-calls take 'str' as the first argument (#17168)
## Summary

Similar to #17163, a minor fix in the signature of `type(…)`.

## Test Plan

New MD tests
2025-04-03 15:45:08 +02:00
Dylan
d401a5440e Control flow: return and raise (#17121)
We add support for `return` and `raise` statements in the control flow
graph: we simply add an edge to the terminal block, push the statements
to the current block, and proceed.

This implementation will have to be modified somewhat once we add
support for `try` statements - then we will need to check whether to
_defer_ the jump. But for now this will do!

Also in this PR: We fix the `unreachable` diagnostic range so that it
lumps together consecutive unreachable blocks.
2025-04-03 08:30:29 -05:00
471 changed files with 25631 additions and 6516 deletions

View File

@@ -39,17 +39,17 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build sdist"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
command: sdist
args: --out dist
@@ -59,7 +59,7 @@ jobs:
"${MODULE_NAME}" --help
python -m "${MODULE_NAME}" --help
- name: "Upload sdist"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-sdist
path: dist
@@ -68,23 +68,23 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: macos-14
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels - x86_64"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: x86_64
args: --release --locked --out dist
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-macos-x86_64
path: dist
@@ -99,7 +99,7 @@ jobs:
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-macos-x86_64
path: |
@@ -110,18 +110,18 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: macos-14
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: arm64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels - aarch64"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: aarch64
args: --release --locked --out dist
@@ -131,7 +131,7 @@ jobs:
ruff --help
python -m ruff --help
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-aarch64-apple-darwin
path: dist
@@ -146,7 +146,7 @@ jobs:
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-aarch64-apple-darwin
path: |
@@ -166,18 +166,18 @@ jobs:
- target: aarch64-pc-windows-msvc
arch: x64
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.platform.arch }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: ${{ matrix.platform.target }}
args: --release --locked --out dist
@@ -192,7 +192,7 @@ jobs:
"${MODULE_NAME}" --help
python -m "${MODULE_NAME}" --help
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-${{ matrix.platform.target }}
path: dist
@@ -203,7 +203,7 @@ jobs:
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-${{ matrix.platform.target }}
path: |
@@ -219,18 +219,18 @@ jobs:
- x86_64-unknown-linux-gnu
- i686-unknown-linux-gnu
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: ${{ matrix.target }}
manylinux: auto
@@ -242,7 +242,7 @@ jobs:
"${MODULE_NAME}" --help
python -m "${MODULE_NAME}" --help
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-${{ matrix.target }}
path: dist
@@ -260,7 +260,7 @@ jobs:
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-${{ matrix.target }}
path: |
@@ -294,17 +294,17 @@ jobs:
arch: arm
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: ${{ matrix.platform.target }}
manylinux: auto
@@ -325,7 +325,7 @@ jobs:
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
ruff --help
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-${{ matrix.platform.target }}
path: dist
@@ -343,7 +343,7 @@ jobs:
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-${{ matrix.platform.target }}
path: |
@@ -359,18 +359,18 @@ jobs:
- x86_64-unknown-linux-musl
- i686-unknown-linux-musl
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
@@ -387,7 +387,7 @@ jobs:
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
.venv/bin/${{ env.MODULE_NAME }} --help
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-${{ matrix.target }}
path: dist
@@ -405,7 +405,7 @@ jobs:
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-${{ matrix.target }}
path: |
@@ -425,17 +425,17 @@ jobs:
arch: armv7
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
@@ -454,7 +454,7 @@ jobs:
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
.venv/bin/${{ env.MODULE_NAME }} --help
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wheels-${{ matrix.platform.target }}
path: dist
@@ -472,7 +472,7 @@ jobs:
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
- name: "Upload binary"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: artifacts-${{ matrix.platform.target }}
path: |

View File

@@ -33,7 +33,7 @@ jobs:
- linux/amd64
- linux/arm64
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
@@ -96,7 +96,7 @@ jobs:
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digests
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: digests-${{ env.PLATFORM_TUPLE }}
path: /tmp/digests/*
@@ -113,7 +113,7 @@ jobs:
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
- name: Download digests
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
path: /tmp/digests
pattern: digests-*
@@ -256,7 +256,7 @@ jobs:
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
- name: Download digests
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
path: /tmp/digests
pattern: digests-*

View File

@@ -42,7 +42,7 @@ jobs:
# Flag that is set to "true" when code related to the playground changes.
playground: ${{ steps.check_playground.outputs.changed }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
persist-credentials: false
@@ -143,7 +143,7 @@ jobs:
env:
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
run: |
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':**/*' \
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':**' \
':!**/*.md' \
':crates/red_knot_python_semantic/resources/mdtest/**/*.md' \
':!docs/**' \
@@ -196,7 +196,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Install Rust toolchain"
@@ -210,10 +210,10 @@ jobs:
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: |
rustup component add clippy
@@ -230,20 +230,20 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-insta
- name: Red-knot mdtests (GitHub annotations)
@@ -272,7 +272,7 @@ jobs:
env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ruff
path: target/debug/ruff
@@ -284,20 +284,20 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-insta
- name: "Run tests"
@@ -313,14 +313,14 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo nextest"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-nextest
- name: "Run tests"
@@ -340,13 +340,13 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: 20
cache: "npm"
@@ -369,10 +369,10 @@ jobs:
if: ${{ github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
@@ -387,7 +387,7 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: SebRollen/toml-action@b1b3628f55fc3a28208d4203ada8b737e9687876 # v1.2.0
@@ -395,7 +395,7 @@ jobs:
with:
file: "Cargo.toml"
field: "workspace.package.rust-version"
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
env:
MSRV: ${{ steps.msrv.outputs.value }}
@@ -403,11 +403,11 @@ jobs:
- name: "Install mold"
uses: rui314/setup-mold@v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-insta
- name: "Run tests"
@@ -424,10 +424,10 @@ jobs:
if: ${{ github.ref == 'refs/heads/main' || needs.determine_changes.outputs.fuzz == 'true' || needs.determine_changes.outputs.code == 'true' }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
with:
workspaces: "fuzz -> target"
- name: "Install Rust toolchain"
@@ -452,11 +452,11 @@ jobs:
env:
FORCE_COLOR: 1
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
name: Download Ruff binary to test
id: download-cached-binary
with:
@@ -486,10 +486,10 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 5
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup component add rustfmt
# Run all code generation scripts, and verify that the current output is
@@ -518,14 +518,14 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && needs.determine_changes.outputs.code == 'true' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
name: Download comparison Ruff binary
id: ruff-target
with:
@@ -620,13 +620,13 @@ jobs:
run: |
echo ${{ github.event.number }} > pr-number
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
name: Upload PR Number
with:
name: pr-number
path: pr-number
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
name: Upload Results
with:
name: ecosystem-result
@@ -638,7 +638,7 @@ jobs:
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@main
@@ -651,18 +651,18 @@ jobs:
timeout-minutes: 20
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Prep README.md"
run: python scripts/transform_readme.py --target pypi
- name: "Build wheels"
uses: PyO3/maturin-action@22fe573c6ed0c03ab9b84e631cbfa49bddf6e20e # v1.47.3
uses: PyO3/maturin-action@44479ae1b6b1a57f561e03add8832e62c185eb17 # v1.48.1
with:
args: --out dist
- name: "Test wheel"
@@ -678,12 +678,12 @@ jobs:
runs-on: depot-ubuntu-22.04-16
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
- name: "Cache pre-commit"
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
@@ -705,13 +705,13 @@ jobs:
env:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: "3.13"
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
@@ -720,7 +720,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: uv pip install -r docs/requirements-insiders.txt --system
@@ -747,10 +747,10 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.formatter == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup show
- name: "Run checks"
@@ -773,18 +773,18 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
name: "Download ruff-lsp source"
with:
persist-credentials: false
repository: "astral-sh/ruff-lsp"
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
# installation fails on 3.13 and newer
python-version: "3.12"
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
name: Download development ruff binary
id: ruff-target
with:
@@ -815,13 +815,13 @@ jobs:
- determine_changes
if: ${{ (needs.determine_changes.outputs.playground == 'true') }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: 22
cache: "npm"
@@ -847,17 +847,17 @@ jobs:
timeout-minutes: 20
steps:
- name: "Checkout Branch"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Rust toolchain"
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@6aca1cfa12ef3a6b98ee8c70e0171bfa067604f5 # v2
uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc # v2
with:
tool: cargo-codspeed

View File

@@ -31,15 +31,15 @@ jobs:
# Don't run the cron job on forks:
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@v1
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- 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

@@ -30,14 +30,14 @@ jobs:
# Don't run the cron job on forks:
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@v1
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: Build Red Knot
# A release build takes longer (2 min vs 1 min), but the property tests run much faster in release
# mode (1.5 min vs 14 min), so the overall time is shorter with a release build.

View File

@@ -28,16 +28,16 @@ jobs:
runs-on: depot-ubuntu-22.04-16
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
with:
workspaces: "ruff"
- name: Install Rust toolchain
@@ -45,7 +45,7 @@ jobs:
- name: Install mypy_primer
run: |
uv tool install "git+https://github.com/astral-sh/mypy_primer.git@add-red-knot-support-v2"
uv tool install "git+https://github.com/astral-sh/mypy_primer.git@add-red-knot-support-v4"
- name: Run mypy_primer
shell: bash
@@ -68,7 +68,7 @@ jobs:
--type-checker knot \
--old base_commit \
--new "$GITHUB_SHA" \
--project-selector '/(mypy_primer|black|pyp|git-revise|zipp|arrow|isort|itsdangerous|rich|packaging|pybind11|pyinstrument|typeshed-stats|scrapy)$' \
--project-selector '/(mypy_primer|black|pyp|git-revise|zipp|arrow|isort|itsdangerous|rich|packaging|pybind11|pyinstrument|typeshed-stats|scrapy|werkzeug|bidict|async-utils)$' \
--output concise \
--debug > mypy_primer.diff || [ $? -eq 1 ]
@@ -81,13 +81,13 @@ jobs:
echo ${{ github.event.number }} > pr-number
- name: Upload diff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: mypy_primer_diff
path: mypy_primer.diff
- name: Upload pr-number
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pr-number
path: pr-number

View File

@@ -23,12 +23,12 @@ jobs:
env:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ inputs.ref }}
persist-credentials: true
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
with:
python-version: 3.12
@@ -68,7 +68,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}

View File

@@ -30,12 +30,12 @@ jobs:
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: 22
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0

View File

@@ -24,12 +24,12 @@ jobs:
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: 22
cache: "npm"

View File

@@ -22,8 +22,8 @@ jobs:
id-token: write
steps:
- name: "Install uv"
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
pattern: wheels-*
path: wheels

View File

@@ -29,7 +29,7 @@ jobs:
target: [web, bundler, nodejs]
fail-fast: false
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Install Rust toolchain"
@@ -45,7 +45,7 @@ jobs:
jq '.name="@astral-sh/ruff-wasm-${{ matrix.target }}"' crates/ruff_wasm/pkg/package.json > /tmp/package.json
mv /tmp/package.json crates/ruff_wasm/pkg
- run: cp LICENSE crates/ruff_wasm/pkg # wasm-pack does not put the LICENSE file in the pkg
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: 20
registry-url: "https://registry.npmjs.org"

View File

@@ -1,6 +1,7 @@
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
# This file was autogenerated by dist: https://github.com/astral-sh/cargo-dist
#
# Copyright 2022-2024, axodotdev
# Copyright 2025 Astral Software Inc.
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
@@ -59,16 +60,17 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive
- name: Install dist
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.25.2-prerelease.3/cargo-dist-installer.sh | sh"
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.4-prerelease.1/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
@@ -84,7 +86,7 @@ jobs:
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
@@ -121,18 +123,19 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
with:
pattern: artifacts-*
path: target/distrib/
@@ -150,7 +153,7 @@ jobs:
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: artifacts-build-global
path: |
@@ -171,18 +174,19 @@ jobs:
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
with:
pattern: artifacts-*
path: target/distrib/
@@ -196,7 +200,7 @@ jobs:
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
@@ -246,12 +250,13 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
with:
pattern: artifacts-*
path: artifacts

View File

@@ -21,12 +21,12 @@ jobs:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
name: Checkout Ruff
with:
path: ruff
persist-credentials: true
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
name: Checkout typeshed
with:
repository: python/typeshed

View File

@@ -18,6 +18,11 @@ exclude: |
)$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-merge-conflict
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.24.1
hooks:
@@ -60,7 +65,7 @@ repos:
- black==25.1.0
- repo: https://github.com/crate-ci/typos
rev: v1.31.0
rev: v1.31.1
hooks:
- id: typos
@@ -74,7 +79,7 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.2
rev: v0.11.4
hooks:
- id: ruff-format
- id: ruff

View File

@@ -1,5 +1,44 @@
# Changelog
## 0.11.5
### Preview features
- \[`airflow`\] Add missing `AIR302` attribute check ([#17115](https://github.com/astral-sh/ruff/pull/17115))
- \[`airflow`\] Expand module path check to individual symbols (`AIR302`) ([#17278](https://github.com/astral-sh/ruff/pull/17278))
- \[`airflow`\] Extract `AIR312` from `AIR302` rules (`AIR302`, `AIR312`) ([#17152](https://github.com/astral-sh/ruff/pull/17152))
- \[`airflow`\] Update oudated `AIR301`, `AIR302` rules ([#17123](https://github.com/astral-sh/ruff/pull/17123))
- [syntax-errors] Async comprehension in sync comprehension ([#17177](https://github.com/astral-sh/ruff/pull/17177))
- [syntax-errors] Check annotations in annotated assignments ([#17283](https://github.com/astral-sh/ruff/pull/17283))
- [syntax-errors] Extend annotation checks to `await` ([#17282](https://github.com/astral-sh/ruff/pull/17282))
### Bug fixes
- \[`flake8-pie`\] Avoid false positive for multiple assignment with `auto()` (`PIE796`) ([#17274](https://github.com/astral-sh/ruff/pull/17274))
### Rule changes
- \[`ruff`\] Fix `RUF100` to detect unused file-level `noqa` directives with specific codes (#17042) ([#17061](https://github.com/astral-sh/ruff/pull/17061))
- \[`flake8-pytest-style`\] Avoid false positive for legacy form of `pytest.raises` (`PT011`) ([#17231](https://github.com/astral-sh/ruff/pull/17231))
### Documentation
- Fix formatting of "See Style Guide" link ([#17272](https://github.com/astral-sh/ruff/pull/17272))
## 0.11.4
### Preview features
- \[`ruff`\] Implement `invalid-rule-code` as `RUF102` ([#17138](https://github.com/astral-sh/ruff/pull/17138))
- [syntax-errors] Detect duplicate keys in `match` mapping patterns ([#17129](https://github.com/astral-sh/ruff/pull/17129))
- [syntax-errors] Detect duplicate attributes in `match` class patterns ([#17186](https://github.com/astral-sh/ruff/pull/17186))
- [syntax-errors] Detect invalid syntax in annotations ([#17101](https://github.com/astral-sh/ruff/pull/17101))
### Bug fixes
- [syntax-errors] Fix multiple assignment error for class fields in `match` patterns ([#17184](https://github.com/astral-sh/ruff/pull/17184))
- Don't skip visiting non-tuple slice in `typing.Annotated` subscripts ([#17201](https://github.com/astral-sh/ruff/pull/17201))
## 0.11.3
### Preview features

75
Cargo.lock generated
View File

@@ -207,9 +207,9 @@ dependencies = [
[[package]]
name = "boxcar"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225450ee9328e1e828319b48a89726cffc1b0ad26fd9211ad435de9fa376acae"
checksum = "6740c6e2fc6360fa57c35214c7493826aee95993926092606f27c983b40837be"
dependencies = [
"loom",
]
@@ -334,9 +334,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.34"
version = "4.5.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff"
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
dependencies = [
"clap_builder",
"clap_derive",
@@ -344,9 +344,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.34"
version = "4.5.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489"
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
dependencies = [
"anstream",
"anstyle",
@@ -695,9 +695,9 @@ dependencies = [
[[package]]
name = "ctrlc"
version = "3.4.5"
version = "3.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c"
dependencies = [
"nix",
"windows-sys 0.59.0",
@@ -894,9 +894,9 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]]
name = "env_logger"
version = "0.11.7"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697"
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
dependencies = [
"anstream",
"anstyle",
@@ -918,7 +918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1390,9 +1390,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@@ -1499,7 +1499,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi 0.5.0",
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1659,9 +1659,9 @@ dependencies = [
[[package]]
name = "libmimalloc-sys"
version = "0.1.40"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07d0e07885d6a754b9c7993f2625187ad694ee985d60f23355ff0e7077261502"
checksum = "6b20daca3a4ac14dbdc753c5e90fc7b490a48a9131daed3c9a9ced7b2defd37b"
dependencies = [
"cc",
"libc",
@@ -1797,9 +1797,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mimalloc"
version = "0.1.44"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99585191385958383e13f6b822e6b6d8d9cf928e7d286ceb092da92b43c87bc1"
checksum = "03cb1f88093fe50061ca1195d336ffec131347c7b833db31f9ab62a2d1b7925f"
dependencies = [
"libmimalloc-sys",
]
@@ -1973,9 +1973,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordermap"
version = "0.5.6"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e98f974336ceffd5b1b1f4fcbb89a23c8dcd334adc4b8612f11b7fa99944535"
checksum = "7d31b8b7a99f71bdff4235faf9ce9eada0ad3562c8fbeb7d607d9f41a6ec569d"
dependencies = [
"indexmap",
]
@@ -2514,6 +2514,7 @@ dependencies = [
"ruff_python_ast",
"ruff_python_parser",
"ruff_text_size",
"rustc-hash 2.1.1",
"salsa",
"smallvec",
"tracing",
@@ -2537,6 +2538,7 @@ dependencies = [
"ruff_db",
"ruff_macros",
"ruff_python_ast",
"ruff_python_formatter",
"ruff_text_size",
"rustc-hash 2.1.1",
"salsa",
@@ -2607,7 +2609,6 @@ dependencies = [
"red_knot_python_semantic",
"ruff_db",
"ruff_notebook",
"ruff_python_ast",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.1",
@@ -2670,6 +2671,7 @@ dependencies = [
"red_knot_python_semantic",
"ruff_db",
"ruff_notebook",
"ruff_python_formatter",
"ruff_source_file",
"ruff_text_size",
"serde-wasm-bindgen",
@@ -2754,7 +2756,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.11.3"
version = "0.11.5"
dependencies = [
"anyhow",
"argfile",
@@ -2989,7 +2991,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.11.3"
version = "0.11.5"
dependencies = [
"aho-corasick",
"anyhow",
@@ -3133,6 +3135,7 @@ dependencies = [
"memchr",
"regex",
"ruff_cache",
"ruff_db",
"ruff_formatter",
"ruff_macros",
"ruff_python_ast",
@@ -3141,6 +3144,7 @@ dependencies = [
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.1",
"salsa",
"schemars",
"serde",
"serde_json",
@@ -3313,7 +3317,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.11.3"
version = "0.11.5"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -3407,7 +3411,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -3420,7 +3424,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.9.3",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -3438,7 +3442,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
version = "0.19.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=d758691ba17ee1a60c5356ea90888d529e1782ad#d758691ba17ee1a60c5356ea90888d529e1782ad"
source = "git+https://github.com/salsa-rs/salsa.git?rev=87bf6b6c2d5f6479741271da73bd9d30c2580c26#87bf6b6c2d5f6479741271da73bd9d30c2580c26"
dependencies = [
"boxcar",
"compact_str 0.8.1",
@@ -3454,18 +3458,19 @@ dependencies = [
"salsa-macro-rules",
"salsa-macros",
"smallvec",
"thin-vec",
"tracing",
]
[[package]]
name = "salsa-macro-rules"
version = "0.19.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=d758691ba17ee1a60c5356ea90888d529e1782ad#d758691ba17ee1a60c5356ea90888d529e1782ad"
source = "git+https://github.com/salsa-rs/salsa.git?rev=87bf6b6c2d5f6479741271da73bd9d30c2580c26#87bf6b6c2d5f6479741271da73bd9d30c2580c26"
[[package]]
name = "salsa-macros"
version = "0.19.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=d758691ba17ee1a60c5356ea90888d529e1782ad#d758691ba17ee1a60c5356ea90888d529e1782ad"
source = "git+https://github.com/salsa-rs/salsa.git?rev=87bf6b6c2d5f6479741271da73bd9d30c2580c26#87bf6b6c2d5f6479741271da73bd9d30c2580c26"
dependencies = [
"heck",
"proc-macro2",
@@ -3680,9 +3685,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "smallvec"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "snapbox"
@@ -3805,7 +3810,7 @@ dependencies = [
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.2",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -3878,6 +3883,12 @@ dependencies = [
"test-case-core",
]
[[package]]
name = "thin-vec"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
[[package]]
name = "thiserror"
version = "1.0.69"

View File

@@ -4,7 +4,7 @@ resolver = "2"
[workspace.package]
edition = "2021"
rust-version = "1.83"
rust-version = "1.84"
homepage = "https://docs.astral.sh/ruff"
documentation = "https://docs.astral.sh/ruff"
repository = "https://github.com/astral-sh/ruff"
@@ -124,7 +124,7 @@ rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
rustc-hash = { version = "2.0.0" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "d758691ba17ee1a60c5356ea90888d529e1782ad" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "87bf6b6c2d5f6479741271da73bd9d30c2580c26" }
schemars = { version = "0.8.16" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }
@@ -209,6 +209,7 @@ must_use_candidate = "allow"
similar_names = "allow"
single_match_else = "allow"
too_many_lines = "allow"
needless_continue = "allow" # An explicit continue can be more readable, especially if the alternative is an empty block.
# Without the hashes we run into a `rustfmt` bug in some snapshot tests, see #13250
needless_raw_string_hashes = "allow"
# Disallowed restriction lints
@@ -271,7 +272,7 @@ inherits = "release"
# Config for 'dist'
[workspace.metadata.dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.25.2-prerelease.3"
cargo-dist-version = "0.28.4-prerelease.1"
# CI backends to support
ci = "github"
# The installers to generate for each app
@@ -328,9 +329,12 @@ github-custom-job-permissions = { "build-docker" = { packages = "write", content
install-updater = false
# Path that installers should place binaries in
install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"]
# Temporarily allow changes to the `release` workflow, in which we pin actions
# to a SHA instead of a tag (https://github.com/astral-sh/uv/issues/12253)
allow-dirty = ["ci"]
[workspace.metadata.dist.github-custom-runners]
global = "depot-ubuntu-latest-4"
[workspace.metadata.dist.github-action-commits]
"actions/checkout" = "11bd71901bbe5b1630ceea73d27597364c9af683" # v4
"actions/upload-artifact" = "ea165f8d65b6e75b540449e92b4886f43607fa02" # v4.6.2
"actions/download-artifact" = "95815c38cf2ff2164869cbab79da8d1f422bc89e" # v4.2.1
"actions/attest-build-provenance" = "c074443f1aee8d4aeeae555aebba3282517141b2" #v2.2.3

View File

@@ -149,8 +149,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.11.3/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.11.3/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.11.5/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.11.5/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -183,7 +183,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.11.3
rev: v0.11.5
hooks:
# Run the linter.
- id: ruff

View File

@@ -71,6 +71,15 @@ pub(crate) struct CheckCommand {
#[arg(long, value_name = "VERSION", alias = "target-version")]
pub(crate) python_version: Option<PythonVersion>,
/// Target platform to assume when resolving types.
///
/// This is used to specialize the type of `sys.platform` and will affect the visibility
/// of platform-specific functions and attributes. If the value is set to `all`, no
/// assumptions are made about the target platform. If unspecified, the current system's
/// platform will be used.
#[arg(long, value_name = "PLATFORM", alias = "platform")]
pub(crate) python_platform: Option<String>,
#[clap(flatten)]
pub(crate) verbosity: Verbosity,
@@ -116,6 +125,9 @@ impl CheckCommand {
python_version: self
.python_version
.map(|version| RangedValue::cli(version.into())),
python_platform: self
.python_platform
.map(|platform| RangedValue::cli(platform.into())),
python: self.python.map(RelativePathBuf::cli),
typeshed: self.typeshed.map(RelativePathBuf::cli),
extra_paths: self.extra_search_path.map(|extra_search_paths| {
@@ -124,7 +136,6 @@ impl CheckCommand {
.map(RelativePathBuf::cli)
.collect()
}),
..EnvironmentOptions::default()
}),
terminal: Some(TerminalOptions {
output_format: self

View File

@@ -6,9 +6,9 @@ use std::process::Command;
use tempfile::TempDir;
/// Specifying an option on the CLI should take precedence over the same setting in the
/// project's configuration.
/// project's configuration. Here, this is tested for the Python version.
#[test]
fn config_override() -> anyhow::Result<()> {
fn config_override_python_version() -> anyhow::Result<()> {
let case = TestCase::with_files([
(
"pyproject.toml",
@@ -57,6 +57,67 @@ fn config_override() -> anyhow::Result<()> {
Ok(())
}
/// Same as above, but for the Python platform.
#[test]
fn config_override_python_platform() -> anyhow::Result<()> {
let case = TestCase::with_files([
(
"pyproject.toml",
r#"
[tool.knot.environment]
python-platform = "linux"
"#,
),
(
"test.py",
r#"
import sys
from typing_extensions import reveal_type
reveal_type(sys.platform)
"#,
),
])?;
assert_cmd_snapshot!(case.command(), @r#"
success: true
exit_code: 0
----- stdout -----
info: revealed-type: Revealed type
--> <temp_dir>/test.py:5:1
|
3 | from typing_extensions import reveal_type
4 |
5 | reveal_type(sys.platform)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `Literal["linux"]`
|
Found 1 diagnostic
----- stderr -----
"#);
assert_cmd_snapshot!(case.command().arg("--python-platform").arg("all"), @r"
success: true
exit_code: 0
----- stdout -----
info: revealed-type: Revealed type
--> <temp_dir>/test.py:5:1
|
3 | from typing_extensions import reveal_type
4 |
5 | reveal_type(sys.platform)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `LiteralString`
|
Found 1 diagnostic
----- stderr -----
");
Ok(())
}
/// Paths specified on the CLI are relative to the current working directory and not the project root.
///
/// We test this by adding an extra search path from the CLI to the libs directory when
@@ -523,12 +584,12 @@ fn exit_code_only_info() -> anyhow::Result<()> {
success: true
exit_code: 0
----- stdout -----
info: revealed-type
info: revealed-type: Revealed type
--> <temp_dir>/test.py:3:1
|
2 | from typing_extensions import reveal_type
3 | reveal_type(1)
| ^^^^^^^^^^^^^^ Revealed type is `Literal[1]`
| ^^^^^^^^^^^^^^ `Literal[1]`
|
Found 1 diagnostic
@@ -553,12 +614,12 @@ fn exit_code_only_info_and_error_on_warning_is_true() -> anyhow::Result<()> {
success: true
exit_code: 0
----- stdout -----
info: revealed-type
info: revealed-type: Revealed type
--> <temp_dir>/test.py:3:1
|
2 | from typing_extensions import reveal_type
3 | reveal_type(1)
| ^^^^^^^^^^^^^^ Revealed type is `Literal[1]`
| ^^^^^^^^^^^^^^ `Literal[1]`
|
Found 1 diagnostic
@@ -991,6 +1052,39 @@ fn concise_diagnostics() -> anyhow::Result<()> {
Ok(())
}
/// This tests the diagnostic format for revealed type.
///
/// This test was introduced because changes were made to
/// how the revealed type diagnostic was constructed and
/// formatted in "verbose" mode. But it required extra
/// logic to ensure the concise version didn't regress on
/// information content. So this test was introduced to
/// capture that.
#[test]
fn concise_revealed_type() -> anyhow::Result<()> {
let case = TestCase::with_file(
"test.py",
r#"
from typing_extensions import reveal_type
x = "hello"
reveal_type(x)
"#,
)?;
assert_cmd_snapshot!(case.command().arg("--output-format=concise"), @r#"
success: true
exit_code: 0
----- stdout -----
info[revealed-type] <temp_dir>/test.py:5:1: Revealed type: `Literal["hello"]`
Found 1 diagnostic
----- stderr -----
"#);
Ok(())
}
struct TestCase {
_temp_dir: TempDir,
_settings_scope: SettingsBindDropGuard,

View File

@@ -17,6 +17,7 @@ ruff_python_parser = { workspace = true }
ruff_text_size = { workspace = true }
red_knot_python_semantic = { workspace = true }
rustc-hash = { workspace = true }
salsa = { workspace = true }
smallvec = { workspace = true }
tracing = { workspace = true }

View File

@@ -1,5 +1,6 @@
use crate::find_node::covering_node;
use crate::{Db, HasNavigationTargets, NavigationTargets, RangedValue};
use red_knot_python_semantic::types::Type;
use red_knot_python_semantic::{HasType, SemanticModel};
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::{parsed_module, ParsedModule};
@@ -16,40 +17,18 @@ pub fn goto_type_definition(
let goto_target = find_goto_target(parsed, offset)?;
let model = SemanticModel::new(db.upcast(), file);
let ty = match goto_target {
GotoTarget::Expression(expression) => expression.inferred_type(&model),
GotoTarget::FunctionDef(function) => function.inferred_type(&model),
GotoTarget::ClassDef(class) => class.inferred_type(&model),
GotoTarget::Parameter(parameter) => parameter.inferred_type(&model),
GotoTarget::Alias(alias) => alias.inferred_type(&model),
GotoTarget::ExceptVariable(except) => except.inferred_type(&model),
GotoTarget::KeywordArgument(argument) => {
// TODO: Pyright resolves the declared type of the matching parameter. This seems more accurate
// than using the inferred value.
argument.value.inferred_type(&model)
}
// TODO: Support identifier targets
GotoTarget::PatternMatchRest(_)
| GotoTarget::PatternKeywordArgument(_)
| GotoTarget::PatternMatchStarName(_)
| GotoTarget::PatternMatchAsName(_)
| GotoTarget::ImportedModule(_)
| GotoTarget::TypeParamTypeVarName(_)
| GotoTarget::TypeParamParamSpecName(_)
| GotoTarget::TypeParamTypeVarTupleName(_)
| GotoTarget::NonLocal { .. }
| GotoTarget::Globals { .. } => return None,
};
let ty = goto_target.inferred_type(&model)?;
tracing::debug!(
"Inferred type of covering node is {}",
ty.display(db.upcast())
);
let navigation_targets = ty.navigation_targets(db);
Some(RangedValue {
range: FileRange::new(file, goto_target.range()),
value: ty.navigation_targets(db),
value: navigation_targets,
})
}
@@ -149,6 +128,37 @@ pub(crate) enum GotoTarget<'a> {
},
}
impl<'db> GotoTarget<'db> {
pub(crate) fn inferred_type(self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
let ty = match self {
GotoTarget::Expression(expression) => expression.inferred_type(model),
GotoTarget::FunctionDef(function) => function.inferred_type(model),
GotoTarget::ClassDef(class) => class.inferred_type(model),
GotoTarget::Parameter(parameter) => parameter.inferred_type(model),
GotoTarget::Alias(alias) => alias.inferred_type(model),
GotoTarget::ExceptVariable(except) => except.inferred_type(model),
GotoTarget::KeywordArgument(argument) => {
// TODO: Pyright resolves the declared type of the matching parameter. This seems more accurate
// than using the inferred value.
argument.value.inferred_type(model)
}
// TODO: Support identifier targets
GotoTarget::PatternMatchRest(_)
| GotoTarget::PatternKeywordArgument(_)
| GotoTarget::PatternMatchStarName(_)
| GotoTarget::PatternMatchAsName(_)
| GotoTarget::ImportedModule(_)
| GotoTarget::TypeParamTypeVarName(_)
| GotoTarget::TypeParamParamSpecName(_)
| GotoTarget::TypeParamTypeVarTupleName(_)
| GotoTarget::NonLocal { .. }
| GotoTarget::Globals { .. } => return None,
};
Some(ty)
}
}
impl Ranged for GotoTarget<'_> {
fn range(&self) -> TextRange {
match self {
@@ -174,16 +184,18 @@ impl Ranged for GotoTarget<'_> {
}
pub(crate) fn find_goto_target(parsed: &ParsedModule, offset: TextSize) -> Option<GotoTarget> {
let token = parsed.tokens().at_offset(offset).find(|token| {
matches!(
token.kind(),
let token = parsed
.tokens()
.at_offset(offset)
.max_by_key(|token| match token.kind() {
TokenKind::Name
| TokenKind::String
| TokenKind::Complex
| TokenKind::Float
| TokenKind::Int
)
})?;
| TokenKind::String
| TokenKind::Complex
| TokenKind::Float
| TokenKind::Int => 1,
_ => 0,
})?;
let covering_node = covering_node(parsed.syntax().into(), token.range())
.find(|node| node.is_identifier() || node.is_expression())
.ok()?;
@@ -241,27 +253,18 @@ pub(crate) fn find_goto_target(parsed: &ParsedModule, offset: TextSize) -> Optio
#[cfg(test)]
mod tests {
use std::fmt::Write;
use crate::db::tests::TestDb;
use crate::tests::{cursor_test, CursorTest, IntoDiagnostic};
use crate::{goto_type_definition, NavigationTarget};
use insta::assert_snapshot;
use insta::internals::SettingsBindDropGuard;
use red_knot_python_semantic::{
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings,
};
use ruff_db::diagnostic::{
Annotation, Diagnostic, DiagnosticFormat, DiagnosticId, DisplayDiagnosticConfig, LintName,
Severity, Span, SubDiagnostic,
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
};
use ruff_db::files::{system_path_to_file, File, FileRange};
use ruff_db::system::{DbWithWritableSystem, SystemPath, SystemPathBuf};
use ruff_python_ast::PythonVersion;
use ruff_text_size::{Ranged, TextSize};
use ruff_db::files::FileRange;
use ruff_text_size::Ranged;
#[test]
fn goto_type_of_expression_with_class_type() {
let test = goto_test(
let test = cursor_test(
r#"
class Test: ...
@@ -291,7 +294,7 @@ mod tests {
#[test]
fn goto_type_of_expression_with_function_type() {
let test = goto_test(
let test = cursor_test(
r#"
def foo(a, b): ...
@@ -323,7 +326,7 @@ mod tests {
#[test]
fn goto_type_of_expression_with_union_type() {
let test = goto_test(
let test = cursor_test(
r#"
def foo(a, b): ...
@@ -380,7 +383,7 @@ mod tests {
#[test]
fn goto_type_of_expression_with_module() {
let mut test = goto_test(
let mut test = cursor_test(
r#"
import lib
@@ -390,12 +393,12 @@ mod tests {
test.write_file("lib.py", "a = 10").unwrap();
assert_snapshot!(test.goto_type_definition(), @r###"
assert_snapshot!(test.goto_type_definition(), @r"
info: lint:goto-type-definition: Type definition
--> /lib.py:1:1
|
1 | a = 10
| ^
| ^^^^^^
|
info: Source
--> /main.py:4:13
@@ -405,12 +408,12 @@ mod tests {
4 | lib
| ^^^
|
"###);
");
}
#[test]
fn goto_type_of_expression_with_literal_type() {
let test = goto_test(
let test = cursor_test(
r#"
a: str = "test"
@@ -441,7 +444,7 @@ mod tests {
}
#[test]
fn goto_type_of_expression_with_literal_node() {
let test = goto_test(
let test = cursor_test(
r#"
a: str = "te<CURSOR>st"
"#,
@@ -469,7 +472,7 @@ mod tests {
#[test]
fn goto_type_of_expression_with_type_var_type() {
let test = goto_test(
let test = cursor_test(
r#"
type Alias[T: int = bool] = list[T<CURSOR>]
"#,
@@ -493,7 +496,7 @@ mod tests {
#[test]
fn goto_type_of_expression_with_type_param_spec() {
let test = goto_test(
let test = cursor_test(
r#"
type Alias[**P = [int, str]] = Callable[P<CURSOR>, int]
"#,
@@ -507,7 +510,7 @@ mod tests {
#[test]
fn goto_type_of_expression_with_type_var_tuple() {
let test = goto_test(
let test = cursor_test(
r#"
type Alias[*Ts = ()] = tuple[*Ts<CURSOR>]
"#,
@@ -521,7 +524,7 @@ mod tests {
#[test]
fn goto_type_on_keyword_argument() {
let test = goto_test(
let test = cursor_test(
r#"
def test(a: str): ...
@@ -553,7 +556,7 @@ mod tests {
#[test]
fn goto_type_on_incorrectly_typed_keyword_argument() {
let test = goto_test(
let test = cursor_test(
r#"
def test(a: str): ...
@@ -588,7 +591,7 @@ mod tests {
#[test]
fn goto_type_on_kwargs() {
let test = goto_test(
let test = cursor_test(
r#"
def f(name: str): ...
@@ -622,14 +625,13 @@ f(**kwargs<CURSOR>)
#[test]
fn goto_type_of_expression_with_builtin() {
let test = goto_test(
let test = cursor_test(
r#"
def foo(a: str):
a<CURSOR>
"#,
);
// FIXME: This should go to `str`
assert_snapshot!(test.goto_type_definition(), @r###"
info: lint:goto-type-definition: Type definition
--> stdlib/builtins.pyi:443:7
@@ -653,7 +655,7 @@ f(**kwargs<CURSOR>)
#[test]
fn goto_type_definition_cursor_between_object_and_attribute() {
let test = goto_test(
let test = cursor_test(
r#"
class X:
def foo(a, b): ...
@@ -685,7 +687,7 @@ f(**kwargs<CURSOR>)
#[test]
fn goto_between_call_arguments() {
let test = goto_test(
let test = cursor_test(
r#"
def foo(a, b): ...
@@ -715,7 +717,7 @@ f(**kwargs<CURSOR>)
#[test]
fn goto_type_narrowing() {
let test = goto_test(
let test = cursor_test(
r#"
def foo(a: str | None, b):
if a is not None:
@@ -747,7 +749,7 @@ f(**kwargs<CURSOR>)
#[test]
fn goto_type_none() {
let test = goto_test(
let test = cursor_test(
r#"
def foo(a: str | None, b):
a<CURSOR>
@@ -755,6 +757,23 @@ f(**kwargs<CURSOR>)
);
assert_snapshot!(test.goto_type_definition(), @r"
info: lint:goto-type-definition: Type definition
--> stdlib/types.pyi:677:11
|
675 | if sys.version_info >= (3, 10):
676 | @final
677 | class NoneType:
| ^^^^^^^^
678 | def __bool__(self) -> Literal[False]: ...
|
info: Source
--> /main.py:3:17
|
2 | def foo(a: str | None, b):
3 | a
| ^
|
info: lint:goto-type-definition: Type definition
--> stdlib/builtins.pyi:443:7
|
@@ -769,88 +788,13 @@ f(**kwargs<CURSOR>)
--> /main.py:3:17
|
2 | def foo(a: str | None, b):
3 | a
| ^
|
info: lint:goto-type-definition: Type definition
--> stdlib/types.pyi:677:11
|
675 | if sys.version_info >= (3, 10):
676 | @final
677 | class NoneType:
| ^^^^^^^^
678 | def __bool__(self) -> Literal[False]: ...
|
info: Source
--> /main.py:3:17
|
2 | def foo(a: str | None, b):
3 | a
| ^
|
");
}
fn goto_test(source: &str) -> GotoTest {
let mut db = TestDb::new();
let cursor_offset = source.find("<CURSOR>").expect(
"`source`` should contain a `<CURSOR>` marker, indicating the position of the cursor.",
);
let mut content = source[..cursor_offset].to_string();
content.push_str(&source[cursor_offset + "<CURSOR>".len()..]);
db.write_file("main.py", &content)
.expect("write to memory file system to be successful");
let file = system_path_to_file(&db, "main.py").expect("newly written file to existing");
Program::from_settings(
&db,
ProgramSettings {
python_version: PythonVersion::latest(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings {
extra_paths: vec![],
src_roots: vec![SystemPathBuf::from("/")],
custom_typeshed: None,
python_path: PythonPath::KnownSitePackages(vec![]),
},
},
)
.expect("Default settings to be valid");
let mut insta_settings = insta::Settings::clone_current();
insta_settings.add_filter(r#"\\(\w\w|\s|\.|")"#, "/$1");
let insta_settings_guard = insta_settings.bind_to_scope();
GotoTest {
db,
cursor_offset: TextSize::try_from(cursor_offset)
.expect("source to be smaller than 4GB"),
file,
_insta_settings_guard: insta_settings_guard,
}
}
struct GotoTest {
db: TestDb,
cursor_offset: TextSize,
file: File,
_insta_settings_guard: SettingsBindDropGuard,
}
impl GotoTest {
fn write_file(
&mut self,
path: impl AsRef<SystemPath>,
content: &str,
) -> std::io::Result<()> {
self.db.write_file(path, content)
}
impl CursorTest {
fn goto_type_definition(&self) -> String {
let Some(targets) = goto_type_definition(&self.db, self.file, self.cursor_offset)
else {
@@ -861,19 +805,12 @@ f(**kwargs<CURSOR>)
return "No type definitions found".to_string();
}
let mut buf = String::new();
let source = targets.range;
let config = DisplayDiagnosticConfig::default()
.color(false)
.format(DiagnosticFormat::Full);
for target in &*targets {
let diag = GotoTypeDefinitionDiagnostic::new(source, target).into_diagnostic();
write!(buf, "{}", diag.display(&self.db, &config)).unwrap();
}
buf
self.render_diagnostics(
targets
.into_iter()
.map(|target| GotoTypeDefinitionDiagnostic::new(source, &target)),
)
}
}
@@ -889,7 +826,9 @@ f(**kwargs<CURSOR>)
target: FileRange::new(target.file(), target.focus_range()),
}
}
}
impl IntoDiagnostic for GotoTypeDefinitionDiagnostic {
fn into_diagnostic(self) -> Diagnostic {
let mut source = SubDiagnostic::new(Severity::Info, "Source");
source.annotate(Annotation::primary(

View File

@@ -0,0 +1,602 @@
use crate::goto::{find_goto_target, GotoTarget};
use crate::{Db, MarkupKind, RangedValue};
use red_knot_python_semantic::types::Type;
use red_knot_python_semantic::SemanticModel;
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::parsed_module;
use ruff_text_size::{Ranged, TextSize};
use std::fmt;
use std::fmt::Formatter;
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover>> {
let parsed = parsed_module(db.upcast(), file);
let goto_target = find_goto_target(parsed, offset)?;
if let GotoTarget::Expression(expr) = goto_target {
if expr.is_literal_expr() {
return None;
}
}
let model = SemanticModel::new(db.upcast(), file);
let ty = goto_target.inferred_type(&model)?;
tracing::debug!(
"Inferred type of covering node is {}",
ty.display(db.upcast())
);
// TODO: Add documentation of the symbol (not the type's definition).
// TODO: Render the symbol's signature instead of just its type.
let contents = vec![HoverContent::Type(ty)];
Some(RangedValue {
range: FileRange::new(file, goto_target.range()),
value: Hover { contents },
})
}
pub struct Hover<'db> {
contents: Vec<HoverContent<'db>>,
}
impl<'db> Hover<'db> {
/// Renders the hover to a string using the specified markup kind.
pub const fn display<'a>(&'a self, db: &'a dyn Db, kind: MarkupKind) -> DisplayHover<'a> {
DisplayHover {
db,
hover: self,
kind,
}
}
fn iter(&self) -> std::slice::Iter<'_, HoverContent<'db>> {
self.contents.iter()
}
}
impl<'db> IntoIterator for Hover<'db> {
type Item = HoverContent<'db>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.contents.into_iter()
}
}
impl<'a, 'db> IntoIterator for &'a Hover<'db> {
type Item = &'a HoverContent<'db>;
type IntoIter = std::slice::Iter<'a, HoverContent<'db>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct DisplayHover<'a> {
db: &'a dyn Db,
hover: &'a Hover<'a>,
kind: MarkupKind,
}
impl fmt::Display for DisplayHover<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut first = true;
for content in &self.hover.contents {
if !first {
self.kind.horizontal_line().fmt(f)?;
}
content.display(self.db, self.kind).fmt(f)?;
first = false;
}
Ok(())
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum HoverContent<'db> {
Type(Type<'db>),
}
impl<'db> HoverContent<'db> {
fn display(&self, db: &'db dyn Db, kind: MarkupKind) -> DisplayHoverContent<'_, 'db> {
DisplayHoverContent {
db,
content: self,
kind,
}
}
}
pub(crate) struct DisplayHoverContent<'a, 'db> {
db: &'db dyn Db,
content: &'a HoverContent<'db>,
kind: MarkupKind,
}
impl fmt::Display for DisplayHoverContent<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.content {
HoverContent::Type(ty) => self
.kind
.fenced_code_block(ty.display(self.db.upcast()), "text")
.fmt(f),
}
}
}
#[cfg(test)]
mod tests {
use crate::tests::{cursor_test, CursorTest};
use crate::{hover, MarkupKind};
use insta::assert_snapshot;
use ruff_db::diagnostic::{
Annotation, Diagnostic, DiagnosticFormat, DiagnosticId, DisplayDiagnosticConfig, LintName,
Severity, Span,
};
use ruff_text_size::{Ranged, TextRange};
#[test]
fn hover_basic() {
let test = cursor_test(
r#"
a = 10
a<CURSOR>
"#,
);
assert_snapshot!(test.hover(), @r"
Literal[10]
---------------------------------------------
```text
Literal[10]
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:4:9
|
2 | a = 10
3 |
4 | a
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_member() {
let test = cursor_test(
r#"
class Foo:
a: int = 10
def __init__(a: int, b: str):
self.a = a
self.b: str = b
foo = Foo()
foo.<CURSOR>a
"#,
);
assert_snapshot!(test.hover(), @r"
int
---------------------------------------------
```text
int
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:10:9
|
9 | foo = Foo()
10 | foo.a
| ^^^^-
| | |
| | Cursor offset
| source
|
");
}
#[test]
fn hover_function_typed_variable() {
let test = cursor_test(
r#"
def foo(a, b): ...
foo<CURSOR>
"#,
);
assert_snapshot!(test.hover(), @r"
Literal[foo]
---------------------------------------------
```text
Literal[foo]
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:4:13
|
2 | def foo(a, b): ...
3 |
4 | foo
| ^^^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_binary_expression() {
let test = cursor_test(
r#"
def foo(a: int, b: int, c: int):
a + b ==<CURSOR> c
"#,
);
assert_snapshot!(test.hover(), @r"
bool
---------------------------------------------
```text
bool
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:3:17
|
2 | def foo(a: int, b: int, c: int):
3 | a + b == c
| ^^^^^^^^-^
| | |
| | Cursor offset
| source
|
");
}
#[test]
fn hover_keyword_parameter() {
let test = cursor_test(
r#"
def test(a: int): ...
test(a<CURSOR>= 123)
"#,
);
// TODO: This should reveal `int` because the user hovers over the parameter and not the value.
assert_snapshot!(test.hover(), @r"
Literal[123]
---------------------------------------------
```text
Literal[123]
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:4:18
|
2 | def test(a: int): ...
3 |
4 | test(a= 123)
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_union() {
let test = cursor_test(
r#"
def foo(a, b): ...
def bar(a, b): ...
if random.choice([True, False]):
a = foo
else:
a = bar
a<CURSOR>
"#,
);
assert_snapshot!(test.hover(), @r"
Literal[foo, bar]
---------------------------------------------
```text
Literal[foo, bar]
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:12:13
|
10 | a = bar
11 |
12 | a
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_module() {
let mut test = cursor_test(
r#"
import lib
li<CURSOR>b
"#,
);
test.write_file("lib.py", "a = 10").unwrap();
assert_snapshot!(test.hover(), @r"
<module 'lib'>
---------------------------------------------
```text
<module 'lib'>
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:4:13
|
2 | import lib
3 |
4 | lib
| ^^-
| | |
| | Cursor offset
| source
|
");
}
#[test]
fn hover_type_of_expression_with_type_var_type() {
let test = cursor_test(
r#"
type Alias[T: int = bool] = list[T<CURSOR>]
"#,
);
assert_snapshot!(test.hover(), @r"
T
---------------------------------------------
```text
T
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:2:46
|
2 | type Alias[T: int = bool] = list[T]
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_type_of_expression_with_type_param_spec() {
let test = cursor_test(
r#"
type Alias[**P = [int, str]] = Callable[P<CURSOR>, int]
"#,
);
assert_snapshot!(test.hover(), @r"
@Todo
---------------------------------------------
```text
@Todo
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:2:53
|
2 | type Alias[**P = [int, str]] = Callable[P, int]
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_type_of_expression_with_type_var_tuple() {
let test = cursor_test(
r#"
type Alias[*Ts = ()] = tuple[*Ts<CURSOR>]
"#,
);
assert_snapshot!(test.hover(), @r"
@Todo
---------------------------------------------
```text
@Todo
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:2:43
|
2 | type Alias[*Ts = ()] = tuple[*Ts]
| ^^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_class_member_declaration() {
let test = cursor_test(
r#"
class Foo:
a<CURSOR>: int
"#,
);
// TODO: This should be int and not `Never`, https://github.com/astral-sh/ruff/issues/17122
assert_snapshot!(test.hover(), @r"
Never
---------------------------------------------
```text
Never
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:3:13
|
2 | class Foo:
3 | a: int
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_type_narrowing() {
let test = cursor_test(
r#"
def foo(a: str | None, b):
if a is not None:
print(a<CURSOR>)
"#,
);
assert_snapshot!(test.hover(), @r"
str
---------------------------------------------
```text
str
```
---------------------------------------------
info: lint:hover: Hovered content is
--> /main.py:4:27
|
2 | def foo(a: str | None, b):
3 | if a is not None:
4 | print(a)
| ^- Cursor offset
| |
| source
|
");
}
#[test]
fn hover_whitespace() {
let test = cursor_test(
r#"
class C:
<CURSOR>
foo: str = 'bar'
"#,
);
assert_snapshot!(test.hover(), @"Hover provided no content");
}
#[test]
fn hover_literal_int() {
let test = cursor_test(
r#"
print(
0 + 1<CURSOR>
)
"#,
);
assert_snapshot!(test.hover(), @"Hover provided no content");
}
#[test]
fn hover_literal_ellipsis() {
let test = cursor_test(
r#"
print(
.<CURSOR>..
)
"#,
);
assert_snapshot!(test.hover(), @"Hover provided no content");
}
#[test]
fn hover_docstring() {
let test = cursor_test(
r#"
def f():
"""Lorem ipsum dolor sit amet.<CURSOR>"""
"#,
);
assert_snapshot!(test.hover(), @"Hover provided no content");
}
impl CursorTest {
fn hover(&self) -> String {
use std::fmt::Write;
let Some(hover) = hover(&self.db, self.file, self.cursor_offset) else {
return "Hover provided no content".to_string();
};
let source = hover.range;
let mut buf = String::new();
write!(
&mut buf,
"{plaintext}{line}{markdown}{line}",
plaintext = hover.display(&self.db, MarkupKind::PlainText),
line = MarkupKind::PlainText.horizontal_line(),
markdown = hover.display(&self.db, MarkupKind::Markdown),
)
.unwrap();
let config = DisplayDiagnosticConfig::default()
.color(false)
.format(DiagnosticFormat::Full);
let mut diagnostic = Diagnostic::new(
DiagnosticId::Lint(LintName::of("hover")),
Severity::Info,
"Hovered content is",
);
diagnostic.annotate(
Annotation::primary(Span::from(source.file()).with_range(source.range()))
.message("source"),
);
diagnostic.annotate(
Annotation::secondary(
Span::from(source.file()).with_range(TextRange::empty(self.cursor_offset)),
)
.message("Cursor offset"),
);
write!(buf, "{}", diagnostic.display(&self.db, &config)).unwrap();
buf
}
}
}

View File

@@ -0,0 +1,279 @@
use crate::Db;
use red_knot_python_semantic::types::Type;
use red_knot_python_semantic::{HasType, SemanticModel};
use ruff_db::files::File;
use ruff_db::parsed::parsed_module;
use ruff_python_ast::visitor::source_order::{self, SourceOrderVisitor, TraversalSignal};
use ruff_python_ast::{AnyNodeRef, Expr, Stmt};
use ruff_text_size::{Ranged, TextRange, TextSize};
use std::fmt;
use std::fmt::Formatter;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct InlayHint<'db> {
pub position: TextSize,
pub content: InlayHintContent<'db>,
}
impl<'db> InlayHint<'db> {
pub const fn display(&self, db: &'db dyn Db) -> DisplayInlayHint<'_, 'db> {
self.content.display(db)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum InlayHintContent<'db> {
Type(Type<'db>),
ReturnType(Type<'db>),
}
impl<'db> InlayHintContent<'db> {
pub const fn display(&self, db: &'db dyn Db) -> DisplayInlayHint<'_, 'db> {
DisplayInlayHint { db, hint: self }
}
}
pub struct DisplayInlayHint<'a, 'db> {
db: &'db dyn Db,
hint: &'a InlayHintContent<'db>,
}
impl fmt::Display for DisplayInlayHint<'_, '_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.hint {
InlayHintContent::Type(ty) => {
write!(f, ": {}", ty.display(self.db.upcast()))
}
InlayHintContent::ReturnType(ty) => {
write!(f, " -> {}", ty.display(self.db.upcast()))
}
}
}
}
pub fn inlay_hints(db: &dyn Db, file: File, range: TextRange) -> Vec<InlayHint<'_>> {
let mut visitor = InlayHintVisitor::new(db, file, range);
let ast = parsed_module(db.upcast(), file);
visitor.visit_body(ast.suite());
visitor.hints
}
struct InlayHintVisitor<'db> {
model: SemanticModel<'db>,
hints: Vec<InlayHint<'db>>,
in_assignment: bool,
range: TextRange,
}
impl<'db> InlayHintVisitor<'db> {
fn new(db: &'db dyn Db, file: File, range: TextRange) -> Self {
Self {
model: SemanticModel::new(db.upcast(), file),
hints: Vec::new(),
in_assignment: false,
range,
}
}
fn add_type_hint(&mut self, position: TextSize, ty: Type<'db>) {
self.hints.push(InlayHint {
position,
content: InlayHintContent::Type(ty),
});
}
}
impl SourceOrderVisitor<'_> for InlayHintVisitor<'_> {
fn enter_node(&mut self, node: AnyNodeRef<'_>) -> TraversalSignal {
if self.range.intersect(node.range()).is_some() {
TraversalSignal::Traverse
} else {
TraversalSignal::Skip
}
}
fn visit_stmt(&mut self, stmt: &Stmt) {
let node = AnyNodeRef::from(stmt);
if !self.enter_node(node).is_traverse() {
return;
}
match stmt {
Stmt::Assign(assign) => {
self.in_assignment = true;
for target in &assign.targets {
self.visit_expr(target);
}
self.in_assignment = false;
return;
}
// TODO
Stmt::FunctionDef(_) => {}
Stmt::For(_) => {}
Stmt::Expr(_) => {
// Don't traverse into expression statements because we don't show any hints.
return;
}
_ => {}
}
source_order::walk_stmt(self, stmt);
}
fn visit_expr(&mut self, expr: &'_ Expr) {
if !self.in_assignment {
return;
}
match expr {
Expr::Name(name) => {
if name.ctx.is_store() {
let ty = expr.inferred_type(&self.model);
self.add_type_hint(expr.range().end(), ty);
}
}
_ => {
source_order::walk_expr(self, expr);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_snapshot;
use ruff_db::{
files::{system_path_to_file, File},
source::source_text,
};
use ruff_text_size::TextSize;
use crate::db::tests::TestDb;
use red_knot_python_semantic::{
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings,
};
use ruff_db::system::{DbWithWritableSystem, SystemPathBuf};
use ruff_python_ast::PythonVersion;
pub(super) fn inlay_hint_test(source: &str) -> InlayHintTest {
const START: &str = "<START>";
const END: &str = "<END>";
let mut db = TestDb::new();
let start = source.find(START);
let end = source
.find(END)
.map(|x| if start.is_some() { x - START.len() } else { x })
.unwrap_or(source.len());
let range = TextRange::new(
TextSize::try_from(start.unwrap_or_default()).unwrap(),
TextSize::try_from(end).unwrap(),
);
let source = source.replace(START, "");
let source = source.replace(END, "");
db.write_file("main.py", source)
.expect("write to memory file system to be successful");
let file = system_path_to_file(&db, "main.py").expect("newly written file to existing");
Program::from_settings(
&db,
ProgramSettings {
python_version: PythonVersion::latest(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings {
extra_paths: vec![],
src_roots: vec![SystemPathBuf::from("/")],
custom_typeshed: None,
python_path: PythonPath::KnownSitePackages(vec![]),
},
},
)
.expect("Default settings to be valid");
InlayHintTest { db, file, range }
}
pub(super) struct InlayHintTest {
pub(super) db: TestDb,
pub(super) file: File,
pub(super) range: TextRange,
}
impl InlayHintTest {
fn inlay_hints(&self) -> String {
let hints = inlay_hints(&self.db, self.file, self.range);
let mut buf = source_text(&self.db, self.file).as_str().to_string();
let mut offset = 0;
for hint in hints {
let end_position = (hint.position.to_u32() as usize) + offset;
let hint_str = format!("[{}]", hint.display(&self.db));
buf.insert_str(end_position, &hint_str);
offset += hint_str.len();
}
buf
}
}
#[test]
fn test_assign_statement() {
let test = inlay_hint_test("x = 1");
assert_snapshot!(test.inlay_hints(), @r"
x[: Literal[1]] = 1
");
}
#[test]
fn test_tuple_assignment() {
let test = inlay_hint_test("x, y = (1, 'abc')");
assert_snapshot!(test.inlay_hints(), @r#"
x[: Literal[1]], y[: Literal["abc"]] = (1, 'abc')
"#);
}
#[test]
fn test_nested_tuple_assignment() {
let test = inlay_hint_test("x, (y, z) = (1, ('abc', 2))");
assert_snapshot!(test.inlay_hints(), @r#"
x[: Literal[1]], (y[: Literal["abc"]], z[: Literal[2]]) = (1, ('abc', 2))
"#);
}
#[test]
fn test_assign_statement_with_type_annotation() {
let test = inlay_hint_test("x: int = 1");
assert_snapshot!(test.inlay_hints(), @r"
x: int = 1
");
}
#[test]
fn test_assign_statement_out_of_range() {
let test = inlay_hint_test("<START>x = 1<END>\ny = 2");
assert_snapshot!(test.inlay_hints(), @r"
x[: Literal[1]] = 1
y = 2
");
}
}

View File

@@ -1,18 +1,22 @@
mod db;
mod find_node;
mod goto;
use std::ops::{Deref, DerefMut};
mod hover;
mod inlay_hints;
mod markup;
pub use db::Db;
pub use goto::goto_type_definition;
use red_knot_python_semantic::types::{
Class, ClassBase, ClassLiteralType, FunctionType, InstanceType, IntersectionType,
KnownInstanceType, ModuleLiteralType, Type,
};
pub use hover::hover;
pub use inlay_hints::inlay_hints;
pub use markup::MarkupKind;
use rustc_hash::FxHashSet;
use std::ops::{Deref, DerefMut};
use red_knot_python_semantic::types::{Type, TypeDefinition};
use ruff_db::files::{File, FileRange};
use ruff_db::source::source_text;
use ruff_text_size::{Ranged, TextLen, TextRange};
use ruff_text_size::{Ranged, TextRange};
/// Information associated with a text range.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@@ -54,7 +58,7 @@ where
}
/// Target to which the editor can navigate to.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NavigationTarget {
file: File,
@@ -95,6 +99,17 @@ impl NavigationTargets {
Self(smallvec::SmallVec::new())
}
fn unique(targets: impl IntoIterator<Item = NavigationTarget>) -> Self {
let unique: FxHashSet<_> = targets.into_iter().collect();
if unique.is_empty() {
Self::empty()
} else {
let mut targets = unique.into_iter().collect::<Vec<_>>();
targets.sort_by_key(|target| (target.file, target.focus_range.start()));
Self(targets.into())
}
}
fn iter(&self) -> std::slice::Iter<'_, NavigationTarget> {
self.0.iter()
}
@@ -125,7 +140,7 @@ impl<'a> IntoIterator for &'a NavigationTargets {
impl FromIterator<NavigationTarget> for NavigationTargets {
fn from_iter<T: IntoIterator<Item = NavigationTarget>>(iter: T) -> Self {
Self(iter.into_iter().collect())
Self::unique(iter)
}
}
@@ -136,128 +151,146 @@ pub trait HasNavigationTargets {
impl HasNavigationTargets for Type<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
match self {
Type::BoundMethod(method) => method.function(db).navigation_targets(db),
Type::FunctionLiteral(function) => function.navigation_targets(db),
Type::ModuleLiteral(module) => module.navigation_targets(db),
Type::Union(union) => union
.iter(db.upcast())
.flat_map(|target| target.navigation_targets(db))
.collect(),
Type::ClassLiteral(class) => class.navigation_targets(db),
Type::Instance(instance) => instance.navigation_targets(db),
Type::KnownInstance(instance) => instance.navigation_targets(db),
Type::SubclassOf(subclass_of_type) => match subclass_of_type.subclass_of() {
ClassBase::Class(class) => class.navigation_targets(db),
ClassBase::Dynamic(_) => NavigationTargets::empty(),
Type::Intersection(intersection) => {
// Only consider the positive elements because the negative elements are mainly from narrowing constraints.
let mut targets = intersection
.iter_positive(db.upcast())
.filter(|ty| !ty.is_unknown());
let Some(first) = targets.next() else {
return NavigationTargets::empty();
};
match targets.next() {
Some(_) => {
// If there are multiple types in the intersection, we can't navigate to a single one
// because the type is the intersection of all those types.
NavigationTargets::empty()
}
None => first.navigation_targets(db),
}
}
ty => ty
.definition(db.upcast())
.map(|definition| definition.navigation_targets(db))
.unwrap_or_else(NavigationTargets::empty),
}
}
}
impl HasNavigationTargets for TypeDefinition<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
let full_range = self.full_range(db.upcast());
NavigationTargets::single(NavigationTarget {
file: full_range.file(),
focus_range: self.focus_range(db.upcast()).unwrap_or(full_range).range(),
full_range: full_range.range(),
})
}
}
#[cfg(test)]
mod tests {
use crate::db::tests::TestDb;
use insta::internals::SettingsBindDropGuard;
use red_knot_python_semantic::{
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings,
};
use ruff_db::diagnostic::{Diagnostic, DiagnosticFormat, DisplayDiagnosticConfig};
use ruff_db::files::{system_path_to_file, File};
use ruff_db::system::{DbWithWritableSystem, SystemPath, SystemPathBuf};
use ruff_python_ast::PythonVersion;
use ruff_text_size::TextSize;
pub(super) fn cursor_test(source: &str) -> CursorTest {
let mut db = TestDb::new();
let cursor_offset = source.find("<CURSOR>").expect(
"`source`` should contain a `<CURSOR>` marker, indicating the position of the cursor.",
);
let mut content = source[..cursor_offset].to_string();
content.push_str(&source[cursor_offset + "<CURSOR>".len()..]);
db.write_file("main.py", &content)
.expect("write to memory file system to be successful");
let file = system_path_to_file(&db, "main.py").expect("newly written file to existing");
Program::from_settings(
&db,
ProgramSettings {
python_version: PythonVersion::latest(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings {
extra_paths: vec![],
src_roots: vec![SystemPathBuf::from("/")],
custom_typeshed: None,
python_path: PythonPath::KnownSitePackages(vec![]),
},
},
)
.expect("Default settings to be valid");
Type::StringLiteral(_)
| Type::BooleanLiteral(_)
| Type::LiteralString
| Type::IntLiteral(_)
| Type::BytesLiteral(_)
| Type::SliceLiteral(_)
| Type::MethodWrapper(_)
| Type::WrapperDescriptor(_)
| Type::PropertyInstance(_)
| Type::Tuple(_) => self.to_meta_type(db.upcast()).navigation_targets(db),
let mut insta_settings = insta::Settings::clone_current();
insta_settings.add_filter(r#"\\(\w\w|\s|\.|")"#, "/$1");
// Filter out TODO types because they are different between debug and release builds.
insta_settings.add_filter(r"@Todo\(.+\)", "@Todo");
Type::Intersection(intersection) => intersection.navigation_targets(db),
let insta_settings_guard = insta_settings.bind_to_scope();
Type::Dynamic(_)
| Type::Never
| Type::Callable(_)
| Type::AlwaysTruthy
| Type::AlwaysFalsy => NavigationTargets::empty(),
}
}
}
impl HasNavigationTargets for FunctionType<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
let function_range = self.focus_range(db.upcast());
NavigationTargets::single(NavigationTarget {
file: function_range.file(),
focus_range: function_range.range(),
full_range: self.full_range(db.upcast()).range(),
})
}
}
impl HasNavigationTargets for Class<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
let class_range = self.focus_range(db.upcast());
NavigationTargets::single(NavigationTarget {
file: class_range.file(),
focus_range: class_range.range(),
full_range: self.full_range(db.upcast()).range(),
})
}
}
impl HasNavigationTargets for ClassLiteralType<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
self.class().navigation_targets(db)
}
}
impl HasNavigationTargets for InstanceType<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
self.class().navigation_targets(db)
}
}
impl HasNavigationTargets for ModuleLiteralType<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
let file = self.module(db).file();
let source = source_text(db.upcast(), file);
NavigationTargets::single(NavigationTarget {
CursorTest {
db,
cursor_offset: TextSize::try_from(cursor_offset)
.expect("source to be smaller than 4GB"),
file,
focus_range: TextRange::default(),
full_range: TextRange::up_to(source.text_len()),
})
}
}
impl HasNavigationTargets for KnownInstanceType<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
match self {
KnownInstanceType::TypeVar(var) => {
let definition = var.definition(db);
let full_range = definition.full_range(db.upcast());
NavigationTargets::single(NavigationTarget {
file: full_range.file(),
focus_range: definition.focus_range(db.upcast()).range(),
full_range: full_range.range(),
})
}
// TODO: Track the definition of `KnownInstance` and navigate to their definition.
_ => NavigationTargets::empty(),
_insta_settings_guard: insta_settings_guard,
}
}
}
impl HasNavigationTargets for IntersectionType<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
// Only consider the positive elements because the negative elements are mainly from narrowing constraints.
let mut targets = self
.iter_positive(db.upcast())
.filter(|ty| !ty.is_unknown());
pub(super) struct CursorTest {
pub(super) db: TestDb,
pub(super) cursor_offset: TextSize,
pub(super) file: File,
_insta_settings_guard: SettingsBindDropGuard,
}
let Some(first) = targets.next() else {
return NavigationTargets::empty();
};
impl CursorTest {
pub(super) fn write_file(
&mut self,
path: impl AsRef<SystemPath>,
content: &str,
) -> std::io::Result<()> {
self.db.write_file(path, content)
}
match targets.next() {
Some(_) => {
// If there are multiple types in the intersection, we can't navigate to a single one
// because the type is the intersection of all those types.
NavigationTargets::empty()
pub(super) fn render_diagnostics<I, D>(&self, diagnostics: I) -> String
where
I: IntoIterator<Item = D>,
D: IntoDiagnostic,
{
use std::fmt::Write;
let mut buf = String::new();
let config = DisplayDiagnosticConfig::default()
.color(false)
.format(DiagnosticFormat::Full);
for diagnostic in diagnostics {
let diag = diagnostic.into_diagnostic();
write!(buf, "{}", diag.display(&self.db, &config)).unwrap();
}
None => first.navigation_targets(db),
buf
}
}
pub(super) trait IntoDiagnostic {
fn into_diagnostic(self) -> Diagnostic;
}
}

View File

@@ -0,0 +1,66 @@
use std::fmt;
use std::fmt::Formatter;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum MarkupKind {
PlainText,
Markdown,
}
impl MarkupKind {
pub(crate) const fn fenced_code_block<T>(self, code: T, language: &str) -> FencedCodeBlock<T>
where
T: fmt::Display,
{
FencedCodeBlock {
language,
code,
kind: self,
}
}
pub(crate) const fn horizontal_line(self) -> HorizontalLine {
HorizontalLine { kind: self }
}
}
pub(crate) struct FencedCodeBlock<'a, T> {
language: &'a str,
code: T,
kind: MarkupKind,
}
impl<T> fmt::Display for FencedCodeBlock<'_, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
MarkupKind::PlainText => self.code.fmt(f),
MarkupKind::Markdown => write!(
f,
"```{language}\n{code}\n```",
language = self.language,
code = self.code
),
}
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct HorizontalLine {
kind: MarkupKind,
}
impl fmt::Display for HorizontalLine {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.kind {
MarkupKind::PlainText => {
f.write_str("\n---------------------------------------------\n")
}
MarkupKind::Markdown => {
write!(f, "\n---\n")
}
}
}
}

View File

@@ -16,6 +16,7 @@ ruff_cache = { workspace = true }
ruff_db = { workspace = true, features = ["cache", "serde"] }
ruff_macros = { workspace = true }
ruff_python_ast = { workspace = true, features = ["serde"] }
ruff_python_formatter = { workspace = true, optional = true }
ruff_text_size = { workspace = true }
red_knot_ide = { workspace = true }
red_knot_python_semantic = { workspace = true, features = ["serde"] }
@@ -43,8 +44,13 @@ insta = { workspace = true, features = ["redactions", "ron"] }
[features]
default = ["zstd"]
deflate = ["red_knot_vendored/deflate"]
schemars = ["dep:schemars", "ruff_db/schemars", "red_knot_python_semantic/schemars"]
schemars = [
"dep:schemars",
"ruff_db/schemars",
"red_knot_python_semantic/schemars",
]
zstd = ["red_knot_vendored/zstd"]
format = ["ruff_python_formatter"]
[lints]
workspace = true

View File

@@ -159,7 +159,7 @@ impl salsa::Database for ProjectDatabase {
}
let event = event();
if matches!(event.kind, salsa::EventKind::WillCheckCancellation { .. }) {
if matches!(event.kind, salsa::EventKind::WillCheckCancellation) {
return;
}
@@ -174,6 +174,32 @@ impl Db for ProjectDatabase {
}
}
#[cfg(feature = "format")]
mod format {
use crate::ProjectDatabase;
use ruff_db::files::File;
use ruff_db::Upcast;
use ruff_python_formatter::{Db as FormatDb, PyFormatOptions};
#[salsa::db]
impl FormatDb for ProjectDatabase {
fn format_options(&self, file: File) -> PyFormatOptions {
let source_ty = file.source_type(self);
PyFormatOptions::from_source_type(source_ty)
}
}
impl Upcast<dyn FormatDb> for ProjectDatabase {
fn upcast(&self) -> &(dyn FormatDb + 'static) {
self
}
fn upcast_mut(&mut self) -> &mut (dyn FormatDb + 'static) {
self
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::sync::Arc;

View File

@@ -187,7 +187,7 @@ impl ProjectDatabase {
let program = Program::get(self);
if let Err(error) = program.update_from_settings(self, program_settings) {
tracing::error!("Failed to update the program settings, keeping the old program settings: {error}");
};
}
if metadata.root() == project.root(self) {
tracing::debug!("Reloading project after structural change");

View File

@@ -54,20 +54,25 @@ impl Options {
project_root: &SystemPath,
system: &dyn System,
) -> ProgramSettings {
let (python_version, python_platform) = self
let python_version = self
.environment
.as_ref()
.map(|env| {
(
env.python_version.as_deref().copied(),
env.python_platform.as_deref(),
)
})
.and_then(|env| env.python_version.as_deref().copied())
.unwrap_or_default();
let python_platform = self
.environment
.as_ref()
.and_then(|env| env.python_platform.as_deref().cloned())
.unwrap_or_else(|| {
let default = PythonPlatform::default();
tracing::info!(
"Defaulting to default python version for this platform: '{default}'",
);
default
});
ProgramSettings {
python_version: python_version.unwrap_or_default(),
python_platform: python_platform.cloned().unwrap_or_default(),
python_version,
python_platform,
search_paths: self.to_search_path_settings(project_root, system),
}
}
@@ -224,7 +229,7 @@ impl Options {
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct EnvironmentOptions {
/// Specifies the version of Python that will be used to execute the source code.
/// Specifies the version of Python that will be used to analyze the source code.
/// The version should be specified as a string in the format `M.m` where `M` is the major version
/// and `m` is the minor (e.g. "3.0" or "3.6").
/// If a version is provided, knot will generate errors if the source code makes use of language features
@@ -233,11 +238,16 @@ pub struct EnvironmentOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub python_version: Option<RangedValue<PythonVersion>>,
/// Specifies the target platform that will be used to execute the source code.
/// Specifies the target platform that will be used to analyze the source code.
/// If specified, Red Knot will tailor its use of type stub files,
/// which conditionalize type definitions based on the platform.
///
/// If no platform is specified, knot will use `all` or the current platform in the LSP use case.
/// If no platform is specified, knot will use the current platform:
/// - `win32` for Windows
/// - `darwin` for macOS
/// - `android` for Android
/// - `ios` for iOS
/// - `linux` for everything else
#[serde(skip_serializing_if = "Option::is_none")]
pub python_platform: Option<RangedValue<PythonPlatform>>,

View File

@@ -2,7 +2,7 @@
References:
- <https://typing.readthedocs.io/en/latest/spec/callables.html#callable>
- <https://typing.python.org/en/latest/spec/callables.html#callable>
Note that `typing.Callable` is deprecated at runtime, in favour of `collections.abc.Callable` (see:
<https://docs.python.org/3/library/typing.html#deprecated-aliases>). However, removal of
@@ -299,4 +299,4 @@ def _(c: Callable[[int], int]):
reveal_type(c.__call__) # revealed: Unknown
```
[gradual form]: https://typing.readthedocs.io/en/latest/spec/glossary.html#term-gradual-form
[gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form

View File

@@ -2,7 +2,7 @@
In order to support common use cases, an annotation of `float` actually means `int | float`, and an
annotation of `complex` actually means `int | float | complex`. See
[the specification](https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex)
[the specification](https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex)
## float

View File

@@ -1,6 +1,6 @@
# Literal
<https://typing.readthedocs.io/en/latest/spec/literal.html#literals>
<https://typing.python.org/en/latest/spec/literal.html#literals>
## Parameterization

View File

@@ -147,4 +147,4 @@ def f():
reveal_type(x) # revealed: LiteralString
```
[1]: https://typing.readthedocs.io/en/latest/spec/literal.html#literalstring
[1]: https://typing.python.org/en/latest/spec/literal.html#literalstring

View File

@@ -8,7 +8,11 @@ Currently, red-knot doesn't support `typing.NewType` in type annotations.
from typing_extensions import NewType
from types import GenericAlias
X = GenericAlias(type, ())
A = NewType("A", int)
# TODO: typeshed for `typing.GenericAlias` uses `type` for the first argument. `NewType` should be special-cased
# to be compatible with `type`
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `type`, found `NewType`"
B = GenericAlias(A, ())
def _(

View File

@@ -2,7 +2,7 @@
## Annotation
`typing.Union` can be used to construct union types same as `|` operator.
`typing.Union` can be used to construct union types in the same way as the `|` operator.
```py
from typing import Union
@@ -69,3 +69,20 @@ from typing import Union
def f(x: Union) -> None:
reveal_type(x) # revealed: Unknown
```
## Implicit type aliases using new-style unions
We don't recognise these as type aliases yet, but we also don't emit false-positive diagnostics if
you use them in type expressions:
```toml
[environment]
python-version = "3.10"
```
```py
X = int | str
def f(y: X):
reveal_type(y) # revealed: @Todo(Support for `types.UnionType` instances in type expressions)
```

View File

@@ -41,8 +41,7 @@ reveal_type(c_instance.declared_only) # revealed: bytes
reveal_type(c_instance.declared_and_bound) # revealed: bool
# We probably don't want to emit a diagnostic for this being possibly undeclared/unbound.
# mypy and pyright do not show an error here.
# error: [possibly-unbound-attribute]
reveal_type(c_instance.possibly_undeclared_unbound) # revealed: str
# This assignment is fine, as we infer `Unknown | Literal[1, "a"]` for `inferred_from_value`.
@@ -339,8 +338,10 @@ class C:
for self.z in NonIterable():
pass
# Iterable might be empty
# error: [possibly-unbound-attribute]
reveal_type(C().x) # revealed: Unknown | int
# error: [possibly-unbound-attribute]
reveal_type(C().y) # revealed: Unknown | str
```
@@ -409,8 +410,8 @@ reveal_type(c_instance.a) # revealed: Unknown
#### Conditionally declared / bound attributes
We currently do not raise a diagnostic or change behavior if an attribute is only conditionally
defined. This is consistent with what mypy and pyright do.
Attributes are possibly unbound if they, or the method to which they are added are conditionally
declared / bound.
```py
def flag() -> bool:
@@ -428,9 +429,13 @@ class C:
c_instance = C()
# error: [possibly-unbound-attribute]
reveal_type(c_instance.a1) # revealed: str | None
# error: [possibly-unbound-attribute]
reveal_type(c_instance.a2) # revealed: str | None
# error: [possibly-unbound-attribute]
reveal_type(c_instance.b1) # revealed: Unknown | Literal[1]
# error: [possibly-unbound-attribute]
reveal_type(c_instance.b2) # revealed: Unknown | Literal[1]
```
@@ -539,10 +544,88 @@ class C:
if (2 + 3) < 4:
self.x: str = "a"
# TODO: Ideally, this would result in a `unresolved-attribute` error. But mypy and pyright
# do not support this either (for conditions that can only be resolved to `False` in type
# inference), so it does not seem to be particularly important.
reveal_type(C().x) # revealed: str
# error: [unresolved-attribute]
reveal_type(C().x) # revealed: Unknown
```
```py
class C:
def __init__(self, cond: bool) -> None:
if True:
self.a = 1
else:
self.a = "a"
if False:
self.b = 2
if cond:
return
self.c = 3
self.d = 4
self.d = 5
def set_c(self, c: str) -> None:
self.c = c
if False:
def set_e(self, e: str) -> None:
self.e = e
reveal_type(C(True).a) # revealed: Unknown | Literal[1]
# error: [unresolved-attribute]
reveal_type(C(True).b) # revealed: Unknown
reveal_type(C(True).c) # revealed: Unknown | Literal[3] | str
# TODO: this attribute is possibly unbound
reveal_type(C(True).d) # revealed: Unknown | Literal[5]
# error: [unresolved-attribute]
reveal_type(C(True).e) # revealed: Unknown
```
#### Attributes considered always bound
```py
class C:
def __init__(self, cond: bool):
self.x = 1
if cond:
raise ValueError("Something went wrong")
# We consider this attribute is always bound.
# This is because, it is not possible to access a partially-initialized object by normal means.
self.y = 2
reveal_type(C(False).x) # revealed: Unknown | Literal[1]
reveal_type(C(False).y) # revealed: Unknown | Literal[2]
class C:
def __init__(self, b: bytes) -> None:
self.b = b
try:
s = b.decode()
except UnicodeDecodeError:
raise ValueError("Invalid UTF-8 sequence")
self.s = s
reveal_type(C(b"abc").b) # revealed: Unknown | bytes
reveal_type(C(b"abc").s) # revealed: Unknown | str
class C:
def __init__(self, iter) -> None:
self.x = 1
for _ in iter:
pass
# The for-loop may not stop,
# but we consider the subsequent attributes to be definitely-bound.
self.y = 2
reveal_type(C([]).x) # revealed: Unknown | Literal[1]
reveal_type(C([]).y) # revealed: Unknown | Literal[2]
```
#### Diagnostics are reported for the right-hand side of attribute assignments
@@ -1046,13 +1129,18 @@ def _(flag: bool):
def __init(self):
if flag:
self.x = 1
self.y = "a"
else:
self.y = "b"
# Emitting a diagnostic in a case like this is not something we support, and it's unclear
# if we ever will (or want to)
# error: [possibly-unbound-attribute]
reveal_type(Foo().x) # revealed: Unknown | Literal[1]
# Same here
# error: [possibly-unbound-attribute]
Foo().x = 2
reveal_type(Foo().y) # revealed: Unknown | Literal["a", "b"]
Foo().y = "c"
```
### Unions with all paths unbound
@@ -1395,6 +1483,59 @@ def _(ns: argparse.Namespace):
reveal_type(ns.whatever) # revealed: Any
```
## Classes with custom `__setattr__` methods
### Basic
If a type provides a custom `__setattr__` method, we use the parameter type of that method as the
type to validate attribute assignments. Consider the following `CustomSetAttr` class:
```py
class CustomSetAttr:
def __setattr__(self, name: str, value: int) -> None:
pass
```
We can set arbitrary attributes on instances of this class:
```py
c = CustomSetAttr()
c.whatever = 42
```
### Type of the `name` parameter
If the `name` parameter of the `__setattr__` method is annotated with a (union of) literal type(s),
we only consider the attribute assignment to be valid if the assigned attribute is one of them:
```py
from typing import Literal
class Date:
def __setattr__(self, name: Literal["day", "month", "year"], value: int) -> None:
pass
date = Date()
date.day = 8
date.month = 4
date.year = 2025
# error: [unresolved-attribute] "Can not assign object of `Literal["UTC"]` to attribute `tz` on type `Date` with custom `__setattr__` method."
date.tz = "UTC"
```
### `argparse.Namespace`
A standard library example of a class with a custom `__setattr__` method is `argparse.Namespace`:
```py
import argparse
def _(ns: argparse.Namespace):
ns.whatever = 42
```
## Objects of all types have a `__class__` method
The type of `x.__class__` is the same as `x`'s meta-type. `x.__class__` is always the same value as
@@ -1747,5 +1888,5 @@ Some of the tests in the *Class and instance variables* section draw inspiration
[descriptor protocol tests]: descriptor_protocol.md
[pyright's documentation]: https://microsoft.github.io/pyright/#/type-concepts-advanced?id=class-and-instance-variables
[typing spec on `classvar`]: https://typing.readthedocs.io/en/latest/spec/class-compat.html#classvar
[typing spec on `classvar`]: https://typing.python.org/en/latest/spec/class-compat.html#classvar
[`typing.classvar`]: https://docs.python.org/3/library/typing.html#typing.ClassVar

View File

@@ -9,8 +9,8 @@ def _(c: Callable[[], int]):
def _(c: Callable[[int, str], int]):
reveal_type(c(1, "a")) # revealed: int
# error: [invalid-argument-type] "Object of type `Literal["a"]` cannot be assigned to parameter 1; expected type `int`"
# error: [invalid-argument-type] "Object of type `Literal[1]` cannot be assigned to parameter 2; expected type `str`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["a"]`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[1]`"
reveal_type(c("a", 1)) # revealed: int
```

View File

@@ -26,7 +26,11 @@ reveal_type(type(1)) # revealed: Literal[int]
But a three-argument call to type creates a dynamic instance of the `type` class:
```py
class Base: ...
reveal_type(type("Foo", (), {})) # revealed: type
reveal_type(type("Foo", (Base,), {"attr": 1})) # revealed: type
```
Other numbers of arguments are invalid
@@ -39,6 +43,24 @@ type("Foo", ())
type("Foo", (), {}, weird_other_arg=42)
```
The following calls are also invalid, due to incorrect argument types:
```py
class Base: ...
# error: [no-matching-overload] "No overload of class `type` matches arguments"
type(b"Foo", (), {})
# error: [no-matching-overload] "No overload of class `type` matches arguments"
type("Foo", Base, {})
# TODO: this should be an error
type("Foo", (1, 2), {})
# TODO: this should be an error
type("Foo", (Base,), {b"attr": 1})
```
## Calls to `str()`
### Valid calls

View File

@@ -85,7 +85,7 @@ class C:
c = C()
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter 2 (`x`) of bound method `__call__`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(c("foo")) # revealed: int
```
@@ -99,7 +99,7 @@ class C:
c = C()
# error: 13 [invalid-argument-type] "Object of type `C` cannot be assigned to parameter 1 (`self`) of bound method `__call__`; expected type `int`"
# error: 13 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `C`"
reveal_type(c()) # revealed: int
```

View File

@@ -1,7 +1,325 @@
# Constructor
When classes are instantiated, Python calls the meta-class `__call__` method, which can either be
customized by the user or `type.__call__` is used.
The latter calls the `__new__` method of the class, which is responsible for creating the instance
and then calls the `__init__` method on the resulting instance to initialize it with the same
arguments.
Both `__new__` and `__init__` are looked up using full descriptor protocol, but `__new__` is then
called as an implicit static, rather than bound method with `cls` passed as the first argument.
`__init__` has no special handling, it is fetched as bound method and is called just like any other
dunder method.
`type.__call__` does other things too, but this is not yet handled by us.
Since every class has `object` in it's MRO, the default implementations are `object.__new__` and
`object.__init__`. They have some special behavior, namely:
- If neither `__new__` nor `__init__` are defined anywhere in the MRO of class (except for `object`)
\- no arguments are accepted and `TypeError` is raised if any are passed.
- If `__new__` is defined, but `__init__` is not - `object.__init__` will allow arbitrary arguments!
As of today there are a number of behaviors that we do not support:
- `__new__` is assumed to return an instance of the class on which it is called
- User defined `__call__` on metaclass is ignored
## Creating an instance of the `object` class itself
Test the behavior of the `object` class itself. As implementation has to ignore `object` own methods
as defined in typeshed due to behavior not expressible in typeshed (see above how `__init__` behaves
differently depending on whether `__new__` is defined or not), we have to test the behavior of
`object` itself.
```py
reveal_type(object()) # revealed: object
# error: [too-many-positional-arguments] "Too many positional arguments to class `object`: expected 0, got 1"
reveal_type(object(1)) # revealed: object
```
## No init or new
```py
class Foo: ...
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 0, got 1"
reveal_type(Foo(1)) # revealed: Foo
```
## `__new__` present on the class itself
```py
class Foo:
def __new__(cls, x: int) -> "Foo":
return object.__new__(cls)
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```
## `__new__` present on a superclass
If the `__new__` method is defined on a superclass, we can still infer the signature of the
constructor from it.
```py
from typing_extensions import Self
class Base:
def __new__(cls, x: int) -> Self: ...
class Foo(Base): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```
## Conditional `__new__`
```py
def _(flag: bool) -> None:
class Foo:
if flag:
def __new__(cls, x: int): ...
else:
def __new__(cls, x: int, y: int = 1): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["1"]`"
reveal_type(Foo("1")) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```
## A descriptor in place of `__new__`
```py
class SomeCallable:
def __call__(self, cls, x: int) -> "Foo":
obj = object.__new__(cls)
obj.x = x
return obj
class Descriptor:
def __get__(self, instance, owner) -> SomeCallable:
return SomeCallable()
class Foo:
__new__: Descriptor = Descriptor()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
```
## A callable instance in place of `__new__`
### Bound
```py
class Callable:
def __call__(self, cls, x: int) -> "Foo":
return object.__new__(cls)
class Foo:
__new__ = Callable()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
```
### Possibly Unbound
```py
def _(flag: bool) -> None:
class Callable:
if flag:
def __call__(self, cls, x: int) -> "Foo":
return object.__new__(cls)
class Foo:
__new__ = Callable()
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
reveal_type(Foo(1)) # revealed: Foo
# TODO should be - error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
# but we currently infer the signature of `__call__` as unknown, so it accepts any arguments
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
reveal_type(Foo()) # revealed: Foo
```
## `__init__` present on the class itself
If the class has an `__init__` method, we can infer the signature of the constructor from it.
```py
class Foo:
def __init__(self, x: int): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```
## `__init__` present on a superclass
If the `__init__` method is defined on a superclass, we can still infer the signature of the
constructor from it.
```py
class Base:
def __init__(self, x: int): ...
class Foo(Base): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```
## Conditional `__init__`
```py
def _(flag: bool) -> None:
class Foo:
if flag:
def __init__(self, x: int): ...
else:
def __init__(self, x: int, y: int = 1): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["1"]`"
reveal_type(Foo("1")) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```
## A descriptor in place of `__init__`
```py
class SomeCallable:
# TODO: at runtime `__init__` is checked to return `None` and
# a `TypeError` is raised if it doesn't. However, apparently
# this is not true when the descriptor is used as `__init__`.
# However, we may still want to check this.
def __call__(self, x: int) -> str:
return "a"
class Descriptor:
def __get__(self, instance, owner) -> SomeCallable:
return SomeCallable()
class Foo:
__init__: Descriptor = Descriptor()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
```
## A callable instance in place of `__init__`
### Bound
```py
class Callable:
def __call__(self, x: int) -> None:
pass
class Foo:
__init__ = Callable()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
```
### Possibly Unbound
```py
def _(flag: bool) -> None:
class Callable:
if flag:
def __call__(self, x: int) -> None:
pass
class Foo:
__init__ = Callable()
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
reveal_type(Foo(1)) # revealed: Foo
# TODO should be - error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
# but we currently infer the signature of `__call__` as unknown, so it accepts any arguments
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
reveal_type(Foo()) # revealed: Foo
```
## `__new__` and `__init__` both present
### Identical signatures
A common case is to have `__new__` and `__init__` with identical signatures (except for the first
argument). We report errors for both `__new__` and `__init__` if the arguments are incorrect.
At runtime `__new__` is called first and will fail without executing `__init__` if the arguments are
incorrect. However, we decided that it is better to report errors for both methods, since after
fixing the `__new__` method, the user may forget to fix the `__init__` method.
```py
class Foo:
def __new__(cls, x: int) -> "Foo":
return object.__new__(cls)
def __init__(self, x: int): ...
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
reveal_type(Foo(1)) # revealed: Foo
```
### Compatible signatures
But they can also be compatible, but not identical. We should correctly report errors only for the
mthod that would fail.
```py
class Foo:
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
def __init__(self, x: int) -> None:
self.x = x
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
reveal_type(Foo(1)) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
reveal_type(Foo(1, 2)) # revealed: Foo
```

View File

@@ -72,7 +72,7 @@ def _(flag: bool):
def f(x: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter 1 (`x`) of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo")) # revealed: int
```
@@ -82,7 +82,7 @@ reveal_type(f("foo")) # revealed: int
def f(x: int, /) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter 1 (`x`) of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo")) # revealed: int
```
@@ -92,7 +92,7 @@ reveal_type(f("foo")) # revealed: int
def f(*args: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter `*args` of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo")) # revealed: int
```
@@ -102,7 +102,7 @@ reveal_type(f("foo")) # revealed: int
def f(x: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter `x` of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo")) # revealed: int
```
@@ -112,7 +112,7 @@ reveal_type(f(x="foo")) # revealed: int
def f(*, x: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter `x` of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo")) # revealed: int
```
@@ -122,7 +122,7 @@ reveal_type(f(x="foo")) # revealed: int
def f(**kwargs: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter `**kwargs` of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo")) # revealed: int
```
@@ -132,8 +132,8 @@ reveal_type(f(x="foo")) # revealed: int
def f(x: int = 1, y: str = "foo") -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal[2]` cannot be assigned to parameter `y` of function `f`; expected type `str`"
# error: 20 [invalid-argument-type] "Object of type `Literal["bar"]` cannot be assigned to parameter `x` of function `f`; expected type `int`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[2]`"
# error: 20 [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["bar"]`"
reveal_type(f(y=2, x="bar")) # revealed: int
```

View File

@@ -56,7 +56,7 @@ We can access attributes on objects of all kinds:
```py
import sys
reveal_type(inspect.getattr_static(sys, "platform")) # revealed: LiteralString
reveal_type(inspect.getattr_static(sys, "dont_write_bytecode")) # revealed: bool
reveal_type(inspect.getattr_static(inspect, "getattr_static")) # revealed: Literal[getattr_static]
reveal_type(inspect.getattr_static(1, "real")) # revealed: property
@@ -114,7 +114,7 @@ inspect.getattr_static()
# error: [missing-argument] "No argument provided for required parameter `attr`"
inspect.getattr_static(C())
# error: [invalid-argument-type] "Object of type `Literal[1]` cannot be assigned to parameter 2 (`attr`) of function `getattr_static`; expected type `str`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[1]`"
inspect.getattr_static(C(), 1)
# error: [too-many-positional-arguments] "Too many positional arguments to function `getattr_static`: expected 3, got 4"

View File

@@ -24,7 +24,7 @@ to the valid order:
def f(**kw: int, x: str) -> int:
return 1
# error: 15 [invalid-argument-type] "Object of type `Literal[1]` cannot be assigned to parameter 1 (`x`) of function `f`; expected type `str`"
# error: 15 [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[1]`"
reveal_type(f(1)) # revealed: int
```
@@ -38,7 +38,7 @@ def f(x: int = 1, y: str) -> int:
return 1
reveal_type(f(y="foo")) # revealed: int
# error: [invalid-argument-type] "Object of type `Literal["foo"]` cannot be assigned to parameter 1 (`x`) of function `f`; expected type `int`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["foo"]`"
# error: [missing-argument] "No argument provided for required parameter `y` of function `f`"
reveal_type(f("foo")) # revealed: int
```

View File

@@ -350,7 +350,7 @@ class D:
# This function is wrongly annotated, it should be `type[D]` instead of `D`
pass
# error: [invalid-argument-type] "Object of type `Literal[D]` cannot be assigned to parameter 1 (`cls`) of bound method `f`; expected type `D`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `D`, found `Literal[D]`"
D.f()
```

View File

@@ -0,0 +1,50 @@
# `str.startswith`
We special-case `str.startswith` to allow inference of precise Boolean literal types, because those
are used in [`sys.platform` checks].
```py
reveal_type("abc".startswith("")) # revealed: Literal[True]
reveal_type("abc".startswith("a")) # revealed: Literal[True]
reveal_type("abc".startswith("ab")) # revealed: Literal[True]
reveal_type("abc".startswith("abc")) # revealed: Literal[True]
reveal_type("abc".startswith("abcd")) # revealed: Literal[False]
reveal_type("abc".startswith("bc")) # revealed: Literal[False]
reveal_type("AbC".startswith("")) # revealed: Literal[True]
reveal_type("AbC".startswith("A")) # revealed: Literal[True]
reveal_type("AbC".startswith("Ab")) # revealed: Literal[True]
reveal_type("AbC".startswith("AbC")) # revealed: Literal[True]
reveal_type("AbC".startswith("a")) # revealed: Literal[False]
reveal_type("AbC".startswith("aB")) # revealed: Literal[False]
reveal_type("".startswith("")) # revealed: Literal[True]
reveal_type("".startswith(" ")) # revealed: Literal[False]
```
Make sure that we fall back to `bool` for more complex cases:
```py
reveal_type("abc".startswith("b", 1)) # revealed: bool
reveal_type("abc".startswith("bc", 1, 3)) # revealed: bool
reveal_type("abc".startswith(("a", "x"))) # revealed: bool
```
And similiarly, we should still infer `bool` if the instance or the prefix are not string literals:
```py
from typing_extensions import LiteralString
def _(string_instance: str, literalstring: LiteralString):
reveal_type(string_instance.startswith("a")) # revealed: bool
reveal_type(literalstring.startswith("a")) # revealed: bool
reveal_type("a".startswith(string_instance)) # revealed: bool
reveal_type("a".startswith(literalstring)) # revealed: bool
```
[`sys.platform` checks]: https://docs.python.org/3/library/sys.html#sys.platform

View File

@@ -20,9 +20,11 @@ class C:
def _(subclass_of_c: type[C]):
reveal_type(subclass_of_c(1)) # revealed: C
# TODO: Those should all be errors
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["a"]`"
reveal_type(subclass_of_c("a")) # revealed: C
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(subclass_of_c()) # revealed: C
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
reveal_type(subclass_of_c(1, 2)) # revealed: C
```

View File

@@ -94,7 +94,7 @@ def _(flag: bool):
else:
f = f2
# error: [invalid-argument-type] "Object of type `Literal[3]` cannot be assigned to parameter 1 (`a`) of function `f2`; expected type `str`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[3]`"
x = f(3)
reveal_type(x) # revealed: int | str
```

View File

@@ -207,7 +207,7 @@ first argument:
def wrong_signature(f: int) -> str:
return "a"
# error: [invalid-argument-type] "Object of type `Literal[f]` cannot be assigned to parameter 1 (`f`) of function `wrong_signature`; expected type `int`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal[f]`"
@wrong_signature
def f(x): ...

View File

@@ -0,0 +1,106 @@
# `assert_never`
## Basic functionality
`assert_never` makes sure that the type of the argument is `Never`. If it is not, a
`type-assertion-failure` diagnostic is emitted.
```py
from typing_extensions import assert_never, Never, Any
from knot_extensions import Unknown
def _(never: Never, any_: Any, unknown: Unknown, flag: bool):
assert_never(never) # fine
assert_never(0) # error: [type-assertion-failure]
assert_never("") # error: [type-assertion-failure]
assert_never(None) # error: [type-assertion-failure]
assert_never([]) # error: [type-assertion-failure]
assert_never({}) # error: [type-assertion-failure]
assert_never(()) # error: [type-assertion-failure]
assert_never(1 if flag else never) # error: [type-assertion-failure]
assert_never(any_) # error: [type-assertion-failure]
assert_never(unknown) # error: [type-assertion-failure]
```
## Use case: Type narrowing and exhaustiveness checking
`assert_never` can be used in combination with type narrowing as a way to make sure that all cases
are handled in a series of `isinstance` checks or other narrowing patterns that are supported.
```py
from typing_extensions import assert_never, Literal
class A: ...
class B: ...
class C: ...
def if_else_isinstance_success(obj: A | B):
if isinstance(obj, A):
pass
elif isinstance(obj, B):
pass
elif isinstance(obj, C):
pass
else:
assert_never(obj)
def if_else_isinstance_error(obj: A | B):
if isinstance(obj, A):
pass
# B is missing
elif isinstance(obj, C):
pass
else:
# error: [type-assertion-failure] "Expected type `Never`, got `B & ~A & ~C` instead"
assert_never(obj)
def if_else_singletons_success(obj: Literal[1, "a"] | None):
if obj == 1:
pass
elif obj == "a":
pass
elif obj is None:
pass
else:
assert_never(obj)
def if_else_singletons_error(obj: Literal[1, "a"] | None):
if obj == 1:
pass
elif obj is "A": # "A" instead of "a"
pass
elif obj is None:
pass
else:
# error: [type-assertion-failure] "Expected type `Never`, got `Literal["a"]` instead"
assert_never(obj)
def match_singletons_success(obj: Literal[1, "a"] | None):
match obj:
case 1:
pass
case "a":
pass
case None:
pass
case _ as obj:
# TODO: Ideally, we would not emit an error here
# error: [type-assertion-failure] "Expected type `Never`, got `@Todo"
assert_never(obj)
def match_singletons_error(obj: Literal[1, "a"] | None):
match obj:
case 1:
pass
case "A": # "A" instead of "a"
pass
case None:
pass
case _ as obj:
# TODO: We should emit an error here, but the message should
# show the type `Literal["a"]` instead of `@Todo(…)`.
# error: [type-assertion-failure] "Expected type `Never`, got `@Todo"
assert_never(obj)
```

View File

@@ -49,3 +49,22 @@ def f(x: Callable[[dict[str, int]], None], y: tuple[dict[str, int]]):
a = cast(Callable[[list[bytes]], None], x)
b = cast(tuple[list[bytes]], y)
```
A cast from `Todo` or `Unknown` to `Any` is not considered a "redundant cast": even if these are
understood as gradually equivalent types by red-knot, they are understood as different types by
human readers of red-knot's output. For `Unknown` in particular, we may consider it differently in
the context of some opt-in diagnostics, as it indicates that the gradual type has come about due to
an invalid annotation, missing annotation or missing type argument somewhere.
```py
from knot_extensions import Unknown
def f(x: Any, y: Unknown, z: Any | str | int):
a = cast(dict[str, Any], x)
reveal_type(a) # revealed: @Todo(generics)
b = cast(Any, y)
reveal_type(b) # revealed: Any
c = cast(str | int | Any, z) # error: [redundant-cast]
```

View File

@@ -42,7 +42,7 @@ def f(w: Wrapper) -> None:
v: int | None = w.value
# This function call is incorrect, because `w.value` could be `None`. We therefore emit the following
# error: "`Unknown | None` cannot be assigned to parameter 1 (`i`) of function `accepts_int`; expected type `int`"
# error: "Argument to this function is incorrect: Expected `int`, found `Unknown | None`"
c = accepts_int(w.value)
```
@@ -122,4 +122,4 @@ class Wrapper:
reveal_type(Wrapper.value) # revealed: Unknown | None
```
[gradual guarantee]: https://typing.readthedocs.io/en/latest/spec/concepts.html#the-gradual-guarantee
[gradual guarantee]: https://typing.python.org/en/latest/spec/concepts.html#the-gradual-guarantee

View File

@@ -73,3 +73,42 @@ from typing import Any
def g(x: Any = "foo"):
reveal_type(x) # revealed: Any | Literal["foo"]
```
## Stub functions
### In Protocol
```py
from typing import Protocol
class Foo(Protocol):
def x(self, y: bool = ...): ...
def y[T](self, y: T = ...) -> T: ...
class GenericFoo[T](Protocol):
def x(self, y: bool = ...) -> T: ...
```
### In abstract method
```py
from abc import abstractmethod
class Bar:
@abstractmethod
def x(self, y: bool = ...): ...
@abstractmethod
def y[T](self, y: T = ...) -> T: ...
```
### In function overload
```py
from typing import overload
@overload
def x(y: None = ...) -> None: ...
@overload
def x(y: int) -> str: ...
def x(y: int | None = None) -> str | None: ...
```

View File

@@ -310,7 +310,7 @@ def f(cond: bool) -> str:
return "hello" if cond else NotImplemented
def f(cond: bool) -> int:
# error: [invalid-return-type] "Object of type `Literal["hello"]` is not assignable to return type `int`"
# error: [invalid-return-type] "Return type does not match returned value: Expected `int`, found `Literal["hello"]`"
return "hello" if cond else NotImplemented
```

View File

@@ -13,8 +13,6 @@ class C[T]: ...
A class that inherits from a generic class, and fills its type parameters with typevars, is generic:
```py
# TODO: no error
# error: [non-subscriptable]
class D[U](C[U]): ...
```
@@ -22,8 +20,6 @@ A class that inherits from a generic class, but fills its type parameters with c
_not_ generic:
```py
# TODO: no error
# error: [non-subscriptable]
class E(C[int]): ...
```
@@ -57,7 +53,7 @@ class D(C[T]): ...
(Examples `E` and `F` from above do not have analogues in the legacy syntax.)
## Inferring generic class parameters
## Specializing generic classes explicitly
The type parameter can be specified explicitly:
@@ -65,25 +61,77 @@ The type parameter can be specified explicitly:
class C[T]:
x: T
# TODO: no error
# TODO: revealed: C[int]
# error: [non-subscriptable]
reveal_type(C[int]()) # revealed: C
reveal_type(C[int]()) # revealed: C[int]
```
The specialization must match the generic types:
```py
# error: [too-many-positional-arguments] "Too many positional arguments to class `C`: expected 1, got 2"
reveal_type(C[int, int]()) # revealed: Unknown
```
If the type variable has an upper bound, the specialized type must satisfy that bound:
```py
class Bounded[T: int]: ...
class BoundedByUnion[T: int | str]: ...
class IntSubclass(int): ...
reveal_type(Bounded[int]()) # revealed: Bounded[int]
reveal_type(Bounded[IntSubclass]()) # revealed: Bounded[IntSubclass]
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `str`"
reveal_type(Bounded[str]()) # revealed: Unknown
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `int | str`"
reveal_type(Bounded[int | str]()) # revealed: Unknown
reveal_type(BoundedByUnion[int]()) # revealed: BoundedByUnion[int]
reveal_type(BoundedByUnion[IntSubclass]()) # revealed: BoundedByUnion[IntSubclass]
reveal_type(BoundedByUnion[str]()) # revealed: BoundedByUnion[str]
reveal_type(BoundedByUnion[int | str]()) # revealed: BoundedByUnion[int | str]
```
If the type variable is constrained, the specialized type must satisfy those constraints:
```py
class Constrained[T: (int, str)]: ...
reveal_type(Constrained[int]()) # revealed: Constrained[int]
# TODO: error: [invalid-argument-type]
# TODO: revealed: Constrained[Unknown]
reveal_type(Constrained[IntSubclass]()) # revealed: Constrained[IntSubclass]
reveal_type(Constrained[str]()) # revealed: Constrained[str]
# TODO: error: [invalid-argument-type]
# TODO: revealed: Unknown
reveal_type(Constrained[int | str]()) # revealed: Constrained[int | str]
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int | str`, found `object`"
reveal_type(Constrained[object]()) # revealed: Unknown
```
## Inferring generic class parameters
We can infer the type parameter from a type context:
```py
class C[T]:
x: T
c: C[int] = C()
# TODO: revealed: C[int]
reveal_type(c) # revealed: C
reveal_type(c) # revealed: C[Unknown]
```
The typevars of a fully specialized generic class should no longer be visible:
```py
# TODO: revealed: int
reveal_type(c.x) # revealed: T
reveal_type(c.x) # revealed: Unknown
```
If the type parameter is not specified explicitly, and there are no constraints that let us infer a
@@ -92,15 +140,13 @@ specific type, we infer the typevar's default type:
```py
class D[T = int]: ...
# TODO: revealed: D[int]
reveal_type(D()) # revealed: D
reveal_type(D()) # revealed: D[int]
```
If a typevar does not provide a default, we use `Unknown`:
```py
# TODO: revealed: C[Unknown]
reveal_type(C()) # revealed: C
reveal_type(C()) # revealed: C[Unknown]
```
If the type of a constructor parameter is a class typevar, we can use that to infer the type
@@ -111,14 +157,14 @@ class E[T]:
def __init__(self, x: T) -> None: ...
# TODO: revealed: E[int] or E[Literal[1]]
reveal_type(E(1)) # revealed: E
reveal_type(E(1)) # revealed: E[Unknown]
```
The types inferred from a type context and from a constructor parameter must be consistent with each
other:
```py
# TODO: error
# TODO: error: [invalid-argument-type]
wrong_innards: E[int] = E("five")
```
@@ -131,17 +177,33 @@ propagate through:
class Base[T]:
x: T | None = None
# TODO: no error
# error: [non-subscriptable]
class Sub[U](Base[U]): ...
reveal_type(Base[int].x) # revealed: int | None
reveal_type(Sub[int].x) # revealed: int | None
```
## Generic methods
Generic classes can contain methods that are themselves generic. The generic methods can refer to
the typevars of the enclosing generic class, and introduce new (distinct) typevars that are only in
scope for the method.
```py
class C[T]:
def method[U](self, u: U) -> U:
return u
# error: [unresolved-reference]
def cannot_use_outside_of_method(self, u: U): ...
# TODO: error
def cannot_shadow_class_typevar[T](self, t: T): ...
c: C[int] = C[int]()
# TODO: no error
# TODO: revealed: int | None
# error: [non-subscriptable]
reveal_type(Base[int].x) # revealed: T | None
# TODO: revealed: int | None
# error: [non-subscriptable]
reveal_type(Sub[int].x) # revealed: T | None
# TODO: revealed: str or Literal["string"]
# error: [invalid-argument-type]
reveal_type(c.method("string")) # revealed: U
```
## Cyclic class definition
@@ -155,8 +217,6 @@ Here, `Sub` is not a generic class, since it fills its superclass's type paramet
```pyi
class Base[T]: ...
# TODO: no error
# error: [non-subscriptable]
class Sub(Base[Sub]): ...
reveal_type(Sub) # revealed: Literal[Sub]
@@ -168,9 +228,6 @@ A similar case can work in a non-stub file, if forward references are stringifie
```py
class Base[T]: ...
# TODO: no error
# error: [non-subscriptable]
class Sub(Base["Sub"]): ...
reveal_type(Sub) # revealed: Literal[Sub]
@@ -183,8 +240,6 @@ In a non-stub file, without stringified forward references, this raises a `NameE
```py
class Base[T]: ...
# TODO: the unresolved-reference error is correct, the non-subscriptable is not
# error: [non-subscriptable]
# error: [unresolved-reference]
class Sub(Base[Sub]): ...
```

View File

@@ -107,8 +107,7 @@ def good_return[T: int](x: T) -> T:
return x
def bad_return[T: int](x: T) -> T:
# TODO: error: int is not assignable to T
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T` and `Literal[1]`"
# error: [invalid-return-type] "Return type does not match returned value: Expected `T`, found `int`"
return x + 1
```
@@ -121,7 +120,7 @@ def different_types[T, S](cond: bool, t: T, s: S) -> T:
if cond:
return t
else:
# error: [invalid-return-type] "Object of type `S` is not assignable to return type `T`"
# error: [invalid-return-type] "Return type does not match returned value: Expected `T`, found `S`"
return s
def same_types[T](cond: bool, t1: T, t2: T) -> T:

View File

@@ -69,4 +69,4 @@ from typing import TypeVar
T = TypeVar("T", int)
```
[generics]: https://typing.readthedocs.io/en/latest/spec/generics.html
[generics]: https://typing.python.org/en/latest/spec/generics.html

View File

@@ -48,4 +48,523 @@ class C[T]:
reveal_type(x) # revealed: T
```
## Fully static typevars
We consider a typevar to be fully static unless it has a non-fully-static bound or constraint. This
is true even though a fully static typevar might be specialized to a gradual form like `Any`. (This
is similar to how you can assign an expression whose type is not fully static to a target whose type
is.)
```py
from knot_extensions import is_fully_static, static_assert
from typing import Any
def unbounded_unconstrained[T](t: list[T]) -> None:
static_assert(is_fully_static(T))
def bounded[T: int](t: list[T]) -> None:
static_assert(is_fully_static(T))
def bounded_by_gradual[T: Any](t: list[T]) -> None:
static_assert(not is_fully_static(T))
def constrained[T: (int, str)](t: list[T]) -> None:
static_assert(is_fully_static(T))
def constrained_by_gradual[T: (int, Any)](t: list[T]) -> None:
static_assert(not is_fully_static(T))
```
## Subtyping and assignability
(Note: for simplicity, all of the prose in this section refers to _subtyping_ involving fully static
typevars. Unless otherwise noted, all of the claims also apply to _assignability_ involving gradual
typevars.)
We can make no assumption about what type an unbounded, unconstrained, fully static typevar will be
specialized to. Properties are true of the typevar only if they are true for every valid
specialization. Thus, the typevar is a subtype of itself and of `object`, but not of any other type
(including other typevars).
```py
from knot_extensions import is_assignable_to, is_subtype_of, static_assert
class Super: ...
class Base(Super): ...
class Sub(Base): ...
class Unrelated: ...
def unbounded_unconstrained[T, U](t: list[T], u: list[U]) -> None:
static_assert(is_assignable_to(T, T))
static_assert(is_assignable_to(T, object))
static_assert(not is_assignable_to(T, Super))
static_assert(is_assignable_to(U, U))
static_assert(is_assignable_to(U, object))
static_assert(not is_assignable_to(U, Super))
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))
static_assert(is_subtype_of(T, T))
static_assert(is_subtype_of(T, object))
static_assert(not is_subtype_of(T, Super))
static_assert(is_subtype_of(U, U))
static_assert(is_subtype_of(U, object))
static_assert(not is_subtype_of(U, Super))
static_assert(not is_subtype_of(T, U))
static_assert(not is_subtype_of(U, T))
```
A bounded typevar is assignable to its bound, and a bounded, fully static typevar is a subtype of
its bound. (A typevar with a non-fully-static bound is itself non-fully-static, and therefore does
not participate in subtyping.) A fully static bound is not assignable to, nor a subtype of, the
typevar, since the typevar might be specialized to a smaller type. (This is true even if the bound
is a final class, since the typevar can still be specialized to `Never`.)
```py
from typing import Any
from typing_extensions import final
def bounded[T: Super](t: list[T]) -> None:
static_assert(is_assignable_to(T, Super))
static_assert(not is_assignable_to(T, Sub))
static_assert(not is_assignable_to(Super, T))
static_assert(not is_assignable_to(Sub, T))
static_assert(is_subtype_of(T, Super))
static_assert(not is_subtype_of(T, Sub))
static_assert(not is_subtype_of(Super, T))
static_assert(not is_subtype_of(Sub, T))
def bounded_by_gradual[T: Any](t: list[T]) -> None:
static_assert(is_assignable_to(T, Any))
static_assert(is_assignable_to(Any, T))
static_assert(is_assignable_to(T, Super))
static_assert(not is_assignable_to(Super, T))
static_assert(is_assignable_to(T, Sub))
static_assert(not is_assignable_to(Sub, T))
static_assert(not is_subtype_of(T, Any))
static_assert(not is_subtype_of(Any, T))
static_assert(not is_subtype_of(T, Super))
static_assert(not is_subtype_of(Super, T))
static_assert(not is_subtype_of(T, Sub))
static_assert(not is_subtype_of(Sub, T))
@final
class FinalClass: ...
def bounded_final[T: FinalClass](t: list[T]) -> None:
static_assert(is_assignable_to(T, FinalClass))
static_assert(not is_assignable_to(FinalClass, T))
static_assert(is_subtype_of(T, FinalClass))
static_assert(not is_subtype_of(FinalClass, T))
```
Two distinct fully static typevars are not subtypes of each other, even if they have the same
bounds, since there is (still) no guarantee that they will be specialized to the same type. This is
true even if both typevars are bounded by the same final class, since you can specialize the
typevars to `Never` in addition to that final class.
```py
def two_bounded[T: Super, U: Super](t: list[T], u: list[U]) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))
static_assert(not is_subtype_of(T, U))
static_assert(not is_subtype_of(U, T))
def two_final_bounded[T: FinalClass, U: FinalClass](t: list[T], u: list[U]) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))
static_assert(not is_subtype_of(T, U))
static_assert(not is_subtype_of(U, T))
```
A constrained fully static typevar is assignable to the union of its constraints, but not to any of
the constraints individually. None of the constraints are subtypes of the typevar, though the
intersection of all of its constraints is a subtype of the typevar.
```py
from knot_extensions import Intersection
def constrained[T: (Base, Unrelated)](t: list[T]) -> None:
static_assert(not is_assignable_to(T, Super))
static_assert(not is_assignable_to(T, Base))
static_assert(not is_assignable_to(T, Sub))
static_assert(not is_assignable_to(T, Unrelated))
static_assert(is_assignable_to(T, Super | Unrelated))
static_assert(is_assignable_to(T, Base | Unrelated))
static_assert(not is_assignable_to(T, Sub | Unrelated))
static_assert(not is_assignable_to(Super, T))
static_assert(not is_assignable_to(Unrelated, T))
static_assert(not is_assignable_to(Super | Unrelated, T))
static_assert(is_assignable_to(Intersection[Base, Unrelated], T))
static_assert(not is_subtype_of(T, Super))
static_assert(not is_subtype_of(T, Base))
static_assert(not is_subtype_of(T, Sub))
static_assert(not is_subtype_of(T, Unrelated))
static_assert(is_subtype_of(T, Super | Unrelated))
static_assert(is_subtype_of(T, Base | Unrelated))
static_assert(not is_subtype_of(T, Sub | Unrelated))
static_assert(not is_subtype_of(Super, T))
static_assert(not is_subtype_of(Unrelated, T))
static_assert(not is_subtype_of(Super | Unrelated, T))
static_assert(is_subtype_of(Intersection[Base, Unrelated], T))
def constrained_by_gradual[T: (Base, Any)](t: list[T]) -> None:
static_assert(is_assignable_to(T, Super))
static_assert(is_assignable_to(T, Base))
static_assert(not is_assignable_to(T, Sub))
static_assert(not is_assignable_to(T, Unrelated))
static_assert(is_assignable_to(T, Any))
static_assert(is_assignable_to(T, Super | Any))
static_assert(is_assignable_to(T, Super | Unrelated))
static_assert(not is_assignable_to(Super, T))
static_assert(is_assignable_to(Base, T))
static_assert(not is_assignable_to(Unrelated, T))
static_assert(is_assignable_to(Any, T))
static_assert(not is_assignable_to(Super | Any, T))
static_assert(is_assignable_to(Base | Any, T))
static_assert(not is_assignable_to(Super | Unrelated, T))
static_assert(is_assignable_to(Intersection[Base, Unrelated], T))
static_assert(is_assignable_to(Intersection[Base, Any], T))
static_assert(not is_subtype_of(T, Super))
static_assert(not is_subtype_of(T, Base))
static_assert(not is_subtype_of(T, Sub))
static_assert(not is_subtype_of(T, Unrelated))
static_assert(not is_subtype_of(T, Any))
static_assert(not is_subtype_of(T, Super | Any))
static_assert(not is_subtype_of(T, Super | Unrelated))
static_assert(not is_subtype_of(Super, T))
static_assert(not is_subtype_of(Base, T))
static_assert(not is_subtype_of(Unrelated, T))
static_assert(not is_subtype_of(Any, T))
static_assert(not is_subtype_of(Super | Any, T))
static_assert(not is_subtype_of(Base | Any, T))
static_assert(not is_subtype_of(Super | Unrelated, T))
static_assert(not is_subtype_of(Intersection[Base, Unrelated], T))
static_assert(not is_subtype_of(Intersection[Base, Any], T))
```
Two distinct fully static typevars are not subtypes of each other, even if they have the same
constraints, and even if any of the constraints are final. There must always be at least two
distinct constraints, meaning that there is (still) no guarantee that they will be specialized to
the same type.
```py
def two_constrained[T: (int, str), U: (int, str)](t: list[T], u: list[U]) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))
static_assert(not is_subtype_of(T, U))
static_assert(not is_subtype_of(U, T))
@final
class AnotherFinalClass: ...
def two_final_constrained[T: (FinalClass, AnotherFinalClass), U: (FinalClass, AnotherFinalClass)](t: list[T], u: list[U]) -> None:
static_assert(not is_assignable_to(T, U))
static_assert(not is_assignable_to(U, T))
static_assert(not is_subtype_of(T, U))
static_assert(not is_subtype_of(U, T))
```
## Singletons and single-valued types
(Note: for simplicity, all of the prose in this section refers to _singleton_ types, but all of the
claims also apply to _single-valued_ types.)
An unbounded, unconstrained typevar is not a singleton, because it can be specialized to a
non-singleton type.
```py
from knot_extensions import is_singleton, is_single_valued, static_assert
def unbounded_unconstrained[T](t: list[T]) -> None:
static_assert(not is_singleton(T))
static_assert(not is_single_valued(T))
```
A bounded typevar is not a singleton, even if its bound is a singleton, since it can still be
specialized to `Never`.
```py
def bounded[T: None](t: list[T]) -> None:
static_assert(not is_singleton(T))
static_assert(not is_single_valued(T))
```
A constrained typevar is a singleton if all of its constraints are singletons. (Note that you cannot
specialize a constrained typevar to a subtype of a constraint.)
```py
from typing_extensions import Literal
def constrained_non_singletons[T: (int, str)](t: list[T]) -> None:
static_assert(not is_singleton(T))
static_assert(not is_single_valued(T))
def constrained_singletons[T: (Literal[True], Literal[False])](t: list[T]) -> None:
static_assert(is_singleton(T))
def constrained_single_valued[T: (Literal[True], tuple[()])](t: list[T]) -> None:
static_assert(is_single_valued(T))
```
## Unions involving typevars
The union of an unbounded unconstrained typevar with any other type cannot be simplified, since
there is no guarantee what type the typevar will be specialized to.
```py
from typing import Any
class Super: ...
class Base(Super): ...
class Sub(Base): ...
class Unrelated: ...
def unbounded_unconstrained[T](t: T) -> None:
def _(x: T | Super) -> None:
reveal_type(x) # revealed: T | Super
def _(x: T | Base) -> None:
reveal_type(x) # revealed: T | Base
def _(x: T | Sub) -> None:
reveal_type(x) # revealed: T | Sub
def _(x: T | Unrelated) -> None:
reveal_type(x) # revealed: T | Unrelated
def _(x: T | Any) -> None:
reveal_type(x) # revealed: T | Any
```
The union of a bounded typevar with its bound is that bound. (The typevar is guaranteed to be
specialized to a subtype of the bound.) The union of a bounded typevar with a subtype of its bound
cannot be simplified. (The typevar might be specialized to a different subtype of the bound.)
```py
def bounded[T: Base](t: T) -> None:
def _(x: T | Super) -> None:
reveal_type(x) # revealed: Super
def _(x: T | Base) -> None:
reveal_type(x) # revealed: Base
def _(x: T | Sub) -> None:
reveal_type(x) # revealed: T | Sub
def _(x: T | Unrelated) -> None:
reveal_type(x) # revealed: T | Unrelated
def _(x: T | Any) -> None:
reveal_type(x) # revealed: T | Any
```
The union of a constrained typevar with a type depends on how that type relates to the constraints.
If all of the constraints are a subtype of that type, the union simplifies to that type. Inversely,
if the type is a subtype of every constraint, the union simplifies to the typevar. Otherwise, the
union cannot be simplified.
```py
def constrained[T: (Base, Sub)](t: T) -> None:
def _(x: T | Super) -> None:
reveal_type(x) # revealed: Super
def _(x: T | Base) -> None:
reveal_type(x) # revealed: Base
def _(x: T | Sub) -> None:
reveal_type(x) # revealed: T
def _(x: T | Unrelated) -> None:
reveal_type(x) # revealed: T | Unrelated
def _(x: T | Any) -> None:
reveal_type(x) # revealed: T | Any
```
## Intersections involving typevars
The intersection of an unbounded unconstrained typevar with any other type cannot be simplified,
since there is no guarantee what type the typevar will be specialized to.
```py
from knot_extensions import Intersection
from typing import Any
class Super: ...
class Base(Super): ...
class Sub(Base): ...
class Unrelated: ...
def unbounded_unconstrained[T](t: T) -> None:
def _(x: Intersection[T, Super]) -> None:
reveal_type(x) # revealed: T & Super
def _(x: Intersection[T, Base]) -> None:
reveal_type(x) # revealed: T & Base
def _(x: Intersection[T, Sub]) -> None:
reveal_type(x) # revealed: T & Sub
def _(x: Intersection[T, Unrelated]) -> None:
reveal_type(x) # revealed: T & Unrelated
def _(x: Intersection[T, Any]) -> None:
reveal_type(x) # revealed: T & Any
```
The intersection of a bounded typevar with its bound or a supertype of its bound is the typevar
itself. (The typevar might be specialized to a subtype of the bound.) The intersection of a bounded
typevar with a subtype of its bound cannot be simplified. (The typevar might be specialized to a
different subtype of the bound.) The intersection of a bounded typevar with a type that is disjoint
from its bound is `Never`.
```py
def bounded[T: Base](t: T) -> None:
def _(x: Intersection[T, Super]) -> None:
reveal_type(x) # revealed: T
def _(x: Intersection[T, Base]) -> None:
reveal_type(x) # revealed: T
def _(x: Intersection[T, Sub]) -> None:
reveal_type(x) # revealed: T & Sub
def _(x: Intersection[T, None]) -> None:
reveal_type(x) # revealed: Never
def _(x: Intersection[T, Any]) -> None:
reveal_type(x) # revealed: T & Any
```
Constrained typevars can be modeled using a hypothetical `OneOf` connector, where the typevar must
be specialized to _one_ of its constraints. The typevar is not the _union_ of those constraints,
since that would allow the typevar to take on values from _multiple_ constraints simultaneously. The
`OneOf` connector would not be a “type” according to a strict reading of the typing spec, since it
would not represent a single set of runtime objects; it would instead represent a _set of_ sets of
runtime objects. This is one reason we have not actually added this connector to our data model yet.
Nevertheless, describing constrained typevars this way helps explain how we simplify intersections
involving them.
This means that when intersecting a constrained typevar with a type `T`, constraints that are
supertypes of `T` can be simplified to `T`, since intersection distributes over `OneOf`. Moreover,
constraints that are disjoint from `T` are no longer valid specializations of the typevar, since
`Never` is an identity for `OneOf`. After these simplifications, if only one constraint remains, we
can simplify the intersection as a whole to that constraint.
```py
def constrained[T: (Base, Sub, Unrelated)](t: T) -> None:
def _(x: Intersection[T, Base]) -> None:
# With OneOf this would be OneOf[Base, Sub]
reveal_type(x) # revealed: T & Base
def _(x: Intersection[T, Unrelated]) -> None:
reveal_type(x) # revealed: Unrelated
def _(x: Intersection[T, Sub]) -> None:
reveal_type(x) # revealed: Sub
def _(x: Intersection[T, None]) -> None:
reveal_type(x) # revealed: Never
def _(x: Intersection[T, Any]) -> None:
reveal_type(x) # revealed: T & Any
```
We can simplify the intersection similarly when removing a type from a constrained typevar, since
this is modeled internally as an intersection with a negation.
```py
from knot_extensions import Not
def remove_constraint[T: (int, str, bool)](t: T) -> None:
def _(x: Intersection[T, Not[int]]) -> None:
reveal_type(x) # revealed: str & ~int
def _(x: Intersection[T, Not[str]]) -> None:
# With OneOf this would be OneOf[int, bool]
reveal_type(x) # revealed: T & ~str
def _(x: Intersection[T, Not[bool]]) -> None:
reveal_type(x) # revealed: T & ~bool
def _(x: Intersection[T, Not[int], Not[str]]) -> None:
reveal_type(x) # revealed: Never
def _(x: Intersection[T, Not[None]]) -> None:
reveal_type(x) # revealed: T
def _(x: Intersection[T, Not[Any]]) -> None:
reveal_type(x) # revealed: T & Any
```
## Narrowing
We can use narrowing expressions to eliminate some of the possibilities of a constrained typevar:
```py
class P: ...
class Q: ...
class R: ...
def f[T: (P, Q)](t: T) -> None:
if isinstance(t, P):
reveal_type(t) # revealed: P
p: P = t
else:
reveal_type(t) # revealed: Q & ~P
q: Q = t
if isinstance(t, Q):
reveal_type(t) # revealed: Q
q: Q = t
else:
reveal_type(t) # revealed: P & ~Q
p: P = t
def g[T: (P, Q, R)](t: T) -> None:
if isinstance(t, P):
reveal_type(t) # revealed: P
p: P = t
elif isinstance(t, Q):
reveal_type(t) # revealed: Q & ~P
q: Q = t
else:
reveal_type(t) # revealed: R & ~P & ~Q
r: R = t
if isinstance(t, P):
reveal_type(t) # revealed: P
p: P = t
elif isinstance(t, Q):
reveal_type(t) # revealed: Q & ~P
q: Q = t
elif isinstance(t, R):
reveal_type(t) # revealed: R & ~P & ~Q
r: R = t
else:
reveal_type(t) # revealed: Never
```
If the constraints are disjoint, simplification does eliminate the redundant negative:
```py
def h[T: (P, None)](t: T) -> None:
if t is None:
reveal_type(t) # revealed: None
p: None = t
else:
reveal_type(t) # revealed: P
p: P = t
```
[pep 695]: https://peps.python.org/pep-0695/

View File

@@ -82,18 +82,51 @@ class C[T]:
def m2(self, x: T) -> T:
return x
c: C[int] = C()
# TODO: no error
# error: [invalid-argument-type]
c: C[int] = C[int]()
c.m1(1)
# TODO: no error
# error: [invalid-argument-type]
c.m2(1)
# TODO: expected type `int`
# error: [invalid-argument-type] "Object of type `Literal["string"]` cannot be assigned to parameter 2 (`x`) of bound method `m2`; expected type `T`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["string"]`"
c.m2("string")
```
## Functions on generic classes are descriptors
This repeats the tests in the [Functions as descriptors](./call/methods.md) test suite, but on a
generic class. This ensures that we are carrying any specializations through the entirety of the
descriptor protocol, which is how `self` parameters are bound to instance methods.
```py
from inspect import getattr_static
class C[T]:
def f(self, x: T) -> str:
return "a"
reveal_type(getattr_static(C[int], "f")) # revealed: Literal[f[int]]
reveal_type(getattr_static(C[int], "f").__get__) # revealed: <method-wrapper `__get__` of `f[int]`>
reveal_type(getattr_static(C[int], "f").__get__(None, C[int])) # revealed: Literal[f[int]]
# revealed: <bound method `f` of `C[int]`>
reveal_type(getattr_static(C[int], "f").__get__(C[int](), C[int]))
reveal_type(C[int].f) # revealed: Literal[f[int]]
reveal_type(C[int]().f) # revealed: <bound method `f` of `C[int]`>
bound_method = C[int]().f
reveal_type(bound_method.__self__) # revealed: C[int]
reveal_type(bound_method.__func__) # revealed: Literal[f[int]]
reveal_type(C[int]().f(1)) # revealed: str
reveal_type(bound_method(1)) # revealed: str
C[int].f(1) # error: [missing-argument]
reveal_type(C[int].f(C[int](), 1)) # revealed: str
class D[U](C[U]):
pass
reveal_type(D[int]().f) # revealed: <bound method `f` of `D[int]`>
```
## Methods can mention other typevars
> A type variable used in a method that does not match any of the variables that parameterize the
@@ -127,7 +160,6 @@ c: C[int] = C()
# TODO: no errors
# TODO: revealed: str
# error: [invalid-argument-type]
# error: [invalid-argument-type]
reveal_type(c.m(1, "string")) # revealed: S
```
@@ -267,4 +299,4 @@ class C[T]:
ok2: Inner[T]
```
[scoping]: https://typing.readthedocs.io/en/latest/spec/generics.html#scoping-rules-for-type-variables
[scoping]: https://typing.python.org/en/latest/spec/generics.html#scoping-rules-for-type-variables

View File

@@ -4,7 +4,7 @@ This document describes the conventions for importing symbols.
Reference:
- <https://typing.readthedocs.io/en/latest/spec/distributing.html#import-conventions>
- <https://typing.python.org/en/latest/spec/distributing.html#import-conventions>
## Builtins scope

View File

@@ -236,3 +236,36 @@ X: int = 42
```py
from .parser import X # error: [unresolved-import]
```
## Relative imports in `site-packages`
Relative imports in `site-packages` are correctly resolved even when the `site-packages` search path
is a subdirectory of the first-party search path. Note that mdtest sets the first-party search path
to `/src/`, which is why the virtual environment in this test is a subdirectory of `/src/`, even
though this is not how a typical Python project would be structured:
```toml
[environment]
python = "/src/.venv"
python-version = "3.13"
```
`/src/bar.py`:
```py
from foo import A
reveal_type(A) # revealed: Literal[A]
```
`/src/.venv/<path-to-site-packages>/foo/__init__.py`:
```py
from .a import A as A
```
`/src/.venv/<path-to-site-packages>/foo/a.py`:
```py
class A: ...
```

View File

@@ -6,52 +6,52 @@ See the [Python language reference for import statements].
### A simple `*` import
`a.py`:
`exporter.py`:
```py
X: bool = True
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: bool
print(Y) # error: [unresolved-reference]
```
### Overriding existing definition
### Overriding an existing definition
`a.py`:
`exporter.py`:
```py
X: bool = True
```
`b.py`:
`importer.py`:
```py
X = 42
reveal_type(X) # revealed: Literal[42]
from a import *
from exporter import *
reveal_type(X) # revealed: bool
```
### Overridden by later definition
### Overridden by a later definition
`a.py`:
`exporter.py`:
```py
X: bool = True
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: bool
X = False
@@ -78,7 +78,7 @@ from a import *
from b import *
```
`d.py`:
`main.py`:
```py
from c import *
@@ -88,6 +88,9 @@ reveal_type(X) # revealed: bool
### A wildcard import constitutes a re-export
This is specified
[here](https://typing.python.org/en/latest/spec/distributing.html#import-conventions).
`a.pyi`:
```pyi
@@ -107,7 +110,7 @@ from a import *
from b import Y
```
`d.py`:
`main.py`:
```py
# `X` is accessible because the `*` import in `c` re-exports it from `c`
@@ -117,9 +120,15 @@ from c import X
from c import Y # error: [unresolved-import]
```
## Esoteric definitions and redefinintions
We understand all public symbols defined in an external module as being imported by a `*` import,
not just those that are defined in `StmtAssign` nodes and `StmtAnnAssign` nodes. This section
provides tests for definitions, and redefinitions, that use more esoteric AST nodes.
### Global-scope symbols defined using walrus expressions
`a.py`:
`exporter.py`:
```py
X = (Y := 3) + 4
@@ -128,7 +137,7 @@ X = (Y := 3) + 4
`b.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: Unknown | Literal[7]
reveal_type(Y) # revealed: Unknown | Literal[3]
@@ -136,7 +145,7 @@ reveal_type(Y) # revealed: Unknown | Literal[3]
### Global-scope symbols defined in many other ways
`a.py`:
`exporter.py`:
```py
import typing
@@ -158,8 +167,15 @@ def J(): ...
type K = int
with () as L: # error: [invalid-context-manager]
...
class ContextManagerThatMightNotRunToCompletion:
def __enter__(self) -> "ContextManagerThatMightNotRunToCompletion":
return self
def __exit__(self, *args) -> typing.Literal[True]:
return True
with ContextManagerThatMightNotRunToCompletion() as L:
U = ...
match 42:
case {"something": M}:
@@ -172,16 +188,35 @@ match 42:
...
case object(foo=R):
...
match 56:
case x if something_unresolvable: # error: [unresolved-reference]
...
case object(S):
...
match 12345:
case x if something_unresolvable: # error: [unresolved-reference]
...
case T:
...
def boolean_condition() -> bool:
return True
if boolean_condition():
V = ...
while boolean_condition():
W = ...
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
# fmt: off
@@ -192,31 +227,197 @@ print((
D,
E,
F,
G, # TODO: could emit diagnostic about being possibly unbound
H,
G, # error: [possibly-unresolved-reference]
H, # error: [possibly-unresolved-reference]
I,
J,
K,
L,
M, # TODO: could emit diagnostic about being possibly unbound
N, # TODO: could emit diagnostic about being possibly unbound
O, # TODO: could emit diagnostic about being possibly unbound
P, # TODO: could emit diagnostic about being possibly unbound
Q, # TODO: could emit diagnostic about being possibly unbound
R, # TODO: could emit diagnostic about being possibly unbound
S, # TODO: could emit diagnostic about being possibly unbound
T, # TODO: could emit diagnostic about being possibly unbound
M, # error: [possibly-unresolved-reference]
N, # error: [possibly-unresolved-reference]
O, # error: [possibly-unresolved-reference]
P, # error: [possibly-unresolved-reference]
Q, # error: [possibly-unresolved-reference]
R, # error: [possibly-unresolved-reference]
S, # error: [possibly-unresolved-reference]
T, # error: [possibly-unresolved-reference]
U, # TODO: could emit [possibly-unresolved-reference here] (https://github.com/astral-sh/ruff/issues/16996)
V, # error: [possibly-unresolved-reference]
W, # error: [possibly-unresolved-reference]
typing,
OrderedDict,
Foo,
))
```
### Esoteric possible redefinitions following definitely bound prior definitions
There should be no complaint about the symbols being possibly unbound in `b.py` here: although the
second definition might or might not take place, each symbol is definitely bound by a prior
definition.
`exporter.py`:
```py
from typing import Literal
A = 1
B = 2
C = 3
D = 4
E = 5
F = 6
G = 7
H = 8
I = 9
J = 10
K = 11
L = 12
for A in [1]:
...
match 42:
case {"something": B}:
...
case [*C]:
...
case [D]:
...
case E | F:
...
case object(foo=G):
...
case object(H):
...
case I:
...
def boolean_condition() -> bool:
return True
if boolean_condition():
J = ...
while boolean_condition():
K = ...
class ContextManagerThatMightNotRunToCompletion:
def __enter__(self) -> "ContextManagerThatMightNotRunToCompletion":
return self
def __exit__(self, *args) -> Literal[True]:
return True
with ContextManagerThatMightNotRunToCompletion():
L = ...
```
`importer.py`:
```py
from exporter import *
print(A)
print(B)
print(C)
print(D)
print(E)
print(F)
print(G)
print(H)
print(I)
print(J)
print(K)
print(L)
```
### Esoteric possible definitions prior to definitely bound prior redefinitions
The same principle applies here to the symbols in `b.py`. Although the first definition might or
might not take place, each symbol is definitely bound by a later definition.
`exporter.py`:
```py
from typing import Literal
for A in [1]:
...
match 42:
case {"something": B}:
...
case [*C]:
...
case [D]:
...
case E | F:
...
case object(foo=G):
...
case object(H):
...
case I:
...
def boolean_condition() -> bool:
return True
if boolean_condition():
J = ...
while boolean_condition():
K = ...
class ContextManagerThatMightNotRunToCompletion:
def __enter__(self) -> "ContextManagerThatMightNotRunToCompletion":
return self
def __exit__(self, *args) -> Literal[True]:
return True
with ContextManagerThatMightNotRunToCompletion():
L = ...
A = 1
B = 2
C = 3
D = 4
E = 5
F = 6
G = 7
H = 8
I = 9
J = 10
K = 11
L = 12
```
`importer.py`:
```py
from exporter import *
print(A)
print(B)
print(C)
print(D)
print(E)
print(F)
print(G)
print(H)
print(I)
print(J)
print(K)
print(L)
```
### Definitions in function-like scopes are not global definitions
Except for some cases involving walrus expressions inside comprehension scopes.
`a.py`:
`exporter.py`:
```py
class Iterator:
@@ -248,10 +449,10 @@ list(((o := p * 2) for p in Iterable()))
[(lambda s=s: (t := 42))() for s in Iterable()]
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
# error: [unresolved-reference]
reveal_type(a) # revealed: Unknown
@@ -278,15 +479,21 @@ reveal_type(s) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(t) # revealed: Unknown
# TODO: these should all reveal `Unknown | int`.
# TODO: these should all reveal `Unknown | int` and should not emit errors.
# (We don't generally model elsewhere in red-knot that bindings from walruses
# "leak" from comprehension scopes into outer scopes, but we should.)
# See https://github.com/astral-sh/ruff/issues/16954
# error: [unresolved-reference]
reveal_type(g) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(i) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(k) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(m) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(o) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(q) # revealed: Unknown
```
@@ -315,12 +522,19 @@ reveal_type(X) # revealed: bool
reveal_type(Y) # revealed: Unknown
```
## Which symbols are exported
Not all symbols in the global namespace are considered "public". As a result, not all symbols bound
in the global namespace of an `exporter.py` module will be imported by a `from exporter import *`
statement in an `importer.py` module. The tests in this section elaborate on these semantics.
### Global-scope names starting with underscores
Global-scope names starting with underscores are not imported from a `*` import (unless the module
has `__all__` and they are included in `__all__`):
Global-scope names starting with underscores are not imported from a `*` import (unless the
exporting module has an `__all__` symbol in its global scope, and the underscore-prefixed symbols
are included in `__all__`):
`a.py`:
`exporter.py`:
```py
_private: bool = False
@@ -331,10 +545,10 @@ ___thunder___: bool = False
Y: bool = True
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
# error: [unresolved-reference]
reveal_type(_private) # revealed: Unknown
@@ -354,8 +568,11 @@ For `.py` files, we should consider all public symbols in the global namespace e
module when considering which symbols are made available by a `*` import. Here, `b.py` does not use
the explicit `from a import X as X` syntax to explicitly mark it as publicly re-exported, and `X` is
not included in `__all__`; whether it should be considered a "public name" in module `b` is
ambiguous. We could consider an opt-in rule to warn the user when they use `X` in `c.py` that it was
not included in `__all__` and was not marked as an explicit re-export.
ambiguous.
We should consider `X` bound in `c.py`. However, we could consider adding an opt-in rule to warn the
user when they use `X` in `c.py` that it was neither included in `b.__all__` nor marked as an
explicit re-export from `b` through the "redundant alias" convention.
`a.py`:
@@ -380,7 +597,7 @@ reveal_type(X) # revealed: bool
### Only explicit re-exports are considered re-exported from `.pyi` files
For `.pyi` files, we should consider all imports private to the stub unless they are included in
For `.pyi` files, we should consider all imports "private to the stub" unless they are included in
`__all__` or use the explicit `from foo import X as X` syntax.
`a.pyi`:
@@ -409,14 +626,32 @@ reveal_type(X) # revealed: Unknown
reveal_type(Y) # revealed: bool
```
### Symbols in statically known branches
## Visibility constraints
If an `importer` module contains a `from exporter import *` statement in its global namespace, the
statement will *not* necessarily import *all* symbols that have definitions in `exporter.py`'s
global scope. For any given symbol in `exporter.py`'s global scope, that symbol will *only* be
imported by the `*` import if at least one definition for that symbol is visible from the *end* of
`exporter.py`'s global scope.
For example, say that `exporter.py` contains a symbol `X` in its global scope, and the definition
for `X` in `exporter.py` has visibility constraints <code>vis<sub>1</sub></code>. The
`from exporter import *` statement in `importer.py` creates a definition for `X` in `importer`, and
there are visibility constraints <code>vis<sub>2</sub></code> on the import statement in
`importer.py`. This means that the overall visibility constraints on the `X` definnition created by
the import statement in `importer.py` will be <code>vis<sub>1</sub> AND vis<sub>2</sub></code>.
A visibility constraint in the external module must be understood and evaluated whether or not its
truthiness can be statically determined.
### Statically known branches in the external module
```toml
[environment]
python-version = "3.11"
```
`a.py`:
`exporter.py`:
```py
import sys
@@ -428,27 +663,158 @@ else:
Z: int = 42
```
`b.py`:
`importer.py`:
```py
import sys
Z: bool = True
from a import *
from exporter import *
reveal_type(X) # revealed: bool
# TODO: should emit error: [unresolved-reference]
# error: [unresolved-reference]
reveal_type(Y) # revealed: Unknown
# TODO: The `*` import should not be considered a redefinition
# of the global variable in this module, as the symbol in
# The `*` import is not considered a redefinition
# of the global variable `Z` in this module, as the symbol in
# the `a` module is in a branch that is statically known
# to be dead code given the `python-version` configuration.
# Thus this should reveal `Literal[True]`.
reveal_type(Z) # revealed: Unknown
# Thus this still reveals `Literal[True]`.
reveal_type(Z) # revealed: Literal[True]
```
### Relative `*` imports
### Multiple `*` imports with always-false visibility constraints
Our understanding of visibility constraints in an external module remains accurate, even if there
are multiple `*` imports from that module.
```toml
[environment]
python-version = "3.11"
```
`exporter.py`:
```py
import sys
if sys.version_info >= (3, 12):
Z: str = "foo"
```
`importer.py`:
```py
Z = True
from exporter import *
from exporter import *
from exporter import *
reveal_type(Z) # revealed: Literal[True]
```
### Ambiguous visibility constraints
Some constraints in the external module may resolve to an "ambiguous truthiness". For these, we
should emit `possibly-unresolved-reference` diagnostics when they are used in the module in which
the `*` import occurs.
`exporter.py`:
```py
def coinflip() -> bool:
return True
if coinflip():
A = 1
B = 2
else:
B = 3
```
`importer.py`:
```py
from exporter import *
# error: [possibly-unresolved-reference]
reveal_type(A) # revealed: Unknown | Literal[1]
reveal_type(B) # revealed: Unknown | Literal[2, 3]
```
### Visibility constraints in the importing module
`exporter.py`:
```py
A = 1
```
`importer.py`:
```py
def coinflip() -> bool:
return True
if coinflip():
from exporter import *
# error: [possibly-unresolved-reference]
reveal_type(A) # revealed: Unknown | Literal[1]
```
### Visibility constraints in the exporting module *and* the importing module
```toml
[environment]
python-version = "3.11"
```
`exporter.py`:
```py
import sys
if sys.version_info >= (3, 12):
A: bool = True
def coinflip() -> bool:
return True
if coinflip():
B: bool = True
```
`importer.py`:
```py
import sys
if sys.version_info >= (3, 12):
from exporter import *
# it's correct to have no diagnostics here as this branch is unreachable
reveal_type(A) # revealed: Unknown
reveal_type(B) # revealed: bool
else:
from exporter import *
# error: [unresolved-reference]
reveal_type(A) # revealed: Unknown
# error: [possibly-unresolved-reference]
reveal_type(B) # revealed: bool
# error: [unresolved-reference]
reveal_type(A) # revealed: Unknown
# error: [possibly-unresolved-reference]
reveal_type(B) # revealed: bool
```
## Relative `*` imports
Relative `*` imports are also supported by Python:
@@ -478,7 +844,7 @@ If a module `x` contains `__all__`, all symbols included in `x.__all__` are impo
### Simple tuple `__all__`
`a.py`:
`exporter.py`:
```py
__all__ = ("X", "_private", "__protected", "__dunder__", "___thunder___")
@@ -492,10 +858,10 @@ ___thunder___: bool = True
Y: bool = False
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: bool
@@ -515,7 +881,7 @@ reveal_type(Y) # revealed: bool
### Simple list `__all__`
`a.py`:
`exporter.py`:
```py
__all__ = ["X"]
@@ -524,10 +890,10 @@ X: bool = True
Y: bool = False
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: bool
@@ -537,7 +903,9 @@ reveal_type(Y) # revealed: bool
### `__all__` with additions later on in the global scope
The [typing spec] lists certain modifications to `__all__` that must be understood by type checkers.
The
[typing spec](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)
lists certain modifications to `__all__` that must be understood by type checkers.
`a.py`:
@@ -589,7 +957,7 @@ reveal_type(F) # revealed: bool
Whereas there are many ways of adding to `__all__` that type checkers must support, there is only
one way of subtracting from `__all__` that type checkers are required to support:
`a.py`:
`exporter.py`:
```py
__all__ = ["A", "B"]
@@ -599,10 +967,10 @@ A: bool = True
B: bool = True
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(A) # revealed: bool
@@ -618,11 +986,11 @@ a wildcard import from module `a` will fail at runtime.
TODO: Should we:
1. Emit a diagnostic at the invalid definition of `__all__` (which will not fail at runtime)?
1. Emit a diagnostic at the star-import from the module with the invalid `__all__` (which _will_
1. Emit a diagnostic at the star-import from the module with the invalid `__all__` (which *will*
fail at runtime)?
1. Emit a diagnostic on both?
`a.py`:
`exporter.py`:
```py
__all__ = ["a", "b"]
@@ -630,11 +998,11 @@ __all__ = ["a", "b"]
a = 42
```
`b.py`:
`importer.py`:
```py
# TODO we should consider emitting a diagnostic here (see prose description above)
from a import * # fails with `AttributeError: module 'foo' has no attribute 'b'` at runtime
from exporter import * # fails with `AttributeError: module 'foo' has no attribute 'b'` at runtime
```
### Dynamic `__all__`
@@ -645,7 +1013,7 @@ treat the module as though it has no `__all__` at all: all global-scope members
be considered imported by the import statement. We should probably also emit a warning telling the
user that we cannot statically determine the elements of `__all__`.
`a.py`:
`exporter.py`:
```py
def f() -> str:
@@ -658,10 +1026,10 @@ def g() -> int:
__all__ = [f()]
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
# At runtime, `f` is imported but `g` is not; to avoid false positives, however,
# we treat `a` as though it does not have `__all__` at all,
@@ -677,7 +1045,7 @@ reveal_type(g) # revealed: Literal[g]
python-version = "3.11"
```
`a.py`:
`exporter.py`:
```py
import sys
@@ -692,15 +1060,15 @@ else:
Z: bool = True
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: bool
reveal_type(Y) # revealed: bool
# TODO: should error with [unresolved-reference]
# error: [unresolved-reference]
reveal_type(Z) # revealed: Unknown
```
@@ -711,7 +1079,7 @@ reveal_type(Z) # revealed: Unknown
python-version = "3.11"
```
`a.py`:
`exporter.py`:
```py
import sys
@@ -727,15 +1095,15 @@ else:
Z: bool = True
```
`b.py`:
`importer.py`:
```py
from a import *
from exporter import *
reveal_type(X) # revealed: bool
reveal_type(Y) # revealed: bool
# TODO should have an [unresolved-reference] diagnostic
# error: [unresolved-reference]
reveal_type(Z) # revealed: Unknown
```
@@ -776,16 +1144,16 @@ reveal_type(Y) # revealed: bool
If a name is included in `__all__` in a stub file, it is considered re-exported even if it was only
defined using an import without the explicit `from foo import X as X` syntax:
`a.py`:
`a.pyi`:
```py
```pyi
X: bool = True
Y: bool = True
```
`b.py`:
`b.pyi`:
```py
```pyi
from a import X, Y
__all__ = ["X"]
@@ -796,10 +1164,17 @@ __all__ = ["X"]
```py
from b import *
reveal_type(X) # revealed: bool
# TODO: should not error, should reveal `bool`
# (`X` is re-exported from `b.pyi` due to presence in `__all__`)
#
# error: [unresolved-reference]
reveal_type(X) # revealed: Unknown
# TODO this should have an [unresolved-reference] diagnostic and reveal `Unknown`
reveal_type(Y) # revealed: bool
# This diagnostic is accurate: `Y` does not use the "redundant alias" convention in `b.pyi`,
# nor is it included in `b.__all__`, so it is not exported from `b.pyi`
#
# error: [unresolved-reference]
reveal_type(Y) # revealed: Unknown
```
## `global` statements in non-global scopes
@@ -825,7 +1200,12 @@ from a import *
reveal_type(f) # revealed: Literal[f]
# TODO: false positive, should be `bool` with no diagnostic
# TODO: we're undecided about whether we should consider this a false positive or not.
# Mutating the global scope to add a symbol from an inner scope will not *necessarily* result
# in the symbol being bound from the perspective of other modules (the function that creates
# the inner scope, and adds the symbol to the global scope, might never be called!)
# See discussion in https://github.com/astral-sh/ruff/pull/16959
#
# error: [unresolved-reference]
reveal_type(g) # revealed: Unknown
@@ -836,7 +1216,7 @@ reveal_type(h) # revealed: Unknown
## Cyclic star imports
Believe it or not, this code does _not_ raise an exception at runtime!
Believe it or not, this code does *not* raise an exception at runtime!
`a.py`:
@@ -869,11 +1249,14 @@ The `collections.abc` standard-library module provides a good integration test,
are present due to `*` imports.
```py
import typing
import collections.abc
reveal_type(collections.abc.Sequence) # revealed: Literal[Sequence]
reveal_type(collections.abc.Callable) # revealed: typing.Callable
# TODO: false positive as it's only re-exported from `_collections.abc` due to presence in `__all__`
# error: [unresolved-attribute]
reveal_type(collections.abc.Set) # revealed: Unknown
```
## Invalid `*` imports
@@ -892,18 +1275,18 @@ from foo import * # error: [unresolved-import] "Cannot resolve import `foo`"
A `*` import in a nested scope are always a syntax error. Red-knot does not infer any bindings from
them:
`a.py`:
`exporter.py`:
```py
X: bool = True
```
`b.py`:
`importer.py`:
```py
def f():
# TODO: we should emit a syntax errror here (tracked by https://github.com/astral-sh/ruff/issues/11934)
from a import *
from exporter import *
# error: [unresolved-reference]
reveal_type(X) # revealed: Unknown
@@ -948,4 +1331,3 @@ from a import *, *, _Y # error: [invalid-syntax]
<!-- blacken-docs:on -->
[python language reference for import statements]: https://docs.python.org/3/reference/simple_stmts.html#the-import-statement
[typing spec]: https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols

View File

@@ -0,0 +1,286 @@
# Stub packages
Stub packages are packages named `<package>-stubs` that provide typing stubs for `<package>`. See
[specification](https://typing.python.org/en/latest/spec/distributing.html#stub-only-packages).
## Simple stub
```toml
[environment]
extra-paths = ["/packages"]
```
`/packages/foo-stubs/__init__.pyi`:
```pyi
class Foo:
name: str
age: int
```
`/packages/foo/__init__.py`:
```py
class Foo: ...
```
`main.py`:
```py
from foo import Foo
reveal_type(Foo().name) # revealed: str
```
## Stubs only
The regular package isn't required for type checking.
```toml
[environment]
extra-paths = ["/packages"]
```
`/packages/foo-stubs/__init__.pyi`:
```pyi
class Foo:
name: str
age: int
```
`main.py`:
```py
from foo import Foo
reveal_type(Foo().name) # revealed: str
```
## `-stubs` named module
A module named `<module>-stubs` isn't a stub package.
```toml
[environment]
extra-paths = ["/packages"]
```
`/packages/foo-stubs.pyi`:
```pyi
class Foo:
name: str
age: int
```
`main.py`:
```py
from foo import Foo # error: [unresolved-import]
reveal_type(Foo().name) # revealed: Unknown
```
## Namespace package in different search paths
A namespace package with multiple stub packages spread over multiple search paths.
```toml
[environment]
extra-paths = ["/stubs1", "/stubs2", "/packages"]
```
`/stubs1/shapes-stubs/polygons/pentagon.pyi`:
```pyi
class Pentagon:
sides: int
area: float
```
`/stubs2/shapes-stubs/polygons/hexagon.pyi`:
```pyi
class Hexagon:
sides: int
area: float
```
`/packages/shapes/polygons/pentagon.py`:
```py
class Pentagon: ...
```
`/packages/shapes/polygons/hexagon.py`:
```py
class Hexagon: ...
```
`main.py`:
```py
from shapes.polygons.hexagon import Hexagon
from shapes.polygons.pentagon import Pentagon
reveal_type(Pentagon().sides) # revealed: int
reveal_type(Hexagon().area) # revealed: int | float
```
## Inconsistent stub packages
Stub packages where one is a namespace package and the other is a regular package. Module resolution
should stop after the first non-namespace stub package. This matches Pyright's behavior.
```toml
[environment]
extra-paths = ["/stubs1", "/stubs2", "/packages"]
```
`/stubs1/shapes-stubs/__init__.pyi`:
```pyi
```
`/stubs1/shapes-stubs/polygons/__init__.pyi`:
```pyi
```
`/stubs1/shapes-stubs/polygons/pentagon.pyi`:
```pyi
class Pentagon:
sides: int
area: float
```
`/stubs2/shapes-stubs/polygons/hexagon.pyi`:
```pyi
class Hexagon:
sides: int
area: float
```
`/packages/shapes/polygons/pentagon.py`:
```py
class Pentagon: ...
```
`/packages/shapes/polygons/hexagon.py`:
```py
class Hexagon: ...
```
`main.py`:
```py
from shapes.polygons.pentagon import Pentagon
from shapes.polygons.hexagon import Hexagon # error: [unresolved-import]
reveal_type(Pentagon().sides) # revealed: int
reveal_type(Hexagon().area) # revealed: Unknown
```
## Namespace stubs for non-namespace package
The runtime package is a regular package but the stubs are namespace packages. Pyright skips the
stub package if the "regular" package isn't a namespace package. I'm not aware that the behavior
here is specified, and using the stubs without probing the runtime package first requires slightly
fewer lookups.
```toml
[environment]
extra-paths = ["/packages"]
```
`/packages/shapes-stubs/polygons/pentagon.pyi`:
```pyi
class Pentagon:
sides: int
area: float
```
`/packages/shapes-stubs/polygons/hexagon.pyi`:
```pyi
class Hexagon:
sides: int
area: float
```
`/packages/shapes/__init__.py`:
```py
```
`/packages/shapes/polygons/__init__.py`:
```py
```
`/packages/shapes/polygons/pentagon.py`:
```py
class Pentagon: ...
```
`/packages/shapes/polygons/hexagon.py`:
```py
class Hexagon: ...
```
`main.py`:
```py
from shapes.polygons.pentagon import Pentagon
from shapes.polygons.hexagon import Hexagon
reveal_type(Pentagon().sides) # revealed: int
reveal_type(Hexagon().area) # revealed: int | float
```
## Stub package using `__init__.py` over `.pyi`
It's recommended that stub packages use `__init__.pyi` files over `__init__.py` but it doesn't seem
to be an enforced convention. At least, Pyright is fine with the following.
```toml
[environment]
extra-paths = ["/packages"]
```
`/packages/shapes-stubs/__init__.py`:
```py
class Pentagon:
sides: int
area: float
class Hexagon:
sides: int
area: float
```
`/packages/shapes/__init__.py`:
```py
class Pentagon: ...
class Hexagon: ...
```
`main.py`:
```py
from shapes import Hexagon, Pentagon
reveal_type(Pentagon().sides) # revealed: int
reveal_type(Hexagon().area) # revealed: int | float
```

View File

@@ -76,6 +76,9 @@ No narrowing should occur if `type` is used to dynamically create a class:
```py
def _(x: str | int):
# The following diagnostic is valid, since the three-argument form of `type`
# can only be called with `str` as the first argument.
# error: [no-matching-overload] "No overload of class `type` matches arguments"
if type(x, (), {}) is str:
reveal_type(x) # revealed: str | int
else:
@@ -106,6 +109,24 @@ def _(x: A | B):
reveal_type(x) # revealed: A
```
## Narrowing for generic classes
Note that `type` returns the runtime class of an object, which does _not_ include specializations in
the case of a generic class. (The typevars are erased.) That means we cannot narrow the type to the
specialization that we compare with; we must narrow to an unknown specialization of the generic
class.
```py
class A[T = int]: ...
class B: ...
def _[T](x: A | B):
if type(x) is A[str]:
reveal_type(x) # revealed: A[int] & A[Unknown] | B & A[Unknown]
else:
reveal_type(x) # revealed: A[int] | B
```
## Limitations
```py

View File

@@ -146,7 +146,7 @@ class C:
@property
def attr(self) -> int:
return 1
# error: [invalid-argument-type] "Object of type `Literal[attr]` cannot be assigned to parameter 2 (`fset`) of bound method `setter`; expected type `(Any, Any, /) -> None`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `(Any, Any, /) -> None`, found `Literal[attr]`"
@attr.setter
def attr(self) -> None:
pass
@@ -156,7 +156,7 @@ class C:
```py
class C:
# error: [invalid-argument-type] "Object of type `Literal[attr]` cannot be assigned to parameter 1 (`fget`) of class `property`; expected type `((Any, /) -> Any) | None`"
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `((Any, /) -> Any) | None`, found `Literal[attr]`"
@property
def attr(self, x: int) -> int:
return 1

View File

@@ -40,13 +40,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:11:5
|
9 | # error: [not-iterable]
10 | for x in Iterable():
11 | reveal_type(x) # revealed: int
| ^^^^^^^^^^^^^^ Revealed type is `int`
| ^^^^^^^^^^^^^^ `int`
|
```

View File

@@ -37,13 +37,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:8:5
|
6 | # error: [not-iterable]
7 | for x in Bad():
8 | reveal_type(x) # revealed: Unknown
| ^^^^^^^^^^^^^^ Revealed type is `Unknown`
| ^^^^^^^^^^^^^^ `Unknown`
|
```

View File

@@ -59,13 +59,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:24:9
|
22 | for x in Iterable1():
23 | # TODO... `int` might be ideal here?
24 | reveal_type(x) # revealed: int | Unknown
| ^^^^^^^^^^^^^^ Revealed type is `int | Unknown`
| ^^^^^^^^^^^^^^ `int | Unknown`
25 |
26 | # error: [not-iterable]
|
@@ -86,13 +86,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:29:9
|
27 | for y in Iterable2():
28 | # TODO... `int` might be ideal here?
29 | reveal_type(y) # revealed: int | Unknown
| ^^^^^^^^^^^^^^ Revealed type is `int | Unknown`
| ^^^^^^^^^^^^^^ `int | Unknown`
|
```

View File

@@ -56,13 +56,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:22:9
|
20 | for x in Iterable1():
21 | # TODO: `str` might be better
22 | reveal_type(x) # revealed: str | Unknown
| ^^^^^^^^^^^^^^ Revealed type is `str | Unknown`
| ^^^^^^^^^^^^^^ `str | Unknown`
23 |
24 | # error: [not-iterable]
|
@@ -82,13 +82,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:26:9
|
24 | # error: [not-iterable]
25 | for y in Iterable2():
26 | reveal_type(y) # revealed: str | int
| ^^^^^^^^^^^^^^ Revealed type is `str | int`
| ^^^^^^^^^^^^^^ `str | int`
|
```

View File

@@ -59,13 +59,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:18:9
|
16 | # error: [not-iterable]
17 | for x in Iterable1():
18 | reveal_type(x) # revealed: int
| ^^^^^^^^^^^^^^ Revealed type is `int`
| ^^^^^^^^^^^^^^ `int`
19 |
20 | class Iterable2:
|
@@ -86,13 +86,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:30:9
|
28 | for x in Iterable2():
29 | # TODO: `int` would probably be better here:
30 | reveal_type(x) # revealed: int | Unknown
| ^^^^^^^^^^^^^^ Revealed type is `int | Unknown`
| ^^^^^^^^^^^^^^ `int | Unknown`
|
```

View File

@@ -63,13 +63,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:29:9
|
27 | # error: [not-iterable]
28 | for x in Iterable1():
29 | reveal_type(x) # revealed: int | str
| ^^^^^^^^^^^^^^ Revealed type is `int | str`
| ^^^^^^^^^^^^^^ `int | str`
30 |
31 | # error: [not-iterable]
|
@@ -90,13 +90,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:34:9
|
32 | for y in Iterable2():
33 | # TODO: `int` would probably be better here:
34 | reveal_type(y) # revealed: int | Unknown
| ^^^^^^^^^^^^^^ Revealed type is `int | Unknown`
| ^^^^^^^^^^^^^^ `int | Unknown`
|
```

View File

@@ -48,13 +48,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:19:9
|
17 | # error: [not-iterable]
18 | for x in Iterable():
19 | reveal_type(x) # revealed: int | bytes
| ^^^^^^^^^^^^^^ Revealed type is `int | bytes`
| ^^^^^^^^^^^^^^ `int | bytes`
|
```

View File

@@ -67,13 +67,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:33:9
|
31 | for x in Iterable1():
32 | # TODO: `bytes | str` might be better
33 | reveal_type(x) # revealed: bytes | str | Unknown
| ^^^^^^^^^^^^^^ Revealed type is `bytes | str | Unknown`
| ^^^^^^^^^^^^^^ `bytes | str | Unknown`
34 |
35 | # error: [not-iterable]
|
@@ -93,13 +93,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:37:9
|
35 | # error: [not-iterable]
36 | for y in Iterable2():
37 | reveal_type(y) # revealed: bytes | str | int
| ^^^^^^^^^^^^^^ Revealed type is `bytes | str | int`
| ^^^^^^^^^^^^^^ `bytes | str | int`
|
```

View File

@@ -47,13 +47,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:18:9
|
16 | # error: [not-iterable]
17 | for x in Iterable():
18 | reveal_type(x) # revealed: int | bytes
| ^^^^^^^^^^^^^^ Revealed type is `int | bytes`
| ^^^^^^^^^^^^^^ `int | bytes`
|
```

View File

@@ -49,13 +49,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:19:9
|
17 | # error: [not-iterable]
18 | for x in Test() if flag else Test2():
19 | reveal_type(x) # revealed: int
| ^^^^^^^^^^^^^^ Revealed type is `int`
| ^^^^^^^^^^^^^^ `int`
|
```

View File

@@ -44,13 +44,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:14:9
|
12 | # error: [not-iterable]
13 | for x in Test() if flag else 42:
14 | reveal_type(x) # revealed: int
| ^^^^^^^^^^^^^^ Revealed type is `int`
| ^^^^^^^^^^^^^^ `int`
|
```

View File

@@ -57,13 +57,13 @@ warning: lint:possibly-unresolved-reference
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:16:5
|
14 | # revealed: Unknown
15 | # error: [possibly-unresolved-reference]
16 | reveal_type(x)
| ^^^^^^^^^^^^^^ Revealed type is `Unknown`
| ^^^^^^^^^^^^^^ `Unknown`
|
```

View File

@@ -38,13 +38,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:9:5
|
7 | # error: [not-iterable]
8 | for x in Bad():
9 | reveal_type(x) # revealed: Unknown
| ^^^^^^^^^^^^^^ Revealed type is `Unknown`
| ^^^^^^^^^^^^^^ `Unknown`
|
```

View File

@@ -42,13 +42,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:13:5
|
11 | # error: [not-iterable]
12 | for x in Iterable():
13 | reveal_type(x) # revealed: int
| ^^^^^^^^^^^^^^ Revealed type is `int`
| ^^^^^^^^^^^^^^ `int`
|
```

View File

@@ -53,13 +53,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:20:5
|
18 | # error: [not-iterable]
19 | for x in Iterable1():
20 | reveal_type(x) # revealed: int
| ^^^^^^^^^^^^^^ Revealed type is `int`
| ^^^^^^^^^^^^^^ `int`
21 |
22 | # error: [not-iterable]
|
@@ -79,13 +79,13 @@ error: lint:not-iterable
```
```
info: revealed-type
info: revealed-type: Revealed type
--> /src/mdtest_snippet.py:24:5
|
22 | # error: [not-iterable]
23 | for y in Iterable2():
24 | reveal_type(y) # revealed: Unknown
| ^^^^^^^^^^^^^^ Revealed type is `Unknown`
| ^^^^^^^^^^^^^^ `Unknown`
|
```

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:5
|
2 | return x * x
3 |
4 | foo("hello") # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 1 (`x`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:9
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * x
|

View File

@@ -23,19 +23,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:6:10
|
5 | c = C()
6 | c.square("hello") # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 2 (`x`) of bound method `square`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:2:22
info: Function defined here
--> /src/mdtest_snippet.py:2:9
|
1 | class C:
2 | def square(self, x: int) -> int:
| ------ parameter declared in function definition here
| ^^^^^^ ------ Parameter declared here
3 | return x * x
|

View File

@@ -27,19 +27,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:3:13
|
1 | import package
2 |
3 | package.foo("hello") # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 1 (`x`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/package.py:1:9
info: Function defined here
--> /src/package.py:1:5
|
1 | def foo(x: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * x
|

View File

@@ -22,22 +22,22 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:2:9
|
1 | def bar():
2 | foo("hello") # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 1 (`x`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
3 |
4 | def foo(x: int) -> int:
|
info
--> /src/mdtest_snippet.py:4:9
info: Function defined here
--> /src/mdtest_snippet.py:4:5
|
2 | foo("hello") # error: [invalid-argument-type]
3 |
4 | def foo(x: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
5 | return x * x
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:8
|
2 | return x * y * z
3 |
4 | foo(1, "hello", 3) # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 2 (`y`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:17
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, z: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * y * z
|

View File

@@ -25,21 +25,22 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:8:8
|
6 | return x * y * z
7 |
8 | foo(1, "hello", 3) # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 2 (`y`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:3:5
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(
| ^^^
2 | x: int,
3 | y: int,
| ------ parameter declared in function definition here
| ------ Parameter declared here
4 | z: int,
5 | ) -> int:
|

View File

@@ -24,57 +24,57 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:7:5
|
5 | # error: [invalid-argument-type]
6 | # error: [invalid-argument-type]
7 | foo("a", "b", "c")
| ^^^ Object of type `Literal["a"]` cannot be assigned to parameter 1 (`x`) of function `foo`; expected type `int`
| ^^^ Expected `int`, found `Literal["a"]`
|
info
--> /src/mdtest_snippet.py:1:9
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, z: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * y * z
|
```
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:7:10
|
5 | # error: [invalid-argument-type]
6 | # error: [invalid-argument-type]
7 | foo("a", "b", "c")
| ^^^ Object of type `Literal["b"]` cannot be assigned to parameter 2 (`y`) of function `foo`; expected type `int`
| ^^^ Expected `int`, found `Literal["b"]`
|
info
--> /src/mdtest_snippet.py:1:17
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, z: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * y * z
|
```
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:7:15
|
5 | # error: [invalid-argument-type]
6 | # error: [invalid-argument-type]
7 | foo("a", "b", "c")
| ^^^ Object of type `Literal["c"]` cannot be assigned to parameter 3 (`z`) of function `foo`; expected type `int`
| ^^^ Expected `int`, found `Literal["c"]`
|
info
--> /src/mdtest_snippet.py:1:25
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, z: int) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * y * z
|

View File

@@ -20,21 +20,23 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:3:12
|
1 | import json
2 |
3 | json.loads(5) # error: [invalid-argument-type]
| ^ Object of type `Literal[5]` cannot be assigned to parameter 1 (`s`) of function `loads`; expected type `str | bytes | bytearray`
| ^ Expected `str | bytes | bytearray`, found `Literal[5]`
|
info
--> stdlib/json/__init__.pyi:40:5
info: Function defined here
--> stdlib/json/__init__.pyi:39:5
|
37 | **kwds: Any,
38 | ) -> None: ...
39 | def loads(
| ^^^^^
40 | s: str | bytes | bytearray,
| -------------------------- parameter declared in function definition here
| -------------------------- Parameter declared here
41 | *,
42 | cls: type[JSONDecoder] | None = None,
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:11
|
2 | return x * y * z
3 |
4 | foo(1, 2, z="hello") # error: [invalid-argument-type]
| ^^^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter `z` of function `foo`; expected type `int`
| ^^^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:28
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, *, z: int = 0) -> int:
| ---------- parameter declared in function definition here
| ^^^ ---------- Parameter declared here
2 | return x * y * z
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:11
|
2 | return x * y * z
3 |
4 | foo(1, 2, z="hello") # error: [invalid-argument-type]
| ^^^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter `z` of function `foo`; expected type `int`
| ^^^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:31
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, /, y: int, *, z: int = 0) -> int:
| ---------- parameter declared in function definition here
| ^^^ ---------- Parameter declared here
2 | return x * y * z
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:11
|
2 | return x * y * z
3 |
4 | foo(1, 2, "hello") # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 3 (`z`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:25
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, z: int = 0) -> int:
| ---------- parameter declared in function definition here
| ^^^ ---------- Parameter declared here
2 | return x * y * z
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:8
|
2 | return x * y * z
3 |
4 | foo(1, "hello", 3) # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 2 (`y`) of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:17
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(x: int, y: int, z: int, /) -> int:
| ------ parameter declared in function definition here
| ^^^ ------ Parameter declared here
2 | return x * y * z
|

View File

@@ -23,19 +23,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:6:3
|
5 | c = C()
6 | c("wrong") # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["wrong"]` cannot be assigned to parameter 2 (`x`) of bound method `__call__`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["wrong"]`
|
info
--> /src/mdtest_snippet.py:2:24
info: Function defined here
--> /src/mdtest_snippet.py:2:9
|
1 | class C:
2 | def __call__(self, x: int) -> int:
| ------ parameter declared in function definition here
| ^^^^^^^^ ------ Parameter declared here
3 | return 1
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:14
|
2 | return len(numbers)
3 |
4 | foo(1, 2, 3, "hello", 5) # error: [invalid-argument-type]
| ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter `*numbers` of function `foo`; expected type `int`
| ^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:9
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(*numbers: int) -> int:
| ------------- parameter declared in function definition here
| ^^^ ------------- Parameter declared here
2 | return len(numbers)
|

View File

@@ -21,19 +21,19 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/invali
# Diagnostics
```
error: lint:invalid-argument-type
error: lint:invalid-argument-type: Argument to this function is incorrect
--> /src/mdtest_snippet.py:4:20
|
2 | return len(numbers)
3 |
4 | foo(a=1, b=2, c=3, d="hello", e=5) # error: [invalid-argument-type]
| ^^^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter `**numbers` of function `foo`; expected type `int`
| ^^^^^^^^^ Expected `int`, found `Literal["hello"]`
|
info
--> /src/mdtest_snippet.py:1:9
info: Function defined here
--> /src/mdtest_snippet.py:1:5
|
1 | def foo(**numbers: int) -> int:
| -------------- parameter declared in function definition here
| ^^^ -------------- Parameter declared here
2 | return len(numbers)
|

View File

@@ -31,67 +31,56 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/function/return_ty
# Diagnostics
```
error: lint:invalid-return-type
--> /src/mdtest_snippet.py:6:16
|
4 | else:
5 | # error: [invalid-return-type]
6 | return 1
| ^ Object of type `Literal[1]` is not assignable to return type `str`
7 |
8 | def f(cond: bool) -> str:
|
info
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.py:1:22
|
1 | def f(cond: bool) -> str:
| --- Return type is declared here as `str`
| --- Expected `str` because of return type
2 | if cond:
3 | return "a"
4 | else:
5 | # error: [invalid-return-type]
6 | return 1
| ^ Expected `str`, found `Literal[1]`
7 |
8 | def f(cond: bool) -> str:
|
```
```
error: lint:invalid-return-type
--> /src/mdtest_snippet.py:11:16
|
9 | if cond:
10 | # error: [invalid-return-type]
11 | return 1
| ^ Object of type `Literal[1]` is not assignable to return type `str`
12 | else:
13 | # error: [invalid-return-type]
|
info
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.py:8:22
|
6 | return 1
7 |
8 | def f(cond: bool) -> str:
| --- Return type is declared here as `str`
| --- Expected `str` because of return type
9 | if cond:
10 | # error: [invalid-return-type]
11 | return 1
| ^ Expected `str`, found `Literal[1]`
12 | else:
13 | # error: [invalid-return-type]
|
```
```
error: lint:invalid-return-type
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.py:14:16
|
12 | else:
13 | # error: [invalid-return-type]
14 | return 2
| ^ Object of type `Literal[2]` is not assignable to return type `str`
| ^ Expected `str`, found `Literal[2]`
|
info
--> /src/mdtest_snippet.py:8:22
::: /src/mdtest_snippet.py:8:22
|
6 | return 1
7 |
8 | def f(cond: bool) -> str:
| --- Return type is declared here as `str`
| --- Expected `str` because of return type
9 | if cond:
10 | # error: [invalid-return-type]
|

View File

@@ -40,23 +40,17 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/function/return_ty
# Diagnostics
```
error: lint:invalid-return-type
--> /src/mdtest_snippet.py:4:16
|
2 | if False:
3 | # error: [invalid-return-type]
4 | return 1
| ^ Object of type `Literal[1]` is not assignable to return type `None`
5 |
6 | # error: [invalid-return-type]
|
info
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.py:1:12
|
1 | def f() -> None:
| ---- Return type is declared here as `None`
| ---- Expected `None` because of return type
2 | if False:
3 | # error: [invalid-return-type]
4 | return 1
| ^ Expected `None`, found `Literal[1]`
5 |
6 | # error: [invalid-return-type]
|
```

View File

@@ -47,49 +47,35 @@ error: lint:invalid-return-type
```
```
error: lint:invalid-return-type
--> /src/mdtest_snippet.py:7:12
|
5 | def f() -> str:
6 | # error: [invalid-return-type]
7 | return 1
| ^ Object of type `Literal[1]` is not assignable to return type `str`
8 |
9 | def f() -> int:
|
info
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.py:5:12
|
3 | 1
4 |
5 | def f() -> str:
| --- Return type is declared here as `str`
| --- Expected `str` because of return type
6 | # error: [invalid-return-type]
7 | return 1
| ^ Expected `str`, found `Literal[1]`
8 |
9 | def f() -> int:
|
```
```
error: lint:invalid-return-type
--> /src/mdtest_snippet.py:11:5
|
9 | def f() -> int:
10 | # error: [invalid-return-type]
11 | return
| ^^^^^^ Object of type `None` is not assignable to return type `int`
12 |
13 | from typing import TypeVar
|
info
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.py:9:12
|
7 | return 1
8 |
9 | def f() -> int:
| --- Return type is declared here as `int`
| --- Expected `int` because of return type
10 | # error: [invalid-return-type]
11 | return
| ^^^^^^ Expected `int`, found `None`
12 |
13 | from typing import TypeVar
|
```

View File

@@ -30,23 +30,16 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/function/return_ty
# Diagnostics
```
error: lint:invalid-return-type
--> /src/mdtest_snippet.pyi:3:12
|
1 | def f() -> int:
2 | # error: [invalid-return-type]
3 | return ...
| ^^^ Object of type `ellipsis` is not assignable to return type `int`
4 |
5 | # error: [invalid-return-type]
|
info
error: lint:invalid-return-type: Return type does not match returned value
--> /src/mdtest_snippet.pyi:1:12
|
1 | def f() -> int:
| --- Return type is declared here as `int`
| --- Expected `int` because of return type
2 | # error: [invalid-return-type]
3 | return ...
| ^^^ Expected `int`, found `ellipsis`
4 |
5 | # error: [invalid-return-type]
|
```

View File

@@ -1502,13 +1502,14 @@ if True:
from module import symbol
```
## Unsupported features
## Unreachable code
We do not support full unreachable code analysis yet. We also raise diagnostics from
statically-known to be false branches:
A closely related feature is the ability to detect unreachable code. For example, we do not emit a
diagnostic here:
```py
if False:
# error: [unresolved-reference]
x
```
See [unreachable.md](unreachable.md) for more tests on this topic.

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