Compare commits

..

1951 Commits

Author SHA1 Message Date
Dhruv Manilawala
c76e15a45d Check context parameters directly from function definition 2025-01-23 14:13:29 +05:30
Ankit Chaurasia
d0aff2bbff Add find_parameter 2025-01-23 12:19:31 +05:45
Ankit Chaurasia
65db31f0e1 highlights the parameter itself 2025-01-22 12:59:39 +05:45
Wei Lee
c2c37b8052 test: update test fixture 2025-01-22 10:06:15 +08:00
Ankit Chaurasia
51613d9107 Add lint error for removed context variables for get_current_context 2025-01-22 09:57:26 +08:00
Ankit Chaurasia
f20e70cd62 remove use of vectors 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
4737824345 fix the logic for lint error message 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
1961b76d03 Refactor functions to use ExprSubscript 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
62a1e55705 refactor rename functions 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
8a7ec4c0a3 Fix PR comments 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
9b9540c3cd Check arguments and function decorated with @task 2025-01-22 09:57:25 +08:00
Ankit Chaurasia
ccafaf8e30 Add more checks for removed context variables
add lint rule to show error for removed context variables in airflow
2025-01-22 09:57:25 +08:00
Alex Waygood
fbb06fe0ac [red-knot] Small simplifications to Type::is_subtype_of and Type::is_disjoint_from (#15622)
## Summary

This PR generalizes some of the logic we have in `Type::is_subtype_of`
and `Type::is_disjoint_from` so that we fallback to the instance type of
the metaclass more often in `Type::ClassLiteral` and `Type::SubclassOf`
branches. This simplifies the code (we end up with one less branch in
`is_subtype_of`, and we can remove a helper method that's no longer
used), makes the code more robust (any fixes made to subtyping or
disjointness of instance types will automatically improve our
understanding of subtyping/disjointness for class-literal types and
`type[]` types) and more elegantly expresses the type-system invariants
encoded in these branches.

## Test Plan

No new tests added (it's a pure refactor, adding no new functionality).
All existing tests pass, however, including the property tests.
2025-01-22 00:31:55 +00:00
Zanie Blue
8a8240b8a6 Use new-style Windows runner tag (#15661)
I changed these org-wide to make it more obvious what runner it maps to;
did not update here as I did in uv
2025-01-21 17:44:17 -06:00
Douglas Creager
ef85c682bd Remove customizable reference enum names (#15647)
The AST generator creates a reference enum for each syntax group — an
enum where each variant contains a reference to the relevant syntax
node. Previously you could customize the name of the reference enum for
a group — primarily because there was an existing `ExpressionRef` type
that wouldn't have lined up with the auto-derived name `ExprRef`. This
follow-up PR is a simple search/replace to switch over to the
auto-derived name, so that we can remove this customization point.
2025-01-21 13:46:31 -05:00
Douglas Creager
fa546b20a6 Separate grouped and ungrouped nodes more clearly in AST generator (#15646)
This is a minor cleanup to the AST generation script to make a clearer
separation between nodes that do appear in a group enum, and those that
don't. There are some types and methods that we create for every syntax
node, and others that refer to the group that the syntax node belongs
to, and which therefore don't make sense for ungrouped nodes. This new
separation makes it clearer which category each definition is in, since
you're either inside of a `for group in ast.groups` loop, or a `for node
in ast.all_nodes` loop.
2025-01-21 13:37:18 -05:00
InSync
fce4adfd41 [flake8-simplify] Mark fixes as unsafe (SIM201, SIM202) (#15626) 2025-01-21 18:17:48 +01:00
David Peter
13a6b5600b [red-knot] mdtest runner: include stderr for crashing tests (#15644)
## Summary

Test executables usually write failure messages (including panics) to
stdout, but I just managed to make a mdtest crash with
```
thread 'mdtest__unary_not' has overflowed its stack
fatal runtime error: stack overflow
```
which is printed to stderr. This test simply appends stderr to stdout
(`stderr=subprocess.STDOUT` can not be used with `capture_output`)

## Test Plan

Make sure that the error message is now visible in the output of `uv -q
run crates/red_knot_python_semantic/mdtest.py`
2025-01-21 14:59:36 +00:00
Micha Reiser
067c6de465 Change EnvironmentOptions::venv-path to Option<SystemPathBuf> (#15631)
## Summary

The `Options` struct is intended to capture the user's configuration
options but
`EnvironmentOptions::venv_path` supports both a `SitePackages::Known`
and `SitePackages::Derived`.

Users should only be able to provide `SitePackages::Derived`—they
specify a path to a venv, and Red Knot derives the path to the
site-packages directory. We'll only use the `Known` variant once we
automatically discover the Python installation.

That's why this PR changes `EnvironmentOptions::venv_path` from
`Option<SitePackages>` to `Option<SystemPathBuf>`.

This requires making some changes to the file watcher test, and I
decided to use `extra_paths` over venv path
because our venv validation is annoyingly correct -- making mocking a
venv rather involved.

## Test Plan

`cargo test`
2025-01-21 14:10:41 +00:00
David Salvisberg
4366473d9b [flake8-type-checking] Fix some safe fixes being labeled unsafe (#15638)
## Summary

We were mistakenly using `CommentRanges::has_comments` to determine
whether our edits
were safe, which sometimes expands the checked range to the end of a
line. But in order to
determine safety we need to check exactly the range we're replacing.

This bug affected the rules `runtime-cast-value` (`TC006`) and
`quoted-type-alias` (`TC008`)
although it was very unlikely to be hit for `TC006` and for `TC008` we
never hit it because we
were checking the wrong expression.

## Test Plan

`cargo nextest run`
2025-01-21 15:08:46 +01:00
Chandra Kiran G
cff9c13c42 feat: Update RUF055 to do var == value (#15605)
This commit fixes RUF055 rule to format `re.fullmatch(pattern, var)` to
`var == pattern` instead of the current `pattern == var` behaviour. This
is more idiomatic and easy to understand.

## Summary

This changes the current formatting behaviour of `re.fullmatch(pattern,
var)` to format it to `var == pattern` instead of `pattern == var`.

## Test Plan

I used a code file locally to see the updated formatting behaviour.

Fixes https://github.com/astral-sh/ruff/issues/14733
2025-01-21 08:47:06 -05:00
David Peter
4656e3c90f [red-knot] Markdown test runner (#15632)
## Summary

As more and more tests move to Markdown, running the mdtest suite
becomes one of the most common tasks for developers working on Red Knot.
There are a few pain points when doing so, however:

- The `build.rs` script enforces recompilation (~five seconds) whenever
something changes in the `resource/mdtest` folder. This is strictly
necessary, because whenever files are added or removed, the test harness
needs to be updated. But this is very rarely the case! The most common
scenario is that a Markdown file has *changed*, and in this case, no
recompilation is necessary. It is currently not possible to distinguish
these two cases using `cargo::rerun-if-changed`. One can work around
this by running the test executable manually, but it requires finding
the path to the correct `mdtest-<random-hash>` executable.
- All Markdown tests are run by default. This is needed whenever Rust
code changes, but while working on the tests themselves, it is often
much more convenient to only run the tests for a single file. This can
be done by using a `mdtest__path_to_file` filter, but this needs to be
manually spelled out or copied from the test output.
- `cargo`s test output for a failing Markdown test is often
unnecessarily verbose. Unless there is an *actual* panic somewhere in
the code, mdtests usually fail with the explicit *"Some tests failed"*
panic in the mdtest suite. But in those cases, we are not interested in
the pointer to the source of this panic, but only in the mdtest suite
output.

This PR adds a Markdown test runner tool that attempts to make the
developer experience better.

Once it is started using
```bash
uv run -q crates/red_knot_python_semantic/mdtest.py
```
it will first recompile the tests once (if cargo requires it), find the
path to the `mdtest` executable, and then enter into a mode where it
watches for changes in the `red_knot_python_semantic` crate. Whenever …
* … a Markdown file changes, it will rerun the mdtest for this specific
  file automatically (no recompilation!).
* … a Markdown file is added, it will recompile the tests and then run
  the mdtest for the new file
* … Rust code is changed, it will recompile the tests and run all of
  them

The tool also trims down `cargo test` output and only shows the actual
mdtest errors.

The tool will certainly require a few more iterations before it becomes
mature, but I'm curious to hear if there is any interest for something
like this.

## Test Plan

- Tested the new runner under various scenarios.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-21 14:06:35 +01:00
Alex Waygood
187a358d7a [red-knot] Heterogeneous tuple types with differently ordered (but equivalent) unions at the same index should be considered equivalent (#15637) 2025-01-21 12:51:20 +00:00
Calum Young
023c52d82b Standardise ruff config (#15558) 2025-01-21 12:09:11 +01:00
InSync
c616650dfa [ruff] Needless else clause (RUF047) (#15051)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-21 08:21:19 +00:00
InSync
4cfa355519 [ruff] Exempt NewType calls where the original type is immutable (RUF009) (#15588)
## Summary

Resolves #6447.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-20 20:14:47 +05:30
David Peter
134fefa945 [red-knot] Rename bindings_ty, declarations_ty (#15618)
## Summary

Rename two functions with outdated names (they used to return `Type`s):

* `bindings_ty` => `symbol_from_bindings` (returns `Symbol`)
* `declarations_ty` => `symbol_from_declarations` (returns a
`SymbolAndQualifiers` result)

I chose `symbol_from_*` instead of `*_symbol` as I found the previous
name quite confusing. Especially since `binding_ty` and `declaration_ty`
also exist (singular).

## Test Plan

—

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-20 15:23:33 +01:00
Micha Reiser
73798327c6 Flatten red_knot_project import paths (#15616) 2025-01-20 14:57:57 +01:00
wooly18
f82ef32e53 [red-knot] No cyclic-class-def diagnostics for subclasses of cyclic classes (#15561)
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-01-20 13:35:29 +00:00
renovate[bot]
2a2ac8a483 Update pre-commit dependencies (#15596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-01-20 13:10:09 +00:00
Micha Reiser
d70d959612 Rename red_knot_workspace to red_knot_project (#15615) 2025-01-20 14:02:36 +01:00
Micha Reiser
80345e72c4 show-settings: Properly filter out backslashes on windows (#15612)
<!--
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? -->

## Test Plan

<!-- How was it tested? -->
2025-01-20 10:57:21 +01:00
renovate[bot]
36c90ebe86 Update Rust crate notify to v8 (#15435)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [notify](https://redirect.github.com/notify-rs/notify) |
workspace.dependencies | major | `7.0.0` -> `8.0.0` |

---

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

---

### Release Notes

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

###
[`v8.0.0`](https://redirect.github.com/notify-rs/notify/blob/HEAD/CHANGELOG.md#notify-800-2025-01-10)

[Compare
Source](https://redirect.github.com/notify-rs/notify/compare/notify-7.0.0...notify-8.0.0)

-   CHANGE: update notify-types to version 2.0.0
-   CHANGE: raise MSRV to 1.77 **breaking**
- FEATURE: add config option to disable following symbolic links
[#&#8203;635]
- FIX: unaligned access to FILE_NOTIFY_INFORMATION [#&#8203;647]
**breaking**

[#&#8203;635]: https://redirect.github.com/notify-rs/notify/pull/635

[#&#8203;647]: https://redirect.github.com/notify-rs/notify/pull/647

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS45Mi4wIiwidXBkYXRlZEluVmVyIjoiMzkuOTIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 10:41:16 +01:00
Wei Lee
cbf9b66fc1 [airflow] Extend AIR303 with more symbols (#15611)
## Summary

Extend `AIR303` with the following rules

* `airflow.operators.datetime.*` → `airflow.providers.standard.time.operators.datetime.*`
* `airflow.operators.weekday.*` → `airflow.providers.standard.time.operators.weekday.*`
* `airflow.sensors.date_time.*` → `airflow.providers.standard.time.sensors.date_time.*`
* `airflow.sensors.time_sensor.*` → `airflow.providers.standard.time.sensors.time.*`
* `airflow.sensors.time_delta.*` → `airflow.providers.standard.time.sensors.time_delta.*`
* `airflow.sensors.weekday.*` → `airflow.providers.standard.time.sensors.weekday.*`
* `airflow.hooks.filesystem.*` → `airflow.providers.standard.hooks.filesystem.*`
* `airflow.hooks.package_index.*` → `airflow.providers.standard.hooks.package_index.*`
* `airflow.hooks.subprocess.*` → `airflow.providers.standard.hooks.subprocess.*`
* `airflow.triggers.external_task.*` → `airflow.providers.standard.triggers.external_task.*`
* `airflow.triggers.file.*` → `airflow.providers.standard.triggers.file.*`
* `airflow.triggers.temporal.*` → `airflow.providers.standard.triggers.temporal.*`
* `airflow.sensors.filesystem.FileSensor` → `airflow.providers.standard.sensors.filesystem.FileSensor`
* `airflow.operators.trigger_dagrun.TriggerDagRunOperator` → `airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunOperator`
* `airflow.sensors.external_task.ExternalTaskMarker` → `airflow.providers.standard.sensors.external_task.ExternalTaskMarker`
* `airflow.sensors.external_task.ExternalTaskSensor` → `airflow.providers.standard.sensors.external_task.ExternalTaskSensor`

## Test Plan

a test fixture has been updated
2025-01-20 15:00:26 +05:30
Micha Reiser
248f0ec6b2 Isolate show_settings test from Ruff's `pyproject.toml (#15610)
## Summary

In preperation for https://github.com/astral-sh/ruff/pull/15558

Isolate the `show_settings` test instead of reading Ruff's
`pyproject.toml` for better test isolation.

## Test Plan

`cargo test`
2025-01-20 09:28:01 +00:00
Dhruv Manilawala
d8cabf62b1 Avoid large closure to make rustfmt work (#15609)
## Summary

I noticed this while reviewing
https://github.com/astral-sh/ruff/pull/15541 that the code inside the
large closure cannot be formatted by the Rust formatter. This PR
extracts the qualified name and inlines the match expression.

## Test Plan

`cargo clippy` and `cargo insta`
2025-01-20 09:15:48 +00:00
David Peter
912247635d [red-knot] Move Ty enum to property tests (#15608)
## Summary

Move the `Ty` enum into the `property_tests` module, as it was only used
in a single place in `types.rs`.
2025-01-20 10:15:31 +01:00
InSync
5cd1f79864 [flake8-bandit] Report all references to suspicious functions (S3) (#15541)
## Summary

Resolves #15522.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-01-20 09:02:53 +00:00
David Peter
4eb465ee95 [red-knot] Move type_alias_types test to Markdown (#15607)
## Summary

Move `type_alias_types` test to Markdown

## Test Plan

New MD test
2025-01-20 09:55:54 +01:00
David Peter
9725a2d476 [red-knot] More exhaustive disjointness tests (#15606)
## Summary

Mostly just brings the structure/format of the disjointness-tests closer
to what we have for `is_subtype_of` etc.
2025-01-20 09:47:51 +01:00
InSync
975d1457c5 [red-knot] Migrate is_disjoint_from unit tests to Markdown tests (#15580)
## Summary

Part of and resolves #15397, built on top of #15579.

## Test Plan

Markdown tests.
2025-01-20 08:42:22 +01:00
Dhruv Manilawala
d97502d647 Include more labels in CHANGELOG sections (#15566)
## Summary

This PR updates Rooster config to include more labels, specifically:
* Include
[`diagnostics`](https://github.com/astral-sh/ruff/labels/diagnostics),
[`docstring`](https://github.com/astral-sh/ruff/labels/docstring),
[`fixes`](https://github.com/astral-sh/ruff/labels/fixes) and
[`isort`](https://github.com/astral-sh/ruff/labels/isort) for "Rule
changes" section
* Use
[`performance`](https://github.com/astral-sh/ruff/labels/performance)
for "Performance" section. We already have included "Performance"
section in various version, so let's make it official :)
* Ignore [`testing`](https://github.com/astral-sh/ruff/labels/testing)
label

The following is open:
* [`suppression`](https://github.com/astral-sh/ruff/labels/suppression)
2025-01-20 10:34:22 +05:30
renovate[bot]
15c6cb50a0 Update NPM Development dependencies (#15601)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[@types/react](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react))
| [`19.0.6` ->
`19.0.7`](https://renovatebot.com/diffs/npm/@types%2freact/19.0.6/19.0.7)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2freact/19.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2freact/19.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2freact/19.0.6/19.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2freact/19.0.6/19.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/eslint-plugin](https://typescript-eslint.io/packages/eslint-plugin)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin))
| [`8.19.1` ->
`8.20.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/8.19.1/8.20.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2feslint-plugin/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2feslint-plugin/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2feslint-plugin/8.19.1/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2feslint-plugin/8.19.1/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/parser](https://typescript-eslint.io/packages/parser)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser))
| [`8.19.1` ->
`8.20.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/8.19.1/8.20.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2fparser/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2fparser/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2fparser/8.19.1/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2fparser/8.19.1/8.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[eslint-config-prettier](https://redirect.github.com/prettier/eslint-config-prettier)
| [`^9.0.0` ->
`^10.0.0`](https://renovatebot.com/diffs/npm/eslint-config-prettier/9.1.0/10.0.1)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-config-prettier/10.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-config-prettier/10.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-config-prettier/9.1.0/10.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-config-prettier/9.1.0/10.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[eslint-plugin-react](https://redirect.github.com/jsx-eslint/eslint-plugin-react)
| [`7.37.3` ->
`7.37.4`](https://renovatebot.com/diffs/npm/eslint-plugin-react/7.37.3/7.37.4)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-react/7.37.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-plugin-react/7.37.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-plugin-react/7.37.3/7.37.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-react/7.37.3/7.37.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[miniflare](https://redirect.github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#readme)
([source](https://redirect.github.com/cloudflare/workers-sdk/tree/HEAD/packages/miniflare))
| [`3.20241230.1` ->
`3.20241230.2`](https://renovatebot.com/diffs/npm/miniflare/3.20241230.1/3.20241230.2)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/miniflare/3.20241230.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/miniflare/3.20241230.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/miniflare/3.20241230.1/3.20241230.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/miniflare/3.20241230.1/3.20241230.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [postcss](https://postcss.org/)
([source](https://redirect.github.com/postcss/postcss)) | [`8.4.49` ->
`8.5.1`](https://renovatebot.com/diffs/npm/postcss/8.4.49/8.5.1) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/postcss/8.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/postcss/8.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/postcss/8.4.49/8.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/postcss/8.4.49/8.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [wrangler](https://redirect.github.com/cloudflare/workers-sdk)
([source](https://redirect.github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler))
| [`3.101.0` ->
`3.103.2`](https://renovatebot.com/diffs/npm/wrangler/3.101.0/3.103.2) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/wrangler/3.103.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/wrangler/3.103.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/wrangler/3.101.0/3.103.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/wrangler/3.101.0/3.103.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

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

---

### Release Notes

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/eslint-plugin)</summary>

###
[`v8.20.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8200-2025-01-13)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.19.1...v8.20.0)

##### 🚀 Features

- **eslint-plugin:** \[consistent-type-assertions] add
arrayLiteralTypeAssertions options
([#&#8203;10565](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10565))
- **eslint-plugin:** \[no-deprecated] add allow options
([#&#8203;10585](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10585))
- **eslint-plugin:** \[no-misused-spread] add new rule
([#&#8203;10551](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10551))

##### 🩹 Fixes

- **eslint-plugin:** \[no-unnecessary-condition] don't flag optional
chaining for union types with an unconstrained type parameters
([#&#8203;10602](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10602))
- **eslint-plugin:** \[no-shadow] ignore ordering of type declarations
([#&#8203;10593](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10593))

##### ❤️ Thank You

-   Josh Goldberg 
-   Ronen Amiel
-   YeonJuan [@&#8203;yeonjuan](https://redirect.github.com/yeonjuan)

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/parser)</summary>

###
[`v8.20.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#8200-2025-01-13)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.19.1...v8.20.0)

This was a version bump only for parser to align it with other projects,
there were no code changes.

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>prettier/eslint-config-prettier
(eslint-config-prettier)</summary>

###
[`v10.0.1`](https://redirect.github.com/prettier/eslint-config-prettier/releases/tag/v10.0.1)

[Compare
Source](https://redirect.github.com/prettier/eslint-config-prettier/compare/v10.0.0...v10.0.1)

### eslint-config-prettier

#### 10.0.1

#### What's Changed

- chore: migrate to changeset for automatically releasing by
[@&#8203;JounQin](https://redirect.github.com/JounQin) in
[https://github.com/prettier/eslint-config-prettier/pull/278](https://redirect.github.com/prettier/eslint-config-prettier/pull/278)
- add support for `@stylistic/eslint-plugin` by
[@&#8203;abrahamguo](https://redirect.github.com/abrahamguo) in
[https://github.com/prettier/eslint-config-prettier/pull/272](https://redirect.github.com/prettier/eslint-config-prettier/pull/272)

#### New Contributors

- [@&#8203;JounQin](https://redirect.github.com/JounQin) made their
first contribution in
[https://github.com/prettier/eslint-config-prettier/pull/278](https://redirect.github.com/prettier/eslint-config-prettier/pull/278)
- [@&#8203;abrahamguo](https://redirect.github.com/abrahamguo) made
their first contribution in
[https://github.com/prettier/eslint-config-prettier/pull/272](https://redirect.github.com/prettier/eslint-config-prettier/pull/272)

**Full Changelog**:
https://github.com/prettier/eslint-config-prettier/compare/v9.1.0...v10.0.1

###
[`v10.0.0`](https://redirect.github.com/prettier/eslint-config-prettier/blob/HEAD/CHANGELOG.md#1000)

[Compare
Source](https://redirect.github.com/prettier/eslint-config-prettier/compare/v9.1.0...v10.0.0)

##### Major Changes

-
[#&#8203;272](https://redirect.github.com/prettier/eslint-config-prettier/pull/272)
[`5be64be`](5be64bef68)
Thanks [@&#8203;abrahamguo](https://redirect.github.com/abrahamguo)! -
add support for
[@&#8203;stylistic](https://redirect.github.com/stylistic) formatting
rules

</details>

<details>
<summary>jsx-eslint/eslint-plugin-react (eslint-plugin-react)</summary>

###
[`v7.37.4`](https://redirect.github.com/jsx-eslint/eslint-plugin-react/blob/HEAD/CHANGELOG.md#7374---20250112)

[Compare
Source](https://redirect.github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.3...v7.37.4)

##### Fixed

- \[`no-unknown-property`]: support `onBeforeToggle`, `popoverTarget`,
`popoverTargetAction` attributes ([#&#8203;3865][]
[@&#8203;acusti](https://redirect.github.com/acusti))
- \[types] fix types of flat configs ([#&#8203;3874][]
[@&#8203;ljharb](https://redirect.github.com/ljharb))

[7.37.4]:
https://redirect.github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.3...v7.37.4

[#&#8203;3874]:
https://redirect.github.com/jsx-eslint/eslint-plugin-react/pull/3874

[#&#8203;3865]:
https://redirect.github.com/jsx-eslint/eslint-plugin-react/pull/3865

</details>

<details>
<summary>cloudflare/workers-sdk (miniflare)</summary>

###
[`v3.20241230.2`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/miniflare/CHANGELOG.md#3202412302)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/miniflare@3.20241230.1...miniflare@3.20241230.2)

##### Patch Changes

-
[#&#8203;7738](https://redirect.github.com/cloudflare/workers-sdk/pull/7738)
[`8e9aa40`](8e9aa40a6c)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - Use
TEXT bindings for plain text values in Miniflare. This is an internal
detail that should have no user facing impact.

</details>

<details>
<summary>postcss/postcss (postcss)</summary>

###
[`v8.5.1`](https://redirect.github.com/postcss/postcss/blob/HEAD/CHANGELOG.md#851)

[Compare
Source](https://redirect.github.com/postcss/postcss/compare/8.5.0...8.5.1)

- Fixed backwards compatibility for complex cases (by
[@&#8203;romainmenke](https://redirect.github.com/romainmenke)).

###
[`v8.5.0`](https://redirect.github.com/postcss/postcss/releases/tag/8.5.0):
8.5 “Duke Alloces”

[Compare
Source](https://redirect.github.com/postcss/postcss/compare/8.4.49...8.5.0)

<img
src="https://github.com/user-attachments/assets/6ef654a0-d675-4ba0-a670-e28ef27062f5"
align="right" width="200" height="200" alt="President Alloces seal">

PostCSS 8.5 brought API to work better with non-CSS sources like HTML,
Vue.js/Svelte sources or CSS-in-JS.

[@&#8203;romainmenke](https://redirect.github.com/romainmenke) during
[his work](https://redirect.github.com/postcss/postcss/issues/1995) on
[Stylelint](https://stylelint.io) added `Input#document` in additional
to `Input#css`.

```js
root.source.input.document //=> "<p>Hello</p>
                           //    <style>
                           //    p {
                           //      color: green;
                           //    }
                           //    </style>"
root.source.input.css      //=> "p {
                           //      color: green;
                           //    }"

```

#### Thanks to Sponsors

This release was possible thanks to our community.

If your company wants to support the sustainability of front-end
infrastructure or wants to give some love to PostCSS, you can join our
supporters by:

- [**Tidelift**](https://tidelift.com/) with a Spotify-like subscription
model supporting all projects from your lock file.
- Direct donations at [**GitHub
Sponsors**](https://redirect.github.com/sponsors/ai) or [**Open
Collective**](https://opencollective.com/postcss#section-contributors).

</details>

<details>
<summary>cloudflare/workers-sdk (wrangler)</summary>

###
[`v3.103.2`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#31032)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.103.1...wrangler@3.103.2)

##### Patch Changes

-
[#&#8203;7804](https://redirect.github.com/cloudflare/workers-sdk/pull/7804)
[`16a9460`](16a9460ea6)
Thanks [@&#8203;vicb](https://redirect.github.com/vicb)! -
fix(wrangler): use require.resolve to resolve unenv path

###
[`v3.103.1`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#31031)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.103.0...wrangler@3.103.1)

##### Patch Changes

-
[#&#8203;7798](https://redirect.github.com/cloudflare/workers-sdk/pull/7798)
[`a1ff045`](a1ff045cfc)
Thanks
[@&#8203;CarmenPopoviciu](https://redirect.github.com/CarmenPopoviciu)!
- Reverts
[#&#8203;7720](https://redirect.github.com/cloudflare/workers-sdk/issues/7720)
as it introduced breakage in some of the C3 templates (eg. Nuxt)

###
[`v3.103.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#31030)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.102.0...wrangler@3.103.0)

##### Minor Changes

-
[#&#8203;5086](https://redirect.github.com/cloudflare/workers-sdk/pull/5086)
[`8faf2c0`](8faf2c0741)
Thanks
[@&#8203;dario-piotrowicz](https://redirect.github.com/dario-piotrowicz)!
- add `--strict-vars` option to `wrangler types`

add a new `--strict-vars` option to `wrangler types` that developers can
(by setting the
flag to `false`) use to disable the default strict/literal types
generation for their variables

opting out of strict variables can be useful when developers change
often their `vars` values,
    even more so when multiple environments are involved

###
[`v3.102.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#31020)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.101.0...wrangler@3.102.0)

##### Minor Changes

-
[#&#8203;7592](https://redirect.github.com/cloudflare/workers-sdk/pull/7592)
[`f613276`](f6132761c8)
Thanks [@&#8203;garrettgu10](https://redirect.github.com/garrettgu10)! -
New filter validation logic supporting set and range queries in
Vectorize CLI

##### Patch Changes

-
[#&#8203;7750](https://redirect.github.com/cloudflare/workers-sdk/pull/7750)
[`df0e5be`](df0e5bef81)
Thanks [@&#8203;andyjessop](https://redirect.github.com/andyjessop)! -
bug: Removes the (local) tag on Vectorize bindings in the console output
of `wrangler dev`, and adds-in the same tag for Durable Objects (which
are emulated locally in `wrangler dev`).

-
[#&#8203;7732](https://redirect.github.com/cloudflare/workers-sdk/pull/7732)
[`d102b60`](d102b60238)
Thanks [@&#8203;Ankcorn](https://redirect.github.com/Ankcorn)! - fix
pages secret bulk copy

-
[#&#8203;7706](https://redirect.github.com/cloudflare/workers-sdk/pull/7706)
[`c63f1b0`](c63f1b0790)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! -
Remove the server-based dev registry in favour of the more stable
file-based dev registry. There should be no user-facing impact.

- Updated dependencies
\[[`8e9aa40`](8e9aa40a6c)]:
    -   miniflare@3.20241230.2

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xMDcuMCIsInVwZGF0ZWRJblZlciI6IjM5LjEwNy4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 10:25:36 +05:30
Dhruv Manilawala
613951d848 Ignore notebook with syntax error in ecosystem (#15604)
I'll open a PR in `openai/openai-cookbook` to fix these syntax errors
later but for now let's just ignore the notebook.

<img width="2560" alt="Screenshot 2025-01-20 at 10 17 43 AM"
src="https://github.com/user-attachments/assets/075127e4-956d-43f9-b175-b492600dbdee"
/>
2025-01-20 04:53:22 +00:00
renovate[bot]
070c160fd5 Update dependency tomli_w to v1.2.0 (#15600)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [tomli_w](https://redirect.github.com/hukkin/tomli-w)
([changelog](https://redirect.github.com/hukkin/tomli-w/blob/master/CHANGELOG.md))
| `==1.1.0` -> `==1.2.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/tomli-w/1.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/tomli-w/1.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/tomli-w/1.1.0/1.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/tomli-w/1.1.0/1.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

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

---

### Release Notes

<details>
<summary>hukkin/tomli-w (tomli_w)</summary>

###
[`v1.2.0`](https://redirect.github.com/hukkin/tomli-w/blob/HEAD/CHANGELOG.md#120)

[Compare
Source](https://redirect.github.com/hukkin/tomli-w/compare/1.1.0...1.2.0)

-   Added
    -   Always preserve `decimal.Decimal` type in parse round trips
-   Improved
    -   Better `TypeError` message on invalid mapping keys
    -   Unify type representation in error messages
-   Performance
    -   Improve import time by removing `typing` import
    -   Improve import time by removing `string` import
    -   Improve import time by lazy importing `decimal`

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xMDcuMCIsInVwZGF0ZWRJblZlciI6IjM5LjEwNy4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 10:18:43 +05:30
renovate[bot]
c0383daf58 Update Rust crate similar to v2.7.0 (#15602) 2025-01-19 22:46:45 -05:00
renovate[bot]
780335b50d Update Rust crate uuid to v1.12.0 (#15599) 2025-01-19 22:45:12 -05:00
renovate[bot]
07f2308557 Update cloudflare/wrangler-action action to v3.13.1 (#15594) 2025-01-19 22:31:47 -05:00
renovate[bot]
06d9f8ba00 Update Rust crate tracing-indicatif to v0.3.9 (#15593) 2025-01-19 22:31:38 -05:00
renovate[bot]
57a84a3354 Update dependency ruff to v0.9.2 (#15595) 2025-01-19 22:31:26 -05:00
renovate[bot]
b49dee0959 Update Rust crate bitflags to v2.8.0 (#15597) 2025-01-19 22:31:19 -05:00
renovate[bot]
68755ac1e3 Update Rust crate ordermap to v0.5.5 (#15591) 2025-01-19 22:31:02 -05:00
renovate[bot]
b9402688b1 Update Rust crate serde_json to v1.0.137 (#15592) 2025-01-19 22:30:54 -05:00
renovate[bot]
75eccfd2a2 Update Rust crate log to v0.4.25 (#15590) 2025-01-19 22:30:32 -05:00
renovate[bot]
3c043e7e70 Update Rust crate indexmap to v2.7.1 (#15589) 2025-01-19 22:30:24 -05:00
Charlie Marsh
98fccec2e7 Avoid removing too many imports in redefined-while-unused (#15585)
## Summary

Closes https://github.com/astral-sh/ruff/issues/15583.
2025-01-19 13:28:08 -05:00
InSync
444f799f5e [red-knot] Two gradual equivalent fully static types are also equivalent (#15579) 2025-01-19 16:37:22 +00:00
Alex Waygood
2b24b3b316 [red-knot] Ensure differently ordered unions and intersections are considered equivalent (#15516) 2025-01-19 16:10:42 +00:00
Charlie Marsh
b8e5b95423 Avoid quadratic membership check in import fixes (#15576)
## Summary

This leads to an explosion in runtime for (admittedly absurd) cases with
tens of thousands of imports.
2025-01-18 23:01:26 +00:00
Charlie Marsh
6004c8c003 Apply redefinition fixes by source code order (#15575)
## Summary

Right now, these are being applied in random order, since if we have two
`RedefinitionWhileUnused`, it just takes the first-generated (whereas
the next comparator in the sort here orders by location)... Which means
we frequently have to re-run!
2025-01-18 17:44:10 -05:00
Charlie Marsh
1344c8a4e2 Group redefinition fixes by source statement (#15574)
## Summary

Like unused imports, we should create a single fix for all redefined
members in a single statement.

Closes https://github.com/astral-sh/ruff/issues/15182.
2025-01-18 17:31:58 -05:00
Dylan
8a50f3f361 [isort] Omit trailing whitespace in unsorted-imports (I001) (#15518)
## Summary
The fix range for sorting imports accounts for trailing whitespace, but
we should only show the trimmed range to the user when displaying the
diagnostic. So this PR changes the diagnostic range.

Closes #15504 

## Test Plan

Reviewed snapshot changes
2025-01-18 11:08:58 -06:00
InSync
001e5adec5 [flake8-simplify] Avoid double negations (SIM103) (#15562)
## Summary

Related to [this
comment](https://github.com/astral-sh/ruff/issues/6184#issuecomment-2578673788)
at #6184.

---------

Co-authored-by: Dylan <53534755+dylwil3@users.noreply.github.com>
2025-01-18 17:06:46 +00:00
Will Lachance
38adc7f702 TRY300: Add some extra notes on not catching exceptions you didn't expect (#15036)
## Summary

Added some extra notes on why you should have focused try...except
blocks to
[TRY300](https://docs.astral.sh/ruff/rules/try-consider-else/).

When fixing a violation of this rule, a co-worker of mine (very
understandably) asked why this was better. The current docs just say
putting the return in the else is "more explicit", but if you look at
the [linked reference in the python
documentation](https://docs.python.org/3/tutorial/errors.html) they are
more clear on why violations like this is bad:

> The use of the else clause is better than adding additional code to
the [try](https://docs.python.org/3/reference/compound_stmts.html#try)
clause because it avoids accidentally catching an exception that wasn’t
raised by the code being protected by the try … except statement.

This is my attempt at adding more context to the docs on this. Open to
suggestions for wording!

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-18 10:23:43 -06:00
Dylan
4caeeb8d98 [pylint] Include name of base class in message for redefined-slots-in-subclass (W0244) (#15559)
In the following situation:

```python
class Grandparent:
  __slots__ = "a"

class Parent(Grandparent): ...

class Child(Parent):
  __slots__ = "a"
```

the message for `W0244` now specifies that `a` is overwriting a slot
from `Grandparent`.

To implement this, we introduce a helper function `iter_super_classes`
which does a breadth-first traversal of the superclasses of a given
class (as long as they are defined in the same file, due to the usual
limitations of the semantic model).

Note: Python does not allow conflicting slots definitions under multiple
inheritance. Unless I'm misunderstanding something, I believe It follows
that the subposet of superclasses of a given class that redefine a given
slot is in fact totally ordered. There is therefore a unique _nearest_
superclass whose slot is being overwritten. So, you know, in case anyone
was super worried about that... you can just chill.

This is a followup to #9640 .
2025-01-18 09:50:27 -06:00
David Peter
fb15da5694 [red-knot] Add support for typing.ClassVar (#15550)
## Summary

Add support for `typing.ClassVar`, i.e. emit a diagnostic in this
scenario:
```py
from typing import ClassVar

class C:
    x: ClassVar[int] = 1

c = C()
c.x = 3  # error: "Cannot assign to pure class variable `x` from an instance of type `C`"
```

## Test Plan

- New tests for the `typing.ClassVar` qualifier
- Fixed one TODO in `attributes.md`
2025-01-18 13:51:35 +01:00
InSync
9730ff3a25 Generate documentation redirects for lowercase rule codes (#15564)
## Summary

Resolves #15016.

## Test Plan

Generate the docs with:

```console
uv run --with-requirements docs/requirements-insiders.txt scripts/generate_mkdocs.py
```

and, check whether the mapping was created in `mkdocs.generated.yml` and run the server using:

```console
uvx --with-requirements docs/requirements-insiders.txt -- mkdocs serve -f mkdocs.insiders.yml -o
```
2025-01-18 10:09:23 +05:30
InSync
9d845ec8f5 [red-knot] Migrate is_gradual_equivalent_to unit tests to Markdown tests (#15563)
## Summary

Part of #15397 and #15516.

## Test Plan

Markdown tests.
2025-01-17 16:48:01 -08:00
Douglas Creager
98ef564170 Remove AstNode and AnyNode (#15479)
While looking into potential AST optimizations, I noticed the `AstNode`
trait and `AnyNode` type aren't used anywhere in Ruff or Red Knot. It
looks like they might be historical artifacts of previous ways of
consuming AST nodes?

- `AstNode::cast`, `AstNode::cast_ref`, and `AstNode::can_cast` are not
used anywhere.
- Since `cast_ref` isn't needed anymore, the `Ref` associated type isn't
either.

This is a pure refactoring, with no intended behavior changes.
2025-01-17 17:11:00 -05:00
Douglas Creager
8e3633f55a Auto-generate AST boilerplate (#15544)
This PR replaces most of the hard-coded AST definitions with a
generation script, similar to what happens in `rust_python_formatter`.
I've replaced every "rote" definition that I could find, where the
content is entirely boilerplate and only depends on what syntax nodes
there are and which groups they belong to.

This is a pretty massive diff, but it's entirely a refactoring. It
should make absolutely no changes to the API or implementation. In
particular, this required adding some configuration knobs that let us
override default auto-generated names where they don't line up with
types that we created previously by hand.

## Test plan

There should be no changes outside of the `rust_python_ast` crate, which
verifies that there were no API changes as a result of the
auto-generation. Aggressive `cargo clippy` and `uvx pre-commit` runs
after each commit in the branch.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-17 14:23:02 -05:00
Alex Waygood
4351d85d24 [red-knot] Inline SubclassOfType::as_instance_type_of_metaclass() (#15556) 2025-01-17 19:01:36 +00:00
wooly18
1ba8e61875 [flake8-comprehensions] strip parentheses around generators in unnecessary-generator-set (C401) (#15553)
<!--
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

Fixes parentheses not being stripped in C401. Pretty much the same as
#11607 which fixed it for C400.

## Test Plan
`cargo nextest run`
2025-01-17 18:08:22 +01:00
Akira Noda
5cdac2533e [pylint] Implement redefined-slots-in-subclass (W0244) (#9640)
## Summary

- Implementation of [redefined-slots-in-subclass /
W0244](https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/redefined-slots-in-subclass.html).
- Related to #970

---------

Co-authored-by: Akira Noda <akira.noda@onecareer.com>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-17 09:54:15 -06:00
guillaumeLepape
4fdf8af747 [flake8-bugbear] Do not raise error if keyword argument is present and target-python version is less or equals than 3.9 (B903) (#15549) 2025-01-17 12:48:14 +01:00
Alex Waygood
4328df7226 [red-knot] type[T] is disjoint from type[S] if the metaclass of T is disjoint from the metaclass of S (#15547) 2025-01-17 10:41:36 +00:00
David Peter
6771b8ebd2 [red-knot] Pure instance variables declared in class body (#15515)
## Summary

This is a small, tentative step towards the bigger goal of understanding
instance attributes.

- Adds partial support for pure instance variables declared in the class
  body, i.e. this case:
  ```py
  class C:
      variable1: str = "a"
      variable2 = "b"

  reveal_type(C().variable1)  # str
  reveal_type(C().variable2)  # Unknown | Literal["b"]
  ```
- Adds `property` as a known class to query for `@property` decorators
- Splits up various `@Todo(instance attributes)` cases into
  sub-categories.

## Test Plan

Modified existing MD tests.
2025-01-17 10:48:20 +01:00
Micha Reiser
dbb2efdb87 Update snapshots of #15507 with new annotated snipetts rendering (#15546) 2025-01-17 09:39:15 +00:00
InSync
dbfdaaded1 [pylint] Do not report methods with only one EM101-compatible raise (PLR6301) (#15507) 2025-01-17 10:17:39 +01:00
Micha Reiser
1ecb7ce645 Fix unstable f-string formatting for expressions containing a trailing comma (#15545) 2025-01-17 10:08:09 +01:00
Micha Reiser
fdb9f4e404 Support knot.toml files in project discovery (#15505) 2025-01-17 09:01:58 +00:00
Micha Reiser
eb47a6634d Add support for configuring knot in pyproject.toml files (#15493)
## Summary

This PR adds support for configuring Red Knot in the `tool.knot` section
of the project's
`pyproject.toml` section. Options specified on the CLI precede the
options in the configuration file.

This PR only supports the `environment` and the `src.root` options for
now.
Other options will be added as separate PRs.

There are also a few concerns that I intentionally ignored as part of
this PR:

* Handling of relative paths: We need to anchor paths relative to the
current working directory (CLI), or the project (`pyproject.toml` or
`knot.toml`)
* Tracking the source of a value. Diagnostics would benefit from knowing
from which configuration a value comes so that we can point the user to
the right configuration file (or CLI) if the configuration is invalid.
* Schema generation and there's a lot more; see
https://github.com/astral-sh/ruff/issues/15491

This PR changes the default for first party codes: Our existing default
was to only add the project root. Now, Red Knot adds the project root
and `src` (if such a directory exists).

Theoretically, we'd have to add a file watcher event that changes the
first-party search paths if a user later creates a `src` directory. I
think this is pretty uncommon, which is why I ignored the complexity for
now but I can be persuaded to handle it if it's considered important.

Part of https://github.com/astral-sh/ruff/issues/15491

## Test Plan

Existing tests, new file watching test demonstrating that changing the
python version and platform is correctly reflected.
2025-01-17 09:41:06 +01:00
Micha Reiser
9ed67ba33e Fix bracket spacing for single-element tuples in f-string expressions (#15537) 2025-01-17 08:02:34 +00:00
InSync
556116ee76 [flake8-simplify] Do not emit diagnostics for expressions inside string type annotations (SIM222, SIM223) (#15405)
## Summary

Resolves #7127.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-17 12:18:35 +05:30
InSync
7ddf59be5f [flake8-pytest-style] Do not emit diagnostics for empty for loops (PT012, PT031) (#15542)
## Summary

Resolves #9730.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2025-01-17 01:44:07 +00:00
InSync
fa239f76ea [pyupgrade] Avoid syntax error when the iterable is an non-parenthesized tuple (UP028) (#15543)
## Summary

Resolves #15540.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-16 20:13:50 -05:00
Alex Waygood
3950b00ee4 [red-knot] Implement disjointness for Instance types where the underlying class is @final (#15539)
## Summary

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

For any two instance types `T` and `S`, we know they are disjoint if
either `T` is final and `T` is not a subclass of `S` or `S` is final and
`S` is not a subclass of `T`.

Correspondingly, for any two types `type[T]` and `S` where `S` is an
instance type, `type[T]` can be said to be disjoint from `S` if `S` is
disjoint from `U`, where `U` is the type that represents all instances
of `T`'s metaclass.

And a heterogeneous tuple type can be said to be disjoint from an
instance type if the instance type is disjoint from `tuple` (a type
representing all instances of the `tuple` class at runtime).

## Test Plan

- A new mdtest added. Most of our `is_disjoint_from()` tests are not
written as mdtests just yet, but it's pretty hard to test some of these
edge cases from a Rust unit test!
- Ran `QUICKCHECK_TESTS=1000000 cargo test --release -p
red_knot_python_semantic -- --ignored types::property_tests::stable`

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-01-16 23:48:52 +00:00
Auguste Lalande
e84c82424d [pydoclint] Allow ignoring one line docstrings for DOC rules (#13302)
## Summary

Add a setting to allow ignoring one line docstrings for the pydoclint
rules.

Resolves #13086

Part of #12434

## Test Plan

Run tests with setting enabled.

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-16 16:05:10 -06:00
Aleksei Latyshev
177bf72598 [refurb] Implement for-loop-writes (FURB122) (#10630)
## Summary
Implement `for-loop-writes` (FURB122) lint
- https://github.com/astral-sh/ruff/issues/1348
- [original
lint](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/writelines.py)

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-16 15:02:46 -06:00
InSync
2e6729d900 [red-knot] Migrate bool/str/repr unit tests to Markdown tests (#15534)
## Summary

Part of #15397.

## Test Plan

Markdown tests.
2025-01-16 11:21:56 -08:00
Brent Westbrook
e2da33a45c [unconventional-import-alias] Fix infinite loop between ICN001 and I002 (ICN001) (#15480)
## Summary

This fixes the infinite loop reported in #14389 by raising an error to
the user about conflicting ICN001 (`unconventional-import-alias`) and
I002 (`missing-required-import`) configuration options.

## Test Plan

Added a CLI integration test reproducing the old behavior and then
confirming the fix.

Closes #14389

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-16 10:45:24 -05:00
Brent Westbrook
ca3b210f2e [pyflakes] Fix infinite loop with unused local import in __init__.py (F401) (#15517)
## Summary

This fixes the infinite loop reported in #12897, where an
`unused-import` that is undefined at the scope of `__all__` is "fixed"
by adding it to `__all__` repeatedly. These changes make it so that only
imports in the global scope will be suggested to add to `__all__` and
the unused local import is simply removed.

## Test Plan

Added a CLI integration test that sets up the same module structure as
the original report

Closes #12897

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-16 10:43:32 -05:00
InSync
6f0b66278f [red-knot] Migrate is_fully_static/is_single_valued/is_singleton unit tests to Markdown tests (#15533)
## Summary

Part of #15397.

## Test Plan

Markdown tests.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-16 07:40:41 -08:00
InSync
aed0bf1c11 [ruff] itertools.starmap(..., zip(...)) (RUF058) (#15483)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-16 15:18:12 +01:00
Dhruv Manilawala
c20255abe4 Bump version to 0.9.2 (#15529)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-16 13:07:26 +00:00
Micha Reiser
420365811f Fix joining of f-strings with different quotes when using quote style Preserve (#15524) 2025-01-16 12:01:42 +01:00
Wei Lee
fc9dd63d64 [airflow] extend and fix AIR302 rules (#15525) 2025-01-16 10:40:00 +01:00
Dhruv Manilawala
79e52c7fdf [pyflakes] Show syntax error message for F722 (#15523)
## Summary

Ref: https://github.com/astral-sh/ruff/pull/15387#discussion_r1917796907

This PR updates `F722` to show syntax error message instead of the
string content.

I think it's more useful to show the syntax error message than the
string content. In the future, when the diagnostics renderer is more
capable, we could even highlight the exact location of the syntax error
along with the annotation string.

This is also in line with how we show the diagnostic in red knot.

## Test Plan

Update existing test snapshots.
2025-01-16 12:44:01 +05:30
Shaygan Hooshyari
cf4ab7cba1 Parse triple quoted string annotations as if parenthesized (#15387)
## Summary

Resolves #9467 

Parse quoted annotations as if the string content is inside parenthesis.
With this logic `x` and `y` in this example are equal:

```python
y: """
   int |
   str
"""

z: """(
    int |
    str
)
"""
```

Also this rule only applies to triple
quotes([link](https://github.com/python/typing-council/issues/9#issuecomment-1890808610)).

This PR is based on the
[comments](https://github.com/astral-sh/ruff/issues/9467#issuecomment-2579180991)
on the issue.

I did one extra change, since we don't want any indentation tokens I am
setting the `State::Other` as the initial state of the Lexer.

Remaining work:

- [x] Add a test case for red-knot.
- [x] Add more tests.

## Test Plan

Added a test which previously failed because quoted annotation contained
indentation.
Added an mdtest for red-knot.
Updated previous test.

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-16 11:38:15 +05:30
Dylan
d2656e88a3 [flake8-todos] Allow VSCode GitHub PR extension style links in missing-todo-link (TD003) (#15519)
## Summary
Allow links to issues that appear on the same line as the TODO
directive, if they conform to the format that VSCode's GitHub PR
extension produces.

Revival of #9627 (the branch was stale enough that rebasing was a lot
harder than just making the changes anew). Credit should go to the
author of that PR though.

Closes #8061

Co-authored-by: Martin Bernstorff <martinbernstorff@gmail.com>
2025-01-15 23:47:33 +00:00
Alex Waygood
c53ee608a1 Typeshed-sync workflow: add appropriate labels, link directly to failing run (#15520) 2025-01-15 23:42:35 +00:00
David Peter
c034e280a9 [red-knot] Instance attributes: type inference clarifications (#15512)
## Summary

Some clarifications in the instance-attributes tests, mostly regarding
type inference behavior following this discussion:

https://github.com/astral-sh/ruff/pull/15474#discussion_r1917044566
2025-01-15 21:17:55 +01:00
Alex Waygood
49557a9129 [red-knot] Simplify object out of intersections (#15511) 2025-01-15 20:06:48 +00:00
Andrew Gallant
c9b99e4bee ruff_linter: adjust empty spans after line terminator more generally
Instead of doing this on a lint-by-lint basis, we now just do it right
before rendering. This is more broadly applicable.

Note that this doesn't fix the diagnostic rendering for the Python
parser. But that's using a different path anyway (`annotate-snippets` is
only used in tests).
2025-01-15 13:37:52 -05:00
Andrew Gallant
2ff2a54f56 test: update a few indentation related diagnostics
Previously, these were pointing to the right place, but were missing the
`^`. With the `annotate-snippets` upgrade, the `^` was added, but they
started pointing to the end of the previous line instead of the
beginning of the following line. In this case, we really want it to
point to the beginning of the following line since we're calling out
indentation issues.

As in a prior commit, we fix this by tweaking the offsets emitted by the
lint itself. Instead of an empty range at the beginning of the line, we
point to the first character in the line. This "forces" the renderer to
point to the beginning of the line instead of the end of the preceding
line.

The end effect here is that the rendering is fixed by adding `^` in the
proper location.
2025-01-15 13:37:52 -05:00
Andrew Gallant
17f01a4355 test: add more missing carets
This update includes some missing `^` in the diagnostic annotations.

This update also includes some shifting of "syntax error" annotations to
the end of the preceding line. I believe this is technically a
regression, but fixing them has proven quite difficult. I *think* the
best way to do that might be to tweak the spans generated by the Python
parser errors, but I didn't want to dig into that. (Another approach
would be to change the `annotate-snippets` rendering, but when I tried
that and managed to fix these regressions, I ended up causing a bunch of
other regressions.)

Ref 77d454525e (r1915458616)
2025-01-15 13:37:52 -05:00
Andrew Gallant
5021f32449 test: another update to add back a caret
This change also requires some shuffling to the offsets we generate for
the diagnostic. Previously, we were generating an empty range
immediately *after* the line terminator and immediate before the first
byte of the subsequent line. How this is rendered is somewhat open to
interpretation, but the new version of `annotate-snippets` chooses to
render this at the end of the preceding line instead of the beginning of
the following line.

In this case, we want the diagnostic to point to the beginning of the
following line. So we either need to change `annotate-snippets` to
render such spans at the beginning of the following line, or we need to
change our span to point to the first full character in the following
line. The latter will force `annotate-snippets` to move the caret to the
proper location.

I ended up deciding to change our spans instead of changing how
`annotate-snippets` renders empty spans after a line terminator. While I
didn't investigate it, my guess is that they probably had good reason
for doing so, and it doesn't necessarily strike me as _wrong_.
Furthermore, fixing up our spans seems like a good idea regardless, and
was pretty easy to do.
2025-01-15 13:37:52 -05:00
Andrew Gallant
75b4ed5ad1 codeowners: make BurntSushi owner of ruff_annotate_snippets 2025-01-15 13:37:52 -05:00
Andrew Gallant
e6e610c274 test: tweak in alignment involving unprintable characters
This looks like a bug fix since the caret is now pointing right at the
position of the unprintable character. I'm not sure if this is a result
of an improvement via the `annotate-snippets` upgrade, or because of
more accurate tracking of annotation ranges even after unprintable
characters are replaced. I'm tempted to say the former since in theory
the offsets were never wrong before because they were codepoint offsets.

Regardless, this looks like an improvement.
2025-01-15 13:37:52 -05:00
Andrew Gallant
670fcecd1b test: update snapshots with trimmed lines
This updates snapshots where long lines now get trimmed with
`annotate-snippets`. And an ellipsis is inserted to indicate trimming.

This is a little hokey to test since in tests we don't do any styling.
And I believe this just uses the default "max term width" for rendering.
But in real life, it seems like a big improvement to have long lines
trimmed if they would otherwise wrap in the terminal. So this seems like
an improvement to me.

There are some other fixes here that overlap with previous categories.
2025-01-15 13:37:52 -05:00
Andrew Gallant
84ba4ecaf5 ruff_annotate_snippets: support overriding the "cut indicator"
We do this because `...` is valid Python, which makes it pretty likely
that some line trimming will lead to ambiguous output. So we add support
for overriding the cut indicator. This also requires changing some of
the alignment math, which was previously tightly coupled to `...`.

For Ruff, we go with `…` (`U+2026 HORIZONTAL ELLIPSIS`) for our cut
indicator.

For more details, see the patch sent to upstream:
https://github.com/rust-lang/annotate-snippets-rs/pull/172
2025-01-15 13:37:52 -05:00
Andrew Gallant
a45f4de683 ruff_annotate_snippets: fix false positive line trimming
This fix was sent upstream and the PR description includes more details:
https://github.com/rust-lang/annotate-snippets-rs/pull/170

Without this fix, there was an errant snapshot diff that looked like
this:

  |
1 |   version = "0.1.0"
2 |   # Ensure that the spans from toml handle utf-8 correctly
3 |   authors = [
  |  ___________^
4 | |     { name = "Z͑ͫ̓ͪ̂ͫ̽͏̴̙...A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘", email = 1 }
5 | | ]
  | |_^ RUF200
  |

That ellipsis should _not_ be inserted since the line is not actually
truncated. The handling of line length (in bytes versus actual rendered
length) wasn't quite being handled correctly in all cases.

With this fix, there's (correctly) no snapshot diff.
2025-01-15 13:37:52 -05:00
Andrew Gallant
88df168b63 ruff_annotate_snippets: update snapshot for single ASCII whitespace source
The change to the rendering code is elaborated on in more detail here,
where I attempted to upstream it:
https://github.com/rust-lang/annotate-snippets-rs/pull/169

Otherwise, the snapshot diff also shows a bug fix: a `^` is now rendered
where as it previously was not.
2025-01-15 13:37:52 -05:00
Andrew Gallant
59edee2aca test: update another improperly rendered range
This one almost looks like it fits into the other failure categories,
but without identifying root causes, it's hard to say for sure. The span
here does end after a line terminator, so it feels like it's like the
rest.

I also isolated this change since I found the snapshot diff pretty hard
to read and wanted to look at it more closely. In this case, the before
is:

    E204.py:31:2: E204 [*] Whitespace after decorator
       |
    30 |   # E204
    31 |   @ \
       |  __^
    32 | | foo
       | |_^ E204
    33 |   def baz():
    34 |       print('baz')
       |
       = help: Remove whitespace

And the after is:

    E204.py:31:2: E204 [*] Whitespace after decorator
       |
    30 | # E204
    31 | @ \
       |  ^^ E204
    32 | foo
    33 | def baz():
    34 |     print('baz')
       |
       = help: Remove whitespace

The updated rendering is clearly an improvement, since `foo` itself is
not really the subject of the diagnostic. The whitespace is.

Also, the new rendering matches the span fed to `annotate-snippets`,
where as the old rendering does not.
2025-01-15 13:37:52 -05:00
Andrew Gallant
9fdb1e9bc8 test: update snapshot with fixed annotation but carets include whitespace
I separated out this snapshot update since the string of `^` including
whitespace looked a little odd. I investigated this one specifically,
and indeed, our span in this case is telling `annotate-snippets` to
point at the whitespace. So this is `annotate-snippets` doing what it's
told with a mildly sub-optimal span.

For clarity, the before rendering is:

    skip.py:34:1: I001 [*] Import block is un-sorted or un-formatted
       |
    32 |       import sys; import os  # isort:skip
    33 |       import sys; import os  # isort:skip  # isort:skip
    34 | /     import sys; import os
       |
       = help: Organize imports

And now after is:

    skip.py:34:1: I001 [*] Import block is un-sorted or un-formatted
       |
    32 |     import sys; import os  # isort:skip
    33 |     import sys; import os  # isort:skip  # isort:skip
    34 |     import sys; import os
       | ^^^^^^^^^^^^^^^^^^^^^^^^^ I001
       |
       = help: Organize imports

This is a clear bug fix since it adds in the `I001` annotation, even
though the carets look a little funny by including the whitespace
preceding `import sys; import os`.
2025-01-15 13:37:52 -05:00
Andrew Gallant
eed0595b18 test: another set of updates related to line terminator handling
This group of updates is similar to the last one, but they call out the
fact that while the change is an improvement, it does still seem to be a
little buggy.

As one example, previously we would have this:

       |
     1 | / from __future__ import annotations
     2 | |
     3 | | from typing import Any
     4 | |
     5 | | from requests import Session
     6 | |
     7 | | from my_first_party import my_first_party_object
     8 | |
     9 | | from . import my_local_folder_object
    10 | |
    11 | |
    12 | |
    13 | | class Thing(object):
       | |_^ I001
    14 |     name: str
    15 |     def __init__(self, name: str):
       |
       = help: Organize imports

And now here's what it looks like after:

       |
     1 | / from __future__ import annotations
     2 | |
     3 | | from typing import Any
     4 | |
     5 | | from requests import Session
     6 | |
     7 | | from my_first_party import my_first_party_object
     8 | |
     9 | | from . import my_local_folder_object
    10 | |
    11 | |
    12 | |
       | |__^ Organize imports
    13 |   class Thing(object):
    14 |     name: str
    15 |     def __init__(self, name: str):
       |
       = help: Organize imports

So at least now, the diagnostic is not pointing to a completely
unrelated thing (`class Thing`), but it's still not quite pointing to
the imports directly. And the `^` is a bit offset. After looking at
some examples more closely, I think this is probably more of a bug
with how we're generating offsets, since we are actually pointing to
a location that is a few empty lines _below_ the last import. And
`annotate-snippets` is rendering that part correctly. However, the
offset from the left (the `^` is pointing at `r` instead of `f` or even
at the end of `from . import my_local_folder_object`) appears to be a
problem with `annotate-snippets` itself.

We accept this under the reasoning that it's an improvement, albeit not
perfect.
2025-01-15 13:37:52 -05:00
Andrew Gallant
79e71cbbcd test: another line terminator bug fix
I believe this case is different from the last in that it happens when
the end of a *multi-line* annotation occurs after a line terminator.
Previously, the diagnostic would render on the next line, which is
definitely a bit weird. This new update renders it at the end of the
line the annotation ends on.

In some cases, the annotation was previously rendered to point at source
lines below where the error occurred, which is probably pretty
confusing.
2025-01-15 13:37:52 -05:00
Andrew Gallant
5caef89af3 test: update snapshots with improper end-of-line placement
This looks like a bug fix that occurs when the annotation is a
zero-width span immediately following a line terminator. Previously, the
caret seems to be rendered on the next line, but it should be rendered
at the end of the line the span corresponds to.

I admit that this one is kinda weird. I would somewhat expect that our
spans here are actually incorrect, and that to obtain this sort of
rendering, we should identify a span just immediately _before_ the line
terminator and not after it. But I don't want to dive into that rabbit
hole for now (and given how `annotate-snippets` now renders these
spans, perhaps there is more to it than I see), and this does seem like
a clear improvement given the spans we feed to `annotate-snippets`.
2025-01-15 13:37:52 -05:00
Andrew Gallant
f49cfb6c28 test: update snapshots with missing ^
The previous rendering just seems wrong in that a `^` is omitted. The
new version of `annotate-snippets` seems to get this right. I checked a
pseudo random sample of these, and it seems to only happen when the
position pointed at a line terminator.
2025-01-15 13:37:52 -05:00
Andrew Gallant
f29f58105b test: update formatting of multi-line annotations
It's hard to grok the change from the snapshot diffs alone, so here's
one example. Before:

    PYI021.pyi:15:5: PYI021 [*] Docstrings should not be included in stubs
       |
    14 |   class Baz:
    15 |       """Multiline docstring
       |  _____^
    16 | |
    17 | |     Lorem ipsum dolor sit amet
    18 | |     """
       | |_______^ PYI021
    19 |
    20 |       def __init__(self) -> None: ...
       |
       = help: Remove docstring

And now after:

    PYI021.pyi:15:5: PYI021 [*] Docstrings should not be included in stubs
       |
    14 |   class Baz:
    15 | /     """Multiline docstring
    16 | |
    17 | |     Lorem ipsum dolor sit amet
    18 | |     """
       | |_______^ PYI021
    19 |
    20 |       def __init__(self) -> None: ...
       |
       = help: Remove docstring

I personally think both of these are fine. If we felt strongly, I could
investigate reverting to the old style, but the new style seems okay to
me.

In other words, these updates I believe are just cosmetic and not a bug
fix.
2025-01-15 13:37:52 -05:00
Andrew Gallant
3fa4479c85 test: update snapshots with missing annotations
These updates center around the addition of annotations in the
diagnostic rendering. Previously, the annotation was just not rendered
at all. With the `annotate-snippets` upgrade, it is now rendered. I
examined a pseudo random sample of these, and they all look correct.

As will be true in future batches, some of these snapshots also have
changes to whitespace in them as well.
2025-01-15 13:37:52 -05:00
Andrew Gallant
0de8216a25 test: update snapshots with just whitespace changes
These snapshot changes should *all* only be a result of changes to
trailing whitespace in the output. I checked a psuedo random sample of
these, and the whitespace found in the previous snapshots seems to be an
artifact of the rendering and _not_ of the source data. So this seems
like a strict bug fix to me.

There are other snapshots with whitespace changes, but they also have
other changes that we split out into separate commits. Basically, we're
going to do approximately one commit per category of change.

This represents, by far, the biggest chunk of changes to snapshots as a
result of the `annotate-snippets` upgrade.
2025-01-15 13:37:52 -05:00
Andrew Gallant
2922490cb8 ruff_linter: fix handling of unprintable characters
Previously, we were replacing unprintable ASCII characters with a
printable representation of them via fancier Unicode characters. Since
`annotate-snippets` used to use codepoint offsets, this didn't make our
ranges incorrect: we swapped one codepoint for another.

But now, with the `annotate-snippets` upgrade, we use byte offsets
(which is IMO the correct choice). However, this means our ranges can be
thrown off since an ASCII codepoint is always one byte and a non-ASCII
codepoint is always more than one byte.

Instead of tweaking the `ShowNonprinting` trait and making it more
complicated (which is used in places other than this diagnostic
rendering it seems), we instead change `replace_whitespace` to handle
non-printable characters. This works out because `replace_whitespace`
was already updating the annotation range to account for the tab
replacement. We copy that approach for unprintable characters.
2025-01-15 13:37:52 -05:00
Andrew Gallant
84179aaa96 ruff_linter,ruff_python_parser: migrate to updated annotate-snippets
This is pretty much just moving to the new API and taking care to use
byte offsets. This is *almost* enough. The next commit will fix a bug
involving the handling of unprintable characters as a result of
switching to byte offsets.
2025-01-15 13:37:52 -05:00
Andrew Gallant
1b97677779 ruff_annotate_snippets: make small change to enable omitting header
This is a tiny change that, perhaps slightly shady, permits us to use
the `annotate-snippets` renderer without its mandatory header (which
wasn't there in `annotate-snippets 0.9`). Specifically, we can now do
this:

    Level::None.title("")

The combination of a "none" level and an empty label results in the
`annotate-snippets` header being skipped entirely. (Not even an empty
line is written.)

This is maybe not the right API for upstream `annotate-snippets`, but
it's very easy for us to do and unblocks the upgrade (albeit relying on
a vendored copy).

Ref https://github.com/rust-lang/annotate-snippets-rs/issues/167
2025-01-15 13:37:52 -05:00
Andrew Gallant
9c27c57b5b crates: vendor annotate-snippets crate
This merely adds the crate to our repository. Some cosmetic changes are
made to make it work in our repo and follow our conventions, such as
changing the name to `ruff_annotate_snippets`. We retain the original
license information. We do drop some things, such as benchmarks, but
keep tests and examples.
2025-01-15 13:37:52 -05:00
David Peter
4f3209a3ec [red-knot] More comprehensive 'is_subtype_of' tests (#15490)
## Summary

Make the `is_subtype_of` tests a bit easier to understand and
more comprehensive.
2025-01-15 18:33:29 +00:00
Brent Westbrook
1a77a75935 [FastAPI] Update Annotated fixes (FAST002) (#15462)
## Summary

The initial purpose was to fix #15043, where code like this:
```python
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/test")
def handler(echo: str = Query("")):
    return echo
```

was being fixed to the invalid code below:

```python
from typing import Annotated
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/test")
def handler(echo: Annotated[str, Query("")]): # changed
    return echo
```

As @MichaReiser pointed out, the correct fix is:

```python
from typing import Annotated
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/test")
def handler(echo: Annotated[str, Query()] = ""): # changed
    return echo 
```

After fixing the issue for `Query`, I realized that other classes like
`Path`, `Body`, `Cookie`, `Header`, `File`, and `Form` also looked
susceptible to this issue. The last few commits should handle these too,
which I think means this will also close #12913.

I had to reorder the arguments to the `do_stuff` test case because the
new fix removes some default argument values (eg for `Path`:
`some_path_param: str = Path()` becomes `some_path_param: Annotated[str,
Path()]`).

There's also #14484 related to this rule. I'm happy to take a stab at
that here or in a follow up PR too.

## Test Plan

`cargo test`

I also checked the fixed output with `uv run --with fastapi
FAST002_0.py`, but it required making a bunch of additional changes to
the test file that I wasn't sure we wanted in this PR.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-15 13:05:53 -05:00
David Peter
48e6541893 [red-knot] Negation reverses subtyping order (#15503)
## Summary

If `S <: T`, then `~T <: ~S`. This test currently fails with example
like:

```
S = tuple[()]
T = ~Literal[True] & ~Literal[False]
```

`T` is equivalent to `~(Literal[True] | Literal[False])` and therefore
equivalent to `~bool`, but the minimal example for a failure is what is
stated above. We correctly recognize that `S <: T`, but fail to see that
`~T <: ~S`, i.e. `bool <: ~tuple[()]`.

This is why the tests goes into the "flaky" section as well.

## Test Plan

```
export QUICKCHECK_TESTS=100000
while cargo test --release -p red_knot_python_semantic -- --ignored types::property_tests::flaky::negation_reverses_subtype_order; do :; done
```
2025-01-15 16:32:21 +01:00
Alex Waygood
55a7f72035 [red-knot] Fix more edge cases for intersection simplification with LiteralString and AlwaysTruthy/AlwaysFalsy (#15496) 2025-01-15 15:02:41 +00:00
David Peter
8712438aec [red-knot] Initial tests for instance attributes (#15474)
## Summary

Adds some initial tests for class and instance attributes, mostly to
document (and discuss) what we want to support eventually. These
tests are not exhaustive yet. The idea is to specify the coarse-grained
behavior first.

Things that we'll eventually want to test:

- Interplay with inheritance
- Support `Final` in addition to `ClassVar`
- Specific tests for `ClassVar`, like making sure that we support things
like `x: Annotated[ClassVar[int], "metadata"]`
- … or making sure that we raise an error here:
  ```py
  class Foo:
      def __init__(self):
          self.x: ClassVar[str] = "x"
  ```
- Add tests for `__new__` in addition to the tests for `__init__`
- Add tests that show that we use the union of types if multiple methods
define the symbol with different types
- Make sure that diagnostics are raised if, e.g., the inferred type of
an assignment within a method does not match the declared type in the
class body.
- https://github.com/astral-sh/ruff/pull/15474#discussion_r1916556284
- Method calls are completely left out for now.
- Same for `@property`
- … and the descriptor protocol

## Test Plan

New Markdown tests

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-15 14:43:41 +00:00
Dhruv Manilawala
b5dbb2a1d7 Avoid indexing the same workspace multiple times (#15495)
## Summary

This is not lazy indexing but it should somewhat help with #13686.

Currently, processing the change notifications for config files doesn't
account for the fact that multiple config files could belong to the same
workspace. This means that the server will re-index the same workspace
`n` times where `n` is the number of file events which belongs to the
same workspace. This is evident in the following trace logs:

**Trace logs:**

```
[Trace - 6:21:15 PM] Sending notification 'workspace/didChangeWatchedFiles'.
Params: {
    "changes": [
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/pylint/ruff.toml",
            "type": 1
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/script/ruff.toml",
            "type": 2
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/script/scaffold/templates/ruff.toml",
            "type": 2
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/pyproject.toml",
            "type": 2
        }
    ]
}

...

[Trace - 6:21:19 PM] Sending notification 'workspace/didChangeWatchedFiles'.
Params: {
    "changes": [
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/tests/testing_config/custom_components/ruff.toml",
            "type": 1
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/tests/ruff.toml",
            "type": 2
        }
    ]
}

...
```

**Server logs:**

```
 14.838004208s TRACE     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::server::api: enter
  14.838043583s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  14.854324541s DEBUG ThreadId(55) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  14.854388500s DEBUG ThreadId(55) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  14.937713291s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  14.954429833s DEBUG ThreadId(75) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  14.954675708s DEBUG ThreadId(66) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  15.041465500s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  15.056731541s DEBUG ThreadId(78) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  15.056796833s DEBUG ThreadId(78) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  15.117545833s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  15.133091666s DEBUG ThreadId(90) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  15.133146500s DEBUG ThreadId(90) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  15.220340666s TRACE ruff:worker:6 request{id=5 method="textDocument/diagnostic"}: ruff_server::server::api: enter
  15.220401458s DEBUG ruff:worker:6 request{id=5 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/homeassistant/bootstrap.py
  18.577521250s TRACE     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::server::api: enter
  18.577561291s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  18.616564583s DEBUG ThreadId(102) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  18.616627291s DEBUG ThreadId(102) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  18.687424250s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  18.704441416s DEBUG ThreadId(114) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  18.704694958s DEBUG ThreadId(121) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  18.769627500s TRACE ruff:worker:4 request{id=6 method="textDocument/diagnostic"}: ruff_server::server::api: enter
  18.769696791s DEBUG ruff:worker:4 request{id=6 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/homeassistant/bootstrap.py
```

This PR updates the logic to consider all the change events at once
keeping track of the workspace path that have been already indexed.

I want to include this in tomorrow's release to check how this change
would affect the linked issue.

## Test Plan

Run the same scenario as above and check the logs to see that the server
isn't re-indexing the same workspace multiple times:

**Trace logs:**

```
[Trace - 6:04:07 PM] Sending notification 'workspace/didChangeWatchedFiles'.
Params: {
    "changes": [
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/script/ruff.toml",
            "type": 1
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/script/scaffold/templates/ruff.toml",
            "type": 1
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/pylint/ruff.toml",
            "type": 2
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/pyproject.toml",
            "type": 2
        }
    ]
}

...

[Trace - 6:04:11 PM] Sending notification 'workspace/didChangeWatchedFiles'.
Params: {
    "changes": [
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/tests/testing_config/custom_components/ruff.toml",
            "type": 1
        },
        {
            "uri": "file:///Users/dhruv/work/astral/parser-checkouts/home-assistant-core/tests/ruff.toml",
            "type": 2
        }
    ]
}

...
```

**Server logs:**

```
  17.047706750s TRACE     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::server::api: enter
  17.047747875s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  17.080006083s DEBUG ThreadId(54) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  17.080085708s DEBUG ThreadId(54) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  17.145328791s TRACE ruff:worker:6 request{id=5 method="textDocument/diagnostic"}: ruff_server::server::api: enter
  17.145386166s DEBUG ruff:worker:6 request{id=5 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/homeassistant/bootstrap.py
  20.756845958s TRACE     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::server::api: enter
  20.756923375s DEBUG     ruff:main notification{method="workspace/didChangeWatchedFiles"}: ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core
  20.781733916s DEBUG ThreadId(66) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.vscode
  20.781825875s DEBUG ThreadId(75) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/.git
  20.848340750s TRACE ruff:worker:7 request{id=6 method="textDocument/diagnostic"}: ruff_server::server::api: enter
  20.848408041s DEBUG ruff:worker:7 request{id=6 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/work/astral/parser-checkouts/home-assistant-core/homeassistant/bootstrap.py
```
2025-01-15 18:58:28 +05:30
David Salvisberg
73488e71f8 [flake8-type-checking] Avoid false positives for | in TC008 (#15201) 2025-01-15 14:27:24 +01:00
InSync
8331326cb6 Remove legacy issue template (#15163) 2025-01-15 11:59:22 +01:00
David Peter
3a6238d8c2 [red-knot] Typeshed sync and sys.platform fixes (#15492)
## Summary

The next sync of typeshed would have failed without manual changes
anyway, so I'm doing one manual sync + the required changes in our
`sys.platform` tests (which are necessary because of my tiny typeshed PR
here: https://github.com/python/typeshed/pull/13378).

closes #15485 (the next run of the pipeline in two weeks should be fine
as the bug has been fixed upstream)
2025-01-15 11:21:01 +01:00
David Peter
d4862844f1 [red-knot] 'is_equivalent_to' is an equivalence relation (#15488)
## Summary

Adds two additional tests for `is_equivalent_to` so that we cover all
properties of an [equivalence relation].

## Test Plan

```
while cargo test --release -p red_knot_python_semantic -- --ignored types::property_tests::stable; do :; done
```

[equivalence relation]:
https://en.wikipedia.org/wiki/Equivalence_relation
2025-01-15 09:25:46 +01:00
Micha Reiser
96c2d0996d Fix curly bracket spacing around curly f-string expressions (#15471) 2025-01-15 09:22:47 +01:00
Dhruv Manilawala
6aef4ad008 Fix LSP show message macro to allow format args (#15487)
## Summary

This PR fixes the `show_*_msg` macros to pass all the tokens instead of
just a single token. This allows for using various expressions right in
the macro similar to how it would be in `format_args!`.

## Test Plan

`cargo clippy`
2025-01-15 08:11:49 +00:00
Micha Reiser
18d5dbfb7f Remove workspace support (#15472) 2025-01-15 09:03:38 +01:00
Dhruv Manilawala
bec8441cf5 Use tool specific function to perform exclude checks (#15486)
## Summary

This PR creates separate functions to check whether the document path is
excluded for linting or formatting. The main motivation is to avoid the
double `Option` for the call sites and makes passing the correct
settings simpler.
2025-01-15 13:18:46 +05:30
InSync
aefb607405 [red-knot] Migrate is_equivalent_to unit tests to Markdown tests (#15470)
## Summary

Part of #15397, built on top of #15469.

## Test Plan

Markdown tests.
2025-01-14 18:57:23 +00:00
Alex Waygood
bcf0a715c2 [red-knot] Corrections and improvements to intersection simplification (#15475) 2025-01-14 18:15:38 +00:00
InSync
5ed7b55b15 [red-knot] Migrate is_subtype_of unit tests to Markdown tests (#15469)
## Summary

Part of #15397.

## Test Plan

Markdown tests.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-01-14 15:57:24 +01:00
David Peter
8aac69bb2e [red-knot] Add boundness and declaredness tests (#15453)
## Summary

This changeset adds new tests for public uses of symbols,
considering all possible declaredness and boundness states.

Note that this is a mere documentation of the current behavior. There is
still an [open ticket] questioning some of these choices (or unintential
behaviors).

## Test plan

Made sure that the respective test fails if I add the questionable case
again in `symbol_by_id`:

```rs
Symbol::Type(inferred_ty, Boundness::Bound) => {
    Symbol::Type(inferred_ty, Boundness::Bound)
}
```

[open ticket]: https://github.com/astral-sh/ruff/issues/14297
2025-01-14 13:07:16 +01:00
Tom Kuson
9dfc61bf09 [flake8-pytest-style] Tweak documentation and message (#15465) 2025-01-14 08:47:45 +01:00
Tom Kuson
369cbb5424 [flake8-builtins] Improve A005 documentation (#15466) 2025-01-14 08:42:13 +01:00
Garrett Reynolds
dc491e8ade [ruff] Fix false positive on global keyword (RUF052) (#15235) 2025-01-14 08:36:40 +01:00
Wei Lee
a2dc8c93ef [airflow] Replace typo "security_managr" as "security_manager" (AIR303) (#15463)
## Summary

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

Replace typo "security_managr" in AIR303 as "security_manager"

## Test Plan

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

a test fixture has been updated
2025-01-13 18:38:09 -05:00
Carl Meyer
d54c19b983 [red-knot] remove CallOutcome::Cast variant (#15461)
## Summary

Simplification follow-up to #15413.

There's no need to have a dedicated `CallOutcome` variant for every
known function, it's only necessary if the special-cased behavior of the
known function includes emitting extra diagnostics. For `typing.cast`,
there's no such need; we can use the regular `Callable` outcome variant,
and update the return type according to the cast. (This is the same way
we already handle `len`.)

One reason to avoid proliferating unnecessary `CallOutcome` variants is
that currently we have to explicitly add emitting call-binding
diagnostics, for each outcome variant. So we were previously wrongly
silencing any binding diagnostics on calls to `typing.cast`. Fixing this
revealed a separate bug, that we were emitting a bogus error anytime
more than one keyword argument mapped to a `**kwargs` parameter. So this
PR also adds test and fix for that bug.

## Test Plan

Existing `cast` tests pass unchanged, added new test for `**kwargs` bug.
2025-01-13 10:58:53 -08:00
Micha Reiser
5ad546f187 Change ProgramSettings::python_platform to return a reference (#15457) 2025-01-13 16:23:34 +01:00
InSync
47d0a8ba96 [flake8-pytest-style] Test function parameters with default arguments (PT028) (#15449) 2025-01-13 13:40:54 +01:00
Dhruv Manilawala
56b14454dc Display context for ruff.configuration errors (#15452)
## Summary

I noticed this while trying out
https://github.com/astral-sh/ruff-vscode/issues/665 that we use the
`Display` implementation to show the error which hides the context. This
PR changes it to use the `Debug` implementation and adds the message as
a context.

## Test Plan

**Before:**

```
   0.001228084s ERROR main ruff_server::session::index::ruff_settings: Unable to find editor-specified configuration file: Failed to parse /private/tmp/hatch-test/ruff.toml
```

**After:**

```
   0.002348750s ERROR main ruff_server::session::index::ruff_settings: Unable to load editor-specified configuration file

Caused by:
    0: Failed to parse /private/tmp/hatch-test/ruff.toml
    1: TOML parse error at line 2, column 18
         |
       2 | extend-select = ["ASYNC101"]
         |                  ^^^^^^^^^^
       Unknown rule selector: `ASYNC101`
```
2025-01-13 15:43:20 +05:30
David Peter
eb3cb8d4b2 [red-knot] Use BitSet::union for merging of declarations (#15451)
## Summary

In `SymbolState` merging, use `BitSet::union` instead of inserting
declarations one by one. This used to be the case but was changed in
https://github.com/astral-sh/ruff/pull/15019 because we had to iterate
over declarations anyway.

This is an alternative to https://github.com/astral-sh/ruff/pull/15419
by @MichaReiser. It's similar in performance, but a bit more
declarative and less imperative.
2025-01-13 11:10:42 +01:00
InSync
6f35a4d8d5 [fastapi] Handle parameters with Depends correctly (FAST003) (#15364)
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-13 08:51:02 +00:00
cake-monotone
82d06a198d [red-knot] Remove duplicate property test (#15450)
## Summary

Follow-up PR from https://github.com/astral-sh/ruff/pull/15415  🥲 

The exact same property test already exists:
`intersection_assignable_to_both` and
`all_type_pairs_can_be_assigned_from_their_intersection`

## Test Plan

`cargo test -p red_knot_python_semantic -- --ignored
types::property_tests::flaky`
2025-01-13 08:18:41 +01:00
InSync
70c3be88b9 [flake8-pie] Reuse parsed tokens (PIE800) (#15438)
## Summary

Follow-up to #15394. See [this review
comment](https://github.com/astral-sh/ruff/pull/15394#discussion_r1910526741).

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-12 21:03:11 -05:00
Tom Kuson
347ab5b47a [flake8-pytest-style] Implement pytest.warns diagnostics (PT029, PT030, PT031) (#15444)
## Summary

Implements upstream diagnostics `PT029`, `PT030`, `PT031` that function
as pytest.warns corollaries of `PT010`, `PT011`, `PT012` respectively.
Most of the implementation and documentation is designed to mirror those
existing diagnostics.

Closes #14239

## Test Plan

Tests for `PT029`, `PT030`, `PT031` largely copied from `PT010`,
`PT011`, `PT012` respectively.

`cargo nextest run`

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2025-01-13 01:46:59 +00:00
renovate[bot]
fa11b08766 Update dependency @types/react to v19.0.6 (#15448) 2025-01-13 01:11:51 +00:00
renovate[bot]
6f3e4e5062 Update NPM Development dependencies to v19.0.5 (#15445) 2025-01-12 20:06:02 -05:00
Charlie Marsh
2454305ef8 [flake8-pathlib] Fix --select for os-path-dirname (PTH120) (#15446)
## Summary

Closes https://github.com/astral-sh/ruff/issues/15439.
2025-01-13 00:55:46 +00:00
InSync
4f37fdeff2 [flake8-bandit] Check for builtins instead of builtin (S102, PTH123) (#15443)
## Summary

Resolves #15442.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-12 19:45:31 -05:00
InSync
d1666fbbee [red-knot] Add AlwaysTruthy and AlwaysFalsy to knot_extensions (#15437)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-12 17:00:57 +00:00
Alex Waygood
06b7f4495e [red-knot] Minor improvements to KnownFunction API (#15441)
A small PR to reduce some of the code duplication between the various
branches, make it a little more readable and move the API closer to what
we already have for `KnownClass`
2025-01-12 16:06:31 +00:00
Alex Waygood
c8795fcb37 [red-knot] Minor improvements to property_tests.rs (#15440) 2025-01-12 13:55:18 +00:00
cake-monotone
ccfde37619 [red-knot] Add Property Tests for Intersection and Union (#15415) 2025-01-12 13:21:29 +00:00
InSync
6ae3e8f8d7 [red-knot] Support cast (#15413)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-12 13:05:45 +00:00
renovate[bot]
60d7a464fb Update Rust crate colored to v3 (#15434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:52:52 +00:00
renovate[bot]
c0259e7bf2 Update dependency ruff to v0.9.1 (#15432)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-11 17:18:38 +00:00
renovate[bot]
22edee2353 Update pre-commit dependencies (#15433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-01-11 17:18:13 +00:00
renovate[bot]
7d20277111 Update Rust crate libcst to v1.6.0 (#15431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:14:44 +00:00
renovate[bot]
bce07f6564 Update Rust crate bitflags to v2.7.0 (#15430)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:14:19 +00:00
renovate[bot]
8ea6605a6d Update NPM Development dependencies (#15428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:13:32 +00:00
renovate[bot]
d323f2019b Update dependency uuid to v11.0.5 (#15427)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:13:17 +00:00
renovate[bot]
ad883d9b31 Update Rust crate uuid to v1.11.1 (#15426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:12:54 +00:00
renovate[bot]
7240212d27 Update Rust crate thiserror to v2.0.11 (#15425)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:12:34 +00:00
renovate[bot]
925ee41317 Update Rust crate syn to v2.0.96 (#15424)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:12:23 +00:00
renovate[bot]
78b242fe3f Update Rust crate proc-macro2 to v1.0.93 (#15422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:12:07 +00:00
renovate[bot]
7ed46d0823 Update Rust crate serde_json to v1.0.135 (#15423)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:11:48 +00:00
renovate[bot]
bff4edb717 Update Rust crate clap to v4.5.26 (#15420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 17:09:54 +00:00
Dhruv Manilawala
38f873ba52 Remove flatten to improve deserialization error messages (#15414)
## Summary

Closes: #9719  

## Test Plan

**Before:**

```
ruff failed
  Cause: Failed to parse /Users/dhruv/playground/ruff/pyproject.toml
  Cause: TOML parse error at line 22, column 1
   |
22 | [tool.ruff.lint]
   | ^^^^^^^^^^^^^^^^
invalid type: string "false", expected a boolean
```

**After:**

```
ruff failed
  Cause: Failed to parse /Users/dhruv/playground/ruff/pyproject.toml
  Cause: TOML parse error at line 27, column 20
   |
27 | mypy-init-return = "false"
   |                    ^^^^^^^
invalid type: string "false", expected a boolean
```
2025-01-11 22:08:21 +05:30
Micha Reiser
c39ca8fe6d Upgrade Rust toolchain to 1.84.0 (#15408) 2025-01-11 09:51:58 +01:00
David Peter
2d82445794 [red-knot] Simplify unions of T and ~T (#15400)
## Summary

Simplify unions of `T` and `~T` to `object`.

## Test Plan

Adapted existing tests.
2025-01-10 23:00:52 +01:00
David Peter
398f2e8b0c [red-knot] Minor fixes in intersection-types tests (#15410)
## Summary

Minor fixes in intersection-types tests
2025-01-10 22:53:03 +01:00
InSync
232fbc1300 [red-knot] Understand type[Unknown] (#15409)
## Summary

Follow-up to #15194.

## Test Plan

Markdown tests.
2025-01-10 13:25:59 -08:00
Alex Waygood
c82932e580 [red-knot] Refactor KnownFunction::takes_expression_arguments() (#15406) 2025-01-10 19:09:03 +00:00
Micha Reiser
12f86f39a4 Ruff 0.9.1 (#15407) 2025-01-10 19:45:06 +01:00
Micha Reiser
2b28d566a4 Associate a trailing end-of-line comment in a parenthesized implicit concatenated string with the last literal (#15378) 2025-01-10 19:21:34 +01:00
Calum Young
adca7bd95c Remove pygments pin (#15404)
## Summary

The recent release of Pygments
([2.19.1](https://github.com/pygments/pygments/releases/tag/2.19.1))
allows the pinned version to be removed as the PYI alias for Python
syntax highlighting has been removed.

## Test Plan

- Follow the steps outlined in
https://github.com/astral-sh/ruff/blob/main/CONTRIBUTING.md#mkdocs to
get the documentation site running locally.
- Spot test rules pages that have PYI code blocks to ensure that syntax
highlighting remains e.g.
[http://127.0.0.1:8000/ruff/rules/complex-if-statement-in-stub/](http://127.0.0.1:8000/ruff/rules/complex-if-statement-in-stub/).

**Note:** I am unable to test the insiders build but would assume that
it functions locally as I do not have access to MkDocs Insiders, but I
would like to assume that it functions in the same way as the
non-insiders build.
2025-01-10 12:15:13 -05:00
InSync
6b98a26452 [red-knot] Support assert_type (#15194)
## Summary

See #15103.

## Test Plan

Markdown tests and unit tests.
2025-01-10 08:45:02 -08:00
David Peter
c87463842a [red-knot] Move tuple-containing-Never tests to Markdown (#15402)
## Summary

See title.

Part of #15397

## Test Plan

Ran new Markdown test.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-10 15:31:30 +00:00
InSync
c364b586f9 [flake8-pie] Correctly remove wrapping parentheses (PIE800) (#15394)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-10 14:52:32 +00:00
Antoine Dechaume
73d424ee5e Fix outdated doc for handling the default file types with the pre-commit hook (#15401)
Co-authored-by: Antoine DECHAUME <>
2025-01-10 15:49:23 +01:00
Dhruv Manilawala
6e9ff445fd Insert the cells from the start position (#15398)
## Summary

The cause of this bug is from
https://github.com/astral-sh/ruff/pull/12575 which was itself a bug fix
but the fix wasn't completely correct.

fixes: #14768 
fixes: https://github.com/astral-sh/ruff-vscode/issues/644

## Test Plan

Consider the following three cells:

1.
```python
class Foo:
    def __init__(self):
        self.x = 1

    def __str__(self):
        return f"Foo({self.x})"
```

2.
```python
def hello():
    print("hello world")
```

3.
```python
y = 1
```

The test case is moving cell 2 to the top i.e., cell 2 goes to position
1 and cell 1 goes to position 2.

Before this fix, it can be seen that the cells were pushed at the end of
the vector:

```
  12.643269917s  INFO ruff:main ruff_server::edit:📓 Before update: [
    NotebookCell {
        document: TextDocument {
            contents: "class Foo:\n    def __init__(self):\n        self.x = 1\n\n    def __str__(self):\n        return f\"Foo({self.x})\"",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "def hello():\n    print(\"hello world\")",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "y = 1",
        },
    },
]
  12.643777667s  INFO ruff:main ruff_server::edit:📓 After update: [
    NotebookCell {
        document: TextDocument {
            contents: "y = 1",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "class Foo:\n    def __init__(self):\n        self.x = 1\n\n    def __str__(self):\n        return f\"Foo({self.x})\"",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "def hello():\n    print(\"hello world\")",
        },
    },
]
```

After the fix in this PR, it can be seen that the cells are being pushed
at the correct `start` index:

```
   6.520570917s  INFO ruff:main ruff_server::edit:📓 Before update: [
    NotebookCell {
        document: TextDocument {
            contents: "class Foo:\n    def __init__(self):\n        self.x = 1\n\n    def __str__(self):\n        return f\"Foo({self.x})\"",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "def hello():\n    print(\"hello world\")",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "y = 1",
        },
    },
]
   6.521084792s  INFO ruff:main ruff_server::edit:📓 After update: [
    NotebookCell {
        document: TextDocument {
            contents: "def hello():\n    print(\"hello world\")",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "class Foo:\n    def __init__(self):\n        self.x = 1\n\n    def __str__(self):\n        return f\"Foo({self.x})\"",
        },
    },
    NotebookCell {
        document: TextDocument {
            contents: "y = 1",
        },
    },
]
```
2025-01-10 13:11:56 +00:00
David Peter
f2c3ddc5ea [red-knot] Move intersection type tests to Markdown (#15396)
## Summary

[**Rendered version of the new test
suite**](https://github.com/astral-sh/ruff/blob/david/intersection-type-tests/crates/red_knot_python_semantic/resources/mdtest/intersection_types.md)

Moves most of our existing intersection-types tests to a dedicated
Markdown test suite, extends the test coverage, unifies the notation for
these tests, groups tests into a proper structure, and adds some
explanations for various simplification strategies.

This changeset also:
- Adds a new simplification where `~Never` is removed from
intersections.
- Adds a new simplification where adding `~object` simplifies the whole
intersection to `Never`
- Avoids unnecessary assignment-checks between inferred and declared
type. This was added to this changeset to avoid many false positive
errors in this test suite.

Resolves the task described in this old comment
[here](e01da82a5a..e7e432bca2 (r1819924085)).

## Test Plan

Running the new Markdown tests

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-10 14:04:03 +01:00
Dhruv Manilawala
b861551b6a Remove unnecessary backticks (#15393)
Ref: https://github.com/astral-sh/ruff/pull/15367#discussion_r1909448140
2025-01-10 09:22:26 +00:00
Dylan
443bf38565 [ruff] Omit diagnostic for shadowed private function parameters in used-dummy-variable (RUF052) (#15376) 2025-01-10 03:09:25 -06:00
Tom Kuson
23ad319b55 [flake8-bugbear] Improve assert-raises-exception (B017) message (#15389) 2025-01-10 08:48:18 +01:00
InSync
3d9433ca66 [pyupgrade] Handle comments and multiline expressions correctly (UP037) (#15337) 2025-01-10 08:46:01 +01:00
Douglas Creager
baf068361a [red-knot] Consolidate all gradual types into single Type variant (#15386)
Prompted by

> One nit: I think we need to consider `Any` and `Unknown` and `Todo` as
all (gradually) equivalent to each other, and thus `type & Any` and
`type & Unknown` and `type & Todo` as also equivalent. The distinction
between `Any` vs `Unknown` vs `Todo` is entirely about
provenance/debugging, there is no type level distinction. (And I've been
wondering if the `Any` vs `Unknown` distinction is really worth it.)

The thought here is that _most_ places want to treat `Any`, `Unknown`,
and `Todo` identically. So this PR simplifies things by having a single
`Type::Any` variant, and moves the provenance part into a new `AnyType`
type. If you need to treat e.g. `Todo` differently, you still can by
pattern-matching into the `AnyType`. But if you don't, you can just use
`Type::Any(_)`.

(This would also allow us to (more easily) distinguish "unknown via an
unannotated value" from "unknown because of a typing error" should we
want to do that in the future)

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-01-09 21:32:20 -05:00
David Peter
b33cf5baba [red-knot] Move UnionBuilder tests to Markdown (#15374)
## Summary

This moves almost all of our existing `UnionBuilder` tests to a
Markdown-based test suite.

I see how this could be a more controversial change, since these tests
where written specifically for `UnionBuilder`, and by creating the union
types using Python type expressions, we add an additional layer on top
(parsing and inference of these expressions) that moves these tests away
from clean unit tests more in the direction of integration tests. Also,
there are probably a few implementation details of `UnionBuilder` hidden
in the test assertions (e.g. order of union elements after
simplifications).

That said, I think we would like to see all those properties that are
being tested here from *any* implementation of union types. And the
Markdown tests come with the usual advantages:

- More consice
- Better readability
- No re-compiliation when working on tests
- Easier to add additional explanations and structure to the test suite

This changeset adds a few additional tests, but keeps the logic of the
existing tests except for a few minor modifications for consistency.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: T-256 <132141463+T-256@users.noreply.github.com>
2025-01-09 21:45:06 +01:00
Dylan
b0905c4b04 [pycodestyle] Handle each cell separately for too-many-newlines-at-end-of-file (W391) (#15308)
Jupyter notebooks are converted into source files by joining with
newlines, which confuses the check [too-many-newlines-at-end-of-file
(W391)](https://docs.astral.sh/ruff/rules/too-many-newlines-at-end-of-file/#too-many-newlines-at-end-of-file-w391).
This PR introduces logic to apply the check cell-wise (and, in
particular, correctly handles empty cells.)

Closes #13763
2025-01-09 10:50:39 -06:00
Micha Reiser
d0b2bbd55e Release 0.9.0 (#15371)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-09 14:53:08 +01:00
Josiah Outram Halstead
8628f169e9 [ruff] Stop parsing diagnostics from other sources for code action requests (#15373) 2025-01-09 14:38:13 +01:00
InSync
8bc11c49b2 [flake8-django] Recognize other magic methods (DJ012) (#15365) 2025-01-09 14:36:42 +01:00
David Peter
bf5b0c2688 [red-knot] Minor refactor of red_knot_vendored/build.rs (#15372)
## Summary

See https://github.com/astral-sh/ruff/pull/15370#discussion_r1908611461:

- Rename `zip_dir` to `write_zipped_typeshed_to` to clarify it's not a
generic function (anymore)
- Hard-code `TYPESHED_SOURCE_DIR` instead of using a `directory_path`
argument
2025-01-09 12:23:42 +00:00
David Peter
097aa04c04 [red-knot] Typeshed patching: use build.rs instead of workflow (#15370)
## Summary

The symlink-approach in the typeshed-sync workflow caused some problems
on Windows, even though it seemed to work fine in CI:

https://github.com/astral-sh/ruff/pull/15138#issuecomment-2578642129

Here, we rely on `build.rs` to patch typeshed instead, which allows us
to get rid of the modifications in the workflow (thank you
@MichaReiser for the idea).

## Test Plan

- Made sure that changes to `knot_extensions.pyi` result in a recompile
  of `red_knot_vendored`.
2025-01-09 11:50:32 +01:00
Dhruv Manilawala
f706c3fdf2 Add f-string formatting to the docs (#15367)
Revive https://github.com/astral-sh/ruff/pull/15341 as it got removed
from the latest rebase in https://github.com/astral-sh/ruff/pull/15238.
2025-01-09 10:20:06 +01:00
Micha Reiser
29f6653318 [ruff] Stabilize useless-if-else (RUF034) (#15351) 2025-01-09 10:20:06 +01:00
Micha Reiser
d645525afc [pylint]: Stabilize boolean-chained-comparison (PLR1716) (#15354) 2025-01-09 10:20:06 +01:00
Micha Reiser
6dcf7b35b9 [ruff] Stabilize post-init-default (RUF033) (#15352) 2025-01-09 10:20:06 +01:00
Micha Reiser
943d4fc160 Update formatter preview documentation (#15349) 2025-01-09 10:20:06 +01:00
Micha Reiser
3ea4c63d2c [flake8-pyi] Stabilize: include all python file types for PYI006 (#15340) 2025-01-09 10:20:06 +01:00
Micha Reiser
8e8a07144d [flake8-pyi]: Stabilize: Provide more automated fixes for duplicate-union-members (PYI016) (#15342) 2025-01-09 10:20:06 +01:00
Micha Reiser
225dd0a027 [ruff] Stabilize: Detect attrs dataclasses (RUF008, RUF009) (#15345) 2025-01-09 10:20:06 +01:00
Micha Reiser
52aeb8ae11 [flake8-pyi] Stabilize autofix for redundant-numeric-union (PYI041) (#15343) 2025-01-09 10:20:06 +01:00
Micha Reiser
71b6ac81a6 Remove unnecessary PreviewMode::Enabled in tests (#15344) 2025-01-09 10:20:06 +01:00
Alex Waygood
75fc2c3116 [ruff-0.9] Stabilise two flake8-builtins rules (#15322) 2025-01-09 10:20:06 +01:00
Micha Reiser
9c4d124ba0 [pycodestyle] Stabilize: Exempt pytest.importorskip calls (E402) (#15338) 2025-01-09 10:20:06 +01:00
InSync
8c620b9b4b [flake8-pytest-style] Stabilize "Detect more pytest.mark.parametrize calls" (PT006) (#15327)
Co-authored-by: Micha Reiser <micha@reiser.io>
Resolves #15324. Stabilizes the behavior changes introduced in #14515.
2025-01-09 10:20:06 +01:00
Dylan
1eda27d1a5 [ruff-0.9] Stabilize decimal-from-float-literal (RUF032) (#15333) 2025-01-09 10:20:06 +01:00
Alex Waygood
aaa86cf38d [ruff-0.9] Stabilise slice-to-remove-prefix-or-suffix (FURB188) (#15329)
Stabilise [`slice-to-remove-prefix-or-suffix`](https://docs.astral.sh/ruff/rules/slice-to-remove-prefix-or-suffix/) (`FURB188`) for the Ruff 0.9 release.

This is a stylistic rule, but I think it's a pretty uncontroversial one. There are no open issues or PRs regarding it and it's been in preview for a while now.
2025-01-09 10:20:06 +01:00
Micha Reiser
7821206b7b Update Black deviations to reflect 2025 style changes (#15127) 2025-01-09 10:20:06 +01:00
Micha Reiser
b76d05e283 Remove formatter incompatibility warning for ISC001 (#15123) 2025-01-09 10:20:06 +01:00
Micha Reiser
424b720c19 Ruff 2025 style guide (#13906)
Closes #13371
2025-01-09 10:20:06 +01:00
Carl Meyer
a95deec00f [red-knot] handle synthetic 'self' argument in call-binding diagnostics (#15362) 2025-01-09 00:36:48 -08:00
InSync
21aa12a073 [red-knot] More precise inference for classes with non-class metaclasses (#15138)
## Summary

Resolves #14208.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-01-09 00:34:04 +00:00
Douglas Creager
5f5eb7c0dd [red-knot] Print non-string panic payloads and (sometimes) backtraces (#15363)
More refinements to the panic messages for failing mdtests to mimic the
output of the default panic hook more closely:

- We now print out `Box<dyn Any>` if the panic payload is not a string
(which is typically the case for salsa panics).
- We now include the panic's backtrace if you set the `RUST_BACKTRACE`
environment variable.
2025-01-08 18:12:16 -05:00
David Peter
b6562ed57e [red-knot] Property test workflow: Fix issue label, link to CI run (#15361)
## Summary

See title. Had to make a minor change, because it failed the zizmor
pre-commit check otherwise:

```
error[template-injection]: code injection via template expansion
  --> /home/shark/ruff/.github/workflows/daily_fuzz.yaml:68:9
   |
68 |          - uses: actions/github-script@v7
   |  __________^
69 | |          with:
70 | |            github-token: ${{ secrets.GITHUB_TOKEN }}
71 | |            script: |
   | | ___________^
72 | ||             await github.rest.issues.create({
...  ||
77 | ||               labels: ["bug", "parser", "fuzzer"],
78 | ||             })
   | ||               ^
   | ||_______________|
   |  |_______________this step
   |                  github.server_url may expand into attacker-controllable code
   |
   = note: audit confidence → High
```
2025-01-08 22:47:16 +01:00
David Peter
4fd82d5f35 [red-knot] Property test improvements (#15358)
## Summary

- Add a workflow to run property tests on a daily basis (based on
`daily_fuzz.yaml`)
- Mark `assignable_to_is_reflexive` as flaky (related to #14899)
- Add new (failing) `intersection_assignable_to_both` test (also related
to #14899)

## Test Plan

Ran:

```bash
export QUICKCHECK_TESTS=100000
while cargo test --release -p red_knot_python_semantic -- \
  --ignored types::property_tests::stable; do :; done
```

Observed successful property_tests CI run
2025-01-08 22:24:57 +01:00
David Peter
beb8e2dfe0 [red-knot] More comprehensive is_assignable_to tests (#15353)
## Summary

This changeset migrates all existing `is_assignable_to` tests to a
Markdown-based test. It also increases our test coverage in a hopefully
meaningful way (not claiming to be complete in any sense). But at least
I found and fixed one bug while doing so.

## Test Plan

Ran property tests to make sure the new test succeeds after fixing it.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-08 20:25:08 +01:00
Alex Waygood
88d07202c1 [red-knot] Reduce Name clones in call signature checking (#15335) 2025-01-08 18:29:35 +00:00
Douglas Creager
2ca31e4b43 Fall back on previous panic hook when not in catch_unwind wrapper (#15319)
This fixes #15317. Our `catch_unwind` wrapper installs a panic hook that
captures (the rendered contents of) the panic info when a panic occurs.
Since the intent is that the caller will render the panic info in some
custom way, the hook silences the default stderr panic output.

However, the panic hook is a global resource, so if any one thread was
in the middle of a `catch_unwind` call, we would silence the default
panic output for _all_ threads.

The solution is to also keep a thread local that indicates whether the
current thread is in the middle of our `catch_unwind`, and to fall back
on the default panic hook if not.

## Test Plan

Artificially added an mdtest parse error, ran tests via `cargo test -p
red_knot_python_semantic` to run a large number of tests in parallel.
Before this patch, the panic message was swallowed as reported in
#15317. After, the panic message was shown.
2025-01-08 11:34:51 -05:00
Auguste Lalande
450d4e0e0c [pylint] Fix unreachable infinite loop (PLW0101) (#15278)
<!--
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

Fix infinite loop issue reported here #15248.
The issue was caused by the break inside the if block, which caused the
flow to exit in an unforeseen way. This caused other issues, eventually
leading to an infinite loop.

Resolves #15248. Resolves #15336.

## Test Plan

Added failing code to fixture.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-08 09:45:04 -06:00
Alex Waygood
7284d68157 fix invalid syntax in workflow file (#15357) 2025-01-08 15:34:33 +00:00
InSync
3820af2f1b [pycodestyle] Avoid false positives related to type aliases (E252) (#15356) 2025-01-08 16:04:08 +01:00
Alex Waygood
ee9a912f47 [flake8-builtins] Disapply A005 to stub files (#15350) 2025-01-08 12:59:27 +00:00
Dhruv Manilawala
1447553bc2 Improve logging system using logLevel, avoid trace value (#15232)
## Summary

Refer to the VS Code PR
(https://github.com/astral-sh/ruff-vscode/pull/659) for details on the
change.

This PR changes the following:

1. Add tracing span for both request (request id and method name) and
notification (method name) handler
2. Remove the `RUFF_TRACE` environment variable. This was being used to
turn on / off logging for the server
3. Similarly, remove reading the `trace` value from the initialization
options
4. Remove handling the `$/setTrace` notification
5. Remove the specialized `TraceLogWriter` used for Zed and VS Code
(https://github.com/astral-sh/ruff/pull/12564)

Regarding the (5) for the Zed editor, the reason that was implemented
was because there was no way of looking at the stderr messages in the
editor which has been changed. Now, it captures the stderr as part of
the "Server Logs".
(82492d74a8/crates/language_tools/src/lsp_log.rs (L548-L552))

### Question

Regarding (1), I think having just a simple trace level message should
be good for now as the spans are not hierarchical. This could be tackled
with #12744. The difference between the two:

<details><summary>Using <code>tracing::trace</code></summary>
<p>

```
   0.019243416s DEBUG ThreadId(08) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
   0.026398750s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/playground/ruff
   0.026802125s TRACE ruff:main ruff_server::server::api: Received notification "textDocument/didOpen"
   0.026930666s TRACE ruff:main ruff_server::server::api: Received notification "textDocument/didOpen"
   0.026962333s TRACE ruff:main ruff_server::server::api: Received request "textDocument/diagnostic" (1)
   0.027042875s TRACE ruff:main ruff_server::server::api: Received request "textDocument/diagnostic" (2)
   0.027097500s TRACE ruff:main ruff_server::server::api: Received request "textDocument/codeAction" (3)
   0.027107458s DEBUG ruff:worker:0 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
   0.027123541s DEBUG ruff:worker:3 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/organize_imports.py
   0.027514875s  INFO     ruff:main ruff_server::server: Configuration file watcher successfully registered
   0.285689833s TRACE     ruff:main ruff_server::server::api: Received request "textDocument/codeAction" (4)
  45.741101666s TRACE     ruff:main ruff_server::server::api: Received notification "textDocument/didClose"
  47.108745500s TRACE     ruff:main ruff_server::server::api: Received notification "textDocument/didOpen"
  47.109802041s TRACE     ruff:main ruff_server::server::api: Received request "textDocument/diagnostic" (5)
  47.109926958s TRACE     ruff:main ruff_server::server::api: Received request "textDocument/codeAction" (6)
  47.110027791s DEBUG ruff:worker:6 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
  51.863679125s TRACE     ruff:main ruff_server::server::api: Received request "textDocument/hover" (7)
```

</p>
</details> 

<details><summary>Using <code>tracing::trace_span</code></summary>
<p>

Only logging the enter event:

```
   0.018638750s DEBUG ThreadId(11) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
   0.025895791s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/playground/ruff
   0.026378791s TRACE ruff:main notification{method="textDocument/didOpen"}: ruff_server::server::api: enter
   0.026531208s TRACE ruff:main notification{method="textDocument/didOpen"}: ruff_server::server::api: enter
   0.026567583s TRACE ruff:main request{id=1 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   0.026652541s TRACE ruff:main request{id=2 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   0.026711041s DEBUG ruff:worker:2 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/organize_imports.py
   0.026729166s DEBUG ruff:worker:1 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
   0.027023083s  INFO     ruff:main ruff_server::server: Configuration file watcher successfully registered
   5.197554750s TRACE     ruff:main notification{method="textDocument/didClose"}: ruff_server::server::api: enter
   6.534458000s TRACE     ruff:main notification{method="textDocument/didOpen"}: ruff_server::server::api: enter
   6.535027958s TRACE     ruff:main request{id=3 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   6.535271166s DEBUG ruff:worker:3 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/organize_imports.py
   6.544240583s TRACE     ruff:main request{id=4 method="textDocument/codeAction"}: ruff_server::server::api: enter
   7.049692458s TRACE     ruff:main request{id=5 method="textDocument/codeAction"}: ruff_server::server::api: enter
   7.508142541s TRACE     ruff:main request{id=6 method="textDocument/hover"}: ruff_server::server::api: enter
   7.872421958s TRACE     ruff:main request{id=7 method="textDocument/hover"}: ruff_server::server::api: enter
   8.024498583s TRACE     ruff:main request{id=8 method="textDocument/codeAction"}: ruff_server::server::api: enter
  13.895063666s TRACE     ruff:main request{id=9 method="textDocument/codeAction"}: ruff_server::server::api: enter
  14.774706083s TRACE     ruff:main request{id=10 method="textDocument/hover"}: ruff_server::server::api: enter
  16.058918958s TRACE     ruff:main notification{method="textDocument/didChange"}: ruff_server::server::api: enter
  16.060562208s TRACE     ruff:main request{id=11 method="textDocument/diagnostic"}: ruff_server::server::api: enter
  16.061109083s DEBUG ruff:worker:8 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
  21.561742875s TRACE     ruff:main notification{method="textDocument/didChange"}: ruff_server::server::api: enter
  21.563573791s TRACE     ruff:main request{id=12 method="textDocument/diagnostic"}: ruff_server::server::api: enter
  21.564206750s DEBUG ruff:worker:4 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
  21.826691375s TRACE     ruff:main request{id=13 method="textDocument/codeAction"}: ruff_server::server::api: enter
  22.091080125s TRACE     ruff:main request{id=14 method="textDocument/codeAction"}: ruff_server::server::api: enter
```

</p>
</details> 


**Todo**

- [x] Update documentation (I'll be adding a troubleshooting section
under "Editors" as a follow-up which is for all editors)
- [x] Check for backwards compatibility. I don't think this should break
backwards compatibility as it's mainly targeted towards improving the
debugging experience.

~**Before I go on to updating the documentation, I'd appreciate initial
review on the chosen approach.**~

resolves: #14959 

## Test Plan

Refer to the test plan in
https://github.com/astral-sh/ruff-vscode/pull/659.

Example logs at `debug` level:

```
   0.010770083s DEBUG ThreadId(15) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
   0.018101916s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/playground/ruff
   0.018559916s DEBUG ruff:worker:4 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
   0.018992375s  INFO     ruff:main ruff_server::server: Configuration file watcher successfully registered
  23.408802375s DEBUG ruff:worker:11 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
  24.329127416s DEBUG  ruff:worker:6 ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
```

Example logs at `trace` level:

```
   0.010296375s DEBUG ThreadId(13) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
   0.017422583s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/playground/ruff
   0.018034458s TRACE ruff:main notification{method="textDocument/didOpen"}: ruff_server::server::api: enter
   0.018199708s TRACE ruff:worker:0 request{id=1 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   0.018251167s DEBUG ruff:worker:0 request{id=1 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
   0.018528708s  INFO     ruff:main ruff_server::server: Configuration file watcher successfully registered
   1.611798417s TRACE ruff:worker:1 request{id=2 method="textDocument/codeAction"}: ruff_server::server::api: enter
   1.861757542s TRACE ruff:worker:4 request{id=3 method="textDocument/codeAction"}: ruff_server::server::api: enter
   7.027361792s TRACE ruff:worker:2 request{id=4 method="textDocument/codeAction"}: ruff_server::server::api: enter
   7.851361500s TRACE ruff:worker:5 request{id=5 method="textDocument/codeAction"}: ruff_server::server::api: enter
   7.901690875s TRACE     ruff:main notification{method="textDocument/didChange"}: ruff_server::server::api: enter
   7.903063167s TRACE ruff:worker:10 request{id=6 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   7.903183500s DEBUG ruff:worker:10 request{id=6 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
   8.702385292s TRACE      ruff:main notification{method="textDocument/didChange"}: ruff_server::server::api: enter
   8.704106625s TRACE  ruff:worker:3 request{id=7 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   8.704304875s DEBUG  ruff:worker:3 request{id=7 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/play.py
   8.966853458s TRACE  ruff:worker:9 request{id=8 method="textDocument/codeAction"}: ruff_server::server::api: enter
   9.229622792s TRACE  ruff:worker:6 request{id=9 method="textDocument/codeAction"}: ruff_server::server::api: enter
  10.513111583s TRACE  ruff:worker:7 request{id=10 method="textDocument/codeAction"}: ruff_server::server::api: enter
```
2025-01-08 18:18:00 +05:30
Alex Waygood
9a27b37a91 [flake8-builtins] Rename A005 and improve its error message (#15348) 2025-01-08 12:38:34 +00:00
Alex Waygood
487f2f5df0 Spruce up docs for pydoclint rules (#15325) 2025-01-08 12:22:37 +00:00
David Salvisberg
339167d372 [flake8-type-checking] Apply TC008 more eagerly in TYPE_CHECKING blocks and disapply it in stubs (#15180)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-08 12:09:06 +00:00
David Peter
235fdfc57a [red-knot] knot_extensions Python API (#15103)
## Summary

Adds a type-check-time Python API that allows us to create and
manipulate types and to test various of their properties. For example,
this can be used to write a Markdown test to make sure that `A & B` is a
subtype of `A` and `B`, but not of an unrelated class `C` (something
that requires quite a bit more code to do in Rust):
```py
from knot_extensions import Intersection, is_subtype_of, static_assert

class A: ...
class B: ...

type AB = Intersection[A, B]

static_assert(is_subtype_of(AB, A))
static_assert(is_subtype_of(AB, B))

class C: ...
static_assert(not is_subtype_of(AB, C))
```

I think this functionality is also helpful for interactive debugging
sessions, in order to query various properties of Red Knot's type
system. Which is something that otherwise requires a custom Rust unit
test, some boilerplate code and constant re-compilation.

## Test Plan

- New Markdown tests
- Tested the modified typeshed_sync workflow locally
2025-01-08 12:52:07 +01:00
Shaygan Hooshyari
03ff883626 Display Union of Literals as a Literal (#14993)
## Summary

Resolves #14988

Display union of Literals like other type checkers do.

With this change we lose the sorting behavior. And we show the types as
they appeared. So it's deterministic and tests should not be flaky.
This is similar to how Mypy [reveals the
type](https://mypy-play.net/?mypy=latest&python=3.12&gist=51ad03b153bfca3b940d5084345e230f).

In some cases this makes it harder to know what is the order in revealed
type when writing tests but since it's consistent after the test fails
we know the order.

## Test Plan

I adjusted mdtests for this change. Basically merged the int and string
types of the unions.

In cases where we have types other than numbers and strings like this
[one](https://github.com/astral-sh/ruff/pull/14993/files#diff-ac50bce02b9f0ad4dc7d6b8e1046d60dad919ac52d0aeb253e5884f89ea42bfeL51).
We only group the strings and numbers as the issue suggsted.

```
def _(flag: bool, flag2: bool):
    if flag:
        f = 1
    elif flag2:
        f = "foo"
    else:
        def f() -> int:
            return 1
    # error: "Object of type `Literal[1, "foo", f]` is not callable (due to union elements Literal[1], Literal["foo"])"
    # revealed: Unknown | int
    reveal_type(f())
```

[pyright
example](https://pyright-play.net/?code=GYJw9gtgBALgngBwJYDsDmUkQWEMoAySMApiAIYA2AUNQCYnBQD6AFMJeWgFxQBGYMJQA0UDlwBMvAUICU3alCWYm4nouWamAXigBGDUpKUkqzmimHNYqLoBEwQXavGAziQXXlDVa1lQAWgA%2BTBQYTy9rEBIYAFcQFH0rAGIoMnAQXjsAeT4AKxIAY3wwJngEEigAAyJSCkoAbT1RBydRYABdKsxXKBQwfEKqTj5KStY6WMqYMChYlCQwROMSCBIw3tqyKiaO0S36htawOw7ZZ01U6IA3EioSOl4AVRQAa36Ad0SAH1CYKxud0ozHKJHYflk1CAA)

[mypy
example](https://mypy-play.net/?mypy=latest&python=3.12&gist=31c8bdaa5521860cfeca4b92841cb3b7)

---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
2025-01-08 00:58:38 +00:00
Carl Meyer
fdca2b422e [red-knot] all types are assignable to object (#15332)
## Summary

`Type[Any]` should be assignable to `object`. All types should be
assignable to `object`.

We specifically didn't understand the former; this PR adds a test for
it, and a case to ensure that `Type[Any]` is assignable to anything that
`type` is assignable to (which includes `object`).

This PR also adds a property test that all types are assignable to
object. In order to make it pass, I added a special case to check early
if we are assigning to `object` and just return `true`. In principle,
once we get all the more general cases correct, this special case might
be removable. But having the special case for now allows the property
test to pass.

And we add a property test that all types are subtypes of object. This
failed for the case of an intersection with no positive elements (that
is, a negation type). This really does need to be a special case for
`object`, because there is no other type we can know that a negation
type is a subtype of.

## Test Plan

Added unit test and property test.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-07 15:19:07 -08:00
Dylan
71ad9a2ab1 [ruff] Parenthesize arguments to int when removing int would change semantics in unnecessary-cast-to-int (RUF046) (#15277)
When removing `int` in calls like `int(expr)` we may need to keep
parentheses around `expr` even when it is a function call or subscript,
since there may be newlines in between the function/value name and the
opening parentheses/bracket of the argument.

This PR implements that logic.

Closes #15263

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2025-01-07 21:43:50 +00:00
InSync
3b3c2c5aa4 [eradicate] Correctly handle metadata blocks directly followed by normal blocks (ERA001) (#15330)
## Summary

Resolves #15321.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-07 16:22:22 -05:00
Douglas Creager
b2a0d68d70 Narrowing for class patterns in match statements (#15223)
We now support class patterns in a match statement, adding a narrowing
constraint that within the body of that match arm, we can assume that
the subject is an instance of that class.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-07 15:58:12 -05:00
Carl Meyer
f2a86fcfda [red-knot] add call checking (#15200)
## Summary

This implements checking of calls.

I ended up following Micha's original suggestion from back when the
signature representation was first introduced, and flattening it to a
single array of parameters. This turned out to be easier to manage,
because we can represent parameters using indices into that array, and
represent the bound argument types as an array of the same length.

Starred and double-starred arguments are still TODO; these won't be very
useful until we have generics.

The handling of diagnostics is just hacked into `return_ty_result`,
which was already inconsistent about whether it emitted diagnostics or
not; now it's even more inconsistent. This needs to be addressed, but
could be a follow-up.

The new benchmark errors here surface the need for intersection support
in `is_assignable_to`.

Fixes #14161.

## Test Plan

Added mdtests.
2025-01-07 20:39:45 +00:00
Alex Waygood
ac72aca27c Spruce up docs for slice-to-remove-prefix-or-suffix (FURB188) (#15328) 2025-01-07 19:58:35 +00:00
Dylan
a876090715 [internal] Return statements in finally block point to end block for unreachable (PLW0101) (#15276)
Note: `PLW0101` remains in testing rather than preview, so this PR does
not modify any public behavior (hence the title beginning with
`internal` rather than `pylint`, for the sake of the changelog.)

Fixes an error in the processing of `try` statements in the control flow
graph builder.

When processing a try statement, the block following a `return` was
forced to point to the `finally` block. However, if the return was _in_
the `finally` block, this caused the block to point to itself. In the
case where the whole `try-finally` statement was also included inside of
a loop, this caused an infinite loop in the builder for the control flow
graph as it attempted to resolve edges.

Closes #15248

## Test function
### Source
```python
def l():
    while T:
        try:
            while ():
                if 3:
                    break
        finally:
            return
```

### Control Flow Graph
```mermaid
flowchart TD
  start(("Start"))
  return(("End"))
  block0[["`*(empty)*`"]]
  block1[["Loop continue"]]
  block2["return\n"]
  block3[["Loop continue"]]
  block4["break\n"]
  block5["if 3:
                    break\n"]
  block6["while ():
                if 3:
                    break\n"]
  block7[["Exception raised"]]
  block8["try:
            while ():
                if 3:
                    break
        finally:
            return\n"]
  block9["while T:
        try:
            while ():
                if 3:
                    break
        finally:
            return\n"]
  start --> block9
  block9 -- "T" --> block8
  block9 -- "else" --> block0
  block8 -- "Exception raised" --> block7
  block8 -- "else" --> block6
  block7 --> block2
  block6 -- "()" --> block5
  block6 -- "else" --> block2
  block5 -- "3" --> block4
  block5 -- "else" --> block3
  block4 --> block2
  block3 --> block6
  block2 --> return
  block1 --> block9
  block0 --> return
```
2025-01-07 11:26:04 -06:00
InSync
e4139568b8 [ruff] Treat ) as a regex metacharacter (RUF043, RUF055) (#15318)
## Summary

Resolves #15316.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-01-07 12:11:05 -05:00
Charlie Marsh
5567e7c26b Use uv consistently throughout the documentation (#15302)
## Summary

Closes
https://github.com/astral-sh/ruff/issues/15301#issuecomment-2573350821.
2025-01-07 14:43:25 +00:00
Alex Waygood
95294e657c [red-knot] Eagerly normalize type[] types (#15272)
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-01-07 12:53:07 +00:00
InSync
0dc00e63f4 [pyupgrade] Split UP007 to two individual rules for Union and Optional (UP007, UP045) (#15313)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-07 10:22:59 +00:00
David Peter
ce9c4968ae [red-knot] Improve symbol-lookup tracing (#14907)
## Summary

When debugging, I frequently want to know which symbols are being looked
up. `symbol_by_id` adds tracing information, but it only shows the
`ScopedSymbolId`. Since `symbol_by_id` is only called from `symbol`, it
seems reasonable to move the tracing call one level up from
`symbol_by_id` to `symbol`, where we can also show the name of the
symbol.

**Before**:

```
6      └─┐red_knot_python_semantic::types::infer::infer_expression_types{expression=Id(60de), file=/home/shark/tomllib_modified/_parser.py}
6        └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(33)}
6        ┌─┘
6        └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(123)}
6        ┌─┘
6        └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(54)}
6        ┌─┘
6        └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(122)}
6        ┌─┘
6        └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(165)}
6        ┌─┘
6      ┌─┘
6      └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(32)}
6      ┌─┘
6      └─┐red_knot_python_semantic::types::symbol_by_id{symbol=ScopedSymbolId(232)}
6      ┌─┘
6    ┌─┘
6  ┌─┘
6┌─┘
```

**After**:

```
5      └─┐red_knot_python_semantic::types::infer::infer_expression_types{expression=Id(60de), file=/home/shark/tomllib_modified/_parser.py}
5        └─┐red_knot_python_semantic::types::symbol{name="dict"}
5        ┌─┘
5        └─┐red_knot_python_semantic::types::symbol{name="dict"}
5        ┌─┘
5        └─┐red_knot_python_semantic::types::symbol{name="list"}
5        ┌─┘
5        └─┐red_knot_python_semantic::types::symbol{name="list"}
5        ┌─┘
5        └─┐red_knot_python_semantic::types::symbol{name="isinstance"}
5        ┌─┘
5        └─┐red_knot_python_semantic::types::symbol{name="isinstance"}
5        ┌─┘
5      ┌─┘
5      └─┐red_knot_python_semantic::types::symbol{name="ValueError"}
5      ┌─┘
5      └─┐red_knot_python_semantic::types::symbol{name="ValueError"}
5      ┌─┘
5    ┌─┘
5  ┌─┘
5┌─┘
```

## Test Plan

```
cargo run --bin red_knot -- --current-directory path/to/tomllib -vvv
```
2025-01-07 10:41:27 +01:00
Raphael Gaschignard
066239fe5b [red-knot] improve type shrinking coverage in red-knot property tests (#15297)
## Summary

While looking at #14899, I looked at seeing if I could get shrinking on
the examples. It turned out to be straightforward, with a couple of
caveats.

I'm calling `clone` a lot during shrinking. Since by the shrink step
we're already looking at a test failure this feels fine? Unless I
misunderstood `quickcheck`'s core loop

When shrinking `Intersection`s, in order to just rely on `quickcheck`'s
`Vec` shrinking without thinking about it too much, the shrinking
strategy is:
- try to shrink the negative side (keeping the positive side the same)
- try to shrink the positive side (keeping the negative side the same)

This means that you can't shrink from `(A & B & ~C & ~D)` directly to
`(A & ~C)`! You would first need an intermediate failure at `(A & B &
~C)` or `(A & ~C & ~D)`. This feels good enough. Shrinking the negative
side first also has the benefit of trying to strip down negative
elements in these intersections.

## Test Plan
`cargo test -p red_knot_python_semantic -- --ignored
types::property_tests::stable` still fails as it current does on `main`,
but now the errors seem more minimal.
2025-01-07 10:09:18 +01:00
Victor Westerhuis
1e948f739c [flake8-return] Recognize functions returning Never as non-returning (RET503) (#15298)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-07 07:57:34 +00:00
Steve C
78e26cec02 [flake8-bugbear] Implement class-as-data-structure (B903) (#9601)
## Summary

Adds `class-as-data-structure` rule (`B903`). Also compare pylint's `too-few-public-methods` (`PLR0903`).

Took some creative liberty with this by allowing the class to have any
decorators or base classes. There are years-old issues on pylint that
don't approve of the strictness when it comes to these things.

Especially considering that dataclass is a decorator and namedtuple _can
be_ a base class. I feel ignoring those explicitly is redundant all
things considered, but it's not a hill I'm willing to die on!

See: #970 

## Test Plan

`cargo test`

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-06 21:18:28 -06:00
Charlie Marsh
e7248ee43e Avoid treating newline-separated sections as sub-sections (#15311)
## Summary

Closes https://github.com/astral-sh/ruff/issues/15224.
2025-01-06 22:13:35 -05:00
Charlie Marsh
065274d353 Remove call when removing final argument from format (#15309)
## Summary

Closes https://github.com/astral-sh/ruff/issues/15303.
2025-01-07 02:53:42 +00:00
Charlie Marsh
75a24bbc67 Don't enforce object-without-hash-method in stubs (#15310)
## Summary

Closes https://github.com/astral-sh/ruff/issues/15292.
2025-01-07 02:51:06 +00:00
Douglas Creager
5e9259c96c Don't special-case class instances in binary expression inference (#15161)
Just like in #15045 for unary expressions: In binary expressions, we
were only looking for dunder expressions for `Type::Instance` types. We
had some special cases for coercing the various `Literal` types into
their corresponding `Instance` types before doing the lookup. But we can
side-step all of that by using the existing `Type::to_meta_type` and
`Type::to_instance` methods.
2025-01-06 13:50:20 -05:00
Alex Waygood
d45c1ee44f Upgrade zizmor to the latest version in CI (#15300)
## Summary

This PR upgrades zizmor to the latest release in our CI. zizmor is a
static analyzer checking for security issues in GitHub workflows. The
new release finds some new issues in our workflows; this PR fixes some
of the issues, and adds ignores for some other issues.

The issues fixed in this PR are new cases of zizmor's
[`template-injection`](https://woodruffw.github.io/zizmor/audits/#template-injection)
rule being emitted. The issues I'm ignoring for now are all to do with
the
[`cache-poisoning`](https://woodruffw.github.io/zizmor/audits/#cache-poisoning)
rule. The main reason I'm fixing some but ignoring others is that I'm
confident fixing the template-injection diagnostics won't have any
impact on how our workflows operate in CI, but I'm worried that fixing
the cache-poisoning diagnostics could slow down our CI a fair bit. I
don't mind if somebody else is motivated to try to fix these
diagnostics, but for now I think I'd prefer to just ignore them; it
doesn't seem high-priority enough to try to fix them right now :-)

## Test Plan

- `uvx pre-commit run -a --hook-stage=manual` passes locally
- Let's see if CI passes on this PR...
2025-01-06 15:07:46 +00:00
InSync
636288038f [ruff] Dataclass enums (RUF049) (#15299)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-06 14:44:20 +01:00
InSync
832c0fa04b Better error message when --config is given a table key and a non-inline-table value (#15266)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-06 13:20:28 +00:00
renovate[bot]
f29c9e48a4 Update pre-commit dependencies (#15289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-06 11:35:02 +00:00
Enric Calabuig
bafe8714a8 Don't fix in ecosystem check (#15267) 2025-01-06 10:21:34 +01:00
renovate[bot]
e5270e2ac2 Update Rust crate itertools to 0.14.0 (#15287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-06 07:55:32 +00:00
Avasam
643fd7fe07 Remove accidental empty block at the bottom of split-static-string (SIM905) doc (#15290)
## Summary

Removes the following empty code block

![image](https://github.com/user-attachments/assets/4adbacac-0bd9-4dac-af3a-93da2619a1cb)
2025-01-06 04:24:40 +00:00
renovate[bot]
bd02beec85 Update Rust crate clearscreen to v4 (#15288) 2025-01-05 20:06:13 -05:00
renovate[bot]
7f299fceef Update Rust crate insta to v1.42.0 (#15286) 2025-01-05 20:06:02 -05:00
renovate[bot]
6e2800df85 Update NPM Development dependencies (#15285) 2025-01-05 20:05:51 -05:00
renovate[bot]
391332a835 Update dependency uuid to v11.0.4 (#15284) 2025-01-05 20:05:45 -05:00
renovate[bot]
84e13cea14 Update dependency ruff to v0.8.6 (#15283) 2025-01-05 20:05:36 -05:00
renovate[bot]
bcb5f621c5 Update Rust crate syn to v2.0.95 (#15282) 2025-01-05 20:05:32 -05:00
renovate[bot]
47c8f1ad65 Update Rust crate matchit to v0.8.6 (#15281) 2025-01-05 20:04:02 -05:00
renovate[bot]
a4f8b9311e Update Rust crate bstr to v1.11.3 (#15280) 2025-01-05 20:03:54 -05:00
Alex Waygood
6097fd9bbe [red-knot] Future-proof Type::is_disjoint_from() (#15262) 2025-01-05 22:56:16 +00:00
Alex Waygood
0743838438 [red-knot] Improve Type::is_disjoint_from() for KnownInstanceTypes (#15261) 2025-01-05 22:49:42 +00:00
Alex Waygood
980ce941c7 [red-knot] Minor simplifications and improvements to constraint narrowing logic (#15270) 2025-01-05 21:51:22 +00:00
Shaygan Hooshyari
b26448926a Allow assigning ellipsis literal as parameter default value (#14982)
Resolves #14840

## Summary

Usage of ellipsis literal as default argument is allowed in stub files.

## Test Plan

Added mdtest for both python files and stub files.


---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-05 13:11:32 -06:00
Carl Meyer
2ea63620cf [red-knot] fix control flow for assignment expressions in elif tests (#15274)
## Summary

The test expression in an `elif` clause is evaluated whether or not we
take the branch. Our control flow model for if/elif chains failed to
reflect this, causing wrong inference in cases where an assignment
expression occurs inside an `elif` test expression. Our "no branch taken
yet" snapshot (which is the starting state for every new elif branch)
can't simply be the pre-if state, it must be updated after visiting each
test expression.

Once we do this, it also means we no longer need to track a vector of
narrowing constraints to reapply for each new branch, since our "branch
not taken" state (which is the initial state for each branch) is
continuously updated to include the negative narrowing constraints of
all previous branches.

Fixes #15033.

## Test Plan

Added mdtests.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-05 18:35:29 +00:00
InSync
00aa387d9d [refurb] Mark fix as unsafe when the right-hand side is a string (FURB171) (#15273) 2025-01-05 17:54:32 +00:00
Alex Waygood
eb82089551 [red-knot] Type::SubclassOf(SubclassOfType { base: ClassBase::Unknown }).to_instance() should be Unknown, not Any (#15269) 2025-01-05 15:14:01 +00:00
InSync
f144b9684d Add a test for overshadowing redirects (#15259)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-05 09:47:01 +01:00
InSync
df6e5c0293 [ruff] Recode RUF025 to RUF037 (RUF037) (#15258) 2025-01-05 09:35:08 +01:00
Alex Waygood
8f0e01787f [red-knot] Minor cleanup to Type::is_disjoint_from() and Type::is_subtype_of() (#15260) 2025-01-04 17:34:37 +00:00
Micha Reiser
6b907c1305 Ruff 0.8.6 (#15253) 2025-01-04 13:09:26 +01:00
Micha Reiser
f319531632 Make unreachable a test rule for now (#15252) 2025-01-04 12:52:08 +01:00
Micha Reiser
e4d9fe036a Revert "Add all PEP-585 names to UP006 rule" (#15250) 2025-01-04 12:23:53 +01:00
Micha Reiser
baf0d660eb Update salsa (#15243) 2025-01-03 20:04:35 +01:00
Alex Waygood
bde8ecddca [red-knot] Remove unneeded branch in Type::is_equivalent_to() (#15242)
## Summary

We understand `sys.version_info` branches now! As such, I _believe_ this
branch is no longer required; all tests pass without it. I also ran
`QUICKCHECK_TESTS=100000 cargo test -p red_knot_python_semantic --
--ignored types::property_tests::stable`, and no tests failed except for
the known issue with `Type::is_assignable_to()`
(https://github.com/astral-sh/ruff/issues/14899)

## Test Plan

See above
2025-01-03 19:04:01 +00:00
InSync
842f882ef0 [ruff] Avoid reporting when ndigits is possibly negative (RUF057) (#15234) 2025-01-03 19:48:03 +01:00
Douglas Creager
75015b0ed9 Attribute panics to the mdtests that cause them (#15241)
This updates the mdtest harness to catch any panics that occur during
type checking, and to display the panic message as an mdtest failure.
(We don't know which specific line causes the failure, so we attribute
panics to the first line of the test case.)
2025-01-03 13:45:56 -05:00
Dylan
706d87f239 Show errors for attempted fixes only when passed --verbose (#15237)
The default logging level for diagnostics includes logs written using
the `log` crate with level `error`, `warn`, and `info`. An unsuccessful
fix attached to a diagnostic via `try_set_fix` or `try_set_optional_fix`
was logged at level `error`. Note that the user would see these messages
even without passing `--fix`, and possibly also on lines with `noqa`
comments.

This PR changes the logging level here to a `debug`. We also found
ad-hoc instances of error logging in the implementations of several
rules, and have replaced those with either a `debug` or call to
`try_set{_optional}_fix`.

Closes #15229
2025-01-03 08:50:13 -06:00
w0nder1ng
0837cdd931 [RUF] Add rule to detect empty literal in deque call (RUF025) (#15104) 2025-01-03 11:57:13 +01:00
Mike Bernard
0dbfa8d0e0 TD003: remove issue code length restriction (#15175)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-03 10:42:04 +01:00
Micha Reiser
1218bc65ed Preserve multiline implicit concatenated strings in docstring positions (#15126) 2025-01-03 10:27:14 +01:00
InSync
6180f78da4 [pyflakes] Ignore errors in @no_type_check string annotations (F722, F821) (#15215) 2025-01-03 10:05:45 +01:00
Wei Lee
835b453bfd style(AIR302): rename removed_airflow_plugin_extension as check_airflow_plugin_extension (#15233)
## Summary

during the previous refactor, this renaming was missed

## Test Plan

no functionality changed
2025-01-03 10:37:21 +05:30
Auguste Lalande
a3d873ef66 [pylint] Re-implement unreachable (PLW0101) (#10891)
## Summary

This PR re-introduces the control-flow graph implementation which was
first introduced in #5384, and then removed in #9463 due to not being
feature complete. Mainly, it lacked the ability to process
`try`-`except` blocks, along with some more minor bugs.

Closes #8958 and #8959 and #14881.

## Overview of Changes

I will now highlight the major changes implemented in this PR, in order
of implementation.

1. Introduced a post-processing step in loop handling to find any
`continue` or `break` statements within the loop body and redirect them
appropriately.
2. Introduced a loop-continue block which is always placed at the end of
loop blocks, and ensures proper looping regardless of the internal logic
of the block. This resolves #8958.
3. Implemented `try` processing with the following logic (resolves
#8959):
1. In the example below the cfg first encounters a conditional
`ExceptionRaised` forking if an exception was (or will be) raised in the
try block. This is not possible to know (except for trivial cases) so we
assume both paths can be taken unconditionally.
2. Going down the `try` path the cfg goes `try`->`else`->`finally`
unconditionally.
3. Going down the `except` path the cfg will meet several conditional
`ExceptionCaught` which fork depending on the nature of the exception
caught. Again there's no way to know which exceptions may be raised so
both paths are assumed to be taken unconditionally.
4. If none of the exception blocks catch the exception then the cfg
terminates by raising a new exception.
5. A post-processing step is also implemented to redirect any `raises`
or `returns` within the blocks appropriately.
```python
def func():
    try:
        print("try")
    except Exception:
        print("Exception")
    except OtherException as e:
        print("OtherException")
    else:
        print("else")
    finally:
        print("finally")
```
```mermaid
flowchart TD
  start(("Start"))
  return(("End"))
  block0[["`*(empty)*`"]]
  block1["print(#quot;finally#quot;)\n"]
  block2["print(#quot;else#quot;)\n"]
  block3["print(#quot;try#quot;)\n"]
  block4[["Exception raised"]]
  block5["print(#quot;OtherException#quot;)\n"]
  block6["try:
        print(#quot;try#quot;)
    except Exception:
        print(#quot;Exception#quot;)
    except OtherException as e:
        print(#quot;OtherException#quot;)
    else:
        print(#quot;else#quot;)
    finally:
        print(#quot;finally#quot;)\n"]
  block7["print(#quot;Exception#quot;)\n"]
  block8["try:
        print(#quot;try#quot;)
    except Exception:
        print(#quot;Exception#quot;)
    except OtherException as e:
        print(#quot;OtherException#quot;)
    else:
        print(#quot;else#quot;)
    finally:
        print(#quot;finally#quot;)\n"]
  block9["try:
        print(#quot;try#quot;)
    except Exception:
        print(#quot;Exception#quot;)
    except OtherException as e:
        print(#quot;OtherException#quot;)
    else:
        print(#quot;else#quot;)
    finally:
        print(#quot;finally#quot;)\n"]

  start --> block9
  block9 -- "Exception raised" --> block8
  block9 -- "else" --> block3
  block8 -- "Exception" --> block7
  block8 -- "else" --> block6
  block7 --> block1
  block6 -- "OtherException" --> block5
  block6 -- "else" --> block4
  block5 --> block1
  block4 --> return
  block3 --> block2
  block2 --> block1
  block1 --> block0
  block0 --> return
``` 
6. Implemented `with` processing with the following logic:
1. `with` statements have no conditional execution (apart from the
hidden logic handling the enter and exit), so the block is assumed to
execute unconditionally.
2. The one exception is that exceptions raised within the block may
result in control flow resuming at the end of the block. Since it is not
possible know if an exception will be raised, or if it will be handled
by the context manager, we assume that execution always continues after
`with` blocks even if the blocks contain `raise` or `return` statements.
This is handled in a post-processing step.

## Test Plan

Additional test fixtures and control-flow fixtures were added.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-01-02 21:54:59 -06:00
Wei Lee
d464ef67cf refactor(AIR303): move duplicate qualified_name.to_string() to Diagnostic argument (#15220)
## Summary

Refactor airflow rule logic like
86bdc2e7b1

## Test Plan

No functionality change. Existing test cases work as it was
2025-01-03 09:10:37 +05:30
Charlie Marsh
2355472d61 Misc. clean up to rounding rules (#15231) 2025-01-02 17:51:35 -05:00
Charlie Marsh
3c3f35a548 Avoid syntax error when removing int over multiple lines (#15230)
## Summary

Closes https://github.com/astral-sh/ruff/issues/15226.
2025-01-02 17:43:15 -05:00
renovate[bot]
2327082c43 Migrate renovate config (#15228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-01-02 21:53:45 +00:00
David Peter
7671a3bbc7 Remove Type::tuple in favor of TupleType::from_elements (#15218)
## Summary

Remove `Type::tuple` in favor of `TupleType::from_elements`, avoid a few
intermediate `Vec`tors. Resolves an old [review
comment](https://github.com/astral-sh/ruff/pull/14744#discussion_r1867493706).

## Test Plan

New regression test for something I ran into while implementing this.
2025-01-02 17:22:32 +01:00
Dhruv Manilawala
11e873eb45 Bump version to 0.8.5 (#15219) 2025-01-02 17:21:21 +05:30
InSync
89ea0371a4 [ruff] Unnecessary rounding (RUF057) (#14828)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-01-02 10:00:57 +01:00
Wei Lee
f8c9665742 [airflow] Extend names moved from core to provider (AIR303) (#15216)
## Summary

Many core Airflow features have been deprecated and moved to Airflow
Providers since users might need to install an additional package (e.g.,
`apache-airflow-provider-fab==1.0.0`); a separate rule (AIR303) is
created for this.

* `airflow.kubernetes.kubernetes_helper_functions.add_pod_suffix` →
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.add_pod_suffix`
*
`airflow.kubernetes.kubernetes_helper_functions.annotations_for_logging_task_metadata`
→
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.annotations_for_logging_task_metadata`
* `airflow.kubernetes.kubernetes_helper_functions.annotations_to_key` →
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.annotations_to_key`
* `airflow.kubernetes.kubernetes_helper_functions.create_pod_id` →
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.create_pod_id`
*
`airflow.kubernetes.kubernetes_helper_functions.get_logs_task_metadata`
→
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.get_logs_task_metadata`
* `airflow.kubernetes.kubernetes_helper_functions.rand_str` →
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.rand_str`
* `airflow.kubernetes.pod.Port` →
`kubernetes.client.models.V1ContainerPort`
* `airflow.kubernetes.pod.Resources` →
`kubernetes.client.models.V1ResourceRequirements`
* `airflow.kubernetes.pod_launcher.PodLauncher` →
`airflow.providers.cncf.kubernetes.pod_launcher.PodLauncher`
* `airflow.kubernetes.pod_launcher.PodStatus` →
`airflow.providers.cncf.kubernetes.pod_launcher.PodStatus`
* `airflow.kubernetes.pod_launcher_deprecated.PodLauncher` →
`airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodLauncher`
* `airflow.kubernetes.pod_launcher_deprecated.PodStatus` →
`airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodStatus`
* `airflow.kubernetes.pod_launcher_deprecated.get_kube_client` →
`airflow.providers.cncf.kubernetes.kube_client.get_kube_client`
* `airflow.kubernetes.pod_launcher_deprecated.PodDefaults` →
`airflow.providers.cncf.kubernetes.pod_generator_deprecated.PodDefaults`
* `airflow.kubernetes.pod_runtime_info_env.PodRuntimeInfoEnv` →
`kubernetes.client.models.V1EnvVar`
* `airflow.kubernetes.volume.Volume` →
`kubernetes.client.models.V1Volume`
* `airflow.kubernetes.volume_mount.VolumeMount` →
`kubernetes.client.models.V1VolumeMount`
* `airflow.kubernetes.k8s_model.K8SModel` →
`airflow.providers.cncf.kubernetes.k8s_model.K8SModel`
* `airflow.kubernetes.k8s_model.append_to_pod` →
`airflow.providers.cncf.kubernetes.k8s_model.append_to_pod`
* `airflow.kubernetes.kube_client._disable_verify_ssl` →
`airflow.kubernetes.airflow.providers.cncf.kubernetes.kube_client._disable_verify_ssl`
* `airflow.kubernetes.kube_client._enable_tcp_keepalive` →
`airflow.kubernetes.airflow.providers.cncf.kubernetes.kube_client._enable_tcp_keepalive`
* `airflow.kubernetes.kube_client.get_kube_client` →
`airflow.kubernetes.airflow.providers.cncf.kubernetes.kube_client.get_kube_client`
* `airflow.kubernetes.pod_generator.datetime_to_label_safe_datestring` →
`airflow.providers.cncf.kubernetes.pod_generator.datetime_to_label_safe_datestring`
* `airflow.kubernetes.pod_generator.extend_object_field` →
`airflow.kubernetes.airflow.providers.cncf.kubernetes.pod_generator.extend_object_field`
* `airflow.kubernetes.pod_generator.label_safe_datestring_to_datetime` →
`airflow.providers.cncf.kubernetes.pod_generator.label_safe_datestring_to_datetime`
* `airflow.kubernetes.pod_generator.make_safe_label_value` →
`airflow.providers.cncf.kubernetes.pod_generator.make_safe_label_value`
* `airflow.kubernetes.pod_generator.merge_objects` →
`airflow.providers.cncf.kubernetes.pod_generator.merge_objects`
* `airflow.kubernetes.pod_generator.PodGenerator` →
`airflow.providers.cncf.kubernetes.pod_generator.PodGenerator`
* `airflow.kubernetes.pod_generator.PodGeneratorDeprecated` →
`airflow.providers.cncf.kubernetes.pod_generator.PodGenerator`
* `airflow.kubernetes.pod_generator.PodDefaults` →
`airflow.providers.cncf.kubernetes.pod_generator_deprecated.PodDefaults`
* `airflow.kubernetes.pod_generator.add_pod_suffix` →
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.add_pod_suffix`
* `airflow.kubernetes.pod_generator.rand_str` →
`airflow.providers.cncf.kubernetes.kubernetes_helper_functions.rand_str`
* `airflow.kubernetes.pod_generator_deprecated.make_safe_label_value` →
`airflow.providers.cncf.kubernetes.pod_generator_deprecated.make_safe_label_value`
* `airflow.kubernetes.pod_generator_deprecated.PodDefaults` →
`airflow.providers.cncf.kubernetes.pod_generator_deprecated.PodDefaults`
* `airflow.kubernetes.pod_generator_deprecated.PodGenerator` →
`airflow.providers.cncf.kubernetes.pod_generator_deprecated.PodGenerator`
* `airflow.kubernetes.secret.Secret` →
`airflow.providers.cncf.kubernetes.secret.Secret`
* `airflow.kubernetes.secret.K8SModel` →
`airflow.providers.cncf.kubernetes.k8s_model.K8SModel`

## Test Plan

A test fixture has been included for the rule.
2025-01-02 10:30:12 +05:30
InSync
af95f6b577 [pycodestyle] Avoid false positives and negatives related to type parameter default syntax (E225, E251) (#15214) 2025-01-01 11:28:25 +01:00
github-actions[bot]
79682a28b8 Sync vendored typeshed stubs (#15213)
Co-authored-by: typeshedbot <>
2025-01-01 01:14:40 +00:00
David Salvisberg
1ef0f615f1 [flake8-type-checking] Improve flexibility of runtime-evaluated-decorators (#15204)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-31 16:28:10 +00:00
Christian Clauss
7ca3f9515c Update references to astral-sh/ruff-action from v2 to v3 (#15212) 2024-12-31 16:43:17 +01:00
Wei Lee
32de5801f7 [airflow]: extend names moved from core to provider (AIR303) (#15196) 2024-12-31 15:16:07 +01:00
InSync
cfd6093579 [pydocstyle] Add setting to ignore missing documentation for*args and **kwargs parameters (D417) (#15210)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-31 12:16:55 +01:00
Arnav Gupta
3c9021ffcb [ruff] Implement falsy-dict-get-fallback (RUF056) (#15160)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-31 11:40:51 +01:00
Wei Lee
68d2466832 [airflow] Remove additional spaces (AIR302) (#15211)
<!--
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? -->

During https://github.com/astral-sh/ruff/pull/15209, additional spaces
was accidentally added to the rule
`airflow.operators.latest_only.LatestOnlyOperator`. This PR fixes this
issue

## Test Plan

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

A test fixture has been included for the rule.
2024-12-31 13:58:11 +05:30
Avasam
ecf00cdf6a Fix incorrect doc in shebang-not-executable (EXE001) and add git+windows solution to executable bit (#15208)
## Summary


I noticed that the solution mentioned in [shebang-not-executable
(EXE001)](https://docs.astral.sh/ruff/rules/shebang-not-executable/#shebang-not-executable-exe001)
was incorrect and likely copy-pasted from
[shebang-missing-executable-file
(EXE002)](https://docs.astral.sh/ruff/rules/shebang-missing-executable-file/#shebang-missing-executable-file-exe002)

It was telling users to remove the executable bit from a non-executable
file. Which does nothing.

I also noticed locally that:
- `chmod` wouldn't cause any file change to be noticed by git (`EXE` was
also passing locally) under WSL
- Using git allows anyone to fix this lint across OSes, for projects
with CIs using git

So I added a solution using [git update-index
--chmod](https://git-scm.com/docs/git-update-index#Documentation/git-update-index.txt---chmod-x)

## Test Plan

No test plan, doc changes only.
As for running the chmod commands:
https://github.com/python/typeshed/pull/13346
2024-12-31 11:05:44 +05:30
Dhruv Manilawala
86bdc2e7b1 Refactor Airflow removal in 3 code (#15209)
This PR contains a couple of refactors for the Airflow removal in 3
code, nothing major just some minor nits.
2024-12-31 05:27:12 +00:00
Wei Lee
253c274afa [airflow] Extend rule to check class attributes, methods, arguments (AIR302) (#15083)
## Summary

Airflow 3.0 removes various deprecated functions, members, modules, and
other values. They have been deprecated in 2.x, but the removal causes
incompatibilities that we want to detect. This PR add rules for the
following.

* Removed class attribute
* `airflow.providers_manager.ProvidersManager.dataset_factories` →
`airflow.providers_manager.ProvidersManager.asset_factories`
* `airflow.providers_manager.ProvidersManager.dataset_uri_handlers` →
`airflow.providers_manager.ProvidersManager.asset_uri_handlers`
*
`airflow.providers_manager.ProvidersManager.dataset_to_openlineage_converters`
→
`airflow.providers_manager.ProvidersManager.asset_to_openlineage_converters`
* `airflow.lineage.hook.DatasetLineageInfo.dataset` →
`airflow.lineage.hook.AssetLineageInfo.asset`
* Removed class method (subclasses in airflow should also checked)
* `airflow.secrets.base_secrets.BaseSecretsBackend.get_conn_uri` →
`airflow.secrets.base_secrets.BaseSecretsBackend.get_conn_value`
* `airflow.secrets.base_secrets.BaseSecretsBackend.get_connections` →
`airflow.secrets.base_secrets.BaseSecretsBackend.get_connection`
* `airflow.hooks.base.BaseHook.get_connections` → use `get_connection`
* `airflow.datasets.BaseDataset.iter_datasets` →
`airflow.sdk.definitions.asset.BaseAsset.iter_assets`
* `airflow.datasets.BaseDataset.iter_dataset_aliases` →
`airflow.sdk.definitions.asset.BaseAsset.iter_asset_aliases`
* Removed constructor args (subclasses in airflow should also checked)
* argument `filename_template`
in`airflow.utils.log.file_task_handler.FileTaskHandler`
    * in `BaseOperator`
        * `sla`
        * `task_concurrency` → `max_active_tis_per_dag`
    * in `BaseAuthManager`
        * `appbuilder`
* Removed class variable (subclasses anywhere should be checked)
    * in `airflow.plugins_manager.AirflowPlugin`
        * `executors` (from #43289)
        * `hooks`
        * `operators`
        * `sensors`
* Replaced names
	* `airflow.hooks.base_hook.BaseHook` → `airflow.hooks.base.BaseHook`
* `airflow.operators.dagrun_operator.TriggerDagRunLink` →
`airflow.operators.trigger_dagrun.TriggerDagRunLink`
* `airflow.operators.dagrun_operator.TriggerDagRunOperator` →
`airflow.operators.trigger_dagrun.TriggerDagRunOperator`
* `airflow.operators.python_operator.BranchPythonOperator` →
`airflow.operators.python.BranchPythonOperator`
* `airflow.operators.python_operator.PythonOperator` →
`airflow.operators.python.PythonOperator`
* `airflow.operators.python_operator.PythonVirtualenvOperator` →
`airflow.operators.python.PythonVirtualenvOperator`
* `airflow.operators.python_operator.ShortCircuitOperator` →
`airflow.operators.python.ShortCircuitOperator`
* `airflow.operators.latest_only_operator.LatestOnlyOperator` →
`airflow.operators.latest_only.LatestOnlyOperator`


In additional to the changes above, this PR also add utility functions
and improve docstring.


## Test Plan

A test fixture is included in the PR.
2024-12-31 09:49:18 +05:30
InSync
2a1aa29366 [pylint] Detect nested methods correctly (PLW1641) (#15032)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-30 16:55:14 +01:00
purajit
42cc67a87c [docs] improve and fix entry for analyze.include-dependencies (#15197)
## Summary

Changes two things about the entry:
* make the example valid TOML - inline tables must be a single line, at
least till v1.1.0 is released,
but also while in the future the toml version used by ruff might handle
it, it would probably be
good to stick to a spec that's readable by the vast majority of other
tools and versions as well,
especially if people are using `pyproject.toml`. The current example
leads to `ruff` failure.
See https://github.com/toml-lang/toml/pull/904
* adds a line about the ability to add non-Python files to the map,
which I think is a specific and
important feature people should know about (in fact, I would assume this
could potentially
become the single biggest use-case for this).

## Test Plan

Ran doc creation as described in the
[contribution](https://docs.astral.sh/ruff/contributing/#mkdocs) guide.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-12-30 15:45:19 +00:00
InSync
280ba75100 [flake8-pie] Allow cast(SomeType, ...) (PIE796) (#15141) 2024-12-30 15:52:35 +01:00
wookie184
04d538113a Add all PEP-585 names to UP006 rule (#5454)
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-30 12:21:42 +01:00
InSync
0b15f17939 [flake8-simplify] More precise inference for dictionaries (SIM300) (#15164)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-30 10:41:33 +00:00
Micha Reiser
0caab81d3d @no_type_check support (#15122)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-30 09:42:18 +00:00
InSync
d4ee6abf4a Visit PEP 764 inline TypedDicts' keys as non-type-expressions (#15073)
## Summary

Resolves #10812.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-12-30 15:04:55 +05:30
Dhruv Manilawala
8a98d88847 [red-knot] Add diagnostic for invalid unpacking (#15086)
## Summary

Part of #13773 

This PR adds diagnostics when there is a length mismatch during
unpacking between the number of target expressions and the number of
types for the unpack value expression.

There are 3 cases of diagnostics here where the first two occurs when
there isn't a starred expression and the last one occurs when there's a
starred expression:
1. Number of target expressions is **less** than the number of types
that needs to be unpacked
2. Number of target expressions is **greater** then the number of types
that needs to be unpacked
3. When there's a starred expression as one of the target expression and
the number of target expressions is greater than the number of types

Examples for all each of the above cases:
```py
# red-knot: Too many values to unpack (expected 2, got 3) [lint:invalid-assignment]
a, b = (1, 2, 3)

# red-knot: Not enough values to unpack (expected 2, got 1) [lint:invalid-assignment]
a, b = (1,)

# red-knot: Not enough values to unpack (expected 3 or more, got 2) [lint:invalid-assignment]
a, *b, c, d = (1, 2)
```

The (3) case is a bit special because it uses a distinct wording
"expected n or more" instead of "expected n" because of the starred
expression.

### Location

The diagnostic location is the target expression that's being unpacked.
For nested targets, the location will be the nested expression. For
example:

```py
(a, (b, c), d) = (1, (2, 3, 4), 5)
#   ^^^^^^
#   red-knot: Too many values to unpack (expected 2, got 3) [lint:invalid-assignment]
```

For future improvements, it would be useful to show the context for why
this unpacking failed. For example, for why the expected number of
targets is `n`, we can highlight the relevant elements for the value
expression.

In the **ecosystem**, **Pyright** uses the target expressions for
location while **mypy** uses the value expression for the location. For
example:

```py
if 1:
#          mypy: Too many values to unpack (2 expected, 3 provided)  [misc]
#          vvvvvvvvv
	a, b = (1, 2, 3)
#   ^^^^
#   Pyright: Expression with type "tuple[Literal[1], Literal[2], Literal[3]]" cannot be assigned to target tuple
#     Type "tuple[Literal[1], Literal[2], Literal[3]]" is incompatible with target tuple
#       Tuple size mismatch; expected 2 but received 3 [reportAssignmentType]
#   red-knot: Too many values to unpack (expected 2, got 3) [lint:invalid-assignment]
```

## Test Plan

Update existing test cases TODO with the error directives.
2024-12-30 13:10:29 +05:30
InSync
901b7dd8f8 [flake8-use-pathlib] Catch redundant joins in PTH201 and avoid syntax errors (#15177)
## Summary

Resolves #10453, resolves #15165.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-12-30 03:31:35 +00:00
renovate[bot]
d3492178e1 Update Rust crate glob to v0.3.2 (#15185) 2024-12-29 21:29:46 -05:00
renovate[bot]
383f6d0967 Update astral-sh/setup-uv action to v5 (#15193) 2024-12-29 21:29:37 -05:00
renovate[bot]
e953ecf42f Update dependency mdformat-mkdocs to v4.1.1 (#15192) 2024-12-29 21:29:33 -05:00
renovate[bot]
e8cf2d3027 Update Rust crate serde_with to v3.12.0 (#15191) 2024-12-29 21:29:27 -05:00
renovate[bot]
0701437104 Update NPM Development dependencies (#15190) 2024-12-29 21:29:23 -05:00
renovate[bot]
db0468bae5 Update pre-commit hook rhysd/actionlint to v1.7.5 (#15189) 2024-12-29 21:29:19 -05:00
renovate[bot]
7e491c4d94 Update Rust crate syn to v2.0.93 (#15188) 2024-12-29 21:29:15 -05:00
renovate[bot]
957df82400 Update Rust crate serde to v1.0.217 (#15187) 2024-12-29 21:29:11 -05:00
renovate[bot]
9ba2fb0a48 Update Rust crate quote to v1.0.38 (#15186) 2024-12-29 21:29:04 -05:00
renovate[bot]
8ff9cb75cd Update Rust crate compact_str to v0.8.1 (#15184) 2024-12-29 21:28:56 -05:00
David Salvisberg
f170932585 [flake8-type-checking] Disable TC006 & TC007 in stub files (#15179)
Fixes: #15176

## Summary

Neither of these rules make any sense in stub files. Technically TC007
should already not have triggered, due to the typing only context of the
binding, but it's better to be explicit.

Keeping TC008 enabled on the other hand makes sense to me, although we
could probably be more aggressive with unquoting in a typing runtime
context.

## Test Plan

`cargo nextest run`
2024-12-29 14:39:16 -05:00
Shantanu
bc3a735d93 Test explicit shadowing involving defs (#15174)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-29 00:47:03 +00:00
Victorien
7ea3a549b2 Fix typo in NameImport.qualified_name docstring (#15170) 2024-12-28 23:44:01 +01:00
Wei Lee
2288cc7478 [airflow]: extend names moved from core to provider (AIR303) (#15159) 2024-12-27 17:04:02 +00:00
Enoch Kan
79816f965c Fix SyntaxError in example replacement snippet in nonlocal-without-binding (#15157) 2024-12-27 12:15:13 +00:00
InSync
8d2d1a73c5 [red-knot] Report classes inheriting from bases with incompatible __slots__ (#15129) 2024-12-27 11:43:48 +00:00
Victorien
2419fdb2ef Fix typo in LogicalLine docstring (#15150) 2024-12-26 18:45:45 +01:00
Micha Reiser
6ed27c3786 Rename the knot|type-ignore mdtest files (#15147) 2024-12-26 10:25:05 +00:00
Wei Lee
5d6aae839e [airflow]: extend moved names (AIR303) (#15145) 2024-12-26 10:55:07 +01:00
sobolevn
8b9c843c72 Fix docs highlight in dict_iter_missing_items.rs (#15140) 2024-12-25 18:52:17 +01:00
Enoch Kan
5bc9d6d3aa Rename rules currently not conforming to naming convention (#15102)
## Summary

This pull request renames 19 rules which currently do not conform to
Ruff's [naming
convention](https://github.com/astral-sh/ruff/blob/main/CONTRIBUTING.md#rule-naming-convention).

## Description

Fixes astral-sh/ruff#15009.
2024-12-23 15:48:45 -06:00
Micha Reiser
97965ff114 Rename --current-directory to --project in Red Knot benchmark script (#15124) 2024-12-23 12:50:35 +00:00
Micha Reiser
8d327087ef Add invalid-ignore-comment rule (#15094) 2024-12-23 10:38:10 +00:00
Micha Reiser
2835d94ec5 Add unknown-rule (#15085)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-23 11:30:54 +01:00
Dhruv Manilawala
68ada05b00 [red-knot] Infer value expr for empty list / tuple target (#15121)
## Summary

This PR resolves
https://github.com/astral-sh/ruff/pull/15058#discussion_r1893868406 by
inferring the value expression even if there are no targets in the list
/ tuple expression.

## Test Plan

Remove TODO from corpus tests, making sure it doesn't panic.
2024-12-23 16:00:35 +05:30
Micha Reiser
2a99c0be02 Add unused-ignore-comment rule (#15084) 2024-12-23 11:15:28 +01:00
Micha Reiser
dcb85b7088 Update benchmark scripts, use uv (#15120) 2024-12-23 11:14:15 +01:00
Harutaka Kawamura
8440f3ea9f [fastapi] Update FAST002 to check keyword-only arguments (#15119)
## Summary

Close #15117. Update `FAST002` to check keyword-only arguments.

## Test Plan

New test case
2024-12-23 15:32:42 +05:30
Micha Reiser
1c3d11e8a8 Support file-level type: ignore comments (#15081) 2024-12-23 09:59:04 +00:00
Micha Reiser
2f85749fa0 type: ignore[codes] and knot: ignore (#15078) 2024-12-23 10:52:43 +01:00
InSync
9eb73cb7e0 [pycodestyle] Preserve original value format (E731) (#15097)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-23 09:29:46 +00:00
Dhruv Manilawala
03bb9425df [red-knot] Avoid Ranged for definition target range (#15118)
## Summary

Ref:
3533d7f5b4 (r150651102)

This PR removes the `Ranged` implementation on `DefinitionKind` and
instead uses a method called `target_range` to avoid any confusion about
what range this is for i.e., it's not the range of the node that
represents the definition.
2024-12-23 14:07:09 +05:30
Dhruv Manilawala
113c804a62 [red-knot] Add support for unpacking for target (#15058)
## Summary

Related to #13773 

This PR adds support for unpacking `for` statement targets.

This involves updating the `value` field in the `Unpack` target to use
an enum which specifies the "where did the value expression came from?".
This is because for an iterable expression, we need to unpack the
iterator type while for assignment statement we need to unpack the value
type itself. And, this needs to be done in the unpack query.

### Question

One of the ways unpacking works in `for` statement is by looking at the
union of the types because if the iterable expression is a tuple then
the iterator type will be union of all the types in the tuple. This
means that the test cases that will test the unpacking in `for`
statement will also implicitly test the unpacking union logic. I was
wondering if it makes sense to merge these cases and only add the ones
that are specific to the union unpacking or for statement unpacking
logic.

## Test Plan

Add test cases involving iterating over a tuple type. I've intentionally
left out certain cases for now and I'm curious to know any thoughts on
the above query.
2024-12-23 06:13:49 +00:00
Arnav Gupta
b6c8f5d79e Fix RUF200 doc to have name and email in single object (#15099)
## Summary
Closes #14975 by modifying the docstring of the InvalidPyprojectToml
rule. Previously the docs were incorrectly stating that author name and
emails must be individual items in the authors list, rather than part of
a single object for each respective author.

## Test Plan
This was a docstring change, no tests needed.
2024-12-23 10:30:16 +05:30
renovate[bot]
da8acabc55 Update dependency mdformat to v0.7.21 (#15113)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [mdformat](https://redirect.github.com/hukkin/mdformat)
([changelog](https://mdformat.readthedocs.io/en/stable/users/changelog.html))
| `==0.7.19` -> `==0.7.21` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mdformat/0.7.21?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/mdformat/0.7.21?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/mdformat/0.7.19/0.7.21?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mdformat/0.7.19/0.7.21?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>hukkin/mdformat (mdformat)</summary>

###
[`v0.7.21`](https://redirect.github.com/hukkin/mdformat/compare/0.7.20...0.7.21)

[Compare
Source](https://redirect.github.com/hukkin/mdformat/compare/0.7.20...0.7.21)

###
[`v0.7.20`](https://redirect.github.com/hukkin/mdformat/compare/0.7.19...0.7.20)

[Compare
Source](https://redirect.github.com/hukkin/mdformat/compare/0.7.19...0.7.20)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:14:06 +05:30
renovate[bot]
fd2b8deddd Update dependency ruff to v0.8.4 (#15114)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Preview features

- \[`airflow`] Extend `AIR302` with additional functions and classes
([#&#8203;15015](https://redirect.github.com/astral-sh/ruff/pull/15015))
- \[`airflow`] Implement `moved-to-provider-in-3` for modules that has
been moved to Airflow providers (`AIR303`)
([#&#8203;14764](https://redirect.github.com/astral-sh/ruff/pull/14764))
- \[`flake8-use-pathlib`] Extend check for invalid path suffix to
include the case `"."` (`PTH210`)
([#&#8203;14902](https://redirect.github.com/astral-sh/ruff/pull/14902))
- \[`perflint`] Fix panic in `PERF401` when list variable is after the
`for` loop
([#&#8203;14971](https://redirect.github.com/astral-sh/ruff/pull/14971))
- \[`perflint`] Simplify finding the loop target in `PERF401`
([#&#8203;15025](https://redirect.github.com/astral-sh/ruff/pull/15025))
- \[`pylint`] Preserve original value format (`PLR6104`)
([#&#8203;14978](https://redirect.github.com/astral-sh/ruff/pull/14978))
- \[`ruff`] Avoid false positives for `RUF027` for typing context
bindings
([#&#8203;15037](https://redirect.github.com/astral-sh/ruff/pull/15037))
- \[`ruff`] Check for ambiguous pattern passed to `pytest.raises()`
(`RUF043`)
([#&#8203;14966](https://redirect.github.com/astral-sh/ruff/pull/14966))

##### Rule changes

- \[`flake8-bandit`] Check `S105` for annotated assignment
([#&#8203;15059](https://redirect.github.com/astral-sh/ruff/pull/15059))
- \[`flake8-pyi`] More autofixes for `redundant-none-literal` (`PYI061`)
([#&#8203;14872](https://redirect.github.com/astral-sh/ruff/pull/14872))
- \[`pydocstyle`] Skip leading whitespace for `D403`
([#&#8203;14963](https://redirect.github.com/astral-sh/ruff/pull/14963))
- \[`ruff`] Skip `SQLModel` base classes for `mutable-class-default`
(`RUF012`)
([#&#8203;14949](https://redirect.github.com/astral-sh/ruff/pull/14949))

##### Bug

- \[`perflint`] Parenthesize walrus expressions in autofix for
`manual-list-comprehension` (`PERF401`)
([#&#8203;15050](https://redirect.github.com/astral-sh/ruff/pull/15050))

##### Server

- Check diagnostic refresh support from client capability which enables
dynamic configuration for various editors
([#&#8203;15014](https://redirect.github.com/astral-sh/ruff/pull/15014))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:12:07 +05:30
renovate[bot]
efbadbd966 Update pre-commit dependencies (#15115)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.8.3` -> `v0.8.4` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | patch | `v1.28.3` -> `v1.28.4` |
|
[executablebooks/mdformat](https://redirect.github.com/executablebooks/mdformat)
| repository | patch | `0.7.19` -> `0.7.21` |
|
[woodruffw/zizmor-pre-commit](https://redirect.github.com/woodruffw/zizmor-pre-commit)
| repository | minor | `v0.9.2` -> `v0.10.0` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

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

###
[`v0.8.4`](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.8.3...v0.8.4)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.8.3...v0.8.4)

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.28.4`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.28.4)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.28.3...v1.28.4)

#### \[1.28.4] - 2024-12-16

##### Features

-   `--format sarif` support

</details>

<details>
<summary>executablebooks/mdformat (executablebooks/mdformat)</summary>

###
[`v0.7.21`](https://redirect.github.com/executablebooks/mdformat/compare/0.7.20...0.7.21)

[Compare
Source](https://redirect.github.com/executablebooks/mdformat/compare/0.7.20...0.7.21)

###
[`v0.7.20`](https://redirect.github.com/executablebooks/mdformat/compare/0.7.19...0.7.20)

[Compare
Source](https://redirect.github.com/executablebooks/mdformat/compare/0.7.19...0.7.20)

</details>

<details>
<summary>woodruffw/zizmor-pre-commit
(woodruffw/zizmor-pre-commit)</summary>

###
[`v0.10.0`](https://redirect.github.com/woodruffw/zizmor-pre-commit/releases/tag/v0.10.0)

[Compare
Source](https://redirect.github.com/woodruffw/zizmor-pre-commit/compare/v0.9.2...v0.10.0)

See: https://github.com/woodruffw/zizmor/releases/tag/v0.10.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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:11:52 +05:30
renovate[bot]
130cc24e2c Update Rust crate anyhow to v1.0.95 (#15106)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [anyhow](https://redirect.github.com/dtolnay/anyhow) |
workspace.dependencies | patch | `1.0.94` -> `1.0.95` |

---

### Release Notes

<details>
<summary>dtolnay/anyhow (anyhow)</summary>

###
[`v1.0.95`](https://redirect.github.com/dtolnay/anyhow/releases/tag/1.0.95)

[Compare
Source](https://redirect.github.com/dtolnay/anyhow/compare/1.0.94...1.0.95)

- Add
[`Error::from_boxed`](https://docs.rs/anyhow/latest/anyhow/struct.Error.html#method.from_boxed)
([#&#8203;401](https://redirect.github.com/dtolnay/anyhow/issues/401),
[#&#8203;402](https://redirect.github.com/dtolnay/anyhow/issues/402))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:07:48 +05:30
renovate[bot]
d555eca729 Update Rust crate env_logger to v0.11.6 (#15107)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [env_logger](https://redirect.github.com/rust-cli/env_logger) |
workspace.dependencies | patch | `0.11.5` -> `0.11.6` |

---

### Release Notes

<details>
<summary>rust-cli/env_logger (env_logger)</summary>

###
[`v0.11.6`](https://redirect.github.com/rust-cli/env_logger/blob/HEAD/CHANGELOG.md#0116---2024-12-20)

[Compare
Source](https://redirect.github.com/rust-cli/env_logger/compare/v0.11.5...v0.11.6)

##### Features

-   Opt-in file and line rendering

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:05:45 +05:30
renovate[bot]
489f4fb27a Update Rust crate libc to v0.2.169 (#15108)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [libc](https://redirect.github.com/rust-lang/libc) |
workspace.dependencies | patch | `0.2.168` -> `0.2.169` |

---

### Release Notes

<details>
<summary>rust-lang/libc (libc)</summary>

###
[`v0.2.169`](https://redirect.github.com/rust-lang/libc/releases/tag/0.2.169)

[Compare
Source](https://redirect.github.com/rust-lang/libc/compare/0.2.168...0.2.169)

##### Added

- FreeBSD: add more socket TCP stack constants
[#&#8203;4193](https://redirect.github.com/rust-lang/libc/pull/4193)
- Fuchsia: add a `sockaddr_vm` definition
[#&#8203;4194](https://redirect.github.com/rust-lang/libc/pull/4194)

##### Fixed

**Breaking**:
[rust-lang/rust#132975](https://redirect.github.com/rust-lang/rust/pull/132975)
corrected the signedness of `core::ffi::c_char` on various Tier 2 and
Tier 3 platforms (mostly Arm and RISC-V) to match Clang. This release
contains the corresponding changes to `libc`, including the following
specific pull requests:

- ESP-IDF: Replace arch-conditional `c_char` with a reexport
[#&#8203;4195](https://redirect.github.com/rust-lang/libc/pull/4195)
- Fix `c_char` on various targets
[#&#8203;4199](https://redirect.github.com/rust-lang/libc/pull/4199)
- Mirror `c_char` configuration from `rust-lang/rust`
[#&#8203;4198](https://redirect.github.com/rust-lang/libc/pull/4198)

##### Cleanup

- Do not re-export `c_void` in target-specific code
[#&#8203;4200](https://redirect.github.com/rust-lang/libc/pull/4200)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:04:20 +05:30
renovate[bot]
429d3d78b8 Update Rust crate lsp-server to v0.7.8 (#15109)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [lsp-server](https://redirect.github.com/rust-lang/rust-analyzer)
([source](https://redirect.github.com/rust-lang/rust-analyzer/tree/HEAD/lib/lsp-server))
| workspace.dependencies | patch | `0.7.7` -> `0.7.8` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 09:57:13 +05:30
renovate[bot]
4dae9ee1cf Update Rust crate serde_json to v1.0.134 (#15110)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde_json](https://redirect.github.com/serde-rs/json) |
workspace.dependencies | patch | `1.0.133` -> `1.0.134` |

---

### Release Notes

<details>
<summary>serde-rs/json (serde_json)</summary>

###
[`v1.0.134`](https://redirect.github.com/serde-rs/json/releases/tag/v1.0.134)

[Compare
Source](https://redirect.github.com/serde-rs/json/compare/v1.0.133...v1.0.134)

- Add `RawValue` associated constants for literal `null`, `true`,
`false`
([#&#8203;1221](https://redirect.github.com/serde-rs/json/issues/1221),
thanks [@&#8203;bheylin](https://redirect.github.com/bheylin))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 09:55:26 +05:30
renovate[bot]
d8b4779a87 Update Rust crate syn to v2.0.91 (#15111)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [syn](https://redirect.github.com/dtolnay/syn) |
workspace.dependencies | patch | `2.0.90` -> `2.0.91` |

---

### Release Notes

<details>
<summary>dtolnay/syn (syn)</summary>

###
[`v2.0.91`](https://redirect.github.com/dtolnay/syn/releases/tag/2.0.91)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.90...2.0.91)

- Support parsing `Vec<Arm>` using `parse_quote!`
([#&#8203;1796](https://redirect.github.com/dtolnay/syn/issues/1796),
[#&#8203;1797](https://redirect.github.com/dtolnay/syn/issues/1797))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 09:54:41 +05:30
renovate[bot]
88cdfcfb11 Update Rust crate thiserror to v2.0.9 (#15112)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [thiserror](https://redirect.github.com/dtolnay/thiserror) |
workspace.dependencies | patch | `2.0.7` -> `2.0.9` |

---

### Release Notes

<details>
<summary>dtolnay/thiserror (thiserror)</summary>

###
[`v2.0.9`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.9)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.8...2.0.9)

- Work around `missing_inline_in_public_items` clippy restriction being
triggered in macro-generated code
([#&#8203;404](https://redirect.github.com/dtolnay/thiserror/issues/404))

###
[`v2.0.8`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.8)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.7...2.0.8)

- Improve support for macro-generated `derive(Error)` call sites
([#&#8203;399](https://redirect.github.com/dtolnay/thiserror/issues/399))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS44MC4wIiwidXBkYXRlZEluVmVyIjoiMzkuODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 09:53:37 +05:30
InSync
f764f59971 [red-knot] Treat classes as instances of their respective metaclasses in boolean tests (#15105)
## Summary

Follow-up to #15089.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-23 01:30:51 +00:00
InSync
3b27d5dbad [red-knot] More precise inference for chained boolean expressions (#15089)
## Summary

Resolves #13632.

## Test Plan

Markdown tests.
2024-12-22 10:02:28 -08:00
InSync
60e433c3b5 Rename round_applicability.rs back to unnecessary_cast_to_int.rs (#15095) 2024-12-21 21:32:26 +01:00
TomerBin
2fb6b320d8 Use TypeChecker for detecting fastapi routes (#15093) 2024-12-21 15:45:28 +01:00
Aaron Miller
fd4bea52e5 Fix type subscript on older python versions (#15090) 2024-12-21 15:28:02 +01:00
InSync
bd023c4500 [ruff] Detect more strict-integer expressions (RUF046) (#14833)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-21 14:23:26 +00:00
David Peter
000948ad3b [red-knot] Statically known branches (#15019)
## Summary

This changeset adds support for precise type-inference and
boundness-handling of definitions inside control-flow branches with
statically-known conditions, i.e. test-expressions whose truthiness we
can unambiguously infer as *always false* or *always true*.

This branch also includes:
- `sys.platform` support
- statically-known branches handling for Boolean expressions and while
  loops
- new `target-version` requirements in some Markdown tests which were
  now required due to the understanding of `sys.version_info` branches.

closes #12700 
closes #15034 

## Performance

### `tomllib`, -7%, needs to resolve one additional module (sys)

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./red_knot_main --project /home/shark/tomllib` | 22.2 ± 1.3 | 19.1 |
25.6 | 1.00 |
| `./red_knot_feature --project /home/shark/tomllib` | 23.8 ± 1.6 | 20.8
| 28.6 | 1.07 ± 0.09 |

### `black`, -6%

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./red_knot_main --project /home/shark/black` | 129.3 ± 5.1 | 119.0 |
137.8 | 1.00 |
| `./red_knot_feature --project /home/shark/black` | 136.5 ± 6.8 | 123.8
| 147.5 | 1.06 ± 0.07 |

## Test Plan

- New Markdown tests for the main feature in
  `statically-known-branches.md`
- New Markdown tests for `sys.platform`
- Adapted tests for `EllipsisType`, `Never`, etc
2024-12-21 11:33:10 +01:00
my1e5
d3f51cf3a6 [pydocstyle] Split on first whitespace character (D403) (#15082)
## Summary

This PR fixes an issue where Ruff's `D403` rule
(`first-word-uncapitalized`) was not detecting some single-word edge
cases that are picked up by `pydocstyle`.

The change involves extracting the first word of the docstring by
identifying the first whitespace character. This is consistent with
`pydocstyle` which uses `.split()` - see
8d0cdfc93e/src/pydocstyle/checker.py (L581C13-L581C64)

## Example

Here is a playground example -
https://play.ruff.rs/eab9ea59-92cf-4e44-b1a9-b54b7f69b178

```py
def example1():
    """foo"""

def example2():
    """foo
    
    Hello world!
    """

def example3():
    """foo bar

    Hello world!
    """

def example4():
    """
    foo
    """

def example5():
    """
    foo bar
    """
```

`pydocstyle` detects all five cases:
```bash
$ pydocstyle test.py --select D403
dev/test.py:2 in public function `example1`:
        D403: First word of the first line should be properly capitalized ('Foo', not 'foo')
dev/test.py:5 in public function `example2`:
        D403: First word of the first line should be properly capitalized ('Foo', not 'foo')
dev/test.py:11 in public function `example3`:
        D403: First word of the first line should be properly capitalized ('Foo', not 'foo')
dev/test.py:17 in public function `example4`:
        D403: First word of the first line should be properly capitalized ('Foo', not 'foo')
dev/test.py:22 in public function `example5`:
        D403: First word of the first line should be properly capitalized ('Foo', not 'foo')
```
Ruff (`0.8.4`) fails to catch example2 and example4.

## Test Plan

* Added two new test cases to cover the previously missed single-word
docstring cases.
2024-12-20 12:55:50 -06:00
Dhruv Manilawala
d47fba1e4a [red-knot] Add support for unpacking union types (#15052)
## Summary

Refer:
https://github.com/astral-sh/ruff/issues/13773#issuecomment-2548020368

This PR adds support for unpacking union types. 

Unpacking a union type requires us to first distribute the types for all
the targets that are involved in an unpacking. For example, if there are
two targets and a union type that needs to be unpacked, each target will
get a type from each element in the union type.

For example, if the type is `tuple[int, int] | tuple[int, str]` and the
target has two elements `(a, b)`, then
* The type of `a` will be a union of `int` and `int` which are at index
0 in the first and second tuple respectively which resolves to an `int`.
* Similarly, the type of `b` will be a union of `int` and `str` which
are at index 1 in the first and second tuple respectively which will be
`int | str`.

### Refactors

There are couple of refactors that are added in this PR:
* Add a `debug_assertion` to validate that the unpack target is a list
or a tuple
* Add a separate method to handle starred expression

## Test Plan

Update `unpacking.md` with additional test cases that uses union types.
This is done using parameter type hints style.
2024-12-20 16:31:15 +05:30
David Salvisberg
089a98e904 [ruff] Adds an allowlist for unsafe-markup-use (RUF035) (#15076)
Closes: #14523

## Summary

Adds a whitelist of calls allowed to be used within a
`markupsafe.Markup` call.

## Test Plan

`cargo nextest run`
2024-12-20 09:36:12 +00:00
Micha Reiser
913bce3cd5 Basic support for type: ignore comments (#15046)
## Summary

This PR adds initial support for `type: ignore`. It doesn't do anything
fancy yet like:

* Detecting invalid type ignore comments
* Detecting type ignore comments that are part of another suppression
comment: `# fmt: skip # type: ignore`
* Suppressing specific lints `type: ignore [code]`
* Detecting unsused type ignore comments
* ...

The goal is to add this functionality in separate PRs.

## Test Plan

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-20 10:35:09 +01:00
Harutaka Kawamura
6195c026ff [flake8-comprehensions] Skip C416 if comprehension contains unpacking (#14909)
<!--
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? -->

Fix #11482. Applies
https://github.com/adamchainz/flake8-comprehensions/pull/205 to ruff.
`C416` should be skipped if comprehension contains unpacking. Here's an
example:

```python
list_of_lists = [[1, 2], [3, 4]]

# ruff suggests `list(list_of_lists)` here, but that would change the result.
# `list(list_of_lists)` is not `[(1, 2), (3, 4)]`
a = [(x, y) for x, y in list_of_lists]

# This is equivalent to `list(list_of_lists)`
b = [x for x in list_of_lists]
```

## Test Plan

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

Existing checks

---------

Signed-off-by: harupy <hkawamura0130@gmail.com>
2024-12-20 14:35:30 +05:30
KotlinIsland
c0b0491703 Update docs for eq-without-hash (#14885)
## Summary

resolves #14883

This PR removes the known limitation section in the documentation of
`eq-without-hash`. That is not actually a limitation as a subclass
overriding the `__eq__` method would have its `__hash__` set to `None`
implicitly. The user should explicitly inherit the `__hash__` method
from the parent class.

## Test Plan

<img width="619" alt="Screenshot 2024-12-20 at 2 02 47 PM"
src="https://github.com/user-attachments/assets/552defcd-25e1-4153-9ab9-e5b9d5fbe8cc"
/>

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-12-20 14:03:16 +05:30
Micha Reiser
51863b460b Use &'static str for Replacement (#15075)
## Summary

Smaller nits follow up to https://github.com/astral-sh/ruff/pull/15054
because I wasn't able to push to the branch directly.

## Test Plan

`cargo test`
2024-12-20 07:36:21 +00:00
Wei Lee
14a5a2629e [airflow]: extend removed method calls (AIR302) (#15054)
## Summary


Airflow 3.0 removes various deprecated functions, members, modules, and
other values. They have been deprecated in 2.x, but the removal causes
incompatibilities that we want to detect. This PR deprecates the
following names and add a function for removed methods

* `airflow.datasets.manager.DatasetManager.register_dataset_change` →
`airflow.assets.manager.AssetManager.register_asset_change`
* `airflow.datasets.manager.DatasetManager.create_datasets` →
`airflow.assets.manager.AssetManager.create_assets`
* `airflow.datasets.manager.DatasetManager.notify_dataset_created` →
`airflow.assets.manager.AssetManager.notify_asset_created`
* `airflow.datasets.manager.DatasetManager.notify_dataset_changed` →
`airflow.assets.manager.AssetManager.notify_asset_changed`
* `airflow.datasets.manager.DatasetManager.notify_dataset_alias_created`
→ `airflow.assets.manager.AssetManager.notify_asset_alias_created`
*
`airflow.providers.amazon.auth_manager.aws_auth_manager.AwsAuthManager.is_authorized_dataset`
→
`airflow.providers.amazon.auth_manager.aws_auth_manager.AwsAuthManager.is_authorized_asset`
* `airflow.lineage.hook.HookLineageCollector.create_dataset` →
`airflow.lineage.hook.HookLineageCollector.create_asset`
* `airflow.lineage.hook.HookLineageCollector.add_input_dataset` →
`airflow.lineage.hook.HookLineageCollector.add_input_asset`
* `airflow.lineage.hook.HookLineageCollector.add_output_dataset` →
`airflow.lineage.hook.HookLineageCollector.dd_output_asset`
* `airflow.lineage.hook.HookLineageCollector.collected_datasets` →
`airflow.lineage.hook.HookLineageCollector.collected_assets`
*
`airflow.providers_manager.ProvidersManager.initialize_providers_dataset_uri_resources`
→
`airflow.providers_manager.ProvidersManager.initialize_providers_asset_uri_resources`

## Test Plan

A test fixture is included in the PR.
2024-12-20 08:30:30 +01:00
Alex Waygood
3aed14935d [red-knot] Add support for @final classes (#15070)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-19 21:02:14 +00:00
Alex Waygood
bcec5e615b [red-knot] Rename and rework the CoreStdlibModule enum (#15071) 2024-12-19 20:59:00 +00:00
Alex Waygood
a06099dffe [red-knot] Move attribute access on ModuleLiteral types into a dedicated method (#15067) 2024-12-19 16:02:16 +00:00
Alex Waygood
bb43085939 [red-knot] Reduce TODOs in Type::member() (#15066) 2024-12-19 15:54:01 +00:00
Dylan
c1eaf6ff72 Modify parsing of raise with cause when exception is absent (#15049)
When confronted with `raise from exc` the parser will now create a
`StmtRaise` that has `None` for the exception and `exc` for the cause.

Before, the parser created a `StmtRaise` with `from` for the exception,
no cause, and a spurious expression `exc` afterwards.
2024-12-19 13:36:32 +00:00
Dhruv Manilawala
3bb0dac235 Bump version to 0.8.4 (#15064) 2024-12-19 13:15:42 +00:00
Alex Waygood
40cba5dc8a [red-knot] Cleanup various todo_type!() messages (#15063)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-19 13:03:41 +00:00
Dylan
596d80cc8e [perflint] Parenthesize walrus expressions in autofix for manual-list-comprehension (PERF401) (#15050) 2024-12-19 06:56:45 -06:00
Alex Waygood
d8b9a366c8 Disable actionlint hook by default when running pre-commit locally (#15061) 2024-12-19 12:45:17 +00:00
Taras Matsyk
85e71ba91a [flake8-bandit] Check S105 for annotated assignment (#15059)
## Summary

A follow up PR on https://github.com/astral-sh/ruff/issues/14991
Ruff ignores hardcoded passwords for typed variables. Add a rule to
catch passwords in typed code bases

## Test Plan

Includes 2 more test typed variables
2024-12-19 12:26:40 +00:00
Douglas Creager
2802cbde29 Don't special-case class instances in unary expression inference (#15045)
We have a handy `to_meta_type` that does the right thing for class
instances, and also works for all of the other types that are “instances
of” something. Unless I'm missing something, this should let us get rid
of the catch-all clause in one fell swoop.

cf #14548
2024-12-18 14:37:17 -05:00
InSync
ed2bce6ebb [red-knot] Report invalid exceptions (#15042)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-18 18:31:24 +00:00
InSync
f0012df686 Fix typos in RUF043.py (#15044)
(Accidentally introduced in #14966.)
2024-12-18 15:39:55 +00:00
Micha Reiser
0fc4e8f795 Introduce InferContext (#14956)
## Summary

I'm currently on the fence about landing the #14760 PR because it's
unclear how we'd support tracking used and unused suppression comments
in a performant way:
* Salsa adds an "untracked" dependency to every query reading
accumulated values. This has the effect that the query re-runs on every
revision. For example, a possible future query
`unused_suppression_comments(db, file)` would re-run on every
incremental change and for every file. I don't expect the operation
itself to be expensive, but it all adds up in a project with 100k+ files
* Salsa collects the accumulated values by traversing the entire query
dependency graph. It can skip over sub-graphs if it is known that they
contain no accumulated values. This makes accumulators a great tool for
when they are rare; diagnostics are a good example. Unfortunately,
suppressions are more common, and they often appear in many different
files, making the "skip over subgraphs" optimization less effective.

Because of that, I want to wait to adopt salsa accumulators for type
check diagnostics (we could start using them for other diagnostics)
until we have very specific reasons that justify regressing incremental
check performance.

This PR does a "small" refactor that brings us closer to what I have in
#14760 but without using accumulators. To emit a diagnostic, a method
needs:

* Access to the db
* Access to the currently checked file

This PR introduces a new `InferContext` that holds on to the db, the
current file, and the reported diagnostics. It replaces the
`TypeCheckDiagnosticsBuilder`. We pass the `InferContext` instead of the
`db` to methods that *might* emit diagnostics. This simplifies some of
the `Outcome` methods, which can now be called with a context instead of
a `db` and the diagnostics builder. Having the `db` and the file on a
single type like this would also be useful when using accumulators.

This PR doesn't solve the issue that the `Outcome` types feel somewhat
complicated nor that it can be annoying when you need to report a
`Diagnostic,` but you don't have access to an `InferContext` (or the
file). However, I also believe that accumulators won't solve these
problems because:

* Even with accumulators, it's necessary to have a reference to the file
that's being checked. The struggle would be to get a reference to that
file rather than getting a reference to `InferContext`.
* Users of the `HasTy` trait (e.g., a linter) don't want to bother
getting the `File` when calling `Type::return_ty` because they aren't
interested in the created diagnostics. They just want to know what
calling the current expression would return (and if it even is a
callable). This is what the different methods of `Outcome` enable today.
I can ask for the return type without needing extra data that's only
relevant for emitting a diagnostic.

A shortcoming of this approach is that it is now a bit confusing when to
pass `db` and when an `InferContext`. An option is that we'd make the
`file` on `InferContext` optional (it won't collect any diagnostics if
`None`) and change all methods on `Type` to take `InferContext` as the
first argument instead of a `db`. I'm interested in your opinion on
this.

Accumulators are definitely harder to use incorrectly because they
remove the need to merge the diagnostics explicitly and there's no risk
that we accidentally merge the diagnostics twice, resulting in
duplicated diagnostics. I still value performance more over making our
life slightly easier.
2024-12-18 12:22:33 +00:00
InSync
ac81c72bf3 [ruff] Ambiguous pattern passed to pytest.raises() (RUF043) (#14966) 2024-12-18 11:53:48 +00:00
David Salvisberg
c0b7c36d43 [ruff] Avoid false positives for RUF027 for typing context bindings. (#15037)
Closes #14000 

## Summary

For typing context bindings we know that they won't be available at
runtime. We shouldn't recommend a fix, that will result in name errors
at runtime.

## Test Plan

`cargo nextest run`
2024-12-18 08:50:49 +01:00
Douglas Creager
e8e461da6a Prioritize attribute in from/import statement (#15041)
This tweaks the new semantics from #15026 a bit when a symbol could be
interpreted both as an attribute and a submodule of a package. For
`from...import`, we should actually prioritize the attribute, because of
how the statement itself is implemented [1].

> 1. check if the imported module has an attribute by that name
> 2. if not, attempt to import a submodule with that name and then check
the imported module again for that attribute

[1] https://docs.python.org/3/reference/simple_stmts.html#the-import-statement
2024-12-17 16:58:23 -05:00
Douglas Creager
91c9168dd7 Handle nested imports correctly in from ... import (#15026)
#14946 fixed our handling of nested imports with the `import` statement,
but didn't touch `from...import` statements.

cf
https://github.com/astral-sh/ruff/issues/14826#issuecomment-2525344515
2024-12-17 14:23:34 -05:00
Micha Reiser
80577a49f8 Upgrade salsa in fuzzer script (#15040) 2024-12-17 18:01:58 +01:00
cake-monotone
f463fa7b7c [red-knot] Narrowing For Truthiness Checks (if x or if not x) (#14687)
## Summary

Fixes #14550.

Add `AlwaysTruthy` and `AlwaysFalsy` types, representing the set of objects whose `__bool__` method can only ever return `True` or `False`, respectively, and narrow `if x` and `if not x` accordingly.


## Test Plan

- New Markdown test for truthiness narrowing `narrow/truthiness.md`
- unit tests in `types.rs` and `builders.rs` (`cargo test --package
red_knot_python_semantic --lib -- types`)
2024-12-17 08:37:07 -08:00
Micha Reiser
c3b6139f39 Upgrade salsa (#15039)
The only code change is that Salsa now requires the `Db` to implement
`Clone` to create "lightweight" snapshots.
2024-12-17 15:50:33 +00:00
InSync
c9fdb1f5e3 [pylint] Preserve original value format (PLR6104) (#14978)
## Summary

Resolves #11672.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-17 16:07:07 +01:00
Alex Waygood
463046ae07 [red-knot] Explicitly test diagnostics are emitted for unresolvable submodule imports (#15035) 2024-12-17 12:55:50 +00:00
Micha Reiser
dcb99cc817 Fix stale File status in tests (#15030)
## Summary

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

The `MemoryFileSystem::write_file` API automatically creates
non-existing ancestor directoryes
but we failed to update the status of the now created ancestor
directories in the `Files` data structure.


## Test Plan

Tested that the case in https://github.com/astral-sh/ruff/issues/15027
now passes regardless of whether the *Simple* case is commented out or
not
2024-12-17 12:45:36 +01:00
InSync
7c2e7cf25e [red-knot] Basic support for other legacy typing aliases (#14998)
## Summary

Resolves #14997.

## Test Plan

Markdown tests.
2024-12-17 09:33:15 +00:00
Wei Lee
867a8f9497 feat(AIR302): extend the following rules (#15015)
## Summary


Airflow 3.0 removes various deprecated functions, members, modules, and
other values. They have been deprecated in 2.x, but the removal causes
incompatibilities that we want to detect. This PR deprecates the
following names.

* `airflow.api_connexion.security.requires_access_dataset` →
`airflow.api_connexion.security.requires_access_asset`
* `airflow.auth.managers.base_auth_manager.is_authorized_dataset` →
`airflow.auth.managers.base_auth_manager.is_authorized_asset`
* `airflow.auth.managers.models.resource_details.DatasetDetails` →
`airflow.auth.managers.models.resource_details.AssetDetails`
* `airflow.lineage.hook.DatasetLineageInfo` →
`airflow.lineage.hook.AssetLineageInfo`
* `airflow.security.permissions.RESOURCE_DATASET` →
`airflow.security.permissions.RESOURCE_ASSET`
* `airflow.www.auth.has_access_dataset` →
`airflow.www.auth.has_access_dataset.has_access_asset`
* remove `airflow.datasets.DatasetAliasEvent`
* `airflow.datasets.Dataset` → `airflow.sdk.definitions.asset.Asset`
* `airflow.Dataset` → `airflow.sdk.definitions.asset.Asset`
* `airflow.datasets.DatasetAlias` →
`airflow.sdk.definitions.asset.AssetAlias`
* `airflow.datasets.DatasetAll` →
`airflow.sdk.definitions.asset.AssetAll`
* `airflow.datasets.DatasetAny` →
`airflow.sdk.definitions.asset.AssetAny`
* `airflow.datasets.metadata` → `airflow.sdk.definitions.asset.metadata`
* `airflow.datasets.expand_alias_to_datasets` →
`airflow.sdk.definitions.asset.expand_alias_to_assets`
* `airflow.datasets.manager.dataset_manager` → `airflow.assets.manager`
* `airflow.datasets.manager.resolve_dataset_manager` →
`airflow.assets.resolve_asset_manager`
* `airflow.datasets.manager.DatasetManager` →
`airflow.assets.AssetManager`
* `airflow.listeners.spec.dataset.on_dataset_created` →
`airflow.listeners.spec.asset.on_asset_created`
* `airflow.listeners.spec.dataset.on_dataset_changed` →
`airflow.listeners.spec.asset.on_asset_changed`
* `airflow.timetables.simple.DatasetTriggeredTimetable` →
`airflow.timetables.simple.AssetTriggeredTimetable`
* `airflow.timetables.datasets.DatasetOrTimeSchedule` →
`airflow.timetables.assets.AssetOrTimeSchedule`
*
`airflow.providers.amazon.auth_manager.avp.entities.AvpEntities.DATASET`
→ `airflow.providers.amazon.auth_manager.avp.entities.AvpEntities.ASSET`
* `airflow.providers.amazon.aws.datasets.s3.create_dataset` →
`airflow.providers.amazon.aws.assets.s3.create_asset`
*
`airflow.providers.amazon.aws.datasets.s3.convert_dataset_to_openlineage`
→
`airflow.providers.amazon.aws.datasets.s3.convert_dataset_to_openlineage`
* `airflow.providers.amazon.aws.datasets.s3.sanitize_uri` →
`airflow.providers.amazon.aws.assets.s3.sanitize_uri`
*
`airflow.providers.common.io.datasets.file.convert_dataset_to_openlineage`
→ `airflow.providers.common.io.assets.file.convert_asset_to_openlineage`
* `airflow.providers.common.io.datasets.file.sanitize_uri` →
`airflow.providers.common.io.assets.file.sanitize_uri`
* `airflow.providers.common.io.datasets.file.create_dataset` →
`airflow.providers.common.io.assets.file.create_asset`
* `airflow.providers.google.datasets.bigquery.sanitize_uri` →
`airflow.providers.google.assets.bigquery.sanitize_uri`
* `airflow.providers.google.datasets.gcs.create_dataset` →
`airflow.providers.google.assets.gcs.create_asset`
* `airflow.providers.google.datasets.gcs.sanitize_uri` →
`airflow.providers.google.assets.gcs.sanitize_uri`
* `airflow.providers.google.datasets.gcs.convert_dataset_to_openlineage`
→ `airflow.providers.google.assets.gcs.convert_asset_to_openlineage`
*
`airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_dataset`
→
`airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_asset`
* `airflow.providers.openlineage.utils.utils.DatasetInfo` →
`airflow.providers.openlineage.utils.utils.AssetInfo`
* `airflow.providers.openlineage.utils.utils.translate_airflow_dataset`
→ `airflow.providers.openlineage.utils.utils.translate_airflow_asset`
* `airflow.providers.postgres.datasets.postgres.sanitize_uri` →
`airflow.providers.postgres.assets.postgres.sanitize_uri`
* `airflow.providers.mysql.datasets.mysql.sanitize_uri` →
`airflow.providers.mysql.assets.mysql.sanitize_uri`
* `airflow.providers.trino.datasets.trino.sanitize_uri` →
`airflow.providers.trino.assets.trino.sanitize_uri`

In additional to the newly added rules above, the message for
`airflow.contrib.*` and `airflow.subdag.*` has been extended,
`airflow.sensors.external_task.ExternalTaskSensorLink` error has been
fixed and the test fixture has been reorganized

## Test Plan

A test fixture is included in the PR.
2024-12-17 08:32:48 +01:00
w0nder1ng
e22718f25f [perflint] Simplify finding the loop target in PERF401 (#15025)
Fixes #15012.

```python
def f():
    # panics when the code can't find the loop variable
    values = [1, 2, 3]
    result = []
    for i in values:
        result.append(i + 1)
    del i
```

I'm not sure exactly why this test case panics, but I suspect the `del
i` removes the binding from the semantic model's symbols.

I changed the code to search for the correct binding by directly
iterating through the bindings. Since we know exactly which binding we
want, this should find the loop variable without any complications.
2024-12-17 08:30:32 +01:00
Dhruv Manilawala
dcdc6e7c64 [red-knot] Avoid undeclared path when raising conflicting declarations (#14958)
## Summary

This PR updates the logic when raising conflicting declarations
diagnostic to avoid the undeclared path if present.

The conflicting declaration diagnostics is added when there are two or
more declarations in the control flow path of a definition whose type
isn't equivalent to each other. This can be seen in the following
example:

```py
if flag:
	x: int
x = 1  # conflicting-declarations: Unknown, int
```

After this PR, we'd avoid considering "Unknown" as part of the
conflicting declarations. This means we'd still flag it for the
following case:

```py
if flag:
	x: int
else:
	x: str
x = 1  # conflicting-declarations: int, str
```

A solution that's local to the exception control flow was also explored
which required updating the logic for merging the flow snapshot to avoid
considering declarations using a flag. This is preserved here:
https://github.com/astral-sh/ruff/compare/dhruv/control-flow-no-declarations?expand=1.

The main motivation to avoid that is we don't really understand what the
user experience is w.r.t. the Unknown type and the
conflicting-declaration diagnostics. This makes us unsure on what the
right semantics are as to whether that diagnostics should be raised or
not and when to raise them. For now, we've decided to move forward with
this PR and could decide to adopt another solution or remove the
conflicting-declaration diagnostics in the future.

Closes: #13966 

## Test Plan

Update the existing mdtest case. Add an additional case specific to
exception control flow to verify that the diagnostic is not being raised
now.
2024-12-17 09:49:39 +05:30
Douglas Creager
4ddf9228f6 Bind top-most parent when importing nested module (#14946)
When importing a nested module, we were correctly creating a binding for
the top-most parent, but we were binding that to the nested module, not
to that parent module. Moreover, we weren't treating those submodules as
members of their containing parents. This PR addresses both issues, so
that nested imports work as expected.

As discussed in ~Slack~ whatever chat app I find myself in these days
😄, this requires keeping track of which modules have been imported
within the current file, so that when we resolve member access on a
module reference, we can see if that member has been imported as a
submodule. If so, we return the submodule reference immediately, instead
of checking whether the parent module's definition defines the symbol.

This is currently done in a flow insensitive manner. The `SemanticIndex`
now tracks all of the modules that are imported (via `import`, not via
`from...import`). The member access logic mentioned above currently only
considers module imports in the file containing the attribute
expression.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-16 16:15:40 -05:00
Alex Waygood
6d72be2683 Bump zizmor pre-commit hook to the latest version and fix new warnings (#15022) 2024-12-16 17:45:46 +00:00
Alex Waygood
712c886749 Add actionlint as a pre-commit hook (with shellcheck integration) (#15021) 2024-12-16 17:32:49 +00:00
renovate[bot]
50739f91dc Update dependency mdformat-mkdocs to v4 (#15011)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[mdformat-mkdocs](https://redirect.github.com/kyleking/mdformat-mkdocs)
([changelog](https://redirect.github.com/kyleking/mdformat-mkdocs/releases))
| `==3.1.1` -> `==4.0.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mdformat-mkdocs/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/mdformat-mkdocs/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/mdformat-mkdocs/3.1.1/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mdformat-mkdocs/3.1.1/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>kyleking/mdformat-mkdocs (mdformat-mkdocs)</summary>

###
[`v4.0.0`](https://redirect.github.com/KyleKing/mdformat-mkdocs/releases/tag/v4.0.0)

[Compare
Source](https://redirect.github.com/kyleking/mdformat-mkdocs/compare/v3.1.1...v4.0.0)

#### What's Changed

- fix!: add newline after title for consistency with MKDocs style by
[@&#8203;KyleKing](https://redirect.github.com/KyleKing) in
[https://github.com/KyleKing/mdformat-mkdocs/pull/44](https://redirect.github.com/KyleKing/mdformat-mkdocs/pull/44)

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v3.1.1...v4.0.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:eyJjcmVhdGVkSW5WZXIiOiIzOS41OC4xIiwidXBkYXRlZEluVmVyIjoiMzkuNTguMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
Co-authored-by: Kyle King <KyleKing@users.noreply.github.com>
2024-12-16 22:48:37 +05:30
Dylan
6a5eff6017 [pydocstyle] Skip leading whitespace for D403 (#14963)
This PR introduces three changes to `D403`, which has to do with
capitalizing the first word in a docstring.

1. The diagnostic and fix now skip leading whitespace when determining
what counts as "the first word".
2. The name has been changed to `first-word-uncapitalized` from
`first-line-capitalized`, for both clarity and compliance with our rule
naming policy.
3. The diagnostic message and documentation has been modified slightly
to reflect this.

Closes #14890
2024-12-16 09:09:27 -06:00
renovate[bot]
a623d8f7c4 Update pre-commit dependencies (#15008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-16 11:13:49 +00:00
Dhruv Manilawala
aa429b413f Check diagnostic refresh support from client capability (#15014)
## Summary

Per the LSP spec, the property name is `workspace.diagnostics` with an
`s` at the end but the `lsp-types` dependency uses
`workspace.diagnostic` (without an `s`). Our fork contains this fix
(0f58d62879)
so we should avoid the hardcoded value.

The implication of this is that the client which doesn't support
workspace refresh capability didn't support the [dynamic
configuration](https://docs.astral.sh/ruff/editors/features/#dynamic-configuration)
feature because the server would _always_ send the workspace refresh
request but the client would ignore it. We have a fallback logic to
publish the diagnostics instead:


5f6fc3988b/crates/ruff_server/src/server/api/notifications/did_change_watched_files.rs (L28-L40)

fixes: #15013 

## Test Plan

### VS Code


https://github.com/user-attachments/assets/61ac8e6f-aa20-41cc-b398-998e1866b5bc

### Neovim



https://github.com/user-attachments/assets/4131e13c-3fba-411c-9bb7-478d26eb8d56
2024-12-16 16:26:40 +05:30
renovate[bot]
425c248232 Update Rust crate colored to v2.2.0 (#15010)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [colored](https://redirect.github.com/mackwic/colored) |
workspace.dependencies | minor | `2.1.0` -> `2.2.0` |

---

### Release Notes

<details>
<summary>mackwic/colored (colored)</summary>

###
[`v2.2.0`](https://redirect.github.com/mackwic/colored/compare/v2.1.0...v2.2.0)

[Compare
Source](https://redirect.github.com/mackwic/colored/compare/v2.1.0...v2.2.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:eyJjcmVhdGVkSW5WZXIiOiIzOS41OC4xIiwidXBkYXRlZEluVmVyIjoiMzkuNTguMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 08:48:51 +01:00
renovate[bot]
bcd944347d Update dependency monaco-editor to v0.52.2 (#15006) 2024-12-15 20:26:21 -05:00
renovate[bot]
86eff81c6a Update Rust crate thiserror to v2.0.7 (#15005) 2024-12-15 20:26:14 -05:00
renovate[bot]
24ace68560 Update Rust crate serde to v1.0.216 (#15004) 2024-12-15 20:26:08 -05:00
renovate[bot]
b664505d7b Update Rust crate libc to v0.2.168 (#15003) 2024-12-15 20:25:59 -05:00
renovate[bot]
aa575da1e7 Update Rust crate fern to v0.7.1 (#15002) 2024-12-15 20:25:52 -05:00
renovate[bot]
921eb2acb3 Update Rust crate chrono to v0.4.39 (#15001) 2024-12-15 20:25:46 -05:00
renovate[bot]
8665d2dc95 Update Rust crate bstr to v1.11.1 (#15000) 2024-12-15 20:25:39 -05:00
renovate[bot]
1cc27c995c Update NPM Development dependencies (#14999) 2024-12-15 20:25:10 -05:00
renovate[bot]
a93bc2af6b Update dependency ruff to v0.8.3 (#15007) 2024-12-15 20:25:04 -05:00
Alex Waygood
d848182340 Pin mdformat plugins in pre-commit (#14992) 2024-12-15 19:37:45 +00:00
InSync
7173e6a20b Use stripping block (|-) for page descriptions (#14980)
## Summary

Resolves #14976.

Currently, we uses this "[plain
scalar](https://yaml.org/spec/1.2.2/#733-plain-style)" format:

```yaml
description: Checks for `if key in dictionary: del dictionary[key]`.
```

Plain scalar must not contain the sequence `: `, however, so the above
is invalid.

This PR changes that to:

```yaml
description: |-
  Checks for `if key in dictionary: del dictionary[key]`.
```

`|` denotes a "[block
scalar](https://yaml.org/spec/1.2.2/#81-block-scalar-styles)", whereas
[the `-` chomping
indicator](https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator)
requires that a trailing newline, if any, must be stripped.

## Test Plan


![](https://github.com/user-attachments/assets/f00b606a-d6fe-46ac-a1c5-6a8665204ea3)
2024-12-15 17:07:29 +01:00
w0nder1ng
4a7536dc94 [perflint] Fix panic in perf401 (#14971)
Fixes #14969.

The issue was that this line:

```rust
let from_assign_to_loop = TextRange::new(binding_stmt.end(), for_stmt.start());
```

was not safe if the binding was after the target. The only way (at least
that I can think of) this can happen is if they are in different scopes,
so it now checks for that before checking if there are usages between
the two.
2024-12-15 16:22:04 +01:00
Dimitri Papadopoulos Orfanos
2d15d7d1af Improve the documentation of E201/E202 (#14983)
## Summary

The summary is misleading, as well as the
`whitespace-after-open-bracket` and `whitespace-before-close-bracket`
names - it's not only brackets, but also parentheses and braces. Align
the documentation with the actual behaviour.

Don't change the names, but align the documentation with the behaviour.

## Test Plan

No test (documentation).
2024-12-15 16:20:04 +01:00
Rebecca Chen
112e9d2d82 [ruff_python_ast] Add name and default functions to TypeParam. (#14964)
## Summary

This change adds `name` and `default` functions to `TypeParam` to access
the corresponding attributes more conveniently. I currently have these
as helper functions in code built on top of ruff_python_ast, and they
seemed like they might be generally useful.

## Test Plan

Ran the checks listed in CONTRIBUTING.md#development.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-15 12:04:51 +00:00
Alex Waygood
1389cb8e59 [red-knot] Emit an error if a bare Annotated or Literal is used in a type expression (#14973) 2024-12-15 02:00:52 +00:00
Alex Waygood
fa46ba2306 [red-knot] Fix bugs relating to assignability of dynamic type[] types (#14972) 2024-12-15 01:15:10 +00:00
github-actions[bot]
53c7ef8bfe Sync vendored typeshed stubs (#14977)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-15 01:02:41 +00:00
Alex Waygood
4d64cdb83c [red-knot] ClassLiteral(<T>) is not a disjoint type from Instance(<metaclass of T>) (#14970)
## Summary

A class is an instance of its metaclass, so `ClassLiteral("ABC")` is not
disjoint from `Instance("ABCMeta")`. However, we erroneously consider
the two types disjoint on the `main` branch. This PR fixes that.

This bug was uncovered by adding some more core types to the property
tests that provide coverage for classes that have custom metaclasses.
The additions to the property tests are included in this PR.

## Test Plan

New unit tests and property tests added. Tested with:
- `cargo test -p red_knot_python_semantic`
- `QUICKCHECK_TESTS=100000 cargo test -p red_knot_python_semantic --
--ignored types::property_tests::stable`

The assignability property test fails on this branch, but that's a known
issue that exists on `main`, due to
https://github.com/astral-sh/ruff/issues/14899.
2024-12-14 11:28:09 -08:00
Carl Meyer
ac31b26a0e [red-knot] type[] is disjoint from None, LiteralString (#14967)
## Summary

Teach red-knot that `type[...]` is always disjoint from `None` and from
`LiteralString`. Fixes #14925.

This should properly be generalized to "all instances of final types
which are not subclasses of `type`", but until we support finality,
hardcoding `None` (which is known to be final) allows us to fix the
subtype transitivity property test.

## Test Plan

Existing tests pass, added new unit tests for `is_disjoint_from` and
`is_subtype_of`.

`QUICKCHECK_TESTS=100000 cargo test -p red_knot_python_semantic --
--ignored types::property_tests::stable` fails only the "assignability
is reflexive" test, which is known to fail on `main` (#14899).

The same command, with `property_tests.rs` edited to prevent generating
intersection tests (the cause of #14899), passes all quickcheck tests.
2024-12-14 11:02:49 +01:00
InSync
a80e934838 [red-knot] Error out when an mdtest code block is unterminated (#14965)
## Summary

Resolves #14934.

## Test Plan

Added a unit test.
2024-12-13 21:51:21 -08:00
Alex Waygood
224c8438bd [red-knot] Minor simplifications to types.rs (#14962) 2024-12-13 20:31:51 +00:00
Alex Waygood
90a5439791 [red-knot] Use type[Unknown] rather than Unknown as the fallback metaclass for invalid classes (#14961) 2024-12-13 19:48:51 +00:00
Alex Waygood
4b2b126b9f [red-knot] Make is_subtype_of exhaustive (#14924) 2024-12-13 19:31:22 +00:00
InSync
9798556eb5 [red-knot] Alphabetize rules (#14960)
## Summary

Follow-up from #14950.

## Test Plan

Purely stylistic change. Shouldn't affect any functionalities.
2024-12-13 10:39:18 -08:00
InSync
aa1938f6ba [red-knot] Understand Annotated (#14950)
## Summary

Resolves #14922.

## Test Plan

Markdown tests.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-13 09:41:37 -08:00
Dhruv Manilawala
3533d7f5b4 [red-knot] Display definition range in trace logs (#14955)
I've mainly opened this PR to get some opinions. I've found having some
additional information in the tracing logs to be useful to determine
what we are currently inferring. For the `Definition` ingredient, the
range seems to be much useful. I thought of using the identifier name
but we would have to deconstruct the `Expr` to find out the identifier
which seems a lot for just trace logs. Additionally, multiple
identifiers _could_ have the same name where range would be useful.

The ranges are isolated to the names that have been defined by the
definition except for the `except` block where the entire range is being
used because the name is optional.

***Before:***

```
3      ├─   0.074671s  54ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: infer_definition_types(Id(1402)) } }
3      └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(1402), file=/Users/dhruv/playground/ruff/type_inference/isolated3/play.py}
3      ┌─┘
3      ├─   0.074768s  54ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: inner_fn_name_(Id(2800)) } }
3      ├─   0.074807s  54ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: infer_deferred_types(Id(1735)) } }
3      └─┐red_knot_python_semantic::types::infer::infer_deferred_types{definition=Id(1735), file=vendored://stdlib/typing.pyi}
3        ├─   0.074842s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: infer_definition_types(Id(14f3)) } }
3        └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(14f3), file=vendored://stdlib/typing.pyi}
3          ├─   0.074871s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: infer_expression_types(Id(1820)) } }
3          └─┐red_knot_python_semantic::types::infer::infer_expression_types{expression=Id(1820), file=vendored://stdlib/typing.pyi}
3            ├─   0.074924s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: infer_definition_types(Id(1429)) } }
3            └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(1429), file=vendored://stdlib/typing.pyi}
3              ├─   0.074958s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(3), kind: WillExecute { database_key: infer_definition_types(Id(1428)) } }
3              └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(1428), file=vendored://stdlib/typing.pyi}
3              ┌─┘
```

***After:***

```
12      ├─   0.074609s  55ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: infer_definition_types(Id(1402)) } }
12      └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(1402), range=36..37, file=/Users/dhruv/playground/ruff/type_inference/isolated3/play.py}
12      ┌─┘
12      ├─   0.074705s  55ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: inner_fn_name_(Id(2800)) } }
12      ├─   0.074742s  55ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: infer_deferred_types(Id(1735)) } }
12      └─┐red_knot_python_semantic::types::infer::infer_deferred_types{definition=Id(1735), range=30225..30236, file=vendored://stdlib/typing.pyi}
12        ├─   0.074775s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: infer_definition_types(Id(14f3)) } }
12        └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(14f3), range=9472..9474, file=vendored://stdlib/typing.pyi}
12          ├─   0.074803s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: infer_expression_types(Id(1820)) } }
12          └─┐red_knot_python_semantic::types::infer::infer_expression_types{expression=Id(1820), range=9477..9490, file=vendored://stdlib/typing.pyi}
12            ├─   0.074855s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: infer_definition_types(Id(1429)) } }
12            └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(1429), range=3139..3146, file=vendored://stdlib/typing.pyi}
12              ├─   0.074892s   0ms TRACE red_knot_workspace::db Salsa event: Event { thread_id: ThreadId(12), kind: WillExecute { database_key: infer_definition_types(Id(1428)) } }
12              └─┐red_knot_python_semantic::types::infer::infer_definition_types{definition=Id(1428), range=3102..3107, file=vendored://stdlib/typing.pyi}
12              ┌─┘
```
2024-12-13 14:29:53 +00:00
Alex Waygood
0bbe166720 [red-knot] Move the ClassBase enum to its own submodule (#14957) 2024-12-13 13:12:39 +00:00
David Peter
c3a64b44b7 [red-knot] mdtest: python version requirements (#14954)
## Summary

This is not strictly required yet, but makes these tests future-proof.
They need a `python-version` requirement as they rely on language
features that are not available in 3.9.
2024-12-13 10:40:38 +01:00
Wei Lee
dfd7f38009 [airflow]: Import modules that has been moved to airflow providers (AIR303) (#14764)
## Summary

Many core Airflow features have been deprecated and moved to Airflow
Providers since users might need to install an additional package (e.g.,
`apache-airflow-provider-fab==1.0.0`); a separate rule (AIR303) is
created for this.

As some of the changes only relate to the module/package moved, instead
of listing out all the functions, variables, and classes in a module or
a package, it warns the user to import from the new path instead of the
specific name.

The following is the ones that has been moved to
`apache-airflow-provider-fab==1.0.0`

* module moved
* `airflow.api.auth.backend.basic_auth` →
`airflow.providers.fab.auth_manager.api.auth.backend.basic_auth`
* `airflow.api.auth.backend.kerberos_auth` →
`airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth`
* `airflow.auth.managers.fab.api.auth.backend.kerberos_auth` →
`airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth`
* `airflow.auth.managers.fab.security_manager.override` →
`airflow.providers.fab.auth_manager.security_manager.override`
* classes (e.g., functions, classes) moved
* `airflow.www.security.FabAirflowSecurityManagerOverride` →
`airflow.providers.fab.auth_manager.security_manager.override.FabAirflowSecurityManagerOverride`
* `airflow.auth.managers.fab.fab_auth_manager.FabAuthManager` →
`airflow.providers.fab.auth_manager.security_manager.FabAuthManager`

## Test Plan


A test fixture has been included for the rule.
2024-12-13 10:38:07 +01:00
David Peter
e96b13c027 [red-knot] Support typing.TYPE_CHECKING (#14952)
## Summary

Add support for `typing.TYPE_CHECKING` and
`typing_extensions.TYPE_CHECKING`.

relates to: https://github.com/astral-sh/ruff/issues/14170

## Test Plan

New Markdown-based tests
2024-12-13 09:24:48 +00:00
Micha Reiser
f52b1f4a4d Add tracing support to mdtest (#14935)
## Summary

This PR extends the mdtest configuration with a `log` setting that can
be any of:

* `true`: Enables tracing
* `false`: Disables tracing (default)
* String: An ENV_FILTER similar to `RED_KNOT_LOG`

```toml
log = true
```

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

## Test Plan

I changed a test and tried `log=true`, `log=false`, and `log=INFO`
2024-12-13 09:10:01 +00:00
Micha Reiser
1c8f356e07 Re-enable the fuzzer job on PRs (#14953)
## Summary
This reverts https://github.com/astral-sh/ruff/pull/14478

I now broke main twice because I wasn't aware that the API was used by
the fuzzer.

## Test Plan
2024-12-13 09:07:27 +00:00
David Peter
2ccc9b19a7 [red-knot] Improve match mdtests (#14951)
## Summary

Minor improvement for the `match` tests to make sure we can't infer
statically whether or not a certain `case` applies.
2024-12-13 09:50:17 +01:00
Micha Reiser
c1837e4189 Rename custom-typeshed-dir, target-version and current-directory CLI options (#14930)
## Summary

This PR renames the `--custom-typeshed-dir`, `target-version`, and
`--current-directory` cli options to `--typeshed`,
`--python-version`, and `--project` as discussed in the CLI proposal
document.
I added aliases for `--target-version` (for Ruff compat) and
`--custom-typeshed-dir` (for Alex)

## Test Plan

Long help

```
An extremely fast Python type checker.

Usage: red_knot [OPTIONS] [COMMAND]

Commands:
  server  Start the language server
  help    Print this message or the help of the given subcommand(s)

Options:
      --project <PROJECT>
          Run the command within the given project directory.
          
          All `pyproject.toml` files will be discovered by walking up the directory tree from the project root, as will the project's virtual environment (`.venv`).
          
          Other command-line arguments (such as relative paths) will be resolved relative to the current working directory."#,

      --venv-path <PATH>
          Path to the virtual environment the project uses.
          
          If provided, red-knot will use the `site-packages` directory of this virtual environment to resolve type information for the project's third-party dependencies.

      --typeshed-path <PATH>
          Custom directory to use for stdlib typeshed stubs

      --extra-search-path <PATH>
          Additional path to use as a module-resolution source (can be passed multiple times)

      --python-version <VERSION>
          Python version to assume when resolving types
          
          [possible values: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13]

  -v, --verbose...
          Use verbose output (or `-vv` and `-vvv` for more verbose output)

  -W, --watch
          Run in watch mode by re-running whenever files change

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version
```

Short help 

```
An extremely fast Python type checker.

Usage: red_knot [OPTIONS] [COMMAND]

Commands:
  server  Start the language server
  help    Print this message or the help of the given subcommand(s)

Options:
      --project <PROJECT>         Run the command within the given project directory
      --venv-path <PATH>          Path to the virtual environment the project uses
      --typeshed-path <PATH>      Custom directory to use for stdlib typeshed stubs
      --extra-search-path <PATH>  Additional path to use as a module-resolution source (can be passed multiple times)
      --python-version <VERSION>  Python version to assume when resolving types [possible values: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13]
  -v, --verbose...                Use verbose output (or `-vv` and `-vvv` for more verbose output)
  -W, --watch                     Run in watch mode by re-running whenever files change
  -h, --help                      Print help (see more with '--help')
  -V, --version                   Print version

```

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-13 08:21:52 +00:00
David Peter
d7ce548893 [red-knot] Add narrowing for 'while' loops (#14947)
## Summary

Add type narrowing for `while` loops and corresponding `else` branches.

closes #14861 

## Test Plan

New Markdown tests.
2024-12-13 07:40:14 +01:00
Krishnan Chandra
be4ce16735 [ruff] Skip SQLModel base classes for mutable-class-default (RUF012) (#14949)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14892, by adding
`sqlmodel.SQLModel` to the list of classes with default copy semantics.

## Test Plan

Added a test into `RUF012.py` containing the example from the original
issue.
2024-12-12 22:19:21 -06:00
David Peter
657d26ff20 [red-knot] Tests for 'while' loop boundness (#14944)
## Summary

Regression test(s) for something that broken while implementing #14759.
We have similar tests for other control flow elements, but feel free to
let me know if this seems superfluous.

## Test Plan

New mdtests
2024-12-12 21:06:56 +01:00
Alex Waygood
dbc191d2d6 [red-knot] Fixes to Type::to_meta_type (#14942) 2024-12-12 19:55:11 +00:00
David Peter
d2712c7669 ruff_python_ast: Make Singleton Copy (#14943)
## Summary

Minor changed pulled out from #14759, as it seems to make sense in
isolation.

## Test Plan

—
2024-12-12 20:49:54 +01:00
Chandra Kiran G
e5cb4d6388 [flake8-pyi]: More autofixes for redundant-none-literal (PYI061) (#14872)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-12 19:44:32 +00:00
Sergey Mezentsev
68e8496260 [flake8-use-pathlib] Extend check for invalid path suffix to include the case "." (PTH210) (#14902)
## Summary

`PTH210` renamed to `invalid-pathlib-with-suffix` and extended to check for `.with_suffix(".")`. This caused the fix availability to be downgraded to "Sometimes", since there is no fix offered in this case.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Dylan <53534755+dylwil3@users.noreply.github.com>
2024-12-12 13:30:17 -06:00
Alex Waygood
71239f248e [red-knot] Add explicit TODO branches for many typing special forms and qualifiers (#14936) 2024-12-12 17:57:26 +00:00
Alex Waygood
58930905eb [red-knot] Fixup a few edge cases regarding type[] (#14918) 2024-12-12 16:53:03 +00:00
Dhruv Manilawala
53f2d72e02 Revert certain double quotes from workflow shell script (#14939)
Follow-up from #14938
2024-12-12 20:29:48 +05:30
Dhruv Manilawala
3629cbf35a Use double quotes consistently for shell scripts (#14938)
## Summary

The release failed
(https://github.com/astral-sh/ruff/actions/runs/12298190472/job/34321509636)
because the shell script in the Docker release workflow was using single
quotes instead of double quotes.

This is related to https://www.shellcheck.net/wiki/SC2016. I found it
via [`actionlint`](https://github.com/rhysd/actionlint). Related #14893.

I also went ahead and fixed https://www.shellcheck.net/wiki/SC2086 which
were raised in a couple of places.
2024-12-12 08:45:08 -06:00
Dylan
37f433814c Bump version to 0.8.3 (#14937)
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-12-12 14:13:06 +00:00
Alex Waygood
45b565cbb5 [red-knot] Any cannot be parameterized (#14933) 2024-12-12 11:50:34 +00:00
Micha Reiser
82faa9bb62 Add tests demonstrating f-strings with debug expressions in replacements that contain escaped characters (#14929) 2024-12-12 09:33:20 +00:00
w0nder1ng
2eac00c60f [perflint] fix invalid hoist in perf401 (#14369)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-12 09:11:09 +01:00
Alex Waygood
033ecf5a4b Also have zizmor check for low-severity security issues (#14893)
## Summary

This PR changes our zizmor configuration to also flag low-severity
security issues in our GitHub Actions workflows. It's a followup to
https://github.com/astral-sh/ruff/pull/14844. The issues being fixed
here were all flagged by [zizmor's `template-injection`
rule](https://woodruffw.github.io/zizmor/audits/#template-injection):

> Detects potential sources of code injection via template expansion.
>
> GitHub Actions allows workflows to define template expansions, which
occur within special `${{ ... }}` delimiters. These expansions happen
before workflow and job execution, meaning the expansion of a given
expression appears verbatim in whatever context it was performed in.
>
> Template expansions aren't syntax-aware, meaning that they can result
in unintended shell injection vectors. This is especially true when
they're used with attacker-controllable expression contexts, such as
`github.event.issue.title` (which the attacker can fully control by
supplying a new issue title).

[...]

> To fully remediate the vulnerability, you should not use `${{
env.VARNAME }}`, since that is still a template expansion. Instead, you
should use `${VARNAME}` to ensure that the shell itself performs the
variable expansion.

## Test Plan

I tested that this passes all zizmore warnings by running `pre-commit
run -a zizmor` locally. The other test is obviously to check that the
workflows all still run correctly in CI 😄
2024-12-12 07:43:17 +00:00
Peter Tripp
5509a3d7ae Add LSP settings example for Zed editor (#14894)
## Summary

Add Zed settings examples to in addition to NeoVim and VSCode.

<img width="373" alt="Screenshot 2024-12-11 at 9 54 57"
src="https://github.com/user-attachments/assets/2f00cd8b-b23e-4ecb-8a0d-732ec275ee6b"
/>

## Test Plan

[*] Tested locally. No errors. Looks fine to me.
2024-12-12 12:44:56 +05:30
InSync
e4885a2fb2 [red-knot] Understand typing.Tuple (#14927)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-12 00:58:06 +00:00
David Peter
a7e5e42b88 [red-knot] Make attributes.md test future-proof (#14923)
## Summary

Using `typing.LiteralString` breaks as soon as we understand
`sys.version_info` branches, as it's only available in 3.11 and later.

## Test Plan

Made sure it didn't fail on my #14759 branch anymore.
2024-12-11 20:46:24 +01:00
Alex Waygood
c361cf66ad [red-knot] Precise inference for __class__ attributes on objects of all types (#14921) 2024-12-11 17:30:34 +00:00
Alex Waygood
a54353392f [red-knot] Add failing test for use of type[] as a base class (#14913)
We support using `typing.Type[]` as a base class (and we have tests for
it), but not yet `builtins.type[]`. At some point we should fix that,
but I don't think it';s worth spending much time on now (and it might be
easier once we've implemented generics?). This PR just adds a failing
test with a TODO.
2024-12-11 17:08:00 +00:00
Alex Waygood
ef153a0cce [red-knot] Remove an unnecessary branch and a confusing TODO comment (#14915) 2024-12-11 16:57:40 +00:00
InSync
0c85023cd9 Fix a typo in if_key_in_dict_del.rs (#14920)
(Accidentally introduced in #14553).
2024-12-11 16:28:27 +00:00
Alex Waygood
7135a49aea [red-knot] Record the TODO message in ClassBase::Todo, same as in Type::Todo (#14919) 2024-12-11 15:17:56 +00:00
Micha Reiser
6e11086c98 Support lint:<rule> in mdtests (#14914)
## Summary

Fixes a small scoping issue in `DiagnosticId::matches`

Note: I don't think we should use `lint:id` in mdtests just yet. I worry
that it could lead to many unnecessary churns if we decide **not** to
use `lint:<id>` as the format (e.g., `lint/id`).

The reason why users even see `lint:<rule>` is because the mdtest
framework uses the diagnostic infrastructure

Closes #14910

## Test Plan

Added tests
2024-12-11 14:37:12 +01:00
Micha Reiser
28653c7c47 Strip lint: prefix from mdtest diagnostics 2024-12-11 14:33:31 +01:00
Alex Waygood
1d91dae11f [red-knot] Minor simplifications to mro.rs (#14912) 2024-12-11 13:14:12 +00:00
Micha Reiser
01d16e8941 Fix fuzzer build (#14911) 2024-12-11 13:06:51 +00:00
Micha Reiser
881375a8d9 [red-knot] Lint registry and rule selection (#14874)
## Summary

This is the third and last PR in this stack that adds support for
toggling lints at a per-rule level.

This PR introduces a new `LintRegistry`, a central index of known lints.
The registry is required because we want to support lint rules from many
different crates but need a way to look them up by name, e.g., when
resolving a lint from a name in the configuration or analyzing a
suppression comment.

Adding a lint now requires two steps:

1. Declare the lint with `declare_lint`
2. Register the lint in the registry inside the `register_lints`
function.

I considered some more involved macros to avoid changes in two places.
Still, I ultimately decided against it because a) it's just two places
and b) I'd expect that registering a type checker lint will differ from
registering a lint that runs as a rule in the linter. I worry that any
more opinionated design could limit our options when working on the
linter, so I kept it simple.

The second part of this PR is the `RuleSelection`. It stores which lints
are enabled and what severity they should use for created diagnostics.
For now, the `RuleSelection` always gets initialized with all known
lints and it uses their default level.

## Linter crates

Each crate that defines lints should export a `register_lints` function
that accepts a `&mut LintRegistryBuilder` to register all its known
lints in the registry. This should make registering all known lints in a
top-level crate easy: Just call `register_lints` of every crate that
defines lint rules.

I considered defining a `LintCollection` trait and even some fancy
macros to accomplish the same but decided to go for this very simplistic
approach for now. We can add more abstraction once needed.

## Lint rules

This is a bit hand-wavy. I don't have a good sense for how our linter
infrastructure will look like, but I expect we'll need a way to register
the rules that should run as part of the red knot linter. One way is to
keep doing what Ruff does by having one massive `checker` and each lint
rule adds a call to itself in the relevant AST visitor methods. An
alternative is that we have a `LintRule` trait that provides common
hooks and implementations will be called at the "right time". Such a
design would need a way to register all known lint implementations,
possibly with the lint. This is where we'd probably want a dedicated
`register_rule` method. A third option is that lint rules are handled
separately from the `LintRegistry` and are specific to the linter crate.

The current design should be flexible enough to support the three
options.


## Documentation generation

The documentation for all known lints can be generated by creating a
factory, registering all lints by calling the `register_lints` methods,
and then querying the registry for the metadata.

## Deserialization and Schema generation

I haven't fully decided what the best approach is when it comes to
deserializing lint rule names:

* Reject invalid names in the deserializer. This gives us error messages
with line and column numbers (by serde)
* Don't validate lint rule names during deserialization; defer the
validation until the configuration is resolved. This gives us more
control over handling the error, e.g. emit a warning diagnostic instead
of aborting when a rule isn't known.

One technical challenge for both deserialization and schema generation
is that the `Deserialize` and `JSONSchema` traits do not allow passing
the `LintRegistry`, which is required to look up the lints by name. I
suggest that we either rely on the salsa db being set for the current
thread (`salsa::Attach`) or build our own thread-local storage for the
`LintRegistry`. It's the caller's responsibility to make the lint
registry available before calling `Deserialize` or `JSONSchema`.


## CLI support

I prefer deferring adding support for enabling and disabling lints from
the CLI for now because I think it will be easier
to add once I've figured out how to handle configurations. 

## Bitset optimization

Ruff tracks the enabled rules using a cheap copyable `Bitset` instead of
a hash map. This helped improve performance by a few percent (see
https://github.com/astral-sh/ruff/pull/3606). However, this approach is
no longer possible because lints have no "cheap" way to compute their
index inside the registry (other than using a hash map).

We could consider doing something similar to Salsa where each
`LintMetadata` stores a `LazyLintIndex`.

```
pub struct LazyLintIndex {
	cached: OnceLock<(Nonce, LintIndex)>
}

impl LazyLintIndex {
	pub fn get(registry: &LintRegistry, lint: &'static LintMetadata) {
	
	let (nonce, index) = self.cached.get_or_init(|| registry.lint_index(lint));

	if registry.nonce() == nonce {
		index
	} else {
		registry.lint_index(lint)
	}
}
```

Each registry keeps a map from `LintId` to `LintIndex` where `LintIndex`
is in the range of `0...registry.len()`. The `LazyLintIndex` is based on
the assumption that every program has exactly **one** registry. This
assumption allows to cache the `LintIndex` directly on the
`LintMetadata`. The implementation falls back to the "slow" path if
there is more than one registry at runtime.

I was very close to implementing this optimization because it's kind of
fun to implement. I ultimately decided against it because it adds
complexity and I don't think it's worth doing in Red Knot today:

* Red Knot only queries the rule selection when deciding whether or not
to emit a diagnostic. It is rarely used to detect if a certain code
block should run. This is different from Ruff where the rule selection
is queried many times for every single AST node to determine which rules
*should* run.
* I'm not sure if a 2-3% performance improvement is worth the complexity

I suggest revisiting this decision when working on the linter where a
fast path for deciding if a rule is enabled might be more important (but
that depends on how lint rules are implemented)


## Test Plan

I removed a lint from the default rule registry, and the MD tests
started failing because the diagnostics were no longer emitted.
2024-12-11 13:25:19 +01:00
InSync
6f8d8fa36b [ruff] if k in d: del d[k] (RUF051) (#14553)
## Summary

Resolves #7537.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-11 11:12:23 +00:00
InSync
f30227c436 [red-knot] Understand typing.Type (#14904)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-11 11:01:38 +00:00
InSync
c8d505c8ea [pyupgrade] Do not report when a UTF-8 comment is followed by a non-UTF-8 one (UP009) (#14728)
## Summary

Resolves #14704.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-11 10:30:41 +00:00
Dimitri Papadopoulos Orfanos
a55722e740 Revert disjointness->disjointedness (#14906)
## Summary

Partially revert #14880. While `disjointness` is missing from the
[OED](https://www.oed.com/search/dictionary/?q=disjointness) and [SCOWL
(And
Friends)](http://app.aspell.net/lookup?dict=en_US-large;words=disjointness),
it is commonly used in mathematics to describe disjoint sets.

## Test Plan

CI tests.
2024-12-11 08:26:45 +00:00
Dylan
a3bb0cd5ec Raise syntax error for mixing except and except* (#14895)
This PR adds a syntax error if the parser encounters a `TryStmt` that
has except clauses both with and without a star.

The displayed error points to each except clause that contradicts the
original except clause kind. So, for example,

```python
try:
    ....
except:     #<-- we assume this is the desired except kind
    ....
except*:    #<---  error will point here
    ....
except*:    #<--- and here
    ....
```

Closes #14860
2024-12-10 17:50:55 -06:00
Douglas Creager
d4126f6049 Handle type[Any] correctly (#14876)
This adds support for `type[Any]`, which represents an unknown type (not
an instance of an unknown type), and `type`, which we are choosing to
interpret as `type[object]`.

Closes #14546
2024-12-10 16:12:37 -05:00
Carl Meyer
03fb2e5ac1 [red-knot] split call-outcome enums to their own submodule (#14898)
## Summary

This is already several hundred lines of code, and it will get more
complex with call-signature checking.

## Test Plan

This is a pure code move; the moved code wasn't changed, just imports.
Existing tests pass.
2024-12-10 12:03:29 -08:00
David Peter
1a3c311ac5 [red-knot] Property tests: account non-fully-static types (#14897)
## Summary

Add a `is_fully_static` premise to the equivalence on subtyping property tests.

## Test Plan

```
cargo test -p red_knot_python_semantic -- --ignored types::property_tests::stable
```
2024-12-10 19:55:45 +00:00
Andrew Gallant
2ecd164adb ruff: add worktree support to build.rs (#14896)
Without this, `cargo insta test` re-compiles every time it is run, even
if there are no changes. With this, I can re-run `cargo insta test` (or
other `cargo build` commands) without it resulting in re-compiles.

I made an identical change to uv a while back:
https://github.com/astral-sh/uv/pull/6825
2024-12-10 14:06:59 -05:00
Micha Reiser
5fc8e5d80e [red-knot] Add infrastructure to declare lints (#14873)
## Summary

This is the second PR out of three that adds support for
enabling/disabling lint rules in Red Knot. You may want to take a look
at the [first PR](https://github.com/astral-sh/ruff/pull/14869) in this
stack to familiarize yourself with the used terminology.

This PR adds a new syntax to define a lint: 

```rust
declare_lint! {
    /// ## What it does
    /// Checks for references to names that are not defined.
    ///
    /// ## Why is this bad?
    /// Using an undefined variable will raise a `NameError` at runtime.
    ///
    /// ## Example
    ///
    /// ```python
    /// print(x)  # NameError: name 'x' is not defined
    /// ```
    pub(crate) static UNRESOLVED_REFERENCE = {
        summary: "detects references to names that are not defined",
        status: LintStatus::preview("1.0.0"),
        default_level: Level::Warn,
    }
}
```

A lint has a name and metadata about its status (preview, stable,
removed, deprecated), the default diagnostic level (unless the
configuration changes), and documentation. I use a macro here to derive
the kebab-case name and extract the documentation automatically.

This PR doesn't yet add any mechanism to discover all known lints. This
will be added in the next and last PR in this stack.


## Documentation
I documented some rules but then decided that it's probably not my best
use of time if I document all of them now (it also means that I play
catch-up with all of you forever). That's why I left some rules
undocumented (marked with TODO)

## Where is the best place to define all lints?

I'm not sure. I think what I have in this PR is fine but I also don't
love it because most lints are in a single place but not all of them. If
you have ideas, let me know.


## Why is the message not part of the lint, unlike Ruff's `Violation`

I understand that the main motivation for defining `message` on
`Violation` in Ruff is to remove the need to repeat the same message
over and over again. I'm not sure if this is an actual problem. Most
rules only emit a diagnostic in a single place and they commonly use
different messages if they emit diagnostics in different code paths,
requiring extra fields on the `Violation` struct.

That's why I'm not convinced that there's an actual need for it and
there are alternatives that can reduce the repetition when creating a
diagnostic:

* Create a helper function. We already do this in red knot with the
`add_xy` methods
* Create a custom `Diagnostic` implementation that tailors the entire
diagnostic and pre-codes e.g. the message

Avoiding an extra field on the `Violation` also removes the need to
allocate intermediate strings as it is commonly the place in Ruff.
Instead, Red Knot can use a borrowed string with `format_args`

## Test Plan

`cargo test`
2024-12-10 16:14:44 +00:00
Micha Reiser
5f548072d9 [red-knot] Typed diagnostic id (#14869)
## Summary

This PR introduces a structured `DiagnosticId` instead of using a plain
`&'static str`. It is the first of three in a stack that implements a
basic rules infrastructure for Red Knot.

`DiagnosticId` is an enum over all known diagnostic codes. A closed enum
reduces the risk of accidentally introducing two identical diagnostic
codes. It also opens the possibility of generating reference
documentation from the enum in the future (not part of this PR).

The enum isn't *fully closed* because it uses a `&'static str` for lint
names. This is because we want the flexibility to define lints in
different crates, and all names are only known in `red_knot_linter` or
above. Still, lower-level crates must already reference the lint names
to emit diagnostics. We could define all lint-names in `DiagnosticId`
but I decided against it because:

* We probably want to share the `DiagnosticId` type between Ruff and Red
Knot to avoid extra complexity in the diagnostic crate, and both tools
use different lint names.
* Lints require a lot of extra metadata beyond just the name. That's why
I think defining them close to their implementation is important.

In the long term, we may also want to support plugins, which would make
it impossible to know all lint names at compile time. The next PR in the
stack introduces extra syntax for defining lints.

A closed enum does have a few disadvantages:

* rustc can't help us detect unused diagnostic codes because the enum is
public
* Adding a new diagnostic in the workspace crate now requires changes to
at least two crates: It requires changing the workspace crate to add the
diagnostic and the `ruff_db` crate to define the diagnostic ID. I
consider this an acceptable trade. We may want to move `DiagnosticId` to
its own crate or into a shared `red_knot_diagnostic` crate.


## Preventing duplicate diagnostic identifiers

One goal of this PR is to make it harder to introduce ambiguous
diagnostic IDs, which is achieved by defining a closed enum. However,
the enum isn't fully "closed" because it doesn't explicitly list the IDs
for all lint rules. That leaves the possibility that a lint rule and a
diagnostic ID share the same name.

I made the names unambiguous in this PR by separating them into
different namespaces by using `lint/<rule>` for lint rule codes. I don't
mind the `lint` prefix in a *Ruff next* context, but it is a bit weird
for a standalone type checker. I'd like to not overfocus on this for now
because I see a few different options:

* We remove the `lint` prefix and add a unit test in a top-level crate
that iterates over all known lint rules and diagnostic IDs to ensure the
names are non-overlapping.
* We only render `[lint]` as the error code and add a note to the
diagnostic mentioning the lint rule. This is similar to clippy and has
the advantage that the header line remains short
(`lint/some-long-rule-name` is very long ;))
* Any other form of adjusting the diagnostic rendering to make the
distinction clear

I think we can defer this decision for now because the `DiagnosticId`
contains all the relevant information to change the rendering
accordingly.


## Why `Lint` and not `LintRule`

I see three kinds of diagnostics in Red Knot:

* Non-suppressable: Reveal type, IO errors, configuration errors, etc.
(any `DiagnosticId`)
* Lints: code-related diagnostics that are suppressable. 
* Lint rules: The same as lints, but they can be enabled or disabled in
the configuration. The majority of lints in Red Knot and the Ruff
linter.

Our current implementation doesn't distinguish between lints and Lint
rules because we aren't aware of a suppressible code-related lint that
can't be configured in the configuration. The only lint that comes to my
mind is maybe `division-by-zero` if we're 99.99% sure that it is always
right. However, I want to keep the door open to making this distinction
in the future if it proves useful.

Another reason why I chose lint over lint rule (or just rule) is that I
want to leave room for a future lint rule and lint phase concept:

* lint is the *what*: a specific code smell, pattern, or violation 
* the lint rule is the *how*: I could see a future `LintRule` trait in
`red_knot_python_linter` that provides the necessary hooks to run as
part of the linter. A lint rule produces diagnostics for exactly one
lint. A lint rule differs from all lints in `red_knot_python_semantic`
because they don't run as "rules" in the Ruff sense. Instead, they're a
side-product of type inference.
* the lint phase is a different form of *how*: A lint phase can produce
many different lints in a single pass. This is a somewhat common pattern
in Ruff where running one analysis collects the necessary information
for finding many different lints
* diagnostic is the *presentation*: Unlike a lint, the diagnostic isn't
the what, but how a specific lint gets presented. I expect that many
lints can use one generic `LintDiagnostic`, but a few lints might need
more flexibility and implement their custom diagnostic rendering (at
least custom `Diagnostic` implementation).


## Test Plan

`cargo test`
2024-12-10 15:58:07 +00:00
Karthikeyan Singaravelan
dc0d944608 [airflow] Add fix to remove deprecated keyword arguments (AIR302) (#14887)
## Summary

Add replacement fixes to deprecated arguments of a DAG.

Ref #14582 #14626

## Test Plan

Diff was verified and snapshots were updated.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-12-10 18:49:28 +05:30
InSync
15fe540251 Improve mdtests style (#14884)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-10 13:05:51 +00:00
Micha Reiser
7a0e9b34d0 Reference suppress-dummy-regex-options in documentation of rules supporting it (#14888)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/14663
2024-12-10 09:53:53 +00:00
InSync
4b8c815b27 [flake8-bugbear] itertools.batched() without explicit strict (B911) (#14408)
## Summary

Resolves #14387.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-10 08:39:46 +00:00
Alex Waygood
e3f34b8f5b [ruff] Mark autofix for RUF052 as always unsafe (#14824) 2024-12-09 23:11:44 +00:00
Alex Waygood
ab26d9cf9a [red-knot] Improve type inference for except handlers (#14838) 2024-12-09 22:49:58 +00:00
Dimitri Papadopoulos Orfanos
64944f2cf5 More typos found by codespell (#14880) 2024-12-09 22:47:34 +00:00
Carl Meyer
533e8a6ee6 [red-knot] move standalone expression_ty to TypeInferenceBuilder::file_expression_ty (#14879)
## Summary

Per suggestion in
https://github.com/astral-sh/ruff/pull/14802#discussion_r1875455417

This is a bit less error-prone and allows us to handle both expressions
in the current scope or a different scope. Also, there's currently no
need for this method outside of `TypeInferenceBuilder`, so no reason to
expose it in `types.rs`.

## Test Plan

Pure refactor, no functional change; existing tests pass.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-09 17:02:14 +00:00
InSync
c62ba48ad4 [ruff] Do not simplify round() calls (RUF046) (#14832)
## Summary

Part 1 of the big change introduced in #14828. This temporarily causes
all fixes for `round(...)` to be considered unsafe, but they will
eventually be enhanced.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-12-09 16:51:27 +01:00
Dimitri Papadopoulos Orfanos
68eb0a2511 Stop referring to early ruff versions (#14862)
## Summary

Referring to old versions has become more distracting than useful.

## Test Plan

—
2024-12-09 16:47:26 +01:00
InSync
0f4350e10e Fix a typo in class.rs (#14877)
(Accidentally introduced in #14801.)
2024-12-09 15:36:42 +00:00
InSync
aa6b812a73 [flake8-pyi] Also remove self and cls's annotation (PYI034) (#14801)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-09 14:59:12 +00:00
Harutaka Kawamura
0e9427255f [pyupgrade] Remove unreachable code in UP015 implementation (#14871) 2024-12-09 14:54:57 +00:00
Harutaka Kawamura
9c3c59aca9 [flake8-bugbear] Skip B028 if warnings.warn is called with *args or **kwargs (#14870) 2024-12-09 14:32:37 +00:00
Harutaka Kawamura
172143ae77 [flake8-bugbear] Fix B028 to allow stacklevel to be explicitly assigned as a positional argument (#14868) 2024-12-09 13:15:43 +00:00
InSync
3865fb6641 [red-knot] Understanding type[Union[A, B]] (#14858)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-09 12:47:14 +00:00
Micha Reiser
cf260aef2b Upgrade to react 19 (#14864)
## Summary

Upgrades to React 19. Closes
https://github.com/astral-sh/ruff/issues/14859

## Test Plan

I ran the playground locally and clicked through the different panels. I
didn't see any warning or error.
2024-12-09 10:15:38 +00:00
Dimitri Papadopoulos Orfanos
59145098d6 Fix typos found by codespell (#14863)
## Summary

Just fix typos.

## Test Plan

CI tests.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-09 09:32:12 +00:00
Harutaka Kawamura
3d9ac535e9 Fix pytest-parametrize-names-wrong-type (PT006) to edit both argnames and argvalues if both of them are single-element tuples/lists (#14699)
## Summary

Close #11243. Fix `pytest-parametrize-names-wrong-type (PT006)` to edit
both `argnames` and `argvalues` if both of them are single-element
tuples/lists.

```python
# Before fix
@pytest.mark.parametrize(("x",), [(1,), (2,)])
def test_foo(x):
    ...

# After fix:
@pytest.mark.parametrize("x", [1, 2])
def test_foo(x):
    ...
```

## Test Plan

New test cases
2024-12-09 09:58:52 +01:00
InSync
8df4983057 Promote uv in installation guides (#14056)
> [Because this is an Astral repository
;)](https://github.com/astral-sh/packse/pull/183)

[Originally
reported](https://discord.com/channels/1039017663004942429/1039017663512449056/1302319421204729906)
by clearfram3 on Discord.

`grep`ping for `pip install` in `.md` files reveals a few other places
where the same fix might be applicable.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-09 08:25:18 +00:00
renovate[bot]
56a631a868 Update pre-commit hook python-jsonschema/check-jsonschema to v0.30.0 (#14855)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[python-jsonschema/check-jsonschema](https://redirect.github.com/python-jsonschema/check-jsonschema)
| repository | minor | `0.29.4` -> `0.30.0` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>python-jsonschema/check-jsonschema
(python-jsonschema/check-jsonschema)</summary>

###
[`v0.30.0`](https://redirect.github.com/python-jsonschema/check-jsonschema/blob/HEAD/CHANGELOG.rst#0300)

[Compare
Source](https://redirect.github.com/python-jsonschema/check-jsonschema/compare/0.29.4...0.30.0)

- Update vendored schemas: azure-pipelines, bitbucket-pipelines,
buildkite,
circle-ci, cloudbuild, dependabot, github-workflows, gitlab-ci, mergify,
    readthedocs, renovate, taskfile, woodpecker-ci (2024-11-29)
- Fix caching behavior to always use URL hashes as cache keys. This
fixes a
cache confusion bug in which the wrong schema could be retrieved from
the
cache. This resolves :cve:`2024-53848`. Thanks :user:`sethmlarson` for
reporting!
- Deprecate the `--cache-filename` flag. It no longer has any effect and
will
    be removed in a future release.

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 11:04:59 +05:30
Dylan
9d641fa714 [pylint] Include parentheses and multiple comparators in check for boolean-chained-comparison (PLR1716) (#14781)
This PR introduces three changes to the diagnostic and fix behavior
(still under preview) for [boolean-chained-comparison
(PLR1716)](https://docs.astral.sh/ruff/rules/boolean-chained-comparison/#boolean-chained-comparison-plr1716).

1. We now offer a _fix_ in the case of parenthesized expressions like
`(a < b) and b < c`. The fix will merge the chains of comparisons and
then balance parentheses by _adding_ parentheses to one side of the
expression.
2. We now trigger a diagnostic (and fix) in the case where some
comparisons have multiple comparators like `a < b < c and c < d`.
3. When adjacent comparators are parenthesized, we prefer the left
parenthesization and apply the replacement to the whole parenthesized
range. So, for example, `a < (b) and ((b)) < c` becomes `a < (b) < c`.

While these seem like somewhat disconnected changes, they are actually
related. If we only offered (1), then we would see the following fix
behavior:

```diff
- (a < b) and b < c and ((c < d))
+ (a < b < c) and ((c < d))
```

This is because the fix which add parentheses to the first pair of
comparisons overlaps with the fix that removes the `and` between the
second two comparisons. So the latter fix is deferred. However, the
latter fix does not get a second chance because, upon the next lint
iteration, there is no violation of `PLR1716`.

Upon adopting (2), however, both fixes occur by the time ruff completes
several iterations and we get:

```diff
- (a < b) and b < c and ((c < d))
+ ((a < b < c < d))
```

Finally, (3) fixes a previously unobserved bug wherein the autofix for
`a < (b) and b < c` used to result in `a<(b<c` which gives a syntax
error. It could in theory have been fixed in a separate PR, but seems to
be on theme here.


----------

- Closes #13524
- (1), (2), and (3) are implemented in separate commits for ease of
review and modification.
- Technically a user can trigger an error in ruff (by reaching max
iterations) if they have a humongous boolean chained comparison with
differing parentheses levels.
2024-12-08 22:58:45 -06:00
renovate[bot]
b56b3c813c Update NPM Development dependencies (#14854) 2024-12-08 20:58:30 -05:00
renovate[bot]
c59d54370b Update Rust crate tracing-indicatif to v0.3.8 (#14850) 2024-12-08 20:58:24 -05:00
renovate[bot]
dfaf2de81d Update Rust crate clap to v4.5.23 (#14846) 2024-12-08 20:58:14 -05:00
Dmitry Shachnev
d53e5cd25a [flake8-commas]: Fix example replacement in docs (#14843)
## Summary

Minor change for the documentation of COM818 rule. This was a block
called “In the event that a tuple is intended”, but the suggested change
did not produce a tuple.

## Test Plan

```python
>>> import json
>>> (json.dumps({"bar": 1}),)  # this is a tuple
('{"bar": 1}',)
>>> (json.dumps({"bar": 1}))  # not a tuple
'{"bar": 1}'
```
2024-12-09 00:51:59 +00:00
renovate[bot]
ed6de39725 Update Rust crate pep440_rs to v0.7.3 (#14848)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [pep440_rs](https://redirect.github.com/konstin/pep440-rs) |
workspace.dependencies | patch | `0.7.2` -> `0.7.3` |

---

### Release Notes

<details>
<summary>konstin/pep440-rs (pep440_rs)</summary>

###
[`v0.7.3`](https://redirect.github.com/konstin/pep440-rs/blob/HEAD/Changelog.md#073)

[Compare
Source](https://redirect.github.com/konstin/pep440-rs/compare/v0.7.2...v0.7.3)

-   Use once_cell to lower MSRV

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:48:52 +00:00
renovate[bot]
f4a7da7e93 Update Rust crate dir-test to v0.4.1 (#14847)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [dir-test](https://redirect.github.com/fe-lang/dir-test) |
workspace.dependencies | patch | `0.4.0` -> `0.4.1` |

---

### Release Notes

<details>
<summary>fe-lang/dir-test (dir-test)</summary>

###
[`v0.4.1`](https://redirect.github.com/fe-lang/dir-test/releases/tag/v0.4.1)

[Compare
Source](https://redirect.github.com/fe-lang/dir-test/compare/v0.4.0...v0.4.1)

#### What's Changed

- include license texts in published crates by
[@&#8203;decathorpe](https://redirect.github.com/decathorpe) in
[https://github.com/fe-lang/dir-test/pull/12](https://redirect.github.com/fe-lang/dir-test/pull/12)

**Full Changelog**:
https://github.com/fe-lang/dir-test/compare/v0.4.0...v0.4.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:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:48:06 +00:00
renovate[bot]
8bf04988fb Update Rust crate thiserror to v2.0.6 (#14849)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [thiserror](https://redirect.github.com/dtolnay/thiserror) |
workspace.dependencies | patch | `2.0.3` -> `2.0.6` |

---

### Release Notes

<details>
<summary>dtolnay/thiserror (thiserror)</summary>

###
[`v2.0.6`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.6)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.5...2.0.6)

- Suppress deprecation warning on generated From impls
([#&#8203;396](https://redirect.github.com/dtolnay/thiserror/issues/396))

###
[`v2.0.5`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.5)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.4...2.0.5)

- Prevent deprecation warning on generated impl for deprecated type
([#&#8203;394](https://redirect.github.com/dtolnay/thiserror/issues/394))

###
[`v2.0.4`](https://redirect.github.com/dtolnay/thiserror/releases/tag/2.0.4)

[Compare
Source](https://redirect.github.com/dtolnay/thiserror/compare/2.0.3...2.0.4)

- Eliminate needless_lifetimes clippy lint in generated `From` impls
([#&#8203;391](https://redirect.github.com/dtolnay/thiserror/issues/391),
thanks [@&#8203;matt-phylum](https://redirect.github.com/matt-phylum))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:47:09 +00:00
renovate[bot]
745a4b425e Update dependency ruff to v0.8.2 (#14851)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Preview features

- \[`airflow`] Avoid deprecated values (`AIR302`)
([#&#8203;14582](https://redirect.github.com/astral-sh/ruff/pull/14582))
- \[`airflow`] Extend removed names for `AIR302`
([#&#8203;14734](https://redirect.github.com/astral-sh/ruff/pull/14734))
- \[`ruff`] Extend `unnecessary-regular-expression` to non-literal
strings (`RUF055`)
([#&#8203;14679](https://redirect.github.com/astral-sh/ruff/pull/14679))
- \[`ruff`] Implement `used-dummy-variable` (`RUF052`)
([#&#8203;14611](https://redirect.github.com/astral-sh/ruff/pull/14611))
- \[`ruff`] Implement `unnecessary-cast-to-int` (`RUF046`)
([#&#8203;14697](https://redirect.github.com/astral-sh/ruff/pull/14697))

##### Rule changes

- \[`airflow`] Check `AIR001` from builtin or providers `operators`
module
([#&#8203;14631](https://redirect.github.com/astral-sh/ruff/pull/14631))
- \[`flake8-pytest-style`] Remove `@` in `pytest.mark.parametrize` rule
messages
([#&#8203;14770](https://redirect.github.com/astral-sh/ruff/pull/14770))
- \[`pandas-vet`] Skip rules if the `panda` module hasn't been seen
([#&#8203;14671](https://redirect.github.com/astral-sh/ruff/pull/14671))
- \[`pylint`] Fix false negatives for `ascii` and `sorted` in
`len-as-condition` (`PLC1802`)
([#&#8203;14692](https://redirect.github.com/astral-sh/ruff/pull/14692))
- \[`refurb`] Guard `hashlib` imports and mark `hashlib-digest-hex` fix
as safe (`FURB181`)
([#&#8203;14694](https://redirect.github.com/astral-sh/ruff/pull/14694))

##### Configuration

- \[`flake8-import-conventions`] Improve syntax check for aliases
supplied in configuration for `unconventional-import-alias` (`ICN001`)
([#&#8203;14745](https://redirect.github.com/astral-sh/ruff/pull/14745))

##### Bug fixes

- Revert: \[pyflakes] Avoid false positives in `@no_type_check` contexts
(`F821`, `F722`)
([#&#8203;14615](https://redirect.github.com/astral-sh/ruff/issues/14615))
([#&#8203;14726](https://redirect.github.com/astral-sh/ruff/pull/14726))
- \[`pep8-naming`] Avoid false positive for `class Bar(type(foo))`
(`N804`)
([#&#8203;14683](https://redirect.github.com/astral-sh/ruff/pull/14683))
- \[`pycodestyle`] Handle f-strings properly for
`invalid-escape-sequence` (`W605`)
([#&#8203;14748](https://redirect.github.com/astral-sh/ruff/pull/14748))
- \[`pylint`] Ignore `@overload` in `PLR0904`
([#&#8203;14730](https://redirect.github.com/astral-sh/ruff/pull/14730))
- \[`refurb`] Handle non-finite decimals in
`verbose-decimal-constructor` (`FURB157`)
([#&#8203;14596](https://redirect.github.com/astral-sh/ruff/pull/14596))
- \[`ruff`] Avoid emitting `assignment-in-assert` when all references to
the assigned variable are themselves inside `assert`s (`RUF018`)
([#&#8203;14661](https://redirect.github.com/astral-sh/ruff/pull/14661))

##### Documentation

- Improve docs for `flake8-use-pathlib` rules
([#&#8203;14741](https://redirect.github.com/astral-sh/ruff/pull/14741))
- Improve error messages and docs for `flake8-comprehensions` rules
([#&#8203;14729](https://redirect.github.com/astral-sh/ruff/pull/14729))
- \[`flake8-type-checking`] Expands `TC006` docs to better explain
itself
([#&#8203;14749](https://redirect.github.com/astral-sh/ruff/pull/14749))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:46:20 +00:00
renovate[bot]
dfab134bb6 Update Rust crate ureq to v2.12.1 (#14853)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ureq](https://redirect.github.com/algesten/ureq) |
workspace.dependencies | minor | `2.11.0` -> `2.12.1` |

---

### Release Notes

<details>
<summary>algesten/ureq (ureq)</summary>

###
[`v2.12.1`](https://redirect.github.com/algesten/ureq/blob/HEAD/CHANGELOG.md#2121)

[Compare
Source](https://redirect.github.com/algesten/ureq/compare/2.12.0...2.12.1)

- Do not use multi-version deps (>=x.x.x)
([#&#8203;907](https://redirect.github.com/algesten/ureq/issues/907))

###
[`v2.12.0`](https://redirect.github.com/algesten/ureq/blob/HEAD/CHANGELOG.md#2120)

[Compare
Source](https://redirect.github.com/algesten/ureq/compare/2.11.0...2.12.0)

- Bump MSRV 1.67 -> 1.71 because rustls will soon adopt it
([#&#8203;905](https://redirect.github.com/algesten/ureq/issues/905))
- Unpin rustls dep (>=0.23.19)
([#&#8203;905](https://redirect.github.com/algesten/ureq/issues/905))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:46:05 +00:00
Alex Waygood
58e7db89a1 Run zizmor in CI, and fix most warnings (#14844)
## Summary

A [recent exploit](https://github.com/advisories/GHSA-7x29-qqmq-v6qc)
brought attention to how easy it can be for attackers to use template
expansion in GitHub Actions workflows to inject arbitrary code into a
repository. That vulnerability [would have been caught by the zizmor
linter](https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-injection),
which looks for potential security vulnerabilities in GitHub Actions
workflows. This PR adds [zizmor](https://github.com/woodruffw/zizmor) as
a pre-commit hook and fixes the high- and medium-severity warnings
flagged by the tool.

All the warnings fixed in this PR are related to this zizmor check:
https://woodruffw.github.io/zizmor/audits/#artipacked. The summary of
the check is that `actions/checkout` will by default persist git
configuration for the duration of the workflow, which can be insecure.
It's unnecessary unless you actually need to do things with `git` later
on in the workflow. None of our workflows do except for
`publish-docs.yml` and `sync-typeshed.yml`, so I set
`persist-credentials: true` for those two but `persist-credentials:
false` for all other uses of `actions/checkout`.

Unfortunately there are several warnings in `release.yml`, including
four high-severity warnings. However, this is a generated workflow file,
so I have deliberately excluded this file from the check. These are the
findings in `release.yml`:

<details>
<summary>release.yml findings</summary>

```
warning[artipacked]: credential persistence through GitHub Actions artifacts
  --> /Users/alexw/dev/ruff/.github/workflows/release.yml:62:9
   |
62 |         - uses: actions/checkout@v4
   |  _________-
63 | |         with:
64 | |           submodules: recursive
   | |_______________________________- does not set persist-credentials: false
   |
   = note: audit confidence → Low

warning[artipacked]: credential persistence through GitHub Actions artifacts
   --> /Users/alexw/dev/ruff/.github/workflows/release.yml:124:9
    |
124 |         - uses: actions/checkout@v4
    |  _________-
125 | |         with:
126 | |           submodules: recursive
    | |_______________________________- does not set persist-credentials: false
    |
    = note: audit confidence → Low

warning[artipacked]: credential persistence through GitHub Actions artifacts
   --> /Users/alexw/dev/ruff/.github/workflows/release.yml:174:9
    |
174 |         - uses: actions/checkout@v4
    |  _________-
175 | |         with:
176 | |           submodules: recursive
    | |_______________________________- does not set persist-credentials: false
    |
    = note: audit confidence → Low

warning[artipacked]: credential persistence through GitHub Actions artifacts
   --> /Users/alexw/dev/ruff/.github/workflows/release.yml:249:9
    |
249 |         - uses: actions/checkout@v4
    |  _________-
250 | |         with:
251 | |           submodules: recursive
252 | |       # Create a GitHub Release while uploading all files to it
    | |_______________________________________________________________- does not set persist-credentials: false
    |
    = note: audit confidence → Low

error[excessive-permissions]: overly broad workflow or job-level permissions
  --> /Users/alexw/dev/ruff/.github/workflows/release.yml:17:1
   |
17 | / permissions:
18 | |   "contents": "write"
...  |
39 | | # If there's a prerelease-style suffix to the version, then the release(s)
40 | | # will be marked as a prerelease.
   | |_________________________________^ contents: write is overly broad at the workflow level
   |
   = note: audit confidence → High

error[template-injection]: code injection via template expansion
  --> /Users/alexw/dev/ruff/.github/workflows/release.yml:80:9
   |
80 |          - id: plan
   |   _________^
81 |  |         run: |
   |  |_________^
82 | ||           dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --out...
83 | ||           echo "dist ran successfully"
84 | ||           cat plan-dist-manifest.json
85 | ||           echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
   | ||__________________________________________________________________________________^ this step
   | ||__________________________________________________________________________________^ inputs.tag may expand into attacker-controllable code
   |
   = note: audit confidence → Low

error[template-injection]: code injection via template expansion
  --> /Users/alexw/dev/ruff/.github/workflows/release.yml:80:9
   |
80 |          - id: plan
   |   _________^
81 |  |         run: |
   |  |_________^
82 | ||           dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --out...
83 | ||           echo "dist ran successfully"
84 | ||           cat plan-dist-manifest.json
85 | ||           echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
   | ||__________________________________________________________________________________^ this step
   | ||__________________________________________________________________________________^ inputs.tag may expand into attacker-controllable code
   |
   = note: audit confidence → Low

error[template-injection]: code injection via template expansion
  --> /Users/alexw/dev/ruff/.github/workflows/release.yml:80:9
   |
80 |          - id: plan
   |   _________^
81 |  |         run: |
   |  |_________^
82 | ||           dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --out...
83 | ||           echo "dist ran successfully"
84 | ||           cat plan-dist-manifest.json
85 | ||           echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
   | ||__________________________________________________________________________________^ this step
   | ||__________________________________________________________________________________^ inputs.tag may expand into attacker-controllable code
   |
   = note: audit confidence → Low
```

</details>

## Test Plan

`uvx pre-commit run -a`
2024-12-09 00:42:06 +00:00
renovate[bot]
8c4b22964e Update pre-commit dependencies (#14852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:13:17 +00:00
renovate[bot]
ecd948a083 Update Rust crate anyhow to v1.0.94 (#14845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 00:12:06 +00:00
Alex Waygood
9b8ceb9a2e [pyupgrade] Mark fixes for convert-typed-dict-functional-to-class and convert-named-tuple-functional-to-class as unsafe if they will remove comments (UP013, UP014) (#14842) 2024-12-08 18:51:37 +00:00
Thibaut Decombe
8d9e408dbb Fix PLW1508 false positive for default string created via a mult operation (#14841) 2024-12-08 18:25:47 +00:00
ABDULRAHMAN ALRAHMA
85402097fc Improve error messages for except* (B025, B029, B030, B904) #14791 (#14815)
Improves error message for [except*](https://peps.python.org/pep-0654/)
(Rules: B025, B029, B030, B904)

Example python snippet:
```python
try:
    a = 1
except* ValueError:
    a = 2
except* ValueError:
    a = 2

try:
    pass
except* ():
    pass

try:
    pass
except* 1:  # error
    pass

try:
    raise ValueError
except* ValueError:
    raise UserWarning
```
Error messages
Before:
```
$ ruff check --select=B foo.py
foo.py:6:9: B025 try-except block with duplicate exception `ValueError`
foo.py:11:1: B029 Using `except ():` with an empty tuple does not catch anything; add exceptions to handle
foo.py:16:9: B030 `except` handlers should only be exception classes or tuples of exception classes
foo.py:22:5: B904 Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
Found 4 errors.
```
After:
```
$ ruff check --select=B foo.py
foo.py:6:9: B025 try-except* block with duplicate exception `ValueError`
foo.py:11:1: B029 Using `except* ():` with an empty tuple does not catch anything; add exceptions to handle
foo.py:16:9: B030 `except*` handlers should only be exception classes or tuples of exception classes
foo.py:22:5: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
Found 4 errors.
```

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

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-08 17:37:34 +00:00
Shaygan Hooshyari
269e47be96 Understand type[A | B] special form in annotations (#14830)
resolves https://github.com/astral-sh/ruff/issues/14703

I decided to use recursion to get the type, so if anything is added to
the single element inference it will be applied for the union.
Also added this
[change](https://github.com/astral-sh/ruff/issues/14703#issuecomment-2510286217)
in this PR since it was easy.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-07 17:34:50 +00:00
Dylan
d34013425f [flake8-bugbear] Offer unsafe autofix for no-explicit-stacklevel (B028) (#14829)
This PR introduces an unsafe autofix for [no-explicit-stacklevel
(B028)](https://docs.astral.sh/ruff/rules/no-explicit-stacklevel/#no-explicit-stacklevel-b028):
we add the `stacklevel` argument, set to `2`.

Closes #14805
2024-12-07 08:24:37 -05:00
Dylan
2c13e6513d [flake8-comprehensions] Skip iterables with named expressions in unnecessary-map (C417) (#14827)
This PR modifies [unnecessary-map
(C417)](https://docs.astral.sh/ruff/rules/unnecessary-map/#unnecessary-map-c417)
to skip `map` expressions if the iterable contains a named expression,
since those cannot appear in comprehensions.

Closes #14808
2024-12-06 22:00:33 -05:00
Douglas Creager
8fdd88013d Support type[a.X] with qualified class names (#14825)
This adds support for `type[a.X]`, where the `type` special form is
applied to a qualified name that resolves to a class literal. This works
for both nested classes and classes imported from another module.

Closes #14545
2024-12-06 17:14:51 -05:00
Carl Meyer
3017b3b687 [red-knot] function parameter types (#14802)
## Summary

Inferred and declared types for function parameters, in the function
body scope.

Fixes #13693.

## Test Plan

Added mdtests.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-06 12:55:56 -08:00
Alex Waygood
2119dcab6f [ruff] Teach autofix for used-dummy-variable about TypeVars etc. (RUF052) (#14819) 2024-12-06 17:05:50 +00:00
Wei Lee
3ea14d7a74 [airflow]: extend removed args (AIR302) (#14765)
## Summary

Airflow 3.0 removes various deprecated functions, members, modules, and
other values. They have been deprecated in 2.x, but the removal causes
incompatibilities that we want to detect. This PR deprecates the
following names.

* in `DAG`
    * `sla_miss_callback` was removed
* in `airflow.operators.trigger_dagrun.TriggerDagRunOperator`
    * `execution_date` was removed
* in `airflow.operators.weekday.DayOfWeekSensor`,
`airflow.operators.datetime.BranchDateTimeOperator` and
`airflow.operators.weekday.BranchDayOfWeekOperator`
* `use_task_execution_day` was removed in favor of
`use_task_logical_date`

The full list of rules we will extend
https://github.com/apache/airflow/issues/44556

## Test Plan

<!-- How was it tested? -->
A test fixture is included in the PR.
2024-12-06 17:00:23 +01:00
Alex Waygood
4fdd4ddfaa [ruff] Don't emit used-dummy-variable on function parameters (RUF052) (#14818) 2024-12-06 14:54:42 +00:00
David Peter
6b9f3d7d7c [red-knot] Import LiteralString/Never from typing_extensions (#14817)
## Summary

`typing.Never` and `typing.LiteralString` are only conditionally
exported from `typing` for Python versions 3.11 and later. We run the
Markdown tests with the default Python version of 3.9, so here we change
the import to `typing_extensions` instead, and add a new test to make
sure we'll continue to understand the `typing`-version of these symbols
for newer versions.

This didn't cause problems so far, as we don't understand
`sys.version_info` branches yet.

## Test Plan

New Markdown tests to make sure this will continue to work in the
future.
2024-12-06 13:57:51 +01:00
Alex Waygood
4cb8392523 Further simplifications to PTH210 (#14816) 2024-12-06 12:52:26 +00:00
Alex Waygood
9ee438b02f Minor nitpicks for PTH210 (#14814) 2024-12-06 12:23:21 +00:00
InSync
89368a62a8 [flake8-use-pathlib] Dotless suffix passed to Path.with_suffix() (PTH901) (#14779)
## Summary

Resolves #14441.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-12-06 13:08:20 +01:00
Micha Reiser
1559c73fcd Fix fstring formatting removing overlong implicit concatenated string in expression part (#14811)
## Summary

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


The formatter incorrectly removed the inner implicitly concatenated
string for following single-line f-string:

```py
f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'a' if True else ""}"

# formatted
f"{ if True else ''}"
```

This happened because I changed the `RemoveSoftlinesBuffer` in
https://github.com/astral-sh/ruff/pull/14489 to remove any content
wrapped in `if_group_breaks`. After all, it emulates an *all flat*
layout. This works fine when `if_group_breaks` is only used to **add**
content if the gorup breaks. It doesn't work if the same content is
rendered differently depending on if the group fits using
`if_group_breaks` and `if_groups_fits` because the enclosing `group`
might still *break* if the entire content exceeds the line-length limit.

This PR fixes this by unwrapping any `if_group_fits` content by removing
the `if_group_fits` start and end tags.


## Test Plan

added test
2024-12-06 13:01:04 +01:00
Wei Lee
39623f8d40 [airflow]: extend removed names (AIR302) (#14804)
## Summary

Airflow 3.0 removes various deprecated functions, members, modules, and
other values. They have been deprecated in 2.x, but the removal causes
incompatibilities that we want to detect. This PR deprecates the
following names.
The full list of rules we will extend
https://github.com/apache/airflow/issues/44556


#### package
* `airflow.contrib.*` 

#### module
* `airflow.operators.subdag.*` 

#### class
* `airflow.sensors.external_task.ExternalTaskSensorLink` →
`airflow.sensors.external_task.ExternalDagLin`
* `airflow.operators.bash_operator.BashOperator` →
`airflow.operators.bash.BashOperator`
* `airflow.operators.branch_operator.BaseBranchOperator` →
`airflow.operators.branch.BaseBranchOperator`
* `airflow.operators.dummy.EmptyOperator` →
`airflow.operators.empty.EmptyOperator`
* `airflow.operators.dummy.DummyOperator` →
`airflow.operators.empty.EmptyOperator`
* `airflow.operators.dummy_operator.EmptyOperator` →
`airflow.operators.empty.EmptyOperator`
* `airflow.operators.dummy_operator.DummyOperator` →
`airflow.operators.empty.EmptyOperator`
* `airflow.operators.email_operator.EmailOperator` →
`airflow.operators.email.EmailOperator`
* `airflow.sensors.base_sensor_operator.BaseSensorOperator` →
`airflow.sensors.base.BaseSensorOperator`
* `airflow.sensors.date_time_sensor.DateTimeSensor` →
`airflow.sensors.date_time.DateTimeSensor`
* `airflow.sensors.external_task_sensor.ExternalTaskMarker` →
`airflow.sensors.external_task.ExternalTaskMarker`
* `airflow.sensors.external_task_sensor.ExternalTaskSensor` →
`airflow.sensors.external_task.ExternalTaskSensor`
* `airflow.sensors.external_task_sensor.ExternalTaskSensorLink` →
`airflow.sensors.external_task.ExternalTaskSensorLink`
* `airflow.sensors.time_delta_sensor.TimeDeltaSensor` →
`airflow.sensors.time_delta.TimeDeltaSensor`

#### function
* `airflow.utils.decorators.apply_defaults`
* `airflow.www.utils.get_sensitive_variables_fields` →
`airflow.utils.log.secrets_masker.get_sensitive_variables_fields`
* `airflow.www.utils.should_hide_value_for_key` →
`airflow.utils.log.secrets_masker.should_hide_value_for_key`
* `airflow.configuration.get` → `airflow.configuration.conf.get` 
* `airflow.configuration.getboolean` →
`airflow.configuration.conf.getboolean`
* `airflow.configuration.getfloat` →
`airflow.configuration.conf.getfloat`
* `airflow.configuration.getint` → `airflow.configuration.conf.getint` 
* `airflow.configuration.has_option` →
`airflow.configuration.conf.has_option`
* `airflow.configuration.remove_option` →
`airflow.configuration.conf.remove_option`
* `airflow.configuration.as_dict` → `airflow.configuration.conf.as_dict`
* `airflow.configuration.set` → `airflow.configuration.conf.set` 
* `airflow.secrets.local_filesystem.load_connections` →
`airflow.secrets.local_filesystem.load_connections_dict`
* `airflow.secrets.local_filesystem.get_connection` →
`airflow.secrets.local_filesystem.load_connections_dict`
* `airflow.utils.helpers.chain` → `airflow.models.baseoperator.chain` 
* `airflow.utils.helpers.cross_downstream` →
`airflow.models.baseoperator.cross_downstream`

#### attribute
* in `airflow.utils.trigger_rule.TriggerRule`
    * `DUMMY` 
    * `NONE_FAILED_OR_SKIPPED` 

#### constant / variable
* `airflow.PY\d\d`
2024-12-06 11:34:48 +01:00
Douglas Creager
918358aaa6 Migrate some inference tests to mdtests (#14795)
As part of #13696, this PR ports a smallish number of inference tests
over to the mdtest framework.
2024-12-06 11:19:22 +01:00
David Peter
b01a651e69 [red-knot] Support for TOML configs in Markdown tests (#14785)
## Summary

This adds support for specifying the target Python version from a
Markdown test. It is a somewhat limited ad-hoc solution, but designed to
be future-compatible. TOML blocks can be added to arbitrary sections in
the Markdown block. They have the following format:

````markdown
```toml
[tool.knot.environment]
target-version = "3.13"
```
````

So far, there is nothing else that can be configured, but it should be
straightforward to extend this to things like a custom typeshed path.

This is in preparation for the statically-known branches feature where
we are going to have to specify the target version for lots of tests.

## Test Plan

- New Markdown test that fails without the explicitly specified
`target-version`.
- Manually tested various error paths when specifying a wrong
`target-version` field.
- Made sure that running tests is as fast as before.
2024-12-06 10:22:08 +01:00
Micha Reiser
56afb12ae7 Fix infinite watch loop by ignoring 'uninteresting' watch events (#14809)
## Summary
Fixes https://github.com/astral-sh/ruff/issues/14807

I suspect that this broke when we updated notify, although I'm not quiet
sure how this *ever* worked...

The problem was that the file watcher didn't skip over `Access` events,
but Ruff itself accesses the `pyproject.toml` when checking the project.
That means, Ruff triggers `Access` events but it also schedules a
re-check on every `Access` event... and this goes one forever.

This PR skips over `Access` and `Other` event. `Access` events are
uninteresting because they're only reads, they don't change any file
metadata or content.
The `Other` events should be rare and are mainly to inform about file
watcher changes... we don't need those.

I also added an explicit handling for the `Rescan` event. File watchers
emit a `Rescan` event if they failed to capture some file watching
changes
and it signals that the program should assume that all files might have
changed (the program should do a rescan to *get up to date*).

## Test Plan

I tested that Ruff no longer loops when running `check --watch`. I
verified that Ruff rechecks file after making content changes.
2024-12-06 08:50:29 +00:00
Maksim Bondarenkov
b42e528555 deps: Update cc (#14794)
<!--
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

in unknown moment older versions became broken for windows-gnullvm
targets. this update shouldn't break anything

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

## Test Plan

successfully built for windows-gnullvm with `cargo build`

<!-- How was it tested? -->
2024-12-05 21:47:14 -05:00
Christian Clauss
5aab57b3e9 docs/integrations.md: Upgrade example to astral-sh/ruff-action@v2 (#14800)
<!--
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

https://docs.astral.sh/ruff/integrations/#github-actions upgraded for
https://github.com/astral-sh/ruff-action/releases

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

## Test Plan

<!-- How was it tested? -->
@eifinger Your review, please.
2024-12-05 21:47:03 -05:00
Dhruv Manilawala
40b0b67dd9 [red-knot] Separate invalid syntax code snippets (#14803)
Ref: https://github.com/astral-sh/ruff/pull/14788#discussion_r1872242283

This PR:
* Separates code snippets as individual tests for the invalid syntax
cases
* Adds a general comment explaining why the parser could emit more
syntax errors than expected
2024-12-06 02:41:33 +00:00
Dylan
1bd8fbb6e8 [flake8-pyi] Skip all type definitions in string-or-bytes-too-long (PYI053) (#14797) 2024-12-05 18:48:54 -06:00
Dhruv Manilawala
b0e26e6fc8 Bump version to 0.8.2 (#14789) 2024-12-05 18:06:35 +05:30
Dhruv Manilawala
e9941cd714 [red-knot] Move standalone expr inference to for non-name target (#14788)
## Summary

Ref: https://github.com/astral-sh/ruff/pull/14754#discussion_r1871040646

## Test Plan

Remove the TODO comment and update the mdtest.
2024-12-05 18:06:20 +05:30
Dhruv Manilawala
43bf1a8907 Add tests for "keyword as identifier" syntax errors (#14754)
## Summary

This is related to #13778, more specifically
https://github.com/astral-sh/ruff/issues/13778#issuecomment-2513556004.

This PR adds various test cases where a keyword is being where an
identifier is expected. The tests are to make sure that red knot doesn't
panic, raises the syntax error and the identifier is added to the symbol
table. The final part allows editor related features like renaming the
symbol.
2024-12-05 17:32:48 +05:30
InSync
fda8b1f884 [ruff] Unnecessary cast to int (RUF046) (#14697)
## Summary

Resolves #11412.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-05 10:30:06 +01:00
David Peter
2d3f557875 [red-knot] Fallback for typing._NoDefaultType (#14783)
## Summary

`typing_extensions` has a `>=3.13` re-export for the `typing.NoDefault`
singleton, but not for `typing._NoDefaultType`. This causes problems as
soon as we understand `sys.version_info` branches, so we explicity
switch to `typing._NoDefaultType` for Python 3.13 and later.

This is a part of #14759 that I thought might make sense to break out
and merge in isolation.

## Test Plan

New test that will become more meaningful with #12700

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-05 09:17:55 +01:00
David Peter
bd27bfab5d [red-knot] Unify setup_db() functions, add TestDb builder (#14777)
## Summary

- Instead of seven (more or less similar) `setup_db` functions, use just
one in a single central place.
- For every test that needs customization beyond that, offer a
`TestDbBuilder` that can control the Python target version, custom
typeshed, and pre-existing files.

The main motivation for this is that we're soon going to need
customization of the Python version, and I didn't feel like adding this
to each of the existing `setup_db` functions.
2024-12-04 21:36:54 +01:00
InSync
155d34bbb9 [red-knot] Infer precise types for len() calls (#14599)
## Summary

Resolves #14598.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-04 11:16:53 -08:00
Well404
04c887c8fc Fix references for async-busy-wait (#14775) 2024-12-04 18:05:49 +01:00
David Peter
af43bd4b0f [red-knot] Gradual forms do not participate in equivalence/subtyping (#14758)
## Summary

This changeset contains various improvements concerning non-fully-static
types and their relationships:

- Make sure that non-fully-static types do not participate in
equivalence or subtyping.
- Clarify what `Type::is_equivalent_to` actually implements.
- Introduce `Type::is_fully_static`
- New tests making sure that multiple `Any`/`Unknown`s inside unions and
intersections are collapsed.

closes #14524

## Test Plan

- Added new unit tests for union and intersection builder
- Added new unit tests for `Type::is_equivalent_to`
- Added new unit tests for `Type::is_subtype_of`
- Added new property test making sure that non-fully-static types do not
participate in subtyping
2024-12-04 17:11:25 +01:00
Harutaka Kawamura
614917769e Remove @ in pytest.mark.parametrize rule messages (#14770) 2024-12-04 16:01:10 +01:00
Douglas Creager
8b23086eac [red-knot] Add typing.Any as a spelling for the Any type (#14742)
We already had a representation for the Any type, which we would use
e.g. for expressions without type annotations. We now recognize
`typing.Any` as a way to refer to this type explicitly. Like other
special forms, this is tracked correctly through aliasing, and isn't
confused with local definitions that happen to have the same name.

Closes #14544
2024-12-04 09:56:36 -05:00
David Peter
948549fcdc [red-knot] Test: Hashable/Sized => A/B (#14769)
## Summary

Minor change that uses two plain classes `A` and `B` instead of
`typing.Sized` and `typing.Hashable`.

The motivation is twofold: I remember that I was confused when I first
saw this test. Was there anything specific to `Sized` and `Hashable`
that was relevant here? (there is, these classes are not overlapping;
and you can build a proper intersection from them; but that's true for
almost all non-builtin classes).

I now ran into another problem while working on #14758: `Sized` and
`Hashable` are protocols that we don't fully understand yet. This
causing some trouble when trying to infer whether these are fully-static
types or not.
2024-12-04 15:00:27 +01:00
David Salvisberg
e67f7f243d [flake8-type-checking] Expands TC006 docs to better explain itself (#14749)
Closes: #14676

I think the consensus generally was to keep the rule as-is, but expand
the docs.

## Summary

Expands the docs for TC006 with an explanation for why the type
expression is always quoted, including mention of another potential
benefit to this style.
2024-12-04 13:16:31 +00:00
Dylan
c617b2a48a [pycodestyle] Handle f-strings properly for invalid-escape-sequence (W605) (#14748)
When fixing an invalid escape sequence in an f-string, each f-string
element is analyzed for valid escape characters prior to creating the
diagnostic and fix. This allows us to safely prefix with `r` to create a
raw string if no valid escape characters were found anywhere in the
f-string, and otherwise insert backslashes.

This fixes a bug in the original implementation: each "f-string part"
was treated separately, so it was not possible to tell whether a valid
escape character was or would be used elsewhere in the f-string.

Progress towards #11491 but format specifiers are not handled in this
PR.
2024-12-04 06:59:14 -06:00
Dhruv Manilawala
1685d95ed2 [red-knot] Add fuzzer to catch panics for invalid syntax (#14678)
## Summary

This PR adds a fuzzer harness for red knot that runs the type checker on
source code that contains invalid syntax.

Additionally, this PR also updates the `init-fuzzer.sh` script to
increase the corpus size to:
* Include various crates that includes Python source code
* Use the 3.13 CPython source code

And, remove any non-Python files from the final corpus so that when the
fuzzer tries to minify the corpus, it doesn't produce files that only
contains documentation content as that's just noise.

## Test Plan

Run `./fuzz/init-fuzzer.sh`, say no to the large dataset.
Run the fuzzer with `cargo +night fuzz run red_knot_check_invalid_syntax
-- -timeout=5`
2024-12-04 14:36:58 +05:30
Dhruv Manilawala
575deb5d4d Check AIR001 from builtin or providers operators module (#14631)
## Summary

This PR makes changes to the `AIR001` rule as per
https://github.com/astral-sh/ruff/pull/14627#discussion_r1860212307.

Additionally,
* Avoid returning the `Diagnostic` and update the checker in the rule
logic for consistency
* Remove test case for different keyword position (I don't think it's
required here)

## Test Plan

Add test cases for multiple operators from various modules.
2024-12-04 13:30:47 +05:30
Wei Lee
edce559431 [airflow]: extend removed names (AIR302) (#14734) 2024-12-03 21:39:43 +00:00
Brent Westbrook
62e358e929 [ruff] Extend unnecessary-regular-expression to non-literal strings (RUF055) (#14679)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-12-03 15:17:20 +00:00
Alex Waygood
81bfcc9899 Minor followups to RUF052 (#14755)
## Summary

Just some minor followups to the recently merged RUF052 rule, that was
added in bf0fd04:
- Some small tweaks to the docs
- A minor code-style nit
- Some more tests for my peace of mind, just to check that the new
methods on the semantic model are working correctly

I'm adding the "internal" label as this doesn't deserve a changelog
entry. RUF052 is a new rule that hasn't been released yet.

## Test Plan

`cargo test -p ruff_linter`
2024-12-03 13:33:29 +00:00
David Peter
74309008fd [red-knot] Property tests (#14178)
## Summary

This PR adds a new `property_tests` module with quickcheck-based tests
that verify certain properties of types. The following properties are
currently checked:

* `is_equivalent_to`:
  * is reflexive: `T` is equivalent to itself
* `is_subtype_of`:
  * is reflexive: `T` is a subtype of `T`
* is antisymmetric: if `S <: T` and `T <: S`, then `S` is equivalent to
`T`
  * is transitive: `S <: T` & `T <: U` => `S <: U`
* `is_disjoint_from`:
  * is irreflexive: `T` is not disjoint from `T`
  * is symmetric: `S` disjoint from `T` => `T` disjoint from `S`
* `is_assignable_to`:
  * is reflexive
* `negate`:
  * is an involution: `T.negate().negate()` is equivalent to `T`

There are also some tests that validate higher-level properties like:

* `S <: T` implies that `S` is not disjoint from `T`
* `S <: T` implies that `S` is assignable to `T`
* A singleton type must also be single-valued

These tests found a few bugs so far:

- #14177 
- #14195 
- #14196 
- #14210
- #14731

Some additional notes:

- Quickcheck-based property tests are non-deterministic and finding
counter-examples might take an arbitrary long time. This makes them bad
candidates for running in CI (for every PR). We can think of running
them in a cron-job way from time to time, similar to fuzzing. But for
now, it's only possible to run them locally (see instructions in source
code).
- Some tests currently find false positive "counterexamples" because our
understanding of equivalence of types is not yet complete. We do not
understand that `int | str` is the same as `str | int`, for example.
These tests are in a separate `property_tests::flaky` module.
- Properties can not be formulated in every way possible, due to the
fact that `is_disjoint_from` and `is_subtype_of` can produce false
negative answers.
- The current shrinking implementation is very naive, which leads to
counterexamples that are very long (`str & Any & ~tuple[Any] &
~tuple[Unknown] & ~Literal[""] & ~Literal["a"] | str & int & ~tuple[Any]
& ~tuple[Unknown]`), requiring the developer to simplify manually. It
has not been a major issue so far, but there is a comment in the code
how this can be improved.
- The tests are currently implemented using a macro. This is a single
commit on top which can easily be reverted, if we prefer the plain code
instead. With the macro:
  ```rs
  // `S <: T` implies that `S` can be assigned to `T`.
  type_property_test!(
      subtype_of_implies_assignable_to, db,
forall types s, t. s.is_subtype_of(db, t) => s.is_assignable_to(db, t)
  );
  ```
  without the macro:
  ```rs
  /// `S <: T` implies that `S` can be assigned to `T`.
  #[quickcheck]
  fn subtype_of_implies_assignable_to(s: Ty, t: Ty) -> bool {
      let db = get_cached_db();
  
      let s = s.into_type(&db);
      let t = t.into_type(&db);
  
      !s.is_subtype_of(&*db, t) || s.is_assignable_to(&*db, t)
  }
  ```

## Test Plan

```bash
while cargo test --release -p red_knot_python_semantic --features property_tests types::property_tests; do :; done
```
2024-12-03 13:54:54 +01:00
David Peter
a255d79087 [red-knot] is_subtype_of fix for KnownInstance types (#14750)
## Summary

`KnownInstance::instance_fallback` may return instances of supertypes.
For example, it returns an instance of `_SpecialForm` for `Literal`.
This means it can't be used on the right-hand side of `is_subtype_of`
relationships, because it might lead to false positives.

I can lead to false negatives on the left hand side of `is_subtype_of`,
but this is at least a known limitation. False negatives are fine for
most applications, but false positives can lead to wrong results in
intersection-simplification, for example.

closes #14731

## Test Plan

Added regression test
2024-12-03 12:03:26 +01:00
Alex Waygood
70bd10614f Improve docs for flake8-use-pathlib rules (#14741)
Flag the perf impact more clearly, add more links, clarify the rule
about the glob module
2024-12-03 07:47:31 +00:00
Lokejoke
bf0fd04e4e [ruff] Implemented used-dummy-variable (RUF052) (#14611)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-12-03 08:36:16 +01:00
David Peter
a69dfd4a74 [red-knot] Simplify tuples containing Never (#14744)
## Summary

Simplify tuples containing `Never` to `Never`:

```py
from typing import Never

def never() -> Never: ...

reveal_type((1, never(), "foo"))  # revealed: Never
```

I should note that mypy and pyright do *not* perform this
simplification. I don't know why.


There is [only one
place](5137fcc9c8/crates/red_knot_python_semantic/src/types/infer.rs (L1477-L1484))
where we use `TupleType::new` directly (instead of `Type::tuple`, which
changes behavior here). This appears when creating `TypeVar`
constraints, and it looks to me like it should stay this way, because
we're using `TupleType` to store a list of constraints there, instead of
an actual type. We also store `tuple[constraint1, constraint2, …]` as
the type for the `constraint1, constraint2, …` tuple expression. This
would mean that we infer a type of `tuple[str, Never]` for the following
type variable constraints, without simplifying it to `Never`. This seems
like a weird edge case that's maybe not worth looking further into?!
```py
from typing import Never

#         vvvvvvvvvv
def f[T: (str, Never)](x: T):
    pass
```

## Test Plan

- Added a new unit test. Did not add additional Markdown tests as that
seems superfluous.
- Tested the example above using red knot, mypy, pyright.
- Verified that this allows us to remove `contains_never` from the
property tests
(https://github.com/astral-sh/ruff/pull/14178#discussion_r1866473192)
2024-12-03 08:28:36 +01:00
Micha Reiser
c2e17d0399 Possible fix for flaky file watching test (#14543) 2024-12-03 08:22:42 +01:00
Dylan
10fef8bd5d [flake8-import-conventions] Improve syntax check for aliases supplied in configuration for unconventional-import-alias (ICN001) (#14745)
This PR improves on #14477 by:

- Ensuring user's do not require the module alias "__debug__", which is unassignable
- Validating the linter settings for
`lint.flake8-import-conventions.extend-aliases` (whereas previously we
only did this for `lint.flake8-import-conventions.aliases`).

Closes #14662
2024-12-02 22:41:47 -06:00
InSync
246a6df87d [red-knot] Deeper understanding of LiteralString (#14649)
## Summary

Resolves #14648.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-12-03 03:31:58 +00:00
Connor Skees
3e702e12f7 red-knot: support narrowing for bool(E) (#14668)
Resolves https://github.com/astral-sh/ruff/issues/14547 by delegating
narrowing to `E` for `bool(E)` where `E` is some expression.

This change does not include other builtin class constructors which
should also work in this position, like `int(..)` or `float(..)`, as the
original issue does not mention these. It should be easy enough to add
checks for these as well if we want to.

I don't see a lot of markdown tests for malformed input, maybe there's a
better place for the no args and too many args cases to go?

I did see after the fact that it looks like this task was intended for a
new hire.. my apologies. I got here from
https://github.com/astral-sh/ruff/issues/13694, which is marked
help-wanted.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2024-12-03 03:04:59 +00:00
Dylan
91e2d9a139 [refurb] Handle non-finite decimals in verbose-decimal-constructor (FURB157) (#14596)
This PR extends the Decimal parsing used in [verbose-decimal-constructor
(FURB157)](https://docs.astral.sh/ruff/rules/verbose-decimal-constructor/)
to better handle non-finite `Decimal` objects, avoiding some false
negatives.

Closes #14587

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-12-02 18:13:20 -06:00
David Peter
5137fcc9c8 [red-knot] Re-enable linter corpus tests (#14736)
## Summary

Seeing the fuzzing results from @dhruvmanila in #13778, I think we can
re-enable these tests. We also had one regression that would have been
caught by these tests, so there is some value in having them enabled.
2024-12-02 20:11:30 +01:00
Matt Ord
83651deac7 [pylint] Ignore overload in PLR0904 (#14730)
Fixes #14727

## Summary

Fixes #14727

## Test Plan

cargo test

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-12-02 14:36:51 +00:00
Alex Waygood
6dfe125f44 Improve error messages and docs for flake8-comprehensions rules (#14729) 2024-12-02 13:36:15 +00:00
Micha Reiser
f96dfc179f Revert: [pyflakes] Avoid false positives in @no_type_check contexts (F821, F722) (#14615) (#14726) 2024-12-02 14:28:27 +01:00
Tzu-ping Chung
76d2e56501 [airflow] Avoid deprecated values (AIR302) (#14582) 2024-12-02 07:39:26 +00:00
Micha Reiser
30d80d9746 Sort discovered workspace packages for consistent cross-platform package discovery (#14725) 2024-12-02 07:36:08 +00:00
renovate[bot]
5a67d3269b Update pre-commit dependencies (#14719) 2024-12-02 06:02:56 +00:00
renovate[bot]
02d1e6a94a Update dawidd6/action-download-artifact action to v7 (#14722) 2024-12-02 01:25:51 +00:00
Simon Brugman
48ec3a8add [refurb] Guard hashlib imports and mark hashlib-digest-hex fix as safe (FURB181) (#14694)
## Summary

- Check if `hashlib` and `crypt` imports have been seen for `FURB181`
and `S324`
- Mark the fix for `FURB181` as safe: I think it was accidentally marked
as unsafe in the first place. The rule does not support user-defined
classes as the "fix safety" section suggests.
- Removed `hashlib._Hash`, as it's not part of the `hashlib` module.

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

## Test Plan

Updated the test snapshots
2024-12-01 20:24:49 -05:00
renovate[bot]
289a938ae8 Update astral-sh/setup-uv action to v4 (#14721) 2024-12-02 01:24:22 +00:00
renovate[bot]
3e5ab6cf38 Update NPM Development dependencies (#14720) 2024-12-02 01:24:09 +00:00
renovate[bot]
48d33595b9 Update dependency tomli to v2.2.1 (#14718) 2024-12-02 01:22:18 +00:00
renovate[bot]
23ee7a954e Update cloudflare/wrangler-action action to v3.13.0 (#14716) 2024-12-02 01:18:43 +00:00
renovate[bot]
d4a7c098dc Update Rust crate ureq to v2.11.0 (#14715) 2024-12-02 01:17:31 +00:00
renovate[bot]
0c5f03a059 Update dependency ruff to v0.8.1 (#14717) 2024-12-02 01:13:13 +00:00
renovate[bot]
239bfb6de7 Update Rust crate similar to v2.6.0 (#14714) 2024-12-01 20:04:07 -05:00
renovate[bot]
3c3ec6755c Update Rust crate rustc-hash to v2.1.0 (#14713) 2024-12-01 20:04:00 -05:00
renovate[bot]
4c05f2c8b4 Update tokio-tracing monorepo (#14710) 2024-12-01 20:03:50 -05:00
renovate[bot]
d594796e3a Update rust-wasm-bindgen monorepo (#14709) 2024-12-01 20:03:43 -05:00
renovate[bot]
b5ef2844ef Update Rust crate syn to v2.0.90 (#14708) 2024-12-01 20:03:36 -05:00
renovate[bot]
06183bd8a1 Update Rust crate pathdiff to v0.2.3 (#14707) 2024-12-01 20:03:29 -05:00
renovate[bot]
4068006c5f Update Rust crate libc to v0.2.167 (#14705) 2024-12-01 20:03:23 -05:00
renovate[bot]
145c97c94f Update Rust crate ordermap to v0.5.4 (#14706) 2024-12-01 20:03:05 -05:00
github-actions[bot]
84748be163 Sync vendored typeshed stubs (#14696)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-12-01 01:38:31 +00:00
Brent Westbrook
9e017634cb [pep8-naming] Avoid false positive for class Bar(type(foo)) (N804) (#14683) 2024-11-30 22:37:28 +00:00
Simon Brugman
56ae73a925 [pylint] Fix false negatives for ascii and sorted in len-as-condition (PLC1802) (#14692) 2024-11-30 14:10:30 -06:00
InSync
be07424e80 Increase rule set size (#14689) 2024-11-30 15:12:10 +01:00
Connor Skees
579ef01294 mdtest: include test name in printed rerun command (#14684)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-30 11:01:06 +00:00
Micha Reiser
90487b8cbd Skip panda rules if panda module hasn't been seen (#14671) 2024-11-29 21:32:51 +00:00
Alex Waygood
f3d8c023d3 [ruff] Avoid emitting assignment-in-assert when all references to the assigned variable are themselves inside asserts (RUF018) (#14661) 2024-11-29 13:36:59 +00:00
Micha Reiser
b63c2e126b Upgrade Rust toolchain to 1.83 (#14677) 2024-11-29 12:05:05 +00:00
Connor Skees
a6402fb51e mdtest: allow specifying a specific test inside a file (#14670) 2024-11-29 12:59:07 +01:00
Dhruv Manilawala
b3b2c982cd Update CHANGELOG.md with the new commits for 0.8.1 (#14664)
The 0.8.1 release was delayed, so this PR updates the CHANGELOG.md with
the latest commits on `main`.
2024-11-29 03:15:36 +00:00
Simon Brugman
abb3c6ea95 [flake8-pyi] Avoid rewriting invalid type expressions in unnecessary-type-union (PYI055) (#14660) 2024-11-28 18:30:50 +00:00
Brent Westbrook
224fe75a76 [ruff] Implement unnecessary-regular-expression (RUF055) (#14659)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Simon Brugman <sbrugman@users.noreply.github.com>
2024-11-28 18:29:23 +00:00
Simon Brugman
dc29f52750 [flake8-pyi, ruff] Fix traversal of nested literals and unions (PYI016, PYI051, PYI055, PYI062, RUF041) (#14641) 2024-11-28 18:07:12 +00:00
David Salvisberg
d9cbf2fe44 Avoids unnecessary overhead for TC004, when TC001-003 are disabled (#14657) 2024-11-28 16:28:24 +01:00
Samodya Abeysiriwardane
3f6c65e78c [red-knot] Fix merged type after if-else without explicit else branch (#14621)
## Summary

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

The final type of a variable after if-statement without explicit else
branch should be similar to having an explicit else branch.

## Test Plan

Originally failed test cases from the bug are added.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-28 06:23:55 -08:00
Dhruv Manilawala
976c37a849 Bump version to 0.8.1 (#14655) 2024-11-28 19:12:50 +05:30
David Peter
a378ff38dc [red-knot] Fix Boolean flags in mdtests (#14654)
## Summary

Similar to #14652, but now with conditions that are `Literal[True]`
(instead of `Literal[False]`), where we want them to be `bool`.
2024-11-28 14:29:35 +01:00
Alex Waygood
d8bca0d3a2 Fix bug where methods defined using lambdas were flagged by FURB118 (#14639) 2024-11-28 12:58:23 +00:00
David Peter
6f1cf5b686 [red-knot] Minor fix in MRO tests (#14652)
## Summary

`bool()` is equal to `False`, and we infer `Literal[False]` for it. Which
means that the test here will fail as soon as we treat the body of
this `if` as unreachable.
2024-11-28 10:17:15 +01:00
David Peter
8639f8c1a6 CI: Treat mdtest Markdown files as code (#14653)
## Summary

Make sure we run the tests for mdtest-only changes.

## Test Plan

Tested if positive glob patterns override negative patterns here:
https://codepen.io/mrmlnc/pen/OXQjMe
2024-11-28 10:04:20 +01:00
Alex Waygood
f1b2e85339 py-fuzzer: recommend using uvx rather than uv run to run the fuzzer (#14645) 2024-11-27 22:19:52 +00:00
David Salvisberg
6d61c8aa16 Fixes minor bug in SemanticModel::lookup_symbol (#14643)
## Summary

This came up as part of #12927 when implementing
`SemanticModel::simulate_runtime_load`.

Should be fairly self-explanatory, if the scope returns a binding with
`BindingKind::Annotation` the bottom part of the loop gets skipped, so
there's no chance for `seen_function` to have been updated. So unless
there's something subtle going on here, like function scopes never
containing bindings with `BindingKind::Annotation`, this seems like a
bug.

## Test Plan

`cargo nextest run`
2024-11-27 16:50:19 -05:00
David Salvisberg
8a7ba5d2df [flake8-type-checking] Fixes quote_type_expression (#14634) 2024-11-27 18:58:48 +01:00
Brent Westbrook
6fcbe8efb4 [ruff] Detect redirected-noqa in file-level comments (RUF101) (#14635) 2024-11-27 18:25:47 +01:00
Alexandra Valentine-Ketchum
c40b37aa36 N811 & N814: eliminate false positives for single-letter names (#14584)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-27 14:38:36 +00:00
Alex Waygood
ef0e2a6e1b Refactor crates/ruff_python_stdlib/src/builtins.rs to make it easier to add support for new Python versions (#14632) 2024-11-27 12:20:21 +00:00
Alex Waygood
4fb1416bf4 Minor stylistic improvements for functions detecting PEP-604 unions (#14633) 2024-11-27 11:29:37 +00:00
Simon Brugman
8a860b89b4 Add social icons to the footer (#14591)
## Summary

Add social icons to the footer

`mkdocs-material` update is required for the `x-twitter` icon.

## Test Plan

Tested locally. 

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-11-27 11:07:45 +00:00
Dhruv Manilawala
f96fa6b0e2 Do not consider f-strings with escaped newlines as multiline (#14624)
## Summary

This PR fixes a bug in the f-string formatting to not consider the
escaped newlines for `is_multiline`. This is done by checking if the
f-string is triple-quoted or not similar to normal string literals.

This is not required to be gated behind preview because the logic change
for `is_multiline` was added in
https://github.com/astral-sh/ruff/pull/14454.

## Test Plan

Add a test case which formats differently on `main`:
https://play.ruff.rs/ea3c55c2-f0fe-474e-b6b8-e3365e0ede5e
2024-11-27 10:25:38 +00:00
Dhruv Manilawala
4cd2b9926e Gate is_multiline change behind preview (#14630)
## Summary

Ref:
https://github.com/astral-sh/ruff/pull/14624#pullrequestreview-2464127254

## Test Plan

The test case in the follow-up PR showcases the difference between
preview and non-preview formatting:
https://github.com/astral-sh/ruff/pull/14624/files#diff-dc25bd4df280d9a9180598075b5bc2d0bac30af956767b373561029309c8f024
2024-11-27 15:50:28 +05:30
Simon Brugman
11a2929ed7 [ruff] Implement unnecessary-nested-literal (RUF041) (#14323)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-27 10:01:50 +00:00
InSync
187974eff4 [flake8-use-pathlib] Recommend Path.iterdir() over os.listdir() (PTH208) (#14509)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-27 09:53:13 +00:00
Micha Reiser
14ba469fc0 Use a derive macro for Violations (#14557)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-27 09:41:40 +00:00
David Salvisberg
6fd10e2fe7 [flake8-type-checking] Adds implementation for TC007 and TC008 (#12927)
Co-authored-by: Simon Brugman <sbrugman@users.noreply.github.com>
Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-11-27 09:51:20 +01:00
Alex Waygood
e0f3eaf1dd Turn the fuzz-parser script into a properly packaged Python project (#14606)
## Summary

This PR gets rid of the `requirements.in` and `requirements.txt` files
in the `scripts/fuzz-parser` directory, and replaces them with
`pyproject.toml` and `uv.lock` files. The script is renamed from
`fuzz-parser` to `py-fuzzer` (since it can now also be used to fuzz
red-knot as well as the parser, following
https://github.com/astral-sh/ruff/pull/14566), and moved from the
`scripts/` directory to the `python/` directory, since it's now a
(uv)-pip-installable project in its own right.

I've been resisting this for a while, because conceptually this script
just doesn't feel "complicated" enough to me for it to be a full-blown
package. However, I think it's time to do this. Making it a proper
package has several advantages:
- It means we can run it from the project root using `uv run` without
having to activate a virtual environment and ensure that all required
dependencies are installed into that environment
- Using a `pyproject.toml` file means that we can express that the
project requires Python 3.12+ to run properly; this wasn't possible
before
- I've been running mypy on the project locally when I've been working
on it or reviewing other people's PRs; now I can put the mypy config for
the project in the `pyproject.toml` file

## Test Plan

I manually tested that all the commands detailed in
`python/py-fuzzer/README.md` work for me locally.

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2024-11-27 08:09:04 +00:00
Dhruv Manilawala
c84c690f1e Avoid invalid syntax for format-spec with quotes for all Python versions (#14625)
## Summary

fixes: #14608

The logic that was only applied for 3.12+ target version needs to be
applied for other versions as well.

## Test Plan

I've moved the existing test cases for 3.12 only to `f_string.py` so
that it's tested against the default target version.

I think we should probably enabled testing for two target version (pre
3.12 and 3.12) but it won't highlight any issue because the parser
doesn't consider this. Maybe we should enable this once we have target
version specific syntax errors in place
(https://github.com/astral-sh/ruff/issues/6591).
2024-11-27 13:19:33 +05:30
Dhruv Manilawala
0d649f9afd Check that airflow module is seen for AIR001 (#14627) 2024-11-27 07:25:08 +00:00
Lokejoke
82c01aa662 [pylint] Implement len-test (PLC1802) (#14309)
## Summary

This PR implements [`use-implicit-booleaness-not-len` /
`C1802`](https://pylint.pycqa.org/en/latest/user_guide/messages/convention/use-implicit-booleaness-not-len.html)
> For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.

---------

Co-authored-by: xbrtnik1 <524841@mail.muni.cz>
Co-authored-by: xbrtnik1 <xbrtnik1@mail.muni.cz>
2024-11-26 13:30:17 -06:00
Brent Westbrook
9f446faa6c [pyflakes] Avoid false positives in @no_type_check contexts (F821, F722) (#14615) 2024-11-26 19:13:43 +00:00
David Peter
b94d6cf567 [red-knot] Fix panic related to f-strings in annotations (#14613)
## Summary

Fix panics related to expressions without inferred types in invalid
syntax examples like:
```py
x: f"Literal[{1 + 2}]" = 3
```
where the `1 + 2` expression (and its sub-expressions) inside the
annotation did not have an inferred type.

## Test Plan

Added new corpus test.
2024-11-26 16:35:44 +01:00
David Peter
cd0c97211c [red-knot] Update KNOWN_FAILURES (#14612)
## Summary

Remove entry that was prevously fixed in
5a30ec0df6.

## Test Plan

```sh
cargo test -p red_knot_workspace -- --ignored linter_af linter_gz
```
2024-11-26 15:56:42 +01:00
David Peter
0e71c9e3bb [red-knot] Fix unit tests in release mode (#14604)
## Summary

This is about the easiest patch that I can think of. It has a drawback
in that there is no real guarantee this won't happen again. I think this
might be acceptable, given that all of this is a temporary thing.

And we also add a new CI job to prevent regressions like this in the
future.

For the record though, I'm listing alternative approaches I thought of:

- We could get rid of the debug/release distinction and just add `@Todo`
type metadata everywhere. This has possible affects on runtime. The main
reason I didn't follow through with this is that the size of `Type`
increases. We would either have to adapt the `assert_eq_size!` test or
get rid of it. Even if we add messages everywhere and get rid of the
file-and-line-variant in the enum, it's not enough to get back to the
current release-mode size of `Type`.
- We could generally discard `@Todo` meta information when using it in
tests. I think this would be a huge drawback. I like that we can have
the actual messages in the mdtest. And make sure we get the expected
`@Todo` type, not just any `@Todo`. It's also helpful when debugging
tests.

closes #14594

## Test Plan

```rs
cargo nextest run --release
```
2024-11-26 15:40:02 +01:00
Dylan
24c90d6953 [pylint] Do not wrap function calls in parentheses in the fix for unnecessary-dunder-call (PLC2801) (#14601) 2024-11-26 06:47:01 -06:00
Tzu-ping Chung
fbff4dec3a [airflow] Avoid implicit DAG schedule (AIR301) (#14581) 2024-11-26 13:38:18 +01:00
Dhruv Manilawala
f3dac27e9a Fix f-string formatting in assignment statement (#14454)
## Summary

fixes: #13813

This PR fixes a bug in the formatting assignment statement when the
value is an f-string.

This is resolved by using custom best fit layouts if the f-string is (a)
not already a flat f-string (thus, cannot be multiline) and (b) is not a
multiline string (thus, cannot be flattened). So, it is used in cases
like the following:
```py
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
    expression}moreeeeeeeeeeeeeeeee"
```
Which is (a) `FStringLayout::Multiline` and (b) not a multiline.

There are various other examples in the PR diff along with additional
explanation and context as code comments.

## Test Plan

Add multiple test cases for various scenarios.
2024-11-26 15:07:18 +05:30
Simon Brugman
e4cefd9bf9 Extend test cases for flake8-pyi (#14280) 2024-11-26 09:10:38 +01:00
Lokejoke
9e4ee98109 [ruff] Implement invalid-assert-message-literal-argument (RUF040) (#14488)
## Summary

This PR implements new rule discussed
[here](https://github.com/astral-sh/ruff/discussions/14449).
In short, it searches for assert messages which were unintentionally
used as a expression to be matched against.

## Test Plan

`cargo test` and review of `ruff-ecosystem`
2024-11-25 17:41:07 -06:00
Shaygan Hooshyari
557d583e32 Support typing.NoReturn and typing.Never (#14559)
Fix #14558 
## Summary

- Add `typing.NoReturn` and `typing.Never` to known instances and infer
them as `Type::Never`
- Add `is_assignable_to` cases for `Type::Never`

I skipped emitting diagnostic for when a function is annotated as
`NoReturn` but it actually returns.

## Test Plan

Added tests from

https://github.com/python/typing/blob/main/conformance/tests/specialtypes_never.py
except from generics and checking if the return value of the function
and the annotations match.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-11-25 21:37:55 +00:00
cake-monotone
f98eebdbab [red-knot] Fix Leaking Narrowing Constraint in ast::ExprIf (#14590)
## Summary

Closes #14588


```py
x: Literal[42, "hello"] = 42 if bool_instance() else "hello"
reveal_type(x)  # revealed: Literal[42] | Literal["hello"]

_ = ... if isinstance(x, str) else ...

# The `isinstance` test incorrectly narrows the type of `x`.
# As a result, `x` is revealed as Literal["hello"], but it should remain Literal[42, "hello"].
reveal_type(x)  # revealed: Literal["hello"]
```

## Test Plan
mdtest included!

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-25 10:36:37 -08:00
Simon Brugman
c606bf014e [flake8-pyi] Improve autofix safety for redundant-none-literal (PYI061) (#14583)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-25 17:40:57 +00:00
Simon Brugman
e8fce20736 [ruff] Improve autofix safety for never-union (RUF020) (#14589) 2024-11-25 18:35:07 +01:00
Dhruv Manilawala
5a30ec0df6 Avoid inferring invalid expr types for string annotation (#14447)
## Summary

fixes: #14440

## Test Plan

Add a test case with all the invalid expressions in a string annotation
context.
2024-11-25 21:27:03 +05:30
Alex Waygood
fab1b0d546 fuzz-parser: catch exceptions from pysource-minimize (#14586) 2024-11-25 15:14:01 +00:00
Connor Skees
66abef433b red-knot: adapt fuzz-parser to work with red-knot (#14566)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-25 13:12:28 +00:00
Harutaka Kawamura
fa22bd604a Fix pytest.mark.parametrize rules to check calls instead of decorators (#14515)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-25 13:55:18 +01:00
Dhruv Manilawala
0c9165fc3a Use Result for failed text document retrieval in LSP requests (#14579)
## Summary

Ref:
https://github.com/astral-sh/ruff-vscode/issues/644#issuecomment-2496588452

## Test Plan

Not sure how to test this as this is mainly to get more context on the
panic that the server is raising.
2024-11-25 15:14:30 +05:30
renovate[bot]
9f6147490b Update NPM Development dependencies (#14577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:54:51 +01:00
renovate[bot]
b7571c3e24 Update Rust crate syn to v2.0.89 (#14573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 07:46:06 +00:00
renovate[bot]
d178d115f3 Update dependency mdformat to v0.7.19 (#14576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:40:53 +01:00
renovate[bot]
6501782678 Update Rust crate libcst to v1.5.1 (#14570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:39:22 +01:00
renovate[bot]
bca4341dcc Update Rust crate hashbrown to v0.15.2 (#14569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:38:34 +01:00
renovate[bot]
31ede11774 Update Rust crate quick-junit to v0.5.1 (#14572)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:38:12 +01:00
renovate[bot]
ba9f881687 Update Rust crate proc-macro2 to v1.0.92 (#14571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:38:00 +01:00
renovate[bot]
4357a0a3c2 Update Rust crate unicode-ident to v1.0.14 (#14574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:36:50 +01:00
renovate[bot]
c18afa93b3 Update Rust crate url to v2.5.4 (#14575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:36:28 +01:00
renovate[bot]
8f04202ee4 Update Rust crate dir-test to 0.4.0 (#14578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 08:36:06 +01:00
Dhruv Manilawala
efe54081d6 Remove FormatFStringPart (#14448)
## Summary

This is just a small refactor to remove the `FormatFStringPart` as it's
only used in the case when the f-string is not implicitly concatenated
in which case the only part is going to be `FString`. In implicitly
concatenated f-strings, we use `StringLike` instead.
2024-11-25 10:29:22 +05:30
Alex Waygood
ac23c99744 [ruff] Mark fixes for unsorted-dunder-all and unsorted-dunder-slots as unsafe when there are complex comments in the sequence (RUF022, RUF023) (#14560) 2024-11-24 12:49:29 +00:00
InSync
e5c7d87461 Add @astropy/astropy to ecosystem checks (#14565) 2024-11-24 12:47:11 +01:00
Charlie Marsh
de62e39eba Use truthiness check in auto_attribs detection (#14562) 2024-11-23 22:06:10 -05:00
InSync
d285717da8 [ruff] Handle attrs's auto_attribs correctly (RUF009) (#14520)
## Summary

Resolves #14519.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-11-23 21:46:38 -05:00
InSync
545e9deba3 [flake8-builtins] Exempt private built-in modules (A005) (#14505)
## Summary

Resolves #12949.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-11-23 21:39:04 -05:00
Harutaka Kawamura
e3d792605f [flake8-bugbear] Fix mutable-contextvar-default (B039) to resolve annotated function calls properly (#14532)
## Summary

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

Fix #14525

## Test Plan

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

New test cases

---------

Signed-off-by: harupy <hkawamura0130@gmail.com>
2024-11-23 21:29:25 -05:00
Harutaka Kawamura
1f303a5eb6 Simplify flake8_pytest_style::rules::fail_call implementation (#14556) 2024-11-23 15:14:28 +01:00
Nikolas Hearp
07d13c6b4a [B028-doc-update] Update documentation for B028 (#14338)
## Summary
Resolves #14289
The documentation for B028 no_explicit_stacklevel is updated to be more
clear.

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2024-11-23 07:45:28 +00:00
Dylan
e1838aac29 Ignore more rules for stub files (#14541)
This PR causes the following rules to ignore stub files, on the grounds
that it is not under the author's control to appease these lints:

- `PLR0904` https://docs.astral.sh/ruff/rules/too-many-public-methods/
- `PLR0913` https://docs.astral.sh/ruff/rules/too-many-arguments/
- `PLR0917`
https://docs.astral.sh/ruff/rules/too-many-positional-arguments/
- `PLW3201` https://docs.astral.sh/ruff/rules/bad-dunder-method-name/
- `SLOT` https://docs.astral.sh/ruff/rules/#flake8-slots-slot
- `FBT` https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt
(except for FBT003 since that involves a function call.)

Progress towards #14535
2024-11-23 07:41:10 +00:00
Carl Meyer
4ba847f250 [red-knot] remove wrong typevar attribute implementations (#14540) 2024-11-22 13:17:16 -08:00
renovate[bot]
13e9fc9362 Update dependency smol-toml to v1.3.1 (#14542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-22 21:16:05 +00:00
Dylan
3fda2d17c7 [ruff] Auto-add r prefix when string has no backslashes for unraw-re-pattern (RUF039) (#14536)
This PR adds a sometimes-available, safe autofix for [unraw-re-pattern
(RUF039)](https://docs.astral.sh/ruff/rules/unraw-re-pattern/#unraw-re-pattern-ruf039),
which prepends an `r` prefix. It is used only when the string in
question has no backslahses (and also does not have a `u` prefix, since
that causes a syntax error.)

Closes #14527

Notes: 
- Test fixture unchanged, but snapshot changed to include fix messages.
- This fix is automatically only available in preview since the rule
itself is in preview
2024-11-22 15:09:53 -06:00
Harutaka Kawamura
931fa06d85 Extend invalid-envvar-default (PLW1508) to detect os.environ.get (#14512) 2024-11-22 19:13:58 +00:00
Micha Reiser
e53ac7985d Enable logging for directory-renamed test (#14533) 2024-11-22 16:41:46 +00:00
David Salvisberg
e25e7044ba [flake8-type-checking] Adds implementation for TC006 (#14511)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-22 15:22:59 +01:00
Micha Reiser
b80de52592 Consider quotes inside format-specs when choosing the quotes for an f-string (#14493) 2024-11-22 12:43:53 +00:00
Alex Waygood
2917534279 Fix broken link to PYI063 (#14526) 2024-11-22 12:27:52 +00:00
David Peter
f6b2cd5588 [red-knot] Semantic index: handle invalid breaks (#14522)
## Summary

This fix addresses panics related to invalid syntax like the following
where a `break` statement is used in a nested definition inside a
loop:

```py
while True:

    def b():
        x: int

        break
```

closes #14342

## Test Plan

* New corpus regression tests.
* New unit test to make sure we handle nested while loops correctly.
This test is passing on `main`, but can easily fail if the
`is_inside_loop` state isn't properly saved/restored.
2024-11-22 13:13:55 +01:00
Micha Reiser
302fe76c2b Fix unnecessary space around power op in overlong f-string expressions (#14489) 2024-11-22 13:01:22 +01:00
David Peter
a90e404c3f [red-knot] PEP 695 type aliases (#14357)
## Summary

Add support for (non-generic) type aliases. The main motivation behind
this was to get rid of panics involving expressions in (generic) type
aliases. But it turned out the best way to fix it was to implement
(partial) support for type aliases.

```py
type IntOrStr = int | str

reveal_type(IntOrStr)  # revealed: typing.TypeAliasType
reveal_type(IntOrStr.__name__)  # revealed: Literal["IntOrStr"]

x: IntOrStr = 1

reveal_type(x)  # revealed: Literal[1]

def f() -> None:
    reveal_type(x)  # revealed: int | str
```

## Test Plan

- Updated corpus test allow list to reflect that we don't panic anymore.
- Added Markdown-based test for type aliases (`type_alias.md`)
2024-11-22 08:47:14 +01:00
Micha Reiser
8358ad8d25 Ruff 0.8 release (#14486)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: David Salvisberg <dave@daverball.com>
2024-11-22 08:45:19 +01:00
Alex Waygood
2b8b1ef178 Improve docs for some pycodestyle rules (#14517) 2024-11-21 17:26:06 +00:00
Dylan
2efa3fbb62 [flake8-import-conventions] Syntax check aliases supplied in configuration for unconventional-import-alias (ICN001) (#14477)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-21 15:54:49 +00:00
cmp0xff
b9da4305e6 doc(B024): #14455 add annotated but unassgined class variables (#14502)
# Summary

Closes #14455, migrated from https://github.com/astral-sh/docs/pull/106.
2024-11-21 09:08:02 -06:00
Micha Reiser
87043a2415 Limit type size assertion to 64bit (#14514) 2024-11-21 12:49:55 +00:00
David Peter
f684b6fff4 [red-knot] Fix: Infer type for typing.Union[..] tuple expression (#14510)
## Summary

Fixes a panic related to sub-expressions of `typing.Union` where we fail
to store a type for the `int, str` tuple-expression in code like this:
```
x: Union[int, str] = 1
```

relates to [my
comment](https://github.com/astral-sh/ruff/pull/14499#discussion_r1851794467)
on #14499.

## Test Plan

New corpus test
2024-11-21 11:49:20 +01:00
David Peter
47f39ed1a0 [red-knot] Meta data for Type::Todo (#14500)
## Summary

Adds meta information to `Type::Todo`, allowing developers to easily
trace back the origin of a particular `@Todo` type they encounter.

Instead of `Type::Todo`, we now write either `type_todo!()` which
creates a `@Todo[path/to/source.rs:123]` type with file and line
information, or using `type_todo!("PEP 604 unions not supported")`,
which creates a variant with a custom message.

`Type::Todo` now contains a `TodoType` field. In release mode, this is
just a zero-sized struct, in order not to create any overhead. In debug
mode, this is an `enum` that contains the meta information.

`Type` implements `Copy`, which means that `TodoType` also needs to be
copyable. This limits the design space. We could intern `TodoType`, but
I discarded this option, as it would require us to have access to the
salsa DB everywhere we want to use `Type::Todo`. And it would have made
the macro invocations less ergonomic (requiring us to pass `db`).

So for now, the meta information is simply a `&'static str` / `u32` for
the file/line variant, or a `&'static str` for the custom message.
Anything involving a chain/backtrace of several `@Todo`s or similar is
therefore currently not implemented. Also because we currently don't see
any direct use cases for this, and because all of this will eventually
go away.

Note that the size of `Type` increases from 16 to 24 bytes, but only in
debug mode.

## Test Plan

- Observed the changes in Markdown tests.
- Added custom messages for all `Type::Todo`s that were revealed in the
tests
- Ran red knot in release and debug mode on the following Python file:
  ```py
  def f(x: int) -> int:
      reveal_type(x)
  ```
Prints `@Todo` in release mode and `@Todo(function parameter type)` in
debug mode.
2024-11-21 09:59:47 +01:00
Shaygan Hooshyari
aecdb8c144 [red-knot] support typing.Union in type annotations (#14499)
Fix #14498

## Summary

This PR adds `typing.Union` support

## Test Plan

I created new tests in mdtest.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-11-20 21:55:33 +00:00
Zanie Blue
3c52d2d1bd Improve the performance of the formatter instability check job (#14471)
We should probably get rid of this entirely and subsume it's
functionality in the normal ecosystem checks? I don't think we're using
the black comparison tests anymore, but maybe someone wants it?

There are a few major parts to this:

1. Making the formatter script idempotent, so it can be run repeatedly
and is robust to changing commits
2. Reducing the overhead of the git operations, minimizing the data
transfer
3. Parallelizing all the git operations by repository

This reduces the setup time from 80s to 16s (locally).

The initial motivation for idempotency was to include the repositories
in the GitHub Actions cache. I'm not sure it's worth it yet — they're
about 1GB and would consume our limited cache space. Regardless, it
improves correctness for local invocations.

The total runtime of the job is reduced from ~4m to ~3m.

I also made some cosmetic changes to the output paths and such.
2024-11-20 08:55:10 -06:00
Micha Reiser
942d6eeb9f Stabilize A004 (#14480) 2024-11-20 13:11:51 +01:00
Alex Waygood
4ccacc80f9 [ruff-0.8] [FAST] Further improve docs for fast-api-non-annotated-depencency (FAST002) (#14467) 2024-11-20 13:11:51 +01:00
Micha Reiser
b2bb119c6a Fix failing tests for Ruff 0.8 branch (#14482) 2024-11-20 13:11:51 +01:00
Alex Waygood
cef12f4925 [ruff-0.8] Spruce up docs for newly stabilised rules (#14466)
## Summary

- Expand some docs where they're unclear about the motivation, or assume
some knowledge that hasn't been introduced yet
- Add more links to external docs
- Rename PYI063 from `PrePep570PositionalArgument` to
`Pep484StylePositionalOnlyParameter`
- Rename the file `parenthesize_logical_operators.rs` to
`parenthesize_chained_operators.rs`, since the rule is called
`ParenthesizeChainedOperators`, not `ParenthesizeLogicalOperators`

## Test Plan

`cargo test`
2024-11-20 13:11:51 +01:00
Alex Waygood
aa7ac2ce0f [ruff-0.8] [ruff] Stabilise unsorted-dunder-all and unsorted-dunder-slots (#14468)
## Summary

These rules were implemented in January, have been very stable, and have
no open issues about them. They were highly requested by the community
prior to being implemented. Let's stabilise them!

## Test Plan

Ecosystem check on this PR.
2024-11-20 13:11:51 +01:00
Zanie Blue
70d9c90827 Use XDG (i.e. ~/.local/bin) instead of the Cargo home directory in the installer (#14457)
Closes https://github.com/astral-sh/ruff/issues/13927
2024-11-20 13:11:51 +01:00
Micha Reiser
adfa723464 Stabilize multiple rules (#14462) 2024-11-20 13:11:51 +01:00
Zanie Blue
844c07f1f0 Upgrade cargo-dist from 0.22.1 => 0.25.2-prerelease.3 (#14456)
Needed to prevent updater failures when doing
https://github.com/astral-sh/ruff/issues/13927

See 

- https://github.com/axodotdev/axoupdater/issues/210
- https://github.com/axodotdev/cargo-dist/pull/1538
- https://github.com/astral-sh/uv/pull/8958
2024-11-20 13:11:51 +01:00
Alex Waygood
11d20a1a51 [ruff-0.8] [ruff] Stabilise parenthesize-chained-operators (RUF021) (#14450) 2024-11-20 13:11:51 +01:00
Micha Reiser
e9079e7d95 Remove the deprecated E999 rule code (#14428) 2024-11-20 13:11:51 +01:00
Alex Waygood
c400725713 [ruff 0.8] [flake8-pytest-style] Remove deprecated rules PT004 and PT005 (#14385)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-20 13:11:51 +01:00
Alex Waygood
1081694140 [ruff 0.8] [flake8-annotations] Remove deprecated rules ANN101 and ANN102 (#14384)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-20 13:11:51 +01:00
Micha Reiser
52f526eb38 Warn instead of error when removed rules are used in ignore (#14435)
Closes https://github.com/astral-sh/ruff/issues/13505
2024-11-20 13:11:51 +01:00
David Salvisberg
dc05b38165 [ruff 0.8][flake8-type-checking] Rename TCH to TC (#14438)
Closes #9573
2024-11-20 13:11:51 +01:00
renovate[bot]
8c3c5ee5e3 Update Rust crate unicode-width to 0.2.0 (#13473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 13:11:51 +01:00
konsti
b46cc6ac0b Update pyproject-toml to support PEP 639 (#13902)
Fixes #13869
2024-11-20 13:11:51 +01:00
Dylan
8b925ea626 [pycodestyle] Stabilize behavior to ignore stub files in ambiguous-variable-name (E741) (#14405) 2024-11-20 13:11:51 +01:00
yataka
1b180c8342 Change default for Python version from 3.8 to 3.9 (#13896)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-20 13:11:51 +01:00
Dylan
afeb217452 [pyupgrade] Stabilize behavior to show diagnostic even when unfixable in printf-string-formatting (UP031) (#14406) 2024-11-20 13:11:51 +01:00
Dylan
c0b3dd3745 [ruff] Stabilize unsafe fix for zip-instead-of-pairwise (RUF007) (#14401)
This PR stabilizes the unsafe fix for [zip-instead-of-pairwise
(RUF007)](https://docs.astral.sh/ruff/rules/zip-instead-of-pairwise/#zip-instead-of-pairwise-ruf007),
which replaces the use of zip with that of itertools.pairwise and has
been available under preview since version 0.5.7.

There are no open issues regarding RUF007 at the time of this writing.
2024-11-20 13:11:51 +01:00
Alex Waygood
5f6607bf54 [ruff 0.8] Remove deprecated rule UP027 (#14382) 2024-11-20 13:11:51 +01:00
Zanie Blue
a6deca44b5 Rename the parser fuzz job for consistency with the rest of CI (#14479) 2024-11-20 07:54:42 +00:00
Zanie Blue
0dbceccbc1 Only build the fuzz crate on main (#14478)
It's not actually doing anything per pull request and it's pretty slow?
xref #14469

It seems useful to build on `main` still to find build regressions? e.g.
https://github.com/astral-sh/ruff/issues/9368
2024-11-19 23:07:48 -06:00
Dhruv Manilawala
48680e10b6 Watch for changes to the generated file during documentation serve (#14476)
## Summary

Similar to https://github.com/astral-sh/uv/pull/9244, but we need to use
the `mkdocs.generated.yml` file because the `scripts/generate_mkdocs.py`
uses the `mkdocs.template.yml` to generate the final config.
2024-11-20 04:51:20 +00:00
Zanie Blue
b0c88a2a42 Only test release builds on main (#14475)
This is one of the slowest remaining jobs in the pull request CI. We
could use a larger runner for a trivial speed-up (in exchange for $$),
but I don't think this is going to break often enough to merit testing
on every pull request commit? It's not a required job, so I don't feel
strongly about it, but it feels like a bit of a waste of compute.

Originally added in https://github.com/astral-sh/ruff/pull/11182
2024-11-19 22:47:46 -06:00
InSync
b9c53a74f9 [pycodestyle] Exempt pytest.importorskip() calls (E402) (#14474)
## Summary

Resolves #13537.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-11-19 22:08:15 -05:00
cake-monotone
6a4d207db7 [red-knot] Refactoring the inference logic of lexicographic comparisons (#14422)
## Summary

closes #14279

### Limitations of the Current Implementation
#### Incorrect Error Propagation

In the current implementation of lexicographic comparisons, if the
result of an Eq operation is Ambiguous, the comparison stops
immediately, returning a bool instance. While this may yield correct
inferences, it fails to capture unsupported-operation errors that might
occur in subsequent comparisons.
```py
class A: ...

(int_instance(), A()) < (int_instance(), A())  # should error
```

#### Weak Inference in Specific Cases

> Example: `(int_instance(), "foo") == (int_instance(), "bar")`
> Current result: `bool`
> Expected result: `Literal[False]`

`Eq` and `NotEq` have unique behavior in lexicographic comparisons
compared to other operators. Specifically:
- For `Eq`, if any non-equal pair exists within the tuples being
compared, we can immediately conclude that the tuples are not equal.
- For `NotEq`, if any equal pair exists, we can conclude that the tuples
are unequal.

```py
a = (str_instance(), int_instance(), "foo")

reveal_type(a == a)  # revealed: bool
reveal_type(a != a)  # revealed: bool

b = (str_instance(), int_instance(), "bar")

reveal_type(a == b)  # revealed: bool  # should be Literal[False]
reveal_type(a != b)  # revealed: bool  # should be Literal[True]
```
#### Incorrect Support for Non-Boolean Rich Comparisons

In CPython, aside from `==` and `!=`, tuple comparisons return a
non-boolean result as-is. Tuples do not convert the value into `bool`.

Note: If all pairwise `==` comparisons between elements in the tuples
return Truthy, the comparison then considers the tuples' lengths.
Regardless of the return type of the dunder methods, the final result
can still be a boolean.

```py
from __future__ import annotations

class A:
    def __eq__(self, o: object) -> str:
        return "hello"

    def __ne__(self, o: object) -> bytes:
        return b"world"

    def __lt__(self, o: A) -> float:
        return 3.14

a = (A(), A())

reveal_type(a == a)  # revealed: bool
reveal_type(a != a)  # revealed: bool
reveal_type(a < a)  # revealed: bool # should be: `float | Literal[False]`

```

### Key Changes
One of the major changes is that comparisons no longer end with a `bool`
result when a pairwise `Eq` result is `Ambiguous`. Instead, the function
attempts to infer all possible cases and unions the results. This
improvement allows for more robust type inference and better error
detection.

Additionally, as the function is now optimized for tuple comparisons,
the name has been changed from the more general
`infer_lexicographic_comparison` to `infer_tuple_rich_comparison`.

## Test Plan

mdtest included
2024-11-19 17:32:43 -08:00
Zanie Blue
42c35b6f44 Use larger GitHub runner for testing on Windows (#14461)
Reduces to 3m 50s (extra large) or 6m 5s (large) vs 9m 7s (standard)
2024-11-19 18:00:59 -06:00
Zanie Blue
9e79d64d62 Use Depot 16-core runner for testing on Linux (#14460)
Reduces Linux test CI to 1m 40s (16 core) or 2m 56s (8 core) to from 4m
25s. Times are approximate, as runner performance is pretty variable.

In uv, we use the 16 core runners.
2024-11-19 18:00:51 -06:00
Zanie Blue
582857f292 Use Depot 8-core runner for ecosystem tests (#14463)
I noticed this was exceedingly slow.

Reduces to 3m from 14m
2024-11-19 18:00:38 -06:00
Zanie Blue
9bbeb793e5 Specify the wasm-pack version explicitly (#14465)
There is an upstream bug with latest version detection
https://github.com/jetli/wasm-pack-action/issues/23

This causes random flakes of the wasm build job.
2024-11-19 18:00:27 -06:00
Micha Reiser
dbbe7a773c Mark UP043 fix unsafe when the type annotation contains any comments (#14458) 2024-11-19 15:24:02 +01:00
InSync
5f09d4a90a [ruff] re and regex calls with unraw string as first argument (RUF039) (#14446) 2024-11-19 13:44:55 +01:00
David Peter
f8c20258ae [red-knot] Do not panic on f-string format spec expressions (#14436)
## Summary

Previously, we panicked on expressions like `f"{v:{f'0.2f'}}"` because
we did not infer types for expressions nested inside format spec
elements.

## Test Plan

```
cargo nextest run -p red_knot_workspace -- --ignored linter_af linter_gz
```
2024-11-19 10:04:51 +01:00
David Peter
d8538d8c98 [red-knot] Narrowing for type(x) is C checks (#14432)
## Summary

Add type narrowing for `type(x) is C` conditions (and `else` clauses of
`type(x) is not C` conditionals):

```py
if type(x) is A:
    reveal_type(x)  # revealed: A
else:
    reveal_type(x)  # revealed: A | B
```

closes: #14431, part of: #13694

## Test Plan

New Markdown-based tests.
2024-11-18 16:21:46 +01:00
InSync
3642381489 [ruff] Add rule forbidding map(int, package.__version__.split('.')) (RUF048) (#14373)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-18 13:43:24 +00:00
Micha Reiser
1f07880d5c Add tests for python version compatibility (#14430) 2024-11-18 12:26:55 +00:00
David Peter
d81b6cd334 [red-knot] Types for subexpressions of annotations (#14426)
## Summary

This patches up various missing paths where sub-expressions of type
annotations previously had no type attached. Examples include:
```py
tuple[int, str]
#     ~~~~~~~~

type[MyClass]
#    ~~~~~~~

Literal["foo"]
#       ~~~~~

Literal["foo", Literal[1, 2]]
#              ~~~~~~~~~~~~~

Literal[1, "a", random.illegal(sub[expr + ession])]
#               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

## Test Plan

```
cargo nextest run -p red_knot_workspace -- --ignored linter_af linter_gz
```
2024-11-18 13:03:27 +01:00
Micha Reiser
d99210c049 [red-knot] Default to python 3.9 (#14429) 2024-11-18 11:27:40 +00:00
Steve C
577653551c [pylint] - use sets when possible for PLR1714 autofix (repeated-equality-comparison) (#14372) 2024-11-18 08:57:43 +01:00
Dhruv Manilawala
38a385fb6f Simplify quote annotator logic for list expression (#14425)
## Summary

Follow-up to #14371, this PR simplifies the visitor logic for list
expressions to remove the state management. We just need to make sure
that we visit the nested expressions using the `QuoteAnnotator` and not
the `Generator`. This is similar to what's being done for binary
expressions.

As per the
[grammar](https://typing.readthedocs.io/en/latest/spec/annotations.html#grammar-token-expression-grammar-annotation_expression),
list expressions can be present which can contain other type expressions
(`Callable`):
```
       | <Callable> '[' <Concatenate> '[' (type_expression ',')+
                    (name | '...') ']' ',' type_expression ']'
             (where name must be a valid in-scope ParamSpec)
       | <Callable> '[' '[' maybe_unpacked (',' maybe_unpacked)*
                    ']' ',' type_expression ']'
```

## Test Plan

`cargo insta test`
2024-11-18 12:33:19 +05:30
renovate[bot]
cd2ae5aa2d Update NPM Development dependencies (#14416)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[@cloudflare/workers-types](https://redirect.github.com/cloudflare/workerd)
| [`4.20241106.0` ->
`4.20241112.0`](https://renovatebot.com/diffs/npm/@cloudflare%2fworkers-types/4.20241106.0/4.20241112.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@cloudflare%2fworkers-types/4.20241112.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@cloudflare%2fworkers-types/4.20241112.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@cloudflare%2fworkers-types/4.20241106.0/4.20241112.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@cloudflare%2fworkers-types/4.20241106.0/4.20241112.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/eslint-plugin](https://typescript-eslint.io/packages/eslint-plugin)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin))
| [`8.13.0` ->
`8.14.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/8.13.0/8.14.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2feslint-plugin/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2feslint-plugin/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2feslint-plugin/8.13.0/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2feslint-plugin/8.13.0/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/parser](https://typescript-eslint.io/packages/parser)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser))
| [`8.13.0` ->
`8.14.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/8.13.0/8.14.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2fparser/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2fparser/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2fparser/8.13.0/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2fparser/8.13.0/8.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [postcss](https://postcss.org/)
([source](https://redirect.github.com/postcss/postcss)) | [`8.4.48` ->
`8.4.49`](https://renovatebot.com/diffs/npm/postcss/8.4.48/8.4.49) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/postcss/8.4.49?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/postcss/8.4.49?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/postcss/8.4.48/8.4.49?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/postcss/8.4.48/8.4.49?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [tailwindcss](https://tailwindcss.com)
([source](https://redirect.github.com/tailwindlabs/tailwindcss)) |
[`3.4.14` ->
`3.4.15`](https://renovatebot.com/diffs/npm/tailwindcss/3.4.14/3.4.15) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/tailwindcss/3.4.15?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/tailwindcss/3.4.15?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/tailwindcss/3.4.14/3.4.15?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/tailwindcss/3.4.14/3.4.15?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [vite](https://vite.dev)
([source](https://redirect.github.com/vitejs/vite/tree/HEAD/packages/vite))
| [`5.4.10` ->
`5.4.11`](https://renovatebot.com/diffs/npm/vite/5.4.10/5.4.11) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/vite/5.4.11?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vite/5.4.11?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vite/5.4.10/5.4.11?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite/5.4.10/5.4.11?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [wrangler](https://redirect.github.com/cloudflare/workers-sdk)
([source](https://redirect.github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler))
| [`3.86.0` ->
`3.87.0`](https://renovatebot.com/diffs/npm/wrangler/3.86.0/3.87.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/wrangler/3.87.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/wrangler/3.87.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/wrangler/3.86.0/3.87.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/wrangler/3.86.0/3.87.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>cloudflare/workerd (@&#8203;cloudflare/workers-types)</summary>

###
[`v4.20241112.0`](8bf3af4699...7b28eb5032)

[Compare
Source](8bf3af4699...7b28eb5032)

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/eslint-plugin)</summary>

###
[`v8.14.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8140-2024-11-11)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.13.0...v8.14.0)

##### 🚀 Features

- **eslint-plugin:** \[await-thenable] report unnecessary `await using`
statements
([#&#8203;10209](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10209))
- **eslint-plugin:** \[no-confusing-void-expression] add an option to
ignore void<->void
([#&#8203;10067](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10067))

##### 🩹 Fixes

- **scope-manager:** fix asserted increments not being marked as write
references
([#&#8203;10271](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10271))
- **eslint-plugin:** \[no-misused-promises] improve report loc for
methods
([#&#8203;10216](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10216))
- **eslint-plugin:** \[no-unnecessary-condition] improve error message
for literal comparisons
([#&#8203;10194](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/10194))

##### ❤️  Thank You

-   Gyumong [@&#8203;Gyumong](https://redirect.github.com/Gyumong)
-   Jan Ochwat [@&#8203;janek515](https://redirect.github.com/janek515)
- Kirk Waiblinger
[@&#8203;kirkwaiblinger](https://redirect.github.com/kirkwaiblinger)
-   Ronen Amiel

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/parser)</summary>

###
[`v8.14.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#8140-2024-11-11)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.13.0...v8.14.0)

This was a version bump only for parser to align it with other projects,
there were no code changes.

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>postcss/postcss (postcss)</summary>

###
[`v8.4.49`](https://redirect.github.com/postcss/postcss/blob/HEAD/CHANGELOG.md#8449)

[Compare
Source](https://redirect.github.com/postcss/postcss/compare/8.4.48...8.4.49)

- Fixed custom syntax without `source.offset` (by
[@&#8203;romainmenke](https://redirect.github.com/romainmenke)).

</details>

<details>
<summary>tailwindlabs/tailwindcss (tailwindcss)</summary>

###
[`v3.4.15`](https://redirect.github.com/tailwindlabs/tailwindcss/releases/tag/v3.4.15)

[Compare
Source](https://redirect.github.com/tailwindlabs/tailwindcss/compare/v3.4.14...v3.4.15)

- Bump versions for security vulnerabilities
([#&#8203;14697](https://redirect.github.com/tailwindlabs/tailwindcss/pull/14697))
- Ensure the TypeScript types for the `boxShadow` theme configuration
allows arrays
([#&#8203;14856](https://redirect.github.com/tailwindlabs/tailwindcss/pull/14856))
- Set fallback for opacity variables to ensure setting colors with the
`selection:*` variant works in Chrome 131
([#&#8203;15003](https://redirect.github.com/tailwindlabs/tailwindcss/pull/15003))

</details>

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

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

[Compare
Source](https://redirect.github.com/vitejs/vite/compare/v5.4.10...v5.4.11)

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

</details>

<details>
<summary>cloudflare/workers-sdk (wrangler)</summary>

###
[`v3.87.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3870)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.86.1...wrangler@3.87.0)

##### Minor Changes

-
[#&#8203;7201](https://redirect.github.com/cloudflare/workers-sdk/pull/7201)
[`beed72e`](beed72e7f3)
Thanks [@&#8203;GregBrimble](https://redirect.github.com/GregBrimble)! -
feat: Tail Consumers are now supported for Workers with assets.

You can now configure `tail_consumers` in conjunction with `assets` in
your `wrangler.toml` file. Read more about [Static
Assets](https://developers.cloudflare.com/workers/static-assets/) and
[Tail
Consumers](https://developers.cloudflare.com/workers/observability/logs/tail-workers/)
in the documentation.

-
[#&#8203;7212](https://redirect.github.com/cloudflare/workers-sdk/pull/7212)
[`837f2f5`](837f2f569b)
Thanks [@&#8203;jonesphillip](https://redirect.github.com/jonesphillip)!
- Added r2 bucket info command to Wrangler. Improved formatting of r2
bucket list output

##### Patch Changes

-
[#&#8203;7210](https://redirect.github.com/cloudflare/workers-sdk/pull/7210)
[`c12c0fe`](c12c0fed88)
Thanks [@&#8203;taylorlee](https://redirect.github.com/taylorlee)! -
Avoid an unnecessary GET request during `wrangler deploy`.

-
[#&#8203;7197](https://redirect.github.com/cloudflare/workers-sdk/pull/7197)
[`4814455`](4814455717)
Thanks
[@&#8203;michelheusschen](https://redirect.github.com/michelheusschen)!
- fix console output for `wrangler d1 migrations create`

-
[#&#8203;6795](https://redirect.github.com/cloudflare/workers-sdk/pull/6795)
[`94f07ee`](94f07eec15)
Thanks [@&#8203;benmccann](https://redirect.github.com/benmccann)! -
chore: upgrade chokidar to v4

-
[#&#8203;7133](https://redirect.github.com/cloudflare/workers-sdk/pull/7133)
[`c46e02d`](c46e02dfd7)
Thanks [@&#8203;gpanders](https://redirect.github.com/gpanders)! - Do
not emit escape sequences when stdout is not a TTY

###
[`v3.86.1`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3861)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.86.0...wrangler@3.86.1)

##### Patch Changes

-
[#&#8203;7069](https://redirect.github.com/cloudflare/workers-sdk/pull/7069)
[`b499b74`](b499b743e2)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! -
Internal refactor to remove the non `--x-dev-env` flow from `wrangler
dev`

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOS4wIiwidXBkYXRlZEluVmVyIjoiMzkuMTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 12:25:29 +05:30
renovate[bot]
41694f21c6 Update pre-commit dependencies (#14419)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[abravalheri/validate-pyproject](https://redirect.github.com/abravalheri/validate-pyproject)
| repository | minor | `v0.22` -> `v0.23` |
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.7.3` -> `v0.7.4` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>abravalheri/validate-pyproject
(abravalheri/validate-pyproject)</summary>

###
[`v0.23`](https://redirect.github.com/abravalheri/validate-pyproject/releases/tag/v0.23)

[Compare
Source](https://redirect.github.com/abravalheri/validate-pyproject/compare/v0.22...v0.23)

#### What's Changed

- Validate SPDX license expressions by
[@&#8203;cdce8p](https://redirect.github.com/cdce8p) in
[https://github.com/abravalheri/validate-pyproject/pull/217](https://redirect.github.com/abravalheri/validate-pyproject/pull/217)

#### New Contributors

- [@&#8203;cdce8p](https://redirect.github.com/cdce8p) made their first
contribution in
[https://github.com/abravalheri/validate-pyproject/pull/217](https://redirect.github.com/abravalheri/validate-pyproject/pull/217)

**Full Changelog**:
https://github.com/abravalheri/validate-pyproject/compare/v0.22...v0.23

</details>

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

###
[`v0.7.4`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.7.4)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.7.4)

See: https://github.com/astral-sh/ruff/releases/tag/0.7.4

</details>

---

### Configuration

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

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

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

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOS4wIiwidXBkYXRlZEluVmVyIjoiMzkuMTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 11:27:14 +05:30
Charlie Marsh
fccbe56d23 Reverse order of __contains__ arguments (#14424)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14423.
2024-11-18 03:58:12 +00:00
Shantanu
c46555da41 Drive by typo fix (#14420)
Introduced in
https://github.com/astral-sh/ruff/pull/14397/files#diff-42314c006689490bbdfbeeb973de64046b3e069e3d88f67520aeba375f20e655
2024-11-18 03:03:36 +00:00
InSync
0a27c9dabd [flake8-pie] Mark fix as unsafe if the following statement is a string literal (PIE790) (#14393)
## Summary

Resolves #12616.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-18 02:30:06 +00:00
InSync
3c9e76eb66 [flake8-datetimez] Also exempt .time() (DTZ901) (#14394)
## Summary

Resolves #14378.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-18 02:24:35 +00:00
renovate[bot]
80f5cdcf66 Update dependency tomli to v2.1.0 (#14418) 2024-11-18 01:56:05 +00:00
renovate[bot]
35fe0e90da Update Rust crate bstr to v1.11.0 (#14417) 2024-11-17 20:41:49 -05:00
renovate[bot]
157b49a8ee Update dependency ruff to v0.7.4 (#14415) 2024-11-17 20:41:40 -05:00
renovate[bot]
8a6e223df5 Update dependency react-resizable-panels to v2.1.7 (#14414) 2024-11-17 20:41:34 -05:00
renovate[bot]
5a48da53da Update Rust crate serde_json to v1.0.133 (#14413) 2024-11-17 20:41:29 -05:00
renovate[bot]
58005b590c Update Rust crate serde to v1.0.215 (#14412) 2024-11-17 20:41:23 -05:00
renovate[bot]
884835e386 Update Rust crate libc to v0.2.164 (#14411) 2024-11-17 20:41:17 -05:00
renovate[bot]
efd4407f7f Update Rust crate indicatif to v0.17.9 (#14410) 2024-11-17 20:41:13 -05:00
renovate[bot]
761588a60e Update Rust crate clap to v4.5.21 (#14409) 2024-11-17 20:41:06 -05:00
Charlie Marsh
e1eb188049 Avoid panic in unfixable redundant-numeric-union (#14402)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14396.
2024-11-17 12:15:44 -05:00
Shaygan Hooshyari
ff19629b11 Understand typing.Optional in annotations (#14397) 2024-11-17 17:04:58 +00:00
Micha Reiser
cd80c9d907 Fix Red Knot benchmarks on Windows (#14400) 2024-11-17 16:21:09 +00:00
Matt Norton
abb34828bd Improve rule & options documentation (#14329)
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-17 10:16:47 +01:00
InSync
cab7caf80b [flake8-logging] Suggest .getLogger(__name__) instead of .getLogger(__file__) (LOG015) (#14392) 2024-11-17 09:22:52 +01:00
David Peter
d470f29093 [red-knot] Disable linter-corpus tests (#14391)
## Summary

Disable the no-panic tests for the linter corpus, as there are too many
problems right now, requiring linter-contributors to add their test
files to the allow-list.

We can still run the tests using `cargo test -p red_knot_workspace --
--ignored linter_af linter_gz`. This is also why I left the
`crates/ruff_linter/` entries in the allow list for now, even if they
will get out of sync. But let me know if I should rather remove them.
2024-11-16 23:33:19 +01:00
Simon Brugman
1fbed6c325 [ruff] Implement redundant-bool-literal (RUF038) (#14319)
## Summary

Implements `redundant-bool-literal`

## Test Plan

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

`cargo test`

The ecosystem results are all correct, but for `Airflow` the rule is not
relevant due to the use of overloading (and is marked as unsafe
correctly).

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-16 21:52:51 +00:00
David Peter
4dcb7ddafe [red-knot] Remove duplicates from KNOWN_FAILURES (#14386)
## Summary

- Sort the list of `KNOWN_FAILURES`
- Remove accidental duplicates
2024-11-16 20:54:21 +01:00
Micha Reiser
5be90c3a67 Split the corpus tests into smaller tests (#14367)
## Summary

This PR splits the corpus tests into smaller chunks because running all
of them takes 8s on my windows machine and it's by far the longest test
in `red_knot_workspace`.

Splitting the tests has the advantage that they run in parallel. This PR
brings down the wall time from 8s to 4s.

This PR also limits the glob for the linter tests because it's common to
clone cpython into the `ruff_linter/resources/test` folder for
benchmarks (because that's what's written in the contributing guides)

## Test Plan

`cargo test`
2024-11-16 20:29:21 +01:00
Tom Kuson
d0dca7bfcf [pydoclint] Update diagnostics to target the docstring (#14381)
## Summary

Updates the `pydoclint` diagnostics to target the docstring instead of a
related statement.

Closes #13184

## Test Plan

`cargo nextest run`
2024-11-16 13:32:20 -05:00
Simon Brugman
78210b198b [flake8-pyi] Implement redundant-none-literal (PYI061) (#14316)
## Summary

`Literal[None]` can be simplified into `None` in type annotations.

Surprising to see that this is not that rare:
-
https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/chat_models/base.py#L54
-
https://github.com/sqlalchemy/sqlalchemy/blob/main/lib/sqlalchemy/sql/annotation.py#L69
- https://github.com/jax-ml/jax/blob/main/jax/numpy/__init__.pyi#L961
-
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/inference/_common.py#L179

## Test Plan

`cargo test`

Reviewed all ecosystem results, and they are true positives.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-16 18:22:51 +00:00
Simon Brugman
4a2310b595 [flake8-pyi] Implement autofix for redundant-numeric-union (PYI041) (#14273)
## Summary

This PR adds autofix for `redundant-numeric-union` (`PYI041`)

There are some comments below to explain the reasoning behind some
choices that might help review.

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

Resolves part of https://github.com/astral-sh/ruff/issues/14185.

## Test Plan

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

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-16 18:13:23 +00:00
Dylan
fc392c663a [flake8-type-checking] Fix helper function which surrounds annotations in quotes (#14371)
This PR adds corrected handling of list expressions to the `Visitor`
implementation of `QuotedAnnotator` in `flake8_type_checking::helpers`.

Closes #14368
2024-11-16 12:58:02 -05:00
Alex Waygood
81d3c419e9 [red-knot] Simplify some traits in ast_ids.rs (#14379) 2024-11-16 17:22:23 +00:00
Micha Reiser
a6a3d3f656 Fix file watcher panic when event has no paths (#14364) 2024-11-16 08:36:57 +01:00
Micha Reiser
c847cad389 Update insta snapshots (#14366) 2024-11-15 19:31:15 +01:00
Micha Reiser
81e5830585 Workspace discovery (#14308) 2024-11-15 19:20:15 +01:00
Micha Reiser
2b58705cc1 Remove the optional salsa dependency from the AST crate (#14363) 2024-11-15 16:46:04 +00:00
David Peter
9f3235a37f [red-knot] Expand test corpus (#14360)
## Summary

- Add 383 files from `crates/ruff_python_parser/resources` to the test
corpus
- Add 1296 files from `crates/ruff_linter/resources` to the test corpus
- Use in-memory file system for tests
- Improve test isolation by cleaning the test environment between checks
- Add a mechanism for "known failures". Mark ~80 files as known
failures.
- The corpus test is now a lot slower (6 seconds).

Note:
While `red_knot` as a command line tool can run over all of these
files without panicking, we still have a lot of test failures caused by
explicitly "pulling" all types.

## Test Plan

Run `cargo test -p red_knot_workspace` while making sure that
- Introducing code that is known to lead to a panic fails the test
- Removing code that is known to lead to a panic from
`KNOWN_FAILURES`-files also fails the test
2024-11-15 17:09:15 +01:00
Alex Waygood
62d650226b [red-knot] Derive more Default methods (#14361) 2024-11-15 13:15:41 +00:00
David Peter
5d8a391a3e [red-knot] Mark LoggingGuard as must_use (#14356) 2024-11-15 12:47:25 +01:00
Dhruv Manilawala
ed7b98cf9b Bump version to 0.7.4 (#14358) 2024-11-15 11:17:32 +00:00
Shaygan Hooshyari
6591775cd9 [flake8-type-checking] Skip quoting annotation if it becomes invalid syntax (TCH001) (#14285)
Fix: #13934 

## Summary

Current implementation has a bug when the current annotation contains a
string with single and double quotes.

TL;DR: I think these cases happen less than other use cases of Literal.
So instead of fixing them we skip the fix in those cases.

One of the problematic cases:

```
from typing import Literal
from third_party import Type

def error(self, type1: Type[Literal["'"]]):
    pass
```

The outcome is:

```
- def error(self, type1: Type[Literal["'"]]):
+ def error(self, type1: "Type[Literal[''']]"):
```

While it should be:

```
"Type[Literal['\'']"
```

The solution in this case is that we check if there’s any quotes same as
the quote style we want to use for this Literal parameter then escape
that same quote used in the string.

Also this case is not uncommon to have:
<https://grep.app/search?current=2&q=Literal["'>

But this can get more complicated for example in case of:

```
- def error(self, type1: Type[Literal["\'"]]):
+ def error(self, type1: "Type[Literal[''']]"):
```

Here we escaped the inner quote but in the generated annotation it gets
removed. Then we flip the quote style of the Literal paramter and the
formatting is wrong.

In this case the solution is more complicated.
1. When generating the string of the source code preserve the backslash.
2. After we have the annotation check if there isn’t any escaped quote
of the same type we want to use for the Literal parameter. In this case
check if we have any `’` without `\` before them. This can get more
complicated since there can be multiple backslashes so checking for only
`\’` won’t be enough.

Another problem is when the string contains `\n`. In case of
`Type[Literal["\n"]]` we generate `'Type[Literal["\n"]]'` and both
pyright and mypy reject this annotation.

https://pyright-play.net/?code=GYJw9gtgBALgngBwJYDsDmUkQWEMoAySMApiAIYA2AUAMaXkDOjUAKoiQNqsC6AXFAB0w6tQAmJYLBKMYAfQCOAVzCk5tMChjlUjOQCNytANaMGjABYAKRiUrAANLA4BGAQHJ2CLkVIVKnABEADoogTw87gCUfNRQ8VAITIyiElKksooqahpaOih6hiZmTNa29k7w3m5sHJy%2BZFRBoeE8MXEJScxAA

## Test Plan

I added test cases for the original code in the reported issue and two
more cases for backslash and new line.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-11-15 11:11:46 +00:00
Dhruv Manilawala
1f82731856 Use CWD to resolve settings from ruff.configuration (#14352)
## Summary

This PR fixes a bug in the Ruff language server where the
editor-specified configuration was resolved relative to the
configuration directory and not the current working directory.

The existing behavior is confusing given that this config file is
specified by the user and is not _discovered_ by Ruff itself. The
behavior of resolving this configuration file should be similar to that
of the `--config` flag on the command-line which uses the current
working directory:
3210f1a23b/crates/ruff/src/resolve.rs (L34-L48)

This creates problems where certain configuration options doesn't work
because the paths resolved in that case are relative to the
configuration directory and not the current working directory in which
the editor is expected to be in. For example, the
`lint.per-file-ignores` doesn't work as mentioned in the linked issue
along with `exclude`, `extend-exclude`, etc.

fixes: #14282 

## Test Plan

Using the following directory tree structure:
```
.
├── .config
│   └── ruff.toml
└── src
    └── migrations
        └── versions
            └── a.py
```

where, the `ruff.toml` is:
```toml
# 1. Comment this out to test `per-file-ignores`
extend-exclude = ["**/versions/*.py"]

[lint]
select = ["D"]

# 2. Comment this out to test `extend-exclude`
[lint.per-file-ignores]
"**/versions/*.py" = ["D"]

# 3. Comment both `per-file-ignores` and `extend-exclude` to test selection works
```

And, the content of `a.py`:
```py
"""Test"""
```

And, the VS Code settings:
```jsonc
{
  "ruff.nativeServer": "on",
  "ruff.path": ["/Users/dhruv/work/astral/ruff/target/debug/ruff"],
  // For single-file mode where current working directory is `/`
  // "ruff.configuration": "/tmp/ruff-repro/.config/ruff.toml",
  // When a workspace is opened containing this path
  "ruff.configuration": "./.config/ruff.toml",
  "ruff.trace.server": "messages",
  "ruff.logLevel": "trace"
}
```

I also tested out just opening the file in single-file mode where the
current working directory is `/` in VS Code. Here, the
`ruff.configuration` needs to be updated to use absolute path as shown
in the above VS Code settings.
2024-11-15 13:45:00 +05:30
Dhruv Manilawala
874da9c400 [red-knot] Display raw characters for string literal (#14351)
## Summary

Closes: #14330 

| `main` | PR |
|--------|--------|
| <img width="693" alt="Screenshot 2024-11-15 at 9 41 09 AM"
src="https://github.com/user-attachments/assets/0d10f2be-2155-4387-8d39-eb1b5027cfd4">
| <img width="800" alt="Screenshot 2024-11-15 at 9 40 27 AM"
src="https://github.com/user-attachments/assets/ba68911c-f4bf-405a-a597-44207b4bde7a">
|


## Test Plan

Add test cases for escape and quote characters.
2024-11-15 13:44:04 +05:30
github-actions[bot]
375cead202 Sync vendored typeshed stubs (#14350) 2024-11-14 22:29:29 -08:00
Dhruv Manilawala
9ec690b8f8 [red-knot] Add support for string annotations (#14151)
## Summary

This PR adds support for parsing and inferring types within string
annotations.

### Implementation (attempt 1)

This is preserved in
6217f48924.

The implementation here would separate the inference of string
annotations in the deferred query. This requires the following:
* Two ways of evaluating the deferred definitions - lazily and eagerly. 
* An eager evaluation occurs right outside the definition query which in
this case would be in `binding_ty` and `declaration_ty`.
* A lazy evaluation occurs on demand like using the
`definition_expression_ty` to determine the function return type and
class bases.
* The above point means that when trying to get the binding type for a
variable in an annotated assignment, the definition query won't include
the type. So, it'll require going through the deferred query to get the
type.

This has the following limitations:
* Nested string annotations, although not necessarily a useful feature,
is difficult to implement unless we convert the implementation in an
infinite loop
* Partial string annotations require complex layout because inferring
the types for stringified and non-stringified parts of the annotation
are done in separate queries. This means we need to maintain additional
information

### Implementation (attempt 2)

This is the final diff in this PR.

The implementation here does the complete inference of string annotation
in the same definition query by maintaining certain state while trying
to infer different parts of an expression and take decisions
accordingly. These are:
* Allow names that are part of a string annotation to not exists in the
symbol table. For example, in `x: "Foo"`, if the "Foo" symbol is not
defined then it won't exists in the symbol table even though it's being
used. This is an invariant which is being allowed only for symbols in a
string annotation.
* Similarly, lookup name is updated to do the same and if the symbol
doesn't exists, then it's not bounded.
* Store the final type of a string annotation on the string expression
itself and not for any of the sub-expressions that are created after
parsing. This is because those sub-expressions won't exists in the
semantic index.

Design document:
https://www.notion.so/astral-sh/String-Annotations-12148797e1ca801197a9f146641e5b71?pvs=4

Closes: #13796 

## Test Plan

* Add various test cases in our markdown framework
* Run `red_knot` on LibCST (contains a lot of string annotations,
specifically
https://github.com/Instagram/LibCST/blob/main/libcst/matchers/_matcher_base.py),
FastAPI (good amount of annotated code including `typing.Literal`) and
compare against the `main` branch output
2024-11-15 04:10:18 +00:00
Carl Meyer
a48d779c4e [red-knot] function signature representation (#14304)
## Summary

Add a typed representation of function signatures (parameters and return
type) and infer it correctly from a function.

Convert existing usage of function return types to use the signature
representation.

This does not yet add inferred types for parameters within function body
scopes based on the annotations, but it should be easy to add as a next
step.

Part of #14161 and #13693.

## Test Plan

Added tests.
2024-11-14 23:34:24 +00:00
Dylan
ba6c7f6897 [pylint] Remove check for dot in alias name in useless-import-alias (PLC0414) (#14345)
Follow-up to #14287 : when checking that `name` is the same as `as_name`
in `import name as as_name`, we do not need to first do an early return
if `'.'` is found in `name`.
2024-11-14 16:26:50 -06:00
Dylan
8095ff0e55 enforce required imports even with useless alias (#14287)
This PR handles a panic that occurs when applying unsafe fixes if a user
inserts a required import (I002) that has a "useless alias" in it, like
`import numpy as numpy`, and also selects PLC0414 (useless-import-alias)

In this case, the fixes alternate between adding the required import
statement, then removing the alias, until the recursion limit is
reached. See linked issue for an example.

Closes #14283

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-14 15:39:38 -06:00
Micha Reiser
24cd592a1d Avoid module lookup for known classes when possible (#14343) 2024-11-14 20:24:12 +00:00
Simon Brugman
a40bc6a460 [ruff] Implement none-not-at-end-of-union (RUF036) (#14314) 2024-11-14 19:37:13 +01:00
Alex Waygood
577de6c599 [red-knot] Clarify a TODO comment in a sys.version_info test (#14340) 2024-11-14 17:22:43 +00:00
InSync
d8b1afbc6e [ruff] Also report problems for attrs dataclasses in preview mode (RUF008, RUF009) (#14327)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-14 15:13:49 +00:00
David Peter
9a3001b571 [red-knot] Do not attach diagnostics to wrong file (#14337)
## Summary

Avoid attaching diagnostics to the wrong file. See related issue for
details.

Closes #14334

## Test Plan

New regression test.
2024-11-14 15:39:51 +01:00
Pierre GIRAUD
ec2c7cad0e Improve docs for ALE plugin for vim (#14335)
2 different fixers are available in ALE :
- ruff which runs `ruff check --fix` command (useful for example when
isort is enabled in lint config),
 - ruff_format which runs `run format` command.

The documentation was missing `ruff` as a possible fixer in ALE.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-11-14 13:01:34 +00:00
Shaygan Hooshyari
924741cb11 [red-knot] Infer unary not operation for instances (#13827)
Handle unary `not` on instances by calling the `__bool__` dunder.

## Test Plan

Added a new test case with some examples from these resources:

- https://docs.python.org/3/library/stdtypes.html#truth-value-testing
- <https://docs.python.org/3/reference/datamodel.html#object.__len__>
- <https://docs.python.org/3/reference/datamodel.html#object.__bool__>

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-11-13 23:31:36 +00:00
David Peter
77e8da7497 [red-knot] Avoid panics for ipython magic commands (#14326)
## Summary

Avoids panics when encountering Jupyter notebooks with [IPython magic
commands](https://ipython.readthedocs.io/en/stable/interactive/magics.html).

## Test Plan

Added Jupyter notebook to corpus.
2024-11-13 20:58:08 +01:00
David Peter
5e64863895 [red-knot] Handle invalid assignment targets (#14325)
## Summary

This fixes several panics related to invalid assignment targets. All of
these led to some a crash, previously:
```py
(x.y := 1)  # only name-expressions are valid targets of named expressions
([x, y] := [1, 2])  # same
(x, y): tuple[int, int] = (2, 3)  # tuples are not valid targets for annotated assignments
(x, y) += 2  # tuples are not valid targets for augmented assignments
```

closes #14321
closes #14322

## Test Plan

I symlinked four files from `crates/ruff_python_parser/resources` into
the red knot corpus, as they seemed like ideal test files for this exact
scenario. I think eventually, it might be a good idea to simply include *all*
invalid-syntax examples from the parser tests into red knots corpus (I believe
we're actually not too far from that goal). Or expand the scope of the corpus
test to this directory. Then we can get rid of these symlinks again.
2024-11-13 20:50:39 +01:00
Alex Waygood
78e4753d74 Remove unused flags and functions from the semantic model (#14318) 2024-11-13 17:35:48 +00:00
Simon Brugman
eb55b9b5a0 [flake8-pyi] Always autofix duplicate-union-members (PYI016) (#14270) 2024-11-13 16:42:06 +00:00
David Peter
0eb36e4345 [red-knot] Avoid panic for generic type aliases (#14312)
## Summary

This avoids a panic inside `TypeInferenceBuilder::infer_type_parameters`
when encountering generic type aliases:
```py
type ListOrSet[T] = list[T] | set[T]
```

To fix this properly, we would have to treat type aliases as being their own
annotation scope [1]. The left hand side is a definition for the type parameter
`T` which is being used in the special annotation scope on the right hand side.
Similar to how it works for generic functions and classes.

[1] https://docs.python.org/3/reference/compound_stmts.html#generic-type-aliases


closes #14307

## Test Plan

Added new example to the corpus.
2024-11-13 16:01:15 +01:00
Carl Meyer
5fcf0afff4 [red-knot] simplify type lookup in function/class definitions (#14303)
When we look up the types of class bases or keywords (`metaclass`), we
currently do this little dance: if there are type params, then look up
the type using `SemanticModel` in the type-params scope, if not, look up
the type directly in the definition's own scope, with support for
deferred types.

With inference of function parameter types, I'm now adding another case
of this same dance, so I'm motivated to make it a bit more ergonomic.

Add support to `definition_expression_ty` to handle any sub-expression
of a definition, whether it is in the definition's own scope or in a
type-params sub-scope.

Related to both #13693 and #14161.
2024-11-13 13:53:56 +00:00
David Peter
b946cfd1f7 [red-knot] Use memory address as AST node key (#14317)
## Summary

Use the memory address to uniquely identify AST nodes, instead of
relying on source range and kind. The latter fails for ASTs resulting
from invalid syntax examples. See #14313 for details.

Also results in a 1-2% speedup
(https://codspeed.io/astral-sh/ruff/runs/67349cf55f36b36baa211360)

closes #14313 

## Review

Here are the places where we use `NodeKey` directly or indirectly (via
`ExpressionNodeKey` or `DefinitionNodeKey`):

```rs
// semantic_index.rs
pub(crate) struct SemanticIndex<'db> { 
    // [...]
    /// Map expressions to their corresponding scope.
    scopes_by_expression: FxHashMap<ExpressionNodeKey, FileScopeId>,

    /// Map from a node creating a definition to its definition.
    definitions_by_node: FxHashMap<DefinitionNodeKey, Definition<'db>>,

    /// Map from a standalone expression to its [`Expression`] ingredient.
    expressions_by_node: FxHashMap<ExpressionNodeKey, Expression<'db>>,
    // [...]
}

// semantic_index/builder.rs
pub(super) struct SemanticIndexBuilder<'db> {
    // [...]
    scopes_by_expression: FxHashMap<ExpressionNodeKey, FileScopeId>,
    definitions_by_node: FxHashMap<ExpressionNodeKey, Definition<'db>>,
    expressions_by_node: FxHashMap<ExpressionNodeKey, Expression<'db>>,
}

// semantic_index/ast_ids.rs
pub(crate) struct AstIds {
    /// Maps expressions to their expression id.
    expressions_map: FxHashMap<ExpressionNodeKey, ScopedExpressionId>,
    /// Maps expressions which "use" a symbol (that is, [`ast::ExprName`]) to a use id.
    uses_map: FxHashMap<ExpressionNodeKey, ScopedUseId>,
}

pub(super) struct AstIdsBuilder {
    expressions_map: FxHashMap<ExpressionNodeKey, ScopedExpressionId>,
    uses_map: FxHashMap<ExpressionNodeKey, ScopedUseId>,
}
```

## Test Plan

Added two failing examples to the corpus.
2024-11-13 14:35:54 +01:00
Charlie Marsh
95c8f5fd0f Document comment policy around fix safety (#14300)
## Summary

Closes https://github.com/astral-sh/ruff/issues/9790.
2024-11-13 08:03:58 -05:00
David Salvisberg
89aa804b2d [flake8-type-checking] Fix false positives for typing.Annotated (#14311) 2024-11-13 12:17:52 +00:00
InSync
f789b12705 [flake8-logging] Root logger calls (LOG015) (#14302) 2024-11-13 09:11:55 +00:00
David Peter
3e36a7ab81 [red-knot] Fix assertion for invalid match pattern (#14306)
## Summary

Fixes a failing debug assertion that triggers for the following code:
```py
match some_int:
    case x:=2:
        pass
```

closes #14305

## Test Plan

Added problematic code example to corpus.
2024-11-13 10:07:29 +01:00
InSync
5c548dcc04 [flake8-datetimez] Usages of datetime.max/datetime.min (DTZ901) (#14288)
## Summary

Resolves #13217.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-12 20:36:07 +00:00
Simon Brugman
bd30701980 [flake8-pyi] Improve autofix for nested and mixed type unions unnecessary-type-union (PYI055) (#14272)
## Summary

This PR improves the fix for `PYI055` to be able to handle nested and
mixed type unions.

It also marks the fix as unsafe when comments are present. 
 
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
2024-11-12 15:33:51 -05:00
Harutaka Kawamura
2b6d66b793 Fix pytest-raises-too-broad (PT011) to flag pytest.raises call with keyword expected_exception (#14298)
## Summary

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

`pytest-raises-too-broad (PT011)` should be raised when
`expected_exception` is provided as a keyword argument.

```python
def test_foo():
    with pytest.raises(ValueError):  # raises PT011
        raise ValueError("Can't divide 1 by 0")

    # This is minor but a valid pytest.raises call
    with pytest.raises(expected_exception=ValueError):  # doesn't raise PT011 but should
        raise ValueError("Can't divide 1 by 0")
```

`pytest.raises` doc:
https://docs.pytest.org/en/8.3.x/reference/reference.html#pytest.raises

## Test Plan

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

Unit tests

Signed-off-by: harupy <hkawamura0130@gmail.com>
2024-11-12 14:28:42 -05:00
Charlie Marsh
147ea399fd Remove extraneous baz.py file (#14299) 2024-11-12 14:01:19 +00:00
David Peter
907047bf4b [red-knot] Add tests for member lookup on union types (#14296)
## Summary

- Write tests for member lookups on union types
- Remove TODO comment

part of: #14022

## Test Plan

New MD tests
2024-11-12 14:11:55 +01:00
InSync
13a1483f1e [flake8-pyi] Add "replace with Self" fix (PYI019) (#14238)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-12 11:13:15 +00:00
InSync
be69f61b3e [flake8-simplify] Infer "unknown" truthiness for literal iterables whose items are all unpacks (SIM222) (#14263)
## Summary

Resolves #14237.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-11-11 15:23:34 -05:00
David Peter
f1f3bd1cd3 [red-knot] Review remaining 'possibly unbound' call sites (#14284)
## Summary

- Emit diagnostics when looking up (possibly) unbound attributes
- More explicit test assertions for unbound symbols
- Review remaining call sites of `Symbol::ignore_possibly_unbound`. Most
of them are something like `builtins_symbol(self.db,
"Ellipsis").ignore_possibly_unbound().unwrap_or(Type::Unknown)` which
look okay to me, unless we want to emit additional diagnostics. There is
one additional case in enum literal handling, which has a TODO comment
anyway.

part of #14022

## Test Plan

New MD tests for (possibly) unbound attributes.
2024-11-11 20:48:49 +01:00
David Peter
3bef23669f [red-knot] Diagnostic for possibly unbound imports (#14281)
## Summary

This adds a new diagnostic when possibly unbound symbols are imported.
The `TODO` comment had a question mark, do I'm not sure if this is
really something that we want.

This does not touch the un*declared* case, yet.

relates to: #14022

## Test Plan

Updated already existing tests with new diagnostics
2024-11-11 20:26:01 +01:00
David Salvisberg
f82ee8ea59 [flake8-markupsafe] Adds Implementation for MS001 via RUF035 (#14224)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-11 18:30:03 +00:00
David Peter
b8a65182dd [red-knot] Symbol API improvements, part 2 (#14276)
## Summary

Apart from one small functional change, this is mostly a refactoring of
the `Symbol` API:

- Rename `as_type` to the more explicit `ignore_possibly_unbound`, no
functional change
- Remove `unwrap_or_unknown` in favor of the more explicit
`.ignore_possibly_unbound().unwrap_or(Type::Unknown)`, no functional
change
- Consistently call it "possibly unbound" (not "may be unbound")
- Rename `replace_unbound_with` to `or_fall_back_to` and properly handle
boundness of the fall back. This is the only functional change (did not
have any impact on existing tests).

relates to: #14022

## Test Plan

New unit tests for `Symbol::or_fall_back_to`

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-11 15:24:27 +01:00
Alex Waygood
fc15d8a3bd [red-knot] Infer Literal types from comparisons with sys.version_info (#14244) 2024-11-11 13:58:16 +00:00
Simon Brugman
b3b5c19105 Minor refactoring of some flake-pyi rules (#14275)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-11 13:10:48 +00:00
Simon Brugman
f8aae9b1d6 [flake8-pyi] Mark fix as unsafe when type annotation contains comments for duplicate-literal-member (PYI062) (#14268) 2024-11-11 12:48:14 +00:00
Alex Waygood
9180635171 [red-knot] Cleanup some KnownClass APIs (#14269) 2024-11-11 11:54:42 +00:00
Alex Waygood
3ef4b3bf32 [red-knot] Shorten the paths for some mdtest files (#14267) 2024-11-11 11:34:33 +00:00
w0nder1ng
5a3886c8b5 [perflint] implement quick-fix for manual-list-comprehension (PERF401) (#13919)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-11 11:17:02 +00:00
Alex Waygood
813ec23ecd [red-knot] Improve mdtest output (#14213) 2024-11-11 11:03:41 +00:00
Dhruv Manilawala
13883414af Add "Notebook behavior" section for F704, PLE1142 (#14266)
## Summary

Move the relevant contents into "Notebook behavior" section similar to
other rules.
2024-11-11 10:54:28 +00:00
Simon Brugman
84d4f114ef Use bitshift consistently for bitflag definitions (#14265) 2024-11-11 10:20:17 +00:00
renovate[bot]
1c586b29e2 Update dependency mkdocs-redirects to v1.2.2 (#14252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 10:11:22 +00:00
renovate[bot]
d76a8518c2 Update dependency uuid to v11.0.3 (#14254)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 10:11:12 +00:00
renovate[bot]
5f0ee2670a Update cloudflare/wrangler-action action to v3.12.1 (#14261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 09:56:12 +00:00
renovate[bot]
f8ca6c3316 Update NPM Development dependencies (#14259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 09:55:43 +00:00
renovate[bot]
ba7b023f26 Update Rust crate tempfile to v3.14.0 (#14260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 09:48:51 +00:00
renovate[bot]
e947d163b2 Update Rust crate thiserror to v2 (#14262)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-11-11 09:46:09 +00:00
renovate[bot]
1cf4d2ff69 Update dependency ruff to v0.7.3 (#14253)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Preview features

- Formatter: Disallow single-line implicit concatenated strings
([#&#8203;13928](https://redirect.github.com/astral-sh/ruff/pull/13928))
- \[`flake8-pyi`] Include all Python file types for `PYI006` and
`PYI066`
([#&#8203;14059](https://redirect.github.com/astral-sh/ruff/pull/14059))
- \[`flake8-simplify`] Implement `split-of-static-string` (`SIM905`)
([#&#8203;14008](https://redirect.github.com/astral-sh/ruff/pull/14008))
- \[`refurb`] Implement `subclass-builtin` (`FURB189`)
([#&#8203;14105](https://redirect.github.com/astral-sh/ruff/pull/14105))
- \[`ruff`] Improve diagnostic messages and docs (`RUF031`, `RUF032`,
`RUF034`)
([#&#8203;14068](https://redirect.github.com/astral-sh/ruff/pull/14068))

##### Rule changes

- Detect items that hash to same value in duplicate sets (`B033`,
`PLC0208`)
([#&#8203;14064](https://redirect.github.com/astral-sh/ruff/pull/14064))
- \[`eradicate`] Better detection of IntelliJ language injection
comments (`ERA001`)
([#&#8203;14094](https://redirect.github.com/astral-sh/ruff/pull/14094))
- \[`flake8-pyi`] Add autofix for `docstring-in-stub` (`PYI021`)
([#&#8203;14150](https://redirect.github.com/astral-sh/ruff/pull/14150))
- \[`flake8-pyi`] Update `duplicate-literal-member` (`PYI062`) to alawys
provide an autofix
([#&#8203;14188](https://redirect.github.com/astral-sh/ruff/pull/14188))
- \[`pyflakes`] Detect items that hash to same value in duplicate
dictionaries (`F601`)
([#&#8203;14065](https://redirect.github.com/astral-sh/ruff/pull/14065))
- \[`ruff`] Fix false positive for decorators (`RUF028`)
([#&#8203;14061](https://redirect.github.com/astral-sh/ruff/pull/14061))

##### Bug fixes

- Avoid parsing joint rule codes as distinct codes in `# noqa`
([#&#8203;12809](https://redirect.github.com/astral-sh/ruff/pull/12809))
- \[`eradicate`] ignore `# language=` in commented-out-code rule
(ERA001)
([#&#8203;14069](https://redirect.github.com/astral-sh/ruff/pull/14069))
- \[`flake8-bugbear`] - do not run `mutable-argument-default` on stubs
(`B006`)
([#&#8203;14058](https://redirect.github.com/astral-sh/ruff/pull/14058))
- \[`flake8-builtins`] Skip lambda expressions in
`builtin-argument-shadowing (A002)`
([#&#8203;14144](https://redirect.github.com/astral-sh/ruff/pull/14144))
- \[`flake8-comprehension`] Also remove trailing comma while fixing
`C409` and `C419`
([#&#8203;14097](https://redirect.github.com/astral-sh/ruff/pull/14097))
- \[`flake8-simplify`] Allow `open` without context manager in `return`
statement (`SIM115`)
([#&#8203;14066](https://redirect.github.com/astral-sh/ruff/pull/14066))
- \[`pylint`] Respect hash-equivalent literals in `iteration-over-set`
(`PLC0208`)
([#&#8203;14063](https://redirect.github.com/astral-sh/ruff/pull/14063))
- \[`pylint`] Update known dunder methods for Python 3.13 (`PLW3201`)
([#&#8203;14146](https://redirect.github.com/astral-sh/ruff/pull/14146))
- \[`pyupgrade`] - ignore kwarg unpacking for `UP044`
([#&#8203;14053](https://redirect.github.com/astral-sh/ruff/pull/14053))
- \[`refurb`] Parse more exotic decimal strings in
`verbose-decimal-constructor` (`FURB157`)
([#&#8203;14098](https://redirect.github.com/astral-sh/ruff/pull/14098))

##### Documentation

- Add links to missing related options within rule documentations
([#&#8203;13971](https://redirect.github.com/astral-sh/ruff/pull/13971))
- Add rule short code to mkdocs tags to allow searching via rule codes
([#&#8203;14040](https://redirect.github.com/astral-sh/ruff/pull/14040))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS43LjEiLCJ1cGRhdGVkSW5WZXIiOiIzOS43LjEiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImludGVybmFsIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 08:17:22 +00:00
renovate[bot]
2308522f38 Update pre-commit dependencies (#14256)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.7.2` -> `v0.7.3` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | patch | `v1.27.0` -> `v1.27.3` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

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

###
[`v0.7.3`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.7.3)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.7.3)

See: https://github.com/astral-sh/ruff/releases/tag/0.7.3

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.27.3`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.27.3)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.27.2...v1.27.3)

#### \[1.27.3] - 2024-11-08

##### Fixes

-   Don't correct `alloced`
- Don't correct `registor`, a more domain specific variant of `register`

###
[`v1.27.2`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.27.2)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.27.1...v1.27.2)

#### \[1.27.2] - 2024-11-06

##### Fixes

-   Correct `fand`

###
[`v1.27.1`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.27.1)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.27.0...v1.27.1)

#### \[1.27.1] - 2024-11-06

##### Fixes

-   Correct `alingment` as `alignment`, rather than `alinement`

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOS43LjEiLCJ1cGRhdGVkSW5WZXIiOiIzOS43LjEiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImludGVybmFsIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 08:17:07 +00:00
David Peter
438f3d967b [red-knot] is_disjoint_from: tests for function/module literals (#14264)
## Summary

Add unit tests for `is_disjoint_from` for function and module literals
as a follow-up to #14210.

Ref: https://github.com/astral-sh/ruff/pull/14210/files#r1835069885
2024-11-11 09:14:26 +01:00
Charlie Marsh
5bf4759cff Detect permutations in redundant open modes (#14255)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14235.
2024-11-10 22:48:30 -05:00
renovate[bot]
2e9e96338e Update Rust crate url to v2.5.3 (#14251) 2024-11-10 19:47:14 -05:00
renovate[bot]
5fa7ace1f5 Update Rust crate matchit to v0.8.5 (#14250) 2024-11-10 19:47:09 -05:00
renovate[bot]
704868ca83 Update Rust crate libc to v0.2.162 (#14249) 2024-11-10 19:47:02 -05:00
renovate[bot]
dc71c8a484 Update Rust crate hashbrown to v0.15.1 (#14247) 2024-11-10 19:46:55 -05:00
renovate[bot]
2499297392 Update Rust crate is-macro to v0.3.7 (#14248) 2024-11-10 19:46:48 -05:00
renovate[bot]
7b9189bb2c Update Rust crate anyhow to v1.0.93 (#14246) 2024-11-10 19:46:40 -05:00
Harutaka Kawamura
d4cf61d98b Implement shallow-copy-environ / W1507 (#14241)
<!--
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? -->

Related to #970. Implement [`shallow-copy-environ /
W1507`](https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/shallow-copy-environ.html).

## Test Plan

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

Unit test

---------

Co-authored-by: Simon Brugman <sbrugman@users.noreply.github.com>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-10 22:58:02 +00:00
Randolf Scholz
5d91ba0b10 FBT001: exclude boolean operators (#14203)
Fixes #14202

## Summary

Exclude rule FBT001 for boolean operators.

## Test Plan

Updated existing `FBT.py` test.
2024-11-10 22:40:37 +00:00
Carl Meyer
a7e9f0c4b9 [red-knot] follow-ups to typevar types (#14232) 2024-11-09 20:18:32 -08:00
Charlie Marsh
c7d48e10e6 Detect empty implicit namespace packages (#14236)
## Summary

The implicit namespace package rule currently fails to detect cases like
the following:

```text
foo/
├── __init__.py
└── bar/
    └── baz/
        └── __init__.py
```

The problem is that we detect a root at `foo`, and then an independent
root at `baz`. We _would_ detect that `bar` is an implicit namespace
package, but it doesn't contain any files! So we never check it, and
have no place to raise the diagnostic.

This PR adds detection for these kinds of nested packages, and augments
the `INP` rule to flag the `__init__.py` file above with a specialized
message. As a side effect, I've introduced a dedicated `PackageRoot`
struct which we can pass around in lieu of Yet Another `Path`.

For now, I'm only enabling this in preview (and the approach doesn't
affect any other rules). It's a bug fix, but it may end up expanding the
rule.

Closes https://github.com/astral-sh/ruff/issues/13519.
2024-11-09 22:03:34 -05:00
Charlie Marsh
94dee2a36d Avoid applying PEP 646 rewrites in invalid contexts (#14234)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14231.
2024-11-09 15:47:28 -05:00
Charlie Marsh
555a5c9319 [refurb] Avoid triggering hardcoded-string-charset for reordered sets (#14233)
## Summary

It's only safe to enforce the `x in "1234567890"` case if `x` is exactly
one character, since the set on the right has been reordered as compared
to `string.digits`. We can't know if `x` is exactly one character unless
it's a literal. And if it's a literal, well, it's kind of silly code in
the first place?

Closes https://github.com/astral-sh/ruff/issues/13802.
2024-11-09 15:31:26 -05:00
Charlie Marsh
1279c20ee1 Avoid using typing.Self in stub files pre-Python 3.11 (#14230)
## Summary

See:
https://github.com/astral-sh/ruff/pull/14217#discussion_r1835340869.

This means we're recommending `typing_extensions` in non-stubs pre-3.11,
which may not be a valid project dependency, but that's a separate issue
(https://github.com/astral-sh/ruff/issues/9761).
2024-11-09 13:17:36 -05:00
Charlie Marsh
ce3af27f59 Avoid treating lowercase letters as # noqa codes (#14229)
## Summary

An oversight from the original implementation.

Closes https://github.com/astral-sh/ruff/issues/14228.
2024-11-09 12:49:35 -05:00
Harutaka Kawamura
71da1d6df5 Fix await-outside-async to allow await at the top-level scope of a notebook (#14225)
## Summary

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

Fix `await-outside-async` to allow `await` at the top-level scope of a
notebook.

```python
# foo.ipynb

await asyncio.sleep(1)  # should be allowed
```

## Test Plan

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

A unit test
2024-11-09 12:44:48 -05:00
Alex Waygood
e598240f04 [red-knot] More Type constructors (#14227) 2024-11-09 16:57:11 +00:00
InSync
c9b84e2a85 [ruff] Do not report when Optional has no type arguments (RUF013) (#14181)
## Summary

Resolves #13833.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-09 08:48:56 -05:00
Alex Waygood
d3f1c8e536 [red-knot] Add Type constructors for Instance, ClassLiteral and SubclassOf variants (#14215)
## Summary

Reduces some repetetiveness and verbosity at callsites. Addresses
@carljm's review comments at
https://github.com/astral-sh/ruff/pull/14155/files#r1833252458

## Test Plan

`cargo test -p red_knot_python_semantic`
2024-11-09 09:10:00 +00:00
InSync
eea6b31980 [flake8-pyi] Add "replace with Self" fix (PYI034) (#14217)
## Summary

Resolves #14184.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2024-11-09 02:11:38 +00:00
Dylan
b8dc780bdc [refurb] Further special cases added to verbose-decimal-constructor (FURB157) (#14216)
This PR accounts for further subtleties in `Decimal` parsing:

- Strings which are empty modulo underscores and surrounding whitespace
are skipped
- `Decimal("-0")` is skipped
- `Decimal("{integer literal that is longer than 640 digits}")` are
skipped (see linked issue for explanation)

NB: The snapshot did not need to be updated since the new test cases are
"Ok" instances and added below the diff.

Closes #14204
2024-11-08 21:08:22 -05:00
Charlie Marsh
93fdf7ed36 Fix miscellaneous issues in await-outside-async detection (#14218)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14167.
2024-11-08 21:07:13 -05:00
Michal Čihař
b19f388249 [refurb] Use UserString instead of non-existent UserStr (#14209)
## Summary

The class name is UserString, not a UserStr, see
https://docs.python.org/3.9/library/collections.html#collections.UserString
2024-11-08 20:54:18 -05:00
Alex Waygood
de947deee7 [red-knot] Consolidate detection of cyclically defined classes (#14207) 2024-11-08 22:17:56 +00:00
Carl Meyer
c0c4ae14ac [red-knot] make KnownClass::is_singleton a const fn (#14211)
Follow-up from missed review comment on
https://github.com/astral-sh/ruff/pull/14182
2024-11-08 13:37:25 -08:00
Carl Meyer
645ce7e5ec [red-knot] infer types for PEP695 typevars (#14182)
## Summary

Create definitions and infer types for PEP 695 type variables.

This just gives us the type of the type variable itself (the type of `T`
as a runtime object in the body of `def f[T](): ...`), with special
handling for its attributes `__name__`, `__bound__`, `__constraints__`,
and `__default__`. Mostly the support for these attributes exists
because it is easy to implement and allows testing that we are
internally representing the typevar correctly.

This PR doesn't yet have support for interpreting a typevar as a type
annotation, which is of course the primary use of a typevar. But the
information we store in the typevar's type in this PR gives us
everything we need to handle it correctly in a future PR when the
typevar appears in an annotation.

## Test Plan

Added mdtest.
2024-11-08 21:23:05 +00:00
David Peter
1430f21283 [red-knot] Fix is_disjoint_from for class literals (#14210)
## Summary

`Ty::BuiltinClassLiteral(…)` is a sub~~class~~type of
`Ty::BuiltinInstance("type")`, so it can't be disjoint from it.

## Test Plan

New `is_not_disjoint_from` test case
2024-11-08 20:54:27 +01:00
Alex Waygood
953e862aca [red-knot] Improve error message for metaclass conflict (#14174) 2024-11-08 11:58:57 +00:00
Dhruv Manilawala
fbf140a665 Bump version to 0.7.3 (#14197) 2024-11-08 16:39:37 +05:30
David Peter
670f958525 [red-knot] Fix intersection simplification for ~Any/~Unknown (#14195)
## Summary

Another bug found using [property
testing](https://github.com/astral-sh/ruff/pull/14178).

## Test Plan

New unit test
2024-11-08 10:54:13 +01:00
David Peter
fed35a25e8 [red-knot] Fix is_assignable_to for unions (#14196)
## Summary

Fix `Type::is_assignable_to` for union types on the left hand side (of
`.is_assignable_to`; or the right hand side of the `… = …` assignment):

`Literal[1, 2]` should be assignable to `int`.

## Test Plan

New unit tests that were previously failing.
2024-11-08 10:53:48 +01:00
Simon Brugman
d1ef418bb0 Docs: tweak rules documentation (#14180)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-08 09:01:53 +00:00
Charlie Marsh
272d24bf3e [flake8-pyi] Add a fix for duplicate-literal-member (#14188)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14187.
2024-11-08 03:45:19 +00:00
David Peter
2624249219 [red-knot] Minor: fix Literal[True] <: int (#14177)
## Summary

Minor fix to `Type::is_subtype_of` to make sure that Boolean literals
are subtypes of `int`, to match runtime semantics.

Found this while doing some property-testing experiments [1].

[1] https://github.com/astral-sh/ruff/pull/14178

## Test Plan

New unit test.
2024-11-07 23:23:35 +01:00
Alex Waygood
4b08d17088 [red-knot] Add a new Type::KnownInstanceType variant (#14155)
## Summary

Fixes #14114. I don't think I can really describe the problems with our
current architecture (and therefore the motivations for this PR) any
better than @carljm did in that issue, so I'll just copy it out here!

---

We currently represent "known instances" (e.g. special forms like
`typing.Literal`, which are an instance of `typing._SpecialForm`, but
need to be handled differently from other instances of
`typing._SpecialForm`) as an `InstanceType` with a `known` field that is
`Some(...)`.

This makes it easy to handle a known instance as if it were a regular
instance type (by ignoring the `known` field), and in some cases (e.g.
`Type::member`) that is correct and convenient. But in other cases (e.g.
`Type::is_equivalent_to`) it is not correct, and we currently have a bug
that we would consider the known-instance type of `typing.Literal` as
equivalent to the general instance type for `typing._SpecialForm`, and
we would fail to consider it a singleton type or a single-valued type
(even though it is both.)

An instance type with `known.is_some()` is semantically quite different
from an instance type with `known.is_none()`. The former is a singleton
type that represents exactly one runtime object; the latter is an open
type that represents many runtime objects, including instances of
unknown subclasses. It is too error-prone to represent these
very-different types as a single `Type` variant. We should instead
introduce a dedicated `Type::KnownInstance` variant and force ourselves
to handle these explicitly in all `Type` variant matches.

## Possible followups

There is still a little bit of awkwardness in our current design in some
places, in that we first infer the symbol `typing.Literal` as a
`_SpecialForm` instance, and then later convert that instance-type into
a known-instance-type. We could also use this `KnownInstanceType` enum
to account for other special runtime symbols such as `builtins.Ellipsis`
or `builtins.NotImplemented`.

I think these might be worth pursuing, but I didn't do them here as they
didn't seem essential right now, and I wanted to keep the diff
relatively minimal.

## Test Plan

`cargo test -p red_knot_python_semantic`. New unit tests added for
`Type::is_subtype_of`.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-11-07 22:07:27 +00:00
David Peter
5b6169b02d [red-knot] Minor fix in intersection type comment (#14176)
## Summary

Minor fix in intersection type comment introduced in #14138
2024-11-07 20:23:06 +00:00
Simon Brugman
2040e93add [flake8-logging-format] Fix invalid formatting value in docs of logging-extra-attr-clash (G101) (#14165) 2024-11-07 21:00:05 +01:00
Simon Brugman
794eb886e4 [flake8-bandit] Typo in docs suspicious-pickle-import (S403) (#14175) 2024-11-07 20:59:18 +01:00
David Peter
57ba25caaf [red-knot] Type inference for comparisons involving intersection types (#14138)
## Summary

This adds type inference for comparison expressions involving
intersection types.

For example:
```py
x = get_random_int()

if x != 42:
    reveal_type(x == 42)  # revealed: Literal[False]
    reveal_type(x == 43)  # bool
```

closes #13854

## Test Plan

New Markdown-based tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-11-07 20:51:14 +01:00
David Peter
4f74db5630 [red-knot] Improve Symbol API for callable types (#14137)
## Summary

- Get rid of `Symbol::unwrap_or` (unclear semantics, not needed anymore)
- Introduce `Type::call_dunder`
- Emit new diagnostic for possibly-unbound `__iter__` methods
- Better diagnostics for callables with possibly-unbound /
possibly-non-callable `__call__` methods

part of: #14022 

closes #14016

## Test Plan

- Updated test for iterables with possibly-unbound `__iter__` methods.
- New tests for callables
2024-11-07 19:58:31 +01:00
Alex Waygood
adc4216afb Use Python 3.12 for fuzz-parser in CI (#14159) 2024-11-07 15:51:04 +00:00
Simon Brugman
fe8e49de9a [pyflakes] Typo in docs for if-tuple (F634) (#14158) 2024-11-07 15:28:20 +00:00
Alex Waygood
574eb3f4bd Upgrade locked dependencies for the fuzz-parser script (#14156) 2024-11-07 15:10:17 +00:00
Alex Waygood
311b0bdf9a [red-knot] Cleanup handling of InstanceTypes in a couple of places (#14154) 2024-11-07 14:08:31 +00:00
David Peter
f2546c562c [red-knot] Add narrowing for issubclass checks (#14128)
## Summary

- Adds basic support for `type[C]` as a red knot `Type`. Some things
  might not be supported yet, like `type[Any]`.
- Adds type narrowing for `issubclass` checks.

closes #14117 

## Test Plan

New Markdown-based tests

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-07 14:15:39 +01:00
Micha Reiser
59c0dacea0 Introduce Diagnostic trait (#14130) 2024-11-07 13:26:21 +01:00
InSync
b8188b2262 [flake8-pyi] Add autofix for docstring-in-stub (PYI021) (#14150)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-07 12:00:19 +00:00
Simon Brugman
136721e608 [refurb] Implement subclass-builtin (FURB189) (#14105)
## Summary

Implementation for one of the rules in
https://github.com/astral-sh/ruff/issues/1348
Refurb only deals only with classes with a single base, however the rule
is valid for any base.
(`str, Enum` is common prior to `StrEnum`)

## Test Plan

`cargo test`

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-11-07 17:26:19 +05:30
Dhruv Manilawala
5b500b838b Update known dunder methods for Python 3.13 (#14146)
## Summary

Closes: #14145
2024-11-07 11:39:00 +05:30
Dylan
cb003ebe22 [flake8-builtins] Skip lambda expressions in builtin-argument-shadowing (A002) (#14144)
Flake8-builtins provides two checks for arguments (really, parameters)
of a function shadowing builtins: A002 checks function definitions, and
A006 checks lambda expressions. This PR ensures that A002 is restricted
to functions rather than lambda expressions.

Closes #14135 .
2024-11-07 05:34:09 +00:00
Carl Meyer
03a5788aa1 [red-knot] a few metaclass cleanups (#14142)
Just cleaning up a few small things I noticed in post-land review.
2024-11-06 22:13:39 +00:00
Charlie Marsh
626f716de6 Add support for resolving metaclasses (#14120)
## Summary

I mirrored some of the idioms that @AlexWaygood used in the MRO work.

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

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-06 15:41:35 -05:00
InSync
46c5a13103 [eradicate] Better detection of IntelliJ language injection comments (ERA001) (#14094) 2024-11-06 18:24:15 +00:00
Micha Reiser
31681f66c9 Fix duplicate unpack diagnostics (#14125)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-06 11:28:29 +00:00
Micha Reiser
a56ee9268e Add mdtest support for files with invalid syntax (#14126) 2024-11-06 12:25:52 +01:00
Dhruv Manilawala
4ece8e5c1e Use "Ruff" instead of "uv" for src setting docs (#14121)
## Summary

From
15aa5a6d57
2024-11-06 03:19:32 +00:00
Dhruv Manilawala
34b6a9b909 Remove unpack field from SemanticIndexBuilder (#14101)
## Summary

Related to
https://github.com/astral-sh/ruff/pull/13979#discussion_r1828305790,
this PR removes the `current_unpack` state field from
`SemanticIndexBuilder` and passes the `Unpack` ingredient via the
`CurrentAssignment` -> `DefinitionNodeRef` conversion to finally store
it on `DefintionNodeKind`.

This involves updating the lifetime of `AnyParameterRef` (parameter to
`declare_parameter`) to use the `'db` lifetime. Currently, all AST nodes
stored on various enums are marked with `'a` lifetime but they're always
utilized using the `'db` lifetime.

This also removes the dedicated `'a` lifetime parameter on
`add_definition` which is currently being used in `DefinitionNodeRef`.
As mentioned, all AST nodes live through the `'db` lifetime so we can
remove the `'a` lifetime parameter from that method and use the `'db`
lifetime instead.
2024-11-06 08:42:58 +05:30
Alex Waygood
eead549254 [red-knot] Introduce a new ClassLiteralType struct (#14108) 2024-11-05 22:16:33 +00:00
Lokejoke
abafeb4bee Fix: Recover boolean test flag after visiting subexpressions (#13909)
Co-authored-by: xbrtnik1 <524841@mail.muni.cz>
2024-11-05 20:55:49 +01:00
Dylan
2b76fa8fa1 [refurb] Parse more exotic decimal strings in verbose-decimal-constructor (FURB157) (#14098)
FURB157 suggests replacing expressions like `Decimal("123")` with
`Decimal(123)`. This PR extends the rule to cover cases where the input
string to `Decimal` can be easily transformed into an integer literal.

For example:

```python
Decimal("1__000")   # fix: `Decimal(1000)`
```

Note: we do not implement the full decimal parsing logic from CPython on
the grounds that certain acceptable string inputs to the `Decimal`
constructor may be presumed purposeful on the part of the developer. For
example, as in the linked issue, `Decimal("١٢٣")` is valid and equal to
`Decimal(123)`, but we do not suggest a replacement in this case.

Closes #13807
2024-11-05 13:33:04 -06:00
David Peter
239cbc6f33 [red-knot] Store starred-expression annotation types (#14106)
## Summary

- Store the expression type for annotations that are starred expressions
(see [discussion
here](https://github.com/astral-sh/ruff/pull/14091#discussion_r1828332857))
- Use `self.store_expression_type(…)` consistently throughout, as it
makes sure that no double-insertion errors occur.

closes #14115

## Test Plan

Added an invalid-syntax example to the corpus which leads to a panic on
`main`. Also added a Markdown test with a valid-syntax example that
would lead to a panic once we implement function parameter inference.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-05 20:25:45 +01:00
David Peter
2296627528 [red-knot] Precise inference for identity checks (#14109)
## Summary

Adds more precise type inference for `… is …` and `… is not …` identity
checks in some limited cases where we statically know the answer to be
either `Literal[True]` or `Literal[False]`.

I found this helpful while working on type inference for comparisons
involving intersection types, but I'm not sure if this is at all useful
for real world code (where the answer is most probably *not* statically
known). Note that we already have *type narrowing* for identity tests.
So while we are already able to generate constraints for things like `if
x is None`, we can now — in some limited cases — make an even stronger
conclusion and infer that the test expression itself is `Literal[False]`
(branch never taken) or `Literal[True]` (branch always taken).

## Test Plan

New Markdown tests
2024-11-05 19:48:52 +01:00
Micha Reiser
05687285fe fix double inference of standalone expressions (#14107) 2024-11-05 15:50:31 +01:00
Alex Waygood
05f97bae73 types.rs: remove unused is_stdlib_symbol methods (#14104) 2024-11-05 12:46:17 +00:00
Micha Reiser
4323512a65 Remove AST-node dependency from FunctionType and ClassType (#14087)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-05 08:02:38 +00:00
Shaygan Hooshyari
9dddd73c29 [red-knot] Literal special form (#13874)
Handling `Literal` type in annotations.

Resolves: #13672 

## Implementation

Since Literals are not a fully defined type in typeshed. I used a trick
to figure out when a special form is a literal.
When we are inferring assignment types I am checking if the type of that
assignment was resolved to typing.SpecialForm and the name of the target
is `Literal` if that is the case then I am re creating a new instance
type and set the known instance field to `KnownInstance:Literal`.

**Why not defining a new type?**

From this [issue](https://github.com/python/typeshed/issues/6219) I
learned that we want to resolve members to SpecialMethod class. So if we
create a new instance here we can rely on the member resolving in that
already exists.


## Tests


https://typing.readthedocs.io/en/latest/spec/literal.html#equivalence-of-two-literals
Since the type of the value inside Literal is evaluated as a
Literal(LiteralString, LiteralInt, ...) then the equality is only true
when types and value are equal.


https://typing.readthedocs.io/en/latest/spec/literal.html#legal-and-illegal-parameterizations

The illegal parameterizations are mostly implemented I'm currently
checking the slice expression and the slice type to make sure it's
valid.

https://typing.readthedocs.io/en/latest/spec/literal.html#shortening-unions-of-literals

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-05 01:45:46 +00:00
TomerBin
6c56a7a868 [red-knot] Implement type narrowing for boolean conditionals (#14037)
## Summary

This PR enables red-knot to support type narrowing based on `and` and
`or` conditionals, including nested combinations and their negation (for
`elif` / `else` blocks and for `not` operator). Part of #13694.

In order to address this properly (hopefully 😅), I had to run
`NarrowingConstraintsBuilder` functions recursively. In the first commit
I introduced a minor refactor - instead of mutating `self.constraints`,
the new constraints are now returned as function return values. I also
modified the constraints map to be optional, preventing unnecessary
hashmap allocations.
Thanks @carljm for your support on this :)

The second commit contains the logic and tests for handling boolean ops,
with improvements to intersections handling in `is_subtype_of` .

As I'm still new to Rust and the internals of type checkers, I’d be more
than happy to hear any insights or suggestions.
Thank you!

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-11-04 22:54:35 +00:00
InSync
bb25bd9c6c Also remove trailing comma while fixing C409 and C419 (#14097) 2024-11-04 20:33:30 +00:00
Simon Brugman
b7e32b0a18 Re-enable clippy useless-format (#14095) 2024-11-04 18:25:25 +01:00
Simon Brugman
fb94b71e63 Derive message formats macro support to string (#14093) 2024-11-04 18:06:25 +01:00
Micha Reiser
bc0586d922 Avoid cloning Name when looking up function and class types (#14092) 2024-11-04 15:52:59 +01:00
Simon Brugman
a7a78f939c Replace format! without parameters with .to_string() (#14090)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-04 14:09:30 +00:00
David Peter
6dabf045c3 [red-knot] Do not panic when encountering string annotations (#14091)
## Summary

Encountered this while running red-knot benchmarks on the `black`
codebase.

Fixes two of the issues in #13478.

## Test Plan

Added a regression test.
2024-11-04 15:06:54 +01:00
Alex Waygood
df45a0e3f9 [red-knot] Add MRO resolution for classes (#14027) 2024-11-04 13:31:38 +00:00
David Peter
88d9bb191b [red-knot] Remove Type::None (#14024)
## Summary

Removes `Type::None` in favor of `KnownClass::NoneType.to_instance(…)`.

closes #13670

## Performance

There is a -4% performance regression on our red-knot benchmark. This is due to the fact that we now have to import `_typeshed` as a module, and infer types.

## Test Plan

Existing tests pass.
2024-11-04 14:00:05 +01:00
Dhruv Manilawala
e302c2de7c Cached inference of all definitions in an unpacking (#13979)
## Summary

This PR adds a new salsa query and an ingredient to resolve all the
variables involved in an unpacking assignment like `(a, b) = (1, 2)` at
once. Previously, we'd recursively try to match the correct type for
each definition individually which will result in creating duplicate
diagnostics.

This PR still doesn't solve the duplicate diagnostics issue because that
requires a different solution like using salsa accumulator or
de-duplicating the diagnostics manually.

Related: #13773 

## Test Plan

Make sure that all unpack assignment test cases pass, there are no
panics in the corpus tests.

## Todo

- [x] Look at the performance regression
2024-11-04 17:11:57 +05:30
renovate[bot]
012f385f5d Update dependency uuid to v11 (#14084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 08:09:18 +01:00
renovate[bot]
a6f7f22b27 Update Rust crate notify to v7 (#14083) 2024-11-04 07:39:06 +01:00
renovate[bot]
8d7dda9fb7 Update cloudflare/wrangler-action action to v3.11.0 (#14080)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cloudflare/wrangler-action](https://redirect.github.com/cloudflare/wrangler-action)
| action | minor | `v3.9.0` -> `v3.11.0` |

---

### Release Notes

<details>
<summary>cloudflare/wrangler-action
(cloudflare/wrangler-action)</summary>

###
[`v3.11.0`](https://redirect.github.com/cloudflare/wrangler-action/releases/tag/v3.11.0)

[Compare
Source](https://redirect.github.com/cloudflare/wrangler-action/compare/v3.10.0...v3.11.0)

##### Minor Changes

-
[#&#8203;309](https://redirect.github.com/cloudflare/wrangler-action/pull/309)
[`10d5b9c1c1826adaec0a9ee49fdf5b91113508ef`](10d5b9c1c1)
Thanks [@&#8203;Maximo-Guk](https://redirect.github.com/Maximo-Guk)! -
Revert "Add parity with pages-action for pages deploy outputs"

###
[`v3.10.0`](https://redirect.github.com/cloudflare/wrangler-action/releases/tag/v3.10.0)

[Compare
Source](https://redirect.github.com/cloudflare/wrangler-action/compare/v3.9.0...v3.10.0)

##### Minor Changes

-
[#&#8203;303](https://redirect.github.com/cloudflare/wrangler-action/pull/303)
[`3ec7f8943ef83351f743cfaa8763a9056ef70993`](3ec7f8943e)
Thanks
[@&#8203;courtney-sims](https://redirect.github.com/courtney-sims)! -
Support id, environment, url, and alias outputs for Pages deploys.

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xNDIuNCIsInVwZGF0ZWRJblZlciI6IjM4LjE0Mi40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 08:44:06 +05:30
renovate[bot]
fb0881d836 Update dependency mdformat-mkdocs to v3.1.1 (#14081)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[mdformat-mkdocs](https://redirect.github.com/kyleking/mdformat-mkdocs)
([changelog](https://redirect.github.com/kyleking/mdformat-mkdocs/releases))
| `==3.0.1` -> `==3.1.1` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mdformat-mkdocs/3.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/mdformat-mkdocs/3.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/mdformat-mkdocs/3.0.1/3.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mdformat-mkdocs/3.0.1/3.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>kyleking/mdformat-mkdocs (mdformat-mkdocs)</summary>

###
[`v3.1.1`](https://redirect.github.com/KyleKing/mdformat-mkdocs/releases/tag/v3.1.1)

[Compare
Source](https://redirect.github.com/kyleking/mdformat-mkdocs/compare/v3.1.0...v3.1.1)

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v3.1.0...v3.1.1

###
[`v3.1.0`](https://redirect.github.com/kyleking/mdformat-mkdocs/compare/v3.0.1...v3.1.0)

[Compare
Source](https://redirect.github.com/kyleking/mdformat-mkdocs/compare/v3.0.1...v3.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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xNDIuNCIsInVwZGF0ZWRJblZlciI6IjM4LjE0Mi40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 08:37:53 +05:30
renovate[bot]
ded2b15e05 Update pre-commit dependencies (#14082)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[abravalheri/validate-pyproject](https://redirect.github.com/abravalheri/validate-pyproject)
| repository | minor | `v0.21` -> `v0.22` |
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.7.0` -> `v0.7.2` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | minor | `v1.26.0` -> `v1.27.0` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>abravalheri/validate-pyproject
(abravalheri/validate-pyproject)</summary>

###
[`v0.22`](https://redirect.github.com/abravalheri/validate-pyproject/releases/tag/v0.22)

[Compare
Source](https://redirect.github.com/abravalheri/validate-pyproject/compare/v0.21...v0.22)

#### What's Changed

- Prevent injecting defaults and modifying input in-place, by
[@&#8203;henryiii](https://redirect.github.com/henryiii) in
[https://github.com/abravalheri/validate-pyproject/pull/213](https://redirect.github.com/abravalheri/validate-pyproject/pull/213)

**Full Changelog**:
https://github.com/abravalheri/validate-pyproject/compare/v0.21...v0.22

</details>

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

###
[`v0.7.2`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.7.2)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.7.1...v0.7.2)

See: https://github.com/astral-sh/ruff/releases/tag/0.7.2

###
[`v0.7.1`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.7.1)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.7.0...v0.7.1)

See: https://github.com/astral-sh/ruff/releases/tag/0.7.1

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.27.0`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.27.0)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.8...v1.27.0)

#### \[1.27.0] - 2024-11-01

##### Features

- Updated the dictionary with the [October
2024](https://redirect.github.com/crate-ci/typos/issues/1106) changes

###
[`v1.26.8`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.26.8)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.7...v1.26.8)

#### \[1.26.8] - 2024-10-24

###
[`v1.26.7`](https://redirect.github.com/crate-ci/typos/compare/v1.26.6...v1.26.7)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.6...v1.26.7)

###
[`v1.26.6`](https://redirect.github.com/crate-ci/typos/compare/v1.26.5...v1.26.6)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.5...v1.26.6)

###
[`v1.26.5`](https://redirect.github.com/crate-ci/typos/compare/v1.26.4...v1.26.5)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.4...v1.26.5)

###
[`v1.26.4`](https://redirect.github.com/crate-ci/typos/compare/v1.26.3...v1.26.4)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.3...v1.26.4)

###
[`v1.26.3`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.26.3)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.2...v1.26.3)

#### \[1.26.3] - 2024-10-24

##### Fixes

-   Accept `additionals`

###
[`v1.26.2`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.26.2)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.1...v1.26.2)

#### \[1.26.2] - 2024-10-24

##### Fixes

-   Accept `tesselate` variants

###
[`v1.26.1`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.26.1)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.26.0...v1.26.1)

#### \[1.26.1] - 2024-10-23

##### Fixes

-   Respect `--force-exclude` for binary files

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xNDIuNCIsInVwZGF0ZWRJblZlciI6IjM4LjE0Mi40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 08:35:31 +05:30
renovate[bot]
3133964d8c Update dependency ruff to v0.7.2 (#14077) 2024-11-03 21:16:28 -05:00
renovate[bot]
f00039b6f2 Update NPM Development dependencies (#14078) 2024-11-03 21:16:22 -05:00
renovate[bot]
6ccd0f187b Update Rust crate thiserror to v1.0.67 (#14076) 2024-11-03 21:16:13 -05:00
renovate[bot]
de40f6a3ad Update Rust crate syn to v2.0.87 (#14075) 2024-11-03 21:16:08 -05:00
renovate[bot]
dfbd27dc2f Update Rust crate serde to v1.0.214 (#14074) 2024-11-03 21:16:02 -05:00
renovate[bot]
1531ca8a1b Update Rust crate pep440_rs to v0.7.2 (#14073) 2024-11-03 21:15:56 -05:00
renovate[bot]
71702bbd48 Update Rust crate insta to v1.41.1 (#14072) 2024-11-03 21:15:49 -05:00
renovate[bot]
8d9bdb5b92 Update Rust crate anyhow to v1.0.92 (#14071) 2024-11-03 21:15:42 -05:00
Fábio D. Batista
2b73a1c039 [eradicate] ignore # language= in commented-out-code rule (ERA001) (#14069)
## Summary

The `commented-out-code` rule (ERA001) from `eradicate` is currently
flagging a very common idiom that marks Python strings as another
language, to help with syntax highlighting:


![image](https://github.com/user-attachments/assets/d523e83d-95cb-4668-a793-45f01d162234)

This PR adds this idiom to the list of allowed exceptions to the rule.

## Test Plan

I've added some additional test cases.
2024-11-03 16:50:00 -05:00
Charlie Marsh
2b0cdd2338 Improve some rule messages and docs (#14068) 2024-11-03 19:25:43 +00:00
Charlie Marsh
f09dc8b67c Detect items that hash to same value in duplicate dictionaries (#14065)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12772.
2024-11-03 14:16:34 -05:00
Charlie Marsh
71a122f060 Allow open without context manager in return statement (#14066)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13862.
2024-11-03 14:16:27 -05:00
Matt Norton
3ca24785ae Add links to missing related options within rule documentations (#13971)
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-03 14:15:57 -05:00
Charlie Marsh
1de36cfe4c Fix wrong-size header in open-file-with-context-handler (#14067) 2024-11-03 19:06:40 +00:00
Charlie Marsh
66872a41fc Detect items that hash to same value in duplicate sets (#14064)
## Summary

Like https://github.com/astral-sh/ruff/pull/14063, but ensures that we
catch cases like `{1, True}` in which the items hash to the same value
despite not being identical.
2024-11-03 18:49:11 +00:00
Charlie Marsh
e00594e8d2 Respect hash-equivalent literals in iteration-over-set (#14063)
## Summary

Closes https://github.com/astral-sh/ruff/issues/14049.
2024-11-03 18:44:52 +00:00
Micha Reiser
443fd3b660 Disallow single-line implicit concatenated strings (#13928) 2024-11-03 11:49:26 +00:00
Steve C
ae9f08d1e5 [ruff] - fix false positive for decorators (RUF028) (#14061) 2024-11-03 11:49:03 +00:00
Steve C
f69712c11d [flake8-pyi] - include all python file types for PYI006 and PYI066 (#14059) 2024-11-03 11:47:36 +00:00
Steve C
be485602de Fix preview link references in 2 rule docs (#14060) 2024-11-03 11:45:35 +00:00
Steve C
bc7615af0e [flake8-bugbear] - do not run mutable-argument-default on stubs (B006) (#14058)
## Summary

Early-exits in `B006` when the file is a stub. Fixes #14026 

## Test Plan

`cargo test`
2024-11-02 22:48:48 -04:00
Charlie Marsh
4a3eeeff86 Remove HashableExpr abstraction (#14057)
## Summary

It looks like `ComparableExpr` now implements `Hash` so we can just
remove this.
2024-11-02 20:28:35 +00:00
Charlie Marsh
35c6dfe481 Avoid parsing joint rule codes as distinct codes in # noqa (#12809)
## Summary

We should enable warnings for unsupported codes, but this at least fixes
the parsing for `# noqa: F401F841`.

Closes https://github.com/astral-sh/ruff/issues/12808.
2024-11-02 20:24:59 +00:00
Simon Brugman
f8374280c0 [flake8-simplify] Implementation for split-of-static-string (SIM905) (#14008)
## Summary

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

## Test Plan

Standard snapshot testing

flake8-simplify surprisingly only has a single test case

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-02 17:15:36 +00:00
Steve C
0925513529 [pyupgrade] - ignore kwarg unpacking for UP044 (#14053)
## Summary

Fixes #14047 

## Test Plan

`catgo test`

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-02 13:10:56 -04:00
Charlie Marsh
70bdde4085 Handle unions in augmented assignments (#14045)
## Summary

Removing more TODOs from the augmented assignment test suite. Now, if
the _target_ is a union, we correctly infer the union of results:

```python
if flag:
    f = Foo()
else:
    f = 42.0
f += 12
```
2024-11-01 19:49:18 +00:00
TomerBin
34a5d7cb7f [red-knot] Infer type of if-expression if test has statically known truthiness (#14048)
## Summary

Detecting statically known truthy or falsy test in if expressions
(ternary).

## Test Plan

new mdtest
2024-11-01 12:23:18 -07:00
Charlie Marsh
487941ea66 Handle maybe-unbound __iadd__-like operators in augmented assignments (#14044)
## Summary

One of the follow-ups from augmented assignment inference, now that
`Type::Unbound` has been removed.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-11-01 13:15:35 -04:00
Harry Reeder
099f077311 [docs] Add rule short code to mkdocs tags (#14040)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR updates the metadata in the YAML frontmatter of the mkdocs
documentation to include the rule short code as a tag, so it can be
easily searched.
Ref: #13684

## Test Plan

<!-- How was it tested? -->
This has been tested locally using the documentation provided
[here](https://docs.astral.sh/ruff/contributing/#mkdocs) for generating
docs.

This generates docs that now have the tags section:
```markdown
---
description: Checks for abstract classes without abstract methods.
tags:
- B024
---

# abstract-base-class-without-abstract-method (B024)
... trimmed
```

I've also verified that this gives the ability to get straight to the
page via search when serving mkdocs locally.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-11-01 15:50:12 +00:00
Micha Reiser
8574751911 Give non-existent files a durability of at least Medium (#14034) 2024-11-01 16:44:30 +01:00
Dhruv Manilawala
ddae741b72 Switch to uv publish (#14042)
## Summary

Ref: https://github.com/astral-sh/uv/pull/8065

## Test Plan

Going to re-release `0.7.2` which failed:
https://github.com/astral-sh/ruff/actions/runs/11630280069
2024-11-01 20:24:29 +05:30
Simon Brugman
5053d2c127 Doc: markdown link fix (#14041)
Typo in `mutable-contextvar-default` in `flake8-bugbear`
2024-11-01 14:19:00 +00:00
Dhruv Manilawala
ef72fd79a7 Bump version to 0.7.2 (#14039) 2024-11-01 19:09:07 +05:30
STACIA
658a51ea10 Fix typo for static method decorator (#14038) 2024-11-01 12:30:50 +00:00
github-actions[bot]
7c2da4f06e Sync vendored typeshed stubs (#14030)
Co-authored-by: typeshedbot <>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-11-01 10:51:56 +00:00
Micha Reiser
48fa839c80 Use named function in incremental red knot benchmark (#14033) 2024-11-01 08:44:38 +00:00
Micha Reiser
cf0f5e1318 Fix formatting of single with-item with trailing comment (#14005) 2024-11-01 09:08:06 +01:00
Micha Reiser
20b8a43017 Fix server panic when undoing an edit (#14010) 2024-11-01 08:16:53 +01:00
Carl Meyer
b8acadd6a2 [red-knot] have mdformat wrap mdtest files to 100 columns (#14020)
This makes it easier to read and edit (and review changes to) these
files as source, even though it doesn't affect the rendering.
2024-10-31 21:00:51 +00:00
David Peter
b372fe7198 [red-knot] Add myself as red-knot codeowner (#14023) 2024-10-31 19:17:37 +00:00
David Peter
53fa32a389 [red-knot] Remove Type::Unbound (#13980)
<!--
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

- Remove `Type::Unbound`
- Handle (potential) unboundness as a concept orthogonal to the type
system (see new `Symbol` type)
- Improve existing and add new diagnostics related to (potential)
unboundness

closes #13671 

## Test Plan

- Update existing markdown-based tests
- Add new tests for added/modified functionality
2024-10-31 20:05:53 +01:00
Alex Waygood
d1189c20df [red-knot] Add failing tests for iterating over maybe-iterable unions (#14016) 2024-10-31 18:20:21 +00:00
Simon Brugman
9a6b08b557 [flake8-simplify] Include caveats of enabling if-else-block-instead-of-if-exp (SIM108) (#14019) 2024-10-31 17:26:22 +00:00
Micha Reiser
76e4277696 [red-knot] Handle context managers in (sync) with statements (#13998) 2024-10-31 08:18:18 +00:00
Steve C
2d917d72f6 [pyupgrade] - add PEP646 Unpack conversion to * with fix (UP044) (#13988)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-31 06:58:34 +00:00
Dhruv Manilawala
2629527559 Fix panic when filling up types vector during unpacking (#14006)
## Summary

This PR fixes a panic which can occur in an unpack assignment when:
* (number of target expressions) - (number of tuple types) > 2
* There's a starred expression

The reason being that the `insert` panics because the index is greater
than the length.

This is an error case and so practically it should occur very rarely.
The solution is to resize the types vector to match the number of
expressions and then insert the starred expression type.

## Test Plan

Add a new test case.
2024-10-30 19:13:57 +00:00
Dhruv Manilawala
bf20061268 Separate type check diagnostics builder (#13978)
## Summary

This PR creates a new `TypeCheckDiagnosticsBuilder` for the
`TypeCheckDiagnostics` struct. The main motivation behind this is to
separate the helpers required to build the diagnostics from the type
inference builder itself. This allows us to use such helpers outside of
the inference builder like for example in the unpacking logic in
https://github.com/astral-sh/ruff/pull/13979.

## Test Plan

`cargo insta test`
2024-10-30 18:50:31 +00:00
Charlie Marsh
eddc8d7644 Add failing tests for augmented assignments with partial binding (#14002)
## Summary

These cases aren't handled correctly yet -- some of them are waiting on
refactors to `Unbound` before fixing. Part of #12699.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-30 14:22:34 -04:00
Charlie Marsh
b1ce8a3949 Use Never instead of None for stores (#13984)
## Summary

See:
https://github.com/astral-sh/ruff/pull/13981#issuecomment-2445472433
2024-10-30 12:03:50 -04:00
Charlie Marsh
262c04f297 Use binary semantics when __iadd__ et al are unbound (#13987)
## Summary

I noticed that augmented assignments on floats were yielding "not
supported" diagnostics. If the dunder isn't bound at all, we should use
binary operator semantics, rather than treating it as not-callable.
2024-10-30 13:09:22 +00:00
Charlie Marsh
71536a43db Add remaining augmented assignment dunders (#13985)
## Summary

See: https://github.com/astral-sh/ruff/issues/12699
2024-10-30 13:02:29 +00:00
Alex Waygood
e6dcdf3e49 Switch off the single_match_else Clippy lint (#13994) 2024-10-30 12:24:16 +00:00
Simon Brugman
f426349051 docs: typo in refurb-sorted-min-max (#13993) 2024-10-30 12:07:42 +00:00
Alex Waygood
42c70697d8 [red-knot] Fix bug where union of two iterable types was not recognised as iterable (#13992) 2024-10-30 11:54:16 +00:00
Charlie Marsh
1607d88c22 Use consistent diagnostic messages in augmented assignment inference (#13986) 2024-10-29 22:57:53 -04:00
Charlie Marsh
c6b82151dd Add augmented assignment inference for -= operator (#13981)
## Summary

See: https://github.com/astral-sh/ruff/issues/12699
2024-10-29 22:14:27 -04:00
Alex Waygood
39cf46ecd6 [red-knot] Improve ergonomics for the PySlice trait (#13983) 2024-10-29 20:40:59 +00:00
David Peter
96b3c400fe [red-knot] Minor follow-up on slice expression inference (#13982)
## Summary

Minor follow-up to #13917 — thanks @AlexWaygood for the post-merge
review.

- Add
SliceLiteralType::as_tuple
- Use .expect() instead of SAFETY
comment
- Match on ::try_from
result
- Add TODO comment regarding raising a diagnostic for `"foo"["bar":"baz"]`
2024-10-29 19:40:57 +00:00
jsurany
60a2dc53e7 fix issues in discovering ruff in pip build environments (#13881)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Changes in this PR https://github.com/astral-sh/ruff/pull/13591 did not
allow correct discovery in pip build environments.

```python
# both of these variables are tuple[str, str] (length is 2)
first, second = os.path.split(paths[0]), os.path.split(paths[1])

# so these length checks are guaranteed to fail even for build environment folders
if (
    len(first) >= 3
    and len(second) >= 3 
    ...
)
```

~~Here we instead use `pathlib`, and we check all `pip-build-env-` paths
for the folder that is expected to contain the `ruff` executable.~~

Here we update the logic to more properly split out the path components
that we use for `pip-build-env-` inspection.

## Test Plan

I've checked this manually against a workflow that was failing, I'm not
sure what to do for real tests. The same issues apply as with the
previous PR.

---------

Co-authored-by: Jonathan Surany <jsurany@bloomberg.net>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-10-29 15:50:29 +00:00
Alex Waygood
8d98aea6c4 [red-knot] Infer attribute expressions in type annotations (#13967) 2024-10-29 11:06:44 +00:00
Alex Waygood
d2c9f5e43c [red-knot] Fallback to attributes on types.ModuleType if a symbol can't be found in locals or globals (#13904) 2024-10-29 10:59:03 +00:00
Alex Waygood
7dd0c7f4bd [red-knot] Infer tuple types from annotations (#13943)
## Summary

This PR adds support for heterogenous `tuple` annotations to red-knot.

It does the following:
- Extends `infer_type_expression` so that it understands tuple
annotations
- Changes `infer_type_expression` so that `ExprStarred` nodes in type
annotations are inferred as `Todo` rather than `Unknown` (they're valid
in PEP-646 tuple annotations)
- Extends `Type::is_subtype_of` to understand when one heterogenous
tuple type can be understood to be a subtype of another (without this
change, the PR would have introduced new false-positive errors to some
existing mdtests).
2024-10-29 10:30:03 +00:00
David Peter
56c796acee [red-knot] Slice expression types & subscript expressions with slices (#13917)
## Summary

- Add a new `Type::SliceLiteral` variant
- Infer `SliceLiteral` types for slice expressions, such as
`<int-literal>:<int-literal>:<int-literal>`.
- Infer "sliced" literal types for subscript expressions using slices,
such as `<string-literal>[<slice-literal>]`.
- Infer types for expressions involving slices of tuples:
`<tuple>[<slice-literal>]`.

closes #13853

## Test Plan

- Unit tests for indexing/slicing utility functions
- Markdown-based tests for
  - Subscript expressions `tuple[slice]`
  - Subscript expressions `string_literal[slice]`
  - Subscript expressions `bytes_literal[slice]`
2024-10-29 10:17:31 +01:00
Raphael Gaschignard
2fe203292a [red-knot] Distribute intersections on negation (#13962)
## Summary

This does two things:
- distribute negated intersections when building up intersections (i.e.
going from `A & ~(B & C)` to `(A & ~B) | (A & ~C)`) (fixing #13931)

## Test Plan

`cargo test`
2024-10-29 02:56:04 +00:00
Charlie Marsh
b6847b371e Skip namespace package enforcement for PEP 723 scripts (#13974)
## Summary

Vendors the PEP 723 parser from
[uv](debe67ffdb/crates/uv-scripts/src/lib.rs (L283)).

Closes https://github.com/astral-sh/ruff/issues/13912.
2024-10-29 02:11:31 +00:00
Charlie Marsh
b19862c64a Rename operator-unsupported to unsupported-operator (#13973)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13959.
2024-10-28 21:34:12 -04:00
TomerBin
9a0dade925 [red-knot] Type narrowing inside boolean expressions (#13970)
## Summary

This PR adds type narrowing in `and` and `or` expressions, for example:

```py
class A: ...

x: A | None = A() if bool_instance() else None

isinstance(x, A) or reveal_type(x)  # revealed: None
``` 

## Test Plan
New mdtests 😍
2024-10-28 18:17:48 -07:00
Dhruv Manilawala
ec6208e51b Treat return type of singledispatch as runtime-required (#13957)
## Summary

fixes: #13955 

## Test Plan

Update existing test case to use a return type hint for which `main`
flags `TCH003`.
2024-10-28 20:33:28 -04:00
TomerBin
74cf66e4c2 [red-knot] Narrowing - Not operator (#13942)
## Summary

After #13918 has landed, narrowing constraint negation became easy, so
adding support for `not` operator.

## Test Plan

Added a new mdtest file for `not` expression.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-28 20:27:26 +00:00
Carlo Lepelaars
1f19aca632 [DOCS] Add CrowdCent's numerblox to Ruff users. (#13569)
Hi, our open source project
[NumerBlox](https://github.com/crowdcent/numerblox) migrated to `uv` and
`ruff`. Would appreciate the project being included in the list of Ruff
users.

## Summary

Add [NumerBlox](https://github.com/crowdcent/numerblox) to Ruff users in
README.md.
2024-10-28 10:53:37 -04:00
Charlie Marsh
6f52d573ef Support inference for PEP 604 union annotations (#13964)
## Summary

Supports return type inference for, e.g., `def f() -> int | None:`.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-28 10:13:01 -04:00
Tim Hatch
c593ccb529 Regenerate known_stdlibs.rs with stdlibs 2024.10.25 (#13963)
## Summary

`stdlibs` has a new release to properly categorize the `_wmi` module
which has been [present since
~2022](https://github.com/python/cpython/issues/89545#issuecomment-1227846806).


## Test Plan

Let CI run, this is only a trivial change to categorization data.
2024-10-28 08:37:54 -04:00
Micha Reiser
9f3a38d408 Extract LineIndex independent methods from Locator (#13938) 2024-10-28 07:53:41 +00:00
renovate[bot]
f8eb547fb4 Update dependency react-resizable-panels to v2.1.6 (#13952) 2024-10-28 07:02:12 +00:00
renovate[bot]
b77de359bc Update NPM Development dependencies (#13954)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 07:00:44 +00:00
renovate[bot]
41f74512df Update Rust crate insta to v1.41.0 (#13956)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-28 06:51:53 +00:00
renovate[bot]
387dc664bd Update Rust crate thiserror to v1.0.65 (#13950) 2024-10-28 06:28:58 +00:00
renovate[bot]
41c9bdbd37 Update Rust crate syn to v2.0.85 (#13949) 2024-10-28 06:28:36 +00:00
renovate[bot]
222a646437 Update Rust crate serde to v1.0.213 (#13948) 2024-10-28 06:27:18 +00:00
renovate[bot]
5b411fe606 Update Rust crate proc-macro2 to v1.0.89 (#13946) 2024-10-28 06:26:53 +00:00
renovate[bot]
47dd83e56f Update Rust crate regex to v1.11.1 (#13947) 2024-10-28 06:26:36 +00:00
renovate[bot]
08e23d78aa Update Rust crate anyhow to v1.0.91 (#13945) 2024-10-28 06:25:44 +00:00
Dhruv Manilawala
5af0966057 Remove unreferenced snapshots (#13958) 2024-10-28 07:16:05 +01:00
renovate[bot]
faf9dfaa9d Update dependency ruff to v0.7.1 (#13953) 2024-10-27 21:13:03 -04:00
renovate[bot]
9d131c8c45 Update dependency mdformat-mkdocs to v3.0.1 (#13951) 2024-10-27 21:12:55 -04:00
Micha Reiser
5a56886414 TCH003: Fix false positive for singledispatchmethod (#13941)
## Summary

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

## Test Plan

Added test
2024-10-27 21:02:45 -04:00
TomerBin
66c3aaa307 [red-knot] - Flow-control for boolean operations (#13940)
## Summary

As python uses short-circuiting boolean operations in runtime, we should
mimic that logic in redknot as well.
For example, we should detect that in the following code `x` might be
undefined inside the block:

```py
if flag or (x := 1):
    print(x) 
```

## Test Plan

Added mdtest suit for boolean expressions.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-27 03:33:01 +00:00
cake-monotone
b6ffa51c16 [red-knot] Type inference for comparisons between arbitrary instances (#13903)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-10-26 18:19:56 +00:00
TomerBin
35f007f17f [red-knot] Type narrow in else clause (#13918)
## Summary

Add support for type narrowing in elif and else scopes as part of
#13694.

## Test Plan

- mdtest
- builder unit test for union negation.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-26 16:22:57 +00:00
Jonas Vacek
3006d6da23 Docs: Add GitLab CI/CD to integrations. (#13915) 2024-10-26 18:10:17 +02:00
Micha Reiser
6aaf1d9446 [red-knot] Remove lint-phase (#13922)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-25 18:40:52 +00:00
Alex Waygood
5eb87aa56e [red-knot] Infer Todo, not Unknown, for PEP-604 unions in annotations (#13908) 2024-10-25 18:21:31 +00:00
David Peter
085a43a262 [red-knot] knot benchmark: fix --knot-path arg (#13923)
## Summary

Previously, this would fail with

```
AttributeError: 'str' object has no attribute 'is_file'
```

if I tried to use the `--knot-path` option. I wish we had a type checker
for Python*.

## Test Plan

```sh
uv run benchmark --knot-path ~/.cargo-target/release/red_knot
```

\* to be fair, this would probably require special handling for
`argparse` in the typechecker.
2024-10-25 11:43:39 +02:00
Micha Reiser
32b57b2ee4 Enable nursery rules: 'redundant_clone', 'debug_assert_with_mut_call', and 'unused_peekable' (#13920) 2024-10-25 09:46:30 +02:00
Dhruv Manilawala
337af836d3 Bump version to 0.7.1 (#13913) 2024-10-24 20:57:07 +05:30
Micha Reiser
113ce840a6 Fix normalize arguments when fstring_formatting is disabled (#13910) 2024-10-24 13:07:18 +00:00
Micha Reiser
7272f83868 Fix preview style name in can_omit_parentheses to is_f_string_formatting_enabled (#13907) 2024-10-24 11:32:28 +00:00
Alex Waygood
3eb454699a [red-knot] Format mdtest Python snippets more concisely (#13905) 2024-10-24 11:09:31 +00:00
David Peter
77ae0ccf0f [red-knot] Infer subscript expression types for bytes literals (#13901)
## Summary

Infer subscript expression types for bytes literals:
```py
b = b"\x00abc\xff"

reveal_type(b[0])  # revealed: Literal[b"\x00"]
reveal_type(b[1])  # revealed: Literal[b"a"]
reveal_type(b[-1])  # revealed: Literal[b"\xff"]
reveal_type(b[-2])  # revealed: Literal[b"c"]

reveal_type(b[False])  # revealed: Literal[b"\x00"]
reveal_type(b[True])  # revealed: Literal[b"a"]
```


part of #13689
(https://github.com/astral-sh/ruff/issues/13689#issuecomment-2404285064)

## Test Plan

- New Markdown-based tests (see `mdtest/subscript/bytes.md`)
- Added missing test for `string_literal[bool_literal]`
2024-10-24 12:07:41 +02:00
Micha Reiser
73ee72b665 Join implicit concatenated strings when they fit on a line (#13663) 2024-10-24 11:52:22 +02:00
Micha Reiser
e402e27a09 Use referencial equality in traversal helper methods (#13895) 2024-10-24 11:30:22 +02:00
Mihai Capotă
de4181d7dd Remove "default" remark from ruff check (#13900)
## Summary

`ruff check` has not been the default in a long time. However, the help
message and code comment still designate it as the default. The remark
should have been removed in the deprecation PR #10169.

## Test Plan

Not tested.
2024-10-23 21:17:21 -04:00
David Peter
2c57c2dc8a [red-knot] Type narrowing for isinstance checks (#13894)
## Summary

Add type narrowing for `isinstance(object, classinfo)` [1] checks:
```py
x = 1 if flag else "a"

if isinstance(x, int):
    reveal_type(x)  # revealed: Literal[1]
```

closes #13893

[1] https://docs.python.org/3/library/functions.html#isinstance

## Test Plan

New Markdown-based tests in `narrow/isinstance.md`.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-23 20:51:33 +02:00
Micha Reiser
72c18c8225 Fix E221 and E222 to flag missing or extra whitespace around == operator (#13890) 2024-10-23 15:02:29 +02:00
Micha Reiser
00b078268b Fix stale syntax errors in playground (#13888) 2024-10-23 12:30:10 +00:00
Shaygan Hooshyari
4d109514d6 [flake8-type-checking] Support auto-quoting when annotations contain quotes (#11811)
## Summary

This PR updates the fix generation logic for auto-quoting an annotation
to generate an edit even when there's a quote character present.

The logic uses the visitor pattern, maintaining it's state on where it
is and generating the string value one node at a time. This can be
considered as a specialized form of `Generator`. The state required to
maintain is whether we're currently inside a `typing.Literal` or
`typing.Annotated` because the string value in those types should not be
un-quoted i.e., `Generic[Literal["int"]]` should become
`"Generic[Literal['int']]`, the quotes inside the `Literal` should be
preserved.

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

## Test Plan

Add various test cases to validate this change, validate the snapshots.
There are no ecosystem changes to go through.

---------

Signed-off-by: Shaygan <hey@glyphack.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-10-23 16:34:03 +05:30
David Peter
387076d212 [red-knot] Use track_caller for expect_ methods (#13884)
## Summary

A minor quality-of-life improvement: add
[`#[track_caller]`](https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute)
attribute to `Type::expect_xyz()` methods and some `TypeInference` methods such that the panic-location
is reported one level higher up in the stack trace.

before: reports location inside the `Type::expect_class_literal()`
method. Not very useful.
```
thread 'types::infer::tests::deferred_annotation_builtin' panicked at crates/red_knot_python_semantic/src/types.rs:304:14:
Expected a Type::ClassLiteral variant
```

after: reports location at the `Type::expect_class_literal()` call site,
where the error was made.
```
thread 'types::infer::tests::deferred_annotation_builtin' panicked at crates/red_knot_python_semantic/src/types/infer.rs:4302:14:
Expected a Type::ClassLiteral variant
```

## Test Plan

Called `expect_class_literal()` on something that's not a
`Type::ClassLiteral` and saw that the error was reported at the call
site.
2024-10-23 12:48:19 +02:00
Micha Reiser
2f88f84972 Alternate quotes for strings inside f-strings in preview (#13860) 2024-10-23 07:57:53 +02:00
David Peter
f335fe4d4a [red-knot] rename {Class,Module,Function} => {Class,Module,Function}Literal (#13873)
## Summary

* Rename `Type::Class` => `Type::ClassLiteral`
* Rename `Type::Function` => `Type::FunctionLiteral`
* Do not rename `Type::Module`
* Remove `*Literal` suffixes in `display::LiteralTypeKind` variants, as
per clippy suggestion
* Get rid of `Type::is_class()` in favor of `is_subtype_of(…, 'type')`;
modifiy `is_subtype_of` to support this.
* Add new `Type::is_xyz()` methods and use them instead of matching on
`Type` variants.

closes #13863 

## Test Plan

New `is_subtype_of_class_literals` unit test.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-22 22:10:53 +02:00
David Peter
c6ce52c29e [red-knot] Treat empty intersection as 'object', fix intersection simplification (#13880)
## Summary

- Properly treat the empty intersection as being of type `object`.
- Consequently, change the simplification method to explicitly add
`Never` to the positive side of the intersection when collapsing a type
such as `int & str` to `Never`, as opposed to just clearing both the
positive and the negative side.
- Minor code improvement in `bindings_ty`: use `peekable()` to check
whether the iterator over constraints is empty, instead of handling
first and subsequent elements separately.

fixes #13870

## Test Plan

- New unit tests for `IntersectionBuilder` to make sure the empty
intersection represents `object`.
- Markdown-based regression test for the original issue in #13870
2024-10-22 21:02:46 +02:00
Micha Reiser
5d4edd61bf Fix D204's documentation to correctly mention the conventions when it is enabled (#13867) 2024-10-22 16:51:57 +02:00
samypr100
7dbd8f0f8e ci(docker): incorporate docker release enhancements from uv (#13274)
## Summary

This PR updates `ruff` to match `uv` updated [docker releases
approach](https://github.com/astral-sh/uv/blob/main/.github/workflows/build-docker.yml).
It's a combined PR with changes from these PR's
* https://github.com/astral-sh/uv/pull/6053
* https://github.com/astral-sh/uv/pull/6556
* https://github.com/astral-sh/uv/pull/6734
* https://github.com/astral-sh/uv/pull/7568

Summary of changes / features

1. This change would publish an additional tags that includes only
`major.minor`.

    For a release with `x.y.z`, this would publish the tags:

    * ghcr.io/astral-sh/ruff:latest
    * ghcr.io/astral-sh/ruff:x.y.z
    * ghcr.io/astral-sh/ruff:x.y

2. Parallelizes multi-platform builds using multiple workers (hence the
new docker-build / docker-publish jobs), which cuts docker releases time
in half.

3. This PR introduces additional images with the ruff binaries from
scratch for both amd64/arm64 and makes the mapping easy to configure by
generating the Dockerfile on the fly. This approach focuses on
minimizing CI time by taking advantage of dedicating a worker per
mapping (20-30s~ per job). For example, on release `x.y.z`, this will
publish the following image tags with format
`ghcr.io/astral-sh/ruff:{tag}` with manifests for both amd64/arm64. This
also include `x.y` tags for each respective additional tag. Note, this
version does not include the python based images, unlike `uv`.

* From **scratch**: `latest`, `x.y.z`, `x.y` (currently being published)
* From **alpine:3.20**: `alpine`, `alpine3.20`, `x.y.z-alpine`,
`x.y.z-alpine3.20`
* From **debian:bookworm-slim**: `debian-slim`, `bookworm-slim`,
`x.y.z-debian-slim`, `x.y.z-bookworm-slim`
* From **buildpack-deps:bookworm**: `debian`, `bookworm`,
`x.y.z-debian`, `x.y.z-bookworm`

4. This PR also fixes `org.opencontainers.image.version` for all tags
(including the one from `scratch`) to contain the right release version
instead of branch name `main` (current behavior).

    ```
> docker inspect ghcr.io/astral-sh/ruff:0.6.4 | jq -r
'.[0].Config.Labels'
    {
      ...
      "org.opencontainers.image.version": "main"
    }
    ```

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

## Test Plan

Approach mimics `uv` with almost no changes so risk is low but I still
tested the full workflow.

* I have a working CI release pipeline on my fork run
https://github.com/samypr100/ruff/actions/runs/10966657733
* The resulting images were published to
https://github.com/samypr100/ruff/pkgs/container/ruff
2024-10-22 07:06:49 -05:00
David Peter
46c0961b0b [red-knot] is_subtype_of: treat literals as subtype of 'object' (#13876)
Add the following subtype relations:
- `BooleanLiteral <: object`
- `IntLiteral <: object`
- `StringLiteral <: object`
- `LiteralString <: object`
- `BytesLiteral <: object`

Added a test case for `bool <: int`.

## Test Plan

New unit tests.
2024-10-22 13:32:51 +02:00
aditya pillai
cd6c937194 [red-knot] Report line numbers in mdtest relative to the markdown file, not the test snippet (#13804)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-10-22 07:42:40 +00:00
Alex
9d102799f9 [red-knot] Support for not-equal narrowing (#13749)
Add type narrowing for `!=` expression as stated in
#13694.

###  Test Plan

Add tests in new md format.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2024-10-21 23:08:33 +02:00
renovate[bot]
e39110e18b Update cloudflare/wrangler-action action to v3.9.0 (#13846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 20:53:32 +01:00
Micha Reiser
155be88373 Speedup mdtest parser (#13835) 2024-10-21 19:49:20 +00:00
TomerBin
a77512df68 [red-knot] Improve chained comparisons handling (#13825)
## Summary

A small fix for comparisons of multiple comparators.
Instead of comparing each comparator to the leftmost item, we should
compare it to the closest item on the left.

While implementing this, I noticed that we don’t yet narrow Yoda
comparisons (e.g., `True is x`), so I didn’t change that behavior in
this PR.

## Test Plan

Added some mdtests 🎉
2024-10-21 12:38:08 -07:00
Micha Reiser
e9dd92107c formatter: Introduce QuoteMetadata (#13858) 2024-10-21 20:23:46 +01:00
Micha Reiser
9e3cf14dde Speed up mdtests (#13832)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-21 20:06:41 +01:00
David Peter
fa7626160b [red-knot] handle unions on the LHS of is_subtype_of (#13857)
## Summary

Just a drive-by change that occurred to me while I was looking at
`Type::is_subtype_of`: the existing pattern for unions on the *right
hand side*:
```rs
            (ty, Type::Union(union)) => union
                .elements(db)
                .iter()
                .any(|&elem_ty| ty.is_subtype_of(db, elem_ty)),
```
is not (generally) correct if the *left hand side* is a union.

## Test Plan

Added new test cases for `is_subtype_of` and `!is_subtype_of`
2024-10-21 20:12:03 +02:00
David Peter
d9ef83bfef [red-knot] Consistently rename BoolLiteral => BooleanLiteral (#13856)
## Summary

- Consistent naming: `BoolLiteral` => `BooleanLiteral` (it's mainly the
`Ty::BoolLiteral` variant that was renamed)

  I tripped over this a few times now, so I thought I'll smooth it out.
- Add a new test case for `Literal[True] <: bool`, as suggested here:
https://github.com/astral-sh/ruff/pull/13781#discussion_r1804922827
2024-10-21 13:55:50 +02:00
Steve C
f3612c2717 [pylint] - restrict iteration-over-set to only work on sets of literals (PLC0208) (#13731) 2024-10-21 12:14:02 +01:00
renovate[bot]
c2dc502f3b Update NPM Development dependencies (#13851)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[@cloudflare/workers-types](https://redirect.github.com/cloudflare/workerd)
| [`4.20241004.0` ->
`4.20241018.0`](https://renovatebot.com/diffs/npm/@cloudflare%2fworkers-types/4.20241004.0/4.20241018.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@cloudflare%2fworkers-types/4.20241018.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@cloudflare%2fworkers-types/4.20241018.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@cloudflare%2fworkers-types/4.20241004.0/4.20241018.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@cloudflare%2fworkers-types/4.20241004.0/4.20241018.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@types/react-dom](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom))
| [`18.3.0` ->
`18.3.1`](https://renovatebot.com/diffs/npm/@types%2freact-dom/18.3.0/18.3.1)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2freact-dom/18.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2freact-dom/18.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2freact-dom/18.3.0/18.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2freact-dom/18.3.0/18.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/eslint-plugin](https://typescript-eslint.io/packages/eslint-plugin)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin))
| [`8.8.0` ->
`8.10.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/8.8.0/8.10.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2feslint-plugin/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2feslint-plugin/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2feslint-plugin/8.8.0/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2feslint-plugin/8.8.0/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/parser](https://typescript-eslint.io/packages/parser)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser))
| [`8.8.0` ->
`8.10.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/8.8.0/8.10.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2fparser/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2fparser/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2fparser/8.8.0/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2fparser/8.8.0/8.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [eslint-plugin-react-hooks](https://react.dev/)
([source](https://redirect.github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks))
| [`^4.6.0` ->
`^5.0.0`](https://renovatebot.com/diffs/npm/eslint-plugin-react-hooks/4.6.2/5.0.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-react-hooks/5.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-plugin-react-hooks/5.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-plugin-react-hooks/4.6.2/5.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-react-hooks/4.6.2/5.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[miniflare](https://redirect.github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#readme)
([source](https://redirect.github.com/cloudflare/workers-sdk/tree/HEAD/packages/miniflare))
| [`3.20240925.0` ->
`3.20241011.0`](https://renovatebot.com/diffs/npm/miniflare/3.20240925.0/3.20241011.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/miniflare/3.20241011.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/miniflare/3.20241011.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/miniflare/3.20240925.0/3.20241011.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/miniflare/3.20240925.0/3.20241011.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [tailwindcss](https://tailwindcss.com)
([source](https://redirect.github.com/tailwindlabs/tailwindcss)) |
[`3.4.13` ->
`3.4.14`](https://renovatebot.com/diffs/npm/tailwindcss/3.4.13/3.4.14) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/tailwindcss/3.4.14?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/tailwindcss/3.4.14?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/tailwindcss/3.4.13/3.4.14?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/tailwindcss/3.4.13/3.4.14?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [typescript](https://www.typescriptlang.org/)
([source](https://redirect.github.com/microsoft/TypeScript)) | [`5.6.2`
-> `5.6.3`](https://renovatebot.com/diffs/npm/typescript/5.6.2/5.6.3) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/typescript/5.6.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/typescript/5.6.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/typescript/5.6.2/5.6.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/typescript/5.6.2/5.6.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [vite](https://vite.dev)
([source](https://redirect.github.com/vitejs/vite/tree/HEAD/packages/vite))
| [`5.4.8` ->
`5.4.9`](https://renovatebot.com/diffs/npm/vite/5.4.8/5.4.9) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/vite/5.4.9?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vite/5.4.9?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vite/5.4.8/5.4.9?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite/5.4.8/5.4.9?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [wrangler](https://redirect.github.com/cloudflare/workers-sdk)
([source](https://redirect.github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler))
| [`3.80.0` ->
`3.81.0`](https://renovatebot.com/diffs/npm/wrangler/3.80.0/3.81.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/wrangler/3.81.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/wrangler/3.81.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/wrangler/3.80.0/3.81.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/wrangler/3.80.0/3.81.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>cloudflare/workerd (@&#8203;cloudflare/workers-types)</summary>

###
[`v4.20241018.0`](caeb4e0d9e...fa7168988f)

[Compare
Source](caeb4e0d9e...fa7168988f)

###
[`v4.20241011.0`](7619848185...caeb4e0d9e)

[Compare
Source](7619848185...caeb4e0d9e)

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/eslint-plugin)</summary>

###
[`v8.10.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8100-2024-10-17)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.9.0...v8.10.0)

##### 🚀 Features

- support TypeScript 5.6
([#&#8203;9972](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/9972))

##### ❤️  Thank You

-   Josh Goldberg 

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

###
[`v8.9.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#890-2024-10-14)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.8.1...v8.9.0)

##### 🩹 Fixes

- **eslint-plugin:** \[no-unnecessary-type-parameters] cannot assume
variables are either type or value

- **scope-manager:** \[no-use-before-define] do not treat nested
namespace aliases as variable references

- **eslint-plugin:** \[return-await] sync the behavior with
await-thenable

- **eslint-plugin:** \[prefer-literal-enum-member] report a different
error message when `allowBitwiseExpressions` is enabled

-   **eslint-plugin:** \[no-loop-func] sync from upstream base rule

- **eslint-plugin:** \[no-unused-vars] never report the naming of an
enum member

-   **eslint-plugin:** correct use-at-your-own-risk type definitions

-   **eslint-plugin:** handle unions in await...for

##### ❤️  Thank You

-   Abraham Guo
-   Anna Bocharova
-   Arya Emami
-   auvred
-   Joshua Chen
-   Kirk Waiblinger
-   Lotfi Meklati
-   mdm317
-   Ronen Amiel
-   Sukka
-   YeonJuan

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

###
[`v8.8.1`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#881-2024-10-07)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.8.0...v8.8.1)

##### 🩹 Fixes

- **eslint-plugin:** stop warning on
[@&#8203;ts-nocheck](https://redirect.github.com/ts-nocheck) comments
which aren't at the beginning of the file

##### ❤️  Thank You

-   Brad Zacher
-   Ronen Amiel
-   WhitePiano

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/parser)</summary>

###
[`v8.10.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#8100-2024-10-17)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.9.0...v8.10.0)

##### 🚀 Features

- support TypeScript 5.6
([#&#8203;9972](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/9972))

##### ❤️  Thank You

-   Josh Goldberg 

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

###
[`v8.9.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#890-2024-10-14)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.8.1...v8.9.0)

This was a version bump only for parser to align it with other projects,
there were no code changes.

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

###
[`v8.8.1`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#881-2024-10-07)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.8.0...v8.8.1)

This was a version bump only for parser to align it with other projects,
there were no code changes.

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>facebook/react (eslint-plugin-react-hooks)</summary>

###
[`v5.0.0`](https://redirect.github.com/facebook/react/blob/HEAD/packages/eslint-plugin-react-hooks/CHANGELOG.md#500)

[Compare
Source](a87edf62d7...eslint-plugin-react-hooks@5.0.0)

- **New Violations:** Component names now need to start with an
uppercase letter instead of a non-lowercase letter. This means `_Button`
or `_component` are no longer valid.
([@&#8203;kassens](https://redirect.github.com/kassens)) in
[#&#8203;25162](https://redirect.github.com/facebook/react/pull/25162)

<!---->

- Consider dispatch from `useActionState` stable.
([@&#8203;eps1lon](https://redirect.github.com/eps1lon) in
[#&#8203;29665](https://redirect.github.com/facebook/react/pull/29665))
- Add support for ESLint v9.
([@&#8203;eps1lon](https://redirect.github.com/eps1lon) in
[#&#8203;28773](https://redirect.github.com/facebook/react/pull/28773))
- Accept `as` expression in callback.
([@&#8203;StyleShit](https://redirect.github.com/StyleShit) in
[#&#8203;28202](https://redirect.github.com/facebook/react/pull/28202))
- Accept `as` expressions in deps array.
([@&#8203;StyleShit](https://redirect.github.com/StyleShit) in
[#&#8203;28189](https://redirect.github.com/facebook/react/pull/28189))
- Treat `React.use()` the same as `use()`.
([@&#8203;kassens](https://redirect.github.com/kassens) in
[#&#8203;27769](https://redirect.github.com/facebook/react/pull/27769))
- Move `use()` lint to non-experimental.
([@&#8203;kassens](https://redirect.github.com/kassens) in
[#&#8203;27768](https://redirect.github.com/facebook/react/pull/27768))
- Support Flow `as` expressions.
([@&#8203;cpojer](https://redirect.github.com/cpojer) in
[#&#8203;27590](https://redirect.github.com/facebook/react/pull/27590))
- Allow `useEffect(fn, undefined)`.
([@&#8203;kassens](https://redirect.github.com/kassens) in
[#&#8203;27525](https://redirect.github.com/facebook/react/pull/27525))
- Disallow hooks in async functions.
([@&#8203;acdlite](https://redirect.github.com/acdlite) in
[#&#8203;27045](https://redirect.github.com/facebook/react/pull/27045))
- Rename experimental `useEvent` to `useEffectEvent`.
([@&#8203;sebmarkbage](https://redirect.github.com/sebmarkbage) in
[#&#8203;25881](https://redirect.github.com/facebook/react/pull/25881))
- Lint for presence of `useEvent` functions in dependency lists.
([@&#8203;poteto](https://redirect.github.com/poteto) in
[#&#8203;25512](https://redirect.github.com/facebook/react/pull/25512))
- Check `useEvent` references instead.
([@&#8203;poteto](https://redirect.github.com/poteto) in
[#&#8203;25319](https://redirect.github.com/facebook/react/pull/25319))
- Update `RulesOfHooks` with `useEvent` rules.
([@&#8203;poteto](https://redirect.github.com/poteto) in
[#&#8203;25285](https://redirect.github.com/facebook/react/pull/25285))

</details>

<details>
<summary>cloudflare/workers-sdk (miniflare)</summary>

###
[`v3.20241011.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/miniflare/CHANGELOG.md#3202410110)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/miniflare@3.20241004.0...miniflare@3.20241011.0)

##### Patch Changes

-
[#&#8203;6961](https://redirect.github.com/cloudflare/workers-sdk/pull/6961)
[`5761020`](5761020cb4)
Thanks
[@&#8203;dependabot](https://redirect.github.com/apps/dependabot)! -
chore: update dependencies of "miniflare" package

    The following dependency versions have been updated:

    | Dependency                | From          | To            |
    | ------------------------- | ------------- | ------------- |
    | workerd                   | 1.20241004.0  | 1.20241011.1  |
|
[@&#8203;cloudflare/workers-types](https://redirect.github.com/cloudflare/workers-types)
| ^4.20241004.0 | ^4.20241011.0 |

-
[#&#8203;6943](https://redirect.github.com/cloudflare/workers-sdk/pull/6943)
[`7859a04`](7859a04bcd)
Thanks [@&#8203;sdnts](https://redirect.github.com/sdnts)! - fix: local
queues now respect consumer max delays and retry delays properly

###
[`v3.20241004.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/miniflare/CHANGELOG.md#3202410040)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/miniflare@3.20240925.1...miniflare@3.20241004.0)

##### Patch Changes

-
[#&#8203;6949](https://redirect.github.com/cloudflare/workers-sdk/pull/6949)
[`c863183`](c86318354f)
Thanks
[@&#8203;dependabot](https://redirect.github.com/apps/dependabot)! -
chore: update dependencies of "miniflare" package

    The following dependency versions have been updated:

    | Dependency                | From          | To            |
    | ------------------------- | ------------- | ------------- |
    | workerd                   | 1.20240925.0  | 1.20241004.0  |
|
[@&#8203;cloudflare/workers-types](https://redirect.github.com/cloudflare/workers-types)
| ^4.20240925.0 | ^4.20241004.0 |

###
[`v3.20240925.1`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/miniflare/CHANGELOG.md#3202409251)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/miniflare@3.20240925.0...miniflare@3.20240925.1)

##### Patch Changes

-
[#&#8203;6835](https://redirect.github.com/cloudflare/workers-sdk/pull/6835)
[`5c50949`](5c50949480)
Thanks [@&#8203;emily-shen](https://redirect.github.com/emily-shen)! -
fix: rename asset plugin options slightly to match wrangler.toml better

    Renamed `path` -> `directory`, `bindingName` -> `binding`.

</details>

<details>
<summary>tailwindlabs/tailwindcss (tailwindcss)</summary>

###
[`v3.4.14`](https://redirect.github.com/tailwindlabs/tailwindcss/releases/tag/v3.4.14)

[Compare
Source](https://redirect.github.com/tailwindlabs/tailwindcss/compare/v3.4.13...v3.4.14)

##### Fixed

- Don't set `display: none` on elements that use `hidden="until-found"`
([#&#8203;14625](https://redirect.github.com/tailwindlabs/tailwindcss/pull/14625))

</details>

<details>
<summary>microsoft/TypeScript (typescript)</summary>

###
[`v5.6.3`](https://redirect.github.com/microsoft/TypeScript/compare/v5.6.2...d48a5cf89a62a62d6c6ed53ffa18f070d9458b85)

[Compare
Source](https://redirect.github.com/microsoft/TypeScript/compare/v5.6.2...v5.6.3)

</details>

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

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

[Compare
Source](https://redirect.github.com/vitejs/vite/compare/v5.4.8...v5.4.9)

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

</details>

<details>
<summary>cloudflare/workers-sdk (wrangler)</summary>

###
[`v3.81.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3810)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.80.5...wrangler@3.81.0)

##### Minor Changes

-
[#&#8203;6990](https://redirect.github.com/cloudflare/workers-sdk/pull/6990)
[`586c253`](586c253f7d)
Thanks
[@&#8203;courtney-sims](https://redirect.github.com/courtney-sims)! -
feat: Adds new detailed pages deployment output type

##### Patch Changes

-
[#&#8203;6963](https://redirect.github.com/cloudflare/workers-sdk/pull/6963)
[`a5ac45d`](a5ac45d7d5)
Thanks [@&#8203;RamIdeas](https://redirect.github.com/RamIdeas)! - fix:
make `wrangler dev --remote` respect wrangler.toml's `account_id`
property.

This was a regression in the `--x-dev-env` flow recently turned on by
default.

-
[#&#8203;6996](https://redirect.github.com/cloudflare/workers-sdk/pull/6996)
[`b8ab809`](b8ab8093b9)
Thanks [@&#8203;emily-shen](https://redirect.github.com/emily-shen)! -
fix: improve error messaging when accidentally using Workers commands in
Pages project

If we detect a Workers command used with a Pages project (i.e.
wrangler.toml contains `pages_output_build_dir`), error with Pages
version of command rather than "missing entry-point" etc.

###
[`v3.80.5`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3805)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.80.4...wrangler@3.80.5)

##### Patch Changes

- Updated dependencies
\[[`5761020`](5761020cb4),
[`7859a04`](7859a04bcd)]:
    -   miniflare@3.20241011.0

###
[`v3.80.4`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3804)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.80.3...wrangler@3.80.4)

##### Patch Changes

-
[#&#8203;6937](https://redirect.github.com/cloudflare/workers-sdk/pull/6937)
[`51aedd4`](51aedd4333)
Thanks [@&#8203;lrapoport-cf](https://redirect.github.com/lrapoport-cf)!
- fix: show help when kv commands are run without parameters

- Updated dependencies
\[[`c863183`](c86318354f),
[`fd43068`](fd430687ec)]:
    -   miniflare@3.20241004.0
-
[@&#8203;cloudflare/workers-shared](https://redirect.github.com/cloudflare/workers-shared)[@&#8203;0](https://redirect.github.com/0).6.0

###
[`v3.80.3`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3803)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.80.2...wrangler@3.80.3)

##### Patch Changes

-
[#&#8203;6927](https://redirect.github.com/cloudflare/workers-sdk/pull/6927)
[`2af75ed`](2af75edb3c)
Thanks [@&#8203;emily-shen](https://redirect.github.com/emily-shen)! -
fix: respect `CLOUDFLARE_ACCOUNT_ID` with `wrangler pages project`
commands

Fixes
[#&#8203;4947](https://redirect.github.com/cloudflare/workers-sdk/issues/4947)

-
[#&#8203;6894](https://redirect.github.com/cloudflare/workers-sdk/pull/6894)
[`eaf71b8`](eaf71b86cc)
Thanks
[@&#8203;petebacondarwin](https://redirect.github.com/petebacondarwin)!
- fix: improve the rendering of build errors when bundling

-
[#&#8203;6920](https://redirect.github.com/cloudflare/workers-sdk/pull/6920)
[`2e64968`](2e649686c2)
Thanks [@&#8203;vicb](https://redirect.github.com/vicb)! - chore: update
unenv dependency version

Pulls in [feat(node/net): implement Server
mock](https://redirect.github.com/unjs/unenv/pull/316).

-
[#&#8203;6932](https://redirect.github.com/cloudflare/workers-sdk/pull/6932)
[`4c6aad0`](4c6aad05b9)
Thanks [@&#8203;vicb](https://redirect.github.com/vicb)! - fix: allow
`require`ing unenv aliased packages

    Before this PR `require`ing packages aliased in unenv would fail.
    That's because `require` would load the mjs file.

This PR adds wraps the mjs file in a virtual ES module to allow
`require`ing it.

###
[`v3.80.2`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3802)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.80.1...wrangler@3.80.2)

##### Patch Changes

-
[#&#8203;6923](https://redirect.github.com/cloudflare/workers-sdk/pull/6923)
[`1320f20`](1320f20b38)
Thanks [@&#8203;andyjessop](https://redirect.github.com/andyjessop)! -
chore: adds eslint-disable for ESLint error on empty typescript
interface in workers-configuration.d.ts

###
[`v3.80.1`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3801)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.80.0...wrangler@3.80.1)

##### Patch Changes

-
[#&#8203;6908](https://redirect.github.com/cloudflare/workers-sdk/pull/6908)
[`d696850`](d6968507b7)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - fix:
debounce restarting worker on assets dir file changes when `--x-dev-env`
is enabled.

-
[#&#8203;6902](https://redirect.github.com/cloudflare/workers-sdk/pull/6902)
[`dc92af2`](dc92af28c5)
Thanks
[@&#8203;threepointone](https://redirect.github.com/threepointone)! -
fix: enable esbuild's keepNames: true to set .name on functions/classes

-
[#&#8203;6909](https://redirect.github.com/cloudflare/workers-sdk/pull/6909)
[`82180a7`](82180a7a76)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - fix:
Various fixes for logging in `--x-dev-env`, primarily to ensure the
hotkeys don't wipe useful output and are cleaned up correctly

-
[#&#8203;6903](https://redirect.github.com/cloudflare/workers-sdk/pull/6903)
[`54924a4`](54924a4303)
Thanks
[@&#8203;petebacondarwin](https://redirect.github.com/petebacondarwin)!
- fix: ensure that `alias` config gets passed through to the bundler
when using new `--x-dev-env`

Fixes
[#&#8203;6898](https://redirect.github.com/cloudflare/workers-sdk/issues/6898)

-
[#&#8203;6911](https://redirect.github.com/cloudflare/workers-sdk/pull/6911)
[`30b7328`](30b7328073)
Thanks [@&#8203;emily-shen](https://redirect.github.com/emily-shen)! -
fix: infer experimentalJsonConfig from file extension

Fixes
[#&#8203;5768](https://redirect.github.com/cloudflare/workers-sdk/issues/5768)
- issue with vitest and Pages projects with wrangler.toml

- Updated dependencies
\[[`5c50949`](5c50949480)]:
    -   miniflare@3.20240925.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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMjAuMSIsInVwZGF0ZWRJblZlciI6IjM4LjEyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 09:18:08 +05:30
renovate[bot]
a3a83635f7 Update pre-commit dependencies (#13850)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[abravalheri/validate-pyproject](https://redirect.github.com/abravalheri/validate-pyproject)
| repository | minor | `v0.20.2` -> `v0.21` |
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | minor | `v0.6.9` -> `v0.7.0` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | minor | `v1.25.0` -> `v1.26.0` |
|
[executablebooks/mdformat](https://redirect.github.com/executablebooks/mdformat)
| repository | patch | `0.7.17` -> `0.7.18` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>abravalheri/validate-pyproject
(abravalheri/validate-pyproject)</summary>

###
[`v0.21`](https://redirect.github.com/abravalheri/validate-pyproject/releases/tag/v0.21)

[Compare
Source](https://redirect.github.com/abravalheri/validate-pyproject/compare/v0.20.2...v0.21)

#### What's Changed

- Added support PEP 735 by
[@&#8203;henryiii](https://redirect.github.com/henryiii) in
[https://github.com/abravalheri/validate-pyproject/pull/208](https://redirect.github.com/abravalheri/validate-pyproject/pull/208)
- Added support PEP 639 by
[@&#8203;henryiii](https://redirect.github.com/henryiii) in
[https://github.com/abravalheri/validate-pyproject/pull/210](https://redirect.github.com/abravalheri/validate-pyproject/pull/210)
- Renamed testing extra to test by
[@&#8203;henryiii](https://redirect.github.com/henryiii) in
[https://github.com/abravalheri/validate-pyproject/pull/212](https://redirect.github.com/abravalheri/validate-pyproject/pull/212)
-   General updates in CI setup

**Full Changelog**:
https://github.com/abravalheri/validate-pyproject/compare/v0.20.2...v0.21

</details>

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

###
[`v0.7.0`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.7.0)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.0)

See: https://github.com/astral-sh/ruff/releases/tag/0.7.0

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.26.0`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.26.0)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.25.0...v1.26.0)

#### \[1.26.0] - 2024-10-07

##### Compatibility

-   *(pre-commit)* Requires 3.2+

##### Fixes

- *(pre-commit)* Resolve deprecations in 4.0 about deprecated stage
names

</details>

<details>
<summary>executablebooks/mdformat (executablebooks/mdformat)</summary>

###
[`v0.7.18`](https://redirect.github.com/executablebooks/mdformat/compare/0.7.17...0.7.18)

[Compare
Source](https://redirect.github.com/executablebooks/mdformat/compare/0.7.17...0.7.18)

</details>

---

### Configuration

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

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

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

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMjAuMSIsInVwZGF0ZWRJblZlciI6IjM4LjEyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 09:14:43 +05:30
renovate[bot]
28e995023d Update dependency tomli_w to v1.1.0 (#13849)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [tomli_w](https://redirect.github.com/hukkin/tomli-w)
([changelog](https://redirect.github.com/hukkin/tomli-w/blob/master/CHANGELOG.md))
| `==1.0.0` -> `==1.1.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/tomli_w/1.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/tomli_w/1.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/tomli_w/1.0.0/1.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/tomli_w/1.0.0/1.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>hukkin/tomli-w (tomli_w)</summary>

###
[`v1.1.0`](https://redirect.github.com/hukkin/tomli-w/blob/HEAD/CHANGELOG.md#110)

[Compare
Source](https://redirect.github.com/hukkin/tomli-w/compare/1.0.0...1.1.0)

-   Removed
    -   Support for Python 3.7 and 3.8
-   Added
- Accept generic `collections.abc.Mapping`, not just `dict`, as input.
Thank you [Watal M. Iwasaki](https://redirect.github.com/heavywatal) for
the
        [PR](https://redirect.github.com/hukkin/tomli-w/pull/46).
- `indent` keyword argument for customizing indent width of arrays.
Thank you [Wim Jeantine-Glenn](https://redirect.github.com/wimglenn) for
the
        [PR](https://redirect.github.com/hukkin/tomli-w/pull/49).
-   Type annotations
- Type annotate `dump` function's output stream object as
`typing.IO[bytes]` (previously `typing.BinaryIO`)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMjAuMSIsInVwZGF0ZWRJblZlciI6IjM4LjEyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 09:05:30 +05:30
renovate[bot]
a80d515be3 Update Rust crate uuid to v1.11.0 (#13845) 2024-10-20 22:18:49 -05:00
renovate[bot]
9477058790 Update Rust crate serde_json to v1.0.132 (#13848) 2024-10-20 22:18:16 -05:00
renovate[bot]
19a2fd1c82 Update Rust crate fern to 0.7.0 (#13844) 2024-10-21 01:51:49 +00:00
renovate[bot]
cf26676c60 Update Rust crate syn to v2.0.82 (#13842) 2024-10-21 01:50:19 +00:00
renovate[bot]
7060bf87c0 Update Rust crate proc-macro2 to v1.0.88 (#13841) 2024-10-21 01:49:56 +00:00
renovate[bot]
ca8f10862e Update Rust crate anyhow to v1.0.90 (#13839) 2024-10-21 01:49:36 +00:00
renovate[bot]
c9b74eda4a Update Rust crate libc to v0.2.161 (#13840) 2024-10-21 01:49:11 +00:00
renovate[bot]
a5d52b00ca Update dependency ruff to v0.7.0 (#13847) 2024-10-21 01:48:00 +00:00
renovate[bot]
efa798b4c4 Update dependency mdformat to v0.7.18 (#13843) 2024-10-21 01:46:21 +00:00
Alex Waygood
02bca9a8d0 Modernize build scripts (#13837)
Use the modern `cargo::KEY=VALUE` syntax that was stabilised in MSRV 1.77, rather than the deprecated `cargo:KEY=VALUE` syntax.
2024-10-20 22:35:35 +01:00
Alex Waygood
72adb09bf3 Simplify iteration idioms (#13834)
Remove unnecessary uses of `.as_ref()`, `.iter()`, `&**` and similar, mostly in situations when iterating over variables. Many of these changes are only possible following #13826, when we bumped our MSRV to 1.80: several useful implementations on `&Box<[T]>` were only stabilised in Rust 1.80. Some of these changes we could have done earlier, however.
2024-10-20 22:25:27 +01:00
Aditya Pratap Singh
7fd8e30eed [red-knot] Cleanup generated names of mdtest tests (#13831)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-20 15:11:53 +00:00
renovate[bot]
7ca3571194 Update Rust crate pep440_rs to 0.7.1 (#13654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-20 11:52:55 +02:00
Micha Reiser
27c50bebec Bump MSRV to Rust 1.80 (#13826) 2024-10-20 10:55:36 +02:00
Alex Waygood
075e378b0f Update BREAKING_CHANGES.md for Ruff 0.7 (#13828) 2024-10-20 10:32:58 +02:00
Shaygan Hooshyari
0f0fff4d5a [red-knot] Implement more types in binary and unary expressions (#13803)
Implemented some points from
https://github.com/astral-sh/ruff/issues/12701

- Handle Unknown and Any in Unary operation
- Handle Boolean in binary operations
- Handle instances in unary operation
- Consider division by False to be division by zero

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-20 01:57:21 +00:00
Neil Mitchell
2d2baeca23 [python_ast] Make the iter_mut functions public (#13542) 2024-10-19 20:04:00 +01:00
cake-monotone
fb66f715f3 [red-knot] Enhancing Diagnostics for Compare Expression Inference (#13819)
## Summary

- Refactored comparison type inference functions in `infer.rs`: Changed
the return type from `Option` to `Result` to lay the groundwork for
providing more detailed diagnostics.
- Updated diagnostic messages.

This is a small step toward improving diagnostics in the future.

Please refer to #13787

## Test Plan

mdtest included!

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-19 18:17:01 +00:00
Alex Waygood
55bccf6680 [red-knot] Fix edge case for binary-expression inference where the lhs and rhs are the exact same type (#13823)
## Summary

This fixes an edge case that @carljm and I missed when implementing
https://github.com/astral-sh/ruff/pull/13800. Namely, if the left-hand
operand is the _exact same type_ as the right-hand operand, the
reflected dunder on the right-hand operand is never tried:

```pycon
>>> class Foo:
...     def __radd__(self, other):
...         return 42
...         
>>> Foo() + Foo()
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    Foo() + Foo()
    ~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for +: 'Foo' and 'Foo'
```

This edge case _is_ covered in Brett's blog at
https://snarky.ca/unravelling-binary-arithmetic-operations-in-python/,
but I missed it amongst all the other subtleties of this algorithm. The
motivations and history behind it were discussed in
https://mail.python.org/archives/list/python-dev@python.org/thread/7NZUCODEAPQFMRFXYRMGJXDSIS3WJYIV/

## Test Plan

I added an mdtest for this cornercase.
2024-10-19 11:09:54 -07:00
Carl Meyer
f4b5e70fae [red-knot] binary arithmetic on instances (#13800)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-19 15:22:54 +00:00
Alex Waygood
36cb1199cc [red-knot] Autoformat mdtest Python snippets using blacken-docs (#13809) 2024-10-19 15:57:06 +01:00
Micha Reiser
2ff36530c3 Upgrade to Rust 1.82 (#13816) 2024-10-19 16:05:50 +02:00
Micha Reiser
bd33b4972d Short circuit lex_identifier if the name is longer or shorter than any known keyword (#13815) 2024-10-19 11:07:15 +00:00
David Peter
6964eef369 [red knot] add Type::is_disjoint_from and intersection simplifications (#13775)
## Summary

- Add `Type::is_disjoint_from` as a way to test whether two types
overlap
- Add a first set of simplification rules for intersection types
  - `S & T = S` for `S <: T`
  - `S & ~T = Never` for `S <: T`
  - `~S & ~T = ~T` for `S <: T`
  - `A & ~B = A` for `A` disjoint from `B`
  - `A & B = Never` for `A` disjoint from `B`
  - `bool & ~Literal[bool] = Literal[!bool]`

resolves one item in #12694

## Open questions:

- Can we somehow leverage the (anti) symmetry between `positive` and
`negative` contributions? I could imagine that there would be a way if
we had `Type::Not(type)`/`Type::Negative(type)`, but with the
`positive`/`negative` architecture, I'm not sure. Note that there is a
certain duplication in the `add_positive`/`add_negative` functions (e.g.
`S & ~T = Never` is implemented twice), but other rules are actually not
perfectly symmetric: `S & T = S` vs `~S & ~T = ~T`.
- I'm not particularly proud of the way `add_positive`/`add_negative`
turned out. They are long imperative-style functions with some
mutability mixed in (`to_remove`). I'm happy to look into ways to
improve this code *if we decide to go with this approach* of
implementing a set of ad-hoc rules for simplification.
- ~~Is it useful to perform simplifications eagerly in
`add_positive`/`add_negative`? (@carljm)~~ This is what I did for now.

## Test Plan

- Unit tests for `Type::is_disjoint_from`
- Observe changes in Markdown-based tests
- Unit tests for `IntersectionBuilder::build()`

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-18 21:34:43 +00:00
Alex Waygood
c93a7c7878 Set fail_fast: false in .pre-commit-config.yaml (#13811) 2024-10-18 16:03:59 +01:00
Micha Reiser
6d7da7bdbe Revert "Upgrade to Rust 1.82 toolchain" (#13810) 2024-10-18 12:18:26 +00:00
Micha Reiser
ff72055558 Upgrade to Rust 1.82 toolchain (#13808) 2024-10-18 12:08:15 +00:00
Steve C
4ecfe95295 Update to macOS14 runner image (#13728)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-18 11:43:30 +02:00
David Peter
c2f7c39987 [red-knot] mdtest suite: formatting and cleanup (#13806)
Minor cleanup and consistent formatting of the Markdown-based tests.

- Removed lots of unnecessary `a`, `b`, `c`, … variables.
- Moved test assertions (`# revealed:` comments) closer to the tested
object.
- Always separate `# revealed` and `# error` comments from the code by
two spaces, according to the discussion
[here](https://github.com/astral-sh/ruff/pull/13746/files#r1799385758).
This trades readability for consistency in some cases.
- Fixed some headings
2024-10-18 11:07:53 +02:00
Matthew Spero
f80528fbf2 Make ARG002 compatible with EM101 when raising NotImplementedError (#13714)
## Summary

This pull request resolves some rule thrashing identified in #12427 by
allowing for unused arguments when using `NotImplementedError` with a
variable per [this
comment](https://github.com/astral-sh/ruff/issues/12427#issuecomment-2384727468).

**Note**

This feels a little heavy-handed / edge-case-prone. So, to be clear, I'm
happy to scrap this code and just update the docs to communicate that
`abstractmethod` and friends should be used in this scenario (or
similar). Just let me know what you'd like done!

fixes: #12427 

## Test Plan

I added a test-case to the existing `ARG.py` file and ran...

```sh
cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py --no-cache --preview --select ARG002
```

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-10-18 06:44:22 +00:00
Dhruv Manilawala
040a591cad Avoid indexing the workspace for single-file mode (#13770)
## Summary

This PR updates the language server to avoid indexing the workspace for
single-file mode.

**What's a single-file mode?**

When a user opens the file directly in an editor, and not the folder
that represents the workspace, the editor usually can't determine the
workspace root. This means that during initializing the server, the
`workspaceFolders` field will be empty / nil.

Now, in this case, the server defaults to using the current working
directory which is a reasonable default assuming that the directory
would point to the one where this open file is present. This would allow
the server to index the directory itself for any config file, if
present.

It turns out that in VS Code the current working directory in the above
scenario is the system root directory `/` and so the server will try to
index the entire root directory which would take a lot of time. This is
the issue as described in
https://github.com/astral-sh/ruff-vscode/issues/627. To reproduce, refer
https://github.com/astral-sh/ruff-vscode/issues/627#issuecomment-2401440767.

This PR updates the indexer to avoid traversing the workspace to read
any config file that might be present. The first commit
(8dd2a31eef)
refactors the initialization and introduces two structs `Workspaces` and
`Workspace`. The latter struct includes a field to determine whether
it's the default workspace. The second commit
(61fc39bdb6)
utilizes this field to avoid traversing.

Closes: #11366

## Editor behavior

This is to document the behavior as seen in different editors. The test
scenario used has the following directory tree structure:
```
.
├── nested
│   ├── nested.py
│   └── pyproject.toml
└── test.py
```

where, the contents of the files are:

**test.py**
```py
import os
```

**nested/nested.py**
```py
import os
import math
```

**nested/pyproject.toml**
```toml
[tool.ruff.lint]
select = ["I"]
```

Steps:
1. Open `test.py` directly in the editor
2. Validate that it raises the `F401` violation
3. Open `nested/nested.py` in the same editor instance
4. This file would raise only `I001` if the `nested/pyproject.toml` was
indexed

### VS Code

When (1) is done from above, the current working directory is `/` which
means the server will try to index the entire system to build up the
settings index. This will include the `nested/pyproject.toml` file as
well. This leads to bad user experience because the user would need to
wait for minutes for the server to finish indexing.

This PR avoids that by not traversing the workspace directory in
single-file mode. But, in VS Code, this means that per (4), the file
wouldn't raise `I001` but only raise two `F401` violations because the
`nested/pyproject.toml` was never resolved.

One solution here would be to fix this in the extension itself where we
would detect this scenario and pass in the workspace directory that is
the one containing this open file in (1) above.

### Neovim

**tl;dr** it works as expected because the client considers the presence
of certain files (depending on the server) as the root of the workspace.
For Ruff, they are `pyproject.toml`, `ruff.toml`, and `.ruff.toml`. This
means that the client notifies us as the user moves between single-file
mode and workspace mode.

https://github.com/astral-sh/ruff/pull/13770#issuecomment-2416608055

### Helix

Same as Neovim, additional context in
https://github.com/astral-sh/ruff/pull/13770#issuecomment-2417362097

### Sublime Text

**tl;dr** It works similar to VS Code except that the current working
directory of the current process is different and thus the config file
is never read. So, the behavior remains unchanged with this PR.

https://github.com/astral-sh/ruff/pull/13770#issuecomment-2417362097

### Zed

Zed seems to be starting a separate language server instance for each
file when the editor is running in a single-file mode even though all
files have been opened in a single editor instance.

(Separated the logs into sections separated by a single blank line
indicating 3 different server instances that the editor started for 3
files.)

```
   0.000053375s  INFO main ruff_server::server: No workspace settings found for file:///Users/dhruv/projects/ruff-temp, using default settings
   0.009448792s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/projects/ruff-temp
   0.009906334s DEBUG ruff:main ruff_server::resolve: Included path via `include`: /Users/dhruv/projects/ruff-temp/test.py
   0.011775917s  INFO ruff:main ruff_server::server: Configuration file watcher successfully registered

   0.000060583s  INFO main ruff_server::server: No workspace settings found for file:///Users/dhruv/projects/ruff-temp/nested, using default settings
   0.010387125s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/projects/ruff-temp/nested
   0.011061875s DEBUG ruff:main ruff_server::resolve: Included path via `include`: /Users/dhruv/projects/ruff-temp/nested/nested.py
   0.011545208s  INFO ruff:main ruff_server::server: Configuration file watcher successfully registered

   0.000059125s  INFO main ruff_server::server: No workspace settings found for file:///Users/dhruv/projects/ruff-temp/nested, using default settings
   0.010857583s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/projects/ruff-temp/nested
   0.011428958s DEBUG ruff:main ruff_server::resolve: Included path via `include`: /Users/dhruv/projects/ruff-temp/nested/other.py
   0.011893792s  INFO ruff:main ruff_server::server: Configuration file watcher successfully registered
```

## Test Plan

When using the `ruff` server from this PR, we see that the server starts
quickly as seen in the logs. Next, when I switch to the release binary,
it starts indexing the root directory.

For more details, refer to the "Editor Behavior" section above.
2024-10-18 10:51:43 +05:30
Raphael Gaschignard
3d0bdb426a [red-knot] Use the right scope when considering class bases (#13766)
Summary
---------

PEP 695 Generics introduce a scope inside a class statement's arguments
and keywords.

```
class C[T](A[T]):  # the T in A[T] is not from the global scope but from a type-param-specfic scope
   ...
```

When doing inference on the class bases, we currently have been doing
base class expression lookups in the global scope. Not an issue without
generics (since a scope is only created when generics are present).

This change instead makes sure to stop the global scope inference from
going into expressions within this sub-scope. Since there is a separate
scope, `check_file` and friends will trigger inference on these
expressions still.

Another change as a part of this is making sure that `ClassType` looks
up its bases in the right scope.

Test Plan
----------
`cargo test --package red_knot_python_semantic generics` will run the
markdown test that previously would panic due to scope lookup issues

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-17 22:29:46 +00:00
Carl Meyer
e2a30b71f4 [red-knot] revert change to emit fewer division by zero errors (#13801)
This reverts https://github.com/astral-sh/ruff/pull/13799, and restores
the previous behavior, which I think was the most pragmatic and useful
version of the divide-by-zero error, if we will emit it at all.

In general, a type checker _does_ emit diagnostics when it can detect
something that will definitely be a problem for some inhabitants of a
type, but not others. For example, `x.foo` if `x` is typed as `object`
is a type error, even though some inhabitants of the type `object` will
have a `foo` attribute! The correct fix is to make your type annotations
more precise, so that `x` is assigned a type which definitely has the
`foo` attribute.

If we will emit it divide-by-zero errors, it should follow the same
logic. Dividing an inhabitant of the type `int` by zero may not emit an
error, if the inhabitant is an instance of a subclass of `builtins.int`
that overrides division. But it may emit an error (more likely it will).
If you don't want the diagnostic, you can clarify your type annotations
to require an instance of your safe subclass.

Because the Python type system doesn't have the ability to explicitly
reflect the fact that divide-by-zero is an error in type annotations
(e.g. for `int.__truediv__`), or conversely to declare a type as safe
from divide-by-zero, or include a "nonzero integer" type which it is
always safe to divide by, the analogy doesn't fully apply. You can't
explicitly mark your subclass of `int` as safe from divide-by-zero, we
just semi-arbitrarily choose to silence the diagnostic for subclasses,
to avoid false positives.

Also, if we fully followed the above logic, we'd have to error on every
`int / int` because the RHS `int` might be zero! But this would likely
cause too many false positives, because of the lack of a "nonzero
integer" type.

So this is just a pragmatic choice to emit the diagnostic when it is
very likely to be an error. It's unclear how useful this diagnostic is
in practice, but this version of it is at least very unlikely to cause
harm.
2024-10-17 20:17:22 +00:00
Carl Meyer
5c537b6dbb [red-knot] don't emit divide-by-zero error if we can't be sure (#13799)
If the LHS is just `int` or `float` type, that type includes custom
subclasses which can arbitrarily override division behavior, so we
shouldn't emit a divide-by-zero error in those cases.

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-17 17:11:07 +00:00
Alex Waygood
5e6de4e0c6 Changelog for Ruff v0.7 (#13794)
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-10-17 16:14:21 +00:00
Zanie Blue
70e5c4a8ba Recode TRY302 to TRY203 (#13502)
Closes https://github.com/astral-sh/ruff/issues/13492
2024-10-17 16:35:12 +01:00
Micha Reiser
9218d6bedc Remove allow-unused-imports setting from the common lint options (#13677)
Fixes https://github.com/astral-sh/ruff/issues/13668
2024-10-17 16:35:12 +01:00
Alex Waygood
1b79ae9817 [ruff-0.7] Stabilise the expansion of open-file-with-context-handler to work with other standard-library IO modules (SIM115) (#13680)
Closes #7313.
2024-10-17 16:35:12 +01:00
Alexey Preobrazhenskiy
2b87587ac2 [flake8-pytest-style] Fix defaults when lint.flake8-pytest-style config section is empty (PT001, PT023) (#13292) 2024-10-17 16:35:12 +01:00
Micha Reiser
d1e15f6246 Remove tab-size setting (#12835)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Closes https://github.com/astral-sh/ruff/issues/12041
2024-10-17 16:35:12 +01:00
Micha Reiser
89a82158a1 Remove error messages for removed CLI aliases (#12833)
Closes https://github.com/astral-sh/ruff/issues/10171
2024-10-17 16:35:12 +01:00
Micha Reiser
202c6a6d75 Remove output-format=text setting (#12836) 2024-10-17 16:35:12 +01:00
David Peter
5c3c0c4705 [red-knot] Inference for comparison of union types (#13781)
## Summary

Add type inference for comparisons involving union types. For example:
```py
one_or_two = 1 if flag else 2

reveal_type(one_or_two <= 2)  # revealed: Literal[True]
reveal_type(one_or_two <= 1)  # revealed: bool
reveal_type(one_or_two <= 0)  # revealed: Literal[False]
```

closes #13779

## Test Plan

See `resources/mdtest/comparison/unions.md`
2024-10-17 11:03:37 +02:00
Simon Brugman
6b7a738825 Add explanation of fixable in --statistics command (#13774)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-17 08:02:00 +02:00
Santhosh Solomon
4ea4bbb155 [flake8-bandit] Detect patterns from multi line SQL statements (S608) (#13574)
Co-authored-by: Santhosh Solomon <santhosh@advarisk.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-17 05:42:03 +00:00
aditya pillai
ed4a0b34ba [red-knot] don't include Unknown in the type for a conditionally-defined import (#13563)
## Summary

Fixes the bug described in #13514 where an unbound public type defaulted
to the type or `Unknown`, whereas it should only be the type if unbound.

## Test Plan

Added a new test case

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-16 13:46:03 -07:00
Micha Reiser
2095ea8372 Add scope assertion to TypeInference.extend (#13764)
## Summary

This PR adds a debug assertion that asserts that `TypeInference::extend`
is only called on results that have the same scope.
This is critical because `expressions` uses `ScopedExpressionId` that
are local and merging expressions from different
scopes would lead to incorrect expression types.

We could consider storing `scope` only on `TypeInference` for debug
builds. Doing so has the advantage that the `TypeInference` type is
smaller of which we'll have many. However, a `ScopeId` is a `u32`... so
it shouldn't matter that much and it avoids storing the `scope` both on
`TypeInference` and `TypeInferenceBuilder`

## Test Plan

`cargo test`
2024-10-16 08:44:25 -07:00
Alex Waygood
6282402a8c [red-knot] Add control flow for try/except blocks (#13729) 2024-10-16 13:03:59 +00:00
Raphael Gaschignard
d25673f664 [red-knot] Do not panic if named expressions show up in assignment position (#13711)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-16 12:42:39 +00:00
Micha Reiser
a94914dc35 Enable preview mode for 'unstable' black tests (#13776) 2024-10-16 12:25:34 +00:00
cake-monotone
2ffc3fad47 [red-knot] Implement Type::Tuple Comparisons (#13712)
## Summary

This PR implements comparisons for (tuple, tuple).

It will close #13688 and complete an item in #13618 once merged.

## Test Plan

Basic tests are included for (tuple, tuple) comparisons.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-16 11:39:55 +00:00
Micha Reiser
8f5b2aac9a Refactor: Remove StringPart and AnyStringPart in favor of StringLikePart (#13772) 2024-10-16 12:52:06 +02:00
David Peter
b85be6297e [red knot] Minor follow-up tasks regarding singleton types (#13769)
## Summary

- Do not treat empty tuples as singletons after discussion [1]
- Improve comment regarding intersection types
- Resolve unnecessary TODO in Markdown test

[1]
https://discuss.python.org/t/should-we-specify-in-the-language-reference-that-the-empty-tuple-is-a-singleton/67957

## Test Plan

—
2024-10-16 11:30:03 +02:00
Alex Waygood
fb1d1e3241 [red-knot] Simplify some branches in infer_subscript_expression (#13762)
## Summary

Just a small simplification to remove some unnecessary complexity here.
Rather than using separate branches for subscript expressions involving
boolean literals, we can simply convert them to integer literals and
reuse the logic in the `IntLiteral` branches.

## Test Plan

`cargo test -p red_knot_python_semantic`
2024-10-16 07:58:24 +01:00
Dhruv Manilawala
c6b311c546 Update setup image for PyCharm External Tool (#13767)
## Summary

fixes: #13765 

## Preview

<img width="624" alt="Screenshot 2024-10-16 at 10 05 57"
src="https://github.com/user-attachments/assets/c0eccda5-3cf1-4119-a9b5-d86b01a8c64c">
2024-10-16 04:41:37 +00:00
Dhruv Manilawala
b16f665a81 [red-knot] Infer target types for unpacked tuple assignment (#13316)
## Summary

This PR adds support for unpacking tuple expression in an assignment
statement where the target expression can be a tuple or a list (the
allowed sequence targets).

The implementation introduces a new `infer_assignment_target` which can
then be used for other targets like the ones in for loops as well. This
delegates it to the `infer_definition`. The final implementation uses a
recursive function that visits the target expression in source order and
compares the variable node that corresponds to the definition. At the
same time, it keeps track of where it is on the assignment value type.

The logic also accounts for the number of elements on both sides such
that it matches even if there's a gap in between. For example, if
there's a starred expression like `(a, *b, c) = (1, 2, 3)`, then the
type of `a` will be `Literal[1]` and the type of `b` will be
`Literal[2]`.

There are a couple of follow-ups that can be done:
* Use this logic for other target positions like `for` loop
* Add diagnostics for mis-match length between LHS and RHS

## Test Plan

Add various test cases using the new markdown test framework.
Validate that existing test cases pass.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-15 19:07:11 +00:00
Alex
d77480768d [red-knot] Port type inference tests to new test framework (#13719)
## Summary

Porting infer tests to new markdown tests framework.

Link to the corresponding issue: #13696

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-15 11:23:46 -07:00
github-actions[bot]
5fa82fb0cd Sync vendored typeshed stubs (#13753) 2024-10-15 13:36:11 +00:00
David Peter
74bf4b0653 [red knot] Fix narrowing for '… is not …' type guards, add '… is …' type guards (#13758)
## Summary

- Fix a bug with `… is not …` type guards.
 
  Previously, in an example like
  ```py
  x = [1]
  y = [1]
  
  if x is not y:
      reveal_type(x)
  ```
  we would infer a type of `list[int] & ~list[int] == Never` for `x`
  inside the conditional (instead of `list[int]`), since we built a
  (negative) intersection with the type of the right hand side (`y`).
  However, as this example shows, this assumption can only be made for
  singleton types (types with a single inhabitant) such as `None`.
- Add support for `… is …` type guards.

closes #13715

## Test Plan

Moved existing `narrow_…` tests to Markdown-based tests and added new
ones (including a regression test for the bug described above). Note
that will create some conflicts with
https://github.com/astral-sh/ruff/pull/13719. I tried to establish the
correct organizational structure as proposed in
https://github.com/astral-sh/ruff/pull/13719#discussion_r1800188105
2024-10-15 14:49:32 +02:00
Micha Reiser
5f65e842e8 Upgrade salsa (#13757) 2024-10-15 11:06:32 +00:00
Micha Reiser
72ac6cd5a5 Fix TODO directive out of bounds acccess (#13756) 2024-10-15 10:49:53 +02:00
David Peter
04b636cba2 [red knot] Use memmem::find instead of custom version (#13750)
This is a follow-up on #13746:

- Use `memmem::find` instead of rolling our own inferior version.
- Avoid `x.as_ref()` calls using `&**x`
2024-10-14 15:17:19 +02:00
Alex Waygood
6048f331d9 [red-knot] Add a build.rs file to red_knot_python_semantic, and document pitfalls of using rstest in combination with mdtest (#13747) 2024-10-14 13:02:03 +01:00
David Peter
93097f1c53 [red-knot] feat: Inference for BytesLiteral comparisons (#13746)
Implements inference for `BytesLiteral` comparisons along the lines of
https://github.com/astral-sh/ruff/pull/13634.

closes #13687

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-14 14:01:23 +02:00
Sid
9bb4722ebf [flake8-todos] Allow words starting with todo (#13640)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-14 10:21:45 +00:00
Dhruv Manilawala
5caabe54b6 Allow ipytest cell magic (#13745)
## Summary

fixes: #13718 

## Test Plan

Using the notebook as mentioned in
https://github.com/astral-sh/ruff/issues/13718#issuecomment-2410631674,
this PR does not give the "F821 Undefined name `test_sorted`"
diagnostic.
2024-10-14 15:48:33 +05:30
renovate[bot]
814ab47582 Update dependency @miniflare/storage-memory to v2.14.4 (#13737)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:52:28 +00:00
renovate[bot]
c3a3622e30 Update Rust crate libcst to v1.5.0 (#13739)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 09:51:13 +02:00
renovate[bot]
4ef422d3b4 Update Rust crate clap to v4.5.20 (#13733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 09:44:55 +02:00
renovate[bot]
58bc981677 Update Rust crate pathdiff to v0.2.2 (#13734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 09:41:33 +02:00
renovate[bot]
dd5018ac55 Update dependency @miniflare/kv to v2.14.4 (#13736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:40:49 +00:00
renovate[bot]
63df94b521 Update Rust crate proc-macro2 to v1.0.87 (#13735)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:39:46 +00:00
renovate[bot]
e4c0dd6f96 Update rust-wasm-bindgen monorepo (#13738)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:38:45 +00:00
Micha Reiser
3111dce5b4 Fix mkdocs CI job (#13744) 2024-10-14 09:31:35 +02:00
Micha Reiser
8445e4725c Downgrade benchmarks CI job to ubuntu 22 (#13743) 2024-10-14 09:17:38 +02:00
Alex Waygood
defdc4dd8e [red-knot] Use colors to improve readability of mdtest output (#13725) 2024-10-13 14:20:35 +01:00
Steve C
46bc69d1d4 [flake8-pyi] - fix dropped exprs in PYI030 autofix (#13727) 2024-10-13 11:33:03 +01:00
Carl Meyer
3209953276 [red-knot] clarify mdtest README (#13720)
Address a potential point of confusion that bit a contributor in
https://github.com/astral-sh/ruff/pull/13719

Also remove a no-longer-accurate line about bare `error: ` assertions
(which are no longer allowed) and clarify another point about which
kinds of error assertions to use.
2024-10-11 12:36:48 -07:00
Carl Meyer
6ae833e0c7 [red-knot] mdtest usability improvements for reveal_type (#13709)
## Summary

Fixes #13708.

Silence `undefined-reveal` diagnostic on any line including a `#
revealed:` assertion.

Add more context to un-silenced `undefined-reveal` diagnostics in mdtest
test failures. This doesn't make the failure output less verbose, but it
hopefully clarifies the right fix for an `undefined-reveal` in mdtest,
while still making it clear what red-knot's normal diagnostic for this
looks like.

## Test Plan

Added and updated tests.
2024-10-10 17:33:53 -07:00
Carl Meyer
a3dc5c0529 [red-knot] document test framework (#13695)
This adds documentation for the new test framework.

I also added documentation for the planned design of features we haven't
built yet (clearly marked as such), so that this doc can become the sole
source of truth for the test framework design (we don't need to refer
back to the original internal design document.)

Also fixes a few issues in the test framework implementation that were
discovered in writing up the docs.

---------

Co-authored-by: T-256 <132141463+T-256@users.noreply.github.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-10-10 12:02:01 -07:00
Alex Waygood
d6b24b690a [pycodestyle] Fix whitespace-related false positives and false negatives inside type-parameter lists (#13704) 2024-10-10 17:24:17 +01:00
Alex Waygood
5b4afd30ca Harmonise methods for distinguishing different Python source types (#13682) 2024-10-09 13:18:52 +00:00
Micha Reiser
b9827a4122 Remove layout values from AnyStringPart (#13681) 2024-10-09 07:25:40 +01:00
Carl Meyer
93eff7f174 [red-knot] type inference/checking test framework (#13636)
## Summary

Adds a markdown-based test framework for writing tests of type inference
and type checking. Fixes #11664.

Implements the basic required features. A markdown test file is a suite
of tests, each test can contain one or more Python files, with
optionally specified path/name. The test writes all files to an
in-memory file system, runs red-knot, and matches the resulting
diagnostics against `Type: ` and `Error: ` assertions embedded in the
Python source as comments.

We will want to add features like incremental tests, setting custom
configuration for tests, writing non-Python files, testing syntax
errors, capturing full diagnostic output, etc. There's also plenty of
room for improved UX (colored output?).

## Test Plan

Lots of tests!

Sample of the current output when a test fails:

```
     Running tests/inference.rs (target/debug/deps/inference-7c96590aa84de2a4)

running 1 test
test inference::path_1_resources_inference_numbers_md ... FAILED

failures:

---- inference::path_1_resources_inference_numbers_md stdout ----
inference/numbers.md - Numbers - Floats
  /src/test.py
    line 2: unexpected error: [invalid-assignment] "Object of type `Literal["str"]` is not assignable to `int`"

thread 'inference::path_1_resources_inference_numbers_md' panicked at crates/red_knot_test/src/lib.rs:60:5:
Some tests failed.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    inference::path_1_resources_inference_numbers_md

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s

error: test failed, to rerun pass `-p red_knot_test --test inference`
```

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-08 12:33:19 -07:00
Micha Reiser
fc661e193a Normalize implicit concatenated f-string quotes per part (#13539) 2024-10-08 09:59:17 +00:00
Zanie Blue
42fcbef876 Fix typo in allow-unused-imports documentation (#13669) 2024-10-07 14:08:36 -05:00
Alex Waygood
71b52b83e4 [red-knot] Allow type[] to be subscripted (#13667)
Fixed a TODO by adding another TODO. It's the red-knot way!

## Summary

`builtins.type` can be subscripted at runtime on Python 3.9+, even
though it has no `__class_getitem__` method and its metaclass (which
is... itself) has no `__getitem__` method. The special case is
[hardcoded directly into `PyObject_GetItem` in
CPython](744caa8ef4/Objects/abstract.c (L181-L184)).
We just have to replicate the special case in our semantic model.

This will fail at runtime on Python <3.9. However, there's a bunch of
outstanding questions (detailed in the TODO comment I added) regarding
how we deal with subscriptions of other generic types on lower Python
versions. Since we want to avoid too many false positives for now, I
haven't tried to address this; I've just made `type` subscriptable on
all Python versions.

## Test Plan

`cargo test -p red_knot_python_semantic --lib`
2024-10-07 19:43:47 +01:00
Zanie Blue
fb90f5a13d Add known limitation to C416 with dictionaries (#13627)
Part of https://github.com/astral-sh/ruff/issues/13625

See also #13629
2024-10-07 16:20:45 +00:00
Alex Waygood
d7484e6942 [red-knot] Improve type inference for except handlers where a tuple of exception classes is caught (#13646) 2024-10-07 16:13:06 +01:00
Dylan
14ee5dbfde [refurb] Count codepoints not bytes for slice-to-remove-prefix-or-suffix (FURB188) (#13631) 2024-10-07 16:13:28 +02:00
Alex Waygood
27ac34d683 Rework S606 (start-process-with-no-shell) docs to make clear the security motivations (#13658)
Helps with #13614. This docs rewrite draws on the [documentation for the
original bandit
rule](https://bandit.readthedocs.io/en/latest/plugins/b606_start_process_with_no_shell.html).
2024-10-07 13:31:01 +01:00
Sid
31ca1c3064 [flake8-async] allow async generators (ASYNC100) (#13639)
<!--
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

Treat async generators as "await" in ASYNC100.

Fixes #13637

## Test Plan

Updated snapshot
2024-10-07 07:25:54 -05:00
qdegraaf
646e4136d7 [flake8-bugbear] Tweak B905 message to not suggest setting parameter strict= to False (#13656)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-07 11:56:17 +00:00
Alex Waygood
58a11b33da Fixup docs markup for RUF027 (#13659) 2024-10-07 11:49:45 +00:00
renovate[bot]
7856e90a2c Update pre-commit dependencies (#13650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 10:16:58 +01:00
renovate[bot]
98878c9bf2 Update dependency tomli to v2.0.2 (#13649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 10:16:43 +01:00
Aleksei Latyshev
73aa6ea417 [refurb] implement hardcoded-string-charset (FURB156) (#13530)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-07 07:35:14 +00:00
renovate[bot]
38d872ea4c Update Rust crate hashbrown to 0.15.0 (#13652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-07 08:50:59 +02:00
renovate[bot]
824def2194 Update dependency ruff to v0.6.9 (#13648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:15:58 +02:00
renovate[bot]
2ab78dd6a5 Update NPM Development dependencies (#13651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:15:26 +02:00
renovate[bot]
03fa7f64dd Update Rust crate clap to v4.5.19 (#13647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:05:40 +02:00
renovate[bot]
43330225be Update Rust crate serde_with to v3.11.0 (#13655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:04:04 +02:00
renovate[bot]
383d9d9f6e Update Rust crate once_cell to v1.20.2 (#13653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:03:12 +02:00
Simon
8108f83810 [red-knot] feat: add StringLiteral and LiteralString comparison (#13634)
## Summary

Implements string literal comparisons and fallbacks to `str` instance
for `LiteralString`.
Completes an item in #13618

## Test Plan

- Adds a dedicated test with non exhaustive cases

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-05 12:22:30 -07:00
Simon
f1205177fd [red-knot] fix: when simplifying union, True & False -> instance(bool) (#13644) 2024-10-05 19:01:10 +01:00
Simon
1c2cafc101 [red-knot] more ergonomic and efficient handling of known builtin classes (#13615) 2024-10-05 18:03:46 +01:00
Alex Waygood
7c5a7d909c [red-knot] Improve tests relating to type inference for exception handlers (#13643) 2024-10-05 16:59:36 +00:00
Zanie Blue
2a365bb278 Mark PLE1141 fix as unsafe (#13629)
Closes https://github.com/astral-sh/ruff/issues/13343

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-04 14:22:26 -05:00
Zanie Blue
020f4d4a54 Add test cases for RUF006 with lambdas (#13628)
As discussed in https://github.com/astral-sh/ruff/issues/13619
2024-10-04 14:09:43 -05:00
Simon
888930b7d3 [red-knot] feat: implement integer comparison (#13571)
## Summary

Implements the comparison operator for `[Type::IntLiteral]` and
`[Type::BooleanLiteral]` (as an artifact of special handling of `True` and
`False` in python).
Sets the framework to implement more comparison for types known at
static time (e.g. `BooleanLiteral`, `StringLiteral`), allowing us to only
implement cases of the triplet `<left> Type`, `<right> Type`, `CmpOp`.
Contributes to #12701 (without checking off an item yet).

## Test Plan

- Added a test for the comparison of literals that should include most
cases of note.
- Added a test for the comparison of int instances

Please note that the cases do not cover 100% of the branches as there
are many and the current testing strategy with variables make this
fairly confusing once we have too many in one test.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-04 10:40:59 -07:00
Zanie Blue
d726f09cf0 Fix PTH123 false positive when open is passed a file descriptor (#13616)
Closes https://github.com/astral-sh/ruff/issues/12871

Includes some minor semantic type inference extensions changes to help
with reliably detecting integers
2024-10-04 08:48:47 -05:00
Dhruv Manilawala
975be9c1c6 Bump version to 0.6.9 (#13624) 2024-10-04 18:51:13 +05:30
Zanie Blue
99e4566fce Mark FURB118 fix as unsafe (#13613)
Closes https://github.com/astral-sh/ruff/issues/13421
2024-10-03 21:39:22 +00:00
Simon Høxbro Hansen
7ad07c2c5d Add allow-unused-imports setting for unused-import rule (F401) (#13601)
## Summary
Resolves https://github.com/astral-sh/ruff/issues/9962 by allowing a
configuration setting `allowed-unused-imports`

TODO:
- [x] Figure out the correct name and place for the setting; currently,
I have added it top level.
- [x] The comparison is pretty naive. I tried using `glob::Pattern` but
couldn't get it to work in the configuration.
- [x] Add tests
- [x] Update documentations

## Test Plan

`cargo test`
2024-10-03 19:44:44 +00:00
Bernát Gábor
4aefe52393 Support ruff discovery in pip build environments (#13591)
Resolves https://github.com/astral-sh/ruff/issues/13321.

Contents of overlay:
```bash
/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin:
total 26M
-rwxr-xr-x 1 bgabor8 staff 26M Oct  1 08:22 ruff
drwxr-xr-x 3 bgabor8 staff  96 Oct  1 08:22 .
drwxr-xr-x 4 bgabor8 staff 128 Oct  1 08:22 ..
```

Python executable:
```bash
'/Users/bgabor8/git/github/ruff-find-bin-during-build/.venv/bin/python'
```
PATH is:
```bash
['/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin',
 '/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/normal/bin',
'/Library/Frameworks/Python.framework/Versions/3.11/bin',
'/Library/Frameworks/Python.framework/Versions/3.12/bin',
```
Not sure where to add tests, there does not seem to be any existing one.
Can someone help me with that?
2024-10-03 17:38:07 +00:00
Zanie Blue
cc1f766622 Preserve trivia (i.e. comments) in PLR5501 (#13573)
Closes https://github.com/astral-sh/ruff/issues/13545

As described in the issue, we move comments before the inner `if`
statement to before the newly constructed `elif` statement (previously
`else`).
2024-10-03 10:22:20 -05:00
Bernát Gábor
fdd0a22c03 Move to maintained mirror of prettier (#13592)
https://github.com/pre-commit/mirrors-prettier has been archived and is
no longer maintained.

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
2024-10-03 15:35:27 +01:00
cake-monotone
3728d5b3a2 [pyupgrade] Fix UP043 to apply to collections.abc.Generator and collections.abc.AsyncGenerator (#13611)
## Summary

fix #13602 

Currently, `UP043` only applies to typing.Generator, but it should also
support collections.abc.Generator.

This update ensures `UP043` correctly handles both
`collections.abc.Generator` and `collections.abc.AsyncGenerator`

### UP043
> `UP043`
> Python 3.13 introduced the ability for type parameters to specify
default values. As such, the default type arguments for some types in
the standard library (e.g., Generator, AsyncGenerator) are now optional.
> Omitting type parameters that match the default values can make the
code more concise and easier to read.

```py
Generator[int, None, None] -> Generator[int]
```
2024-10-03 13:06:15 +01:00
Dhruv Manilawala
7e3894f5b3 Avoid short circuiting B017 for multiple context managers (#13609)
## Summary

fixes: #13603
2024-10-03 15:35:05 +05:30
Charlie Marsh
c3b40da0d2 Use backticks for code in red-knot messages (#13599)
## Summary

...and remove periods from messages that don't span more than a single
sentence.

This is more consistent with how we present user-facing messages in uv
(which has a defined style guide).
2024-10-02 03:14:28 +00:00
Charlie Marsh
ef45185dbc Allow users to provide custom diagnostic messages when unwrapping calls (#13597)
## Summary

You can now call `return_ty_result` to operate on a `Result` directly
thereby using your own diagnostics, as in:

```rust
return dunder_getitem_method
    .call(self.db, &[slice_ty])
    .return_ty_result(self.db, value.as_ref().into(), self)
    .unwrap_or_else(|err| {
        self.add_diagnostic(
            (&**value).into(),
            "call-non-callable",
            format_args!(
                "Method `__getitem__` is not callable on object of type '{}'.",
                value_ty.display(self.db),
            ),
        );
        err.return_ty()
    });
```
2024-10-01 21:22:13 +00:00
Charlie Marsh
961fc98344 Use __class_getitem__ for more specific non-subscript errors (#13596) 2024-10-01 18:16:00 +00:00
Charlie Marsh
0a6dc8e1b8 Support __getitem__ type inference for subscripts (#13579)
## Summary

Follow-up to https://github.com/astral-sh/ruff/pull/13562, to add
support for "arbitrary" subscript operations.
2024-10-01 18:04:16 +00:00
Charlie Marsh
8d54996ffb Avoid indirection in class.__call__ lookup (#13595) 2024-10-01 18:01:36 +00:00
Alex Waygood
73e884b232 [red-knot] [minor] Improve helper methods for builtin types (#13594) 2024-10-01 18:38:33 +01:00
Charlie Marsh
edba60106b Support classes that implement __call__ (#13580)
## Summary

This looked straightforward and removes some TODOs.
2024-10-01 17:15:46 +00:00
Alex Waygood
043fba7a57 [red-knot] Fix a few details around Type::call (#13593) 2024-10-01 16:49:09 +00:00
Alex Waygood
20d997784d ruff_benchmark: open all tomllib files in the red-knot benchmark (#13589) 2024-10-01 17:47:36 +01:00
Alex Waygood
82324678cf Rename the ruff_vendored crate to red_knot_vendored (#13586) 2024-10-01 16:16:59 +01:00
Zanie Blue
cfd5d63917 Use operator specific messaging in division by zero diagnostics (#13588)
Requested at
https://github.com/astral-sh/ruff/pull/13576#discussion_r1782530971
2024-10-01 08:58:38 -05:00
Alex Waygood
2a36b47f13 [red-knot] Remove Type::RevealType (#13567) 2024-10-01 10:01:03 +00:00
Tom Gillam
6322639aca Fix tiny typo in _typos.toml (#13583) 2024-10-01 10:54:00 +01:00
github-actions[bot]
360af1bc32 Sync vendored typeshed stubs (#13578)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-10-01 08:05:19 +01:00
Zanie Blue
3af3f74c66 Update dedent_to to support blocks that are composed of comments (#13572)
While looking into https://github.com/astral-sh/ruff/issues/13545 I
noticed that we return `None` here if you pass a block of comments. This
is annoying because it causes `adjust_indentation` to fall back to
LibCST which panics when it cannot find a statement.
2024-10-01 04:38:03 +00:00
Zanie Blue
45f01e7872 Add diagnostic for integer division by zero (#13576)
Adds a diagnostic for division by the integer zero in `//`, `/`, and
`%`.

Doesn't handle `<int> / 0.0` because we don't track the values of float
literals.
2024-09-30 22:38:52 +00:00
Simon
6cdf996af6 [red-knot] feat: introduce a new [Type::Todo] variant (#13548)
This variant shows inference that is not yet implemented..

## Summary

PR #13500 reopened the idea of adding a new type variant to keep track
of not-implemented features in Red Knot.

It was based off of #12986 with a more generic approach of keeping track
of different kind of unknowns. Discussion in #13500 agreed that keeping
track of different `Unknown` is complicated for now, and this feature is
better achieved through a new variant of `Type`.

### Requirements

Requirements for this implementation can be summed up with some extracts
of comment from @carljm on the previous PR

> So at the moment we are leaning towards simplifying this PR to just
use a new top-level variant, which behaves like Any and Unknown but
represents inference that is not yet implemented in red-knot.

> I think the general rule should be that Todo should propagate only
when the presence of the input Todo caused the output to be unknown.
>
> To take a specific example, the inferred result of addition must be
Unknown if either operand is Unknown. That is, Unknown + X will always
be Unknown regardless of what X is. (Same for X + Unknown.) In this
case, I believe that Unknown + Todo (or Todo + Unknown) should result in
Unknown, not result in Todo. If we fix the upstream source of the Todo,
the result would still be Unknown, so it's not useful to propagate the
Todo in this case: it wrongly suggests that the output is unknown
because of a todo item.

## Test Plan

This PR does not introduce new tests, but it did required to edit some
tests with the display of `[Type::Todo]` (currently `@Todo`), which
suggests that those test are placeholders requirements for features we
don't support yet.
2024-09-30 14:28:06 -07:00
Zanie Blue
9d8a4c0057 Improve display of assert_public_ty assertion failures (#13577)
While working on https://github.com/astral-sh/ruff/pull/13576 I noticed
that it was really hard to tell which assertion failed in some of these
test cases. This could be expanded to elsewhere, but I've heard this
test suite format won't be around for long?
2024-09-30 16:12:26 -05:00
Charlie Marsh
c9c748a79e Add some basic subscript type inference (#13562)
## Summary

Just for tuples and strings -- the easiest cases. I think most of the
rest require generic support?
2024-09-30 16:50:46 -04:00
Zanie Blue
32c746bd82 Fix inference when integers are divided (#13575)
Fixes the `Operator::Div` case and adds `Operator::FloorDiv` support

Closes https://github.com/astral-sh/ruff/issues/13570
2024-09-30 15:50:37 -05:00
Zanie Blue
e76f77d711 Use uv in contribution document (#13540) 2024-09-30 14:42:59 -05:00
Charlie Marsh
d86b73eb3d Add unary inference for integer and boolean literals (#13559)
## Summary

Just trying to familiarize myself with the general patterns, testing,
etc.

Part of https://github.com/astral-sh/ruff/issues/12701.
2024-09-30 16:29:06 +00:00
Alex Waygood
5f4b282327 [red-knot] Allow calling bool() with no arguments (#13568) 2024-09-30 13:18:01 +00:00
aditya pillai
d9267132d6 Fix leftover references to red_knot_python_semantic/vendor/ (#13561)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-09-30 11:32:02 +00:00
renovate[bot]
5118166d21 Update NPM Development dependencies (#13560) 2024-09-29 22:09:47 -04:00
renovate[bot]
6fb1d6037a Update pre-commit dependencies (#13558) 2024-09-29 21:50:50 -04:00
renovate[bot]
9237813e0c Update Rust crate tempfile to v3.13.0 (#13557) 2024-09-29 21:50:46 -04:00
renovate[bot]
3bebde3ccc Update Rust crate regex to v1.11.0 (#13556) 2024-09-29 21:50:40 -04:00
renovate[bot]
6c5cbad533 Update dependency ruff to v0.6.8 (#13555) 2024-09-29 21:50:28 -04:00
renovate[bot]
7a2f8d4463 Update dependency react-resizable-panels to v2.1.4 (#13554) 2024-09-29 21:50:22 -04:00
renovate[bot]
ad87ea948d Update Rust crate syn to v2.0.79 (#13553) 2024-09-29 21:50:16 -04:00
renovate[bot]
acfc34d615 Update Rust crate libc to v0.2.159 (#13552) 2024-09-29 21:50:10 -04:00
Charlie Marsh
668730cc28 Link to astral-sh/ruff-action (#13551) 2024-09-29 23:49:24 +00:00
Edouard Choinière
bee498d635 [flake8-use-pathlib] Fix typo in link to Path.stat (PTH116) (#13546)
## Summary

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

There was a typo in the links of the docs of PTH116, where Path.stat
used to link to Path.group.
Another rule, PTH202, does it correctly: 

ec72e675d9/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs (L33)

This PR only fixes a one word typo.

## Test Plan

<!-- How was it tested? -->
I did not test that the doc generation framework picked up these
changes, I assume it will do it successfully.
2024-09-28 12:01:41 -04:00
TomerBin
ec72e675d9 Red Knot - Infer the return value of bool() (#13538)
## Summary
Following #13449, this PR adds custom handling for the bool constructor,
so when the input type has statically known truthiness value, it will be
used as the return value of the bool function.
For example, in the following snippet x will now be resolved to
`Literal[True]` instead of `bool`.
```python
x = bool(1)
```

## Test Plan
Some cargo tests were added.
2024-09-27 12:11:55 -07:00
Simon
1639488082 [red-knot] support fstring expressions (#13511)
<!--
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

Implement inference for `f-string`, contributes to #12701.

### First Implementation

When looking at the way `mypy` handles things, I noticed the following:
- No variables (e.g. `f"hello"`) ⇒ `LiteralString`
- Any variable (e.g. `f"number {1}"`) ⇒ `str`

My first commit (1ba5d0f13fdf70ed8b2b1a41433b32fc9085add2) implements
exactly this logic, except that we deal with string literals just like
`infer_string_literal_expression` (if below `MAX_STRING_LITERAL_SIZE`,
show `Literal["exact string"]`)

### Second Implementation

My second commit (90326ce9af5549af7b4efae89cd074ddf68ada14) pushes
things a bit further to handle cases where the expression within the
`f-string` are all literal values (string representation known at static
time).

Here's an example of when this could happen in code:
```python
BASE_URL = "https://httpbin.org"
VERSION = "v1"
endpoint = f"{BASE_URL}/{VERSION}/post"  # Literal["https://httpbin.org/v1/post"]
```
As this can be sightly more costly (additional allocations), I don't
know if we want this feature.

## Test Plan

- Added a test `fstring_expression` covering all cases I can think of

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-09-27 10:29:21 -07:00
Micha Reiser
f3e464ea4c refactor: Simplify quote selection logic (#13536) 2024-09-27 14:40:28 +02:00
Micha Reiser
253f5f269a refactor: Rename FormatStringContinuation to FormatImplicitConcatenatedString (#13531) 2024-09-27 08:24:50 +00:00
Micha Reiser
c046101b79 Fix codeblock dynamic line length calculation for indented examples (#13523) 2024-09-27 09:09:07 +02:00
Zanie Blue
7706f561a9 Do not offer an invalid fix for PLR1716 when the comparisons contain parenthesis (#13527)
Related to https://github.com/astral-sh/ruff/issues/13524

Doesn't offer a valid fix, opting to instead just not offer a fix at
all. If someone points me to a good way to handle parenthesis here I'm
down to try to fix the fix separately, but it looks quite hard.
2024-09-26 19:01:06 +00:00
Henry Jiang
f5e3662446 Remove jemalloc crate when building on AIX (#13529)
## Summary
Building ruff on AIX breaks on `tiki-jemalloc-sys` due to OS header
incompatibility

## Test Plan
`cargo test`

Co-authored-by: Henry Jiang <henry.jiang1@ibm.com>
2024-09-26 13:20:54 -04:00
Junzhuo ZHOU
a354d9ead6 Expose internal types as public access (#13509) 2024-09-26 17:34:30 +02:00
Zanie Blue
58a8e9c511 Fix handling of slices in tuples for FURB118, e.g., x[:, 1] (#13518)
There was already handling for the singleton `x[:]` case but not the
tuple case.

Closes https://github.com/astral-sh/ruff/issues/13508
2024-09-26 14:20:03 +00:00
ukyen
e83388dcea Don't raise D208 when last line is non-empty (#13372)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-26 14:53:21 +02:00
Micha Reiser
ae39ce56c0 Bump version to 0.6.8 (#13522) 2024-09-26 14:09:03 +02:00
Micha Reiser
ff2d214e11 Don't skip over imports and other nodes containing nested statements in import collector (#13521) 2024-09-26 11:57:05 +00:00
Micha Reiser
9442cd8fae Parenthesize match..case if guards (#13513) 2024-09-26 06:44:33 +00:00
Micha Reiser
8012707348 Align formatting of patterns in match-cases with expression formatting in clause headers (#13510) 2024-09-26 08:35:22 +02:00
Charlie Marsh
d7ffe46054 Disable the typeset plugin (#13517)
## Summary

There seems to be a bad interaction between enabling anchorlinks and the
`typeset` plugin. I think the former is more important than the
latter... so disabling the latter for now.

## Test Plan

Before:

![Screenshot 2024-09-25 at 7 53
21 PM](https://github.com/user-attachments/assets/bf7c70bb-19ab-4ece-9709-4c297f8ba67b)

After:

![Screenshot 2024-09-25 at 7 53
12 PM](https://github.com/user-attachments/assets/e767a575-1664-4288-aecb-82e8b1b1a7bd)
2024-09-25 23:58:35 +00:00
haarisr
7c83af419c red-knot: Implement the not operator for all Type variants (#13432)
Signed-off-by: haaris <haarisrahman@gmail.com>
Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-09-25 13:44:19 -07:00
Zanie Blue
bbb044ebda Detect tuples bound to variadic positional arguments i.e. *args (#13512)
In https://github.com/astral-sh/ruff/pull/13503, we added supported for
detecting variadic keyword arguments as dictionaries, here we use the
same strategy for detecting variadic positional arguments as tuples.
2024-09-25 10:03:25 -05:00
Zanie Blue
481065238b Avoid UP028 false negatives with non-reference shadowed bindings of loop variables (#13504)
Closes https://github.com/astral-sh/ruff/issues/13266

Avoids false negatives for shadowed bindings that aren't actually
references to the loop variable. There are some shadowed bindings we
need to support still, e.g., `del` requires the loop variable to exist.
2024-09-25 10:03:09 -05:00
Zanie Blue
11f06e0d55 Detect SIM910 when using variadic keyword arguments, i.e., **kwargs (#13503)
Closes https://github.com/astral-sh/ruff/issues/13493
2024-09-25 10:02:59 -05:00
Dylan
f27a8b8c7a [internal] ComparableExpr (f)strings and bytes made invariant under concatenation (#13301) 2024-09-25 16:58:57 +02:00
Vince van Noort
ca0ae0a484 [pylint] Implement boolean-chained-comparison (R1716) (#13435)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-25 09:14:12 +00:00
TomerBin
be1d5e3368 [red-knot] Add Type::bool and boolean expression inference (#13449) 2024-09-25 00:02:26 +00:00
Simon Brugman
03503f7f56 C401 message missing closing parenthesis (#13498) 2024-09-24 14:55:32 +02:00
Charlie Marsh
ff4b6d11fa Detect basic wildcard imports in ruff analyze graph (#13486)
## Summary

I guess we can just ignore the `*` entirely for now? This will add the
`__init__.py` for anything that's importing a package.
2024-09-23 18:09:00 -04:00
Charlie Marsh
96e7f3f96f Exit gracefully on broken pipe errors (#13485)
## Summary

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

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

## Test Plan

```
❯ cargo run analyze graph ../django | head -n 10
   Compiling ruff v0.6.7 (/Users/crmarsh/workspace/ruff/crates/ruff)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/ruff analyze graph ../django`
warning: `ruff analyze graph` is experimental and may change without warning
{
  "/Users/crmarsh/workspace/django/django/__init__.py": [
    "/Users/crmarsh/workspace/django/django/apps/__init__.py",
    "/Users/crmarsh/workspace/django/django/conf/__init__.py",
    "/Users/crmarsh/workspace/django/django/urls/__init__.py",
    "/Users/crmarsh/workspace/django/django/utils/log.py",
    "/Users/crmarsh/workspace/django/django/utils/version.py"
  ],
  "/Users/crmarsh/workspace/django/django/__main__.py": [
    "/Users/crmarsh/workspace/django/django/core/management/__init__.py"
```
2024-09-23 13:48:43 +00:00
Charlie Marsh
90dc7438ee Avoid panic when analyze graph hits broken pipe (#13484)
## Summary

I think we should also make the change that @BurntSushi recommended in
the linked issue, but this gets rid of the panic.

See: https://github.com/astral-sh/ruff/issues/13483

See: https://github.com/astral-sh/ruff/issues/13442

## Test Plan

```
warning: `ruff analyze graph` is experimental and may change without warning
{
  "/Users/crmarsh/workspace/django/django/__init__.py": [
    "/Users/crmarsh/workspace/django/django/apps/__init__.py",
    "/Users/crmarsh/workspace/django/django/conf/__init__.py",
    "/Users/crmarsh/workspace/django/django/urls/__init__.py",
    "/Users/crmarsh/workspace/django/django/utils/log.py",
    "/Users/crmarsh/workspace/django/django/utils/version.py"
  ],
  "/Users/crmarsh/workspace/django/django/__main__.py": [
    "/Users/crmarsh/workspace/django/django/core/management/__init__.py"
ruff failed
  Cause: Broken pipe (os error 32)
```
2024-09-23 09:43:09 -04:00
Micha Reiser
3e99ab141c Update Salsa (#13480) 2024-09-23 14:04:04 +02:00
renovate[bot]
115745a8ac Update dependency monaco-editor to ^0.52.0 (#13475)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [monaco-editor](https://redirect.github.com/microsoft/monaco-editor) |
[`^0.51.0` ->
`^0.52.0`](https://renovatebot.com/diffs/npm/monaco-editor/0.51.0/0.52.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/monaco-editor/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/monaco-editor/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/monaco-editor/0.51.0/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/monaco-editor/0.51.0/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>microsoft/monaco-editor (monaco-editor)</summary>

###
[`v0.52.0`](https://redirect.github.com/microsoft/monaco-editor/blob/HEAD/CHANGELOG.md#0520)

[Compare
Source](https://redirect.github.com/microsoft/monaco-editor/compare/v0.51.0...v0.52.0)

-   Comment added inside of `IModelContentChangedEvent`

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC44MC4wIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:55:12 +02:00
renovate[bot]
8bb59d7216 Update Rust crate unicode_names2 to v1.3.0 (#13474)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [unicode_names2](https://redirect.github.com/progval/unicode_names2) |
workspace.dependencies | minor | `1.2.2` -> `1.3.0` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiIzOC44MC4wIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:54:44 +02:00
renovate[bot]
47aac060de Update Rust crate insta to v1.40.0 (#13472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:40:02 +02:00
Steve C
7c55330534 Fix formatting for analyze direction values (#13476) 2024-09-23 09:18:28 +02:00
renovate[bot]
047d77c60b Update pre-commit dependencies (#13467) 2024-09-22 22:54:34 -04:00
renovate[bot]
18fddd458a Update dependency eslint to v8.57.1 (#13465) 2024-09-22 22:54:14 -04:00
Charlie Marsh
db76000521 Use anchorlinks rather than permalinks (#13471)
## Summary

See: https://github.com/astral-sh/uv/pull/7626
2024-09-23 02:44:45 +00:00
renovate[bot]
a2ed1e1cd1 Update Rust crate thiserror to v1.0.64 (#13462) 2024-09-22 22:32:45 -04:00
renovate[bot]
7457679582 Update Rust crate dashmap to v6.1.0 (#13470) 2024-09-22 22:32:26 -04:00
renovate[bot]
1d352872ba Update Rust crate codspeed-criterion-compat to v2.7.2 (#13469) 2024-09-22 22:32:20 -04:00
renovate[bot]
c8b905bc96 Update NPM Development dependencies (#13468) 2024-09-22 22:32:11 -04:00
renovate[bot]
5b593d0397 Update dependency ruff to v0.6.7 (#13466) 2024-09-22 22:32:02 -04:00
renovate[bot]
c5c5acda23 Update Rust crate unicode-normalization to v0.1.24 (#13464) 2024-09-22 22:31:53 -04:00
renovate[bot]
26747aae75 Update Rust crate unicode-ident to v1.0.13 (#13463) 2024-09-22 22:31:47 -04:00
renovate[bot]
85b825a2a1 Update Rust crate syn to v2.0.77 (#13461) 2024-09-22 22:31:40 -04:00
renovate[bot]
9e764ef6d0 Update Rust crate serde_json to v1.0.128 (#13460) 2024-09-23 02:03:47 +00:00
renovate[bot]
0e325a53ef Update Rust crate serde to v1.0.210 (#13459) 2024-09-23 02:03:15 +00:00
renovate[bot]
2a136cfb57 Update Rust crate pretty_assertions to v1.4.1 (#13458) 2024-09-23 02:02:12 +00:00
renovate[bot]
7749164d4a Update Rust crate ordermap to v0.5.3 (#13457) 2024-09-23 02:01:44 +00:00
renovate[bot]
da50e14524 Update Rust crate lsp-server to v0.7.7 (#13456) 2024-09-23 02:00:23 +00:00
renovate[bot]
1886b731a5 Update Rust crate ignore to v0.4.23 (#13455) 2024-09-22 22:00:06 -04:00
renovate[bot]
364eddc95a Update Rust crate globset to v0.4.15 (#13454) 2024-09-22 22:00:01 -04:00
renovate[bot]
48fb340e3b Update Rust crate filetime to v0.2.25 (#13453) 2024-09-22 21:59:50 -04:00
renovate[bot]
71bb4d3bdc Update Rust crate clap to v4.5.18 (#13452) 2024-09-22 21:59:44 -04:00
renovate[bot]
5c20f570d0 Update Rust crate anyhow to v1.0.89 (#13451) 2024-09-23 01:58:14 +00:00
Charlie Marsh
7441da287f Skip traversal for non-compound statements (#13441)
## Summary

None of these can contain imports.
2024-09-21 20:47:30 +00:00
Charlie Marsh
c2a5179d75 Reuse BTreeSets in module resolver (#13440)
## Summary

For dependencies, there's no reason to re-allocate here, since we know
the paths are unique.
2024-09-21 20:14:32 +00:00
Charlie Marsh
17c4690b5e Bump version to v0.6.7 (#13439) 2024-09-21 13:16:36 -04:00
Charlie Marsh
f06d44e6e5 Use forget for module resolver database (#13438)
## Summary

A tiny bit faster and the `red-knot` CLI does the same thing.
2024-09-21 17:00:02 +00:00
Micha Reiser
653c09001a Use an empty vendored file system in Ruff (#13436)
## Summary

This PR changes removes the typeshed stubs from the vendored file system
shipped with ruff
and instead ships an empty "typeshed".

Making the typeshed files optional required extracting the typshed files
into a new `ruff_vendored` crate. I do like this even if all our builds
always include typeshed because it means `red_knot_python_semantic`
contains less code that needs compiling.

This also allows us to use deflate because the compression algorithm
doesn't matter for an archive containing a single, empty file.

## Test Plan

`cargo test`

I verified with ` cargo tree -f "{p} {f}" -p <package> ` that:

* red_knot_wasm: enables `deflate` compression
* red_knot: enables `zstd` compression
* `ruff`: uses stored


I'm not quiet sure how to build the binary that maturin builds but
comparing the release artifact size with `strip = true` shows a `1.5MB`
size reduction

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-09-21 16:31:42 +00:00
Micha Reiser
8921fbb54c vendored_typeshed_versions should use db.vendored (#13434) 2024-09-21 16:35:06 +02:00
Charlie Marsh
3018303c87 Avoid parsing with Salsa (#13437)
## Summary

For reasons I haven't investigated, this speeds up the resolver about 2x
(from 6.404s to 3.612s on an extremely large codebase).

## Test Plan

\cc @BurntSushi 

```
[andrew@duff rippling]$ time ruff analyze graph --preview > /dev/null

real    3.274
user    16.039
sys     7.609
maxmem  11631 MB
faults  0
[andrew@duff rippling]$ time ruff-patch analyze graph --preview > /dev/null

real    1.841
user    14.625
sys     3.639
maxmem  7173 MB
faults  0
[andrew@duff rippling]$ time ruff-patch2 analyze graph --preview > /dev/null

real    2.087
user    15.333
sys     4.869
maxmem  8642 MB
faults  0
```

Where that's `main`, then (`ruff-patch`) using the version with no
`File`, no `SemanticModel`, then (`ruff-patch2`) using `File`.
2024-09-21 13:52:16 +00:00
haarisr
6c303b2445 red-knot: Add not unary operator for boolean literals (#13422)
## Summary

Contributes to #12701

## Test Plan

Added test for boolean literals

Signed-off-by: haaris <haarisrahman@gmail.com>
2024-09-20 15:24:38 -07:00
Charlie Marsh
7579a792c7 Add test coverage for non-Python globs (#13430) 2024-09-20 20:46:00 +00:00
Charlie Marsh
0bbc138037 Upgrade to latest cargo-dist version (#13416)
## Summary

Follows https://github.com/astral-sh/uv/pull/7092.
2024-09-20 15:59:32 -04:00
Charlie Marsh
ff11db61b4 Add Python version support to ruff analyze CLI (#13426) 2024-09-20 15:40:47 -04:00
Charlie Marsh
2823487bf8 Respect lint.exclude in ruff check --add-noqa (#13427)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13423.
2024-09-20 19:39:36 +00:00
Charlie Marsh
910fac781d Add exclude support to ruff analyze (#13425)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13424.
2024-09-20 15:34:35 -04:00
Carl Meyer
149fb2090e [red-knot] more efficient UnionBuilder::add (#13411)
Avoid quadratic time in subsumed elements when adding a super-type of
existing union elements.

Reserve space in advance when adding multiple elements (from another
union) to a union.

Make union elements a `Box<[Type]>` instead of an `FxOrderSet`; the set
doesn't buy much since the rules of union uniqueness are defined in
terms of supertype/subtype, not in terms of simple type identity.

Move sealed-boolean handling out of a separate `UnionBuilder::simplify`
method and into `UnionBuilder::add`; now that `add` is iterating
existing elements anyway, this is more efficient.

Remove `UnionType::contains`, since it's now `O(n)` and we shouldn't
really need it, generally we care about subtype/supertype, not type
identity. (Right now it's used for `Type::Unbound`, which shouldn't even
be a type.)

Add support for `is_subtype_of` for the `object` type.

Addresses comments on https://github.com/astral-sh/ruff/pull/13401
2024-09-20 10:49:45 -07:00
Carl Meyer
40c65dcfa7 [red-knot] dedicated error message for all-union-elements not callable (#13412)
This was mentioned in an earlier review, and seemed easy enough to just
do it. No need to repeat all the types twice when it gives no additional
information.
2024-09-20 08:08:43 -07:00
yahayaohinoyi
03f3a4e855 [pycodestyle] Fix: Don't autofix if the first line ends in a question mark? (D400) (#13399)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-20 11:05:26 +00:00
Micha Reiser
531ebf6dff Fix parentheses around return type annotations (#13381) 2024-09-20 09:23:53 +02:00
Rupert Tombs
7c2011599f Correct Some value is incorrect (#13418) 2024-09-20 08:25:58 +02:00
Charlie Marsh
17e90823da Some minor internal refactors for module graph (#13417) 2024-09-20 00:21:30 -04:00
Charlie Marsh
d01cbf7f8f Bump version to v0.6.6 (#13415) 2024-09-19 23:09:57 -04:00
Charlie Marsh
770b276c21 Cache glob resolutions in import graph (#13413)
## Summary

These are often repeated; caching the resolutions can have a huge
impact.
2024-09-20 02:24:06 +00:00
Charlie Marsh
4e935f7d7d Add a subcommand to generate dependency graphs (#13402)
## Summary

This PR adds an experimental Ruff subcommand to generate dependency
graphs based on module resolution.

A few highlights:

- You can generate either dependency or dependent graphs via the
`--direction` command-line argument.
- Like Pants, we also provide an option to identify imports from string
literals (`--detect-string-imports`).
- Users can also provide additional dependency data via the
`include-dependencies` key under `[tool.ruff.import-map]`. This map uses
file paths as keys, and lists of strings as values. Those strings can be
file paths or globs.

The dependency resolution uses the red-knot module resolver which is
intended to be fully spec compliant, so it's also a chance to expose the
module resolver in a real-world setting.

The CLI is, e.g., `ruff graph build ../autobot`, which will output a
JSON map from file to files it depends on for the `autobot` project.
2024-09-19 21:06:32 -04:00
Carl Meyer
260c2ecd15 [red-knot] visit with-item vars even if not a Name (#13409)
This fixes the last panic on checking pandas.

(Match statement became an `if let` because clippy decided it wanted
that once I added the additional line in the else case?)

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-19 10:37:49 -07:00
Dylan
f110d80279 [refurb] Skip slice-to-remove-prefix-or-suffix (FURB188) when nontrivial slice step is present (#13405) 2024-09-19 12:47:17 -04:00
Carl Meyer
a6d3d2fccd [red-knot] support reveal_type as pseudo-builtin (#13403)
Support using `reveal_type` without importing it, as implied by the type
spec and supported by existing type checkers.

We use `typing_extensions.reveal_type` for the implicit built-in; this
way it exists on all Python versions. (It imports from `typing` on newer
Python versions.)

Emits an "undefined name" diagnostic whenever `reveal_type` is
referenced in this way (in addition to the revealed-type diagnostic when
it is called). This follows the mypy example (with `--enable-error-code
unimported-reveal`) and I think provides a good (and easily
understandable) balance for user experience. If you are using
`reveal_type` for quick temporary debugging, the additional
undefined-name diagnostic doesn't hinder that use case. If we make the
revealed-type diagnostic a non-failing one, the undefined-name
diagnostic can still be a failing diagnostic, helping prevent
accidentally leaving it in place. For any use cases where you want to
leave it in place, you can always import it to avoid the undefined-name
diagnostic.

In the future, we can easily provide configuration options to a) turn
off builtin-reveal_type altogether, and/or b) silence the undefined-name
diagnostic when using it, if we have users on either side (loving or
hating pseudo-builtin `reveal_type`) who are dissatisfied with this
compromise.
2024-09-19 07:58:08 -07:00
Micha Reiser
afdb659111 Fix off-by one error in the LineIndex::offset calculation (#13407) 2024-09-19 11:58:45 +00:00
Simon
a8d9104fa3 Fix/#13070 defer annotations when future is active (#13395) 2024-09-19 10:13:37 +02:00
Micha Reiser
d3530ab997 Fix rendering of FURB188 docs (#13406) 2024-09-19 07:29:31 +00:00
Carl Meyer
cf1e91bb59 [red-knot] simplify subtypes from unions (#13401)
Add `Type::is_subtype_of` method, and simplify subtypes out of unions.
2024-09-18 22:06:39 -07:00
Carl Meyer
125eaafae0 [red-knot] inferred type, not Unknown, for undeclared paths (#13400)
After looking at more cases (for example, the case in the added test in
this PR), I realized that our previous rule, "if a symbol has any
declarations, use only declarations for its public type" is not
adequate. Rather than using `Unknown` as fallback if the symbol is not
declared in some paths, we need to use the inferred type as fallback in
that case.

For the paths where the symbol _was_ declared, we know that any bindings
must be assignable to the declared type in that path, so this won't
change the overall declared type in those paths. But for paths where the
symbol wasn't declared, this will give us a better type in place of
`Unknown`.
2024-09-18 21:47:49 -07:00
Carl Meyer
7aae80903c [red-knot] add support for typing_extensions.reveal_type (#13397)
Before `typing.reveal_type` existed, there was
`typing_extensions.reveal_type`. We should support both.

Also adds a test to verify that we can handle aliasing of `reveal_type`
to a different name.

Adds a bit of code to ensure that if we have a union of different
`reveal_type` functions (e.g. a union containing both
`typing_extensions.reveal_type` and `typing.reveal_type`) we still emit
the reveal-type diagnostic only once. This is probably unlikely in
practice, but it doesn't hurt to handle it smoothly. (It comes up now
because we don't support `version_info` checks yet, so
`typing_extensions.reveal_type` is actually that union.)

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-18 21:39:03 -07:00
Carl Meyer
4aca9b91ba [red-knot] consider imports to be declarations (#13398)
I noticed that this pattern sometimes occurs in typeshed:
```
if ...:
    from foo import bar
else:
    def bar(): ...
```

If we have the rule that symbols with declarations only use declarations
for the public type, then this ends up resolving as `Unknown |
Literal[bar]`, because we didn't consider the import to be a
declaration.

I think the most straightforward thing here is to also consider imports
as declarations. The same rationale applies as for function and class
definitions: if you shadow an import, you should have to explicitly
shadow with an annotation, rather than just doing it
implicitly/accidentally.

We may also ultimately need to re-evaluate the rule that public type
considers only declarations, if there are declarations.
2024-09-18 20:59:03 -07:00
Hamir Mahal
8b3da1867e refactor: remove unnecessary string hashes (#13250) 2024-09-18 19:08:59 +02:00
Carl Meyer
c173ec5bc7 [red-knot] support for typing.reveal_type (#13384)
Add support for the `typing.reveal_type` function, emitting a diagnostic
revealing the type of its single argument. This is a necessary piece for
the planned testing framework.

This puts the cart slightly in front of the horse, in that we don't yet
have proper support for validating call signatures / argument types. But
it's easy to do just enough to make `reveal_type` work.

This PR includes support for calling union types (this is necessary
because we don't yet support `sys.version_info` checks, so
`typing.reveal_type` itself is a union type), plus some nice
consolidated error messages for calls to unions where some elements are
not callable. This is mostly to demonstrate the flexibility in
diagnostics that we get from the `CallOutcome` enum.
2024-09-18 09:59:51 -07:00
Charlie Marsh
44d916fb4e Respect FastAPI aliases in route definitions (#13394)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13263
2024-09-18 12:06:49 -04:00
Micha Reiser
4eb849aed3 Update the revisions of the formatter stability check projects (#13380) 2024-09-18 08:26:40 +02:00
Micha Reiser
6ac61d7b89 Fix placement of inline parameter comments (#13379) 2024-09-18 08:26:06 +02:00
renovate[bot]
c7b2e336f0 Update dependency vite to v5.4.6 (#13385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 08:25:49 +02:00
Charlie Marsh
70748950ae Respect word boundaries when detecting function signature in docs (#13388)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13242.
2024-09-18 00:01:38 -04:00
Carl Meyer
dcfebaa4a8 [red-knot] use declared types in inference/checking (#13335)
Use declared types in inference and checking. This means several things:

* Imports prefer declarations over inference, when declarations are
available.
* When we encounter a binding, we check that the bound value's inferred
type is assignable to the live declarations of the bound symbol, if any.
* When we encounter a declaration, we check that the declared type is
assignable from the inferred type of the symbol from previous bindings,
if any.
* When we encounter a binding+declaration, we check that the inferred
type of the bound value is assignable to the declared type.
2024-09-17 08:11:06 -07:00
Micha Reiser
d86e5ad031 Update Black tests (#13375) 2024-09-17 11:16:50 +02:00
Simon Brugman
bb12fe9d0c DOCS: navigate back to rule overview linter (#13368) 2024-09-16 16:21:26 +00:00
Micha Reiser
3b57faf19b Fix build of ruff_benchmark on NixOS (#13366) 2024-09-16 09:41:46 +02:00
renovate[bot]
c9f7c3d652 Update dependency react-resizable-panels to v2.1.3 (#13360)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 07:38:21 +00:00
Micha Reiser
489dbbaadc Add diagnostics panel and navigation features to playground (#13357) 2024-09-16 07:34:46 +00:00
renovate[bot]
47e9ea2d5d Update pre-commit hook astral-sh/ruff-pre-commit to v0.6.5 (#13362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 09:34:14 +02:00
renovate[bot]
7919a7122a Update NPM Development dependencies (#13363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 09:32:56 +02:00
renovate[bot]
a70d693b1c Update dependency ruff to v0.6.5 (#13361)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 09:32:06 +02:00
github-actions[bot]
1365b0806d Sync vendored typeshed stubs (#13355)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-09-14 20:40:42 -04:00
Alex Waygood
f4de49ab37 [red-knot] Clarify how scopes are pushed and popped for comprehensions and generator expressions (#13353) 2024-09-14 13:31:17 -04:00
François-Michel L'Heureux
8b49845537 Fix documentation for editor vim plugin ALE (#13348)
The documented configuration did not work. On failure, ALE suggest to
run `ALEFixSuggest`, into with it documents the working configuration
key

'ruff_format' - Fix python files with the ruff formatter.

Fix an inaccuracy in the documentation, regarding the ALE plugin for the
Vim text editor.
2024-09-13 23:27:17 +05:30
Carl Meyer
d988204b1b [red-knot] add Declarations support to semantic indexing (#13334)
Add support for declared types to the semantic index. This involves a
lot of renaming to clarify the distinction between bindings and
declarations. The Definition (or more specifically, the DefinitionKind)
becomes responsible for determining which definitions are bindings,
which are declarations, and which are both, and the symbol table
building is refactored a bit so that the `IS_BOUND` (renamed from
`IS_DEFINED` for consistent terminology) flag is always set when a
binding is added, rather than being set separately (and requiring us to
ensure it is set properly).

The `SymbolState` is split into two parts, `SymbolBindings` and
`SymbolDeclarations`, because we need to store live bindings for every
declaration and live declarations for every binding; the split lets us
do this without storing more than we need.

The massive doc comment in `use_def.rs` is updated to reflect bindings
vs declarations.

The `UseDefMap` gains some new APIs which are allow-unused for now,
since this PR doesn't yet update type inference to take declarations
into account.
2024-09-13 13:55:22 -04:00
Dhruv Manilawala
8558126df1 Bump version to 0.6.5 (#13346) 2024-09-13 20:12:26 +05:30
Dhruv Manilawala
9bd9981e70 Create insta snapshot for SARIF output (#13345)
## Summary

Follow-up from #13268, this PR updates the test case to use
`assert_snapshot` now that the output is limited to only include the
rules with diagnostics.

## Test Plan

`cargo insta test`
2024-09-13 14:35:45 +00:00
Micha Reiser
21bfab9b69 Playground: Add Copy as pyproject.toml/ruff.toml and paste from TOML (#13328) 2024-09-13 13:44:24 +01:00
Carl Meyer
43a5922f6f [red-knot] add BitSet::is_empty and BitSet::union (#13333)
Add `::is_empty` and `::union` methods to the `BitSet` implementation.

Allowing unused for now, until these methods become used later with the
declared-types implementation.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-12 14:25:45 -04:00
Carl Meyer
175d067250 [red-knot] add initial Type::is_equivalent_to and Type::is_assignable_to (#13332)
These are quite incomplete, but I needed to start stubbing them out in
order to build and test declared-types.

Allowing unused for now, until they are used later in the declared-types
PR.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-12 14:15:25 -04:00
Alex Waygood
4dc2c257ef [red-knot] Fix type inference for except* definitions (#13320) 2024-09-11 15:05:40 -04:00
Dhruv Manilawala
b72d49be16 Add support for extensionless Python files for server (#13326)
## Summary

Closes: #12539 

## Test Plan

https://github.com/user-attachments/assets/e49b2669-6f12-4684-9e45-a3321b19b659
2024-09-12 00:35:26 +05:30
Alexey Preobrazhenskiy
eded78a39b [pyupgrade] Fix broken doc link and clarify that deprecated aliases were removed in Python 3.12 (UP005) (#13327) 2024-09-11 14:27:08 -04:00
Alex Waygood
a7b8cc08f0 [red-knot] Fix .to_instance() for union types (#13319) 2024-09-10 22:41:45 +00:00
Alex Waygood
b93d0ab57c [red-knot] Add control flow for for loops (#13318) 2024-09-10 22:04:35 +00:00
Alex Waygood
e6b927a583 [red-knot] Add a convenience method for constructing a union from a list of elements (#13315) 2024-09-10 17:38:56 -04:00
Alex Waygood
acab1f4fd8 Remove allocation from ruff_python_stdlib::builtins::python_builtins (#13317) 2024-09-10 16:34:24 -04:00
Alex Waygood
2ca78721e6 [red-knot] Improve type inference for iteration over heterogenous tuples (#13314)
Followup to #13295
2024-09-10 15:13:50 -04:00
Micha Reiser
a528edad35 Disable jemalloc decay in benchmarks (#13299) 2024-09-10 19:32:43 +01:00
Alex Waygood
1d5bd89987 [pyflakes] Improve error message for UndefinedName when a builtin was added in a newer version than specified in Ruff config (F821) (#13293) 2024-09-10 18:03:52 +00:00
Dhruv Manilawala
b7cef6c999 [red-knot] Add heterogeneous tuple type variant (#13295)
## Summary

This PR adds a new `Type` variant called `TupleType` which is used for
heterogeneous elements.

### Display notes

* For an empty tuple, I'm using `tuple[()]` as described in the docs:
https://docs.python.org/3/library/typing.html#annotating-tuples
* For nested elements, it'll use the literal type instead of builtin
type unlike Pyright which does `tuple[Literal[1], tuple[int, int]]`
instead of `tuple[Literal[1], tuple[Literal[2], Literal[3]]]`. Also,
mypy would give `tuple[builtins.int, builtins.int]` instead of
`tuple[Literal[1], Literal[2]]`

## Test Plan

Update test case to account for the display change and add cases for
multiple elements and nested tuple elements.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-09-10 17:54:19 +00:00
Micha Reiser
110193af57 Fix tuple expansion example in formatter compatibility document (#13313) 2024-09-10 17:47:12 +00:00
Auguste Lalande
d6bd841512 [pydoclint] Ignore DOC201 when function name is "__new__" (#13300) 2024-09-10 13:25:38 -04:00
Alexey Preobrazhenskiy
210a9e6068 [isort] Improve rule documentation with a link to the option (I002) (#13308) 2024-09-10 09:36:21 -04:00
Micha Reiser
7c872e639b Only run executable rules when they are enabled (#13298) 2024-09-10 01:46:55 +01:00
Luo Peng
5ef6979d9a Only include rules with diagnostics in SARIF metadata (#13268) 2024-09-09 22:23:53 +01:00
Dhruv Manilawala
62c7d8f6ba [red-knot] Add control flow support for match statement (#13241)
## Summary

This PR adds support for control flow for match statement.

It also adds the necessary infrastructure required for narrowing
constraints in case blocks and implements the logic for
`PatternMatchSingleton` which is either `None` / `True` / `False`. Even
after this the inferred type doesn't get simplified completely, there's
a TODO for that in the test code.

## Test Plan

Add test cases for control flow for (a) when there's a wildcard pattern
and (b) when there isn't. There's also a test case to verify the
narrowing logic.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-09-10 02:14:19 +05:30
Alex Waygood
6f53aaf931 [red-knot] Add type inference for loop variables inside comprehension scopes (#13251) 2024-09-09 20:22:01 +00:00
Micha Reiser
ac720cd705 ERA001: Ignore script-comments with multiple end-tags (#13283) 2024-09-09 19:47:39 +01:00
Micha Reiser
312bd86e48 Fix configuration inheritance for configurations specified in the LSP settings (#13285) 2024-09-09 19:46:39 +01:00
Dylan
b04948fb72 [refurb] Implement slice-to-remove-prefix-or-suffix (FURB188) (#13256) 2024-09-09 15:08:44 +00:00
Calum Young
a98dbcee78 Add meta descriptions to rule pages (#13234)
## Summary

This PR updates the `scripts/generate_mkdocs.py` to add meta
descriptions to each rule as well as a fallback `site_description`.

I was initially planning to add this to `generate_docs.rs`; however
running `mdformat` on the rules caused the format of the additional
description to change into a state that mkdocs could not handle.

Fixes #13197 

## Test Plan

- Run  `python scripts/generate_mkdocs.py` to build the documentation
- Run `mkdocs serve -f mkdocs.public.yml` to serve the docs site locally
- Navigate to a rule on both the local site and the current production
site and note the addition of the description head tag. For example:
  - http://127.0.0.1:8000/ruff/rules/unused-import/

![image](https://github.com/user-attachments/assets/f47ae4fa-fe5b-42e1-8874-cb36a2ef2c9b)
  - https://docs.astral.sh/ruff/rules/unused-import/

![image](https://github.com/user-attachments/assets/6a650bff-2fcb-4df2-9cb6-40f66a2a5b8a)
2024-09-09 10:01:59 -04:00
Alex Waygood
1eb3e4057f [red-knot] Add definitions and limited type inference for exception handlers (#13267) 2024-09-09 07:35:15 -04:00
renovate[bot]
346dbf45b5 Update pre-commit dependencies (#13289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-09-09 11:11:01 +00:00
renovate[bot]
f427a7a5a3 Update NPM Development dependencies (#13290)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-09 11:07:16 +00:00
Micha Reiser
955dc8804a Playground: Fix errors not shown on page load (#13262) 2024-09-09 11:47:39 +01:00
renovate[bot]
e1603e3dca Update dependency ruff to v0.6.4 (#13288) 2024-09-08 22:00:43 -04:00
Micha Reiser
35d45c1e4b refactor: Return copied TextRange in CommentRanges iterator (#13281) 2024-09-08 13:17:37 +02:00
Dylan
e4aa479515 [red-knot] Handle StringLiteral truncation (#13276)
When a type of the form `Literal["..."]` would be constructed with too
large of a string, this PR converts it to `LiteralString` instead.

We also extend inference for binary operations to include the case where
one of the operands is `LiteralString`.

Closes #13224
2024-09-07 20:25:09 -07:00
Dylan
a7c936878d [ruff] Handle unary operators in decimal-from-float-literal (RUF032) (#13275)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-09-07 13:25:49 +00:00
Micha Reiser
c3bcd5c842 Upgrade to Rust 1.81 (#13265) 2024-09-06 15:09:09 +02:00
Simon
594dee1b0b [red-knot] resolve source/stubs over namespace packages (#13254) 2024-09-06 12:14:26 +01:00
Carl Meyer
a4ebe7d344 [red-knot] consolidate diagnostic and inference tests (#13248)
Pull the tests from `types.rs` into `infer.rs`.

All of these are integration tests with the same basic form: create a
code sample, run type inference or check on it, and make some assertions
about types and/or diagnostics. These are the sort of tests we will want
to move into a test framework with a low-boilerplate custom textual
format. In the meantime, having them together (and more importantly,
their helper utilities together) means that it's easy to keep tests for
related language features together (iterable tests with other iterable
tests, callable tests with other callable tests), without an artificial
split based on tests which test diagnostics vs tests which test
inference. And it allows a single test to more easily test both
diagnostics and inference. (Ultimately in the test framework, they will
likely all test diagnostics, just in some cases the diagnostics will
come from `reveal_type()`.)
2024-09-05 09:15:22 -07:00
Carl Meyer
2a3775e525 [red-knot] AnnAssign with no RHS is not a Definition (#13247)
My plan for handling declared types is to introduce a `Declaration` in
addition to `Definition`. A `Declaration` is an annotation of a name
with a type; a `Definition` is an actual runtime assignment of a value
to a name. A few things (an annotated function parameter, an
annotated-assignment with an RHS) are both a `Definition` and a
`Declaration`.

This more cleanly separates type inference (only cares about
`Definition`) from declared types (only impacted by a `Declaration`),
and I think it will work out better than trying to squeeze everything
into `Definition`. One of the tests in this PR
(`annotation_only_assignment_transparent_to_local_inference`)
demonstrates one reason why. The statement `x: int` should have no
effect on local inference of the type of `x`; whatever the locally
inferred type of `x` was before `x: int` should still be the inferred
type after `x: int`. This is actually quite hard to do if `x: int` is
considered a `Definition`, because a core assumption of the use-def map
is that a `Definition` replaces the previous value. To achieve this
would require some hackery to effectively treat `x: int` sort of as if
it were `x: int = x`, but it's not really even equivalent to that, so
this approach gets quite ugly.

As a first step in this plan, this PR stops treating AnnAssign with no
RHS as a `Definition`, which fixes behavior in a couple added tests.

This actually makes things temporarily worse for the ellipsis-type test,
since it is defined in typeshed only using annotated assignments with no
RHS. This will be fixed properly by the upcoming addition of
declarations, which should also treat a declared type as sufficient to
import a name, at least from a stub.
2024-09-05 08:55:00 -07:00
Dhruv Manilawala
65cc6ec41d Bump version to 0.6.4 (#13253) 2024-09-05 21:05:15 +05:30
Carl Meyer
66fe226608 [red-knot] fix lookup of nonlocal names in deferred annotations (#13236)
Initially I had deferred annotation name lookups reuse the "public
symbol type", since that gives the correct "from end of scope" view of
reaching definitions that we want. But there is a key difference; public
symbol types are based only on definitions in the queried scope (or
"name in the given namespace" in runtime terms), they don't ever look up
a name in nonlocal/global/builtin scopes. Deferred annotation resolution
should do this lookup.

Add a test, and fix deferred name resolution to support
nonlocal/global/builtin names.

Fixes #13176
2024-09-04 10:10:54 -07:00
Alex Waygood
e965f9cc0e [red-knot] Infer Unknown for the loop var in async for loops (#13243) 2024-09-04 14:24:58 +00:00
Alex Waygood
0512428a6f [red-knot] Emit a diagnostic if the value of a starred expression or a yield from expression is not iterable (#13240) 2024-09-04 14:19:11 +00:00
Alex Waygood
46a457318d [red-knot] Add type inference for basic for loops (#13195) 2024-09-04 10:19:50 +00:00
Alex Waygood
57289099bb Make mypy pass on black in knot_benchmark (#13235) 2024-09-04 09:35:58 +00:00
Lucas Vieira dos Santos
9d1bd7a8a7 [pylint] removed dunder methods in Python 3 (PLW3201) (#13194)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-04 08:23:08 +02:00
Lucas Vieira dos Santos
e37bde458e [ruff] implement useless if-else (RUF034) (#13218) 2024-09-04 08:22:17 +02:00
Dhruv Manilawala
862bd0c429 [red-knot] Add debug assert to check for duplicate definitions (#13214)
## Summary

Closes: #13085

## Test Plan

`cargo insta test --workspace`
2024-09-04 05:53:32 +00:00
Dhruv Manilawala
e1e9143c47 [red-knot] Handle multiple comprehension targets (#13213)
## Summary

Part of #13085, this PR updates the comprehension definition to handle
multiple targets.

## Test Plan

Update existing semantic index test case for comprehension with multiple
targets. Running corpus tests shouldn't panic.
2024-09-04 11:18:58 +05:30
Carl Meyer
3c4ec82aee [red-knot] support non-local name lookups (#13177)
Add support for non-local name lookups.

There's one TODO around annotated assignments without a RHS; these need
a fair amount of attention, which they'll get in an upcoming PR about
declared vs inferred types.

Fixes #11663
2024-09-03 14:18:05 -07:00
Carl Meyer
29c36a56b2 [red-knot] fix scope inference with deferred types (#13204)
Test coverage for #13131 wasn't as good as I thought it was, because
although we infer a lot of types in stubs in typeshed, we don't check
typeshed, and therefore we don't do scope-level inference and pull all
types for a scope. So we didn't really have good test coverage for
scope-level inference in a stub. And because of this, I got the code for
supporting that wrong, meaning that if we did scope-level inference with
deferred types, we'd end up never populating the deferred types in the
scope's `TypeInference`, which causes panics like #13160.

Here I both add test coverage by running the corpus tests both as `.py`
and as `.pyi` (which reveals the panic), and I fix the code to support
deferred types in scope inference.

This also revealed a problem with deferred types in generic functions,
which effectively span two scopes. That problem will require a bit more
thought, and I don't want to block this PR on it, so for now I just
don't defer annotations on generic functions.

Fixes #13160.
2024-09-03 11:20:43 -07:00
Alex Waygood
dfee65882b [red-knot] Inline Type::is_literal (#13230) 2024-09-03 15:02:50 +01:00
Alex Waygood
50c8ee5175 Fix virtual environment details in knot_benchmark (#13228) 2024-09-03 14:35:45 +01:00
Micha Reiser
c2aac5f826 Enable multithreading for pyright (#13227) 2024-09-03 11:24:42 +00:00
Alex Waygood
387af831f9 Improve detection of whether a symbol refers to a builtin exception (#13215) 2024-09-03 10:33:03 +00:00
Alex Waygood
9d517061f2 [red-knot] Reduce some repetitiveness in tests (#13135) 2024-09-03 11:26:44 +01:00
Dhruv Manilawala
facf6febf0 [red-knot] Remove match pattern definition visitor (#13209)
## Summary

This PR is based on this discussion:
https://github.com/astral-sh/ruff/pull/13147#discussion_r1739408653.

**Todo**

- [x] Add documentation for `MatchPatternState`

## Test Plan

`cargo insta test` and `cargo clippy`
2024-09-03 08:53:35 +00:00
Simon
46e687e8d1 [red-knot] Condense literals display by types (#13185)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-03 07:23:28 +00:00
Micha Reiser
599103c933 Add a few missing #[return_ref] attributes (#13223) 2024-09-03 09:15:43 +02:00
Charlie Marsh
54df960a4a Use | for page separator in meta titles (#13221)
## Summary

Same as https://github.com/astral-sh/uv/pull/6953.
2024-09-03 00:46:15 +00:00
Charlie Marsh
3463683632 Update URL in structured schema (#13220) 2024-09-03 00:21:45 +00:00
Charlie Marsh
6b973b2556 Point docs to Astral favicon (#13219)
## Summary

Same as https://github.com/astral-sh/uv/pull/6951. Unfortunately we have
to use a single favicon for the docs.
2024-09-02 20:11:39 -04:00
Alex Waygood
c0e2c13d0d [flake8-pyi] Teach various rules that annotations might be stringized (#12951) 2024-09-02 13:40:06 +00:00
Ruben van Eldik
591a7a152c Handle singular case for incompatible rules warning (#13212)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-02 15:16:06 +02:00
Alex Waygood
b7c7b4b387 Add a method to Checker for cached parsing of stringified type annotations (#13158) 2024-09-02 12:44:20 +00:00
Tom Kuson
ea0246c51a [ruff] Implement post-init-default (RUF033) (#13192)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-02 13:10:55 +01:00
Ewoud Samuels
0f85769976 Fix example in PLE1520 documentation (#13210) 2024-09-02 13:19:43 +02:00
Dhruv Manilawala
47f0b45be3 Implement AstNode for Identifier (#13207)
## Summary

Follow-up to #13147, this PR implements the `AstNode` for `Identifier`.
This makes it easier to create the `NodeKey` in red knot because it uses
a generic method to construct the key from `AnyNodeRef` and is important
for definitions that are created only on identifiers instead of
`ExprName`.

## Test Plan

`cargo test` and `cargo clippy`
2024-09-02 16:27:12 +05:30
Dhruv Manilawala
f4bed22b05 Ignore invalid notebook in ecosystem checks (#13211) 2024-09-02 16:22:51 +05:30
Dhruv Manilawala
17eb65b26f Add definitions for match statement (#13147)
## Summary

This PR adds definition for match patterns.

## Test Plan

Update the existing test case for match statement symbols to verify that
the definitions are added as well.
2024-09-02 14:40:09 +05:30
Micha Reiser
9986397d56 Avoid allocating OrderedSet in UnionBuilder::simplify (#13206)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-02 09:07:53 +00:00
Alex Waygood
58c641c92f Optimize some SemanticModel methods (#13091) 2024-09-02 10:03:52 +01:00
renovate[bot]
227fa4e035 Update Rust crate quick-junit to 0.5.0 (#13203)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.5.0`](https://redirect.github.com/nextest-rs/quick-junit/blob/HEAD/CHANGELOG.md#050---2024-09-01)

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

##### Changed

- The `Output` type, which strips invalid XML characters from a string,
has been renamed to
    `XmlString`.
-   All internal storage now uses `XmlString` rather than `String`.

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC41Ni4wIiwidXBkYXRlZEluVmVyIjoiMzguNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-09-02 06:58:58 +00:00
renovate[bot]
2b21b77ee6 Update dependency mkdocs to v1.6.1 (#13199)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [mkdocs](https://redirect.github.com/mkdocs/mkdocs)
([changelog](https://www.mkdocs.org/about/release-notes/)) | `==1.6.0`
-> `==1.6.1` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mkdocs/1.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/mkdocs/1.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/mkdocs/1.6.0/1.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mkdocs/1.6.0/1.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>mkdocs/mkdocs (mkdocs)</summary>

###
[`v1.6.1`](https://redirect.github.com/mkdocs/mkdocs/releases/tag/1.6.1)

[Compare
Source](https://redirect.github.com/mkdocs/mkdocs/compare/1.6.0...1.6.1)

### Version 1.6.1 (Friday 30th August, 2024)

##### Fixed

- Fix build error when environment variable `SOURCE_DATE_EPOCH=0` is
set.
[#&#8203;3795](https://redirect.github.com/mkdocs/mkdocs/issues/3795)
- Fix build error when `mkdocs_theme.yml` config is empty.
[#&#8203;3700](https://redirect.github.com/mkdocs/mkdocs/issues/3700)
- Support `python -W` and `PYTHONWARNINGS` instead of overriding the
configuration.
[#&#8203;3809](https://redirect.github.com/mkdocs/mkdocs/issues/3809)
- Support running with Docker under strict mode, by removing `0.0.0.0`
dev server warning.
[#&#8203;3784](https://redirect.github.com/mkdocs/mkdocs/issues/3784)
- Drop unnecessary `changefreq` from `sitemap.xml`.
[#&#8203;3629](https://redirect.github.com/mkdocs/mkdocs/issues/3629)
- Fix JavaScript console error when closing menu dropdown.
[#&#8203;3774](https://redirect.github.com/mkdocs/mkdocs/issues/3774)
- Fix JavaScript console error that occur on repeated clicks.
[#&#8203;3730](https://redirect.github.com/mkdocs/mkdocs/issues/3730)
- Fix JavaScript console error that can occur on dropdown selections.
[#&#8203;3694](https://redirect.github.com/mkdocs/mkdocs/issues/3694)

##### Added

- Added translations for Dutch.
[#&#8203;3804](https://redirect.github.com/mkdocs/mkdocs/issues/3804)
- Added and updated translations for Chinese (Simplified).
[#&#8203;3684](https://redirect.github.com/mkdocs/mkdocs/issues/3684)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC41Ni4wIiwidXBkYXRlZEluVmVyIjoiMzguNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 12:14:19 +05:30
renovate[bot]
ba272b093c Update dependency ruff to v0.6.3 (#13201)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Preview features

- \[`flake8-simplify`] Extend `open-file-with-context-handler` to work
with `dbm.sqlite3` (`SIM115`)
([#&#8203;13104](https://redirect.github.com/astral-sh/ruff/pull/13104))
- \[`pycodestyle`] Disable `E741` in stub files (`.pyi`)
([#&#8203;13119](https://redirect.github.com/astral-sh/ruff/pull/13119))
- \[`pydoclint`] Avoid `DOC201` on explicit returns in functions that
only return `None`
([#&#8203;13064](https://redirect.github.com/astral-sh/ruff/pull/13064))

##### Rule changes

- \[`flake8-async`] Disable check for `asyncio` before Python 3.11
(`ASYNC109`)
([#&#8203;13023](https://redirect.github.com/astral-sh/ruff/pull/13023))

##### Bug fixes

- \[`FastAPI`] Avoid introducing invalid syntax in fix for
`fast-api-non-annotated-dependency` (`FAST002`)
([#&#8203;13133](https://redirect.github.com/astral-sh/ruff/pull/13133))
- \[`flake8-implicit-str-concat`] Normalize octals before merging
concatenated strings in `single-line-implicit-string-concatenation`
(`ISC001`)
([#&#8203;13118](https://redirect.github.com/astral-sh/ruff/pull/13118))
- \[`flake8-pytest-style`] Improve help message for
`pytest-incorrect-mark-parentheses-style` (`PT023`)
([#&#8203;13092](https://redirect.github.com/astral-sh/ruff/pull/13092))
- \[`pylint`] Avoid autofix for calls that aren't `min` or `max` as
starred expression (`PLW3301`)
([#&#8203;13089](https://redirect.github.com/astral-sh/ruff/pull/13089))
- \[`ruff`] Add `datetime.time`, `datetime.tzinfo`, and
`datetime.timezone` as immutable function calls (`RUF009`)
([#&#8203;13109](https://redirect.github.com/astral-sh/ruff/pull/13109))
- \[`ruff`] Extend comment deletion for `RUF100` to include trailing
text from `noqa` directives while preserving any following comments on
the same line, if any
([#&#8203;13105](https://redirect.github.com/astral-sh/ruff/pull/13105))
- Fix dark theme on initial page load for the Ruff playground
([#&#8203;13077](https://redirect.github.com/astral-sh/ruff/pull/13077))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC41Ni4wIiwidXBkYXRlZEluVmVyIjoiMzguNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 12:08:52 +05:30
renovate[bot]
8972e5d175 Update NPM Development dependencies (#13198)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[@types/react](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react))
| [`18.3.4` ->
`18.3.5`](https://renovatebot.com/diffs/npm/@types%2freact/18.3.4/18.3.5)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2freact/18.3.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2freact/18.3.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2freact/18.3.4/18.3.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2freact/18.3.4/18.3.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/eslint-plugin](https://typescript-eslint.io/packages/eslint-plugin)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin))
| [`8.2.0` ->
`8.3.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/8.2.0/8.3.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2feslint-plugin/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2feslint-plugin/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2feslint-plugin/8.2.0/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2feslint-plugin/8.2.0/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[@typescript-eslint/parser](https://typescript-eslint.io/packages/parser)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser))
| [`8.2.0` ->
`8.3.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/8.2.0/8.3.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2fparser/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2fparser/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2fparser/8.2.0/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2fparser/8.2.0/8.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [postcss](https://postcss.org/)
([source](https://redirect.github.com/postcss/postcss)) | [`8.4.41` ->
`8.4.43`](https://renovatebot.com/diffs/npm/postcss/8.4.41/8.4.43) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/postcss/8.4.43?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/postcss/8.4.43?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/postcss/8.4.41/8.4.43?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/postcss/8.4.41/8.4.43?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [wrangler](https://redirect.github.com/cloudflare/workers-sdk)
([source](https://redirect.github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler))
| [`3.72.2` ->
`3.73.0`](https://renovatebot.com/diffs/npm/wrangler/3.72.2/3.73.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/wrangler/3.73.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/wrangler/3.73.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/wrangler/3.72.2/3.73.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/wrangler/3.72.2/3.73.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/eslint-plugin)</summary>

###
[`v8.3.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#830-2024-08-26)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.2.0...v8.3.0)

##### 🚀 Features

-   **eslint-plugin:** \[no-deprecation] add rule

##### 🩹 Fixes

- **eslint-plugin:** \[no-unnecessary-template-expression] add missing
parentheses in autofix

- **eslint-plugin:** \[no-unnecessary-type-parameters] check mapped
alias type arguments

-   **utils:** add `TSDeclareFunction` to `functionTypeTypes`

-   **ast-spec:** use `Expression` in argument of `ThrowStatement`

##### ❤️  Thank You

-   Abraham Guo
-   Daichi Kamiyama
-   Josh Goldberg 
-   Kim Sang Du
-   Sukka
-   Vida Xie

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/parser)</summary>

###
[`v8.3.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#830-2024-08-26)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.2.0...v8.3.0)

This was a version bump only for parser to align it with other projects,
there were no code changes.

You can read about our [versioning
strategy](https://main--typescript-eslint.netlify.app/users/versioning)
and
[releases](https://main--typescript-eslint.netlify.app/users/releases)
on our website.

</details>

<details>
<summary>postcss/postcss (postcss)</summary>

###
[`v8.4.43`](https://redirect.github.com/postcss/postcss/blob/HEAD/CHANGELOG.md#8443)

[Compare
Source](https://redirect.github.com/postcss/postcss/compare/8.4.42...8.4.43)

-   Fixed `markClean is not a function` error.

###
[`v8.4.42`](https://redirect.github.com/postcss/postcss/blob/HEAD/CHANGELOG.md#8442)

[Compare
Source](https://redirect.github.com/postcss/postcss/compare/8.4.41...8.4.42)

- Fixed CSS syntax error on long minified files (by
[@&#8203;varpstar](https://redirect.github.com/varpstar)).

</details>

<details>
<summary>cloudflare/workers-sdk (wrangler)</summary>

###
[`v3.73.0`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3730)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.72.3...wrangler@3.73.0)

##### Minor Changes

-
[#&#8203;6571](https://redirect.github.com/cloudflare/workers-sdk/pull/6571)
[`a7e1bfe`](a7e1bfea3e)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - feat:
Add deployment http targets to wrangler deploy logs, and add url to
pages deploy logs

-
[#&#8203;6497](https://redirect.github.com/cloudflare/workers-sdk/pull/6497)
[`3bd833c`](3bd833cbe2)
Thanks [@&#8203;WalshyDev](https://redirect.github.com/WalshyDev)! -
chore: move `wrangler versions ...`, `wrangler deployments ...`,
`wrangler rollback` and `wrangler triggers ...` out of experimental and
open beta.
These are now available to use without the --x-versions flag, you can
continue to pass this however without issue to keep compatibility with
all the usage today.

A few of the commands had an output that wasn't guarded by
`--x-versions` those have been updated to use the newer output, we have
tried to keep compatibility where possible (for example: `wrangler
rollback` will continue to output "Worker Version ID:" so users can
continue to grab the ID).
If you wish to use the old versions of the commands you can pass the
`--no-x-versions` flag. Note, these will be removed in the future so
please work on migrating.

-
[#&#8203;6586](https://redirect.github.com/cloudflare/workers-sdk/pull/6586)
[`72ea742`](72ea74214d)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - feat:
Inject a 404 response for browser requested `favicon.ico` files when
loading the `/__scheduled` page for scheduled-only Workers

-
[#&#8203;6497](https://redirect.github.com/cloudflare/workers-sdk/pull/6497)
[`3bd833c`](3bd833cbe2)
Thanks [@&#8203;WalshyDev](https://redirect.github.com/WalshyDev)! -
feat: update `wrangler deploy` to use the new versions and deployments
API.
This should have zero user-facing impact but sets up the most used
command to deploy Workers to use the new recommended APIs and move away
from the old ones.
We will still call the old upload path where required (e.g. Durable
Object migration or Service Worker format).

##### Patch Changes

-
[#&#8203;6563](https://redirect.github.com/cloudflare/workers-sdk/pull/6563)
[`da48a70`](da48a70369)
Thanks
[@&#8203;threepointone](https://redirect.github.com/threepointone)! -
chore: remove the warning about local mode flag being removed in the
future

-
[#&#8203;6595](https://redirect.github.com/cloudflare/workers-sdk/pull/6595)
[`0a76d7e`](0a76d7e550)
Thanks [@&#8203;vicb](https://redirect.github.com/vicb)! - feat: update
unenv to the latest available version

-
[#&#8203;5738](https://redirect.github.com/cloudflare/workers-sdk/pull/5738)
[`c2460c4`](c2460c4d89)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - fix:
Prevent spaces in names when validating

-
[#&#8203;6586](https://redirect.github.com/cloudflare/workers-sdk/pull/6586)
[`72ea742`](72ea74214d)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! -
chore: Improve Miniflare CRON warning wording

-
[#&#8203;6593](https://redirect.github.com/cloudflare/workers-sdk/pull/6593)
[`f097cb7`](f097cb73be)
Thanks [@&#8203;vicb](https://redirect.github.com/vicb)! - fix: remove
`experimental:` prefix requirement for nodejs_compat_v2

    See https://jira.cfdata.org/browse/DEVDASH-218

-
[#&#8203;6572](https://redirect.github.com/cloudflare/workers-sdk/pull/6572)
[`0d83428`](0d834284d0)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - fix:
Show a clearer user error when trying to use a python worker without the
`python_workers` compatibility flag specified

-
[#&#8203;6589](https://redirect.github.com/cloudflare/workers-sdk/pull/6589)
[`f4c8cea`](f4c8cea142)
Thanks [@&#8203;vicb](https://redirect.github.com/vicb)! - feat: update
unenv to the latest available version

- Updated dependencies
\[[`45ad2e0`](45ad2e0c83)]:
-
[@&#8203;cloudflare/workers-shared](https://redirect.github.com/cloudflare/workers-shared)[@&#8203;0](https://redirect.github.com/0).4.1

###
[`v3.72.3`](https://redirect.github.com/cloudflare/workers-sdk/blob/HEAD/packages/wrangler/CHANGELOG.md#3723)

[Compare
Source](https://redirect.github.com/cloudflare/workers-sdk/compare/wrangler@3.72.2...wrangler@3.72.3)

##### Patch Changes

-
[#&#8203;6548](https://redirect.github.com/cloudflare/workers-sdk/pull/6548)
[`439e63a`](439e63a4ac)
Thanks [@&#8203;garvit-gupta](https://redirect.github.com/garvit-gupta)!
- fix: Fix Vectorize getVectors, deleteVectors payload in Wrangler
Client; VS-271

-
[#&#8203;6554](https://redirect.github.com/cloudflare/workers-sdk/pull/6554)
[`46aee5d`](46aee5d16c)
Thanks [@&#8203;andyjessop](https://redirect.github.com/andyjessop)! -
fix: nodejs_compat flags no longer error when running wrangler types
--x-include-runtime

-
[#&#8203;6548](https://redirect.github.com/cloudflare/workers-sdk/pull/6548)
[`439e63a`](439e63a4ac)
Thanks [@&#8203;garvit-gupta](https://redirect.github.com/garvit-gupta)!
- fix: Add content-type header to Vectorize POST operations;
[#&#8203;6516](https://redirect.github.com/cloudflare/workers-sdk/issues/6516)/VS-269

-
[#&#8203;6566](https://redirect.github.com/cloudflare/workers-sdk/pull/6566)
[`669ec1c`](669ec1c4d1)
Thanks [@&#8203;penalosa](https://redirect.github.com/penalosa)! - fix:
Ensure esbuild warnings are logged when running wrangler deploy

- Updated dependencies
\[[`6c057d1`](6c057d10b2)]:
-
[@&#8203;cloudflare/workers-shared](https://redirect.github.com/cloudflare/workers-shared)[@&#8203;0](https://redirect.github.com/0).4.0

</details>

---

### Configuration

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

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

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

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOC41Ni4wIiwidXBkYXRlZEluVmVyIjoiMzguNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 12:08:21 +05:30
renovate[bot]
9ac2e61bad Update dependency react-resizable-panels to v2.1.2 (#13200)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[react-resizable-panels](https://redirect.github.com/bvaughn/react-resizable-panels)
| [`2.1.1` ->
`2.1.2`](https://renovatebot.com/diffs/npm/react-resizable-panels/2.1.1/2.1.2)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/react-resizable-panels/2.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/react-resizable-panels/2.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/react-resizable-panels/2.1.1/2.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/react-resizable-panels/2.1.1/2.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>bvaughn/react-resizable-panels
(react-resizable-panels)</summary>

###
[`v2.1.2`](https://redirect.github.com/bvaughn/react-resizable-panels/compare/2.1.1...2.1.2)

[Compare
Source](https://redirect.github.com/bvaughn/react-resizable-panels/compare/2.1.1...2.1.2)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC41Ni4wIiwidXBkYXRlZEluVmVyIjoiMzguNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 12:06:11 +05:30
renovate[bot]
6deb056117 Update pre-commit dependencies (#13202)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[astral-sh/ruff-pre-commit](https://togithub.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.6.2` -> `v0.6.3` |
| [crate-ci/typos](https://togithub.com/crate-ci/typos) | repository |
patch | `v1.24.1` -> `v1.24.3` |

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://togithub.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

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

###
[`v0.6.3`](https://togithub.com/astral-sh/ruff-pre-commit/releases/tag/v0.6.3)

[Compare
Source](https://togithub.com/astral-sh/ruff-pre-commit/compare/v0.6.2...v0.6.3)

See: https://github.com/astral-sh/ruff/releases/tag/0.6.3

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.24.3`](https://togithub.com/crate-ci/typos/releases/tag/v1.24.3)

[Compare
Source](https://togithub.com/crate-ci/typos/compare/v1.24.2...v1.24.3)

#### \[1.24.3] - 2024-08-30

##### Fixes

- Updated the dictionary with the [August
2024](https://togithub.com/crate-ci/typos/issues/1069) changes

###
[`v1.24.2`](https://togithub.com/crate-ci/typos/releases/tag/v1.24.2)

[Compare
Source](https://togithub.com/crate-ci/typos/compare/v1.24.1...v1.24.2)

#### \[1.24.2] - 2024-08-30

##### Performance

- Cap unbounded parsing to avoid worst case performance (hit with test
data)

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://togithub.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiIzOC41Ni4wIiwidXBkYXRlZEluVmVyIjoiMzguNTYuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-09-02 06:33:39 +00:00
Charlie Marsh
c4aad4b161 Use dynamic builtins list based on Python version (#13172)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13037.
2024-09-01 17:03:44 +00:00
Alex Waygood
3abd5c08a5 [pylint] Recurse into subscript subexpressions when searching for list/dict lookups (PLR1733, PLR1736) (#13186)
## Summary

The `SequenceIndexVisitor` currently does not recurse into
subexpressions of subscripts when searching for subscript accesses that
would trigger this rule. That means that we don't currently detect
violations of the rule on snippets like this:

```py
data = {"a": 1, "b": 2}
column_names = ["a", "b"]
for index, column_name in enumerate(column_names):
    _ = data[column_names[index]]
```

Fixes #13183

## Test Plan

`cargo test -p ruff_linter`
2024-09-01 17:22:45 +01:00
Alex Waygood
2014cba87f [red-knot] Fix call expression inference edge case for decorated functions (#13191) 2024-09-01 16:19:40 +01:00
Alex Waygood
5661353334 Fix typo in pydoclint enum variant name (#13193) 2024-09-01 11:58:47 +00:00
Luo Peng
dd5d0d523c Enrich messages of SARIF results (#13180) 2024-09-01 12:13:22 +01:00
James Braza
1be8c2e340 Expand docs for ASYNC109 (#13146)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-09-01 10:16:57 +00:00
Dylan
52d8847b60 [red-knot] Literal[True,False] normalized to builtins.bool (#13178)
The `UnionBuilder` builds `builtins.bool` when handed `Literal[True]`
and `Literal[False]`.

Caveat: If the builtins module is unfindable somehow, the builder falls
back to the union type of these two literals.

First task from #12694

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-08-31 22:57:50 -07:00
Alex Waygood
d3b6e8f58b Remove pylint::helpers::CmpOpExt (#13189) 2024-09-01 01:55:24 +00:00
Tom Kuson
bf620dcb38 [pydoclint] Permit yielding None in DOC402 and DOC403 (#13148)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-09-01 02:03:39 +01:00
Alex Waygood
fae0573817 [red-knot] Fix async function edge case for inference of call expressions (#13187) 2024-09-01 01:58:35 +01:00
github-actions[bot]
0c23b868dc Sync vendored typeshed stubs (#13188)
Co-authored-by: typeshedbot <>
2024-09-01 01:41:27 +01:00
Dylan
3ceedf76b8 [red-knot] Infer type of class constructor call expression (#13171)
This tiny PR implements the following type inference: the type of
`Foo(...)` will be `Foo`.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-08-30 16:48:06 -07:00
Charlie Marsh
828871dc5c [pyupgrade] Detect aiofiles.open calls in UP015 (#13173)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12879.
2024-08-30 19:39:00 -04:00
Charlie Marsh
ee21fc7fd8 Mark sys.version_info[0] < 3 and similar comparisons as outdated (#13175)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12993.
2024-08-30 19:38:46 -04:00
Chris Krycho
28ab5f4065 [red-knot] implement basic call expression inference (#13164)
## Summary

Adds basic support for inferring the type resulting from a call
expression. This only works for the *result* of call expressions; it
performs no inference on parameters. It also intentionally does nothing
with class instantiation, `__call__` implementors, or lambdas.

## Test Plan

Adds a test that it infers the right thing!

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-08-30 12:51:29 -07:00
Charlie Marsh
a73bebcf15 Avoid no-self-use for attrs-style validators (#13166)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12568.
2024-08-30 12:39:05 -04:00
Charlie Marsh
34dafb67a2 Treat sep arguments with effects as unsafe removals (#13165)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13126.
2024-08-30 12:17:47 -04:00
Chris Krycho
f8656ff35e [red-knot] infer basic (name-based) annotation expressions (#13130)
## Summary

- Introduce methods for inferring annotation and type expressions.
- Correctly infer explicit return types from functions where they are
simple names that can be resolved in scope.

Contributes to #12701 by way of helping unlock call expressions (this
does not remotely finish that, as it stands, but it gets us moving that
direction).

## Test Plan

Added a test for function return types which use the name form of an
annotation expression, since this is aiming toward call expressions.
When we extend this to working for other annotation and type expression
positions, we should add explicit tests for those as well.

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-08-30 08:24:36 -07:00
Alex Waygood
34b4732c46 [flake8-pyi] Respect pep8_naming.classmethod-decorators settings when determining if a method is a classmethod in custom-type-var-return-type (PYI019) (#13162) 2024-08-30 14:24:01 +01:00
zhoufanjin
ce68f1cc1b Fix some typos in comments (#13157) 2024-08-30 10:42:23 +00:00
Alex Waygood
281e6d9791 [pydocstyle] Improve heuristics for detecting Google-style docstrings (#13142) 2024-08-29 16:33:18 +01:00
Dhruv Manilawala
ee258caed7 Bump version to 0.6.3 (#13152) 2024-08-29 20:29:33 +05:30
Aditya Pal
b4d9d26020 Update faq.md to highlight changes to src (#13145)
This attempts to close https://github.com/astral-sh/ruff/issues/13134

## Summary

Documentation change to address
https://github.com/astral-sh/ruff/issues/13134

## Test Plan

Markdown Changes were previewed
2024-08-29 11:57:53 +00:00
Steve C
a99832088a [ruff] - extend comment deletions for unused-noqa (RUF100) (#13105)
## Summary

Extends deletions for RUF100, deleting trailing text from noqa
directives, while preserving upcoming comments on the same line if any.

In cases where it deletes a comment up to another comment on the same
line, the whitespace between them is now shown to be in the autofix in
the diagnostic as well. Leading whitespace before the removed comment is
not, though.

Fixes #12251 

## Test Plan

`cargo test`
2024-08-29 10:50:16 +05:30
Carl Meyer
770ef2ab27 [red-knot] support deferred evaluation of type expressions (#13131)
Prototype deferred evaluation of type expressions by deferring
evaluation of class bases in a stub file. This allows self-referential
class definitions, as occur with the definition of `str` in typeshed
(which inherits `Sequence[str]`).

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-28 11:41:01 -07:00
Alex Waygood
c6023c03a2 [red-knot] Add docs on using RAYON_NUM_THREADS for better logging (#13140)
Followup to #13049. We check files concurrently now; to get readable
logs, you probably want to switch that off
2024-08-28 17:14:56 +01:00
Adam Kuhn
df694ca1c1 [FastAPI] Avoid introducing invalid syntax in fix for fast-api-non-annotated-dependency (FAST002) (#13133) 2024-08-28 15:29:00 +00:00
Calum Young
2e75cfbfe7 Format PYI examples in docs as .pyi-file snippets (#13116) 2024-08-28 13:20:40 +01:00
Alex Waygood
cfafaa7637 [red-knot] Remove very noisy tracing call when resolving ImportFrom statements (#13136) 2024-08-28 10:05:00 +00:00
Jonathan Plasse
3e9c7adeee Replace crates by dependi for VS Code Dev Container (#13125)
## Summary
crates is now recommending migrating to dependi
![Screenshot from 2024-08-27
19-33-39](https://github.com/user-attachments/assets/c8f6480e-a07c-41a5-8bd0-d808c5a987a0)

## Test Plan
Opening dev-container installs correctly dependi
2024-08-28 09:53:27 +05:30
Chris Krycho
81cd438d88 red-knot: infer and display ellipsis type (#13124)
## Summary

Just what it says on the tin: adds basic `EllipsisType` inference for
any time `...` appears in the AST.

## Test Plan

Test that `x = ...` produces exactly what we would expect.

---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-08-27 20:52:53 +01:00
Dylan
483748c188 [flake8-implicit-str-concat] Normalize octals before merging concatenated strings in single-line-implicit-string-concatenation (ISC001) (#13118)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-08-27 18:53:27 +01:00
Calum Young
eb3dc37faa Add note about how Ruff handles PYI files wrt target version (#13111)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-08-27 17:28:22 +00:00
Chris Krycho
aba1802828 red-knot: infer multiplication for strings and integers (#13117)
## Summary

The resulting type when multiplying a string literal by an integer
literal is one of two types:

- `StringLiteral`, in the case where it is a reasonably small resulting
string (arbitrarily bounded here to 4096 bytes, roughly a page on many
operating systems), including the fully expanded string.
- `LiteralString`, matching Pyright etc., for strings larger than that.

Additionally:

- Switch to using `Box<str>` instead of `String` for the internal value
of `StringLiteral`, saving some non-trivial byte overhead (and keeping
the total number of allocations the same).
- Be clearer and more accurate about which types we ought to defer to in
`StringLiteral` and `LiteralString` member lookup.

## Test Plan

Added a test case covering multiplication times integers: positive,
negative, zero, and in and out of bounds.

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-08-27 09:00:36 -07:00
Tom Kuson
96b42b0c8f [DOC201] Permit explicit None in functions that only return None (#13064)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-08-27 16:00:18 +00:00
Niels Wouda
e6d0c4a65d Add time, tzinfo, and timezone as immutable function calls (#13109) 2024-08-27 15:51:32 +01:00
Calum Young
4e1b289a67 Disable E741 in stub files (#13119)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-08-27 15:02:14 +01:00
Alex Waygood
a5ef124201 [red-knot] Improve the accuracy of the unresolved-import check (#13055) 2024-08-27 14:17:22 +01:00
Chris Krycho
390bb43276 red-knot: flatten match expression in infer_binary_expression (#13115)
## Summary

This fixes the outstanding TODO and make it easier to work with new
cases. (Tidy first, *then* implement, basically!)

## Test Plan

After making this change all the existing tests still pass. A classic
refactor win. 🎉
2024-08-26 12:34:07 -07:00
Chris Krycho
fe8b15291f red-knot: implement unary minus on integer literals (#13114)
# Summary

Add support for the first unary operator: negating integer literals. The
resulting type is another integer literal, with the value being the
negated value of the literal. All other types continue to return
`Type::Unknown` for the present, but this is designed to make it easy to
extend easily with other combinations of operator and operand.

Contributes to #12701.

## Test Plan

Add tests with basic negation, including of very large integers and
double negation.
2024-08-26 12:08:18 -07:00
Dhruv Manilawala
c8e01d7c53 Update dependency in insider requirements.txt (#13112) 2024-08-26 19:02:27 +00:00
Chris Krycho
c4d628cc4c red-knot: infer string literal types (#13113)
## Summary

Introduce a `StringLiteralType` with corresponding `Display` type and a
relatively basic test that the resulting representation is as expected.

Note: we currently always allocate for `StringLiteral` types. This may
end up being a perf issue later, at which point we may want to look at
other ways of representing `value` here, i.e. with some kind of smarter
string structure which can reuse types. That is most likely to show up
with e.g. concatenation.

Contributes to #12701.

## Test Plan

Added a test for individual strings with both single and double quotes
as well as concatenated strings with both forms.
2024-08-26 11:42:34 -07:00
Calum Young
ab3648c4c5 Format docs with ruff formatter (#13087)
## Summary

Now that Ruff provides a formatter, there is no need to rely on Black to
check that the docs are formatted correctly in
`check_docs_formatted.py`. This PR swaps out Black for the Ruff
formatter and updates inconsistencies between the two.

This PR will be a precursor to another PR
([branch](https://github.com/calumy/ruff/tree/format-pyi-in-docs)),
updating the `check_docs_formatted.py` script to check for pyi files,
fixing #11568.

## Test Plan

- CI to check that the docs are formatted correctly using the updated
script.
2024-08-26 21:25:10 +05:30
Teodoro Freund
a822fd6642 Fixed benchmarking section in Contributing guide (#13107)
## Summary

Noticed there was a wrong tip on the Contributing guide, `cargo
benchmark lexer` wouldn't run any benches.
Probably a missed update on #9535 

It may make sense to remove the `cargo benchmark` command from the guide
altogether, but up to the mantainers.
2024-08-26 18:49:01 +05:30
Calum Young
f8f2e2a442 Add anchor tags to README headers (#13083)
## Summary

This pull request adds anchor tags to the elements referenced in the
table of contents section of the readme used on
[PyPI](https://pypi.org/project/ruff/) as an attempt to fix #7257. This
update follows [this
suggestion](https://github.com/pypa/readme_renderer/issues/169#issuecomment-808577486)
to add anchor tags (with no spaces) after the title that is to be linked
to.

## Test Plan

- This has been tested on GitHub to check that the additional tags do
not interfere with how the read me is rendered; see:
https://github.com/calumy/ruff/blob/add-links-to-pypi-docs/README.md
- MK docs were generated using the `generate_mkdocs.py` script; however
as the added tags are beyond the comment `<!-- End section: Overview
-->`, they are excluded so will not change how the docs are rendered.
- I was unable to verify how PyPI renders this change, any suggestions
would be appreciated and I can follow up on this. Hopefully, the four
thumbs up/heart on [this
comment](https://github.com/pypa/readme_renderer/issues/169#issuecomment-808577486)
and [this
suggestion](https://github.com/pypa/readme_renderer/issues/169#issuecomment-1765616890)
all suggest that this approach should work.
2024-08-26 12:42:35 +05:30
Steve C
0b5828a1e8 [flake8-simplify] - extend open-file-with-context-handler to work with dbm.sqlite3 (SIM115) (#13104)
## Summary

Adds upcoming `dbm.sqlite3` to rule that suggests using context managers
to open things with.

See: https://docs.python.org/3.13/library/dbm.html#module-dbm.sqlite3

## Test Plan

`cargo test`
2024-08-26 08:11:03 +01:00
Steve C
5af48337a5 [pylint] - fix incorrect starred expression replacement for nested-min-max (PLW3301) (#13089)
## Summary

Moves the min/max detection up, and fixes #13088 

## Test Plan

`cargo test`
2024-08-26 10:01:38 +05:30
renovate[bot]
39ad6b9472 Update tj-actions/changed-files action to v45 (#13102) 2024-08-25 22:11:24 -04:00
renovate[bot]
41dec93cd2 Update dependency monaco-editor to ^0.51.0 (#13101) 2024-08-25 22:11:15 -04:00
renovate[bot]
aee2caa733 Update NPM Development dependencies (#13100) 2024-08-25 22:11:07 -04:00
renovate[bot]
fe5544e137 Update dependency react-resizable-panels to v2.1.1 (#13098) 2024-08-25 22:11:01 -04:00
renovate[bot]
14c014a48b Update Rust crate syn to v2.0.76 (#13097) 2024-08-25 22:10:57 -04:00
renovate[bot]
ecd0597d6b Update Rust crate serde_json to v1.0.127 (#13096) 2024-08-25 22:10:50 -04:00
renovate[bot]
202271fba6 Update Rust crate serde to v1.0.209 (#13095) 2024-08-25 22:10:45 -04:00
renovate[bot]
4bdb0b4f86 Update Rust crate quote to v1.0.37 (#13094) 2024-08-25 22:10:38 -04:00
renovate[bot]
2286f916c1 Update Rust crate libc to v0.2.158 (#13093) 2024-08-25 22:10:32 -04:00
renovate[bot]
1e4c944251 Update pre-commit dependencies (#13099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-26 01:49:41 +01:00
Calum Young
f50f8732e9 [flake8-pytest-style] Improve help message for pytest-incorrect-mark-parentheses-style (PT023) (#13092) 2024-08-26 01:37:57 +01:00
Micha Reiser
ecab04e338 Basic concurrent checking (#13049) 2024-08-24 09:53:27 +01:00
Dylan
8c09496b07 [red-knot] Resolve function annotations before adding function symbol (#13084)
This PR has the `SemanticIndexBuilder` visit function definition
annotations before adding the function symbol/name to the builder.

For example, the following snippet no longer causes a panic:

```python
def bool(x) -> bool:
    Return True
```

Note: This fix changes the ordering of the global symbol table.

Closes #13069
2024-08-23 19:31:36 -07:00
Alex Waygood
d19fd1b91c [red-knot] Add symbols for for loop variables (#13075)
## Summary

This PR adds symbols introduced by `for` loops to red-knot:
- `x` in `for x in range(10): pass`
- `x` and `y` in `for x, y in d.items(): pass`
- `a`, `b`, `c` and `d` in `for [((a,), b), (c, d)] in foo: pass`

## Test Plan

Several tests added, and the assertion in the benchmarks has been
updated.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-23 23:40:27 +01:00
Dhruv Manilawala
99df859e20 Include all required keys for Zed settings (#13082)
## Summary

Closes: #13081
2024-08-23 16:18:05 +00:00
Micha Reiser
2d5fe9a6d3 Fix dark theme on initial page load (#13077) 2024-08-23 12:53:43 +00:00
jesse
1f2cb09853 [async-function-with-timeout] Disable check for asyncio before Python 3.11 (ASYNC109) (#13023)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-23 08:02:53 +00:00
Dhruv Manilawala
cfe25ab465 [red-knot] Support untitled files in the server (#13044)
## Summary

This PR adds support for untitled files in the red knot server.

## Test Plan

https://github.com/user-attachments/assets/57fa5db6-e1ad-4694-ae5f-c47a21eaa82b
2024-08-23 12:47:35 +05:30
Dhruv Manilawala
551ed2706b [red-knot] Simplify virtual file support (#13043)
## Summary

This PR simplifies the virtual file support in the red knot core,
specifically:

* Update `File::add_virtual_file` method to `File::virtual_file` which
will always create a new virtual file and override the existing entry in
the lookup table
* Add `VirtualFile` which is a wrapper around `File` and provides
methods to increment the file revision / close the virtual file
* Add a new `File::try_virtual_file` to lookup the `VirtualFile` from
`Files`
* Add `File::sync_virtual_path` which takes in the `SystemVirtualPath`,
looks up the `VirtualFile` for it and calls the `sync` method to
increment the file revision
* Removes the `virtual_path_metadata` method on `System` trait

## Test Plan

- [x] Make sure the existing red knot tests pass
- [x] Updated code works well with the LSP
2024-08-23 07:04:15 +00:00
Dhruv Manilawala
21c5606793 [red-knot] Support textDocument/didChange notification (#13042)
## Summary

This PR adds support for `textDocument/didChange` notification.

There seems to be a bug (probably in Salsa) where it panics with:
```
2024-08-22 15:33:38.802 [info] panicked at /Users/dhruv/.cargo/git/checkouts/salsa-61760caba2b17ca5/f608ff8/src/tracked_struct.rs:377:9:
two concurrent writers to Id(4800), should not be possible
```

## Test Plan


https://github.com/user-attachments/assets/81055feb-ba8e-4acf-ad2f-94084a3efead
2024-08-23 06:58:54 +00:00
Dhruv Manilawala
c73a7bb929 [red-knot] Support files outside of any workspace (#13041)
## Summary

This PR adds basic support for files outside of any workspace in the red
knot server.

This also limits the red knot server to only work in a single workspace.
The server will not start if there are multiple workspaces.

## Test Plan

https://github.com/user-attachments/assets/de601387-0ad5-433c-9d2c-7b6ae5137654
2024-08-23 06:51:48 +00:00
Micha Reiser
4f6accb5c6 Add basic red knot benchmark (#13026)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-23 08:22:42 +02:00
Micha Reiser
1ca14e4335 Move collection of parse errors to check_file (#13059) 2024-08-23 08:22:12 +02:00
Teodoro Freund
b9c8113a8a Added bytes type and some inference (#13061)
## Summary

This PR adds the `bytes` type to red-knot:
- Added the `bytes` type
- Added support for bytes literals
- Support for the `+` operator

Improves on #12701 

Big TODO on supporting and normalizing r-prefixed bytestrings
(`rb"hello\n"`)

## Test Plan

Added a test for a bytes literals, concatenation, and corner values
2024-08-22 13:27:15 -07:00
Dylan
2edd32aa31 [red-knot] SemanticIndexBuilder visits value before target in named expressions (#13053)
The `SemanticIndexBuilder` was causing a cycle in a salsa query by
attempting to resolve the target before the value in a named expression
(e.g. `x := x+1`). This PR swaps the order, avoiding a panic.

Closes #13012.
2024-08-22 07:59:13 -07:00
Dhruv Manilawala
02c4373a49 Bump version to 0.6.2 (#13056) 2024-08-22 18:59:27 +05:30
Steve C
d37e2e5d33 [flake8-simplify] Extend open-file-with-context-handler to work with other standard-library IO modules (SIM115) (#12959)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-08-22 14:18:55 +01:00
Dhruv Manilawala
d1d067896c [red-knot] Remove notebook support from the server (#13040)
## Summary

This PR removes notebook sync support from server capabilities because
it isn't tested, it'll be added back once we actually add full support
for notebook.
2024-08-22 14:55:46 +05:30
olp-cs
93f9023ea3 Add hyperfine installation instructions; update hyperfine code samples (#13034)
## Summary

When following the step-by-step instructions to run the benchmarks in
`CONTRIBUTING.md`, I encountered two errors:

**Error 1:**
`bash: hyperfine: command not found`
    
**Solution**: I updated the instructions to include the step of
installing the benchmark tool.

**Error 2:**
```shell
$ ./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ 
error: `ruff <path>` has been removed. Use `ruff check <path>` instead.
```
**Solution**: I added `check`.

## Test Plan

I tested it by running the benchmark-related commands in a new workspace
within GitHub Codespaces.
2024-08-22 09:05:09 +05:30
Dhruv Manilawala
8144a11f98 [red-knot] Add definition for with items (#12920)
## Summary

This PR adds symbols and definitions introduced by `with` statements.

The symbols and definitions are introduced for each with item. The type
inference is updated to call the definition region type inference
instead.

## Test Plan

Add test case to check for symbol table and definitions.
2024-08-22 08:00:19 +05:30
Micha Reiser
dce87c21fd Eagerly validate typeshed versions (#12786) 2024-08-21 15:49:53 +00:00
Micha Reiser
f873d2ac12 Revert "Use the system allocator for codspeed benchmarks" (#13035) 2024-08-21 17:13:11 +02:00
Alex Waygood
ecd9e6a650 [red-knot] Improve the unresolved-import check (#13007)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-21 13:44:49 +00:00
Micha Reiser
785c39927b Use ZIP file size metadata to allocate string (#13032) 2024-08-21 12:48:44 +00:00
Micha Reiser
a35cdbb275 Fix various panicks when linting black/src (#13033) 2024-08-21 12:35:29 +00:00
Dhruv Manilawala
0c98b5949c Show full error context in server messages (#13029)
## Summary

Reference:
https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations

Closes: #13022 

## Test Plan

```
2024-08-21 15:21:24.831 [info] [Trace - 3:21:24 PM]    0.017255167s ERROR ThreadId(04) ruff_server::session::index::ruff_settings: Failed to parse /Users/dhruv/playground/ruff/pyproject.toml: TOML parse error at line 1, column 1
  |
1 | [tool.ruff.lint]
  | ^^^^^^^^^^^^^^^^
Unknown rule selector: `ME102`
```

Or,
```
2024-08-21 15:23:47.993 [info] [Trace - 3:23:47 PM]  143.179857375s ERROR ThreadId(66) ruff_server::session::index::ruff_settings: Failed to parse /Users/dhruv/playground/ruff/pyproject.toml: TOML parse error at line 2, column 42
  |
2 | select = ["ALL", "TD006", "TD007", "FIX"
  |                                          ^
invalid array
expected `]`
```
2024-08-21 15:36:16 +05:30
Micha Reiser
e5f37a8254 Remove linter dependency from red_knot_server (#13028) 2024-08-21 10:02:42 +00:00
Micha Reiser
5c5dfc11f0 Upgrade to Salsa with tables (#13016) 2024-08-21 06:58:53 +00:00
Micha Reiser
678045e1aa Use the system allocator for codspeed benchmarks (#13005) 2024-08-21 08:46:51 +02:00
François-Michel L'Heureux
dedefd73da Update example for PT001 as per the new default behavior (#13019)
## Summary

Example / Use instead were not updated with the release of ruff 0.6.0.
This updates them accordingly.
2024-08-21 09:34:18 +05:30
Alex Waygood
37a60460ed [red-knot] Improve various tracing logs (#13015) 2024-08-20 18:34:51 +00:00
Micha Reiser
0bd258a370 Use check instead of check_file in benchmarks (#13004) 2024-08-20 12:20:40 +02:00
Dylan
9baab8672a [flake8-pyi] Skip type annotations in string-or-bytes-too-long (PYI053) (#13002) 2024-08-20 10:53:22 +01:00
Micha Reiser
c65e3310d5 Add API to emit type-checking diagnostics (#12988)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-20 07:22:30 +00:00
Micha Reiser
38c19fb96e Fix re-entrance deadlock in Package::files (#12948) 2024-08-20 06:51:08 +00:00
Alex Lowe
abb4cdbf3d pydocstyle: Add ignore setting to linter docs (#12996) 2024-08-20 08:33:50 +02:00
tfardet
fc811f5168 Expand note to use Ruff with other language server in Kate (#12806)
## Summary

Provide instructions to use Ruff together with other servers in the Kate
editor.
Because Kate does not support running multiple servers for the same
language, one needs to use the ``python-lsp-server`` (pylsp) tool.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-08-20 06:18:31 +00:00
Dhruv Manilawala
1a8f29ea41 [red-knot] Add symbols defined by match statements (#12926)
## Summary

This PR adds symbols introduced by `match` statements.

There are three patterns that introduces new symbols:
* `as` pattern
* Sequence pattern
* Mapping pattern

The recursive nature of the visitor makes sure that all symbols are
added.

## Test Plan

Add test case for all types of patterns that introduces a symbol.
2024-08-20 05:16:27 +00:00
Dhruv Manilawala
aefaddeae7 [red-knot] Add definition for augmented assignment (#12892)
## Summary

This PR adds definition for augmented assignment. This is similar to
annotated assignment in terms of implementation.

An augmented assignment should also record a use of the variable but
that's a TODO for now.

## Test Plan

Add test case to validate that a definition is added.
2024-08-20 10:33:55 +05:30
Mathieu Kniewallner
df09045176 docs: add stricter validation options (#12998)
## Summary

Applying the same change as done in
https://github.com/astral-sh/uv/pull/6096. Note that in `uv` repository,
this [broke the docs
build](https://github.com/astral-sh/uv/pull/6096#issuecomment-2290151150)
because `anchors` is `mdkocs` 1.6+ only, and insiders used 1.5.0 while
public dependencies used 1.6.0, but in this repository, both use 1.6.0
([public](049cda2ff3/docs/requirements.txt (L3)),
[insiders](049cda2ff3/docs/requirements-insiders.txt (L3))),
so this should not be an issue to have in the template.

Contrarily to `uv` repository, no violations were reported here, but
this could prevent adding some in the future.

## Test Plan

Local run of the documentation + `mkdocs build --strict`.
2024-08-19 18:07:41 -05:00
Alex Waygood
049cda2ff3 flake8-type-checking: Always recognise relative imports as first-party (#12994) 2024-08-19 19:06:56 +01:00
renovate[bot]
358792f2c9 Update pre-commit dependencies (#12978)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-08-19 13:41:03 +02:00
Alex Waygood
e6d5a7af37 Add the testing feature of ruff_db as a dev-dependency for ruff_workspace (#12985) 2024-08-19 10:22:01 +00:00
Mathieu Kniewallner
f5bff82e70 docs(contributing): remove TOC (#12903)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-19 09:38:08 +00:00
Alex Waygood
ab44152eb5 Improve release instructions for when ruff-lsp and ruff-vscode updates are required (#12952) 2024-08-19 10:29:16 +01:00
Ken Baskett
f4c8c7eb70 [ruff] Implement check for Decimal called with a float literal (RUF032) (#12909)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-19 09:22:19 +00:00
InSync
65de8f2c9b Quote default values consistently (#12981)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-19 08:02:55 +00:00
renovate[bot]
e6226436fd Update NPM Development dependencies (#12976)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-19 07:44:21 +00:00
renovate[bot]
0345d46759 Update dependency react-resizable-panels to v2.1.0 (#12977)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-19 09:40:48 +02:00
renovate[bot]
4d0d3b00cb Update rust-wasm-bindgen monorepo (#12975) 2024-08-18 20:44:00 -04:00
renovate[bot]
2be1c4ff04 Update Rust crate syn to v2.0.75 (#12974) 2024-08-18 20:43:54 -04:00
renovate[bot]
edd86d5603 Update Rust crate serde_json to v1.0.125 (#12973) 2024-08-18 20:43:48 -04:00
renovate[bot]
78ad7959ca Update Rust crate serde to v1.0.208 (#12972) 2024-08-18 20:43:42 -04:00
renovate[bot]
d72ecd6ded Update Rust crate ordermap to v0.5.2 (#12971) 2024-08-18 20:43:37 -04:00
renovate[bot]
8617a508bd Update Rust crate libc to v0.2.157 (#12970) 2024-08-18 20:43:31 -04:00
renovate[bot]
c88bd4e884 Update Rust crate ctrlc to v3.4.5 (#12969) 2024-08-18 20:43:24 -04:00
renovate[bot]
fbcda90316 Update Rust crate camino to v1.1.9 (#12967) 2024-08-18 20:43:18 -04:00
renovate[bot]
169d4390cb Update Rust crate clap to v4.5.16 (#12968) 2024-08-18 20:43:05 -04:00
Charlie Marsh
80ade591df Ignore unused arguments on stub functions (#12966)
## Summary

We already enforce this logic for the other `ARG` rules. I'm guessing
this was an oversight.

Closes https://github.com/astral-sh/ruff/issues/12963.
2024-08-18 19:21:33 -04:00
Steve C
4881d32c80 [pylint] - remove AugAssign errors from self-cls-assignment (W0642) (#12957) 2024-08-18 15:31:09 +00:00
Steve C
81a2220ce1 [pylint] - Allow __new__ methods to have cls as their first argument even if decorated with @staticmethod for bad-staticmethod-argument (PLW0211) (#12958) 2024-08-18 16:30:22 +01:00
Aaron Gokaslan
900e98b584 Fix CHANGELOG.md typo (#12955) 2024-08-17 17:43:07 +01:00
Alex Waygood
f9d8189670 [perflint] Improve docs for try-except-in-loop (PERF203) (#12947) 2024-08-17 16:00:15 +01:00
TomerBin
52ba94191a [ruff] Reduce FastAPI false positives in unused-async (RUF029) (#12938) 2024-08-17 14:25:14 +00:00
Micha Reiser
96802d6a7f [pep8-naming] Don't flag from imports following conventional import names (N817) (#12946)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-17 12:05:42 +00:00
Micha Reiser
dd0a7ec73e Pull all types in corpus tests (#12919) 2024-08-17 11:59:55 +00:00
Daniel Sonbolian
25f5ae44c4 [flake8_bugbear] message based on expression location [B015] (#12944) 2024-08-17 13:54:19 +02:00
Alex Waygood
251efe5c41 [ruff] Ignore fstring-missing-syntax (RUF027) for fastAPI paths (#12939)
## Summary

As suggested by @MichaReiser in
https://github.com/astral-sh/ruff/pull/12886#pullrequestreview-2237679793,
this adds an exemption to `RUF027` for `fastAPI` paths, which require
template strings rather than eagerly evaluated f-strings.

## Test Plan

I added a fixture that causes Ruff to emit a false-positive error on
`main` but no longer does with this PR.
2024-08-17 11:10:34 +01:00
Carl Meyer
6359e55383 [red-knot] type narrowing (#12706)
Extend the `UseDefMap` to also track which constraints (provided by e.g.
`if` tests) apply to each visible definition.

Uses a custom `BitSet` and `BitSetArray` to track which constraints
apply to which definitions, while keeping data inline as much as
possible.
2024-08-16 16:34:13 -07:00
Alex Waygood
a9847af6e8 [red-knot] Use Unknown rather than Unbound for unresolved imports (#12932) 2024-08-16 20:10:33 +01:00
Micha Reiser
d61d75d4fa Select stable import name when multiple possible bindings are in scope (#12888) 2024-08-16 20:16:57 +02:00
Alex Waygood
499c0bd875 Bump version to 0.6.1 (#12937)
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-16 17:48:06 +01:00
Alex Waygood
4cb30b598f N817 docs: refer to the correct setting (#12935) 2024-08-16 15:41:00 +00:00
Micha Reiser
aba0d83c11 [flake8-naming]: Respect import conventions (N817) (#12922) 2024-08-16 16:28:57 +01:00
Dhruv Manilawala
c319414e54 Ignore blank line rules for docs formatting (#12934)
## Summary

fixes: #12933 

## Test Plan

`python scripts/check_docs_formatted.py --generate-docs`
2024-08-16 15:27:36 +00:00
Alex Waygood
ef1f6d98a0 Fix description of where the contributor list comes from in instructions for making a release (#12931) 2024-08-16 15:37:21 +01:00
Dhruv Manilawala
b850b812de Use cell source code instead of the concatenated one (#12929)
## Summary

fixes: #12880

## Test Plan

Test against the notebook provided in the issue.
2024-08-16 19:50:12 +05:30
Alex Waygood
a87b27c075 [red-knot] Add support for relative imports (#12910)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-08-16 12:35:27 +01:00
Micha Reiser
9b73532b11 [flake8-async] Fix examples to use async with (#12924) 2024-08-16 12:24:59 +02:00
Alex Waygood
d8debb7a36 Simplify logic for RUF027 (#12907)
## Summary

This PR is a pure refactor to simplify some of the logic for `RUF027`.
This will make it easier to file some followup PRs to help reduce the
false positives from this rule. I'm separating the refactor out into a
separate PR so it's easier to review, and so I can double-check from the
ecosystem report that this doesn't have any user-facing impact.

## Test Plan

`cargo test -p ruff_linter --lib`
2024-08-16 08:05:15 +01:00
Dhruv Manilawala
bd4a947b29 [red-knot] Add symbol and definition for parameters (#12862)
## Summary

This PR adds support for adding symbols and definitions for function and
lambda parameters to the semantic index.

### Notes

* The default expression of a parameter is evaluated in the enclosing
scope (not the type parameter or function scope).
* The annotation expression of a parameter is evaluated in the type
parameter scope if they're present other in the enclosing scope.
* The symbols and definitions are added in the function parameter scope.

### Type Inference

There are two definitions `Parameter` and `ParameterWithDefault` and
their respective `*_definition` methods on the type inference builder.
These methods are preferred and are re-used when checking from a
different region.

## Test Plan

Add test case for validating that the parameters are defined in the
function / lambda scope.

### Benchmark update

Validated the difference in diagnostics for benchmark code between
`main` and this branch. All of them are either directly or indirectly
referencing one of the function parameters. The diff is in the PR description.
2024-08-16 10:59:59 +05:30
Matthieu LAURENT
f121f8b31b [fastapi] Implement fast-api-unused-path-parameter (FAST003) (#12638)
This adds the `fast-api-unused-path-parameter` lint rule, as described
in #12632.

I'm still pretty new to rust, so the code can probably be improved, feel
free to tell me if there's any changes i should make.

Also, i needed to add the `add_parameter` edit function, not sure if it
was in the scope of the PR or if i should've made another one.
2024-08-16 01:46:35 +00:00
Carl Meyer
80efb865e9 [red-knot] fix lookups of possibly-shadowed builtins (#12898)
If a builtin is conditionally shadowed by a global, we didn't correctly
fall back to builtins for the not-defined-in-globals path (see added
test for an example.)
2024-08-15 14:09:29 -07:00
Jonathan Plasse
52d27befe8 Rename too-many-positional(-arguments) (#12905) 2024-08-15 18:13:25 +02:00
Alex Waygood
6ed06afd28 Fixup description of default values for fixture-parentheses and mark-parentheses (#12904) 2024-08-15 15:20:36 +01:00
Micha Reiser
b9da31610a Bump version to 0.6 (#12894) 2024-08-15 13:17:22 +01:00
github-actions[bot]
ac7b1770e2 Sync vendored typeshed stubs (#12899)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-08-14 18:11:23 -07:00
Dylan
e4c2859c0f [flake8-async] Do not lint yield in context manager cancel-scope-no-checkpoint (ASYNC100) (#12896)
For compatibility with upstream, treat `yield` as a checkpoint inside
cancel scopes.

Closes #12873.
2024-08-15 01:02:57 +00:00
Dylan
6dcd743111 [flake8-comprehensions] Do not lint async for comprehensions in unnecessary-comprehension-in-call (C419) (#12895)
List and set comprehensions using `async for` cannot be replaced with
underlying generators; this PR modifies C419 to skip such
comprehensions.

Closes #12891.
2024-08-15 01:00:10 +00:00
Dhruv Manilawala
73160dc8b6 Stabilize support for Jupyter Notebooks (#12878)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Closes: #12456
Closes: https://github.com/astral-sh/ruff-vscode/issues/546
2024-08-14 18:15:45 +02:00
Charlie Marsh
15aa5a6d57 Detect imports in src layouts by default (#12848)
## Summary

Occasionally, we receive bug reports that imports in `src` directories
aren't correctly detected. The root of the problem is that we default to
`src = ["."]`, so users have to set `src = ["src"]` explicitly. This PR
extends the default to cover _both_ of them: `src = [".", "src"]`.

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

## Test Plan

I replicated the structure described in
https://github.com/astral-sh/ruff/issues/12453, and verified that the
imports were considered sorted, but that adding `src = ["."]` showed an
error.
2024-08-14 18:15:45 +02:00
Alex Waygood
33512a4249 Stabilise redirected-noqa (RUF101) (#12869) 2024-08-14 18:15:45 +02:00
Alex Waygood
d8ebb03591 Improve the error message for PLW0642 (#12866) 2024-08-14 18:15:45 +02:00
Micha Reiser
2e211c5c22 Change default for PT001 and PT023 (#12838)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-14 18:15:45 +02:00
Micha Reiser
9fd8aaaf29 Stabilize two flake8-pyi rules (#12860) 2024-08-14 18:15:45 +02:00
Alex Waygood
d110bd4e60 Stabilise 9 pylint rules (#12857) 2024-08-14 18:15:45 +02:00
Micha Reiser
eb9c7ae869 Stabilize fixes for RET50{5-8} (#12840)
Fixes #10099
2024-08-14 18:15:45 +02:00
Micha Reiser
7defc0d136 Deprecate PT004 and PT005 (#12837)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-14 18:15:45 +02:00
Micha Reiser
45f459bafd Stabilize ASYNC100, ASYNC109, ASYNC110, ASYNC115 and ASYNC116 behavior changes (#12844)
Closes https://github.com/astral-sh/ruff/issues/12268
2024-08-14 18:15:45 +02:00
Micha Reiser
99e946a005 Deprecate UP027 (#12843)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Closes https://github.com/astral-sh/ruff/issues/12754
2024-08-14 18:15:45 +02:00
Charlie Marsh
78a7ac0722 Re-code unnecessary-dict-comprehension-for-iterable (RUF025) as C420 (#12533)
Closes https://github.com/astral-sh/ruff/issues/12110.
2024-08-14 18:15:45 +02:00
edhinard
fa2f3f9f2f add conventional xml.etree.ElementTree import alias (#12455) 2024-08-14 18:15:45 +02:00
Sid
3898d737d8 [pyupgrade] Show violations without auto-fix for UP031 (#11229)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-14 11:59:40 +00:00
Alex Waygood
c487149b7d RUF027: Ignore template strings passed to logging calls and builtins._() calls (#12889) 2024-08-14 11:27:35 +01:00
Alex Waygood
bebed67bf1 Improve docs for non-augmented-assignment (PLR6104) (#12887) 2024-08-14 10:50:00 +01:00
Alex Waygood
3ddcad64f5 Improve docs for missing-fstring-syntax (RUF027) (#12886) 2024-08-14 10:49:49 +01:00
Dhruv Manilawala
05c35b6975 [red-knot] Use line/column for server diagnostics if available (#12881)
## Summary

This PR adds very basic support for using the line / column information
from the diagnostic message. This makes it easier to validate
diagnostics in an editor as oppose to going through the diff one
diagnostic at a time and confirming it at the location.
2024-08-14 15:11:31 +05:30
Jonathan Plasse
7fc39ad624 [flake8-return] Only add return None at end of function (RET503) (#11074)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-14 07:47:45 +00:00
Dhruv Manilawala
2520ebb145 Fallback to kernelspec to check if it's a Python notebook (#12875)
## Summary

This PR adds a fallback logic for `is_python_notebook` to check the
`kernelspec.language` field.

Reference implementation in VS Code:
1c31e75898/extensions/ipynb/src/deserializers.ts (L20-L22)

It's also required for the kernel to provide the `language` they're
implementing based on
https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
reference although that's for the `kernel.json` file but is also
included in the notebook metadata.

Closes: #12281

## Test Plan

Add a test case for `is_python_notebook` and include the test notebook
for round trip validation.

The test notebook contains two cells, one is JavaScript (denoted via the
`vscode.languageId` metadata) and the other is Python (no metadata). The
notebook metadata only contains `kernelspec` and the `language_info` is
absent.

I also verified that this is a valid notebook by opening it in Jupyter
Lab, VS Code and using `nbformat` validator.
2024-08-14 12:36:09 +05:30
Dhruv Manilawala
89c8b49027 Update OpenAI excluded notebooks from ecosystem checks (#12867)
## Summary

Follow-up to #12864, we don't need to exclude these notebooks anymore.

## Test plan

- [x] Make sure that ecosystem checks are green.
2024-08-14 08:03:25 +05:30
Charlie Marsh
e05953a991 Avoid treating dataclasses.KW_ONLY as typing-only (#12863)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12859.
2024-08-13 14:34:56 -04:00
Alex Waygood
d0ac38f9d3 Limit requirements.txt files updated by renovate (#12868) 2024-08-13 17:15:09 +00:00
Dhruv Manilawala
ff53db3d99 Consider VS Code cell metadata to determine valid code cells (#12864)
## Summary

This PR adds support for VS Code specific cell metadata to consider when
collecting valid code cells.

For context, Ruff only runs on valid code cells. These are the code
cells that doesn't contain cell magics. Previously, Ruff only used the
notebook's metadata to determine whether it's a Python notebook. But, in
VS Code, a notebook's preferred language might be Python but it could
still contain code cells for other languages. This can be determined
with the `metadata.vscode.languageId` field.

### References:
* https://code.visualstudio.com/docs/languages/identifiers
* e6c009a3d4/extensions/ipynb/src/serializers.ts (L104-L107)
*
e6c009a3d4/extensions/ipynb/src/serializers.ts (L117-L122)

This brings us one step closer to fixing #12281.

## Test Plan

Add test cases for `is_valid_python_code_cell` and an integration test
case which showcase running it end to end. The test notebook contains a
JavaScript code cell and a Python code cell.
2024-08-13 22:09:56 +05:30
Dhruv Manilawala
899a52390b Evaluate default parameter value in enclosing scope (#12852)
## Summary

This PR fixes a bug in the semantic model where it would evaluate the
default parameter value in the type parameter scope. For example,

```py
def foo[T1: int](a = T1):
    pass
```

Here, the `T1` in `a = T1` is undefined but Ruff doesn't flag it
(https://play.ruff.rs/ba2f7c2f-4da6-417e-aa2a-104aa63e6d5e).

The fix here is to evaluate the default parameter value in the
_enclosing_ scope instead.

## Test Plan

Add a test case which includes the above code under `F821`
(`undefined-name`) and validate the snapshot.
2024-08-13 19:25:49 +05:30
Tzu-ping Chung
82a3e69b8a [flake8-pytest-style] Add a space after comma in CSV output (PT006) (#12853)
## Summary

See #12703. This only addresses the first bullet point, adding a space
after the comma in the suggested fix from list/tuple to string.

## Test Plan

Updated the snapshots and compared.
2024-08-13 13:32:09 +05:30
Dhruv Manilawala
7027344dfc Add scope and definitions for comprehensions (#12748)
## Summary

This PR adds scope and definition for comprehension nodes. This includes
the following nodes:
* List comprehension
* Dictionary comprehension
* Set comprehension 
* Generator expression

### Scope

Each expression here adds it's own scope with one caveat - the `iter`
expression of the first generator is part of the parent scope. For
example, in the following code snippet the `iter1` variable is evaluated
in the outer scope.

```py
[x for x in iter1]
```

> The iterable expression in the leftmost for clause is evaluated
directly in the enclosing scope and then passed as an argument to the
implicitly nested scope.
>
> Reference:
https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries

There's another special case for assignment expressions:

> There is one special case: an assignment expression occurring in a
list, set or dict comprehension or in a generator expression (below
collectively referred to as “comprehensions”) binds the target in the
containing scope, honoring a nonlocal or global declaration for the
target in that scope, if one exists.
>
> Reference: https://peps.python.org/pep-0572/#scope-of-the-target

For example, in the following code snippet, the variables `a` and `b`
are available after the comprehension while `x` isn't:
```py
[a := 1 for x in range(2) if (b := 2)]
```

### Definition

Each comprehension node adds a single definition, the "target" variable
(`[_ for target in iter]`). This has been accounted for and a new
variant has been added to `DefinitionKind`.

### Type Inference

Currently, type inference is limited to a single scope. It doesn't
_enter_ in another scope to infer the types of the remaining expressions
of a node. To accommodate this, the type inference for a **scope**
requires new methods which _doesn't_ infer the type of the `iter`
expression of the leftmost outer generator (that's defined in the
enclosing scope).

The type inference for the scope region is split into two parts:
* `infer_generator_expression` (similarly for comprehensions) infers the
type of the `iter` expression of the leftmost outer generator
* `infer_generator_expression_scope` (similarly for comprehension)
infers the type of the remaining expressions except for the one
mentioned in the previous point

The type inference for the **definition** also needs to account for this
special case of leftmost generator. This is done by defining a `first`
boolean parameter which indicates whether this comprehension definition
occurs first in the enclosing expression.

## Test Plan

New test cases were added to validate multiple scenarios. Refer to the
documentation for each test case which explains what is being tested.
2024-08-13 07:00:33 +05:30
Carl Meyer
fb9f0c448f [red-knot] cleanup doc comments and attributes (#12792)
Make `cargo doc -p red_knot_python_semantic --document-private-items`
run warning-free. I'd still like to do this for all of ruff and start
enforcing it in CI (https://github.com/astral-sh/ruff/issues/12372) but
haven't gotten to it yet. But in the meantime I'm trying to maintain it
for at least `red_knot_python_semantic`, as it helps to ensure our doc
comments stay up to date.

A few of the comments I just removed or shortened, as their continued
relevance wasn't clear to me; please object in review if you think some
of them are important to keep!

Also remove a no-longer-needed `allow` attribute.
2024-08-12 12:15:16 -07:00
Carl Meyer
75131c6f4a [red-knot] add IntersectionBuilder (#12791)
For type narrowing, we'll need intersections (since applying type
narrowing is just a type intersection.)

Add `IntersectionBuilder`, along with some tests for it and
`UnionBuilder` (renamed from `UnionTypeBuilder`).

We use smart builders to ensure that we always keep these types in
disjunctive normal form (DNF). That means that we never have deeply
nested trees of unions and intersections: unions flatten into unions,
intersections flatten into intersections, and intersections distribute
over unions, so the most complex tree we can ever have is a union of
intersections. We also never have a single-element union or a
single-positive-element intersection; these both just simplify to the
contained type.

Maintaining these invariants means that `UnionBuilder` doesn't
necessarily end up building a `Type::Union` (e.g. if you only add a
single type to the union, it'll just return that type instead), and
`IntersectionBuilder` doesn't necessarily build a `Type::Intersection`
(if you add a union to the intersection, we distribute the intersection
over that union, and `IntersectionBuilder` will end up returning a
`Type::Union` of intersections).

We also simplify intersections by ensuring that if a type and its
negation are both in an intersection, they simplify out. (In future this
should also respect subtyping, not just type identity, but we don't have
subtyping yet.) We do implement subtyping of `Never` as a special case
for now.

Most of this PR is unused for now until type narrowing lands; I'm just
breaking it out to reduce the review fatigue of a single massive PR.
2024-08-12 11:56:04 -07:00
Dhruv Manilawala
4b9ddc4a06 [red-knot] Use Windows specific path separator in tests (#12847) 2024-08-12 22:26:59 +05:30
Dhruv Manilawala
99dc208b00 [red-knot] Add filename and source location for diagnostics (#12842)
## Summary

I'm not sure if this is useful but this is a hacky implementation to add
the filename and row / column numbers to the current Red Knot
diagnostics.
2024-08-12 15:56:30 +00:00
Dhruv Manilawala
540023262e Collect errors while building up the settings index (#12781)
## Summary

Related to https://github.com/astral-sh/ruff-vscode/issues/571, this PR
updates the settings index builder to trace all the errors it
encountered. Without this, there's no way for user to know that
something failed and some of the capability might not work as expected.
For example, in the linked PR, the settings were invalid which means
notebooks weren't included and there were no log messages for it.

## Test Plan

Create an invalid `ruff.toml` file:
```toml
[tool.ruff]
extend-exclude = ["*.ipynb"]
```

Logs:
```
2024-08-12 18:33:09.873 [info] [Trace - 6:33:09 PM]   12.217043000s ERROR ruff:main ruff_server::session::index::ruff_settings: Failed to parse /Users/dhruv/playground/ruff/pyproject.toml
```

Notification Preview:

<img width="483" alt="Screenshot 2024-08-12 at 18 33 20"
src="https://github.com/user-attachments/assets/a4f303e5-f073-454f-bdcd-ba6af511e232">

Another way to trigger is to provide an invalid `cache-dir` value:
```toml
[tool.ruff]
cache-dir = "$UNKNOWN"
```

Same notification preview but different log message:
```
2024-08-12 18:41:37.571 [info] [Trace - 6:41:37 PM]   21.700112208s ERROR ThreadId(30) ruff_server::session::index::ruff_settings: Error while resolving settings from /Users/dhruv/playground/ruff/pyproject.toml: Invalid `cache-dir` value: error looking key 'UNKNOWN' up: environment variable not found
```

With multiple `pyproject.toml` file:
```
2024-08-12 18:41:15.887 [info] [Trace - 6:41:15 PM]    0.016636833s ERROR ThreadId(04) ruff_server::session::index::ruff_settings: Error while resolving settings from /Users/dhruv/playground/ruff/pyproject.toml: Invalid `cache-dir` value: error looking key 'UNKNOWN' up: environment variable not found

2024-08-12 18:41:15.888 [info] [Trace - 6:41:15 PM]    0.017378833s ERROR ThreadId(13) ruff_server::session::index::ruff_settings: Failed to parse /Users/dhruv/playground/ruff/tools/pyproject.toml
```
2024-08-12 15:42:45 +00:00
Micha Reiser
2ea79572ae Add link to relevant issue for unused variable preview behavior (#12841) 2024-08-12 11:26:40 +00:00
Alex Waygood
aa0db338d9 Implement iter(), len() and is_empty() for all display-literal AST nodes (#12807) 2024-08-12 10:39:28 +00:00
Micha Reiser
a99a45868c Eagerly validate search paths (#12783)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-12 07:46:59 +00:00
Micha Reiser
fabf19fdc9 Skip checking a file if it failed to read (#12755) 2024-08-12 07:26:37 +00:00
eth3lbert
59f712a566 Improvements to documentation (#12712)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-12 07:17:32 +00:00
renovate[bot]
1d080465de Update NPM Development dependencies (#12825)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 08:56:09 +02:00
renovate[bot]
3481e16cdf Update dependency mkdocs to v1.6.0 (#12828)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [mkdocs](https://togithub.com/mkdocs/mkdocs)
([changelog](https://www.mkdocs.org/about/release-notes/)) | `==1.5.0`
-> `==1.6.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mkdocs/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/mkdocs/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/mkdocs/1.5.0/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mkdocs/1.5.0/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>mkdocs/mkdocs (mkdocs)</summary>

### [`v1.6.0`](https://togithub.com/mkdocs/mkdocs/releases/tag/1.6.0)

[Compare
Source](https://togithub.com/mkdocs/mkdocs/compare/1.5.3...1.6.0)

#### Local preview

- `mkdocs serve` no longer locks up the browser when more than 5 tabs
are open. This is achieved by closing the polling connection whenever a
tab becomes inactive. Background tabs will no longer auto-reload either
- that will instead happen as soon the tab is opened again. Context:
[#&#8203;3391](https://togithub.com/mkdocs/mkdocs/issues/3391)

-   New flag `serve --open` to open the site in a browser.\
After the first build is finished, this flag will cause the default OS
Web browser to be opened at the home page of the local site.\
Context: [#&#8203;3500](https://togithub.com/mkdocs/mkdocs/issues/3500)

##### Drafts

> \[!warning]
> **Changed from version 1.5:**
>
> **The `exclude_docs` config was split up into two separate concepts.**

The `exclude_docs` config no longer has any special behavior for `mkdocs
serve` - it now always completely excludes the listed documents from the
site.

If you wish to use the "drafts" functionality like the `exclude_docs`
key used to do in MkDocs 1.5, please switch to the **new config key
`draft_docs`**.

See
[documentation](https://www.mkdocs.org/user-guide/configuration/#exclude_docs).

Other changes:

- Reduce warning levels when a "draft" page has a link to a non-existent
file. Context:
[#&#8203;3449](https://togithub.com/mkdocs/mkdocs/issues/3449)

#### Update to deduction of page titles

MkDocs 1.5 had a change in behavior in deducing the page titles from the
first heading. Unfortunately this could cause unescaped HTML tags or
entities to appear in edge cases.

Now tags are always fully sanitized from the title. Though it still
remains the case that
[`Page.title`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.pages.Page.title)
is expected to contain HTML entities and is passed directly to the
themes.

Images (notably, emojis in some extensions) get preserved in the title
only through their `alt` attribute's value.

Context: [#&#8203;3564](https://togithub.com/mkdocs/mkdocs/issues/3564),
[#&#8203;3578](https://togithub.com/mkdocs/mkdocs/issues/3578)

#### Themes

- Built-in themes now also support Polish language
([#&#8203;3613](https://togithub.com/mkdocs/mkdocs/issues/3613))

##### "readthedocs" theme

- Fix: "readthedocs" theme can now correctly handle deeply nested nav
configurations (over 2 levels deep), without confusedly expanding all
sections and jumping around vertically.
([#&#8203;3464](https://togithub.com/mkdocs/mkdocs/issues/3464))

- Fix: "readthedocs" theme now shows a link to the repository (with a
generic logo) even when isn't one of the 3 known hosters.
([#&#8203;3435](https://togithub.com/mkdocs/mkdocs/issues/3435))

- "readthedocs" theme now also has translation for the word "theme" in
the footer that mistakenly always remained in English.
([#&#8203;3613](https://togithub.com/mkdocs/mkdocs/issues/3613),
[#&#8203;3625](https://togithub.com/mkdocs/mkdocs/issues/3625))

##### "mkdocs" theme

The "mkdocs" theme got a big update to a newer version of Bootstrap,
meaning a slight overhaul of styles. Colors (most notably of
admonitions) have much better contrast.

The "mkdocs" theme now has support for dark mode - both automatic (based
on the OS/browser setting) and with a manual toggle. Both of these
options are **not** enabled by default and need to be configured
explicitly.\
See `color_mode`, `user_color_mode_toggle` in
[**documentation**](https://www.mkdocs.org/user-guide/choosing-your-theme/#mkdocs).

> \[!warning]
> **Possible breaking change:**
>
> jQuery is no longer included into the "mkdocs" theme. If you were
relying on it in your scripts, you will need to separately add it first
(into mkdocs.yml) as an extra script:
>
> ```yaml
> extra_javascript:
>   - https://code.jquery.com/jquery-3.7.1.min.js
> ```
>
> Or even better if the script file is copied and included from your
docs dir.

Context: [#&#8203;3493](https://togithub.com/mkdocs/mkdocs/issues/3493),
[#&#8203;3649](https://togithub.com/mkdocs/mkdocs/issues/3649)

#### Configuration

##### New "`enabled`" setting for all plugins

You may have seen some plugins take up the convention of having a
setting `enabled: false` (or usually controlled through an environment
variable) to make the plugin do nothing.

Now *every* plugin has this setting. Plugins can still *choose* to
implement this config themselves and decide how it behaves (and unless
they drop older versions of MkDocs, they still should for now), but now
there's always a fallback for every plugin.

See
[**documentation**](https://www.mkdocs.org/user-guide/configuration/#enabled-option).
Context: [#&#8203;3395](https://togithub.com/mkdocs/mkdocs/issues/3395)

#### Validation

##### Validation of hyperlinks between pages

##### Absolute links

> Historically, within Markdown, MkDocs only recognized **relative**
links that lead to another physical `*.md` document (or media file).
This is a good convention to follow because then the source pages are
also freely browsable without MkDocs, for example on GitHub. Whereas
absolute links were left unmodified (making them often not work as
expected or, more recently, warned against).

If you dislike having to always use relative links, now you can opt into
absolute links and have them work correctly.

If you set the setting `validation.links.absolute_links` to the new
value `relative_to_docs`, all Markdown links starting with `/` will be
understood as being relative to the `docs_dir` root. The links will then
be validated for correctness according to all the other rules that were
already working for relative links in prior versions of MkDocs. For the
HTML output, these links will still be turned relative so that the site
still works reliably.

So, now any document (e.g. "dir1/foo.md") can link to the document
"dir2/bar.md" as `[link](/dir2/bar.md)`, in addition to the previously
only correct way `[link](../dir2/bar.md)`.

You have to enable the setting, though. The default is still to just
skip any processing of such links.

See
[**documentation**](https://www.mkdocs.org/user-guide/configuration/#validation-of-absolute-links).
Context: [#&#8203;3485](https://togithub.com/mkdocs/mkdocs/issues/3485)

##### Absolute links within nav

Absolute links within the `nav:` config were also always skipped. It is
now possible to also validate them in the same way with
`validation.nav.absolute_links`. Though it makes a bit less sense
because then the syntax is simply redundant with the syntax that comes
without the leading slash.

##### Anchors

There is a new config setting that is recommended to enable warnings
for:

```yaml
validation:
  anchors: warn
```

Example of a warning that this can produce:

```text
WARNING -  Doc file 'foo/example.md' contains a link '../bar.md#some-heading', but the doc 'foo/bar.md' does not contain an anchor '#some-heading'.
```

Any of the below methods of declaring an anchor will be detected by
MkDocs:

```markdown

#### Heading producing an anchor
#### Another heading {#custom-anchor-for-heading-using-attr-list}

<a id="raw-anchor"></a>

[](){#markdown-anchor-using-attr-list}
```

Plugins and extensions that insert anchors, in order to be compatible
with this, need to be developed as treeprocessors that insert `etree`
elements as their mode of operation, rather than raw HTML which is
undetectable for this purpose.

If you as a user are dealing with falsely reported missing anchors and
there's no way to resolve this, you can choose to disable these messages
by setting this option to `ignore` (and they are at INFO level by
default anyway).

See
[**documentation**](https://www.mkdocs.org/user-guide/configuration/#validation).
Context: [#&#8203;3463](https://togithub.com/mkdocs/mkdocs/issues/3463)

Other changes:

- When the `nav` config is not specified at all, the `not_in_nav`
setting (originally added in 1.5.0) gains an additional behavior:
documents covered by `not_in_nav` will not be part of the automatically
deduced navigation. Context:
[#&#8203;3443](https://togithub.com/mkdocs/mkdocs/issues/3443)

- Fix: the `!relative` YAML tag for `markdown_extensions` (originally
added in 1.5.0) - it was broken in many typical use cases.

See
[**documentation**](https://www.mkdocs.org/user-guide/configuration/#paths-relative-to-the-current-file-or-site).
Context: [#&#8203;3466](https://togithub.com/mkdocs/mkdocs/issues/3466)

- Config validation now exits on first error, to avoid showing bizarre
secondary errors. Context:
[#&#8203;3437](https://togithub.com/mkdocs/mkdocs/issues/3437)

- MkDocs used to shorten error messages for unexpected errors such as
"file not found", but that is no longer the case, the full error message
and stack trace will be possible to see (unless the error has a proper
handler, of course). Context:
[#&#8203;3445](https://togithub.com/mkdocs/mkdocs/issues/3445)

#### Upgrades for plugin developers

##### Plugins can add multiple handlers for the same event type, at
multiple priorities

See
[`mkdocs.plugins.CombinedEvent`](https://www.mkdocs.org/dev-guide/plugins/#mkdocs.plugins.CombinedEvent)
in
[**documentation**](https://www.mkdocs.org/dev-guide/plugins/#event-priorities).
Context: [#&#8203;3448](https://togithub.com/mkdocs/mkdocs/issues/3448)

##### Enabling true generated files and expanding the
[`File`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File)
API

See
[**documentation**](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File).

- There is a new pair of attributes
[`File.content_string`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File.content_string]/\[\`content_bytes\`]\[mkdocs.structure.files.File.content_bytes)
that becomes the official API for obtaining the content of a file and is
used by MkDocs itself.

This replaces the old approach where one had to manually read the file
located at
[`File.abs_src_path`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File.abs_src_path),
although that is still the primary action that these new attributes do
under the hood.

- The content of a `File` can be backed by a string and no longer has to
be a real existing file at `abs_src_path`.

It is possible to **set** the attribute `File.content_string` or
`File.content_bytes` and it will take precedence over `abs_src_path`.

Further, `abs_src_path` is no longer guaranteed to be present and can be
`None` instead. MkDocs itself still uses physical files in all cases,
but eventually plugins will appear that don't populate this attribute.

- There is a new constructor
[`File.generated()`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File.generated)
that should be used by plugins instead of the `File()` constructor. It
is much more convenient because one doesn't need to manually look up the
values such as `docs_dir` and `use_directory_urls`. Its signature is one
of:

    ```python
f = File.generated(config: MkDocsConfig, src_uri: str, content: str |
bytes)
f = File.generated(config: MkDocsConfig, src_uri: str, abs_src_path:
str)
    ```

This way, it is now extremely easy to add a virtual file even from a
hook:

    ```python
    def on_files(files: Files, config: MkDocsConfig):
files.append(File.generated(config, 'fake/path.md', content="Hello,
world!"))
    ```

For large content it is still best to use physical files, but one no
longer needs to manipulate the path by providing a fake unused
`docs_dir`.

- There is a new attribute
[`File.generated_by`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File.generated_by)
that arose by convention - for generated files it should be set to the
name of the plugin (the key in the `plugins:` collection) that produced
this file. This attribute is populated automatically when using the
`File.generated()` constructor.

- It is possible to set the
[`edit_uri`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File.edit_uri)
attribute of a `File`, for example from a plugin or hook, to make it
different from the default (equal to `src_uri`), and this will be
reflected in the edit link of the document. This can be useful because
some pages aren't backed by a real file and are instead created
dynamically from some other source file or script. So a hook could set
the `edit_uri` to that source file or script accordingly.

- The `File` object now stores its original `src_dir`, `dest_dir`,
`use_directory_urls` values as attributes.

- Fields of `File` are computed on demand but cached. Only the three
above attributes are primary ones, and partly also
[`dest_uri`](https://www.mkdocs.org/dev-guide/api/#mkdocs.structure.files.File.dest_uri).
This way, it is possible to, for example, overwrite `dest_uri` of a
`File`, and `abs_dest_path` will be calculated based on it. However you
need to clear the attribute first using `del f.abs_dest_path`, because
the values are cached.

- `File` instances are now hashable (can be used as keys of a `dict`).
Two files can no longer be considered "equal" unless it's the exact same
instance of `File`.

Other changes:

- The internal storage of `File` objects inside a `Files` object has
been reworked, so any plugins that choose to access `Files._files` will
get a deprecation warning.

- The order of `File` objects inside a `Files` collection is no longer
significant when automatically inferring the `nav`. They get forcibly
sorted according to the default alphabetic order.

Context: [#&#8203;3451](https://togithub.com/mkdocs/mkdocs/issues/3451),
[#&#8203;3463](https://togithub.com/mkdocs/mkdocs/issues/3463)

#### Hooks and debugging

- Hook files can now import adjacent \*.py files using the `import`
statement. Previously this was possible to achieve only through a
`sys.path` workaround. See the new mention in
[documentation](https://www.mkdocs.org/user-guide/configuration/#hooks).
Context: [#&#8203;3568](https://togithub.com/mkdocs/mkdocs/issues/3568)

- Verbose `-v` log shows the sequence of plugin events in more detail -
shows each invoked plugin one by one, not only the event type. Context:
[#&#8203;3444](https://togithub.com/mkdocs/mkdocs/issues/3444)

#### Deprecations

- Python 3.7 is no longer supported, Python 3.12 is officially
supported. Context:
[#&#8203;3429](https://togithub.com/mkdocs/mkdocs/issues/3429)

- The theme config file `mkdocs_theme.yml` no longer executes YAML tags.
Context: [#&#8203;3465](https://togithub.com/mkdocs/mkdocs/issues/3465)

- The plugin event `on_page_read_source` is soft-deprecated because
there is always a better alternative to it (see the new `File` API or
just `on_page_markdown`, depending on the desired interaction).

When multiple plugins/hooks apply this event handler, they trample over
each other, so now there is a warning in that case.

See
[**documentation**](https://www.mkdocs.org/dev-guide/plugins/#on_page_read_source).
Context: [#&#8203;3503](https://togithub.com/mkdocs/mkdocs/issues/3503)

##### API deprecations

- It is no longer allowed to set `File.page` to a type other than `Page`
or a subclass thereof. Context:
[#&#8203;3443](https://togithub.com/mkdocs/mkdocs/issues/3443) -
following the deprecation in version 1.5.3 and
[#&#8203;3381](https://togithub.com/mkdocs/mkdocs/issues/3381).

- `Theme._vars` is deprecated - use `theme['foo']` instead of
`theme._vars['foo']`

- `utils`: `modified_time()`, `get_html_path()`, `get_url_path()`,
`is_html_file()`, `is_template_file()` are removed. `path_to_url()` is
deprecated.

-   `LiveReloadServer.watch()` no longer accepts a custom callback.

Context: [#&#8203;3429](https://togithub.com/mkdocs/mkdocs/issues/3429)

#### Misc

- The `sitemap.xml.gz` file is slightly more reproducible and no longer
changes on every build, but instead only once per day (upon a date
change). Context:
[#&#8203;3460](https://togithub.com/mkdocs/mkdocs/issues/3460)

Other small improvements; see [commit
log](https://togithub.com/mkdocs/mkdocs/compare/1.5.3...1.6.0).

### [`v1.5.3`](https://togithub.com/mkdocs/mkdocs/releases/tag/1.5.3)

[Compare
Source](https://togithub.com/mkdocs/mkdocs/compare/1.5.2...1.5.3)

- Fix `mkdocs serve` sometimes locking up all browser tabs when
navigating quickly
([#&#8203;3390](https://togithub.com/mkdocs/mkdocs/issues/3390))

- Add many new supported languages for "search" plugin - update
lunr-languages to 1.12.0
([#&#8203;3334](https://togithub.com/mkdocs/mkdocs/issues/3334))

- Bugfix (regression in 1.5.0): In "readthedocs" theme the styling of
"breadcrumb navigation" was broken for nested pages
([#&#8203;3383](https://togithub.com/mkdocs/mkdocs/issues/3383))

- Built-in themes now also support Chinese (Traditional, Taiwan)
language
([#&#8203;3370](https://togithub.com/mkdocs/mkdocs/issues/3370))

- Plugins can now set `File.page` to their own subclass of `Page`. There
is also now a warning if `File.page` is set to anything other than a
strict subclass of `Page`.
([#&#8203;3367](https://togithub.com/mkdocs/mkdocs/issues/3367),
[#&#8203;3381](https://togithub.com/mkdocs/mkdocs/issues/3381))

Note that just instantiating a `Page` [sets the file
automatically](f94ab3f62d/mkdocs/structure/pages.py (L34)),
so care needs to be taken not to create an unneeded `Page`.

Other small improvements; see [commit
log](https://togithub.com/mkdocs/mkdocs/compare/1.5.2...1.5.3).

### [`v1.5.2`](https://togithub.com/mkdocs/mkdocs/releases/tag/1.5.2)

[Compare
Source](https://togithub.com/mkdocs/mkdocs/compare/1.5.1...1.5.2)

- Bugfix (regression in 1.5.0): Restore functionality of
`--no-livereload`.
([#&#8203;3320](https://togithub.com/mkdocs/mkdocs/issues/3320))

- Bugfix (regression in 1.5.0): The new page title detection would
sometimes be unable to drop anchorlinks - fix that.
([#&#8203;3325](https://togithub.com/mkdocs/mkdocs/issues/3325))

- Partly bring back pre-1.5 API: `extra_javascript` items will once
again be mostly strings, and only sometimes `ExtraStringValue` (when the
extra `script` functionality is used).

Plugins should be free to append strings to `config.extra_javascript`,
but when reading the values, they must still make sure to read it as
`str(value)` in case it is an `ExtraScriptValue` item. For querying the
attributes such as `.type` you need to check `isinstance` first. Static
type checking will guide you in that.
([#&#8203;3324](https://togithub.com/mkdocs/mkdocs/issues/3324))

See [commit
log](https://togithub.com/mkdocs/mkdocs/compare/1.5.1...1.5.2).

### [`v1.5.1`](https://togithub.com/mkdocs/mkdocs/releases/tag/1.5.1)

[Compare
Source](https://togithub.com/mkdocs/mkdocs/compare/1.5.0...1.5.1)

- Bugfix (regression in 1.5.0): Make it possible to treat
`ExtraScriptValue` as a path. This lets some plugins still work despite
the breaking change.

- Bugfix (regression in 1.5.0): Prevent errors for special setups that
have 3 conflicting files, such as `index.html`, `index.md` *and*
`README.md`
([#&#8203;3314](https://togithub.com/mkdocs/mkdocs/issues/3314))

See [commit
log](https://togithub.com/mkdocs/mkdocs/compare/1.5.0...1.5.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://www.mend.io/free-developer-tools/renovate/). View the
[repository job log](https://developer.mend.io/github/astral-sh/ruff).

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 11:49:49 +05:30
renovate[bot]
d7e9280e1e Update dependency react-resizable-panels to v2.0.23 (#12822)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[react-resizable-panels](https://togithub.com/bvaughn/react-resizable-panels)
| [`2.0.22` ->
`2.0.23`](https://renovatebot.com/diffs/npm/react-resizable-panels/2.0.22/2.0.23)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/react-resizable-panels/2.0.23?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/react-resizable-panels/2.0.23?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/react-resizable-panels/2.0.22/2.0.23?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/react-resizable-panels/2.0.22/2.0.23?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>bvaughn/react-resizable-panels
(react-resizable-panels)</summary>

###
[`v2.0.23`](https://togithub.com/bvaughn/react-resizable-panels/releases/tag/2.0.23)

[Compare
Source](ba73ac7d17...2.0.23)

- Improve obfuscation for `React.useId` references
([#&#8203;382](https://togithub.com/bvaughn/react-resizable-panels/issues/382))

</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://www.mend.io/free-developer-tools/renovate/). View the
[repository job log](https://developer.mend.io/github/astral-sh/ruff).

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 10:01:09 +05:30
renovate[bot]
f237d36d2f Update dependency black to v24.8.0 (#12827)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [black](https://togithub.com/psf/black)
([changelog](https://togithub.com/psf/black/blob/main/CHANGES.md)) |
`==24.3.0` -> `==24.8.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/black/24.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/black/24.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/black/24.3.0/24.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/black/24.3.0/24.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>psf/black (black)</summary>

###
[`v24.8.0`](https://togithub.com/psf/black/blob/HEAD/CHANGES.md#2480)

[Compare Source](https://togithub.com/psf/black/compare/24.4.2...24.8.0)

##### Stable style

- Fix crash when `# fmt: off` is used before a closing parenthesis or
bracket. ([#&#8203;4363](https://togithub.com/psf/black/issues/4363))

##### Packaging

- Packaging metadata updated: docs are explictly linked, the issue
tracker is now also
linked. This improves the PyPI listing for Black.
([#&#8203;4345](https://togithub.com/psf/black/issues/4345))

##### Parser

- Fix regression where Black failed to parse a multiline f-string
containing another
multiline string
([#&#8203;4339](https://togithub.com/psf/black/issues/4339))
- Fix regression where Black failed to parse an escaped single quote
inside an f-string
    ([#&#8203;4401](https://togithub.com/psf/black/issues/4401))
- Fix bug with Black incorrectly parsing empty lines with a backslash
([#&#8203;4343](https://togithub.com/psf/black/issues/4343))
- Fix bugs with Black's tokenizer not handling `\{` inside f-strings
very well ([#&#8203;4422](https://togithub.com/psf/black/issues/4422))
- Fix incorrect line numbers in the tokenizer for certain tokens within
f-strings
    ([#&#8203;4423](https://togithub.com/psf/black/issues/4423))

##### Performance

- Improve performance when a large directory is listed in `.gitignore`
([#&#8203;4415](https://togithub.com/psf/black/issues/4415))

##### *Blackd*

- Fix blackd (and all extras installs) for docker container
([#&#8203;4357](https://togithub.com/psf/black/issues/4357))

###
[`v24.4.2`](https://togithub.com/psf/black/blob/HEAD/CHANGES.md#2442)

[Compare Source](https://togithub.com/psf/black/compare/24.4.1...24.4.2)

This is a bugfix release to fix two regressions in the new f-string
parser introduced in
24.4.1.

##### Parser

- Fix regression where certain complex f-strings failed to parse
([#&#8203;4332](https://togithub.com/psf/black/issues/4332))

##### Performance

- Fix bad performance on certain complex string literals
([#&#8203;4331](https://togithub.com/psf/black/issues/4331))

###
[`v24.4.1`](https://togithub.com/psf/black/blob/HEAD/CHANGES.md#2441)

[Compare Source](https://togithub.com/psf/black/compare/24.4.0...24.4.1)

##### Highlights

- Add support for the new Python 3.12 f-string syntax introduced by PEP
701 ([#&#8203;3822](https://togithub.com/psf/black/issues/3822))

##### Stable style

- Fix crash involving indented dummy functions containing newlines
([#&#8203;4318](https://togithub.com/psf/black/issues/4318))

##### Parser

- Add support for type parameter defaults, a new syntactic feature added
to Python 3.13
by PEP 696 ([#&#8203;4327](https://togithub.com/psf/black/issues/4327))

##### Integrations

- Github Action now works even when `git archive` is skipped
([#&#8203;4313](https://togithub.com/psf/black/issues/4313))

###
[`v24.4.0`](https://togithub.com/psf/black/blob/HEAD/CHANGES.md#2440)

[Compare Source](https://togithub.com/psf/black/compare/24.3.0...24.4.0)

##### Stable style

- Fix unwanted crashes caused by AST equivalency check
([#&#8203;4290](https://togithub.com/psf/black/issues/4290))

##### Preview style

- `if` guards in `case` blocks are now wrapped in parentheses when the
line is too long.
    ([#&#8203;4269](https://togithub.com/psf/black/issues/4269))
- Stop moving multiline strings to a new line unless inside brackets
([#&#8203;4289](https://togithub.com/psf/black/issues/4289))

##### Integrations

- Add a new option `use_pyproject` to the GitHub Action `psf/black`.
This will read the
Black version from `pyproject.toml`.
([#&#8203;4294](https://togithub.com/psf/black/issues/4294))

</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://www.mend.io/free-developer-tools/renovate/). View the
[repository job log](https://developer.mend.io/github/astral-sh/ruff).

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 09:56:28 +05:30
renovate[bot]
12f22b1fdd Update dependency mdformat-mkdocs to v3 (#12830)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [mdformat-mkdocs](https://togithub.com/kyleking/mdformat-mkdocs)
([changelog](https://togithub.com/kyleking/mdformat-mkdocs/releases)) |
`==2.0.4` -> `==3.0.0` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/mdformat-mkdocs/3.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/mdformat-mkdocs/3.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/mdformat-mkdocs/2.0.4/3.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/mdformat-mkdocs/2.0.4/3.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>kyleking/mdformat-mkdocs (mdformat-mkdocs)</summary>

###
[`v3.0.0`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v3.0.0)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.1.1...v3.0.0)

##### What's Changed

-
refactor([#&#8203;25](https://togithub.com/kyleking/mdformat-mkdocs/issues/25)):
support anchor links as a plugin in
[https://github.com/KyleKing/mdformat-mkdocs/pull/30](https://togithub.com/KyleKing/mdformat-mkdocs/pull/30)
-
fix([#&#8203;33](https://togithub.com/kyleking/mdformat-mkdocs/issues/33)):
render anchor links above a heading without newlines in
7c1e4892f5
and
4be7ca86af
- refactor!: rename according to syntax source (e.g. `material_*`,
`mkdocs_*`, `pymd_*` (python markdown), `mkdocstrings_*`) in
d6c465aa58
- feat: render HTML for cross-references in
a967d20c49
- ci: major improvements from template
(https://github.com/KyleKing/mdformat-plugin-template)

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.1.1...v3.0.0

###
[`v2.1.1`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.1.1)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.1.0...v2.1.1)

##### What's Changed

-
fix([#&#8203;31](https://togithub.com/kyleking/mdformat-mkdocs/issues/31)):
ignore HTML within Code Blocks by
[@&#8203;KyleKing](https://togithub.com/KyleKing) in
[https://github.com/KyleKing/mdformat-mkdocs/pull/32](https://togithub.com/KyleKing/mdformat-mkdocs/pull/32)

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.1.0...v2.1.1

###
[`v2.1.0`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.1.0)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.11...v2.1.0)

##### What's Changed

-
feat([#&#8203;28](https://togithub.com/kyleking/mdformat-mkdocs/issues/28)):
support "Abbreviations" by
[@&#8203;KyleKing](https://togithub.com/KyleKing) in
[https://github.com/KyleKing/mdformat-mkdocs/pull/29](https://togithub.com/KyleKing/mdformat-mkdocs/pull/29)

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.11...v2.1.0

###
[`v2.0.11`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.11)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.10...v2.0.11)

##### Changes

-
fix([#&#8203;25](https://togithub.com/kyleking/mdformat-mkdocs/issues/25)):
add support for "[markdown
anchors](https://mkdocstrings.github.io/autorefs/#markdown-anchors)"
syntax from the `mkdocs`
[autorefs](https://mkdocstrings.github.io/autorefs) plugin

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.10...v2.0.11

###
[`v2.0.10`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.10)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.9...v2.0.10)

Changes:

-
fix([#&#8203;24](https://togithub.com/kyleking/mdformat-mkdocs/issues/24)):
respect ordered lists that start with `0.`
([#&#8203;26](https://togithub.com/kyleking/mdformat-mkdocs/issues/26))

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.9...v2.0.10

###
[`v2.0.9`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.9)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.8...v2.0.9)

Changelog:

-
fix([#&#8203;23](https://togithub.com/kyleking/mdformat-mkdocs/issues/23)):
ignore empty newlines when in fenced code blocks

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.8...v2.0.9

###
[`v2.0.8`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.8)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.7...v2.0.8)

Changelog:

-
Fix([#&#8203;21](https://togithub.com/kyleking/mdformat-mkdocs/issues/21)):
ignore lists in fenced code

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.7...v2.0.8

###
[`v2.0.7`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.7)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.6...v2.0.7)

Changelog:

-
Fix([#&#8203;20](https://togithub.com/kyleking/mdformat-mkdocs/issues/20)):
01a6916f41

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.6...v2.0.7

###
[`v2.0.6`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.6)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.5...v2.0.6)

##### Changelog

- Resolve typo in CLI for
[#&#8203;19](https://togithub.com/kyleking/mdformat-mkdocs/issues/19)
(3dc80a03f4)
- Make `mdformat-wikilink` optional thanks to a quick release
([https://github.com/tmr232/mdformat-wikilink/issues/6](https://togithub.com/tmr232/mdformat-wikilink/issues/6))!

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.5...v2.0.6

###
[`v2.0.5`](https://togithub.com/KyleKing/mdformat-mkdocs/releases/tag/v2.0.5)

[Compare
Source](https://togithub.com/kyleking/mdformat-mkdocs/compare/v2.0.4...v2.0.5)

Changelog:

- Resolves
[#&#8203;19](https://togithub.com/kyleking/mdformat-mkdocs/issues/19).
Add `--ignore-missing-references` to prevent escaping brackets for
compatibility with python mkdocstrings
- feat: back-port `mdformat-wikilink` to Python 3.8 by default (see:
[https://github.com/tmr232/mdformat-wikilink/issues/6](https://togithub.com/tmr232/mdformat-wikilink/issues/6))

**Full Changelog**:
https://github.com/KyleKing/mdformat-mkdocs/compare/v2.0.5...v2.0.5

</details>

---

### Configuration

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

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

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

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

---

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

---

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 09:54:29 +05:30
renovate[bot]
47d05ee9ea Update pre-commit hook astral-sh/ruff-pre-commit to v0.5.7 (#12824) 2024-08-11 22:28:04 -04:00
renovate[bot]
9caec36b59 Update Rust crate tempfile to v3.12.0 (#12826) 2024-08-11 22:27:44 -04:00
renovate[bot]
cb364780b3 Update dependency mdformat-admon to v2.0.6 (#12821) 2024-08-12 01:59:14 +00:00
renovate[bot]
71b8bf211f Update Rust crate ureq to v2.10.1 (#12819) 2024-08-12 00:21:01 +00:00
renovate[bot]
109b9cc4f9 Update Rust crate syn to v2.0.74 (#12818) 2024-08-12 00:20:55 +00:00
renovate[bot]
5d02627794 Update Rust crate serde_test to v1.0.177 (#12817) 2024-08-12 00:19:58 +00:00
renovate[bot]
65444bb00e Update Rust crate filetime to v0.2.24 (#12813) 2024-08-11 20:19:32 -04:00
renovate[bot]
8822a79b4d Update dependency PyYAML to v6.0.2 (#12820) 2024-08-11 20:19:14 -04:00
renovate[bot]
2df4d23113 Update Rust crate serde_json to v1.0.124 (#12816) 2024-08-11 20:18:45 -04:00
renovate[bot]
603b62607a Update Rust crate serde to v1.0.206 (#12815) 2024-08-11 20:18:39 -04:00
renovate[bot]
2b71fc4510 Update Rust crate is-macro to v0.3.6 (#12814) 2024-08-11 20:18:33 -04:00
renovate[bot]
1b78d872ec Update Rust crate clap to v4.5.15 (#12812) 2024-08-11 20:18:25 -04:00
Yury Fedotov
feba5031dc [Minor typo] Fix article in "an fix" (#12797) 2024-08-10 21:22:00 -04:00
Dylan
0c2b88f224 [flake8-simplify] Further simplify to binary in preview for if-else-block-instead-of-if-exp (SIM108) (#12796)
In most cases we should suggest a ternary operator, but there are three
edge cases where a binary operator is more appropriate.

Given an if-else block of the form

```python
if test:
    target_var = body_value
else:
    target_var = else_value
```
This PR updates the check for SIM108 to the following:

- If `test == body_value` and preview enabled, suggest to replace with
`target_var = test or else_value`
- If `test == not body_value` and preview enabled, suggest to replace
with `target_var = body_value and else_value`
- If `not test == body_value` and preview enabled, suggest to replace
with `target_var = body_value and else_value`
- Otherwise, suggest to replace with `target_var = body_value if test
else else_value`

Closes #12189.
2024-08-10 16:49:25 +00:00
Alex Waygood
cf1a57df5a Remove red_knot_python_semantic::python_version::TargetVersion (#12790) 2024-08-10 14:28:31 +01:00
renovate[bot]
597c5f9124 Update dependency black to v24 (#12728) 2024-08-10 18:04:37 +05:30
Charlie Marsh
69e1c567d4 Treat type(Protocol) et al as metaclass base (#12770)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12736.
2024-08-09 20:10:12 +00:00
Alex Waygood
37b9bac403 [red-knot] Add support for --system-site-packages virtual environments (#12759) 2024-08-09 21:02:16 +01:00
Alex Waygood
83db48d316 RUF031: Ignore unparenthesized tuples in subscripts when the subscript is obviously a type annotation or type alias (#12762) 2024-08-09 20:31:27 +01:00
Alex Waygood
c4e651921b [red-knot] Move, rename and make public the PyVersion type (#12782) 2024-08-09 16:49:17 +01:00
Dylan
b595346213 [ruff] Do not remove parens for tuples with starred expressions in Python <=3.10 RUF031 (#12784) 2024-08-09 17:30:29 +02:00
Ryan Hoban
253474b312 Document that BLE001 supports both BaseException and Exception (#12788) 2024-08-09 17:28:50 +02:00
Micha Reiser
a176679b24 Log warnings when skipping editable installations (#12779) 2024-08-09 16:29:43 +02:00
Charlie Marsh
1f51048fa4 Don't enforce returns and yields in abstract methods (#12771)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12685.
2024-08-09 13:34:14 +00:00
Micha Reiser
2abfab0f9b Move Program and related structs to red_knot_python_semantic (#12777) 2024-08-09 11:50:45 +02:00
Dylan
64f1f3468d [ruff] Skip tuples with slice expressions in incorrectly-parenthesized-tuple-in-subscript (RUF031) (#12768)
## Summary

Adding parentheses to a tuple in a subscript with elements that include
slice expressions causes a syntax error. For example, `d[(1,2,:)]` is a
syntax error.

So, when `lint.ruff.parenthesize-tuple-in-subscript = true` and the
tuple includes a slice expression, we skip this check and fix.

Closes #12766.
2024-08-09 09:22:58 +00:00
Micha Reiser
ffaa35eafe Add test helper to setup tracing (#12741) 2024-08-09 07:04:04 +00:00
Charlie Marsh
c906b0183b Add known problems warning to type-comparison rule (#12769)
## Summary

See: https://github.com/astral-sh/ruff/issues/4560
2024-08-09 01:41:15 +00:00
Carl Meyer
bc5b9b81dd [red-knot] add dev dependency on ruff_db os feature from red_knot_pyt… (#12760) 2024-08-08 18:10:30 +01:00
Dhruv Manilawala
221ea662e0 Bump version to 0.5.7 (#12756) 2024-08-08 20:56:15 +05:30
Alex Waygood
d28c5afd14 [red-knot] Remove mentions of Ruff from the CLI help (#12752)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-08-08 15:35:10 +01:00
Alex Waygood
f1de08c2a0 [red-knot] Merge the semantic and module-resolver crates (#12751) 2024-08-08 15:34:11 +01:00
Christian Clauss
33e9a6a54e SIM110: any() is ~3x slower than the code it replaces (#12746)
> ~Builtins are also more efficient than `for` loops.~

Let's not promise performance because this code transformation does not
deliver.

Benchmark written by @dcbaker

> `any()` seems to be about 1/3 as fast (Python 3.11.9, NixOS):
```python
loop = 'abcdef'.split()
found = 'f'
nfound = 'g'


def test1():
    for x in loop:
        if x == found:
            return True
    return False


def test2():
    return any(x == found for x in loop)


def test3():
    for x in loop:
        if x == nfound:
            return True
    return False


def test4():
    return any(x == nfound for x in loop)


if __name__ == "__main__":
    import timeit

    print('for loop (found)    :', timeit.timeit(test1))
    print('for loop (not found):', timeit.timeit(test3))
    print('any() (found)       :', timeit.timeit(test2))
    print('any() (not found)   :', timeit.timeit(test4))
```
```
for loop (found)    : 0.051076093994197436
for loop (not found): 0.04388196699437685
any() (found)       : 0.15422860698890872
any() (not found)   : 0.15568504799739458
```
I have retested with longer lists and on multiple Python versions with
similar results.
2024-08-08 08:25:43 -04:00
Dylan
f577e03021 [ruff] Ignore empty tuples for incorrectly-parenthesized-tuple-in-subscript (RUF031) (#12749) 2024-08-08 13:18:03 +02:00
Micha Reiser
f53733525c Remove all useEffect usages (#12659) 2024-08-08 13:16:38 +02:00
Micha Reiser
2daa914334 Gracefully handle errors in CLI (#12747) 2024-08-08 11:02:47 +00:00
Steve C
6d9205e346 [ruff_linter] - Use LibCST in adjust_indentation for mixed whitespace (#12740) 2024-08-08 10:49:58 +02:00
Micha Reiser
df7345e118 Exit with an error if there are check failures (#12735) 2024-08-08 07:10:18 +00:00
Micha Reiser
dc6aafecc2 Setup tracing and document tracing usage (#12730) 2024-08-08 06:28:40 +00:00
Charlie Marsh
5107a50ae7 Parenthesize conditions based on precedence when merging if arms (#12737)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12732.
2024-08-07 23:03:24 -04:00
Micha Reiser
a631d600ac Fix cache invalidation for nested pyproject.toml files (#12727) 2024-08-07 21:53:45 +02:00
Alex Waygood
f34b9a77f0 [red-knot] Cleanups to logic resolving site-packages from a venv path (#12731) 2024-08-07 15:48:15 +01:00
Dylan
7997da47f5 [ruff] Implement incorrectly-parenthesized-tuple-in-subscript (RUF031) (#12480)
Implements the new fixable lint rule `RUF031` which checks for the use or omission of parentheses around tuples in subscripts, depending on the setting `lint.ruff.parenthesize-tuple-in-getitem`. By default, the use of parentheses is considered a violation.
2024-08-07 13:11:29 +00:00
Alex Waygood
d380b37a09 Add a new Binding::is_unused method (#12729) 2024-08-07 11:17:56 +01:00
Alex Waygood
b14fee9320 [ruff] Mark RUF023 fix as unsafe if __slots__ is not a set and the binding is used elsewhere (#12692) 2024-08-07 10:41:03 +01:00
Dhruv Manilawala
037e817450 Use struct instead of type alias for workspace settings index (#12726)
## Summary

Follow-up from https://github.com/astral-sh/ruff/pull/12725, this is
just a small refactor to use a wrapper struct instead of type alias for
workspace settings index. This avoids the need to have the
`register_workspace_settings` as a static method on `Index` and instead
is a method on the new struct itself.
2024-08-07 09:26:59 +00:00
Dhruv Manilawala
7fcfedd430 Ignore non-file workspace URL (#12725)
## Summary

This PR updates the server to ignore non-file workspace URL.

This is to avoid crashing the server if the URL scheme is not "file".
We'd still raise an error if the URL to file path conversion fails.

Also, as per the docs of
[`to_file_path`](https://docs.rs/url/2.5.2/url/struct.Url.html#method.to_file_path):

> Note: This does not actually check the URL’s scheme, and may give
nonsensical results for other schemes. It is the user’s responsibility
to check the URL’s scheme before calling this.

resolves: #12660

## Test Plan

I'm not sure how to test this locally but the change is small enough to
validate on its own.
2024-08-07 09:15:55 +00:00
Dhruv Manilawala
50ff5c7544 Include docs requirements for Renovate upgrades (#12724)
## Summary

This PR updates the Renovate config to account for the
`requirements*.txt` files in `docs/` directory.

The `mkdocs-material` upgrade is ignored because we use commit SHA for
the insider version and it should match the corresponding public version
as per the docs:
https://squidfunk.github.io/mkdocs-material/insiders/upgrade/
(`9.x.x-insiders-4.x.x`).

## Test Plan

```console
❯ renovate-config-validator
(node:83193) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
 INFO: Validating .github/renovate.json5
 INFO: Config validated successfully
```
2024-08-07 13:11:18 +05:30
Charlie Marsh
90e5bc2bd9 Avoid false-positives for list concatenations in SQL construction (#12720)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12710.
2024-08-06 16:26:03 -04:00
Alex Waygood
aae9619d3d [red-knot] Fix build on Windows (#12719)
## Summary

Tests are failing on `main` because automerge landed
https://github.com/astral-sh/ruff/pull/12716 despite the Windows tests
failing.
2024-08-06 20:21:25 +01:00
Alex Waygood
7fa76a2b2b [red-knot] Derive site-packages from a venv path (#12716) 2024-08-06 18:34:37 +00:00
Dhruv Manilawala
14dd6d980e [red-knot] Keep subcommands optional for the binary (#12715)
## Summary

This PR updates the `red_knot` CLI to make the subcommand optional.

## Test Plan

Run the following commands:
* `cargo run --bin red_knot --
--current-directory=~/playground/ruff/type_inference` (no subcommand
requirement)
* `cargo run --bin red_knot -- server` (should start the server)
2024-08-06 20:24:49 +05:30
Micha Reiser
846f57fd15 Update salsa (#12711) 2024-08-06 13:17:39 +00:00
Micha Reiser
8e6aa78796 Remove 'cli' module from red_knot (#12714) 2024-08-06 12:10:36 +00:00
Dhruv Manilawala
e91a0fe94a [red-knot] Implement basic LSP server (#12624)
## Summary

This PR adds basic LSP implementation for the Red Knot project.

This is basically a fork of the existing `ruff_server` crate into a
`red_knot_server` crate. The following are the main differences:
1. The `Session` stores a map from workspace root to the corresponding
Red Knot database (`RootDatabase`).
2. The database is initialized with the newly implemented `LSPSystem`
(implementation of `System` trait)
3. The `LSPSystem` contains the server index corresponding to each
workspace and an underlying OS system implementation. For certain
methods, the system first checks if there's an open document in LSP
system and returns the information from that. Otherwise, it falls back
to the OS system to get that information. These methods are
`path_metadata`, `read_to_string` and `read_to_notebook`
4. Add `as_any_mut` method for `System`

**Why fork?**

Forking allows us to experiment with the functionalities that are
specific to Red Knot. The architecture is completely different and so
the requirements for an LSP implementation are different as well. For
example, Red Knot only supports a single workspace, so the LSP system
needs to map the multi-workspace support to each Red Knot instance. In
the end, the server code isn't too big, it will be easier to implement
Red Knot specific functionality without worrying about existing server
limitations and it shouldn't be difficult to port the existing server.

## Review

Most of the server files hasn't been changed. I'm going to list down the
files that have been changed along with highlight the specific part of
the file that's changed from the existing server code.

Changed files:
* Red Knot CLI implementation:
https://github.com/astral-sh/ruff/pull/12624/files#diff-579596339a29d3212a641232e674778c339b446de33b890c7fdad905b5eb50e1
* In
https://github.com/astral-sh/ruff/pull/12624/files#diff-b9a9041a8a2bace014bf3687c3ef0512f25e0541f112fad6131b14242f408db6,
server capabilities have been updated, dynamic capability registration
is removed
* In
https://github.com/astral-sh/ruff/pull/12624/files#diff-b9a9041a8a2bace014bf3687c3ef0512f25e0541f112fad6131b14242f408db6,
the API for `clear_diagnostics` now take in a `Url` instead of
`DocumentQuery` as the document version doesn't matter when clearing
diagnostics after a document is closed
*
[`did_close`](https://github.com/astral-sh/ruff/pull/12624/files#diff-9271370102a6f3be8defaca40c82485b0048731942520b491a3bdd2ee0e25493),
[`did_close_notebook`](https://github.com/astral-sh/ruff/pull/12624/files#diff-96fb53ffb12c1694356e17313e4bb37b3f0931e887878b5d7c896c19ff60283b),
[`did_open`](https://github.com/astral-sh/ruff/pull/12624/files#diff-60e852cf1aa771e993131cabf98eb4c467963a8328f10eccdb43b3e8f0f1fb12),
[`did_open_notebook`](https://github.com/astral-sh/ruff/pull/12624/files#diff-ac356eb5e36c3b2c1c135eda9dfbcab5c12574d1cb77c71f7da8dbcfcfb2d2f1)
are updated to open / close file from the corresponding Red Knot
workspace
* The [diagnostic
handler](https://github.com/astral-sh/ruff/pull/12624/files#diff-4475f318fd0290d0292834569a7df5699debdcc0a453b411b8c3d329f1b879d9)
is updated to request diagnostics from Red Knot
* The [`Session::new`] method in
https://github.com/astral-sh/ruff/pull/12624/files#diff-55c96201296200c1cab37c8b0407b6c733381374b94be7ae50563bfe95264e4d
is updated to construct the Red Knot databases for each workspace. It
also contains the `index_mut` and `MutIndexGuard` implementation
* And, `LSPSystem` implementation is in
https://github.com/astral-sh/ruff/pull/12624/files#diff-4ed62bd359c43b0bf1a13f04349dcd954966934bb8d544de7813f974182b489e

## Test Plan

First, configure VS Code to use the `red_knot` binary

1. Build the `red_knot` binary by `cargo build`
2. Update the VS Code extension to specify the path to this binary
```json
{
	"ruff.path": ["/path/to/ruff/target/debug/red_knot"]
}
```
3. Restart VS Code

Now, open a file containing red-knot specific diagnostics, close the
file and validate that diagnostics disappear.
2024-08-06 11:27:30 +00:00
Micha Reiser
d2c627efb3 Use standard allocator for wasm (#12713) 2024-08-06 11:20:47 +00:00
Micha Reiser
10e977d5f5 [red-knot] Add basic WASM API (#12654) 2024-08-06 09:21:42 +02:00
Auguste Lalande
f0318ff889 [pydoclint] Consider DOC201 satisfied if docstring begins with "Returns" (#12675)
<!--
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

Resolves #12636

Consider docstrings which begin with the word "Returns" as having
satisfactorily documented they're returns. For example
```python
def f():
    """Returns 1."""
    return 1
```
is valid.

## Test Plan

Added example to test fixture.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-08-06 06:46:38 +00:00
Dhruv Manilawala
5cc3fed9a8 [red-knot] Infer float and complex literal expressions (#12689)
## Summary

This PR implements type inference for float and complex literal
expressions.

## Test Plan

Add test cases for both types.
2024-08-06 06:24:28 +00:00
Steve C
39dd732e27 [refurb] - fix unused autofix for implicit-cwd (FURB177) (#12708) 2024-08-06 08:09:35 +02:00
Dylan
52630a1d55 [flake8-comprehensions] Set comprehensions not a violation for sum in unnecessary-comprehension-in-call (C419) (#12691)
## Summary

Removes set comprehension as a violation for `sum` when checking `C419`,
because set comprehension may de-duplicate entries in a generator,
thereby modifying the value of the sum.

Closes #12690.
2024-08-06 02:30:58 +00:00
Steve C
7b5fd63ce8 [flake8-pyi] - add autofix for future-annotations-in-stub (PYI044) (#12676)
## Summary

add autofix for `PYI044`

## Test Plan

`cargo test`
2024-08-05 22:27:55 -04:00
Alex Waygood
5499821c67 [red-knot] Rename workspace_root variables in the module resolver to src_root (#12697)
Fixes #12337
2024-08-05 23:07:18 +01:00
Alex Waygood
7ee7c68f36 Add a new script to generate builtin module names (#12696) 2024-08-05 21:33:36 +01:00
Carl Meyer
2393d19f91 [red-knot] infer instance types for builtins (#12695)
Previously we wrongly inferred the type of the builtin type itself (e.g.
`Literal[int]`); we need to infer the instance type instead.
2024-08-05 13:32:42 -07:00
Dhruv Manilawala
a8e2ba508e [red-knot] Infer boolean literal expression (#12688)
## Summary

This PR implements type inference for boolean literal expressions.

## Test Plan

Add test cases for `True` and `False`.
2024-08-05 11:30:53 -07:00
Alex Waygood
0b4d3ce39b TRY002: fixup docs (#12683) 2024-08-05 08:56:12 +00:00
epenet
0a345dc627 [tryceratops] Add BaseException to raise-vanilla-class rule (TRY002) (#12620) 2024-08-05 09:45:49 +01:00
Micha Reiser
ff2aa3ea00 Revert "Remove criterion/codspeed compat layer (#12524)" (#12680) 2024-08-05 07:49:04 +00:00
Micha Reiser
0d3bad877d Fix module resolver symlink test on macOs (#12682) 2024-08-05 07:22:54 +00:00
Micha Reiser
756060d676 Upgrade Salsa to a version with a 32bit compatible concurrent vec (#12679) 2024-08-05 08:50:32 +02:00
Micha Reiser
b647f3fba8 Disable testing ruff_benchmark by default (#12678) 2024-08-05 06:15:52 +00:00
Dhruv Manilawala
82e69ebf23 Update broken links in the documentation (#12677)
## Summary

Running `mkdocs server -f mkdocs.insiders.yml` gave warnings about these
broken links.

## Test plan

I built the docs locally and verified that the updated links work
properly.
2024-08-05 05:35:23 +00:00
renovate[bot]
2c79045342 Update Rust crate pep440_rs to v0.6.6 (#12666) 2024-08-04 22:42:43 -04:00
Charlie Marsh
3497f5257b Add preview note to unnecessary-comprehension-in-call (#12673) 2024-08-05 02:27:00 +00:00
Dylan
25aabec814 [flake8-comprehensions] Account for list and set comprehensions in unnecessary-literal-within-tuple-call (C409) (#12657)
## Summary

Make it a violation of `C409` to call `tuple` with a list or set
comprehension, and
implement the (unsafe) fix of calling the `tuple` with the underlying
generator instead.

Closes #12648.

## Test Plan

Test fixture updated, cargo test, docs checked for updated description.
2024-08-04 22:14:52 -04:00
renovate[bot]
0e71485ea9 Update Rust crate regex to v1.10.6 (#12667) 2024-08-04 22:10:40 -04:00
renovate[bot]
43a9d282f7 Update Rust crate ordermap to v0.5.1 (#12665) 2024-08-04 22:10:32 -04:00
renovate[bot]
6f357b8b45 Update Rust crate tempfile to v3.11.0 (#12671) 2024-08-05 02:08:20 +00:00
renovate[bot]
73d9f11a9c Update pre-commit dependencies (#12670) 2024-08-05 02:08:07 +00:00
renovate[bot]
d6c6db5a44 Update NPM Development dependencies (#12672) 2024-08-04 22:07:55 -04:00
renovate[bot]
56d985a972 Update Rust crate toml to v0.8.19 (#12669) 2024-08-04 22:07:44 -04:00
renovate[bot]
b3e0655cc9 Update Rust crate serde_json to v1.0.122 (#12668) 2024-08-04 22:07:35 -04:00
renovate[bot]
06baffec9e Update Rust crate clap to v4.5.13 (#12664) 2024-08-04 22:07:26 -04:00
Steve C
67a2ae800a [ruff] - add autofix zip-instead-of-pairwise (RUF007) (#12663)
## Summary

Adds autofix for `RUF007`

## Test Plan

`cargo test`, however I get errors for `test resolver::tests::symlink
... FAILED` which seems to not be my fault
2024-08-04 21:57:50 -04:00
InSync
7a2c75e2fc Replace ruff-lsp links in README.md with links to new documentation page (#12618)
Since `ruff-lsp` has been (semi-)deprecated for sometime, it wouldn't
make sense to mention it in the most prominent sections of the `README`.
Instead, they should point to the new <i>[Editor
Integrations](https://docs.astral.sh/ruff/editors/)</i> documentation
page.
2024-08-04 15:31:36 +05:30
DavideRagazzon
9ee44637ca Fix typo in configuration docs (#12655) 2024-08-04 09:43:51 +02:00
Charlie Marsh
733341ab39 Ignore DOC errors for stub functions (#12651)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12650.
2024-08-03 08:13:21 -04:00
Micha Reiser
341a25eec1 Fix file watching on macOS if a module-search path is a symlink (#12634) 2024-08-03 07:24:07 +00:00
Charlie Marsh
38e178e914 Try both 'Raises' section styles when convention is unspecified (#12649)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12647.
2024-08-02 21:04:46 -04:00
Alex Waygood
daccb3f4f3 [pydoclint] Deduplicate collected exceptions after traversing function bodies (#12642) 2024-08-02 23:17:06 +01:00
Charlie Marsh
c858afe03a [flake8-bugbear] Treat return as equivalent to break (B909) (#12646)
Closes https://github.com/astral-sh/ruff/issues/12640.
2024-08-02 18:14:17 -04:00
Alex Waygood
3c1c3199d0 [pydoclint] Teach rules to understand reraised exceptions as being explicitly raised (#12639)
## Summary

Fixes #12630.

DOC501 and DOC502 now understand functions with constructs like this to
be explicitly raising `TypeError` (which should be documented in a
function's docstring):

```py
try:
    foo():
except TypeError:
    ...
    raise
```

I made an exception for `Exception` and `BaseException`, however.
Constructs like this are reasonably common, and I don't think anybody
would say that it's worth putting in the docstring that it raises "some
kind of generic exception":

```py
try:
    foo()
except BaseException:
    do_some_logging()
    raise
```

## Test Plan

`cargo test -p ruff_linter --lib`
2024-08-02 22:47:22 +01:00
Ran Benita
fbfe2cb2f5 [flake8-async] Fix false positives with multiple async with items (ASYNC100) (#12643)
## Summary

Please see
https://github.com/astral-sh/ruff/pull/12605#discussion_r1699957443 for
a description of the issue.

They way I fixed it is to get the *last* timeout item in the `with`, and
if it's an `async with` and there are items after it, then don't trigger
the lint.

## Test Plan

Updated the fixture with some more cases.
2024-08-02 21:25:13 +00:00
Carl Meyer
1c311e4fdb [red-knot] update benchmark to run on tomllib (#12635)
Changes the red-knot benchmark to run on the stdlib "tomllib" library
(which is self-contained, four files, uses type annotations) instead of
on very small bits of handwritten code.

Also remove the `without_parse` benchmark: now that we are running on
real code that uses typeshed, we'd either have to pre-parse all of
typeshed (slow) or find some way to determine which typeshed modules
will be used by the benchmark (not feasible with reasonable complexity.)

## Test Plan

`cargo bench -p ruff_benchmark --bench red_knot`
2024-08-02 11:23:52 -07:00
Micha Reiser
12177a42e3 Set durabilities for low-durability fields on high-durability inputs (#12627) 2024-08-02 19:42:34 +02:00
Micha Reiser
dfb08856eb Fix file watcher stop data race (#12626) 2024-08-02 19:02:49 +02:00
Auguste Lalande
94d817e1a5 [pydoclint] Add docstring-missing-yields amd docstring-extraneous-yields (DOC402, DOC403) (#12538) 2024-08-02 17:55:42 +01:00
ember91
9296bd4e3f Fix a typo (#12633)
Co-authored-by: Emil Berg <emil.berg@ericsson.com>
2024-08-02 16:39:27 +01:00
Micha Reiser
da824ba316 Release Ruff 0.5.6 (#12629)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-02 17:35:14 +02:00
Micha Reiser
012198a1b0 Enable notebooks by default in preview mode (#12621) 2024-08-02 13:36:53 +00:00
Alex Waygood
fbab04fbe1 [red-knot] Allow multiple site-packages search paths (#12609) 2024-08-02 13:33:19 +00:00
Dhruv Manilawala
9aa43d5f91 Separate red_knot into CLI and red_knot_workspace crates (#12623)
## Summary

This PR separates the current `red_knot` crate into two crates:
1. `red_knot` - This will be similar to the `ruff` crate, it'll act as
the CLI crate
2. `red_knot_workspace` - This includes everything except for the CLI
functionality from the existing `red_knot` crate

Note that the code related to the file watcher is in
`red_knot_workspace` for now but might be required to extract it out in
the future.

The main motivation for this change is so that we can have a `red_knot
server` command. This makes it easier to test the server out without
making any changes in the VS Code extension. All we need is to specify
the `red_knot` executable path in `ruff.path` extension setting.

## Test Plan

- `cargo build`
- `cargo clippy --workspace --all-targets --all-features`
- `cargo shear --fix`
2024-08-02 11:24:36 +00:00
Micha Reiser
966563c79b Add tests for hard and soft links (#12590) 2024-08-02 10:14:28 +00:00
Micha Reiser
27edadec29 Make server panic hook more error resilient (#12610) 2024-08-02 12:10:06 +02:00
InSync
2e2b1b460f Fix a typo in docs/editors/settings.md (#12614)
Diff:

```diff
-- `false: Same as`off\`
+- `false`: Same as `off`
```
2024-08-01 11:23:55 -05:00
Charlie Marsh
a3e67abf4c Add newlines before comments in E305 (#12606)
## Summary

There's still a problem here. Given:

```python
class Class():
    pass

    # comment

    # another comment
a = 1
```

We only add one newline before `a = 1` on the first pass, because
`max_precedling_blank_lines` is 1... We then add the second newline on
the second pass, so it ends up in the right state, but the logic is
clearly wonky.

Closes https://github.com/astral-sh/ruff/issues/11508.
2024-07-31 23:11:00 -04:00
Carl Meyer
ee0518e8f7 [red-knot] implement attribute of union (#12601)
I hit this `todo!` trying to run type inference over some real modules.
Since it's a one-liner to implement it, I just did that rather than
changing to `Type::Unknown`.
2024-07-31 19:45:24 -07:00
Charlie Marsh
d774a3bd48 Avoid unused async when context manager includes TaskGroup (#12605)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12354.
2024-08-01 02:12:43 +00:00
Charlie Marsh
7e6b19048e Don't attach comments with mismatched indents (#12604)
## Summary

Given:

```python
def test_update():
    pass
    # comment
def test_clientmodel():
    pass
```

We don't want `# comment` to be attached to `def test_clientmodel()`.

Closes https://github.com/astral-sh/ruff/issues/12589.
2024-07-31 22:09:05 -04:00
Charlie Marsh
8e383b9587 Respect start index in unnecessary-list-index-lookup (#12603)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12594.
2024-08-01 01:21:15 +00:00
github-actions[bot]
3f49ab126f Sync vendored typeshed stubs (#12602) 2024-08-01 01:44:56 +01:00
Chris Krycho
c1bc7f4dee Remove ecosystem_ci flag from Ruff CLI (#12596)
## Summary

@zanieb noticed while we were discussing #12595 that this flag is now
unnecessary, so remove it and the flags which reference it.

## Test Plan

Question for maintainers: is there a test to add *or* remove here? (I’ve
opened this as a draft PR with that in view!)
2024-07-31 11:40:03 -05:00
Bowen Liang
a44d579f21 Add Dify to Ruff users (#12593)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
- Add the popular LLM Ops project Dify to the user list in Readme, as
Dify introduced Ruff for lining since Feb 2024 in
https://github.com/langgenius/dify/pull/2366
2024-07-31 08:56:52 -04:00
Alex Waygood
a3900d2b0b [pyflakes] Fix preview-mode bugs in F401 when attempting to autofix unused first-party submodule imports in an __init__.py file (#12569) 2024-07-31 13:34:30 +01:00
Alex Waygood
83b1c48a93 Make setting and retrieving pydocstyle settings less tedious (#12582) 2024-07-31 10:39:33 +01:00
Micha Reiser
138e70bd5c Upgrade to Rust 1.80 (#12586) 2024-07-30 19:18:08 +00:00
Eero Vaher
ee103ffb25 Fix an argument name in B905 description (#12588)
The description of `zip-without-explicit-strict` erroneously mentions a
non-existing `check` argument for `zip()`.
2024-07-30 14:40:56 -04:00
Micha Reiser
18f87b9497 Flaky file watching tests, add debug assertions (#12587) 2024-07-30 18:09:55 +00:00
Micha Reiser
adc8d4e1e7 File watch events: Add dynamic wait period before writing new changes (#12585) 2024-07-30 19:18:43 +02:00
Alex Waygood
90db361199 Consider more stdlib decorators to be property-like (#12583) 2024-07-30 17:18:23 +00:00
Alex Waygood
4738135801 Improve consistency between linter rules in determining whether a function is property (#12581) 2024-07-30 17:42:04 +01:00
Micha Reiser
264cd750e9 Add delay between updating a file (#12576) 2024-07-30 18:31:29 +02:00
Alex Waygood
7a4419a2a5 Improve handling of metaclasses in various linter rules (#12579) 2024-07-30 14:48:36 +01:00
Alex Waygood
ac1666d6e2 Remove several incorrect uses of map_callable() (#12580) 2024-07-30 14:30:25 +01:00
epenet
459c85ba27 [flake8-return] Exempt cached properties and other property-like decorators from explicit return rule (RET501) (#12563)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-07-30 11:06:28 +00:00
Alex Waygood
aaa56eb0bd Fix NFKC normalization bug when removing unused imports (#12571) 2024-07-30 09:54:35 +00:00
Dhruv Manilawala
f3c14a4276 Keep track of deleted cell for reorder change request (#12575)
## Summary

This PR fixes a bug where the server wouldn't retain the cell content in
case of a reorder change request.

As mentioned in
https://github.com/astral-sh/ruff/issues/12573#issuecomment-2257819298,
this change request is modeled as (a) remove these cell URIs and (b) add
these cell URIs. The cell content isn't provided. But, the way we've
modeled the `NotebookCell` (it contains the underlying `TextDocument`),
we need to keep track of the deleted cells to get the content.

This is not an ideal solution and a better long term solution would be
to model it as per the spec but that is a big structural change and will
affect multiple parts of the server. Modeling as per the spec would also
avoid bugs like https://github.com/astral-sh/ruff/pull/11864. For
context, that model would add complexity per
https://github.com/astral-sh/ruff/pull/11206#discussion_r1600165481.

fixes: #12573

## Test Plan

This video shows the before and after the bug is fixed:


https://github.com/user-attachments/assets/2fcad4b5-f9af-4776-8640-4cd1fa16e325
2024-07-30 09:51:26 +00:00
Alex Waygood
3169d408fa [red-knot] Fix typos in the module resolver (#12574) 2024-07-30 09:38:38 +00:00
Micha Reiser
a2286c8e47 Set Durability to 'HIGH' for most inputs and third-party libraries (#12566) 2024-07-30 09:03:59 +00:00
Piotr Osiewicz
fb9f566f56 Use $/logTrace for server trace logs in Zed and VS Code (#12564)
## Summary

This pull request adds support for logging via `$/logTrace` RPC
messages. It also enables that code path for when a client is Zed editor
or VS Code (as there's no way for us to generically tell whether a client prefers
`$/logTrace` over stderr.

Related to: #12523

## Test Plan

I've built Ruff from this branch and tested it manually with Zed.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-07-30 08:32:20 +05:30
Micha Reiser
381bd1ff4a Delete left over debug statement (#12567) 2024-07-29 16:16:12 +02:00
Micha Reiser
2f54d05d97 Remove salsa::report_untracked_read when finding the dynamic module resolution paths (#12509) 2024-07-29 09:31:29 +00:00
Micha Reiser
e18b4e42d3 [red-knot] Upgrade to the *new* *new* salsa (#12406) 2024-07-29 07:21:24 +00:00
Dhruv Manilawala
9495331a5f Recommend client config for trace setting in Neovim (#12562) 2024-07-29 06:14:34 +00:00
renovate[bot]
e1076db7d0 Update CodSpeedHQ/action action to v3 (#12559) 2024-07-29 07:37:02 +02:00
renovate[bot]
1986c9e8e2 Update NPM Development dependencies (#12556) 2024-07-28 22:17:44 -04:00
renovate[bot]
d7e80dc955 Update pre-commit dependencies (#12555) 2024-07-28 22:17:34 -04:00
renovate[bot]
87d09f77cd Update Rust crate imperative to v1.0.6 (#12552) 2024-07-28 22:17:28 -04:00
renovate[bot]
bd37ef13b8 Update Rust crate bstr to v1.10.0 (#12557) 2024-07-28 22:17:11 -04:00
renovate[bot]
ec23c974db Update Rust crate toml to v0.8.16 (#12554) 2024-07-28 22:17:01 -04:00
renovate[bot]
122e5ab428 Update Rust crate serde_json to v1.0.121 (#12553) 2024-07-28 22:16:55 -04:00
renovate[bot]
2f2149aca8 Update Rust crate env_logger to v0.11.5 (#12550) 2024-07-28 22:16:49 -04:00
renovate[bot]
9d5c31e7da Update Rust crate imara-diff to v0.1.7 (#12551) 2024-07-28 22:16:42 -04:00
renovate[bot]
25f3ad6238 Update Rust crate clap to v4.5.11 (#12549) 2024-07-28 22:16:36 -04:00
renovate[bot]
79926329a4 Update Rust crate argfile to v0.2.1 (#12548) 2024-07-28 22:16:31 -04:00
Aleksei Latyshev
9cdc578dd9 [flake8-builtins] Implement import, lambda, and module shadowing (#12546)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Extend `flake8-builtins` to imports, lambda-arguments, and modules to be
consistent with original checker
[flake8_builtins](https://github.com/gforcada/flake8-builtins/blob/main/flake8_builtins.py).

closes #12540 

## Details

- Implement builtin-import-shadowing (A004)
- Stop tracking imports shadowing in builtin-variable-shadowing (A001)
in preview mode.
- Implement builtin-lambda-argument-shadowing (A005)
- Implement builtin-module-shadowing (A006)
  - Add new option `linter.flake8_builtins.builtins_allowed_modules`

## Test Plan

cargo test
2024-07-29 01:42:42 +00:00
Charlie Marsh
665c75f7ab Add document for executable determination (#12547)
Closes https://github.com/astral-sh/ruff/issues/12505.
2024-07-28 16:23:00 -04:00
Micha Reiser
f37b39d6cc Allow downloading ecosystem results from forks (#12544) 2024-07-27 19:57:19 +02:00
Charlie Marsh
e18c45c310 Avoid marking required imports as unused (#12537)
## Summary

If an import is marked as "required", we should never flag it as unused.
In practice, this is rare, since required imports are typically used for
`__future__` annotations, which are always considered "used".

Closes https://github.com/astral-sh/ruff/issues/12458.
2024-07-26 14:23:43 -04:00
Charlie Marsh
d930052de8 Move required import parsing out of lint rule (#12536)
## Summary

Instead, make it part of the serialization and deserialization itself.
This makes it _much_ easier to reuse when solving
https://github.com/astral-sh/ruff/issues/12458.
2024-07-26 13:35:45 -04:00
Sigurd Spieckermann
7ad4df9e9f Complete FBT002 example with Enum argument (#12525)
## Summary

I've completed `FBT002` rule example with an `Enum` argument to show the
full usage in this case.
2024-07-26 11:50:19 -04:00
Charlie Marsh
425761e960 Use colon rather than dot formatting for integer-only types (#12534)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12421.
2024-07-26 15:48:19 +00:00
Carl Meyer
4b69271809 [red-knot] resolve int/list/dict/set/tuple to builtin type (#12521)
Now that we have builtins available, resolve some simple cases to the
right builtin type.

We should also adjust the display for types to include their module
name; that's not done yet here.
2024-07-26 08:21:31 -07:00
Micha Reiser
bf23d38a21 Remove unnecessary clone in workspace API (#12529) 2024-07-26 17:19:05 +02:00
Charlie Marsh
49f51583fa Always allow explicit multi-line concatenations when implicit are banned (#12532)
## Summary

Closes https://github.com/astral-sh/ruff/issues/11582.
2024-07-26 10:36:35 -04:00
Charlie Marsh
1fe4a5faed Avoid recommending __slots__ for classes that inherit from more than namedtuple (#12531)
## Summary

Closes https://github.com/astral-sh/ruff/issues/11887.
2024-07-26 14:24:40 +00:00
Charlie Marsh
998bfe0847 Avoid recommending no-argument super in slots=True dataclasses (#12530)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12506.
2024-07-26 10:09:51 -04:00
Dhruv Manilawala
6f4db8675b [red-knot] Add support for untitled files (#12492)
## Summary

This PR adds support for untitled files in the Red Knot project.

Refer to the [design
discussion](https://github.com/astral-sh/ruff/discussions/12336) for
more details.

### Changes
* The `parsed_module` always assumes that the `SystemVirtual` path is of
`PySourceType::Python`.
* For the module resolver, as suggested, I went ahead by adding a new
`SystemOrVendoredPath` enum and renamed `FilePathRef` to
`SystemOrVendoredPathRef` (happy to consider better names here).
* The `file_to_module` query would return if it's a
`FilePath::SystemVirtual` variant because a virtual file doesn't belong
to any module.
* The sync implementation for the system virtual path is basically the
same as that of system path except that it uses the
`virtual_path_metadata`. The reason for this is that the system
(language server) would provide the metadata on whether it still exists
or not and if it exists, the corresponding metadata.

For point (1), VS Code would use `Untitled-1` for Python files and
`Untitled-1.ipynb` for Jupyter Notebooks. We could use this distinction
to determine whether the source type is `Python` or `Ipynb`.

## Test Plan

Added test cases in #12526
2024-07-26 18:13:31 +05:30
Micha Reiser
71f7aa4971 Remove criterion/codspeed compat layer (#12524) 2024-07-26 12:22:16 +02:00
Auguste Lalande
9f72f474e6 [pydoclint] Add docstring-missing-returns amd docstring-extraneous-returns (DOC201, DOC202) (#12485)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-26 06:36:00 +00:00
Carl Meyer
10c993e21a [red-knot] remove wrong __init__.py from file-watching tests (#12519) 2024-07-26 07:14:01 +01:00
Carl Meyer
2d3914296d [red-knot] handle all syntax without panic (#12499)
Extend red-knot type inference to cover all syntax, so that inferring
types for a scope gives all expressions a type. This means we can run
the red-knot semantic lint on all Python code without panics. It also
means we can infer types for `builtins.pyi` without panics.

To keep things simple, this PR intentionally doesn't add any new type
inference capabilities: the expanded coverage is all achieved with
`Type::Unknown`. But this puts the skeleton in place for adding better
inference of all these language features.

I also had to add basic Salsa cycle recovery (with just `Type::Unknown`
for now), because some `builtins.pyi` definitions are cyclic.

To test this, I added a comprehensive corpus of test snippets sourced
from Cinder under [MIT
license](https://github.com/facebookincubator/cinder/blob/cinder/3.10/cinderx/LICENSE),
which matches Ruff's license. I also added to this corpus some
additional snippets for newer language features: all the
`27_func_generic_*` and `73_class_generic_*` files, as well as
`20_lambda_default_arg.py`, and added a test which runs semantic-lint
over all these files. (The test doesn't assert the test-corpus files are
lint-free; just that they are able to lint without a panic.)
2024-07-25 17:38:08 -07:00
Charlie Marsh
7571da8778 Preserve trailing inline comments on import-from statements (#12498)
## Summary

Right now, in the isort comment model, there's nowhere for trailing
comments on the _statement_ to go, as in:

```python
from mylib import (
    MyClient,
    MyMgmtClient,
)  # some comment
```

If the comment is on the _alias_, we do preserve it, because we attach
it to the alias, as in:

```python
from mylib import (
    MyClient,
    MyMgmtClient,  # some comment
)
```

Similarly, if the comment is trailing on an import statement
(non-`from`), we again attach it to the alias, because it can't be
parenthesized, as in:

```python
import foo  # some comment
```

This PR adds logic to track and preserve those trailing comments.

We also no longer drop several other comments, like:

```python
from mylib import (
    # some comment
    MyClient
)
```

Closes https://github.com/astral-sh/ruff/issues/12487.
2024-07-25 17:46:58 -04:00
Alex Waygood
2ceac5f868 [red-knot] Rename some methods in the module resolver (#12517) 2024-07-25 19:28:48 +00:00
Alex Waygood
5ce80827d2 [red-knot] Refactor path.rs in the module resolver (#12494) 2024-07-25 19:29:28 +01:00
Dhruv Manilawala
e047b9685a Use docs bot email for docs publish (#12511)
Ref: https://github.com/astral-sh/uv/pull/5369
2024-07-25 21:50:00 +05:30
Dhruv Manilawala
fc16d8d04d Bump version to 0.5.5 (#12510) 2024-07-25 20:17:01 +05:30
Uriya Harpeness
175e5d7b88 Add missing traceback line in f-string-in-exception docstring. (#12508)
## Summary

Add missing traceback line in `f-string-in-exception` docstring.

Solves https://github.com/astral-sh/ruff/issues/12504.
2024-07-25 10:22:05 -04:00
Dhruv Manilawala
c03f257ed7 Add note about the breaking change in nvim-lspconfig (#12507)
Refer https://github.com/astral-sh/ruff/issues/12408
2024-07-25 14:01:16 +00:00
Dhruv Manilawala
6bbb4a28c2 Add setup docs for Zed editor (#12501)
## Summary

This PR adds the setup documentation for using Ruff with the Zed editor.

Closes: #12388
2024-07-25 13:09:17 +00:00
Micha Reiser
2ce3e3ae60 Fix the search path tests on MacOS (#12503) 2024-07-25 08:21:38 +02:00
Charlie Marsh
2a64cccb61 Avoid applying ignore-names to self and cls function names (#12497)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12465.
2024-07-24 18:08:23 -04:00
Alex Waygood
928ffd6650 Ignore NPY201 inside except blocks for compatibility with older numpy versions (#12490) 2024-07-24 20:03:23 +00:00
Alex Waygood
e52be0951a [red-knot] Improve validation for search paths (#12376) 2024-07-24 15:02:25 +01:00
Dylan
889073578e [flake8-bugbear] Allow singleton tuples with starred expressions in B013 (#12484) 2024-07-24 15:19:30 +02:00
Micha Reiser
eac965ecaf [red-knot] Watch search paths (#12407) 2024-07-24 07:38:50 +00:00
Auguste Lalande
8659f2f4ea [pydoclint] Fix documentation for DOC501 (#12483)
## Summary

The doc was written backwards. mb.
2024-07-24 00:08:53 -04:00
Alex Waygood
c1b292a0dc Refactor NPY201 (#12479) 2024-07-23 18:24:20 +01:00
Micha Reiser
3af6ccb720 Fix Ord of cmp_fix (#12471) 2024-07-23 15:14:22 +02:00
Micha Reiser
f0fc6a95fe [red-knot] Lazy package file discovery (#12452)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-07-23 08:47:15 +00:00
Mateusz Sokół
f96a3c71ff Fix NumPy 2.0 rule for np.alltrue and np.sometrue (#12473)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-23 08:34:43 +00:00
Micha Reiser
b9b7deff17 Implement upcast_mut for new TestDb (#12470) 2024-07-23 07:11:00 +00:00
Micha Reiser
40d9324f5a [red-knot] Improved file watching (#12382) 2024-07-23 08:18:59 +02:00
Pathompong Kwangtong
a9f8bd59b2 Add Eglot setup guide for Emacs editor (#12426)
## Summary

The purpose of this change is to explain how to use ruff as a language
server in Eglot with automatic formatting because I've struggle to use
it with Eglot. I've search it online and found that there are some
people also struggle too. (See [this reddit
post](https://www.reddit.com/r/emacs/comments/118mo6w/eglot_automatic_formatting/)
and
https://github.com/astral-sh/ruff-lsp/issues/19#issuecomment-1435138828)


## Test Plan

I've use this setting myself. And I will continue maintain this part as
long as I use it.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-07-23 10:50:51 +05:30
Piotr Osiewicz
143e172431 Do not bail code action resolution when a quick fix is requested (#12462)
## Summary

When working on improving Ruff integration with Zed I noticed that it
errors out when we try to resolve a code action of a `QUICKFIX` kind;
apparently, per @dhruvmanila we shouldn't need to resolve it, as the
edit is provided in the initial response for the code action. However,
it's possible for the `resolve` call to fill out other fields (such as
`command`).
AFAICT Helix also tries to resolve the code actions unconditionally (as
in, when either `edit` or `command` is absent); so does VSC. They can
still apply the quickfixes though, as they do not error out on a failed
call to resolve code actions - Zed does. Following suit on Zed's side
does not cut it though, as we still get a log request from Ruff for that
failure (which is surfaced in the UI).
There are also other language servers (such as
[rust-analyzer](c1c9e10f72/crates/rust-analyzer/src/handlers/request.rs (L1257)))
that fill out both `command` and `edit` fields as a part of code action
resolution.

This PR makes the resolve calls for quickfix actions return the input
value.

## Test Plan

N/A
2024-07-23 10:30:03 +05:30
Auguste Lalande
b2d3a05ee4 [flake8-async] Fix references in documentation not displaying (#12467)
## Summary

Fix references in documentation of several `ASYNC` rules not displaying

## Test Plan

Validated documentation now displays correctly
2024-07-22 19:38:13 -04:00
Josh Cannon
ef1ca0dd38 Fix bad markdown in CONTRIBUTING.md (#12466)
See
https://github.com/astral-sh/ruff/blob/main/CONTRIBUTING.md#import-categorization
2024-07-23 00:03:30 +01:00
Carl Meyer
c7b13bb8fc [red-knot] add cycle-free while-loop control flow (#12413)
Add support for while-loop control flow.

This doesn't yet include general support for terminals and reachability;
that is wider than just while loops and belongs in its own PR.

This also doesn't yet add support for cyclic definitions in loops; that
comes with enough of its own complexity in Salsa that I want to handle
it separately.
2024-07-22 14:27:33 -07:00
Carl Meyer
dbbe3526ef [red-knot] add while-loop to benchmark (#12464)
So we can get some signal from the benchmark result on
https://github.com/astral-sh/ruff/pull/12413
2024-07-22 14:16:56 -07:00
Carl Meyer
f22c8ab811 [red-knot] add maybe-undefined lint rule (#12414)
Add a lint rule to detect if a name is definitely or possibly undefined
at a given usage.

If I create the file `undef/main.py` with contents:

```python
x = int
def foo():
    z
    return x
if flag:
    y = x
y
```

And then run `cargo run --bin red_knot -- --current-directory
../ruff-examples/undef`, I get the output:

```
Name 'z' used when not defined.
Name 'flag' used when not defined.
Name 'y' used when possibly not defined.
```

If I modify the file to add `y = 0` at the top, red-knot re-checks it
and I get the new output:

```
Name 'z' used when not defined.
Name 'flag' used when not defined.
```

Note that `int` is not flagged, since it's a builtin, and `return x` in
the function scope is not flagged, since it refers to the global `x`.
2024-07-22 13:53:59 -07:00
Alex Waygood
2a8f95c437 [red-knot] Use a distinct type for module search paths in the module resolver (#12379) 2024-07-22 19:44:27 +00:00
Dhruv Manilawala
ea2d51c2bb Add note to include notebook files for native server (#12449)
## Summary

Similar to https://github.com/astral-sh/ruff-vscode/pull/547 but for the
online docs.

Refer to https://github.com/astral-sh/ruff-vscode/issues/546

## Preview

<img width="1728" alt="Screenshot 2024-07-22 at 14 51 40"
src="https://github.com/user-attachments/assets/39014278-c868-45b0-9058-42858a060fd8">
2024-07-22 21:40:30 +05:30
Micha Reiser
ed238e0c76 Fix incorrect placement of leading function comment with type params (#12447) 2024-07-22 14:17:00 +02:00
Micha Reiser
3ace12943e Ignore more open ai notebooks for now (#12448) 2024-07-22 14:16:48 +02:00
Dhruv Manilawala
978909fcf4 Raise syntax error for unparenthesized generator expr in multi-argument call (#12445)
## Summary

This PR fixes a bug to raise a syntax error when an unparenthesized
generator expression is used as an argument to a call when there are
more than one argument.

For reference, the grammar is:
```
primary:
    | ...
    | primary genexp 
    | primary '(' [arguments] ')' 
    | ...

genexp:
    | '(' ( assignment_expression | expression !':=') for_if_clauses ')' 
```

The `genexp` requires the parenthesis as mentioned in the grammar. So,
the grammar for a call expression is either a name followed by a
generator expression or a name followed by a list of argument. In the
former case, the parenthesis are excluded because the generator
expression provides them while in the later case, the parenthesis are
explicitly provided for a list of arguments which means that the
generator expression requires it's own parenthesis.

This was discovered in https://github.com/astral-sh/ruff/issues/12420.

## Test Plan

Add test cases for valid and invalid syntax.

Make sure that the parser from CPython also raises this at the parsing
step:
```console
$ python3.13 -m ast parser/_.py
  File "parser/_.py", line 1
    total(1, 2, x for x in range(5), 6)
                ^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized

$ python3.13 -m ast parser/_.py
  File "parser/_.py", line 1
    sum(x for x in range(10), 10)
        ^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized
```
2024-07-22 14:44:20 +05:30
Dhruv Manilawala
f8735e1ee8 Remove unused dependencies, sync existing versions (#12446)
## Summary

This PR removes unused dependencies from `fuzz` crate and syncs the
`similar` crate to the workspace version. This will help in resolve
https://github.com/astral-sh/ruff/pull/12442.

## Test Plan

Build the fuzz crate:

For Mac (it requires the nightly build):
```
cargo +nightly fuzz build
```
2024-07-22 10:49:05 +05:30
renovate[bot]
d70ceb6a56 Update Rust crate uuid to v1.10.0 (#12444) 2024-07-21 21:50:53 -04:00
renovate[bot]
fc7d9e95b8 Update Rust crate tracing-tree to 0.4.0 (#12443) 2024-07-21 21:50:46 -04:00
renovate[bot]
b578fca9cb Update NPM Development dependencies (#12441) 2024-07-21 21:50:32 -04:00
renovate[bot]
8d3146c2b2 Update pre-commit hook astral-sh/ruff-pre-commit to v0.5.4 (#12440) 2024-07-21 21:50:27 -04:00
renovate[bot]
fa5c841154 Update Rust crate thiserror to v1.0.63 (#12437) 2024-07-21 21:49:42 -04:00
renovate[bot]
f8fcbc19d9 Update dependency react-resizable-panels to v2.0.22 (#12439) 2024-07-21 21:49:33 -04:00
renovate[bot]
97fdd48208 Update Rust crate toml to v0.8.15 (#12438) 2024-07-21 21:49:23 -04:00
renovate[bot]
731ed2e40b Update Rust crate syn to v2.0.72 (#12436) 2024-07-21 21:49:16 -04:00
Auguste Lalande
3a742c17f8 [pydoclint] Fix DOC501 panic #12428 (#12435)
## Summary

Fix panic reported in #12428. Where a string would sometimes get split
within a character boundary. This bypasses the need to split the string.

This does not guarantee the correct formatting of the docstring, but
neither did the previous implementation.

Resolves #12428 

## Test Plan

Test case added to fixture
2024-07-21 19:30:06 +00:00
TomerBin
053243635c [fastapi] Implement FAST001 (fastapi-redundant-response-model) and FAST002 (fastapi-non-annotated-dependency) (#11579)
## Summary

Implements ruff specific role for fastapi routes, and its autofix.

## Test Plan

`cargo test` / `cargo insta review`
2024-07-21 18:28:10 +00:00
Ivan Carvalho
82355712c3 Add IBM to Who is Using ruff (#12433)
<!--
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

Just updating the README to reflect that IBM has been using ruff for a
year already: https://github.com/Qiskit/qiskit/pull/10116.
2024-07-21 11:17:24 -05:00
Auguste Lalande
4bc73dd87e [pydoclint] Implement docstring-missing-exception and docstring-extraneous-exception (DOC501, DOC502) (#11471)
## Summary

These are the first rules implemented as part of #458, but I plan to
implement more.

Specifically, this implements `docstring-missing-exception` which checks
for raised exceptions not documented in the docstring, and
`docstring-extraneous-exception` which checks for exceptions in the
docstring not present in the body.

## Test Plan

Test fixtures added for both google and numpy style.
2024-07-20 19:41:51 +00:00
T-256
53b84ab054 Cleanup redundant spaces from changelog (#12424) 2024-07-20 17:46:15 +00:00
Charlie Marsh
3664f85f45 Bump version to v0.5.4 (#12423) 2024-07-20 17:28:13 +00:00
Charlie Marsh
2c1926beeb Insert parentheses for multi-argument generators (#12422)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12420.
2024-07-20 16:41:55 +00:00
Charlie Marsh
4bcc96ae51 Avoid shadowing diagnostics for @override methods (#12415)
Closes https://github.com/astral-sh/ruff/issues/12412.
2024-07-19 21:32:33 -04:00
FishAlchemist
c0a2b49bac Fix the Github link error for Neovim in the setup for editors in the docs. (#12410)
## Summary

Fix Github link error for Neovim setup editors .

## Test Plan
Click Neovim Github link with mkdocs on local.
2024-07-19 16:24:12 -04:00
Sashko
ca22248628 Update docs Settings output-format default (#12409)
## Update docs Settings output-format default

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

## Test Plan

Run all automation mentioned here
fe04f2b09d/CONTRIBUTING.md (development)

Manually verified changes in the generated MkDocs site.

Co-authored-by: Oleksandr Zavertniev <oleksandr.zavertniev@yellowbrick.com>
2024-07-19 17:51:46 +00:00
Alex Waygood
d8cf8ac2ef [red-knot] Resolve symbols from builtins.pyi in the stdlib if they cannot be found in other scopes (#12390)
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-07-19 17:44:56 +01:00
Carl Meyer
1c7b84059e [red-knot] fix incremental benchmark (#12400)
We should write `BAR_CODE` to `bar.py`, not to `foo.py`.
2024-07-19 08:32:37 -07:00
Carl Meyer
f82bb67555 [red-knot] trace file when inferring types (#12401)
When poring over traces, the ones that just include a definition or
symbol or expression ID aren't very useful, because you don't know which
file it comes from. This adds that information to the trace.

I guess the downside here is that if calling `.file(db)` on a
scope/definition/expression would execute other traced code, it would be
marked as outside the span? I don't think that's a concern, because I
don't think a simple field access on a tracked struct should ever
execute our code. If I'm wrong and this is a problem, it seems like the
tracing crate has this feature where you can record a field as
`tracing::field::Empty` and then fill in its value later with
`span.record(...)`, but when I tried this it wasn't working for me, not
sure why.

I think there's a lot more we can do to make our tracing output more
useful for debugging (e.g. record an event whenever a
definition/symbol/expression/use id is created with the details of that
definition/symbol/expression/use), this is just dipping my toes in the
water.
2024-07-19 07:13:51 -07:00
Alex Waygood
5f96f69151 [red-knot] Fix bug where module resolution would not be invalidated if an entire package was deleted (#12378) 2024-07-19 13:53:09 +01:00
Micha Reiser
ad19b3fd0e [red-knot] Add verbosity argument to CLI (#12404) 2024-07-19 11:38:24 +00:00
Carl Meyer
a62e2d2000 [red-knot] preparse builtins in without_parse benchmark (#12395) 2024-07-19 05:58:27 +00:00
Dylan
d61747093c [ruff] Rename RUF007 to zip-instead-of-pairwise (#12399)
## Summary

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

Renames the rule
[RUF007](https://docs.astral.sh/ruff/rules/pairwise-over-zipped/) from
`pairwise-over-zipped` to `zip-instead-of-pairwise`. This closes #12397.

Specifically, in this PR:

- The file containing the rule was renamed
- The struct was renamed
- The function implementing the rule was renamed

## Testing

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

- `cargo test`
- Docs re-built locally and verified that new rule name is displayed.
(Screenshots below).

<img width="939" alt="New rule name in rule summary"
src="https://github.com/user-attachments/assets/bf638bc9-1b7a-4675-99bf-e4de88fec167">

<img width="805" alt="New rule name in rule details"
src="https://github.com/user-attachments/assets/6fffd745-2568-424a-84e5-f94a41351022">
2024-07-18 19:26:27 -04:00
ukyen
0ba7fc63d0 [pydocstyle] Escaped docstring in docstring (D301 ) (#12192)
<!--
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? -->
This PR updates D301 rule to allow inclduing escaped docstring, e.g.
`\"""Foo.\"""` or `\"\"\"Bar.\"\"\"`, within a docstring.

Related issue: #12152 

## Test Plan

Add more test cases to D301.py and update the snapshot file.

<!-- How was it tested? -->
2024-07-18 18:36:05 -04:00
Carl Meyer
fa5b19d4b6 [red-knot] use a simpler builtin in the benchmark (#12393)
In preparation for supporting resolving builtins, simplify the benchmark
so it doesn't look up `str`, which is actually a complex builtin to deal
with because it inherits `Sequence[str]`.

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-07-18 14:04:33 -07:00
Carl Meyer
181e7b3c0d [red-knot] rename module_global to global (#12385)
Per comments in https://github.com/astral-sh/ruff/pull/12269, "module
global" is kind of long, and arguably redundant.

I tried just using "module" but there were too many cases where I felt
this was ambiguous. I like the way "global" works out better, though it
does require an understanding that in Python "global" generally means
"module global" not "globally global" (though in a sense module globals
are also globally global since modules are singletons).
2024-07-18 13:05:30 -07:00
Carl Meyer
519eca9fe7 [red-knot] support implicit global name lookups (#12374)
Support falling back to a global name lookup if a name isn't defined in
the local scope, in the cases where that is correct according to Python
semantics.

In class scopes, a name lookup checks the local namespace first, and if
the name isn't found there, looks it up in globals.

In function scopes (and type parameter scopes, which are function-like),
if a name has any definitions in the local scope, it is a local, and
accessing it when none of those definitions have executed yet just
results in an `UnboundLocalError`, it does not fall back to a global. If
the name does not have any definitions in the local scope, then it is an
implicit global.

Public symbol type lookups never include such a fall back. For example,
if a name is not defined in a class scope, it is not available as a
member on that class, even if a name lookup within the class scope would
have fallen back to a global lookup.

This PR makes the `@override` lint rule work again.

Not yet included/supported in this PR:

* Support for free variables / closures: a free symbol in a nested
function-like scope referring to a symbol in an outer function-like
scope.
* Support for `global` and `nonlocal` statements, which force a symbol
to be treated as global or nonlocal even if it has definitions in the
local scope.
* Module-global lookups should fall back to builtins if the name isn't
found in the module scope.

I would like to expose nicer APIs for the various kinds of symbols
(explicit global, implicit global, free, etc), but this will also wait
for a later PR, when more kinds of symbols are supported.
2024-07-18 10:50:43 -07:00
Dhruv Manilawala
f0d589d7a3 Provide custom job permissions to cargo-dist (#12386)
We can't just directly update the `release.yml` file because that's
auto-generated using `cargo-dist`. So, update the permissions in
`Cargo.toml` and then use `cargo dist generate` to make sure there's no
diff.
2024-07-18 16:49:38 +00:00
Dhruv Manilawala
512c8b2cc5 Provide contents read permission to wasm publish job (#12384)
The job has asked for the permission:
811f78d94d/.github/workflows/publish-wasm.yml (L25)
2024-07-18 22:02:49 +05:30
Carl Meyer
811f78d94d [red-knot] small efficiency improvements and bugfixes to use-def map building (#12373)
Adds inference tests sufficient to give full test coverage of the
`UseDefMapBuilder::merge` method.

In the process I realized that we could implement visiting of if
statements in `SemanticBuilder` with fewer `snapshot`, `restore`, and
`merge` operations, so I restructured that visit a bit.

I also found one correctness bug in the `merge` method (it failed to
extend the given snapshot with "unbound" for any missing symbols,
meaning we would just lose the fact that the symbol could be unbound in
the merged-in path), and two efficiency bugs (if one of the ranges to
merge is empty, we can just use the other one, no need for copies, and
if the ranges are overlapping -- which can occur with nested branches --
we can still just merge them with no copies), and fixed all three.
2024-07-18 09:24:58 -07:00
Dhruv Manilawala
8f1be31289 Update 0.5.3 changelog caption (#12383)
As suggested in
https://github.com/astral-sh/ruff/pull/12381#discussion_r1683123202
2024-07-18 16:17:07 +00:00
Dhruv Manilawala
8cfbac71a4 Bump version to 0.5.3 (#12381) 2024-07-18 16:07:34 +00:00
Charlie Marsh
9460857932 Migrate to standalone docs repo (#12341)
## Summary

See: https://github.com/astral-sh/uv/pull/5081
2024-07-18 15:35:49 +00:00
Dhruv Manilawala
a028ca22f0 Add VS Code specific extension settings (#12380)
## Summary

This PR adds VS Code specific extension settings in the online
documentation.

The content is basically taken from the `package.json` file in the
`ruff-vscode` repository.
2024-07-18 20:58:14 +05:30
Dhruv Manilawala
7953f6aa79 Update versioning policy for editor integration (#12375)
## Summary

Following the stabilization of the Ruff language server, we need to
update our versioning policy to account for any changes in it. This
could be server settings, capability, etc.

This PR also adds a new section for the VS Code extension which is
adopted from [Biome's versioning
policy](https://biomejs.dev/internals/versioning/#visual-studio-code-extension)
for the same.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
2024-07-18 15:17:36 +00:00
Charlie Marsh
764d9ab4ee Allow repeated-equality-comparison for mixed operations (#12369)
## Summary

This PR allows us to fix both expressions in `foo == "a" or foo == "b"
or ("c" != bar and "d" != bar)`, but limits the rule to consecutive
comparisons, following https://github.com/astral-sh/ruff/issues/7797.

I think this logic was _probably_ added because of
https://github.com/astral-sh/ruff/pull/12368 -- the intent being that
we'd replace the _entire_ expression.
2024-07-18 11:16:40 -04:00
Charlie Marsh
9b9d701500 Allow additional arguments for sum and max comprehensions (#12364)
## Summary

These can have other arguments, so it seems wrong to gate on single
argument here.

Closes https://github.com/astral-sh/ruff/issues/12358.
2024-07-18 08:37:28 -04:00
Dhruv Manilawala
648cca199b Add docs for Ruff language server (#12344)
## Summary

This PR adds documentation for the Ruff language server.

It mainly does the following:
1. Combines various READMEs containing instructions for different editor
setup in their respective section on the online docs
2. Provide an enumerated list of server settings. Additionally, it also
provides a section for VS Code specific options.
3. Adds a "Features" section which enumerates all the current
capabilities of the native server

For (2), the settings documentation is done manually but a future
improvement (easier after `ruff-lsp` is deprecated) is to move the docs
in to Rust struct and generate the documentation from the code itself.
And, the VS Code extension specific options can be generated by diffing
against the `package.json` in `ruff-vscode` repository.

### Structure

1. Setup: This section contains the configuration for setting up the
language server for different editors
2. Features: This section contains a list of capabilities provided by
the server along with short GIF to showcase it
3. Settings: This section contains an enumerated list of settings in a
similar format to the one for the linter / formatter
4. Migrating from `ruff-lsp`

> [!NOTE]
>
> The settings page is manually written but could possibly be
auto-generated via a macro similar to `OptionsMetadata` on the
`ClientSettings` struct

resolves: #11217 

## Test Plan

Generate and open the documentation locally using:
1. `python scripts/generate_mkdocs.py`
2. `mkdocs serve -f mkdocs.insiders.yml`
2024-07-18 17:41:43 +05:30
Dhruv Manilawala
2e77b775b0 Consider --preview flag for server subcommand (#12208)
## Summary

This PR removes the requirement of `--preview` flag to run the `ruff
server` and instead considers it to be an indicator to turn on preview
mode for the linter and the formatter.

resolves: #12161 

## Test Plan

Add test cases to assert the `preview` value is updated accordingly.

In an editor context, I used the local `ruff` executable in Neovim with
the `--preview` flag and verified that the preview-only violations are
being highlighted.

Running with:
```lua
require('lspconfig').ruff.setup({
  cmd = {
    '/Users/dhruv/work/astral/ruff/target/debug/ruff',
    'server',
    '--preview',
  },
})
```
The screenshot shows that `E502` is highlighted with the below config in
`pyproject.toml`:

<img width="877" alt="Screenshot 2024-07-17 at 16 43 09"
src="https://github.com/user-attachments/assets/c7016ef3-55b1-4a14-bbd3-a07b1bcdd323">
2024-07-18 11:05:01 +05:30
Dhruv Manilawala
ebe5b06c95 Use fallback settings when indexing the project (#12362)
## Summary

This PR updates the settings index building logic in the language server
to consider the fallback settings for applying ignore filters in
`WalkBuilder` and the exclusion via `exclude` / `extend-exclude`.

This flow matches the one in the `ruff` CLI where the root settings is
built by (1) finding the workspace setting in the ancestor directory (2)
finding the user configuration if that's missing and (3) fallback to
using the default configuration.

Previously, the index building logic was being executed before (2) and
(3). This PR reverses the logic so that the exclusion /
`respect_gitignore` is being considered from the default settings if
there's no workspace / user settings. This has the benefit that the
server no longer enters the `.git` directory or any other excluded
directory when a user opens a file in the home directory.

Related to #11366

## Test plan

Opened a test file from the home directory and confirmed with the debug
trace (removed in #12360) that the server excludes the `.git` directory
when indexing.
2024-07-18 09:16:45 +05:30
Carl Meyer
b2a49d8140 [red-knot] better docs for use-def maps (#12357)
Add better doc comments and comments, as well as one debug assertion, to
use-def map building.
2024-07-17 17:50:58 -07:00
Carl Meyer
985a999234 [red-knot] better docs for type inference (#12356)
Add some docs for how type inference works.

Also a couple minor code changes to rearrange or rename for better
clarity.
2024-07-17 13:36:58 -07:00
cake-monotone
1df51b1fbf [pyupgrade] Implement unnecessary-default-type-args (UP043) (#12371)
## Summary

Add new rule and implement for `unnecessary default type arguments`
under the `UP` category (`UP043`).

```py
// < py313
Generator[int, None, None] 

// >= py313
Generator[int]
```

I think that as Python 3.13 develops, there might be more default type
arguments added besides `Generator` and `AsyncGenerator`. So, I made
this more flexible to accommodate future changes.

related issue: #12286

## Test Plan

snapshot included..!
2024-07-17 19:45:43 +00:00
Charlie Marsh
1435b0f022 Remove discard, remove, and pop allowance for loop-iterator-mutation (#12365)
## Summary

Pretty sure this should still be an error, but also, I think I added
this because of ecosystem CI? So want to see what pops up.

Closes https://github.com/astral-sh/ruff/issues/12164.
2024-07-17 17:42:14 +00:00
Charlie Marsh
e39298dcbc Use UTF-8 as default encoding in unspecified-encoding fix (#12370)
## Summary

This is the _intended_ default that PEP 597 _wants_, but it's not
backwards compatible. The fix is already unsafe, so it's better for us
to recommend the desired and expected behavior.

Closes https://github.com/astral-sh/ruff/issues/12069.
2024-07-17 12:57:27 -04:00
Charlie Marsh
1de8ff3308 Detect enumerate iterations in loop-iterator-mutation (#12366)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12164.
2024-07-17 12:03:36 -04:00
Charlie Marsh
72e02206d6 Avoid dropping extra boolean operations in repeated-equality-comparison (#12368)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12062.
2024-07-17 11:49:27 -04:00
Charlie Marsh
80f0116641 Ignore self and cls when counting arguments (#12367)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12320.
2024-07-17 10:49:38 -04:00
Micha Reiser
79b535587b [red-knot] Reload notebook on file change (#12361) 2024-07-17 12:23:48 +00:00
Dhruv Manilawala
6e0cbe0f35 Remove leftover debug log (#12360)
This was a leftover from #12299
2024-07-17 17:52:44 +05:30
Micha Reiser
91338ae902 [red-knot] Add basic workspace support (#12318) 2024-07-17 11:34:21 +02:00
Micha Reiser
0c72577b5d [red-knot] Add notebook support (#12338) 2024-07-17 08:26:33 +00:00
Matthew Runyon
fe04f2b09d Publish wasm API to npm (#12317) 2024-07-17 08:50:38 +02:00
Carl Meyer
073588b48e [red-knot] improve semantic index tests (#12355)
Improve semantic index tests with better assertions than just `.len()`,
and re-add use-definition test that was commented out in the switch to
Salsa initially.
2024-07-16 23:46:49 -07:00
Alex Waygood
9a2dafb43d [red-knot] Add support for editable installs to the module resolver (#12307)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Carl Meyer <carl@astral.sh>
2024-07-16 18:17:47 +00:00
Carl Meyer
595b1aa4a1 [red-knot] per-definition inference, use-def maps (#12269)
Implements definition-level type inference, with basic control flow
(only if statements and if expressions so far) in Salsa.

There are a couple key ideas here:

1) We can do type inference queries at any of three region
granularities: an entire scope, a single definition, or a single
expression. These are represented by the `InferenceRegion` enum, and the
entry points are the salsa queries `infer_scope_types`,
`infer_definition_types`, and `infer_expression_types`. Generally
per-scope will be used for scopes that we are directly checking and
per-definition will be used anytime we are looking up symbol types from
another module/scope. Per-expression should be uncommon: used only for
the RHS of an unpacking or multi-target assignment (to avoid
re-inferring the RHS once per symbol defined in the assignment) and for
test nodes in type narrowing (e.g. the `test` of an `If` node). All
three queries return a `TypeInference` with a map of types for all
definitions and expressions within their region. If you do e.g.
scope-level inference, when it hits a definition, or an
independently-inferable expression, it should use the relevant query
(which may already be cached) to get all types within the smaller
region. This avoids double-inferring smaller regions, even though larger
regions encompass smaller ones.

2) Instead of building a control-flow graph and lazily traversing it to
find definitions which reach a use of a name (which is O(n^2) in the
worst case), instead semantic indexing builds a use-def map, where every
use of a name knows which definitions can reach that use. We also no
longer track all definitions of a symbol in the symbol itself; instead
the use-def map also records which defs remain visible at the end of the
scope, and considers these the publicly-visible definitions of the
symbol (see below).

Major items left as TODOs in this PR, to be done in follow-up PRs:

1) Free/global references aren't supported yet (only lookup based on
definitions in current scope), which means the override-check example
doesn't currently work. This is the first thing I'll fix as follow-up to
this PR.

2) Control flow outside of if statements and expressions.

3) Type narrowing.

There are also some smaller relevant changes here:

1) Eliminate `Option` in the return type of member lookups; instead
always return `Type::Unbound` for a name we can't find. Also use
`Type::Unbound` for modules we can't resolve (not 100% sure about this
one yet.)

2) Eliminate the use of the terms "public" and "root" to refer to
module-global scope or symbols. Instead consistently use the term
"module-global". It's longer, but it's the clearest, and the most
consistent with typical Python terminology. In particular I don't like
"public" for this use because it has other implications around author
intent (is an underscore-prefixed module-global symbol "public"?). And
"root" is just not commonly used for this in Python.

3) Eliminate the `PublicSymbol` Salsa ingredient. Many non-module-global
symbols can also be seen from other scopes (e.g. by a free var in a
nested scope, or by class attribute access), and thus need to have a
"public type" (that is, the type not as seen from a particular use in
the control flow of the same scope, but the type as seen from some other
scope.) So all symbols need to have a "public type" (here I want to keep
the use of the term "public", unless someone has a better term to
suggest -- since it's "public type of a symbol" and not "public symbol"
the confusion with e.g. initial underscores is less of an issue.) At
least initially, I would like to try not having special handling for
module-global symbols vs other symbols.

4) Switch to using "definitions that reach end of scope" rather than
"all definitions" in determining the public type of a symbol. I'm
convinced that in general this is the right way to go. We may want to
refine this further in future for some free-variable cases, but it can
be changed purely by making changes to the building of the use-def map
(the `public_definitions` index in it), without affecting any other
code. One consequence of combining this with no control-flow support
(just last-definition-wins) is that some inference tests now give more
wrong-looking results; I left TODO comments on these tests to fix them
when control flow is added.

And some potential areas for consideration in the future:

1) Should `symbol_ty` be a Salsa query? This would require making all
symbols a Salsa ingredient, and tracking even more dependencies. But it
would save some repeated reconstruction of unions, for symbols with
multiple public definitions. For now I'm not making it a query, but open
to changing this in future with actual perf evidence that it's better.
2024-07-16 11:02:30 -07:00
Charlie Marsh
30cef67b45 Remove BindingKind::ComprehensionVar (#12347)
## Summary

This doesn't seem to be used anywhere. Maybe it mattered when we didn't
handle generator scopes properly?
2024-07-16 11:18:04 -04:00
Charlie Marsh
d0c5925672 Consider expression before statement when determining binding kind (#12346)
## Summary

I believe these should always bind more tightly -- e.g., in:

```python
for _ in bar(baz for foo in [1]):
    pass
```

The inner `baz` and `foo` should be considered comprehension variables,
not for loop bindings.

We need to revisit this more holistically. In some of these cases,
`BindingKind` should probably be a flag, not an enum, since the values
aren't mutually exclusive. Separately, we should probably be more
precise in how we set it (e.g., by passing down from the parent rather
than sniffing in `handle_node_store`).

Closes https://github.com/astral-sh/ruff/issues/12339
2024-07-16 14:49:26 +00:00
Micha Reiser
b1487b6b4f Ignore more OpenAI notebooks with syntax errors in the ecosystem check (#12342) 2024-07-16 10:32:00 +02:00
Micha Reiser
85ae02d62e [red-knot] Add walk_directories to System (#12297) 2024-07-16 06:40:10 +00:00
konsti
9a817a2922 Insert empty line between suite and alternative branch after def/class (#12294)
When there is a function or class definition at the end of a suite
followed by the beginning of an alternative block, we have to insert a
single empty line between them.

In the if-else-statement example below, we insert an empty line after
the `foo` in the if-block, but none after the else-block `foo`, since in
the latter case the enclosing suite already adds empty lines.

```python
if sys.version_info >= (3, 10):
    def foo():
        return "new"
else:
    def foo():
        return "old"
class Bar:
    pass
```

To do so, we track whether the current suite is the last one in the
current statement with a new option on the suite kind.

Fixes #12199

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-15 12:59:33 +02:00
Dhruv Manilawala
ecd4b4d943 Build settings index in parallel for the native server (#12299)
## Summary

This PR updates the server to build the settings index in parallel using
similar logic as `python_files_in_path`.

This should help with https://github.com/astral-sh/ruff/issues/11366 but
ideally we would want to build it lazily.

## Test Plan

`cargo insta test`
2024-07-15 09:57:54 +00:00
github-actions[bot]
b9a8cd390f Sync vendored typeshed stubs (#12325)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-07-15 07:46:55 +01:00
renovate[bot]
2348714081 Update pre-commit dependencies (#12330) 2024-07-15 07:27:10 +01:00
renovate[bot]
3817b207cf Update NPM Development dependencies (#12331)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 08:08:10 +02:00
renovate[bot]
b1cf9ea663 Update Rust crate clap_complete_command to 0.6.0 (#12332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 08:05:07 +02:00
renovate[bot]
8ad10b9307 Update Rust crate compact_str to 0.8.0 (#12333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-15 06:03:23 +00:00
renovate[bot]
9c5524a9a2 Update Rust crate tikv-jemallocator to 0.6.0 (#12335)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 08:01:43 +02:00
renovate[bot]
1530223311 Update Rust crate serde_with to v3.9.0 (#12334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 07:58:18 +02:00
renovate[bot]
b9671522c4 Update Rust crate thiserror to v1.0.62 (#12329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 05:54:00 +00:00
renovate[bot]
9918202422 Update Rust crate syn to v2.0.71 (#12328)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 07:52:09 +02:00
renovate[bot]
42e7147860 Update Rust crate clap to v4.5.9 (#12326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 07:51:48 +02:00
renovate[bot]
25feab93f8 Update Rust crate matchit to v0.8.4 (#12327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 07:50:58 +02:00
Charlie Marsh
dc8db1afb0 Make some amendments to the v0.5.2 changelog (#12319) 2024-07-14 14:47:51 +00:00
Charlie Marsh
18c364d5df [flake8-bandit] Support explicit string concatenations in S310 HTTP detection (#12315)
Closes https://github.com/astral-sh/ruff/issues/12314.
2024-07-14 10:44:08 -04:00
Charlie Marsh
7a7c601d5e Bump version to v0.5.2 (#12316) 2024-07-14 10:43:58 -04:00
Charlie Marsh
3bfbbbc78c Avoid allocation when validating HTTP and HTTPS prefixes (#12313) 2024-07-13 17:25:02 -04:00
Tim Chan
1a3ee45b23 [flake8-bandit] Avoid S310 violations for HTTP-safe f-strings (#12305)
this resolves https://github.com/astral-sh/ruff/issues/12245
2024-07-13 20:57:05 +00:00
Charlie Marsh
65848869d5 [refurb] Make list-reverse-copy an unsafe fix (#12303)
## Summary

I don't know that there's more to do here. We could consider not raising
the violation at all for arguments, but that would have some false
negatives and could also be surprising to users.

Closes https://github.com/astral-sh/ruff/issues/12267.
2024-07-13 15:45:35 -04:00
Charlie Marsh
456d6a2fb2 Consider with blocks as single-item branches (#12311)
## Summary

Ensures that, e.g., the following is not considered a
redefinition-without-use:

```python
import contextlib

foo = None
with contextlib.suppress(ImportError):
    from some_module import foo
```

Closes https://github.com/astral-sh/ruff/issues/12309.
2024-07-13 15:22:17 -04:00
Charlie Marsh
940df67823 Omit code frames for fixes with empty ranges (#12304)
## Summary

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

## Test Plan

```shell
❯ cargo run check ../uv/foo --select INP
/Users/crmarsh/workspace/uv/foo/bar/baz.py:1:1: INP001 File `/Users/crmarsh/workspace/uv/foo/bar/baz.py` is part of an implicit namespace package. Add an `__init__.py`.
Found 1 error.
```
2024-07-12 15:21:28 +00:00
Charlie Marsh
e58713e2ac Make cache-write failures non-fatal (#12302)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12284.
2024-07-12 10:33:54 -04:00
Charlie Marsh
aa5c53b38b Remove 'non-obvious' allowance for E721 (#12300)
## Summary

I don't fully understand the purpose of this. In #7905, it was just
copied over from the previous non-preview implementation. But it means
that (e.g.) we don't treat `type(self.foo)` as a type -- which is wrong.

Closes https://github.com/astral-sh/ruff/issues/12290.
2024-07-12 09:21:43 -04:00
Charlie Marsh
4e6ecb2348 Treat not operations as boolean tests (#12301)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12285.
2024-07-12 08:53:37 -04:00
Alex Waygood
6febd96dfe [red-knot] Add a read_directory() method to the ruff_db::system::System trait (#12289) 2024-07-12 12:31:05 +00:00
Matthias
17e84d5f40 [numpy] Update NPY201: add np.NAN to exception (#12292)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-12 12:09:55 +00:00
Victorien
b6545ce5d6 Use indentation consistently (#12293) 2024-07-12 14:08:56 +02:00
Dhruv Manilawala
90e9aae3f4 Consider nested configs for settings reloading (#12253)
## Summary

This PR fixes a bug in the settings reloading logic to consider nested
configuration in a workspace.

fixes: #11766

## Test Plan


https://github.com/astral-sh/ruff/assets/67177269/69704b7b-44b9-4cc7-b5a7-376bf87c6ef4
2024-07-12 05:00:08 +00:00
Micha Reiser
bd01004a42 Use space separator before parenthesiszed expressions in comprehensions with leading comments. (#12282) 2024-07-11 22:38:12 +02:00
Gaétan Lepage
d0298dc26d Explicitly add schemars to ruff_python_ast Cargo.toml (#12275)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-11 06:46:34 +00:00
Jack Desert
bbb9fe1692 [Docs] Clear instruction for single quotes (linter and formatter) (#12015)
## Summary

In order to use single quotes with both the ruff linter and the ruff
formatter,
two different rules must be applied. This was not clear to me when 
internet searching "configure ruff single quotes" and it eventually
I filed this issue:

https://github.com/astral-sh/ruff/issues/12003
2024-07-10 16:29:29 +00:00
Alex Waygood
5b21922420 [red-knot] Add more stress tests for module resolver invalidation (#12272) 2024-07-10 14:34:06 +00:00
Micha Reiser
abcf07c8c5 Change File::touch_path to only take a SystemPath (#12273) 2024-07-10 12:15:14 +00:00
Alex Waygood
e8b5341c97 [red-knot] Rework module resolver tests (#12260) 2024-07-10 10:40:21 +00:00
Auguste Lalande
880c31d164 [flake8-async] Update ASYNC116 to match upstream (#12266)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-10 09:58:33 +02:00
Auguste Lalande
d365f1a648 [flake8-async] Update ASYNC115 to match upstream (#12262)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-10 09:43:11 +02:00
Micha Reiser
4cc7bc9d32 Use more threads when discovering python files (#12258) 2024-07-10 09:29:17 +02:00
Dhruv Manilawala
0bb2fc6eec Conside include, extend-include for the native server (#12252)
## Summary

This PR updates the native server to consider the `include` and
`extend-include` file resolver settings.

fixes: #12242 

## Test Plan

Note: Settings reloading doesn't work for nested configs which is fixed
in #12253 so the preview here only showcases root level config.

https://github.com/astral-sh/ruff/assets/67177269/e8969128-c175-4f98-8114-0d692b906cc8
2024-07-10 04:12:57 +00:00
Auguste Lalande
855d62cdde [flake8-async] Update ASYNC110 to match upstream (#12261)
## Summary

Update the name of `ASYNC110` to match
[upstream](https://flake8-async.readthedocs.io/en/latest/rules.html).

Also update to the functionality to match upstream by adding support for
`asyncio` and `anyio` (gated behind preview).

Part of https://github.com/astral-sh/ruff/issues/12039.

## Test Plan

Added tests for `asyncio` and `anyio`
2024-07-09 17:17:28 -07:00
Auguste Lalande
88abc6aed8 [flake8-async] Update ASYNC100 to match upstream (#12221)
## Summary

Update the name of `ASYNC100` to match
[upstream](https://flake8-async.readthedocs.io/en/latest/rules.html).

Also update to the functionality to match upstream by supporting
additional context managers from asyncio and anyio. Matching this
[list](https://flake8-async.readthedocs.io/en/latest/glossary.html#timeout-context).

Part of #12039.

## Test Plan

Added the new context managers to the fixture.
2024-07-09 17:55:18 +00:00
Alex Waygood
6fa4e32ad3 [red-knot] Use vendored typeshed stubs for stdlib module resolution (#12224) 2024-07-09 09:21:52 +00:00
Alex Waygood
000dabcd88 [red-knot] Allow module-resolution options to be specified via the CLI (#12246) 2024-07-09 09:16:28 +00:00
Micha Reiser
f8ff42a13d [red-knot] Prevent salsa cancellation from aborting the program (#12183) 2024-07-09 08:26:15 +00:00
Micha Reiser
b5834d57af [red-knot] Only store absolute paths in Files (#12215) 2024-07-09 09:52:13 +02:00
renovate[bot]
3d3ff10bb9 Update dependency react-resizable-panels to v2.0.20 (#12231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-09 07:26:08 +00:00
Micha Reiser
ac04380f36 [red-knot] Rename FileSystem to System (#12214) 2024-07-09 07:20:51 +00:00
Auguste Lalande
16a63c88cf [flake8-async] Update ASYNC109 to match upstream (#12236)
## Summary

Update the name of `ASYNC109` to match
[upstream](https://flake8-async.readthedocs.io/en/latest/rules.html).

Also update to the functionality to match upstream by supporting
additional context managers from `asyncio` and `anyio`. This doesn't
change any of the detection functionality, but recommends additional
context managers from `asyncio` and `anyio` depending on context.

Part of https://github.com/astral-sh/ruff/issues/12039.

## Test Plan

Added fixture for asyncio recommendation
2024-07-09 04:14:27 +00:00
Dani Bodor
10f07d88a2 Update help and documentation for --output-format to reflect "full" default (#12248)
fix #12247 

changed help to list "full" as the default for --output-format and
removed "text" as an option (as this is no longer supported).
2024-07-09 02:45:24 +00:00
Evan Rittenhouse
1e04bd0b73 Restrict fowarding newline argument in open() calls to Python versions >= 3.10 (#12244)
Fixes https://github.com/astral-sh/ruff/issues/12222
2024-07-09 02:43:31 +00:00
epenet
2041b0e5fb [flake8-return] Exempt properties from explicit return rule (RET501) (#12243)
First contribution - apologies if something is missing

Fixes #12197
2024-07-08 19:39:30 -07:00
Micha Reiser
bf3d903939 Warn about D203 formatter incompatibility (#12238) 2024-07-08 15:12:14 +02:00
Micha Reiser
64855c5f06 Remove default-run from 'red_knot' crate (#12241) 2024-07-08 09:31:45 +00:00
renovate[bot]
b5ab4ce293 Update pre-commit dependencies (#12232) 2024-07-08 01:51:24 +00:00
renovate[bot]
c396b9f08b Update cloudflare/wrangler-action action to v3.7.0 (#12235) 2024-07-07 21:41:24 -04:00
renovate[bot]
9ed3893e6d Update Rust crate ureq to v2.10.0 (#12234) 2024-07-07 21:41:18 -04:00
renovate[bot]
30c9604c1d Update NPM Development dependencies (#12233) 2024-07-07 21:41:09 -04:00
renovate[bot]
7e4a1c2b33 Update Rust crate syn to v2.0.69 (#12230) 2024-07-07 21:40:42 -04:00
renovate[bot]
e379160941 Update Rust crate serde_with to v3.8.3 (#12229) 2024-07-07 21:40:35 -04:00
renovate[bot]
38b503ebcc Update Rust crate serde_json to v1.0.120 (#12228) 2024-07-07 21:40:29 -04:00
renovate[bot]
dac476f2c0 Update Rust crate serde to v1.0.204 (#12227) 2024-07-07 21:40:20 -04:00
renovate[bot]
754e5d6a7d Update Rust crate imara-diff to v0.1.6 (#12226) 2024-07-07 21:40:03 -04:00
Alex Waygood
d9c15e7a12 [Ecosystem checks] trio has changed its default branch name to main (#12225)
Fixes CI errors seen in
https://github.com/astral-sh/ruff/pull/12224#issuecomment-2212594024

x-ref
17b3644f64
2024-07-07 23:37:27 +01:00
Trim21
757c75752e [flake8-bandit] fix S113 false positive for httpx without timeout argument (#12213)
## Summary

S113 exists because `requests` doesn't have a default timeout, so
request without timeout may hang indefinitely

> B113: Test for missing requests timeout
This plugin test checks for requests or httpx calls without a timeout
specified.
>
> Nearly all production code should use this parameter in nearly all
requests, **Failure to do so can cause your program to hang
indefinitely.**


But httpx has default timeout 5s, so S113 for httpx request without
`timeout` argument is a false positive, only valid case would be
`timeout=None`.

https://www.python-httpx.org/advanced/timeouts/

> HTTPX is careful to enforce timeouts everywhere by default.
>
> The default behavior is to raise a TimeoutException after 5 seconds of
network inactivity.


## Test Plan

snap updated
2024-07-06 14:08:40 -05:00
Micha Reiser
9d61727289 [red-knot] Exclude drop time in benchmark (#12218) 2024-07-06 17:35:00 +02:00
Alex Waygood
a62a432a48 [red-knot] Respect typeshed's VERSIONS file when resolving stdlib modules (#12141) 2024-07-05 22:43:31 +00:00
Charlie Marsh
8198723201 Move SELinux docs to example (#12211) 2024-07-05 20:42:43 +00:00
Maximilian Kolb
7df10ea3e9 Docs: Respect SELinux with podman for docker mount (#12102)
Tested on Fedora 40 with Podman 5.1.1 and ruff "0.5.0" and "latest".
source: https://unix.stackexchange.com/q/651198


## Error without fix

````
$ podman run --rm -it -v .:/io ghcr.io/astral-sh/ruff:latest check
error: Failed to initialize cache at /io/.ruff_cache: Permission denied (os error 13)
warning: Encountered error: Permission denied (os error 13)
All checks passed!

$ podman run --rm -it -v .:/io ghcr.io/astral-sh/ruff:latest format
error: Failed to initialize cache at /io/.ruff_cache: Permission denied (os error 13)
error: Encountered error: Permission denied (os error 13)
````

## Summary

Running ruff by using a docker container requires `:Z` when mounting the
current directory on Fedora with SELinux and Podman.

## Test Plan

````
$ podman run --rm -it -v .:/io:Z ghcr.io/astral-sh/ruff:latest check
$ podman run --rm -it -v .:/io:Z ghcr.io/astral-sh/ruff:0.5.0 check
````
2024-07-05 15:39:00 -05:00
Carl Meyer
0e44235981 [red-knot] intern types using Salsa (#12061)
Intern types using Salsa interning instead of in the `TypeInference`
result.

This eliminates the need for `TypingContext`, and also paves the way for
finer-grained type inference queries.
2024-07-05 12:16:37 -07:00
Dhruv Manilawala
7b50061b43 Fix eslint errors for playground source code (#12207)
Refer
https://github.com/astral-sh/ruff/actions/runs/9808907924/job/27086333001
2024-07-05 13:43:16 +00:00
Dhruv Manilawala
3a72400202 Rename publish workflow file extension (yaml -> yml) (#12206) 2024-07-05 13:12:49 +00:00
Dhruv Manilawala
1b3bff0330 Bump version to 0.5.1 (#12205) 2024-07-05 18:33:14 +05:30
Alex Waygood
0f6f73ecf3 [red-knot] Require that FileSystem objects implement Debug (#12204) 2024-07-05 12:53:30 +01:00
Dhruv Manilawala
7910beecc4 Consider the content of the new cells during notebook sync (#12203)
## Summary

This PR fixes the bug where the server was not considering the
`cells.structure.didOpen` field to sync up the new content of the newly
added cells.

The parameters corresponding to this request provides two fields to get
the newly added cells:
1. `cells.structure.array.cells`: This is a list of `NotebookCell` which
doesn't contain any cell content. The only useful information from this
array is the cell kind and the cell document URI which we use to
initialize the new cell in the index.
2. `cells.structure.didOpen`: This is a list of `TextDocumentItem` which
corresponds to the newly added cells. This actually contains the text
content and the version.

This wasn't a problem before because we initialize the cell with an
empty string and this isn't a problem when someone just creates an empty
cell. But, when someone copy-pastes a cell, the cell needs to be
initialized with the content.

fixes: #12201 

## Test Plan

First, let's see the panic in action:

1. Press <kbd>Esc</kbd> to allow using the keyboard to perform cell
actions (move around, copy, paste, etc.)
2. Copy the second cell with <kbd>c</kbd> key
3. Delete the second cell with <kbd>dd</kbd> key
4. Paste the copied cell with <kbd>p</kbd> key

You can see that the content isn't synced up because the `unused-import`
for `sys` is still being highlighted but it's being used in the second
cell. And, the hover isn't working either. Then, as I start editing the
second cell, it panics.


https://github.com/astral-sh/ruff/assets/67177269/fc58364c-c8fc-4c11-a917-71b6dd90c1ef

Now, here's the preview of the fixed version:


https://github.com/astral-sh/ruff/assets/67177269/207872dd-dca6-49ee-8b6e-80435c7ef22e
2024-07-05 17:10:00 +05:30
Dhruv Manilawala
f3ccd152e9 Revert "Remove --preview as a required argument for ruff server (#12053)" (#12196)
This reverts commit b28dc9ac14.

We're not ready to stabilize the server yet. There's some pending work
for the VS Code extension and documentation improvements.

This change is to unblock Ruff release.
2024-07-05 11:58:35 +05:30
Javier Kauer
1e07bfa373 [pycodestyle] Whitespace after decorator (E204) (#12140)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This is the implementation for the new rule of `pycodestyle (E204)`. It
follows the guidlines described in the contributing site, and as such it
has a new file named `whitespace_after_decorator.rs`, a new test file
called `E204.py`, and as such invokes the `function` in the `AST
statement checker` for functions and functions in classes. Linking #2402
because it has all the pycodestyle rules.

## Test Plan

<!-- How was it tested? -->
The file E204.py, has a `decorator` defined called wrapper, and this
decorator is used for 2 cases. The first one is when a `function` which
has a `decorator` is called in the file, and the second one is when
there is a `class` and 2 `methods` are defined for the `class` with a
`decorator` attached it.

Test file:

``` python
def foo(fun):
    def wrapper():
        print('before')
        fun()
        print('after')
    return wrapper

# No error
@foo
def bar():
    print('bar')

# E204
@ foo
def baz():
    print('baz')

class Test:
    # No error
    @foo
    def bar(self):
        print('bar')

    # E204
    @ foo
    def baz(self):
        print('baz')
```

I am still new to rust and any suggestion is appreciated. Specially with
the way im using native ruff utilities.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-07-04 23:31:03 +00:00
Mathieu Kniewallner
5e7ba05612 docs(*): fix a few typos, consistency issues and links (#12193)
## Summary

Fixes a few typos, consistency issues and dead links found across the
documentation.
2024-07-04 19:05:51 -04:00
Mathieu Kniewallner
d12570ea00 docs(options): fix some typos and improve consistency (#12191)
## Summary

Fixes a few typos and consistency issues in the "Settings"
documentation:
- use "Ruff" consistently in the few places where "ruff" is used
- use double quotes in the few places where single quotes are used
- add backticks around rule codes where they are currently missing
- update a few example values where they are the same as the defaults,
for consistency

2nd commit might be controversial, as there are many options mentioned
where we don't currently link to the documentation sections, so maybe
it's done on purpose, as this will also appear in the JSON schema where
it's not desirable? If that's the case, I can easily drop it.

## Test Plan

Local testing.
2024-07-04 19:05:03 -04:00
Mathieu Kniewallner
2f3264e148 fix(rules): skip dummy variables for PLR1704 (#12190)
## Summary

Resolves #12157.

## Test Plan

Snapshot tests.
2024-07-04 20:09:31 +00:00
Micha Reiser
e2e0889a30 [red-knot] Add very basic benchmark (#12182) 2024-07-04 15:29:00 +00:00
Micha Reiser
497fd4c505 Update code owners for red knot (#12187) 2024-07-04 12:14:24 +00:00
Dhruv Manilawala
6cdf3e7af8 Reorder installation section in README (#12177)
See https://github.com/astral-sh/ruff/pull/12163#issuecomment-2207016631
2024-07-04 14:11:54 +05:30
Micha Reiser
4d385b60c8 [red-knot] Migrate CLI to Salsa (#11972) 2024-07-04 07:23:45 +00:00
Micha Reiser
262053f85c [red-knot]: Implement HasTy for Alias (#11971) 2024-07-04 07:17:10 +00:00
Micha Reiser
3ce8b9fcae Make Definition a salsa-ingredient (#12151) 2024-07-04 06:46:08 +00:00
Dhruv Manilawala
e6e09ea93a Avoid syntax error notification for source code actions (#12148)
## Summary

This PR avoids the error notification if a user selects the source code
actions and there's a syntax error in the source.

Before https://github.com/astral-sh/ruff/pull/12134, the change would've
been different. But that PR disables generating fixes if there's a
syntax error. This means that we can return an empty map instead as
there won't be any fixes in the diagnostics returned by the `lint_fix`
function.

For reference, following are the screenshot as on `main` with the error:

**VS Code:**

<img width="1715" alt="Screenshot 2024-07-02 at 16 39 59"
src="https://github.com/astral-sh/ruff/assets/67177269/62f3e99b-0b0c-4608-84a2-26aeabcc6933">

**Neovim:**

<img width="1717" alt="Screenshot 2024-07-02 at 16 38 50"
src="https://github.com/astral-sh/ruff/assets/67177269/5d637c36-d7f8-4a3b-8011-9a89708919a8">

fixes: #11931

## Test Plan

Considering the following code snippet where there are two diagnostics
(syntax error and useless semicolon `E703`):
```py
x;

y =
```

### VS Code


https://github.com/astral-sh/ruff/assets/67177269/943537fc-ed8d-448d-8a36-1e34536c4f3e

### Neovim


https://github.com/astral-sh/ruff/assets/67177269/4e6f3372-6e5b-4380-8919-6221066efd5b
2024-07-04 09:37:16 +05:30
Dhruv Manilawala
d870720841 Fix replacement edit range computation (#12171)
## Summary

This PR fixes various bugs for computing the replacement range between
the original and modified source for the language server.

1. When finding the end offset of the source and modified range, we
should apply `zip` on the reversed iterator. The bug was that it was
reversing the already zipped iterator. The problem here is that the
length of both slices aren't going to be the same unless the source
wasn't modified at all. Refer to the [Rust
playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=44f860d31bd26456f3586b6ab530c22f)
where you can see this in action.
2. Skip the first line when computing the start offset because the first
line start value will always be 0 and the default value of the source /
modified range start is also 0. So, comparing 0 and 0 is not useful
which means we can skip the first value.
3. While iterating in the reverse direction, we should only stop if the
line start is strictly less than the source start i.e., we should use
`<` instead of `<=`.

fixes: #12128 

## Test Plan

Add test cases where the text is being inserted, deleted, and replaced
between the original and new source code, validate the replacement
ranges.
2024-07-04 09:24:07 +05:30
Mathieu Kniewallner
8210c1ed5b [flake8-bandit] Detect httpx for S113 (#12174)
## Summary

Bandit now also reports `B113` on `httpx`
(https://github.com/PyCQA/bandit/pull/1060). This PR implements the same
logic, to detect missing or `None` timeouts for `httpx` alongside
`requests`.

## Test Plan

Snapshot tests.
2024-07-03 19:26:55 -04:00
Charlie Marsh
a184f84f69 Upgrade cargo-dist to v0.18.0 (#12175)
## Summary

This enables us to get rid of `allow-dirty`!
2024-07-03 22:38:29 +00:00
Zanie Blue
c487b99e93 Add standalone installers to Ruff installation in README (#12163)
Note this is already included in our installation page at
`docs/installation.md`

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-07-03 13:39:58 -05:00
Thomas Faivre
24524771f2 Fix typo in CHANGELOG for misplaced-bare-raise URL (#12173)
Hi all!

## Summary

Fix a typo.

## Test Plan

URL was tested with curl.



Not much left to say, except that I originally saw this issue on the
blog post: https://astral.sh/blog/ruff-v0.5.0
Not sure how it is related to the CHANGELOG.md file, so the post might
need fixing as well.

Thanks for this incredible tool!

Signed-off-by: Thomas Faivre <thomas.faivre@6wind.com>
2024-07-03 13:39:33 -05:00
Micha Reiser
b950a6c389 Replace Mutex<RefCell> with Mutex in vendored file system" (#12170) 2024-07-03 15:12:13 +02:00
Zanie Blue
47eb6ee42b Fix cache key collisions for paths with separators (#12159)
Closes https://github.com/astral-sh/ruff/issues/12158

Hashing `Path` does not take into account path separators so `foo/bar`
is the same as `foobar` which is no good for our case. I'm guessing this
is an upstream bug, perhaps introduced by
45082b077b?
I'm investigating that further.
2024-07-03 07:36:46 -05:00
Zanie Blue
b4f7d5b2fb Fix latest version detection during Rooster invocations (#12162)
We no longer use the "v" prefix so Rooster detects the wrong version.
2024-07-03 07:35:34 -05:00
Zanie Blue
c13c60bc47 Update release script to match uv (#11496)
See https://github.com/astral-sh/uv/pull/3764

---------

Co-authored-by: T-256 <132141463+T-256@users.noreply.github.com>
2024-07-03 07:35:28 -05:00
Dhruv Manilawala
ee90017d3f Remove demisto/content from ecosystem checks (#12160)
## Summary

Follow-up to https://github.com/astral-sh/ruff/pull/12129 to remove the
`demisto/content` from ecosystem checks. The previous PR removed it from
the deprecated script which I didn't notice until recently.

## Test Plan

Ecosystem comment
2024-07-03 08:25:29 +00:00
Micha Reiser
adfd78e05a Correct parenthesized long nested-expressions example to match Ruff's output (#12153) 2024-07-03 10:03:08 +02:00
Alex Waygood
7c8112614a Remove use of deprecated E999 from the fuzz-parser script (#12150) 2024-07-02 14:18:25 +01:00
Dhruv Manilawala
8f40928534 Enable token-based rules on source with syntax errors (#11950)
## Summary

This PR updates the linter, specifically the token-based rules, to work
on the tokens that come after a syntax error.

For context, the token-based rules only diagnose the tokens up to the
first lexical error. This PR builds up an error resilience by
introducing a `TokenIterWithContext` which updates the `nesting` level
and tries to reflect it with what the lexer is seeing. This isn't 100%
accurate because if the parser recovered from an unclosed parenthesis in
the middle of the line, the context won't reduce the nesting level until
it sees the newline token at the end of the line.

resolves: #11915

## Test Plan

* Add test cases for a bunch of rules that are affected by this change.
* Run the fuzzer for a long time, making sure to fix any other bugs.
2024-07-02 08:57:46 +00:00
Dhruv Manilawala
88a4cc41f7 Disable auto-fix when source has syntax errors (#12134)
## Summary

This PR updates Ruff to **not** generate auto-fixes if the source code
contains syntax errors as determined by the parser.

The main motivation behind this is to avoid infinite autofix loop when
the token-based rules are run over any source with syntax errors in
#11950.

Although even after this, it's not certain that there won't be an
infinite autofix loop because the logic might be incorrect. For example,
https://github.com/astral-sh/ruff/issues/12094 and
https://github.com/astral-sh/ruff/pull/12136.

This requires updating the test infrastructure to not validate for fix
availability status when the source contained syntax errors. This is
required because otherwise the fuzzer might fail as it uses the test
function to run the linter and validate the source code.

resolves: #11455 

## Test Plan

`cargo insta test`
2024-07-02 14:22:51 +05:30
Micha Reiser
dcb9523b1e Address review feedback from 11963 (#12145) 2024-07-02 09:05:55 +02:00
Micha Reiser
25080acb7a [red-knot] Introduce ExpressionNodeKey to improve typing of expression_map (#12142) 2024-07-01 16:15:53 +02:00
Micha Reiser
228b1c4235 [red-knot] Remove Scope::name (#12137) 2024-07-01 15:55:50 +02:00
Micha Reiser
955138b74a Refactor ast_ids traits to take ScopeId instead of VfsFile plus FileScopeId. (#12139) 2024-07-01 15:50:07 +02:00
Dhruv Manilawala
5677614079 Use char-wise width instead of str-width (#12135)
## Summary

This PR updates various references in the linter to compute the
line-width for summing the width of each `char` in a `str` instead of
computing the width of the `str` itself.

Refer to #12133 for more details.

fixes: #12130 

## Test Plan

Add a file with null (`\0`) character which is zero-width. Run this test
case on `main` to make sure it panics and switch over to this branch to
make sure it doesn't panic now.
2024-07-01 18:56:27 +05:30
Micha Reiser
37f260b5af Introduce HasTy trait and SemanticModel facade (#11963) 2024-07-01 14:48:27 +02:00
Dhruv Manilawala
3f25561511 Avoid E275 if keyword followed by comma (#12136)
## Summary

Use the following to reproduce this:
```console
$ cargo run -- check --select=E275,E203 --preview --no-cache ~/playground/ruff/src/play.py --fix
debug error: Failed to converge after 100 iterations in `/Users/dhruv/playground/ruff/src/play.py` with rule codes E275:---
yield,x

---
/Users/dhruv/playground/ruff/src/play.py:1:1: E275 Missing whitespace after keyword
  |
1 | yield,x
  | ^^^^^ E275
  |
  = help: Added missing whitespace after keyword

Found 101 errors (100 fixed, 1 remaining).
[*] 1 fixable with the `--fix` option.
```

## Test Plan

Add a test case and run `cargo insta test`.
2024-07-01 18:04:23 +05:30
Charlie Marsh
eaf33d85ed Remove demisto/content from ecosystem checks (#12129)
## Summary

Unfortunately `demisto/content` uses an explicit `select` for `E999`, so
it will _always_ fail in preview. And they're on a fairly old version.
I'd like to keep checking it, but seems easiest for now to just disable
it.

In response, I've added a few new repos.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-07-01 12:20:13 +00:00
renovate[bot]
aaa6cabf3a Update Rust crate dashmap to v6 (#12126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-01 08:48:26 +00:00
Micha Reiser
9a4d9072c1 Update salsa (#12132) 2024-07-01 08:33:04 +00:00
Micha Reiser
4cb6a09fc0 Use CompactString for ModuleName (#12131) 2024-07-01 10:22:34 +02:00
Micha Reiser
5109b50bb3 Use CompactString for Identifier (#12101) 2024-07-01 10:06:02 +02:00
github-actions[bot]
db6ee74cbe Sync vendored typeshed stubs (#12116) 2024-07-01 07:07:45 +01:00
Tom Kuson
d1aeadc009 [pytest] Reverse PT001 and PT0023 defaults (#12106)
## Summary

This patch inverts the defaults for
[pytest-fixture-incorrect-parentheses-style
(PT001)](https://docs.astral.sh/ruff/rules/pytest-fixture-incorrect-parentheses-style/)
and [pytest-incorrect-mark-parentheses-style
(PT003)](https://docs.astral.sh/ruff/rules/pytest-incorrect-mark-parentheses-style/)
to prefer dropping superfluous parentheses.

Presently, Ruff defaults to adding superfluous parentheses on pytest
mark and fixture decorators for documented purpose of consistency; for
example,

```diff
 import pytest


-@pytest.mark.foo
+@pytest.mark.foo()
 def test_bar(): ...
```

This behaviour is counter to the official pytest recommendation and
diverges from the flake8-pytest-style plugin as of version 2.0.0 (see
https://github.com/m-burst/flake8-pytest-style/issues/272). Seeing as
either default satisfies the documented benefit of consistency across a
codebase, it makes sense to change the behaviour to be consistent with
pytest and the flake8 plugin as well.

This change is breaking, so is gated behind preview (at least under my
understanding of Ruff versioning). The implementation of this gating
feature is a bit hacky, but seemed to be the least disruptive solution
without performing invasive surgery on the `#[option()]` macro.

Related to #8796.

### Caveat

Whilst updating the documentation, I sought to reference the pytest
recommendation to drop superfluous parentheses, but couldn't find any
official instruction beyond it being a revealed preference within the
pytest documentation code examples (as well as the linked issues from a
core pytest developer). Thus, the wording of the preference is
deliberately timid; it's to cohere with pytest rather than follow an
explicit guidance.

## Test Plan

`cargo nextest run`

I also ran

```sh
cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT001.py --no-cache --diff --select PT001
```

and compared against it with `--preview` to verify that the default does
change under preview (I also repeated this with `echo
'[tool.ruff]\npreview = true' > pyproject.toml` to verify that it works
with a configuration file).

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-07-01 02:06:11 +00:00
Tom Kuson
d80a9d9ce9 [flake8-bugbear] Implement mutable-contextvar-default (B039) (#12113)
## Summary

Implement mutable-contextvar-default (B039) which was added to
flake8-bugbear in https://github.com/PyCQA/flake8-bugbear/pull/476.

This rule is similar to [mutable-argument-default
(B006)](https://docs.astral.sh/ruff/rules/mutable-argument-default) and
[function-call-in-default-argument
(B008)](https://docs.astral.sh/ruff/rules/function-call-in-default-argument),
except that it checks the `default` keyword argument to
`contextvars.ContextVar`.

```
B039.py:19:26: B039 Do not use mutable data structures for ContextVar defaults
   |
18 | # Bad
19 | ContextVar("cv", default=[])
   |                          ^^ B039
20 | ContextVar("cv", default={})
21 | ContextVar("cv", default=list())
   |
   = help: Replace with `None`; initialize with `.set()` after checking for `None`
```

In the upstream flake8-plugin, this rule is written expressly as a
corollary to B008 and shares much of its logic. Likewise, this
implementation reuses the logic of the Ruff implementation of B008,
namely


f765d19402/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs (L104-L106)

and 


f765d19402/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs (L106)

Thus, this rule deliberately replicates B006's and B008's heuristics.
For example, this rule assumes that all functions are mutable unless
otherwise qualified. If improvements are to be made to B039 heuristics,
they should probably be made to B006 and B008 as well (whilst trying to
match the upstream implementation).

This rule does not have an autofix as it is unknown where the ContextVar
next used (and it might not be within the same file).

Closes #12054

## Test Plan

`cargo nextest run`
2024-07-01 01:55:49 +00:00
renovate[bot]
85ede4a88c Update docker/build-push-action action to v6 (#12127) 2024-06-30 21:25:24 -04:00
renovate[bot]
d2fefc8bf3 Update NPM Development dependencies (#12122) 2024-06-30 21:21:20 -04:00
renovate[bot]
5fd3f43de1 Update pre-commit hook astral-sh/ruff-pre-commit to v0.5.0 (#12125) 2024-06-30 21:21:03 -04:00
renovate[bot]
ab372f5f48 Update Rust crate uuid to v1.9.1 (#12124) 2024-06-30 21:20:48 -04:00
renovate[bot]
6a8a7b65e9 Update Rust crate bitflags to v2.6.0 (#12123) 2024-06-30 21:20:17 -04:00
renovate[bot]
211cafc571 Update Rust crate log to v0.4.22 (#12118) 2024-06-30 21:19:50 -04:00
renovate[bot]
0b1b94567a Update Rust crate serde_with to v3.8.2 (#12121) 2024-06-30 21:19:33 -04:00
renovate[bot]
168112d343 Update Rust crate serde_json to v1.0.119 (#12120) 2024-06-30 21:19:10 -04:00
renovate[bot]
a5355084b5 Update Rust crate matchit to v0.8.3 (#12119) 2024-06-30 21:18:51 -04:00
renovate[bot]
deedb29e75 Update Rust crate clap to v4.5.8 (#12117) 2024-06-30 21:18:21 -04:00
Micha Reiser
f765d19402 Mention that Cursor is based on rustc's implementation. (#12109) 2024-06-30 16:53:25 +01:00
Gilles Peiffer
d1079680bb [pylint] Add fix for duplicate-bases (PLE0241) (#12105)
## Summary

This adds a fix for the `duplicate-bases` rule that removes the
duplicate base from the class definition.

## Test Plan

`cargo nextest run duplicate_bases`, `cargo insta review`.
2024-06-29 17:48:24 +00:00
Micha Reiser
da78de0439 Remove allcation in parse_identifier (#12103) 2024-06-29 15:00:24 +02:00
5959 changed files with 246782 additions and 77357 deletions

View File

@@ -20,7 +20,7 @@
"extensions": [
"ms-python.python",
"rust-lang.rust-analyzer",
"serayuzgur.crates",
"fill-labs.dependi",
"tamasfe.even-better-toml",
"Swellaby.vscode-rust-test-adapter",
"charliermarsh.ruff"

View File

@@ -17,4 +17,7 @@ indent_size = 4
trim_trailing_whitespace = false
[*.md]
max_line_length = 100
max_line_length = 100
[*.toml]
indent_size = 4

4
.gitattributes vendored
View File

@@ -14,5 +14,7 @@ crates/ruff_python_parser/resources/invalid/re_lex_logical_token_mac_eol.py text
crates/ruff_python_parser/resources/inline linguist-generated=true
ruff.schema.json linguist-generated=true text=auto eol=lf
ruff.schema.json -diff linguist-generated=true text=auto eol=lf
crates/ruff_python_ast/src/generated.rs -diff linguist-generated=true text=auto eol=lf
crates/ruff_python_formatter/src/generated.rs -diff linguist-generated=true text=auto eol=lf
*.md.snap linguist-language=Markdown

9
.github/CODEOWNERS vendored
View File

@@ -9,12 +9,15 @@
/crates/ruff_formatter/ @MichaReiser
/crates/ruff_python_formatter/ @MichaReiser
/crates/ruff_python_parser/ @MichaReiser @dhruvmanila
/crates/ruff_annotate_snippets/ @BurntSushi
# flake8-pyi
/crates/ruff_linter/src/rules/flake8_pyi/ @AlexWaygood
# Script for fuzzing the parser
/scripts/fuzz-parser/ @AlexWaygood
# Script for fuzzing the parser/red-knot etc.
/python/py-fuzzer/ @AlexWaygood
# red-knot
/crates/red_knot/ @carljm @MichaReiser
/crates/red_knot* @carljm @MichaReiser @AlexWaygood @sharkdp
/crates/ruff_db/ @carljm @MichaReiser @AlexWaygood @sharkdp
/scripts/knot_benchmark/ @carljm @MichaReiser @AlexWaygood @sharkdp

View File

@@ -1,12 +0,0 @@
<!--
Thank you for taking the time to report an issue! We're glad to have you involved with Ruff.
If you're filing a bug report, please consider including the following information:
* List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
e.g. "RUF001", "unused variable", "Jupyter notebook"
* A minimal code snippet that reproduces the bug.
* The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
* The current Ruff settings (any relevant sections from your `pyproject.toml`).
* The current Ruff version (`ruff --version`).
-->

10
.github/actionlint.yaml vendored Normal file
View File

@@ -0,0 +1,10 @@
# Configuration for the actionlint tool, which we run via pre-commit
# to verify the correctness of the syntax in our GitHub Actions workflows.
self-hosted-runner:
# Various runners we use that aren't recognized out-of-the-box by actionlint:
labels:
- depot-ubuntu-latest-8
- depot-ubuntu-22.04-16
- github-windows-2025-x86_64-8
- github-windows-2025-x86_64-16

View File

@@ -8,15 +8,32 @@
semanticCommits: "disabled",
separateMajorMinor: false,
prHourlyLimit: 10,
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "npm"],
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "pip_requirements", "npm"],
cargo: {
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
rangeStrategy: "update-lockfile",
},
pep621: {
// The default for this package manager is to only search for `pyproject.toml` files
// found at the repository root: https://docs.renovatebot.com/modules/manager/pep621/#file-matching
fileMatch: ["^(python|scripts)/.*pyproject\\.toml$"],
},
pip_requirements: {
// The default for this package manager is to run on all requirements.txt files:
// https://docs.renovatebot.com/modules/manager/pip_requirements/#file-matching
// `fileMatch` doesn't work for excluding files; to exclude `requirements.txt` files
// outside the `doc/` directory, we instead have to use `ignorePaths`. Unlike `fileMatch`,
// which takes a regex string, `ignorePaths` takes a glob string, so we have to use
// a "negative glob pattern".
// See:
// - https://docs.renovatebot.com/modules/manager/#ignoring-files-that-match-the-default-filematch
// - https://docs.renovatebot.com/configuration-options/#ignorepaths
// - https://docs.renovatebot.com/string-pattern-matching/#negative-matching
ignorePaths: ["!docs/requirements*.txt"]
},
npm: {
// The default for this package manager is to only search for `package.json` files
// found at the repository root: https://docs.renovatebot.com/modules/manager/npm/#file-matching
fileMatch: ["^playground/.*package\\.json$"],
},
"pre-commit": {
@@ -28,7 +45,7 @@
groupName: "Artifact GitHub Actions dependencies",
matchManagers: ["github-actions"],
matchDatasources: ["gitea-tags", "github-tags"],
matchPackagePatterns: ["actions/.*-artifact"],
matchPackageNames: ["actions/.*-artifact"],
description: "Weekly update of artifact-related GitHub Actions dependencies",
},
{
@@ -44,10 +61,18 @@
{
// Disable updates of `zip-rs`; intentionally pinned for now due to ownership change
// See: https://github.com/astral-sh/uv/issues/3642
matchPackagePatterns: ["zip"],
matchPackageNames: ["zip"],
matchManagers: ["cargo"],
enabled: false,
},
{
// `mkdocs-material` requires a manual update to keep the version in sync
// with `mkdocs-material-insider`.
// See: https://squidfunk.github.io/mkdocs-material/insiders/upgrade/
matchManagers: ["pip_requirements"],
matchPackageNames: ["mkdocs-material"],
enabled: false,
},
{
groupName: "pre-commit dependencies",
matchManagers: ["pre-commit"],
@@ -62,13 +87,13 @@
{
groupName: "Monaco",
matchManagers: ["npm"],
matchPackagePatterns: ["monaco"],
matchPackageNames: ["monaco"],
description: "Weekly update of the Monaco editor",
},
{
groupName: "strum",
matchManagers: ["cargo"],
matchPackagePatterns: ["strum"],
matchPackageNames: ["strum"],
description: "Weekly update of strum dependencies",
},
{

View File

@@ -40,6 +40,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -52,9 +53,9 @@ jobs:
args: --out dist
- name: "Test sdist"
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
pip install dist/"${PACKAGE_NAME}"-*.tar.gz --force-reinstall
"${MODULE_NAME}" --help
python -m "${MODULE_NAME}" --help
- name: "Upload sdist"
uses: actions/upload-artifact@v4
with:
@@ -63,11 +64,12 @@ jobs:
macos-x86_64:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: macos-12
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -109,6 +111,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -122,7 +125,7 @@ jobs:
args: --release --locked --out dist
- name: "Test wheel - aarch64"
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
pip install dist/"${PACKAGE_NAME}"-*.whl --force-reinstall
ruff --help
python -m ruff --help
- name: "Upload wheels"
@@ -164,6 +167,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -182,9 +186,9 @@ jobs:
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
shell: bash
run: |
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
python -m pip install dist/"${PACKAGE_NAME}"-*.whl --force-reinstall
"${MODULE_NAME}" --help
python -m "${MODULE_NAME}" --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
@@ -216,6 +220,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -231,9 +236,9 @@ jobs:
- name: "Test wheel"
if: ${{ startsWith(matrix.target, 'x86_64') }}
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
pip install dist/"${PACKAGE_NAME}"-*.whl --force-reinstall
"${MODULE_NAME}" --help
python -m "${MODULE_NAME}" --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
@@ -290,6 +295,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -354,6 +360,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -419,6 +426,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

View File

@@ -17,16 +17,26 @@ on:
paths:
- .github/workflows/build-docker.yml
env:
RUFF_BASE_IMG: ghcr.io/${{ github.repository_owner }}/ruff
jobs:
docker-publish:
name: Build Docker image (ghcr.io/astral-sh/ruff)
docker-build:
name: Build Docker image (ghcr.io/astral-sh/ruff) for ${{ matrix.platform }}
runs-on: ubuntu-latest
environment:
name: release
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- uses: docker/setup-buildx-action@v3
@@ -36,33 +46,254 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/astral-sh/ruff
- name: Check tag consistency
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
env:
TAG: ${{ inputs.plan != '' && fromJson(inputs.plan).announcement_tag || 'dry-run' }}
run: |
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
if [ "${{ fromJson(inputs.plan).announcement_tag }}" != "${version}" ]; then
if [ "${TAG}" != "${version}" ]; then
echo "The input tag does not match the version from pyproject.toml:" >&2
echo "${{ fromJson(inputs.plan).announcement_tag }}" >&2
echo "${TAG}" >&2
echo "${version}" >&2
exit 1
else
echo "Releasing ${version}"
fi
- name: "Build and push Docker image"
uses: docker/build-push-action@v5
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.RUFF_BASE_IMG }}
# Defining this makes sure the org.opencontainers.image.version OCI label becomes the actual release version and not the branch name
tags: |
type=raw,value=dry-run,enable=${{ inputs.plan == '' || fromJson(inputs.plan).announcement_tag_is_implicit }}
type=pep440,pattern={{ version }},value=${{ inputs.plan != '' && fromJson(inputs.plan).announcement_tag || 'dry-run' }},enable=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
- name: Normalize Platform Pair (replace / with -)
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_TUPLE=${platform//\//-}" >> "$GITHUB_ENV"
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
cache-from: type=gha,scope=ruff-${{ env.PLATFORM_TUPLE }}
cache-to: type=gha,mode=min,scope=ruff-${{ env.PLATFORM_TUPLE }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.RUFF_BASE_IMG }},push-by-digest=true,name-canonical=true,push=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
- name: Export digests
env:
digest: ${{ steps.build.outputs.digest }}
run: |
mkdir -p /tmp/digests
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digests
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_TUPLE }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
docker-publish:
name: Publish Docker image (ghcr.io/astral-sh/ruff)
runs-on: ubuntu-latest
environment:
name: release
needs:
- docker-build
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.RUFF_BASE_IMG }}
# Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version
tags: |
type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }}
type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }}
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
- name: Create manifest list and push
working-directory: /tmp/digests
# The jq command expands the docker/metadata json "tags" array entry to `-t tag1 -t tag2 ...` for each tag in the array
# The printf will expand the base image with the `<RUFF_BASE_IMG>@sha256:<sha256> ...` for each sha256 in the directory
# The final command becomes `docker buildx imagetools create -t tag1 -t tag2 ... <RUFF_BASE_IMG>@sha256:<sha256_1> <RUFF_BASE_IMG>@sha256:<sha256_2> ...`
run: |
# shellcheck disable=SC2046
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf "${RUFF_BASE_IMG}@sha256:%s " *)
docker-publish-extra:
name: Publish additional Docker image based on ${{ matrix.image-mapping }}
runs-on: ubuntu-latest
environment:
name: release
needs:
- docker-publish
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
strategy:
fail-fast: false
matrix:
# Mapping of base image followed by a comma followed by one or more base tags (comma separated)
# Note, org.opencontainers.image.version label will use the first base tag (use the most specific tag first)
image-mapping:
- alpine:3.20,alpine3.20,alpine
- debian:bookworm-slim,bookworm-slim,debian-slim
- buildpack-deps:bookworm,bookworm,debian
steps:
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate Dynamic Dockerfile Tags
shell: bash
env:
TAG_VALUE: ${{ fromJson(inputs.plan).announcement_tag }}
run: |
set -euo pipefail
# Extract the image and tags from the matrix variable
IFS=',' read -r BASE_IMAGE BASE_TAGS <<< "${{ matrix.image-mapping }}"
# Generate Dockerfile content
cat <<EOF > Dockerfile
FROM ${BASE_IMAGE}
COPY --from=${RUFF_BASE_IMG}:latest /ruff /usr/local/bin/ruff
ENTRYPOINT []
CMD ["/usr/local/bin/ruff"]
EOF
# Initialize a variable to store all tag docker metadata patterns
TAG_PATTERNS=""
# Loop through all base tags and append its docker metadata pattern to the list
# Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version
IFS=','; for TAG in ${BASE_TAGS}; do
TAG_PATTERNS="${TAG_PATTERNS}type=pep440,pattern={{ version }},suffix=-${TAG},value=${TAG_VALUE}\n"
TAG_PATTERNS="${TAG_PATTERNS}type=pep440,pattern={{ major }}.{{ minor }},suffix=-${TAG},value=${TAG_VALUE}\n"
TAG_PATTERNS="${TAG_PATTERNS}type=raw,value=${TAG}\n"
done
# Remove the trailing newline from the pattern list
TAG_PATTERNS="${TAG_PATTERNS%\\n}"
# Export image cache name
echo "IMAGE_REF=${BASE_IMAGE//:/-}" >> "$GITHUB_ENV"
# Export tag patterns using the multiline env var syntax
{
echo "TAG_PATTERNS<<EOF"
echo -e "${TAG_PATTERNS}"
echo EOF
} >> "$GITHUB_ENV"
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
# ghcr.io prefers index level annotations
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
with:
images: ${{ env.RUFF_BASE_IMG }}
flavor: |
latest=false
tags: |
${{ env.TAG_PATTERNS }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
# Reuse the builder
cache-from: type=gha
cache-to: type=gha,mode=max
push: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
tags: ghcr.io/astral-sh/ruff:latest,ghcr.io/astral-sh/ruff:${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || 'dry-run' }}
# We do not really need to cache here as the Dockerfile is tiny
#cache-from: type=gha,scope=ruff-${{ env.IMAGE_REF }}
#cache-to: type=gha,mode=min,scope=ruff-${{ env.IMAGE_REF }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
# This is effectively a duplicate of `docker-publish` to make https://github.com/astral-sh/ruff/pkgs/container/ruff
# show the ruff base image first since GitHub always shows the last updated image digests
# This works by annotating the original digests (previously non-annotated) which triggers an update to ghcr.io
docker-republish:
name: Annotate Docker image (ghcr.io/astral-sh/ruff)
runs-on: ubuntu-latest
environment:
name: release
needs:
- docker-publish-extra
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
with:
images: ${{ env.RUFF_BASE_IMG }}
# Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version
tags: |
type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }}
type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }}
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
- name: Create manifest list and push
working-directory: /tmp/digests
# The readarray part is used to make sure the quoting and special characters are preserved on expansion (e.g. spaces)
# The jq command expands the docker/metadata json "tags" array entry to `-t tag1 -t tag2 ...` for each tag in the array
# The printf will expand the base image with the `<RUFF_BASE_IMG>@sha256:<sha256> ...` for each sha256 in the directory
# The final command becomes `docker buildx imagetools create -t tag1 -t tag2 ... <RUFF_BASE_IMG>@sha256:<sha256_1> <RUFF_BASE_IMG>@sha256:<sha256_2> ...`
run: |
readarray -t lines <<< "$DOCKER_METADATA_OUTPUT_ANNOTATIONS"; annotations=(); for line in "${lines[@]}"; do annotations+=(--annotation "$line"); done
# shellcheck disable=SC2046
docker buildx imagetools create \
"${annotations[@]}" \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf "${RUFF_BASE_IMG}@sha256:%s " *)

View File

@@ -16,7 +16,7 @@ env:
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
PACKAGE_NAME: ruff
PYTHON_VERSION: "3.11"
PYTHON_VERSION: "3.12"
jobs:
determine_changes:
@@ -32,12 +32,15 @@ jobs:
# Flag that is raised when any code is changed
# This is superset of the linter and formatter
code: ${{ steps.changed.outputs.code_any_changed }}
# Flag that is raised when any code that affects the fuzzer is changed
fuzz: ${{ steps.changed.outputs.fuzz_any_changed }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: tj-actions/changed-files@v44
- uses: tj-actions/changed-files@v45
id: changed
with:
files_yaml: |
@@ -49,7 +52,7 @@ jobs:
- crates/ruff_text_size/**
- crates/ruff_python_ast/**
- crates/ruff_python_parser/**
- scripts/fuzz-parser/**
- python/py-fuzzer/**
- .github/workflows/ci.yaml
linter:
@@ -79,9 +82,15 @@ jobs:
- python/**
- .github/workflows/ci.yaml
fuzz:
- fuzz/Cargo.toml
- fuzz/Cargo.lock
- fuzz/fuzz_targets/**
code:
- "**/*"
- "!**/*.md"
- "crates/red_knot_python_semantic/resources/mdtest/**/*.md"
- "!docs/**"
- "!assets/**"
@@ -91,6 +100,8 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup component add rustfmt
- run: cargo fmt --all --check
@@ -103,6 +114,8 @@ jobs:
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: |
rustup component add clippy
@@ -111,16 +124,18 @@ jobs:
- name: "Clippy"
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
- name: "Clippy (wasm)"
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
run: cargo clippy -p ruff_wasm -p red_knot_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
cargo-test-linux:
name: "cargo test (linux)"
runs-on: ubuntu-latest
runs-on: depot-ubuntu-22.04-16
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
@@ -142,6 +157,13 @@ jobs:
# Check for broken links in the documentation.
- run: cargo doc --all --no-deps
env:
RUSTDOCFLAGS: "-D warnings"
# Use --document-private-items so that all our doc comments are kept in
# sync, not just public items. Eventually we should do this for all
# crates; for now add crates here as they are warning-clean to prevent
# regression.
- run: cargo doc --no-deps -p red_knot_python_semantic -p red_knot -p red_knot_test -p ruff_db --document-private-items
env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
@@ -150,14 +172,45 @@ jobs:
name: ruff
path: target/debug/ruff
cargo-test-windows:
name: "cargo test (windows)"
runs-on: windows-latest
cargo-test-linux-release:
name: "cargo test (linux, release)"
runs-on: depot-ubuntu-22.04-16
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@v2
with:
tool: cargo-insta
- uses: Swatinem/rust-cache@v2
- name: "Run tests"
shell: bash
env:
NEXTEST_PROFILE: "ci"
run: cargo insta test --release --all-features --unreferenced reject --test-runner nextest
cargo-test-windows:
name: "cargo test (windows)"
runs-on: github-windows-2025-x86_64-16
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo nextest"
@@ -182,28 +235,37 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-pack-action@v0.4.0
with:
version: v0.13.1
- uses: Swatinem/rust-cache@v2
- name: "Run wasm-pack"
- name: "Test ruff_wasm"
run: |
cd crates/ruff_wasm
wasm-pack test --node
- name: "Test red_knot_wasm"
run: |
cd crates/red_knot_wasm
wasm-pack test --node
cargo-build-release:
name: "cargo build (release)"
runs-on: macos-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
if: ${{ github.ref == 'refs/heads/main' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
@@ -220,13 +282,17 @@ jobs:
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: SebRollen/toml-action@v1.2.0
id: msrv
with:
file: "Cargo.toml"
field: "workspace.package.rust-version"
- name: "Install Rust toolchain"
run: rustup default ${{ steps.msrv.outputs.value }}
env:
MSRV: ${{ steps.msrv.outputs.value }}
run: rustup default "${MSRV}"
- name: "Install mold"
uses: rui314/setup-mold@v1
- name: "Install cargo nextest"
@@ -242,16 +308,19 @@ jobs:
shell: bash
env:
NEXTEST_PROFILE: "ci"
run: cargo +${{ steps.msrv.outputs.value }} insta test --all-features --unreferenced reject --test-runner nextest
MSRV: ${{ steps.msrv.outputs.value }}
run: cargo "+${MSRV}" insta test --all-features --unreferenced reject --test-runner nextest
cargo-fuzz:
name: "cargo fuzz"
cargo-fuzz-build:
name: "cargo fuzz build"
runs-on: ubuntu-latest
needs: determine_changes
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
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@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
@@ -267,7 +336,7 @@ jobs:
- run: cargo fuzz build -s none
fuzz-parser:
name: "Fuzz the parser"
name: "fuzz parser"
runs-on: ubuntu-latest
needs:
- cargo-test-linux
@@ -278,13 +347,9 @@ jobs:
FORCE_COLOR: 1
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install Python requirements
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
persist-credentials: false
- uses: astral-sh/setup-uv@v5
- uses: actions/download-artifact@v4
name: Download Ruff binary to test
id: download-cached-binary
@@ -292,11 +357,21 @@ jobs:
name: ruff
path: ruff-to-test
- name: Fuzz
env:
DOWNLOAD_PATH: ${{ steps.download-cached-binary.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ${{ steps.download-cached-binary.outputs.download-path }}/ruff
chmod +x "${DOWNLOAD_PATH}/ruff"
python scripts/fuzz-parser/fuzz.py 0-500 --test-executable ${{ steps.download-cached-binary.outputs.download-path }}/ruff
(
uvx \
--python="${PYTHON_VERSION}" \
--from=./python/py-fuzzer \
fuzz \
--test-executable="${DOWNLOAD_PATH}/ruff" \
--bin=ruff \
0-500
)
scripts:
name: "test scripts"
@@ -306,10 +381,18 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup component add rustfmt
- uses: Swatinem/rust-cache@v2
- run: ./scripts/add_rule.py --name DoTheThing --prefix PL --code C0999 --linter pylint
# Run all code generation scripts, and verify that the current output is
# already checked into git.
- run: python crates/ruff_python_ast/generate.py
- run: python crates/ruff_python_formatter/generate.py
- run: test -z "$(git status --porcelain)"
# Verify that adding a plugin or rule produces clean code.
- run: ./scripts/add_rule.py --name DoTheThing --prefix F --code 999 --linter pyflakes
- run: cargo check
- run: cargo fmt --all --check
- run: |
@@ -320,7 +403,7 @@ jobs:
ecosystem:
name: "ecosystem"
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest-8
needs:
- cargo-test-linux
- determine_changes
@@ -330,6 +413,8 @@ jobs:
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -341,7 +426,7 @@ jobs:
name: ruff
path: target/debug
- uses: dawidd6/action-download-artifact@v6
- uses: dawidd6/action-download-artifact@v7
name: Download baseline Ruff binary
with:
name: ruff
@@ -355,64 +440,72 @@ jobs:
- name: Run `ruff check` stable ecosystem check
if: ${{ needs.determine_changes.outputs.linter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem check ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff --cache ./checkouts --output-format markdown | tee ecosystem-result-check-stable
ruff-ecosystem check ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown | tee ecosystem-result-check-stable
cat ecosystem-result-check-stable > $GITHUB_STEP_SUMMARY
cat ecosystem-result-check-stable > "$GITHUB_STEP_SUMMARY"
echo "### Linter (stable)" > ecosystem-result
cat ecosystem-result-check-stable >> ecosystem-result
echo "" >> ecosystem-result
- name: Run `ruff check` preview ecosystem check
if: ${{ needs.determine_changes.outputs.linter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem check ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-check-preview
ruff-ecosystem check ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-check-preview
cat ecosystem-result-check-preview > $GITHUB_STEP_SUMMARY
cat ecosystem-result-check-preview > "$GITHUB_STEP_SUMMARY"
echo "### Linter (preview)" >> ecosystem-result
cat ecosystem-result-check-preview >> ecosystem-result
echo "" >> ecosystem-result
- name: Run `ruff format` stable ecosystem check
if: ${{ needs.determine_changes.outputs.formatter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem format ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff --cache ./checkouts --output-format markdown | tee ecosystem-result-format-stable
ruff-ecosystem format ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown | tee ecosystem-result-format-stable
cat ecosystem-result-format-stable > $GITHUB_STEP_SUMMARY
cat ecosystem-result-format-stable > "$GITHUB_STEP_SUMMARY"
echo "### Formatter (stable)" >> ecosystem-result
cat ecosystem-result-format-stable >> ecosystem-result
echo "" >> ecosystem-result
- name: Run `ruff format` preview ecosystem check
if: ${{ needs.determine_changes.outputs.formatter == 'true' }}
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
chmod +x ./ruff "${DOWNLOAD_PATH}/ruff"
# Set pipefail to avoid hiding errors with tee
set -eo pipefail
ruff-ecosystem format ./ruff ${{ steps.ruff-target.outputs.download-path }}/ruff --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-format-preview
ruff-ecosystem format ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-format-preview
cat ecosystem-result-format-preview > $GITHUB_STEP_SUMMARY
cat ecosystem-result-format-preview > "$GITHUB_STEP_SUMMARY"
echo "### Formatter (preview)" >> ecosystem-result
cat ecosystem-result-format-preview >> ecosystem-result
echo "" >> ecosystem-result
@@ -440,6 +533,8 @@ jobs:
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@main
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear
@@ -450,6 +545,8 @@ jobs:
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -463,7 +560,7 @@ jobs:
args: --out dist
- name: "Test wheel"
run: |
pip install --force-reinstall --find-links dist ${{ env.PACKAGE_NAME }}
pip install --force-reinstall --find-links dist "${PACKAGE_NAME}"
ruff --help
python -m ruff --help
- name: "Remove wheels from cache"
@@ -475,6 +572,8 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -490,13 +589,14 @@ jobs:
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: "Run pre-commit"
run: |
echo '```console' > $GITHUB_STEP_SUMMARY
echo '```console' > "$GITHUB_STEP_SUMMARY"
# Enable color output for pre-commit and remove it for the summary
SKIP=cargo-fmt,clippy,dev-generate-all pre-commit run --all-files --show-diff-on-failure --color=always | \
tee >(sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGK]//g' >> $GITHUB_STEP_SUMMARY) >&1
exit_code=${PIPESTATUS[0]}
echo '```' >> $GITHUB_STEP_SUMMARY
exit $exit_code
# Use --hook-stage=manual to enable slower pre-commit hooks that are skipped by default
SKIP=cargo-fmt,clippy,dev-generate-all pre-commit run --all-files --show-diff-on-failure --color=always --hook-stage=manual | \
tee >(sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGK]//g' >> "$GITHUB_STEP_SUMMARY") >&1
exit_code="${PIPESTATUS[0]}"
echo '```' >> "$GITHUB_STEP_SUMMARY"
exit "$exit_code"
docs:
name: "mkdocs"
@@ -506,7 +606,11 @@ jobs:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@v0.9.0
@@ -514,13 +618,15 @@ jobs:
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@v5
- uses: Swatinem/rust-cache@v2
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: pip install -r docs/requirements-insiders.txt
run: uv pip install -r docs/requirements-insiders.txt --system
- name: "Install dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: pip install -r docs/requirements.txt
run: uv pip install -r docs/requirements.txt --system
- name: "Update README File"
run: python scripts/transform_readme.py --target mkdocs
- name: "Generate docs"
@@ -542,16 +648,18 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Cache rust"
uses: Swatinem/rust-cache@v2
- name: "Formatter progress"
- name: "Run checks"
run: scripts/formatter_ecosystem_checks.sh
- name: "Github step summary"
run: cat target/progress_projects_stats.txt > $GITHUB_STEP_SUMMARY
run: cat target/formatter-ecosystem/stats.txt > "$GITHUB_STEP_SUMMARY"
- name: "Remove checkouts from cache"
run: rm -r target/progress_projects
run: rm -r target/formatter-ecosystem
check-ruff-lsp:
name: "test ruff-lsp"
@@ -569,6 +677,7 @@ jobs:
- uses: actions/checkout@v4
name: "Download ruff-lsp source"
with:
persist-credentials: false
repository: "astral-sh/ruff-lsp"
- uses: actions/setup-python@v5
@@ -587,23 +696,27 @@ jobs:
just install
- name: Run ruff-lsp tests
env:
DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }}
run: |
# Setup development binary
pip uninstall --yes ruff
chmod +x ${{ steps.ruff-target.outputs.download-path }}/ruff
export PATH=${{ steps.ruff-target.outputs.download-path }}:$PATH
chmod +x "${DOWNLOAD_PATH}/ruff"
export PATH="${DOWNLOAD_PATH}:${PATH}"
ruff version
just test
benchmarks:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: determine_changes
if: ${{ github.repository == 'astral-sh/ruff' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20
steps:
- name: "Checkout Branch"
uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
@@ -619,7 +732,7 @@ jobs:
run: cargo codspeed build --features codspeed -p ruff_benchmark
- name: "Run benchmarks"
uses: CodSpeedHQ/action@v2
uses: CodSpeedHQ/action@v3
with:
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}

View File

@@ -32,13 +32,9 @@ jobs:
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install Python requirements
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
persist-credentials: false
- uses: astral-sh/setup-uv@v5
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
@@ -49,7 +45,17 @@ jobs:
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
run: cargo build --locked
- name: Fuzz
run: python scripts/fuzz-parser/fuzz.py $(shuf -i 0-9999999999999999999 -n 1000) --test-executable target/debug/ruff
run: |
# shellcheck disable=SC2046
(
uvx \
--python=3.12 \
--from=./python/py-fuzzer \
fuzz \
--test-executable=target/debug/ruff \
--bin=ruff \
$(shuf -i 0-9999999999999999999 -n 1000)
)
create-issue-on-failure:
name: Create an issue if the daily fuzz surfaced any bugs
@@ -67,6 +73,6 @@ jobs:
owner: "astral-sh",
repo: "ruff",
title: `Daily parser fuzz failed on ${new Date().toDateString()}`,
body: "Runs listed here: https://github.com/astral-sh/ruff/actions/workflows/daily_fuzz.yml",
body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
labels: ["bug", "parser", "fuzzer"],
})

View File

@@ -0,0 +1,71 @@
name: Daily property test run
on:
workflow_dispatch:
schedule:
- cron: "0 12 * * *"
pull_request:
paths:
- ".github/workflows/daily_property_tests.yaml"
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
FORCE_COLOR: 1
jobs:
property_tests:
name: Property tests
runs-on: ubuntu-latest
timeout-minutes: 20
# Don't run the cron job on forks:
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@v1
- uses: Swatinem/rust-cache@v2
- 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.
run: cargo build --locked --release --package red_knot_python_semantic --tests
- name: Run property tests
shell: bash
run: |
export QUICKCHECK_TESTS=100000
for _ in {1..5}; do
cargo test --locked --release --package red_knot_python_semantic -- --ignored types::property_tests::stable
done
create-issue-on-failure:
name: Create an issue if the daily property test run surfaced any bugs
runs-on: ubuntu-latest
needs: property_tests
if: ${{ github.repository == 'astral-sh/ruff' && always() && github.event_name == 'schedule' && needs.property_tests.result == 'failure' }}
permissions:
issues: write
steps:
- uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.issues.create({
owner: "astral-sh",
repo: "ruff",
title: `Daily property test run failed on ${new Date().toDateString()}`,
body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
labels: ["bug", "red-knot", "testing"],
})

View File

@@ -10,29 +10,29 @@ on:
description: The ecosystem workflow that triggers the workflow run
required: true
permissions:
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: dawidd6/action-download-artifact@v6
- uses: dawidd6/action-download-artifact@v7
name: Download pull request number
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- name: Parse pull request number
id: pr-number
run: |
if [[ -f pr-number ]]
then
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
echo "pr-number=$(<pr-number)" >> "$GITHUB_OUTPUT"
fi
- uses: dawidd6/action-download-artifact@v6
- uses: dawidd6/action-download-artifact@v7
name: "Download ecosystem results"
id: download-ecosystem-result
if: steps.pr-number.outputs.pr-number
@@ -43,6 +43,7 @@ jobs:
path: pr/ecosystem
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- name: Generate comment content
id: generate-comment
@@ -64,9 +65,9 @@ jobs:
cat pr/ecosystem/ecosystem-result >> comment.txt
echo "" >> comment.txt
echo 'comment<<EOF' >> $GITHUB_OUTPUT
cat comment.txt >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT
echo 'comment<<EOF' >> "$GITHUB_OUTPUT"
cat comment.txt >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: Find existing comment
uses: peter-evans/find-comment@v3

View File

@@ -1,62 +0,0 @@
# Publish the Ruff documentation.
#
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce
# job within `cargo-dist`.
name: mkdocs
on:
workflow_dispatch:
inputs:
ref:
description: "The commit SHA, tag, or branch to publish. Uses the default branch if not specified."
default: ""
type: string
workflow_call:
inputs:
plan:
required: true
type: string
jobs:
mkdocs:
runs-on: ubuntu-latest
env:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
- uses: actions/setup-python@v5
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: pip install -r docs/requirements-insiders.txt
- name: "Install dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: pip install -r docs/requirements.txt
- name: "Copy README File"
run: |
python scripts/transform_readme.py --target mkdocs
python scripts/generate_mkdocs.py
- name: "Build Insiders docs"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: mkdocs build --strict -f mkdocs.insiders.yml
- name: "Build docs"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: mkdocs build --strict -f mkdocs.public.yml
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
uses: cloudflare/wrangler-action@v3.6.1
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
# `github.head_ref` is only set during pull requests and for manual runs or tags we use `main` to deploy to production
command: pages deploy site --project-name=astral-docs --branch ${{ github.head_ref || 'main' }} --commit-hash ${GITHUB_SHA}

144
.github/workflows/publish-docs.yml vendored Normal file
View File

@@ -0,0 +1,144 @@
# Publish the Ruff documentation.
#
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce
# job within `cargo-dist`.
name: mkdocs
on:
workflow_dispatch:
inputs:
ref:
description: "The commit SHA, tag, or branch to publish. Uses the default branch if not specified."
default: ""
type: string
workflow_call:
inputs:
plan:
required: true
type: string
jobs:
mkdocs:
runs-on: ubuntu-latest
env:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
persist-credentials: true
- uses: actions/setup-python@v5
with:
python-version: 3.12
- name: "Set docs version"
env:
version: ${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || inputs.ref }}
run: |
# if version is missing, use 'latest'
if [ -z "$version" ]; then
echo "Using 'latest' as version"
version="latest"
fi
# Use version as display name for now
display_name="$version"
echo "version=$version" >> "$GITHUB_ENV"
echo "display_name=$display_name" >> "$GITHUB_ENV"
- name: "Set branch name"
run: |
timestamp="$(date +%s)"
# create branch_display_name from display_name by replacing all
# characters disallowed in git branch names with hyphens
branch_display_name="$(echo "${display_name}" | tr -c '[:alnum:]._' '-' | tr -s '-')"
echo "branch_name=update-docs-$branch_display_name-$timestamp" >> "$GITHUB_ENV"
echo "timestamp=$timestamp" >> "$GITHUB_ENV"
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: pip install -r docs/requirements-insiders.txt
- name: "Install dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: pip install -r docs/requirements.txt
- name: "Copy README File"
run: |
python scripts/transform_readme.py --target mkdocs
python scripts/generate_mkdocs.py
- name: "Build Insiders docs"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: mkdocs build --strict -f mkdocs.insiders.yml
- name: "Build docs"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: mkdocs build --strict -f mkdocs.public.yml
- name: "Clone docs repo"
run: git clone https://${{ secrets.ASTRAL_DOCS_PAT }}@github.com/astral-sh/docs.git astral-docs
- name: "Copy docs"
run: rm -rf astral-docs/site/ruff && mkdir -p astral-docs/site && cp -r site/ruff astral-docs/site/
- name: "Commit docs"
working-directory: astral-docs
run: |
git config user.name "astral-docs-bot"
git config user.email "176161322+astral-docs-bot@users.noreply.github.com"
git checkout -b "${branch_name}"
git add site/ruff
git commit -m "Update ruff documentation for $version"
- name: "Create Pull Request"
working-directory: astral-docs
env:
GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }}
run: |
# set the PR title
pull_request_title="Update ruff documentation for ${display_name}"
# Delete any existing pull requests that are open for this version
# by checking against pull_request_title because the new PR will
# supersede the old one.
gh pr list --state open --json title --jq '.[] | select(.title == "$pull_request_title") | .number' | \
xargs -I {} gh pr close {}
# push the branch to GitHub
git push origin "${branch_name}"
# create the PR
gh pr create \
--base=main \
--head="${branch_name}" \
--title="${pull_request_title}" \
--body="Automated documentation update for ${display_name}" \
--label="documentation"
- name: "Merge Pull Request"
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
working-directory: astral-docs
env:
GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }}
run: |
# auto-merge the PR if the build was triggered by a release. Manual builds should be reviewed by a human.
# give the PR a few seconds to be created before trying to auto-merge it
sleep 10
gh pr merge --squash "${branch_name}"

View File

@@ -25,11 +25,13 @@ jobs:
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-pack-action@v0.4.0
@@ -47,7 +49,7 @@ jobs:
working-directory: playground
- name: "Deploy to Cloudflare Pages"
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
uses: cloudflare/wrangler-action@v3.6.1
uses: cloudflare/wrangler-action@v3.13.1
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}

View File

@@ -21,14 +21,12 @@ jobs:
# For PyPI's trusted publishing.
id-token: write
steps:
- name: "Install uv"
uses: astral-sh/setup-uv@v5
- uses: actions/download-artifact@v4
with:
pattern: wheels-*
path: wheels
merge-multiple: true
- name: Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
packages-dir: wheels
verbose: true
run: uv publish -v wheels/*

57
.github/workflows/publish-wasm.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
# Build and publish ruff-api for wasm.
#
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish
# job within `cargo-dist`.
name: "Build and publish wasm"
on:
workflow_dispatch:
workflow_call:
inputs:
plan:
required: true
type: string
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUSTUP_MAX_RETRIES: 10
jobs:
ruff_wasm:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
strategy:
matrix:
target: [web, bundler, nodejs]
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: jetli/wasm-pack-action@v0.4.0
- uses: jetli/wasm-bindgen-action@v0.2.0
- name: "Run wasm-pack build"
run: wasm-pack build --target ${{ matrix.target }} crates/ruff_wasm
- name: "Rename generated package"
run: | # Replace the package name w/ jq
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@v4
with:
node-version: 20
registry-url: "https://registry.npmjs.org"
- name: "Publish (dry-run)"
if: ${{ inputs.plan == '' || fromJson(inputs.plan).announcement_tag_is_implicit }}
run: npm publish --dry-run crates/ruff_wasm/pkg
- name: "Publish"
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
run: npm publish --provenance --access public crates/ruff_wasm/pkg
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -1,10 +1,12 @@
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
#
# * checks for a Git Tag that looks like a release
# * builds artifacts with cargo-dist (archives, installers, hashes)
# * builds artifacts with dist (archives, installers, hashes)
# * uploads those artifacts to temporary workflow zip
# * on success, uploads the artifacts to a GitHub Release
#
@@ -12,9 +14,8 @@
# title/body based on your changelogs.
name: Release
permissions:
contents: write
"contents": "write"
# This task will run whenever you workflow_dispatch with a tag that looks like a version
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
@@ -23,10 +24,10 @@ permissions:
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
#
# If PACKAGE_NAME is specified, then the announcement will be for that
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
# package (erroring out if it doesn't have the given version or isn't dist-able).
#
# If PACKAGE_NAME isn't specified, then the announcement will be for all
# (cargo-dist-able) packages in the workspace with that version (this mode is
# (dist-able) packages in the workspace with that version (this mode is
# intended for workspaces with only one dist-able package, or with all dist-able
# packages versioned/released in lockstep).
#
@@ -47,9 +48,9 @@ on:
type: string
jobs:
# Run 'cargo dist plan' (or host) to determine what tasks we need to do
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: ubuntu-latest
runs-on: "ubuntu-20.04"
outputs:
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ (inputs.tag != 'dry-run' && inputs.tag) || '' }}
@@ -61,11 +62,16 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cargo-dist
- 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.14.0/cargo-dist-installer.sh | sh"
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"
- name: Cache dist
uses: actions/upload-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
# sure would be cool if github gave us proper conditionals...
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
# functionality based on whether this is a pull_request, and whether it's from a fork.
@@ -73,8 +79,8 @@ jobs:
# but also really annoying to build CI around when it needs secrets to work right.)
- id: plan
run: |
cargo dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "cargo dist ran successfully"
dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "dist ran successfully"
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
@@ -101,8 +107,8 @@ jobs:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
permissions:
packages: write
contents: read
"contents": "read"
"packages": "write"
# Build and package all the platform-agnostic(ish) things
build-global-artifacts:
@@ -118,9 +124,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cargo-dist
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.14.0/cargo-dist-installer.sh | sh"
- name: Install cached dist
uses: actions/download-artifact@v4
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@v4
@@ -131,8 +140,8 @@ jobs:
- id: cargo-dist
shell: bash
run: |
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "cargo dist ran successfully"
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "dist ran successfully"
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
@@ -165,8 +174,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cargo-dist
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.14.0/cargo-dist-installer.sh | sh"
- name: Install cached dist
uses: actions/download-artifact@v4
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@v4
@@ -178,7 +191,7 @@ jobs:
- id: host
shell: bash
run: |
cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
echo "artifacts uploaded and released successfully"
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
@@ -200,8 +213,23 @@ jobs:
secrets: inherit
# publish jobs get escalated permissions
permissions:
id-token: write
packages: write
"id-token": "write"
"packages": "write"
custom-publish-wasm:
needs:
- plan
- host
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
uses: ./.github/workflows/publish-wasm.yml
with:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
# publish jobs get escalated permissions
permissions:
"contents": "read"
"id-token": "write"
"packages": "write"
# Create a GitHub Release while uploading all files to it
announce:
@@ -209,10 +237,11 @@ jobs:
- plan
- host
- custom-publish-pypi
- custom-publish-wasm
# use "always() && ..." to allow us to wait for all publish jobs while
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }}
if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') && (needs.custom-publish-wasm.result == 'skipped' || needs.custom-publish-wasm.result == 'success') }}
runs-on: "ubuntu-20.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -220,6 +249,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@v4
with:
@@ -231,13 +261,16 @@ jobs:
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
tag: ${{ needs.plan.outputs.tag }}
name: ${{ fromJson(needs.host.outputs.val).announcement_title }}
body: ${{ fromJson(needs.host.outputs.val).announcement_github_body }}
prerelease: ${{ fromJson(needs.host.outputs.val).announcement_is_prerelease }}
artifacts: "artifacts/*"
env:
PRERELEASE_FLAG: "${{ fromJson(needs.host.outputs.val).announcement_is_prerelease && '--prerelease' || '' }}"
ANNOUNCEMENT_TITLE: "${{ fromJson(needs.host.outputs.val).announcement_title }}"
ANNOUNCEMENT_BODY: "${{ fromJson(needs.host.outputs.val).announcement_github_body }}"
RELEASE_COMMIT: "${{ github.sha }}"
run: |
# Write and read notes from a file to avoid quoting breaking things
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
custom-notify-dependents:
needs:

View File

@@ -25,11 +25,13 @@ jobs:
name: Checkout Ruff
with:
path: ruff
persist-credentials: true
- uses: actions/checkout@v4
name: Checkout typeshed
with:
repository: python/typeshed
path: typeshed
persist-credentials: false
- name: Setup git
run: |
git config --global user.name typeshedbot
@@ -37,13 +39,13 @@ jobs:
- name: Sync typeshed
id: sync
run: |
rm -rf ruff/crates/red_knot_module_resolver/vendor/typeshed
mkdir ruff/crates/red_knot_module_resolver/vendor/typeshed
cp typeshed/README.md ruff/crates/red_knot_module_resolver/vendor/typeshed
cp typeshed/LICENSE ruff/crates/red_knot_module_resolver/vendor/typeshed
cp -r typeshed/stdlib ruff/crates/red_knot_module_resolver/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_module_resolver/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_module_resolver/vendor/typeshed/source_commit.txt
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed
mkdir ruff/crates/red_knot_vendored/vendor/typeshed
cp typeshed/README.md ruff/crates/red_knot_vendored/vendor/typeshed
cp typeshed/LICENSE ruff/crates/red_knot_vendored/vendor/typeshed
cp -r typeshed/stdlib ruff/crates/red_knot_vendored/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_vendored/vendor/typeshed/source_commit.txt
- name: Commit the changes
id: commit
if: ${{ steps.sync.outcome == 'success' }}
@@ -57,7 +59,7 @@ jobs:
run: |
cd ruff
git push --force origin typeshedbot/sync-typeshed
gh pr list --repo $GITHUB_REPOSITORY --head typeshedbot/sync-typeshed --json id --jq length | grep 1 && exit 0 # exit if there is existing pr
gh pr list --repo "$GITHUB_REPOSITORY" --head typeshedbot/sync-typeshed --json id --jq length | grep 1 && exit 0 # exit if there is existing pr
gh pr create --title "Sync vendored typeshed stubs" --body "Close and reopen this PR to trigger CI" --label "internal"
create-issue-on-failure:
@@ -76,5 +78,6 @@ jobs:
owner: "astral-sh",
repo: "ruff",
title: `Automated typeshed sync failed on ${new Date().toDateString()}`,
body: "Runs are listed here: https://github.com/astral-sh/ruff/actions/workflows/sync_typeshed.yaml",
body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
labels: ["bug", "red-knot"],
})

12
.github/zizmor.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# Configuration for the zizmor static analysis tool, run via pre-commit in CI
# https://woodruffw.github.io/zizmor/configuration/
#
# TODO: can we remove the ignores here so that our workflows are more secure?
rules:
dangerous-triggers:
ignore:
- pr-comment.yaml
cache-poisoning:
ignore:
- build-docker.yml
- publish-playground.yml

8
.gitignore vendored
View File

@@ -21,6 +21,14 @@ flamegraph.svg
# `CARGO_TARGET_DIR=target-llvm-lines RUSTFLAGS="-Csymbol-mangling-version=v0" cargo llvm-lines -p ruff --lib`
/target*
# samply profiles
profile.json
# tracing-flame traces
tracing.folded
tracing-flamechart.svg
tracing-flamegraph.svg
###
# Rust.gitignore
###

View File

@@ -14,7 +14,18 @@ MD041: false
# MD013/line-length
MD013: false
# MD014/commands-show-output
MD014: false
# MD024/no-duplicate-heading
MD024:
# Allow when nested under different parents e.g. CHANGELOG.md
siblings_only: true
# MD046/code-block-style
#
# Ignore this because it conflicts with the code block style used in content
# tabs of mkdocs-material which is to add a blank line after the content title.
#
# Ref: https://github.com/astral-sh/ruff/pull/15011#issuecomment-2544790854
MD046: false

View File

@@ -1,10 +1,14 @@
fail_fast: true
fail_fast: false
exclude: |
(?x)^(
crates/red_knot_module_resolver/vendor/.*|
.github/workflows/release.yml|
crates/red_knot_vendored/vendor/.*|
crates/red_knot_project/resources/.*|
crates/ruff_linter/resources/.*|
crates/ruff_linter/src/rules/.*/snapshots/.*|
crates/ruff_notebook/resources/.*|
crates/ruff_server/resources/.*|
crates/ruff/resources/.*|
crates/ruff_python_formatter/resources/.*|
crates/ruff_python_formatter/tests/snapshots/.*|
@@ -14,17 +18,17 @@ exclude: |
repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.18
rev: v0.23
hooks:
- id: validate-pyproject
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.17
rev: 0.7.21
hooks:
- id: mdformat
additional_dependencies:
- mdformat-mkdocs
- mdformat-admon
- mdformat-mkdocs==4.0.0
- mdformat-footnote==0.1.1
exclude: |
(?x)^(
docs/formatter/black\.md
@@ -32,7 +36,7 @@ repos:
)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.41.0
rev: v0.43.0
hooks:
- id: markdownlint-fix
exclude: |
@@ -41,8 +45,21 @@ repos:
| docs/\w+\.md
)$
- repo: https://github.com/adamchainz/blacken-docs
rev: 1.19.1
hooks:
- id: blacken-docs
args: ["--pyi", "--line-length", "130"]
files: '^crates/.*/resources/mdtest/.*\.md'
exclude: |
(?x)^(
.*?invalid(_.+)*_syntax\.md
)$
additional_dependencies:
- black==24.10.0
- repo: https://github.com/crate-ci/typos
rev: v1.22.9
rev: v1.29.4
hooks:
- id: typos
@@ -56,25 +73,52 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.10
rev: v0.9.2
hooks:
- id: ruff-format
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
types_or: [python, pyi]
require_serial: true
exclude: |
(?x)^(
crates/ruff_linter/resources/.*|
crates/ruff_python_formatter/resources/.*
)$
# Prettier
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.4.2
hooks:
- id: prettier
types: [yaml]
# zizmor detects security vulnerabilities in GitHub Actions workflows.
# Additional configuration for the tool is found in `.github/zizmor.yml`
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.1.1
hooks:
- id: zizmor
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.31.0
hooks:
- id: check-github-workflows
# `actionlint` hook, for verifying correct syntax in GitHub Actions workflows.
# Some additional configuration for `actionlint` can be found in `.github/actionlint.yaml`.
- repo: https://github.com/rhysd/actionlint
rev: v1.7.7
hooks:
- id: actionlint
stages:
# This hook is disabled by default, since it's quite slow.
# To run all hooks *including* this hook, use `uvx pre-commit run -a --hook-stage=manual`.
# To run *just* this hook, use `uvx pre-commit run -a actionlint --hook-stage=manual`.
- manual
args:
- "-ignore=SC2129" # ignorable stylistic lint from shellcheck
- "-ignore=SC2016" # another shellcheck lint: seems to have false positives?
additional_dependencies:
# actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions
# and checks these with shellcheck. This is arguably its most useful feature,
# but the integration only works if shellcheck is installed
- "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.10.0"
ci:
skip: [cargo-fmt, dev-generate-all]

View File

@@ -1,5 +1,85 @@
# Breaking Changes
## 0.9.0
Ruff now formats your code according to the 2025 style guide. As a result, your code might now get formatted differently. See the [changelog](./CHANGELOG.md#090) for a detailed list of changes.
## 0.8.0
- **Default to Python 3.9**
Ruff now defaults to Python 3.9 instead of 3.8 if no explicit Python version is configured using [`ruff.target-version`](https://docs.astral.sh/ruff/settings/#target-version) or [`project.requires-python`](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#python-requires) ([#13896](https://github.com/astral-sh/ruff/pull/13896))
- **Changed location of `pydoclint` diagnostics**
[`pydoclint`](https://docs.astral.sh/ruff/rules/#pydoclint-doc) diagnostics now point to the first-line of the problematic docstring. Previously, this was not the case.
If you've opted into these preview rules but have them suppressed using
[`noqa`](https://docs.astral.sh/ruff/linter/#error-suppression) comments in
some places, this change may mean that you need to move the `noqa` suppression
comments. Most users should be unaffected by this change.
- **Use XDG (i.e. `~/.local/bin`) instead of the Cargo home directory in the standalone installer**
Previously, Ruff's installer used `$CARGO_HOME` or `~/.cargo/bin` for its target install directory. Now, Ruff will be installed into `$XDG_BIN_HOME`, `$XDG_DATA_HOME/../bin`, or `~/.local/bin` (in that order).
This change is only relevant to users of the standalone Ruff installer (using the shell or PowerShell script). If you installed Ruff using uv or pip, you should be unaffected.
- **Changes to the line width calculation**
Ruff now uses a new version of the [unicode-width](https://github.com/unicode-rs/unicode-width) Rust crate to calculate the line width. In very rare cases, this may lead to lines containing Unicode characters being reformatted, or being considered too long when they were not before ([`E501`](https://docs.astral.sh/ruff/rules/line-too-long/)).
## 0.7.0
- The pytest rules `PT001` and `PT023` now default to omitting the decorator parentheses when there are no arguments
([#12838](https://github.com/astral-sh/ruff/pull/12838), [#13292](https://github.com/astral-sh/ruff/pull/13292)).
This was a change that we attempted to make in Ruff v0.6.0, but only partially made due to an error on our part.
See the [blog post](https://astral.sh/blog/ruff-v0.7.0) for more details.
- The `useless-try-except` rule (in our `tryceratops` category) has been recoded from `TRY302` to
`TRY203` ([#13502](https://github.com/astral-sh/ruff/pull/13502)). This ensures Ruff's code is consistent with
the same rule in the [`tryceratops`](https://github.com/guilatrova/tryceratops) linter.
- The `lint.allow-unused-imports` setting has been removed ([#13677](https://github.com/astral-sh/ruff/pull/13677)). Use
[`lint.pyflakes.allow-unused-imports`](https://docs.astral.sh/ruff/settings/#lint_pyflakes_allowed-unused-imports)
instead.
## 0.6.0
- Detect imports in `src` layouts by default for `isort` rules ([#12848](https://github.com/astral-sh/ruff/pull/12848))
- The pytest rules `PT001` and `PT023` now default to omitting the decorator parentheses when there are no arguments ([#12838](https://github.com/astral-sh/ruff/pull/12838)).
- Lint and format Jupyter Notebook by default ([#12878](https://github.com/astral-sh/ruff/pull/12878)).
You can disable specific rules for notebooks using [`per-file-ignores`](https://docs.astral.sh/ruff/settings/#lint_per-file-ignores):
```toml
[tool.ruff.lint.per-file-ignores]
"*.ipynb" = ["E501"] # disable line-too-long in notebooks
```
If you'd prefer to either only lint or only format Jupyter Notebook files, you can use the
section-specific `exclude` option to do so. For example, the following would only lint Jupyter
Notebook files and not format them:
```toml
[tool.ruff.format]
exclude = ["*.ipynb"]
```
And, conversely, the following would only format Jupyter Notebook files and not lint them:
```toml
[tool.ruff.lint]
exclude = ["*.ipynb"]
```
You can completely disable Jupyter Notebook support by updating the [`extend-exclude`](https://docs.astral.sh/ruff/settings/#extend-exclude) setting:
```toml
[tool.ruff]
extend-exclude = ["*.ipynb"]
```
## 0.5.0
- Follow the XDG specification to discover user-level configurations on macOS (same as on other Unix platforms)
@@ -116,7 +196,7 @@ flag or `unsafe-fixes` configuration option can be used to enable unsafe fixes.
See the [docs](https://docs.astral.sh/ruff/configuration/#fix-safety) for details.
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
Previously, Ruff enabled all implemented rules in Pycodestyle (`E`) by default. Ruff now only includes the
Pycodestyle prefixes `E4`, `E7`, and `E9` to exclude rules that conflict with automatic formatters. Consequently,

File diff suppressed because it is too large Load Diff

View File

@@ -2,35 +2,6 @@
Welcome! We're happy to have you here. Thank you in advance for your contribution to Ruff.
- [The Basics](#the-basics)
- [Prerequisites](#prerequisites)
- [Development](#development)
- [Project Structure](#project-structure)
- [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
- [Rule naming convention](#rule-naming-convention)
- [Rule testing: fixtures and snapshots](#rule-testing-fixtures-and-snapshots)
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
- [MkDocs](#mkdocs)
- [Release Process](#release-process)
- [Creating a new release](#creating-a-new-release)
- [Ecosystem CI](#ecosystem-ci)
- [Benchmarking and Profiling](#benchmarking-and-profiling)
- [CPython Benchmark](#cpython-benchmark)
- [Microbenchmarks](#microbenchmarks)
- [Benchmark-driven Development](#benchmark-driven-development)
- [PR Summary](#pr-summary)
- [Tips](#tips)
- [Profiling Projects](#profiling-projects)
- [Linux](#linux)
- [Mac](#mac)
- [`cargo dev`](#cargo-dev)
- [Subsystems](#subsystems)
- [Compilation Pipeline](#compilation-pipeline)
- [Import Categorization](#import-categorization)
- [Project root](#project-root)
- [Package root](#package-root)
- [Import categorization](#import-categorization-1)
## The Basics
Ruff welcomes contributions in the form of pull requests.
@@ -58,16 +29,14 @@ You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
cargo install cargo-insta
```
And you'll need pre-commit to run some validation checks:
```shell
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
```
You'll need [uv](https://docs.astral.sh/uv/getting-started/installation/) (or `pipx` and `pip`) to
run Python utility commands.
You can optionally install pre-commit hooks to automatically run the validation checks
when making a commit:
```shell
uv tool install pre-commit
pre-commit install
```
@@ -95,7 +64,7 @@ and that it passes both the lint and test validation checks:
```shell
cargo clippy --workspace --all-targets --all-features -- -D warnings # Rust linting
RUFF_UPDATE_SCHEMA=1 cargo test # Rust testing and updating ruff.schema.json
pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
uvx pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
```
These checks will run on GitHub Actions when you open your pull request, but running them locally
@@ -170,7 +139,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
1. Create a file for your rule (e.g., `crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs`).
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
`#[violation]` to see examples.
`#[derive(ViolationMetadata)]` to see examples.
1. In that file, define a function that adds the violation to the diagnostic list as appropriate
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
@@ -280,7 +249,7 @@ These represent, respectively: the schema used to parse the `pyproject.toml` fil
intermediate representation; and the final, internal representation used to power Ruff.
To add a new configuration option, you'll likely want to modify these latter few files (along with
`arg.rs`, if appropriate). If you want to pattern-match against an existing example, grep for
`args.rs`, if appropriate). If you want to pattern-match against an existing example, grep for
`dummy_variable_rgx`, which defines a regular expression to match against acceptable unused
variables (e.g., `_`).
@@ -296,26 +265,20 @@ To preview any changes to the documentation locally:
1. Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
1. Install MkDocs and Material for MkDocs with:
```shell
pip install -r docs/requirements.txt
```
1. Generate the MkDocs site with:
```shell
python scripts/generate_mkdocs.py
uv run --no-project --isolated --with-requirements docs/requirements.txt scripts/generate_mkdocs.py
```
1. Run the development server with:
```shell
# For contributors.
mkdocs serve -f mkdocs.public.yml
uvx --with-requirements docs/requirements.txt -- mkdocs serve -f mkdocs.public.yml
# For members of the Astral org, which has access to MkDocs Insiders via sponsorship.
mkdocs serve -f mkdocs.insiders.yml
uvx --with-requirements docs/requirements-insiders.txt -- mkdocs serve -f mkdocs.insiders.yml
```
The documentation should then be available locally at
@@ -333,22 +296,34 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
### Creating a new release
1. Install `uv`: `curl -LsSf https://astral.sh/uv/install.sh | sh`
1. Run `./scripts/release/bump.sh`; this command will:
1. Run `./scripts/release.sh`; this command will:
- Generate a temporary virtual environment with `rooster`
- Generate a changelog entry in `CHANGELOG.md`
- Update versions in `pyproject.toml` and `Cargo.toml`
- Update references to versions in the `README.md` and documentation
- Display contributors for the release
1. The changelog should then be editorialized for consistency
- Often labels will be missing from pull requests they will need to be manually organized into the proper section
- Changes should be edited to be user-facing descriptions, avoiding internal details
1. Highlight any breaking changes in `BREAKING_CHANGES.md`
1. Run `cargo check`. This should update the lock file with new versions.
1. Create a pull request with the changelog and version updates
1. Merge the PR
1. Run the [release workflow](https://github.com/astral-sh/ruff/actions/workflows/release.yml) with:
- The new version number (without starting `v`)
1. The release workflow will do the following:
1. Build all the assets. If this fails (even though we tested in step 4), we haven't tagged or
uploaded anything, you can restart after pushing a fix. If you just need to rerun the build,
make sure you're [re-running all the failed
@@ -359,14 +334,25 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
1. Attach artifacts to draft GitHub release
1. Trigger downstream repositories. This can fail non-catastrophically, as we can run any
downstream jobs manually if needed.
1. Verify the GitHub release:
1. The Changelog should match the content of `CHANGELOG.md`
1. Append the contributors from the `bump.sh` script
1. Append the contributors from the `scripts/release.sh` script
1. If needed, [update the schemastore](https://github.com/astral-sh/ruff/blob/main/scripts/update_schemastore.py).
1. One can determine if an update is needed when
`git diff old-version-tag new-version-tag -- ruff.schema.json` returns a non-empty diff.
1. Once run successfully, you should follow the link in the output to create a PR.
1. If needed, update the `ruff-lsp` and `ruff-vscode` repositories.
1. If needed, update the [`ruff-lsp`](https://github.com/astral-sh/ruff-lsp) and
[`ruff-vscode`](https://github.com/astral-sh/ruff-vscode) repositories and follow
the release instructions in those repositories. `ruff-lsp` should always be updated
before `ruff-vscode`.
This step is generally not required for a patch release, but should always be done
for a minor release.
## Ecosystem CI
@@ -374,9 +360,8 @@ GitHub Actions will run your changes against a number of real-world projects fro
report on any linter or formatter differences. You can also run those checks locally via:
```shell
pip install -e ./python/ruff-ecosystem
ruff-ecosystem check ruff "./target/debug/ruff"
ruff-ecosystem format ruff "./target/debug/ruff"
uvx --from ./python/ruff-ecosystem ruff-ecosystem check ruff "./target/debug/ruff"
uvx --from ./python/ruff-ecosystem ruff-ecosystem format ruff "./target/debug/ruff"
```
See the [ruff-ecosystem package](https://github.com/astral-sh/ruff/tree/main/python/ruff-ecosystem) for more details.
@@ -389,7 +374,7 @@ We have several ways of benchmarking and profiling Ruff:
- Microbenchmarks which run the linter or the formatter on individual files. These run on pull requests.
- Profiling the linter on either the microbenchmarks or entire projects
> \[!NOTE\]
> **Note**
> When running benchmarks, ensure that your CPU is otherwise idle (e.g., close any background
> applications, like web browsers). You may also want to switch your CPU to a "performance"
> mode, if it exists, especially when benchmarking short-lived processes.
@@ -403,12 +388,18 @@ which makes it a good target for benchmarking.
git clone --branch 3.10 https://github.com/python/cpython.git crates/ruff_linter/resources/test/cpython
```
Install `hyperfine`:
```shell
cargo install hyperfine
```
To benchmark the release build:
```shell
cargo build --release && hyperfine --warmup 10 \
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache -e" \
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ -e"
"./target/release/ruff check ./crates/ruff_linter/resources/test/cpython/ --no-cache -e" \
"./target/release/ruff check ./crates/ruff_linter/resources/test/cpython/ -e"
Benchmark 1: ./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache
Time (mean ± σ): 293.8 ms ± 3.2 ms [User: 2384.6 ms, System: 90.3 ms]
@@ -427,7 +418,7 @@ To benchmark against the ecosystem's existing tools:
```shell
hyperfine --ignore-failure --warmup 5 \
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache" \
"./target/release/ruff check ./crates/ruff_linter/resources/test/cpython/ --no-cache" \
"pyflakes crates/ruff_linter/resources/test/cpython" \
"autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
"pycodestyle crates/ruff_linter/resources/test/cpython" \
@@ -473,10 +464,10 @@ To benchmark a subset of rules, e.g. `LineTooLong` and `DocLineTooLong`:
```shell
cargo build --release && hyperfine --warmup 10 \
"./target/release/ruff ./crates/ruff_linter/resources/test/cpython/ --no-cache -e --select W505,E501"
"./target/release/ruff check ./crates/ruff_linter/resources/test/cpython/ --no-cache -e --select W505,E501"
```
You can run `poetry install` from `./scripts/benchmarks` to create a working environment for the
You can run `uv venv --project ./scripts/benchmarks`, activate the venv and then run `uv sync --project ./scripts/benchmarks` to create a working environment for the
above. All reported benchmarks were computed using the versions specified by
`./scripts/benchmarks/pyproject.toml` on Python 3.11.
@@ -530,6 +521,8 @@ You can run the benchmarks with
cargo benchmark
```
`cargo benchmark` is an alias for `cargo bench -p ruff_benchmark --bench linter --bench formatter --`
#### Benchmark-driven Development
Ruff uses [Criterion.rs](https://bheisler.github.io/criterion.rs/book/) for benchmarks. You can use
@@ -568,7 +561,7 @@ cargo install critcmp
#### Tips
- Use `cargo bench -p ruff_benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark lexer`
- Use `cargo bench -p ruff_benchmark <filter>` to only run specific benchmarks. For example: `cargo bench -p ruff_benchmark lexer`
to only run the lexer benchmarks.
- Use `cargo bench -p ruff_benchmark -- --quiet` for a more cleaned up output (without statistical relevance)
- Use `cargo bench -p ruff_benchmark -- --quick` to get faster results (more prone to noise)
@@ -870,7 +863,7 @@ each configuration file.
The package root is used to determine a file's "module path". Consider, again, `baz.py`. In that
case, `./my_project/src/foo` was identified as the package root, so the module path for `baz.py`
would resolve to `foo.bar.baz` — as computed by taking the relative path from the package root
would resolve to `foo.bar.baz` — as computed by taking the relative path from the package root
(inclusive of the root itself). The module path can be thought of as "the path you would use to
import the module" (e.g., `import foo.bar.baz`).
@@ -905,15 +898,11 @@ There are three ways in which an import can be categorized as "first-party":
package (e.g., `from foo import bar` or `import foo.bar`), they'll be classified as first-party
automatically. This check is as simple as comparing the first segment of the current file's
module path to the first segment of the import.
1. **Source roots**: Ruff supports a `[src](https://docs.astral.sh/ruff/settings/#src)` setting, which
1. **Source roots**: Ruff supports a [`src`](https://docs.astral.sh/ruff/settings/#src) setting, which
sets the directories to scan when identifying first-party imports. The algorithm is
straightforward: given an import, like `import foo`, iterate over the directories enumerated in
the `src` setting and, for each directory, check for the existence of a subdirectory `foo` or a
file `foo.py`.
By default, `src` is set to the project root. In the above example, we'd want to set
`src = ["./src"]` to ensure that we locate `./my_project/src/foo` and thus categorize `import foo`
as first-party in `baz.py`. In practice, for this limited example, setting `src = ["./src"]` is
unnecessary, as all imports within `./my_project/src/foo` would be categorized as first-party via
the same-package heuristic; but if your project contains multiple packages, you'll want to set `src`
explicitly.
By default, `src` is set to the project root, along with `"src"` subdirectory in the project root.
This ensures that Ruff supports both flat and "src" layouts out of the box.

2088
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ resolver = "2"
[workspace.package]
edition = "2021"
rust-version = "1.75"
rust-version = "1.80"
homepage = "https://docs.astral.sh/ruff"
documentation = "https://docs.astral.sh/ruff"
repository = "https://github.com/astral-sh/ruff"
@@ -13,10 +13,12 @@ license = "MIT"
[workspace.dependencies]
ruff = { path = "crates/ruff" }
ruff_annotate_snippets = { path = "crates/ruff_annotate_snippets" }
ruff_cache = { path = "crates/ruff_cache" }
ruff_db = { path = "crates/ruff_db" }
ruff_db = { path = "crates/ruff_db", default-features = false }
ruff_diagnostics = { path = "crates/ruff_diagnostics" }
ruff_formatter = { path = "crates/ruff_formatter" }
ruff_graph = { path = "crates/ruff_graph" }
ruff_index = { path = "crates/ruff_index" }
ruff_linter = { path = "crates/ruff_linter" }
ruff_macros = { path = "crates/ruff_macros" }
@@ -33,13 +35,19 @@ ruff_python_trivia = { path = "crates/ruff_python_trivia" }
ruff_server = { path = "crates/ruff_server" }
ruff_source_file = { path = "crates/ruff_source_file" }
ruff_text_size = { path = "crates/ruff_text_size" }
red_knot_vendored = { path = "crates/red_knot_vendored" }
ruff_workspace = { path = "crates/ruff_workspace" }
red_knot_module_resolver = { path = "crates/red_knot_module_resolver" }
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
red_knot_server = { path = "crates/red_knot_server" }
red_knot_test = { path = "crates/red_knot_test" }
red_knot_project = { path = "crates/red_knot_project", default-features = false }
aho-corasick = { version = "1.1.3" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
anstream = { version = "0.6.18" }
anstyle = { version = "1.0.10" }
anyhow = { version = "1.0.80" }
assert_fs = { version = "1.1.0" }
argfile = { version = "0.2.0" }
bincode = { version = "1.3.3" }
bitflags = { version = "2.5.0" }
@@ -48,35 +56,42 @@ cachedir = { version = "0.3.1" }
camino = { version = "1.1.7" }
chrono = { version = "0.4.35", default-features = false, features = ["clock"] }
clap = { version = "4.5.3", features = ["derive"] }
clap_complete_command = { version = "0.5.1" }
clearscreen = { version = "3.0.0" }
clap_complete_command = { version = "0.6.0" }
clearscreen = { version = "4.0.0" }
codspeed-criterion-compat = { version = "2.6.0", default-features = false }
colored = { version = "2.1.0" }
colored = { version = "3.0.0" }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "1.0.0" }
countme = { version = "3.0.1" }
compact_str = "0.8.0"
criterion = { version = "0.5.1", default-features = false }
crossbeam = { version = "0.8.4" }
dashmap = { version = "5.5.3" }
dashmap = { version = "6.0.1" }
dir-test = { version = "0.4.0" }
dunce = { version = "1.0.5" }
drop_bomb = { version = "0.1.5" }
env_logger = { version = "0.11.0" }
etcetera = { version = "0.8.0" }
fern = { version = "0.6.1" }
fern = { version = "0.7.0" }
filetime = { version = "0.2.23" }
glob = { version = "0.3.1" }
globset = { version = "0.4.14" }
hashbrown = "0.14.3"
globwalk = { version = "0.9.1" }
hashbrown = { version = "0.15.0", default-features = false, features = [
"raw-entry",
"inline-more",
] }
ignore = { version = "0.4.22" }
imara-diff = { version = "0.1.5" }
imperative = { version = "1.0.4" }
indexmap = { version = "2.2.6" }
indexmap = { version = "2.6.0" }
indicatif = { version = "0.17.8" }
indoc = { version = "2.0.4" }
insta = { version = "1.35.1" }
insta-cmd = { version = "0.6.0" }
is-macro = { version = "0.3.5" }
is-wsl = { version = "0.4.0" }
itertools = { version = "0.13.0" }
itertools = { version = "0.14.0" }
js-sys = { version = "0.3.69" }
jod-thread = { version = "0.1.2" }
libc = { version = "0.2.153" }
@@ -84,29 +99,29 @@ libcst = { version = "1.1.0", default-features = false }
log = { version = "0.4.17" }
lsp-server = { version = "0.7.6" }
lsp-types = { git = "https://github.com/astral-sh/lsp-types.git", rev = "3512a9f", features = [
"proposed",
"proposed",
] }
matchit = { version = "0.8.1" }
memchr = { version = "2.7.1" }
mimalloc = { version = "0.1.39" }
natord = { version = "1.0.9" }
notify = { version = "6.1.1" }
once_cell = { version = "1.19.0" }
notify = { version = "8.0.0" }
ordermap = { version = "0.5.0" }
path-absolutize = { version = "3.1.1" }
path-slash = { version = "0.2.1" }
pathdiff = { version = "0.2.1" }
parking_lot = "0.12.1"
pep440_rs = { version = "0.6.0", features = ["serde"] }
pep440_rs = { version = "0.7.1" }
pretty_assertions = "1.3.0"
proc-macro2 = { version = "1.0.79" }
pyproject-toml = { version = "0.9.0" }
quick-junit = { version = "0.4.0" }
pyproject-toml = { version = "0.13.4" }
quick-junit = { version = "0.5.0" }
quote = { version = "1.0.23" }
rand = { version = "0.8.5" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
rustc-hash = { version = "2.0.0" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "f706aa2d32d473ee633a77c1af01d180c85da308" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "88a1d7774d78f048fbd77d40abca9ebd729fd1f0" }
schemars = { version = "0.8.16" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }
@@ -114,48 +129,57 @@ serde-wasm-bindgen = { version = "0.6.4" }
serde_json = { version = "1.0.113" }
serde_test = { version = "1.0.152" }
serde_with = { version = "3.6.0", default-features = false, features = [
"macros",
"macros",
] }
shellexpand = { version = "3.0.0" }
similar = { version = "2.4.0", features = ["inline"] }
smallvec = { version = "1.13.2" }
smol_str = { version = "0.2.2" }
snapbox = { version = "0.6.0", features = ["diff", "term-svg", "cmd", "examples"] }
static_assertions = "1.1.0"
strum = { version = "0.26.0", features = ["strum_macros"] }
strum_macros = { version = "0.26.0" }
syn = { version = "2.0.55" }
tempfile = { version = "3.9.0" }
test-case = { version = "3.3.1" }
thiserror = { version = "1.0.58" }
tikv-jemallocator = { version = "0.5.0" }
thiserror = { version = "2.0.0" }
tikv-jemallocator = { version = "0.6.0" }
toml = { version = "0.8.11" }
tracing = { version = "0.1.40" }
tracing-flame = { version = "0.2.0" }
tracing-indicatif = { version = "0.3.6" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-tree = { version = "0.3.0" }
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
"env-filter",
"fmt",
] }
tracing-tree = { version = "0.4.0" }
tryfn = { version = "0.2.1" }
typed-arena = { version = "2.0.2" }
unic-ucd-category = { version = "0.9" }
unicode-ident = { version = "1.0.12" }
unicode-width = { version = "0.1.11" }
unicode-width = { version = "0.2.0" }
unicode_names2 = { version = "1.2.2" }
unicode-normalization = { version = "0.1.23" }
ureq = { version = "2.9.6" }
url = { version = "2.5.0" }
uuid = { version = "1.6.1", features = [
"v4",
"fast-rng",
"macro-diagnostics",
"js",
"v4",
"fast-rng",
"macro-diagnostics",
"js",
] }
walkdir = { version = "2.3.2" }
wasm-bindgen = { version = "0.2.92" }
wasm-bindgen-test = { version = "0.3.42" }
wild = { version = "2" }
zip = { version = "0.6.6", default-features = false, features = ["zstd"] }
zip = { version = "0.6.6", default-features = false }
[workspace.lints.rust]
unsafe_code = "warn"
unreachable_pub = "warn"
unexpected_cfgs = { level = "warn", check-cfg = [
"cfg(fuzzing)",
"cfg(codspeed)",
] }
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -2 }
@@ -171,8 +195,9 @@ missing_panics_doc = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"
similar_names = "allow"
single_match_else = "allow"
too_many_lines = "allow"
# To allow `#[allow(clippy::all)]` in `crates/ruff_python_parser/src/python.rs`.
# Without the hashes we run into a `rustfmt` bug in some snapshot tests, see #13250
needless_raw_string_hashes = "allow"
# Disallowed restriction lints
print_stdout = "warn"
@@ -185,6 +210,13 @@ get_unwrap = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
rest_pat_in_fully_bound_structs = "warn"
# nursery rules
redundant_clone = "warn"
debug_assert_with_mut_call = "warn"
unused_peekable = "warn"
# Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved.
large_stack_arrays = "allow"
[profile.release]
# Note that we set these explicitly, and these values
@@ -224,12 +256,12 @@ debug = 1
[profile.dist]
inherits = "release"
# Config for 'cargo dist'
# Config for 'dist'
[workspace.metadata.dist]
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.14.0"
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.25.2-prerelease.3"
# CI backends to support
ci = ["github"]
ci = "github"
# The installers to generate for each app
installers = ["shell", "powershell"]
# The archive format to use for windows builds (defaults .zip)
@@ -238,41 +270,45 @@ windows-archive = ".zip"
unix-archive = ".tar.gz"
# Target platforms to build apps for (Rust target-triple syntax)
targets = [
"aarch64-apple-darwin",
"aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"arm-unknown-linux-musleabihf",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"i686-unknown-linux-musl",
"powerpc64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"aarch64-apple-darwin",
"aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"arm-unknown-linux-musleabihf",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"i686-unknown-linux-musl",
"powerpc64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
]
# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)
auto-includes = false
# Whether cargo-dist should create a Github Release or use an existing draft
# Whether dist should create a Github Release or use an existing draft
create-release = true
# Publish jobs to run in CI
# Which actions to run on pull requests
pr-run-mode = "skip"
# Whether CI should trigger releases with dispatches instead of tag pushes
dispatch-releases = true
# Which phase dist should use to create the GitHub release
github-release = "announce"
# Whether CI should include auto-generated code to build local artifacts
build-local-artifacts = false
# Local artifacts jobs to run in CI
local-artifacts-jobs = ["./build-binaries", "./build-docker"]
# Publish jobs to run in CI
publish-jobs = ["./publish-pypi"]
# Announcement jobs to run in CI
publish-jobs = ["./publish-pypi", "./publish-wasm"]
# Post-announce jobs to run in CI
post-announce-jobs = ["./notify-dependents", "./publish-docs", "./publish-playground"]
# Skip checking whether the specified configuration files are up to date
allow-dirty = ["ci"]
# Custom permissions for GitHub Jobs
github-custom-job-permissions = { "build-docker" = { packages = "write", contents = "read" }, "publish-wasm" = { contents = "read", id-token = "write", packages = "write" } }
# Whether to install an updater program
install-updater = false
# Path that installers should place binaries in
install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"]

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM ubuntu as build
FROM --platform=$BUILDPLATFORM ubuntu AS build
ENV HOME="/root"
WORKDIR $HOME

25
LICENSE
View File

@@ -1371,3 +1371,28 @@ are:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- pydoclint, licensed as follows:
"""
MIT License
Copyright (c) 2023 jsh9
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

View File

@@ -29,14 +29,14 @@ An extremely fast Python linter and code formatter, written in Rust.
- 🐍 Installable via `pip`
- 🛠️ `pyproject.toml` support
- 🤝 Python 3.13 compatibility
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8), isort, and Black
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruffs-linter-compare-to-flake8), isort, and [Black](https://docs.astral.sh/ruff/faq/#how-does-ruffs-formatter-compare-to-black)
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
- 📏 Over [800 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
of popular Flake8 plugins, like flake8-bugbear
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/integrations/) for
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://docs.astral.sh/ruff/configuration/#pyprojecttoml-discovery)
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://docs.astral.sh/ruff/editors/setup)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://docs.astral.sh/ruff/configuration/#config-file-discovery)
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
functionality behind a single, common interface.
@@ -110,16 +110,47 @@ For more, see the [documentation](https://docs.astral.sh/ruff/).
1. [Who's Using Ruff?](#whos-using-ruff)
1. [License](#license)
## Getting Started
## Getting Started<a id="getting-started"></a>
For more, see the [documentation](https://docs.astral.sh/ruff/).
### Installation
Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI.
Invoke Ruff directly with [`uvx`](https://docs.astral.sh/uv/):
```shell
uvx ruff check # Lint all files in the current directory.
uvx ruff format # Format all files in the current directory.
```
Or install Ruff with `uv` (recommended), `pip`, or `pipx`:
```shell
# With uv.
uv tool install ruff@latest # Install Ruff globally.
uv add --dev ruff # Or add Ruff to your project.
# With pip.
pip install ruff
# With pipx.
pipx install ruff
```
Starting with version `0.5.0`, Ruff can be installed with our standalone installers:
```shell
# On macOS and Linux.
curl -LsSf https://astral.sh/ruff/install.sh | sh
# On Windows.
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
# For a specific version.
curl -LsSf https://astral.sh/ruff/0.9.2/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.9.2/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -152,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.5.0
rev: v0.9.2
hooks:
# Run the linter.
- id: ruff
@@ -161,11 +192,10 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
- id: ruff-format
```
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or
alongside any other editor through the [Ruff LSP](https://github.com/astral-sh/ruff-lsp).
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or with [various other editors](https://docs.astral.sh/ruff/editors/setup).
Ruff can also be used as a [GitHub Action](https://github.com/features/actions) via
[`ruff-action`](https://github.com/chartboost/ruff-action):
[`ruff-action`](https://github.com/astral-sh/ruff-action):
```yaml
name: Ruff
@@ -175,10 +205,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
- uses: astral-sh/ruff-action@v3
```
### Configuration
### Configuration<a id="configuration"></a>
Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (see:
[_Configuration_](https://docs.astral.sh/ruff/configuration/), or [_Settings_](https://docs.astral.sh/ruff/settings/)
@@ -221,8 +251,8 @@ exclude = [
line-length = 88
indent-width = 4
# Assume Python 3.8
target-version = "py38"
# Assume Python 3.9
target-version = "py39"
[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
@@ -274,7 +304,7 @@ features that may change prior to stabilization.
See `ruff help` for more on Ruff's top-level commands, or `ruff help check` and `ruff help format`
for more on the linting and formatting commands, respectively.
## Rules
## Rules<a id="rules"></a>
<!-- Begin section: Rules -->
@@ -350,21 +380,21 @@ quality tools, including:
For a complete enumeration of the supported rules, see [_Rules_](https://docs.astral.sh/ruff/rules/).
## Contributing
## Contributing<a id="contributing"></a>
Contributions are welcome and highly appreciated. To get started, check out the
[**contributing guidelines**](https://docs.astral.sh/ruff/contributing/).
You can also join us on [**Discord**](https://discord.com/invite/astral-sh).
## Support
## Support<a id="support"></a>
Having trouble? Check out the existing issues on [**GitHub**](https://github.com/astral-sh/ruff/issues),
or feel free to [**open a new one**](https://github.com/astral-sh/ruff/issues/new).
You can also ask for help on [**Discord**](https://discord.com/invite/astral-sh).
## Acknowledgements
## Acknowledgements<a id="acknowledgements"></a>
Ruff's linter draws on both the APIs and implementation details of many other
tools in the Python ecosystem, especially [Flake8](https://github.com/PyCQA/flake8), [Pyflakes](https://github.com/PyCQA/pyflakes),
@@ -388,7 +418,7 @@ Ruff is the beneficiary of a large number of [contributors](https://github.com/a
Ruff is released under the MIT license.
## Who's Using Ruff?
## Who's Using Ruff?<a id="whos-using-ruff"></a>
Ruff is used by a number of major open-source projects and companies, including:
@@ -400,12 +430,14 @@ Ruff is used by a number of major open-source projects and companies, including:
- [Babel](https://github.com/python-babel/babel)
- Benchling ([Refac](https://github.com/benchling/refac))
- [Bokeh](https://github.com/bokeh/bokeh)
- CrowdCent ([NumerBlox](https://github.com/crowdcent/numerblox)) <!-- typos: ignore -->
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
- CERN ([Indico](https://getindico.io/))
- [DVC](https://github.com/iterative/dvc)
- [Dagger](https://github.com/dagger/dagger)
- [Dagster](https://github.com/dagster-io/dagster)
- Databricks ([MLflow](https://github.com/mlflow/mlflow))
- [Dify](https://github.com/langgenius/dify)
- [FastAPI](https://github.com/tiangolo/fastapi)
- [Godot](https://github.com/godotengine/godot)
- [Gradio](https://github.com/gradio-app/gradio)
@@ -416,6 +448,7 @@ Ruff is used by a number of major open-source projects and companies, including:
- Hugging Face ([Transformers](https://github.com/huggingface/transformers),
[Datasets](https://github.com/huggingface/datasets),
[Diffusers](https://github.com/huggingface/diffusers))
- IBM ([Qiskit](https://github.com/Qiskit/qiskit))
- ING Bank ([popmon](https://github.com/ing-bank/popmon), [probatus](https://github.com/ing-bank/probatus))
- [Ibis](https://github.com/ibis-project/ibis)
- [ivy](https://github.com/unifyai/ivy)
@@ -505,7 +538,7 @@ If you're using Ruff, consider adding the Ruff badge to your project's `README.m
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
```
## License
## License<a id="license"></a>
This repository is licensed under the [MIT License](https://github.com/astral-sh/ruff/blob/main/LICENSE)

View File

@@ -1,6 +1,10 @@
[files]
# https://github.com/crate-ci/typos/issues/868
extend-exclude = ["crates/red_knot_module_resolver/vendor/**/*", "**/resources/**/*", "**/snapshots/**/*"]
extend-exclude = [
"crates/red_knot_vendored/vendor/**/*",
"**/resources/**/*",
"**/snapshots/**/*",
]
[default.extend-words]
"arange" = "arange" # e.g. `numpy.arange`
@@ -8,14 +12,18 @@ hel = "hel"
whos = "whos"
spawnve = "spawnve"
ned = "ned"
pn = "pn" # `import panel as pd` is a thing
pn = "pn" # `import panel as pn` is a thing
poit = "poit"
BA = "BA" # acronym for "Bad Allowed", used in testing.
jod = "jod" # e.g., `jod-thread`
Numer = "Numer" # Library name 'NumerBlox' in "Who's Using Ruff?"
[default]
extend-ignore-re = [
# Line ignore with trailing "spellchecker:disable-line"
"(?Rm)^.*#\\s*spellchecker:disable-line$",
"LICENSEs",
# Line ignore with trailing "spellchecker:disable-line"
"(?Rm)^.*#\\s*spellchecker:disable-line$",
"LICENSEs",
]
[default.extend-identifiers]
"FrIeNdLy" = "FrIeNdLy"

View File

@@ -1,13 +1,25 @@
doc-valid-idents = [
"..",
"CodeQL",
"FastAPI",
"IPython",
"LangChain",
"LibCST",
"McCabe",
"NumPy",
"SCREAMING_SNAKE_CASE",
"SQLAlchemy",
"StackOverflow",
"..",
"CodeQL",
"FastAPI",
"IPython",
"LangChain",
"LibCST",
"McCabe",
"NumPy",
"SCREAMING_SNAKE_CASE",
"SQLAlchemy",
"StackOverflow",
"PyCharm",
"SNMPv1",
"SNMPv2",
"SNMPv3",
"PyFlakes"
]
ignore-interior-mutability = [
# Interned is read-only. The wrapped `Rc` never gets updated.
"ruff_formatter::format_element::Interned",
# The expression is read-only.
"ruff_python_ast::hashable::HashableExpr",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "red_knot"
version = "0.1.0"
version = "0.0.0"
edition.workspace = true
rust-version.workspace = true
homepage.workspace = true
@@ -12,33 +12,34 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
red_knot_module_resolver = { workspace = true }
ruff_python_parser = { workspace = true }
ruff_python_ast = { workspace = true }
ruff_text_size = { workspace = true }
ruff_index = { workspace = true }
ruff_notebook = { workspace = true }
red_knot_python_semantic = { workspace = true }
red_knot_project = { workspace = true, features = ["zstd"] }
red_knot_server = { workspace = true }
ruff_db = { workspace = true, features = ["os", "cache"] }
anyhow = { workspace = true }
bitflags = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["wrap_help"] }
colored = { workspace = true }
countme = { workspace = true, features = ["enable"] }
crossbeam = { workspace = true }
ctrlc = { version = "3.4.4" }
dashmap = { workspace = true }
hashbrown = { workspace = true }
indexmap = { workspace = true }
is-macro = { workspace = true }
notify = { workspace = true }
parking_lot = { workspace = true }
rayon = { workspace = true }
rustc-hash = { workspace = true }
smol_str = { version = "0.2.1" }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
salsa = { workspace = true }
tracing = { workspace = true, features = ["release_max_level_debug"] }
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
tracing-flame = { workspace = true }
tracing-tree = { workspace = true }
[dev-dependencies]
ruff_db = { workspace = true, features = ["testing"] }
insta = { workspace = true, features = ["filters"] }
insta-cmd = { workspace = true }
filetime = { workspace = true }
regex = { workspace = true }
tempfile = { workspace = true }
toml = { workspace = true }
[lints]
workspace = true

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,128 @@
# Tracing
Traces are a useful tool to narrow down the location of a bug or, at least, to understand why the compiler is doing a particular thing.
Note, tracing messages with severity `debug` or greater are user-facing. They should be phrased accordingly.
Tracing spans are only shown when using `-vvv`.
## Verbosity levels
The CLI supports different verbosity levels.
- default: Only show errors and warnings.
- `-v` activates `info!`: Show generally useful information such as paths of configuration files, detected platform, etc., but it's not a lot of messages, it's something you'll activate in CI by default. cargo build e.g. shows you which packages are fresh.
- `-vv` activates `debug!` and timestamps: This should be enough information to get to the bottom of bug reports. When you're processing many packages or files, you'll get pages and pages of output, but each line is link to a specific action or state change.
- `-vvv` activates `trace!` (only in debug builds) and shows tracing-spans: At this level, you're logging everything. Most of this is wasted, it's really slow, we dump e.g. the entire resolution graph. Only useful to developers, and you almost certainly want to use `RED_KNOT_LOG` to filter it down to the area your investigating.
## Better logging with `RED_KNOT_LOG` and `RAYON_NUM_THREADS`
By default, the CLI shows messages from the `ruff` and `red_knot` crates. Tracing messages from other crates are not shown.
The `RED_KNOT_LOG` environment variable allows you to customize which messages are shown by specifying one
or more [filter directives](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives).
The `RAYON_NUM_THREADS` environment variable, meanwhile, can be used to control the level of concurrency red-knot uses.
By default, red-knot will attempt to parallelize its work so that multiple files are checked simultaneously,
but this can result in a confused logging output where messages from different threads are intertwined.
To switch off concurrency entirely and have more readable logs, use `RAYON_NUM_THREADS=1`.
### Examples
#### Show all debug messages
Shows debug messages from all crates.
```bash
RED_KNOT_LOG=debug
```
#### Show salsa query execution messages
Show the salsa `execute: my_query` messages in addition to all red knot messages.
```bash
RED_KNOT_LOG=ruff=trace,red_knot=trace,salsa=info
```
#### Show typing traces
Only show traces for the `red_knot_python_semantic::types` module.
```bash
RED_KNOT_LOG="red_knot_python_semantic::types"
```
Note: Ensure that you use `-vvv` to see tracing spans.
#### Show messages for a single file
Shows all messages that are inside of a span for a specific file.
```bash
RED_KNOT_LOG=red_knot[{file=/home/micha/astral/test/x.py}]=trace
```
**Note**: Tracing still shows all spans because tracing can't know at the time of entering the span
whether one if its children has the file `x.py`.
**Note**: Salsa currently logs the entire memoized values. In our case, the source text and parsed AST.
This very quickly leads to extremely long outputs.
## Tracing and Salsa
Be mindful about using `tracing` in Salsa queries, especially when using `warn` or `error` because it isn't guaranteed
that the query will execute after restoring from a persistent cache. In which case the user won't see the message.
For example, don't use `tracing` to show the user a message when generating a lint violation failed
because the message would only be shown when linting the file the first time, but not on subsequent analysis
runs or when restoring from a persistent cache. This can be confusing for users because they
don't understand why a specific lint violation isn't raised. Instead, change your
query to return the failure as part of the query's result or use a Salsa accumulator.
## Tracing in tests
You can use `ruff_db::testing::setup_logging` or `ruff_db::testing::setup_logging_with_filter` to set up logging in tests.
```rust
use ruff_db::testing::setup_logging;
#[test]
fn test() {
let _logging = setup_logging();
tracing::info!("This message will be printed to stderr");
}
```
Note: Most test runners capture stderr and only show its output when a test fails.
Note also that `setup_logging` only sets up logging for the current thread because [`set_global_default`](https://docs.rs/tracing/latest/tracing/subscriber/fn.set_global_default.html) can only be
called **once**.
## Release builds
`trace!` events are removed in release builds.
## Profiling
Red Knot generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable `RED_KNOT_LOG_PROFILE` to `1` or `true`.
```bash
RED_KNOT_LOG_PROFILE=1 red_knot -- --current-directory=../test -vvv
```
You can convert the textual representation into a visual one using `inferno`.
```shell
cargo install inferno
```
```shell
# flamegraph
cat tracing.folded | inferno-flamegraph > tracing-flamegraph.svg
# flamechart
cat tracing.folded | inferno-flamegraph --flamechart > tracing-flamechart.svg
```
![Example flamegraph](./tracing-flamegraph.png)
See [`tracing-flame`](https://crates.io/crates/tracing-flame) for more details.

View File

@@ -1,418 +0,0 @@
use std::any::type_name;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use rustc_hash::FxHashMap;
use ruff_index::{Idx, IndexVec};
use ruff_python_ast::visitor::source_order;
use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, TraversalSignal};
use ruff_python_ast::{
AnyNodeRef, AstNode, ExceptHandler, ExceptHandlerExceptHandler, Expr, MatchCase, ModModule,
NodeKind, Parameter, Stmt, StmtAnnAssign, StmtAssign, StmtAugAssign, StmtClassDef,
StmtFunctionDef, StmtGlobal, StmtImport, StmtImportFrom, StmtNonlocal, StmtTypeAlias,
TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, WithItem,
};
use ruff_text_size::{Ranged, TextRange};
/// A type agnostic ID that uniquely identifies an AST node in a file.
#[ruff_index::newtype_index]
pub struct AstId;
/// A typed ID that uniquely identifies an AST node in a file.
///
/// This is different from [`AstId`] in that it is a combination of ID and the type of the node the ID identifies.
/// Typing the ID prevents mixing IDs of different node types and allows to restrict the API to only accept
/// nodes for which an ID has been created (not all AST nodes get an ID).
pub struct TypedAstId<N: HasAstId> {
erased: AstId,
_marker: PhantomData<fn() -> N>,
}
impl<N: HasAstId> TypedAstId<N> {
/// Upcasts this ID from a more specific node type to a more general node type.
pub fn upcast<M: HasAstId>(self) -> TypedAstId<M>
where
N: Into<M>,
{
TypedAstId {
erased: self.erased,
_marker: PhantomData,
}
}
}
impl<N: HasAstId> Copy for TypedAstId<N> {}
impl<N: HasAstId> Clone for TypedAstId<N> {
fn clone(&self) -> Self {
*self
}
}
impl<N: HasAstId> PartialEq for TypedAstId<N> {
fn eq(&self, other: &Self) -> bool {
self.erased == other.erased
}
}
impl<N: HasAstId> Eq for TypedAstId<N> {}
impl<N: HasAstId> Hash for TypedAstId<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.erased.hash(state);
}
}
impl<N: HasAstId> Debug for TypedAstId<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TypedAstId")
.field(&self.erased)
.field(&type_name::<N>())
.finish()
}
}
pub struct AstIds {
ids: IndexVec<AstId, NodeKey>,
reverse: FxHashMap<NodeKey, AstId>,
}
impl AstIds {
// TODO rust analyzer doesn't allocate an ID for every node. It only allocates ids for
// nodes with a corresponding HIR element, that is nodes that are definitions.
pub fn from_module(module: &ModModule) -> Self {
let mut visitor = AstIdsVisitor::default();
// TODO: visit_module?
// Make sure we visit the root
visitor.create_id(module);
visitor.visit_body(&module.body);
while let Some(deferred) = visitor.deferred.pop() {
match deferred {
DeferredNode::FunctionDefinition(def) => {
def.visit_source_order(&mut visitor);
}
DeferredNode::ClassDefinition(def) => def.visit_source_order(&mut visitor),
}
}
AstIds {
ids: visitor.ids,
reverse: visitor.reverse,
}
}
/// Returns the ID to the root node.
pub fn root(&self) -> NodeKey {
self.ids[AstId::new(0)]
}
/// Returns the [`TypedAstId`] for a node.
pub fn ast_id<N: HasAstId>(&self, node: &N) -> TypedAstId<N> {
let key = node.syntax_node_key();
TypedAstId {
erased: self.reverse.get(&key).copied().unwrap(),
_marker: PhantomData,
}
}
/// Returns the [`TypedAstId`] for the node identified with the given [`TypedNodeKey`].
pub fn ast_id_for_key<N: HasAstId>(&self, node: &TypedNodeKey<N>) -> TypedAstId<N> {
let ast_id = self.ast_id_for_node_key(node.inner);
TypedAstId {
erased: ast_id,
_marker: PhantomData,
}
}
/// Returns the untyped [`AstId`] for the node identified by the given `node` key.
pub fn ast_id_for_node_key(&self, node: NodeKey) -> AstId {
self.reverse
.get(&node)
.copied()
.expect("Can't find node in AstIds map.")
}
/// Returns the [`TypedNodeKey`] for the node identified by the given [`TypedAstId`].
pub fn key<N: HasAstId>(&self, id: TypedAstId<N>) -> TypedNodeKey<N> {
let syntax_key = self.ids[id.erased];
TypedNodeKey::new(syntax_key).unwrap()
}
pub fn node_key<H: HasAstId>(&self, id: TypedAstId<H>) -> NodeKey {
self.ids[id.erased]
}
}
impl std::fmt::Debug for AstIds {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_map();
for (key, value) in self.ids.iter_enumerated() {
map.entry(&key, &value);
}
map.finish()
}
}
impl PartialEq for AstIds {
fn eq(&self, other: &Self) -> bool {
self.ids == other.ids
}
}
impl Eq for AstIds {}
#[derive(Default)]
struct AstIdsVisitor<'a> {
ids: IndexVec<AstId, NodeKey>,
reverse: FxHashMap<NodeKey, AstId>,
deferred: Vec<DeferredNode<'a>>,
}
impl<'a> AstIdsVisitor<'a> {
fn create_id<A: HasAstId>(&mut self, node: &A) {
let node_key = node.syntax_node_key();
let id = self.ids.push(node_key);
self.reverse.insert(node_key, id);
}
}
impl<'a> SourceOrderVisitor<'a> for AstIdsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt {
Stmt::FunctionDef(def) => {
self.create_id(def);
self.deferred.push(DeferredNode::FunctionDefinition(def));
return;
}
// TODO defer visiting the assignment body, type alias parameters etc?
Stmt::ClassDef(def) => {
self.create_id(def);
self.deferred.push(DeferredNode::ClassDefinition(def));
return;
}
Stmt::Expr(_) => {
// Skip
return;
}
Stmt::Return(_) => {}
Stmt::Delete(_) => {}
Stmt::Assign(assignment) => self.create_id(assignment),
Stmt::AugAssign(assignment) => {
self.create_id(assignment);
}
Stmt::AnnAssign(assignment) => self.create_id(assignment),
Stmt::TypeAlias(assignment) => self.create_id(assignment),
Stmt::For(_) => {}
Stmt::While(_) => {}
Stmt::If(_) => {}
Stmt::With(_) => {}
Stmt::Match(_) => {}
Stmt::Raise(_) => {}
Stmt::Try(_) => {}
Stmt::Assert(_) => {}
Stmt::Import(import) => self.create_id(import),
Stmt::ImportFrom(import_from) => self.create_id(import_from),
Stmt::Global(global) => self.create_id(global),
Stmt::Nonlocal(non_local) => self.create_id(non_local),
Stmt::Pass(_) => {}
Stmt::Break(_) => {}
Stmt::Continue(_) => {}
Stmt::IpyEscapeCommand(_) => {}
}
source_order::walk_stmt(self, stmt);
}
fn visit_expr(&mut self, _expr: &'a Expr) {}
fn visit_parameter(&mut self, parameter: &'a Parameter) {
self.create_id(parameter);
source_order::walk_parameter(self, parameter);
}
fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) {
match except_handler {
ExceptHandler::ExceptHandler(except_handler) => {
self.create_id(except_handler);
}
}
source_order::walk_except_handler(self, except_handler);
}
fn visit_with_item(&mut self, with_item: &'a WithItem) {
self.create_id(with_item);
source_order::walk_with_item(self, with_item);
}
fn visit_match_case(&mut self, match_case: &'a MatchCase) {
self.create_id(match_case);
source_order::walk_match_case(self, match_case);
}
fn visit_type_param(&mut self, type_param: &'a TypeParam) {
self.create_id(type_param);
}
}
enum DeferredNode<'a> {
FunctionDefinition(&'a StmtFunctionDef),
ClassDefinition(&'a StmtClassDef),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct TypedNodeKey<N: AstNode> {
/// The type erased node key.
inner: NodeKey,
_marker: PhantomData<fn() -> N>,
}
impl<N: AstNode> TypedNodeKey<N> {
pub fn from_node(node: &N) -> Self {
let inner = NodeKey::from_node(node.as_any_node_ref());
Self {
inner,
_marker: PhantomData,
}
}
pub fn new(node_key: NodeKey) -> Option<Self> {
N::can_cast(node_key.kind).then_some(TypedNodeKey {
inner: node_key,
_marker: PhantomData,
})
}
pub fn resolve<'a>(&self, root: AnyNodeRef<'a>) -> Option<N::Ref<'a>> {
let node_ref = self.inner.resolve(root)?;
Some(N::cast_ref(node_ref).unwrap())
}
pub fn resolve_unwrap<'a>(&self, root: AnyNodeRef<'a>) -> N::Ref<'a> {
self.resolve(root).expect("node should resolve")
}
pub fn erased(&self) -> &NodeKey {
&self.inner
}
}
struct FindNodeKeyVisitor<'a> {
key: NodeKey,
result: Option<AnyNodeRef<'a>>,
}
impl<'a> SourceOrderVisitor<'a> for FindNodeKeyVisitor<'a> {
fn enter_node(&mut self, node: AnyNodeRef<'a>) -> TraversalSignal {
if self.result.is_some() {
return TraversalSignal::Skip;
}
if node.range() == self.key.range && node.kind() == self.key.kind {
self.result = Some(node);
TraversalSignal::Skip
} else if node.range().contains_range(self.key.range) {
TraversalSignal::Traverse
} else {
TraversalSignal::Skip
}
}
fn visit_body(&mut self, body: &'a [Stmt]) {
// TODO it would be more efficient to use binary search instead of linear
for stmt in body {
if stmt.range().start() > self.key.range.end() {
break;
}
self.visit_stmt(stmt);
}
}
}
// TODO an alternative to this is to have a `NodeId` on each node (in increasing order depending on the position).
// This would allow to reduce the size of this to a u32.
// What would be nice if we could use an `Arc::weak_ref` here but that only works if we use
// `Arc` internally
// TODO: Implement the logic to resolve a node, given a db (and the correct file).
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct NodeKey {
kind: NodeKind,
range: TextRange,
}
impl NodeKey {
pub fn from_node(node: AnyNodeRef) -> Self {
NodeKey {
kind: node.kind(),
range: node.range(),
}
}
pub fn resolve<'a>(&self, root: AnyNodeRef<'a>) -> Option<AnyNodeRef<'a>> {
// We need to do a binary search here. Only traverse into a node if the range is withint the node
let mut visitor = FindNodeKeyVisitor {
key: *self,
result: None,
};
if visitor.enter_node(root) == TraversalSignal::Traverse {
root.visit_preorder(&mut visitor);
}
visitor.result
}
}
/// Marker trait implemented by AST nodes for which we extract the `AstId`.
pub trait HasAstId: AstNode {
fn node_key(&self) -> TypedNodeKey<Self>
where
Self: Sized,
{
TypedNodeKey {
inner: self.syntax_node_key(),
_marker: PhantomData,
}
}
fn syntax_node_key(&self) -> NodeKey {
NodeKey {
kind: self.as_any_node_ref().kind(),
range: self.range(),
}
}
}
impl HasAstId for StmtFunctionDef {}
impl HasAstId for StmtClassDef {}
impl HasAstId for StmtAnnAssign {}
impl HasAstId for StmtAugAssign {}
impl HasAstId for StmtAssign {}
impl HasAstId for StmtTypeAlias {}
impl HasAstId for ModModule {}
impl HasAstId for StmtImport {}
impl HasAstId for StmtImportFrom {}
impl HasAstId for Parameter {}
impl HasAstId for TypeParam {}
impl HasAstId for Stmt {}
impl HasAstId for TypeParamTypeVar {}
impl HasAstId for TypeParamTypeVarTuple {}
impl HasAstId for TypeParamParamSpec {}
impl HasAstId for StmtGlobal {}
impl HasAstId for StmtNonlocal {}
impl HasAstId for ExceptHandlerExceptHandler {}
impl HasAstId for WithItem {}
impl HasAstId for MatchCase {}

View File

@@ -1,165 +0,0 @@
use std::fmt::Formatter;
use std::hash::Hash;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::db::QueryResult;
use dashmap::mapref::entry::Entry;
use crate::FxDashMap;
/// Simple key value cache that locks on a per-key level.
pub struct KeyValueCache<K, V> {
map: FxDashMap<K, V>,
statistics: CacheStatistics,
}
impl<K, V> KeyValueCache<K, V>
where
K: Eq + Hash + Clone,
V: Clone,
{
pub fn try_get(&self, key: &K) -> Option<V> {
if let Some(existing) = self.map.get(key) {
self.statistics.hit();
Some(existing.clone())
} else {
self.statistics.miss();
None
}
}
pub fn get<F>(&self, key: &K, compute: F) -> QueryResult<V>
where
F: FnOnce(&K) -> QueryResult<V>,
{
Ok(match self.map.entry(key.clone()) {
Entry::Occupied(cached) => {
self.statistics.hit();
cached.get().clone()
}
Entry::Vacant(vacant) => {
self.statistics.miss();
let value = compute(key)?;
vacant.insert(value.clone());
value
}
})
}
pub fn set(&mut self, key: K, value: V) {
self.map.insert(key, value);
}
pub fn remove(&mut self, key: &K) -> Option<V> {
self.map.remove(key).map(|(_, value)| value)
}
pub fn clear(&mut self) {
self.map.clear();
self.map.shrink_to_fit();
}
pub fn statistics(&self) -> Option<Statistics> {
self.statistics.to_statistics()
}
}
impl<K, V> Default for KeyValueCache<K, V>
where
K: Eq + Hash,
V: Clone,
{
fn default() -> Self {
Self {
map: FxDashMap::default(),
statistics: CacheStatistics::default(),
}
}
}
impl<K, V> std::fmt::Debug for KeyValueCache<K, V>
where
K: std::fmt::Debug + Eq + Hash,
V: std::fmt::Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_map();
for entry in &self.map {
debug.entry(&entry.value(), &entry.key());
}
debug.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Statistics {
pub hits: usize,
pub misses: usize,
}
impl Statistics {
#[allow(clippy::cast_precision_loss)]
pub fn hit_rate(&self) -> Option<f64> {
if self.hits + self.misses == 0 {
return None;
}
Some((self.hits as f64) / (self.hits + self.misses) as f64)
}
}
#[cfg(debug_assertions)]
pub type CacheStatistics = DebugStatistics;
#[cfg(not(debug_assertions))]
pub type CacheStatistics = ReleaseStatistics;
pub trait StatisticsRecorder {
fn hit(&self);
fn miss(&self);
fn to_statistics(&self) -> Option<Statistics>;
}
#[derive(Debug, Default)]
pub struct DebugStatistics {
hits: AtomicUsize,
misses: AtomicUsize,
}
impl StatisticsRecorder for DebugStatistics {
// TODO figure out appropriate Ordering
fn hit(&self) {
self.hits.fetch_add(1, Ordering::SeqCst);
}
fn miss(&self) {
self.misses.fetch_add(1, Ordering::SeqCst);
}
fn to_statistics(&self) -> Option<Statistics> {
let hits = self.hits.load(Ordering::SeqCst);
let misses = self.misses.load(Ordering::SeqCst);
Some(Statistics { hits, misses })
}
}
#[derive(Debug, Default)]
pub struct ReleaseStatistics;
impl StatisticsRecorder for ReleaseStatistics {
#[inline]
fn hit(&self) {}
#[inline]
fn miss(&self) {}
#[inline]
fn to_statistics(&self) -> Option<Statistics> {
None
}
}

View File

@@ -1,42 +0,0 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
pub struct CancellationTokenSource {
signal: Arc<AtomicBool>,
}
impl CancellationTokenSource {
pub fn new() -> Self {
Self {
signal: Arc::new(AtomicBool::new(false)),
}
}
#[tracing::instrument(level = "trace", skip_all)]
pub fn cancel(&self) {
self.signal.store(true, std::sync::atomic::Ordering::SeqCst);
}
pub fn is_cancelled(&self) -> bool {
self.signal.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn token(&self) -> CancellationToken {
CancellationToken {
signal: self.signal.clone(),
}
}
}
#[derive(Clone, Debug)]
pub struct CancellationToken {
signal: Arc<AtomicBool>,
}
impl CancellationToken {
/// Returns `true` if cancellation has been requested.
pub fn is_cancelled(&self) -> bool {
self.signal.load(std::sync::atomic::Ordering::SeqCst)
}
}

View File

@@ -1,248 +0,0 @@
use std::sync::Arc;
pub use jars::{HasJar, HasJars};
pub use query::{QueryError, QueryResult};
pub use runtime::DbRuntime;
pub use storage::JarsStorage;
use crate::files::FileId;
use crate::lint::{LintSemanticStorage, LintSyntaxStorage};
use crate::module::ModuleResolver;
use crate::parse::ParsedStorage;
use crate::semantic::SemanticIndexStorage;
use crate::semantic::TypeStore;
use crate::source::SourceStorage;
mod jars;
mod query;
mod runtime;
mod storage;
pub trait Database {
/// Returns a reference to the runtime of the current worker.
fn runtime(&self) -> &DbRuntime;
/// Returns a mutable reference to the runtime. Only one worker can hold a mutable reference to the runtime.
fn runtime_mut(&mut self) -> &mut DbRuntime;
/// Returns `Ok` if the queries have not been cancelled and `Err(QueryError::Cancelled)` otherwise.
fn cancelled(&self) -> QueryResult<()> {
self.runtime().cancelled()
}
/// Returns `true` if the queries have been cancelled.
fn is_cancelled(&self) -> bool {
self.runtime().is_cancelled()
}
}
/// Database that supports running queries from multiple threads.
pub trait ParallelDatabase: Database + Send {
/// Creates a snapshot of the database state that can be used to query the database in another thread.
///
/// The snapshot is a read-only view of the database but query results are shared between threads.
/// All queries will be automatically cancelled when applying any mutations (calling [`HasJars::jars_mut`])
/// to the database (not the snapshot, because they're readonly).
///
/// ## Creating a snapshot
///
/// Creating a snapshot of the database's jars is cheap but creating a snapshot of
/// other state stored on the database might require deep-cloning data. That's why you should
/// avoid creating snapshots in a hot function (e.g. don't create a snapshot for each file, instead
/// create a snapshot when scheduling the check of an entire program).
///
/// ## Salsa compatibility
/// Salsa prohibits creating a snapshot while running a local query (it's fine if other workers run a query) [[source](https://github.com/salsa-rs/salsa/issues/80)].
/// We should avoid creating snapshots while running a query because we might want to adopt Salsa in the future (if we can figure out persistent caching).
/// Unfortunately, the infrastructure doesn't provide an automated way of knowing when a query is run, that's
/// why we have to "enforce" this constraint manually.
#[must_use]
fn snapshot(&self) -> Snapshot<Self>;
}
pub trait DbWithJar<Jar>: Database + HasJar<Jar> {}
/// Readonly snapshot of a database.
///
/// ## Dead locks
/// A snapshot should always be dropped as soon as it is no longer necessary to run queries.
/// Storing the snapshot without running a query or periodically checking if cancellation was requested
/// can lead to deadlocks because mutating the [`Database`] requires cancels all pending queries
/// and waiting for all [`Snapshot`]s to be dropped.
#[derive(Debug)]
pub struct Snapshot<DB: ?Sized>
where
DB: ParallelDatabase,
{
db: DB,
}
impl<DB> Snapshot<DB>
where
DB: ParallelDatabase,
{
pub fn new(db: DB) -> Self {
Snapshot { db }
}
}
impl<DB> std::ops::Deref for Snapshot<DB>
where
DB: ParallelDatabase,
{
type Target = DB;
fn deref(&self) -> &DB {
&self.db
}
}
pub trait Upcast<T: ?Sized> {
fn upcast(&self) -> &T;
}
// Red knot specific databases code.
pub trait SourceDb: DbWithJar<SourceJar> {
// queries
fn file_id(&self, path: &std::path::Path) -> FileId;
fn file_path(&self, file_id: FileId) -> Arc<std::path::Path>;
}
pub trait SemanticDb: SourceDb + DbWithJar<SemanticJar> + Upcast<dyn SourceDb> {}
pub trait LintDb: SemanticDb + DbWithJar<LintJar> + Upcast<dyn SemanticDb> {}
pub trait Db: LintDb + Upcast<dyn LintDb> {}
#[derive(Debug, Default)]
pub struct SourceJar {
pub sources: SourceStorage,
pub parsed: ParsedStorage,
}
#[derive(Debug, Default)]
pub struct SemanticJar {
pub module_resolver: ModuleResolver,
pub semantic_indices: SemanticIndexStorage,
pub type_store: TypeStore,
}
#[derive(Debug, Default)]
pub struct LintJar {
pub lint_syntax: LintSyntaxStorage,
pub lint_semantic: LintSemanticStorage,
}
#[cfg(test)]
pub(crate) mod tests {
use std::path::Path;
use std::sync::Arc;
use crate::db::{
Database, DbRuntime, DbWithJar, HasJar, HasJars, JarsStorage, LintDb, LintJar, QueryResult,
SourceDb, SourceJar, Upcast,
};
use crate::files::{FileId, Files};
use super::{SemanticDb, SemanticJar};
// This can be a partial database used in a single crate for testing.
// It would hold fewer data than the full database.
#[derive(Debug, Default)]
pub(crate) struct TestDb {
files: Files,
jars: JarsStorage<Self>,
}
impl HasJar<SourceJar> for TestDb {
fn jar(&self) -> QueryResult<&SourceJar> {
Ok(&self.jars()?.0)
}
fn jar_mut(&mut self) -> &mut SourceJar {
&mut self.jars_mut().0
}
}
impl HasJar<SemanticJar> for TestDb {
fn jar(&self) -> QueryResult<&SemanticJar> {
Ok(&self.jars()?.1)
}
fn jar_mut(&mut self) -> &mut SemanticJar {
&mut self.jars_mut().1
}
}
impl HasJar<LintJar> for TestDb {
fn jar(&self) -> QueryResult<&LintJar> {
Ok(&self.jars()?.2)
}
fn jar_mut(&mut self) -> &mut LintJar {
&mut self.jars_mut().2
}
}
impl SourceDb for TestDb {
fn file_id(&self, path: &Path) -> FileId {
self.files.intern(path)
}
fn file_path(&self, file_id: FileId) -> Arc<Path> {
self.files.path(file_id)
}
}
impl DbWithJar<SourceJar> for TestDb {}
impl Upcast<dyn SourceDb> for TestDb {
fn upcast(&self) -> &(dyn SourceDb + 'static) {
self
}
}
impl SemanticDb for TestDb {}
impl DbWithJar<SemanticJar> for TestDb {}
impl Upcast<dyn SemanticDb> for TestDb {
fn upcast(&self) -> &(dyn SemanticDb + 'static) {
self
}
}
impl LintDb for TestDb {}
impl Upcast<dyn LintDb> for TestDb {
fn upcast(&self) -> &(dyn LintDb + 'static) {
self
}
}
impl DbWithJar<LintJar> for TestDb {}
impl HasJars for TestDb {
type Jars = (SourceJar, SemanticJar, LintJar);
fn jars(&self) -> QueryResult<&Self::Jars> {
self.jars.jars()
}
fn jars_mut(&mut self) -> &mut Self::Jars {
self.jars.jars_mut()
}
}
impl Database for TestDb {
fn runtime(&self) -> &DbRuntime {
self.jars.runtime()
}
fn runtime_mut(&mut self) -> &mut DbRuntime {
self.jars.runtime_mut()
}
}
}

View File

@@ -1,37 +0,0 @@
use crate::db::query::QueryResult;
/// Gives access to a specific jar in the database.
///
/// Nope, the terminology isn't borrowed from Java but from Salsa <https://salsa-rs.github.io/salsa/>,
/// which is an analogy to storing the salsa in different jars.
///
/// The basic idea is that each crate can define its own jar and the jars can be combined to a single
/// database in the top level crate. Each crate also defines its own `Database` trait. The combination of
/// `Database` trait and the jar allows to write queries in isolation without having to know how they get composed at the upper levels.
///
/// Salsa further defines a `HasIngredient` trait which slices the jar to a specific storage (e.g. a specific cache).
/// We don't need this just yet because we write our queries by hand. We may want a similar trait if we decide
/// to use a macro to generate the queries.
pub trait HasJar<T> {
/// Gives a read-only reference to the jar.
fn jar(&self) -> QueryResult<&T>;
/// Gives a mutable reference to the jar.
fn jar_mut(&mut self) -> &mut T;
}
/// Gives access to the jars in a database.
pub trait HasJars {
/// A type storing the jars.
///
/// Most commonly, this is a tuple where each jar is a tuple element.
type Jars: Default;
/// Gives access to the underlying jars but tests if the queries have been cancelled.
///
/// Returns `Err(QueryError::Cancelled)` if the queries have been cancelled.
fn jars(&self) -> QueryResult<&Self::Jars>;
/// Gives mutable access to the underlying jars.
fn jars_mut(&mut self) -> &mut Self::Jars;
}

View File

@@ -1,20 +0,0 @@
use std::fmt::{Display, Formatter};
/// Reason why a db query operation failed.
#[derive(Debug, Clone, Copy)]
pub enum QueryError {
/// The query was cancelled because the DB was mutated or the query was cancelled by the host (e.g. on a file change or when pressing CTRL+C).
Cancelled,
}
impl Display for QueryError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
QueryError::Cancelled => f.write_str("query was cancelled"),
}
}
}
impl std::error::Error for QueryError {}
pub type QueryResult<T> = Result<T, QueryError>;

View File

@@ -1,41 +0,0 @@
use crate::cancellation::CancellationTokenSource;
use crate::db::{QueryError, QueryResult};
/// Holds the jar agnostic state of the database.
#[derive(Debug, Default)]
pub struct DbRuntime {
/// The cancellation token source used to signal other works that the queries should be aborted and
/// exit at the next possible point.
cancellation_token: CancellationTokenSource,
}
impl DbRuntime {
pub(super) fn snapshot(&self) -> Self {
Self {
cancellation_token: self.cancellation_token.clone(),
}
}
/// Cancels the pending queries of other workers. The current worker cannot have any pending
/// queries because we're holding a mutable reference to the runtime.
pub(super) fn cancel_other_workers(&mut self) {
self.cancellation_token.cancel();
// Set a new cancellation token so that we're in a non-cancelled state again when running the next
// query.
self.cancellation_token = CancellationTokenSource::default();
}
/// Returns `Ok` if the queries have not been cancelled and `Err(QueryError::Cancelled)` otherwise.
pub(super) fn cancelled(&self) -> QueryResult<()> {
if self.cancellation_token.is_cancelled() {
Err(QueryError::Cancelled)
} else {
Ok(())
}
}
/// Returns `true` if the queries have been cancelled.
pub(super) fn is_cancelled(&self) -> bool {
self.cancellation_token.is_cancelled()
}
}

View File

@@ -1,117 +0,0 @@
use std::fmt::Formatter;
use std::sync::Arc;
use crossbeam::sync::WaitGroup;
use crate::db::query::QueryResult;
use crate::db::runtime::DbRuntime;
use crate::db::{HasJars, ParallelDatabase};
/// Stores the jars of a database and the state for each worker.
///
/// Today, all state is shared across all workers, but it may be desired to store data per worker in the future.
pub struct JarsStorage<T>
where
T: HasJars + Sized,
{
// It's important that `jars_wait_group` is declared after `jars` to ensure that `jars` is dropped first.
// See https://doc.rust-lang.org/reference/destructors.html
/// Stores the jars of the database.
jars: Arc<T::Jars>,
/// Used to count the references to `jars`. Allows implementing `jars_mut` without requiring to clone `jars`.
jars_wait_group: WaitGroup,
/// The data agnostic state.
runtime: DbRuntime,
}
impl<Db> JarsStorage<Db>
where
Db: HasJars,
{
pub(super) fn new() -> Self {
Self {
jars: Arc::new(Db::Jars::default()),
jars_wait_group: WaitGroup::default(),
runtime: DbRuntime::default(),
}
}
/// Creates a snapshot of the jars.
///
/// Creating the snapshot is cheap because it doesn't clone the jars, it only increments a ref counter.
#[must_use]
pub fn snapshot(&self) -> JarsStorage<Db>
where
Db: ParallelDatabase,
{
Self {
jars: self.jars.clone(),
jars_wait_group: self.jars_wait_group.clone(),
runtime: self.runtime.snapshot(),
}
}
pub(crate) fn jars(&self) -> QueryResult<&Db::Jars> {
self.runtime.cancelled()?;
Ok(&self.jars)
}
/// Returns a mutable reference to the jars without cloning their content.
///
/// The method cancels any pending queries of other works and waits for them to complete so that
/// this instance is the only instance holding a reference to the jars.
pub(crate) fn jars_mut(&mut self) -> &mut Db::Jars {
// We have a mutable ref here, so no more workers can be spawned between calling this function and taking the mut ref below.
self.cancel_other_workers();
// Now all other references to `self.jars` should have been released. We can now safely return a mutable reference
// to the Arc's content.
let jars =
Arc::get_mut(&mut self.jars).expect("All references to jars should have been released");
jars
}
pub(crate) fn runtime(&self) -> &DbRuntime {
&self.runtime
}
pub(crate) fn runtime_mut(&mut self) -> &mut DbRuntime {
// Note: This method may need to use a similar trick to `jars_mut` if `DbRuntime` is ever to store data that is shared between workers.
&mut self.runtime
}
#[tracing::instrument(level = "trace", skip(self))]
fn cancel_other_workers(&mut self) {
self.runtime.cancel_other_workers();
// Wait for all other works to complete.
let existing_wait = std::mem::take(&mut self.jars_wait_group);
existing_wait.wait();
}
}
impl<Db> Default for JarsStorage<Db>
where
Db: HasJars,
{
fn default() -> Self {
Self::new()
}
}
impl<T> std::fmt::Debug for JarsStorage<T>
where
T: HasJars,
<T as HasJars>::Jars: std::fmt::Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SharedStorage")
.field("jars", &self.jars)
.field("jars_wait_group", &self.jars_wait_group)
.field("runtime", &self.runtime)
.finish()
}
}

View File

@@ -1,180 +0,0 @@
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::path::Path;
use std::sync::Arc;
use hashbrown::hash_map::RawEntryMut;
use parking_lot::RwLock;
use rustc_hash::FxHasher;
use ruff_index::{newtype_index, IndexVec};
type Map<K, V> = hashbrown::HashMap<K, V, ()>;
#[newtype_index]
pub struct FileId;
// TODO we'll need a higher level virtual file system abstraction that allows testing if a file exists
// or retrieving its content (ideally lazily and in a way that the memory can be retained later)
// I suspect that we'll end up with a FileSystem trait and our own Path abstraction.
#[derive(Default)]
pub struct Files {
inner: Arc<RwLock<FilesInner>>,
}
impl Files {
#[tracing::instrument(level = "debug", skip(self))]
pub fn intern(&self, path: &Path) -> FileId {
self.inner.write().intern(path)
}
pub fn try_get(&self, path: &Path) -> Option<FileId> {
self.inner.read().try_get(path)
}
#[tracing::instrument(level = "debug", skip(self))]
pub fn path(&self, id: FileId) -> Arc<Path> {
self.inner.read().path(id)
}
/// Snapshots files for a new database snapshot.
///
/// This method should not be used outside a database snapshot.
#[must_use]
pub fn snapshot(&self) -> Files {
Files {
inner: self.inner.clone(),
}
}
}
impl Debug for Files {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let files = self.inner.read();
let mut debug = f.debug_map();
for item in files.iter() {
debug.entry(&item.0, &item.1);
}
debug.finish()
}
}
impl PartialEq for Files {
fn eq(&self, other: &Self) -> bool {
self.inner.read().eq(&other.inner.read())
}
}
impl Eq for Files {}
#[derive(Default)]
struct FilesInner {
by_path: Map<FileId, ()>,
// TODO should we use a map here to reclaim the space for removed files?
// TODO I think we should use our own path abstraction here to avoid having to normalize paths
// and dealing with non-utf paths everywhere.
by_id: IndexVec<FileId, Arc<Path>>,
}
impl FilesInner {
/// Inserts the path and returns a new id for it or returns the id if it is an existing path.
// TODO should this accept Path or PathBuf?
pub(crate) fn intern(&mut self, path: &Path) -> FileId {
let hash = FilesInner::hash_path(path);
let entry = self
.by_path
.raw_entry_mut()
.from_hash(hash, |existing_file| &*self.by_id[*existing_file] == path);
match entry {
RawEntryMut::Occupied(entry) => *entry.key(),
RawEntryMut::Vacant(entry) => {
let id = self.by_id.push(Arc::from(path));
entry.insert_with_hasher(hash, id, (), |file| {
FilesInner::hash_path(&self.by_id[*file])
});
id
}
}
}
fn hash_path(path: &Path) -> u64 {
let mut hasher = FxHasher::default();
path.hash(&mut hasher);
hasher.finish()
}
pub(crate) fn try_get(&self, path: &Path) -> Option<FileId> {
let mut hasher = FxHasher::default();
path.hash(&mut hasher);
let hash = hasher.finish();
Some(
*self
.by_path
.raw_entry()
.from_hash(hash, |existing_file| &*self.by_id[*existing_file] == path)?
.0,
)
}
/// Returns the path for the file with the given id.
pub(crate) fn path(&self, id: FileId) -> Arc<Path> {
self.by_id[id].clone()
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (FileId, Arc<Path>)> + '_ {
self.by_path.keys().map(|id| (*id, self.by_id[*id].clone()))
}
}
impl PartialEq for FilesInner {
fn eq(&self, other: &Self) -> bool {
self.by_id == other.by_id
}
}
impl Eq for FilesInner {}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn insert_path_twice_same_id() {
let files = Files::default();
let path = PathBuf::from("foo/bar");
let id1 = files.intern(&path);
let id2 = files.intern(&path);
assert_eq!(id1, id2);
}
#[test]
fn insert_different_paths_different_ids() {
let files = Files::default();
let path1 = PathBuf::from("foo/bar");
let path2 = PathBuf::from("foo/bar/baz");
let id1 = files.intern(&path1);
let id2 = files.intern(&path2);
assert_ne!(id1, id2);
}
#[test]
fn four_files() {
let files = Files::default();
let foo_path = PathBuf::from("foo");
let foo_id = files.intern(&foo_path);
let bar_path = PathBuf::from("bar");
files.intern(&bar_path);
let baz_path = PathBuf::from("baz");
files.intern(&baz_path);
let qux_path = PathBuf::from("qux");
files.intern(&qux_path);
let foo_id_2 = files.try_get(&foo_path).expect("foo_path to be found");
assert_eq!(foo_id_2, foo_id);
}
}

View File

@@ -1,67 +0,0 @@
//! Key observations
//!
//! The HIR (High-Level Intermediate Representation) avoids allocations to large extends by:
//! * Using an arena per node type
//! * using ids and id ranges to reference items.
//!
//! Using separate arena per node type has the advantage that the IDs are relatively stable, because
//! they only change when a node of the same kind has been added or removed. (What's unclear is if that matters or if
//! it still triggers a re-compute because the AST-id in the node has changed).
//!
//! The HIR does not store all details. It mainly stores the *public* interface. There's a reference
//! back to the AST node to get more details.
//!
//!
use crate::ast_ids::{HasAstId, TypedAstId};
use crate::files::FileId;
use std::fmt::Formatter;
use std::hash::{Hash, Hasher};
pub struct HirAstId<N: HasAstId> {
file_id: FileId,
node_id: TypedAstId<N>,
}
impl<N: HasAstId> Copy for HirAstId<N> {}
impl<N: HasAstId> Clone for HirAstId<N> {
fn clone(&self) -> Self {
*self
}
}
impl<N: HasAstId> PartialEq for HirAstId<N> {
fn eq(&self, other: &Self) -> bool {
self.file_id == other.file_id && self.node_id == other.node_id
}
}
impl<N: HasAstId> Eq for HirAstId<N> {}
impl<N: HasAstId> std::fmt::Debug for HirAstId<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HirAstId")
.field("file_id", &self.file_id)
.field("node_id", &self.node_id)
.finish()
}
}
impl<N: HasAstId> Hash for HirAstId<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.file_id.hash(state);
self.node_id.hash(state);
}
}
impl<N: HasAstId> HirAstId<N> {
pub fn upcast<M: HasAstId>(self) -> HirAstId<M>
where
N: Into<M>,
{
HirAstId {
file_id: self.file_id,
node_id: self.node_id.upcast(),
}
}
}

View File

@@ -1,556 +0,0 @@
use std::ops::{Index, Range};
use ruff_index::{newtype_index, IndexVec};
use ruff_python_ast::visitor::preorder;
use ruff_python_ast::visitor::preorder::PreorderVisitor;
use ruff_python_ast::{
Decorator, ExceptHandler, ExceptHandlerExceptHandler, Expr, MatchCase, ModModule, Stmt,
StmtAnnAssign, StmtAssign, StmtClassDef, StmtFunctionDef, StmtGlobal, StmtImport,
StmtImportFrom, StmtNonlocal, StmtTypeAlias, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
TypeParamTypeVarTuple, WithItem,
};
use crate::ast_ids::{AstIds, HasAstId};
use crate::files::FileId;
use crate::hir::HirAstId;
use crate::Name;
#[newtype_index]
pub struct FunctionId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Function {
ast_id: HirAstId<StmtFunctionDef>,
name: Name,
parameters: Range<ParameterId>,
type_parameters: Range<TypeParameterId>, // TODO: type_parameters, return expression, decorators
}
#[newtype_index]
pub struct ParameterId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Parameter {
kind: ParameterKind,
name: Name,
default: Option<()>, // TODO use expression HIR
ast_id: HirAstId<ruff_python_ast::Parameter>,
}
// TODO or should `Parameter` be an enum?
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ParameterKind {
PositionalOnly,
Arguments,
Vararg,
KeywordOnly,
Kwarg,
}
#[newtype_index]
pub struct ClassId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Class {
name: Name,
ast_id: HirAstId<StmtClassDef>,
// TODO type parameters, inheritance, decorators, members
}
#[newtype_index]
pub struct AssignmentId;
// This can have more than one name...
// but that means we can't implement `name()` on `ModuleItem`.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Assignment {
// TODO: Handle multiple names / targets
name: Name,
ast_id: HirAstId<StmtAssign>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct AnnotatedAssignment {
name: Name,
ast_id: HirAstId<StmtAnnAssign>,
}
#[newtype_index]
pub struct AnnotatedAssignmentId;
#[newtype_index]
pub struct TypeAliasId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TypeAlias {
name: Name,
ast_id: HirAstId<StmtTypeAlias>,
parameters: Range<TypeParameterId>,
}
#[newtype_index]
pub struct TypeParameterId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TypeParameter {
TypeVar(TypeParameterTypeVar),
ParamSpec(TypeParameterParamSpec),
TypeVarTuple(TypeParameterTypeVarTuple),
}
impl TypeParameter {
pub fn ast_id(&self) -> HirAstId<TypeParam> {
match self {
TypeParameter::TypeVar(type_var) => type_var.ast_id.upcast(),
TypeParameter::ParamSpec(param_spec) => param_spec.ast_id.upcast(),
TypeParameter::TypeVarTuple(type_var_tuple) => type_var_tuple.ast_id.upcast(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TypeParameterTypeVar {
name: Name,
ast_id: HirAstId<TypeParamTypeVar>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TypeParameterParamSpec {
name: Name,
ast_id: HirAstId<TypeParamParamSpec>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TypeParameterTypeVarTuple {
name: Name,
ast_id: HirAstId<TypeParamTypeVarTuple>,
}
#[newtype_index]
pub struct GlobalId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Global {
// TODO track names
ast_id: HirAstId<StmtGlobal>,
}
#[newtype_index]
pub struct NonLocalId;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct NonLocal {
// TODO track names
ast_id: HirAstId<StmtNonlocal>,
}
pub enum DefinitionId {
Function(FunctionId),
Parameter(ParameterId),
Class(ClassId),
Assignment(AssignmentId),
AnnotatedAssignment(AnnotatedAssignmentId),
Global(GlobalId),
NonLocal(NonLocalId),
TypeParameter(TypeParameterId),
TypeAlias(TypeAlias),
}
pub enum DefinitionItem {
Function(Function),
Parameter(Parameter),
Class(Class),
Assignment(Assignment),
AnnotatedAssignment(AnnotatedAssignment),
Global(Global),
NonLocal(NonLocal),
TypeParameter(TypeParameter),
TypeAlias(TypeAlias),
}
// The closest is rust-analyzers item-tree. It only represents "Items" which make the public interface of a module
// (it excludes any other statement or expressions). rust-analyzer uses it as the main input to the name resolution
// algorithm
// > It is the input to the name resolution algorithm, as well as to the queries defined in `adt.rs`,
// > `data.rs`, and most things in `attr.rs`.
//
// > One important purpose of this layer is to provide an "invalidation barrier" for incremental
// > computations: when typing inside an item body, the `ItemTree` of the modified file is typically
// > unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`).
//
// I haven't fully figured this out but I think that this composes the "public" interface of a module?
// But maybe that's too optimistic.
//
//
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct Definitions {
functions: IndexVec<FunctionId, Function>,
parameters: IndexVec<ParameterId, Parameter>,
classes: IndexVec<ClassId, Class>,
assignments: IndexVec<AssignmentId, Assignment>,
annotated_assignments: IndexVec<AnnotatedAssignmentId, AnnotatedAssignment>,
type_aliases: IndexVec<TypeAliasId, TypeAlias>,
type_parameters: IndexVec<TypeParameterId, TypeParameter>,
globals: IndexVec<GlobalId, Global>,
non_locals: IndexVec<NonLocalId, NonLocal>,
}
impl Definitions {
pub fn from_module(module: &ModModule, ast_ids: &AstIds, file_id: FileId) -> Self {
let mut visitor = DefinitionsVisitor {
definitions: Definitions::default(),
ast_ids,
file_id,
};
visitor.visit_body(&module.body);
visitor.definitions
}
}
impl Index<FunctionId> for Definitions {
type Output = Function;
fn index(&self, index: FunctionId) -> &Self::Output {
&self.functions[index]
}
}
impl Index<ParameterId> for Definitions {
type Output = Parameter;
fn index(&self, index: ParameterId) -> &Self::Output {
&self.parameters[index]
}
}
impl Index<ClassId> for Definitions {
type Output = Class;
fn index(&self, index: ClassId) -> &Self::Output {
&self.classes[index]
}
}
impl Index<AssignmentId> for Definitions {
type Output = Assignment;
fn index(&self, index: AssignmentId) -> &Self::Output {
&self.assignments[index]
}
}
impl Index<AnnotatedAssignmentId> for Definitions {
type Output = AnnotatedAssignment;
fn index(&self, index: AnnotatedAssignmentId) -> &Self::Output {
&self.annotated_assignments[index]
}
}
impl Index<TypeAliasId> for Definitions {
type Output = TypeAlias;
fn index(&self, index: TypeAliasId) -> &Self::Output {
&self.type_aliases[index]
}
}
impl Index<GlobalId> for Definitions {
type Output = Global;
fn index(&self, index: GlobalId) -> &Self::Output {
&self.globals[index]
}
}
impl Index<NonLocalId> for Definitions {
type Output = NonLocal;
fn index(&self, index: NonLocalId) -> &Self::Output {
&self.non_locals[index]
}
}
impl Index<TypeParameterId> for Definitions {
type Output = TypeParameter;
fn index(&self, index: TypeParameterId) -> &Self::Output {
&self.type_parameters[index]
}
}
struct DefinitionsVisitor<'a> {
definitions: Definitions,
ast_ids: &'a AstIds,
file_id: FileId,
}
impl DefinitionsVisitor<'_> {
fn ast_id<N: HasAstId>(&self, node: &N) -> HirAstId<N> {
HirAstId {
file_id: self.file_id,
node_id: self.ast_ids.ast_id(node),
}
}
fn lower_function_def(&mut self, function: &StmtFunctionDef) -> FunctionId {
let name = Name::new(&function.name);
let first_type_parameter_id = self.definitions.type_parameters.next_index();
let mut last_type_parameter_id = first_type_parameter_id;
if let Some(type_params) = &function.type_params {
for parameter in &type_params.type_params {
let id = self.lower_type_parameter(parameter);
last_type_parameter_id = id;
}
}
let parameters = self.lower_parameters(&function.parameters);
self.definitions.functions.push(Function {
name,
ast_id: self.ast_id(function),
parameters,
type_parameters: first_type_parameter_id..last_type_parameter_id,
})
}
fn lower_parameters(&mut self, parameters: &ruff_python_ast::Parameters) -> Range<ParameterId> {
let first_parameter_id = self.definitions.parameters.next_index();
let mut last_parameter_id = first_parameter_id;
for parameter in &parameters.posonlyargs {
last_parameter_id = self.definitions.parameters.push(Parameter {
kind: ParameterKind::PositionalOnly,
name: Name::new(&parameter.parameter.name),
default: None,
ast_id: self.ast_id(&parameter.parameter),
});
}
if let Some(vararg) = &parameters.vararg {
last_parameter_id = self.definitions.parameters.push(Parameter {
kind: ParameterKind::Vararg,
name: Name::new(&vararg.name),
default: None,
ast_id: self.ast_id(vararg),
});
}
for parameter in &parameters.kwonlyargs {
last_parameter_id = self.definitions.parameters.push(Parameter {
kind: ParameterKind::KeywordOnly,
name: Name::new(&parameter.parameter.name),
default: None,
ast_id: self.ast_id(&parameter.parameter),
});
}
if let Some(kwarg) = &parameters.kwarg {
last_parameter_id = self.definitions.parameters.push(Parameter {
kind: ParameterKind::KeywordOnly,
name: Name::new(&kwarg.name),
default: None,
ast_id: self.ast_id(kwarg),
});
}
first_parameter_id..last_parameter_id
}
fn lower_class_def(&mut self, class: &StmtClassDef) -> ClassId {
let name = Name::new(&class.name);
self.definitions.classes.push(Class {
name,
ast_id: self.ast_id(class),
})
}
fn lower_assignment(&mut self, assignment: &StmtAssign) {
// FIXME handle multiple names
if let Some(Expr::Name(name)) = assignment.targets.first() {
self.definitions.assignments.push(Assignment {
name: Name::new(&name.id),
ast_id: self.ast_id(assignment),
});
}
}
fn lower_annotated_assignment(&mut self, annotated_assignment: &StmtAnnAssign) {
if let Expr::Name(name) = &*annotated_assignment.target {
self.definitions
.annotated_assignments
.push(AnnotatedAssignment {
name: Name::new(&name.id),
ast_id: self.ast_id(annotated_assignment),
});
}
}
fn lower_type_alias(&mut self, type_alias: &StmtTypeAlias) {
if let Expr::Name(name) = &*type_alias.name {
let name = Name::new(&name.id);
let lower_parameters_id = self.definitions.type_parameters.next_index();
let mut last_parameter_id = lower_parameters_id;
if let Some(type_params) = &type_alias.type_params {
for type_parameter in &type_params.type_params {
let id = self.lower_type_parameter(type_parameter);
last_parameter_id = id;
}
}
self.definitions.type_aliases.push(TypeAlias {
name,
ast_id: self.ast_id(type_alias),
parameters: lower_parameters_id..last_parameter_id,
});
}
}
fn lower_type_parameter(&mut self, type_parameter: &TypeParam) -> TypeParameterId {
match type_parameter {
TypeParam::TypeVar(type_var) => {
self.definitions
.type_parameters
.push(TypeParameter::TypeVar(TypeParameterTypeVar {
name: Name::new(&type_var.name),
ast_id: self.ast_id(type_var),
}))
}
TypeParam::ParamSpec(param_spec) => {
self.definitions
.type_parameters
.push(TypeParameter::ParamSpec(TypeParameterParamSpec {
name: Name::new(&param_spec.name),
ast_id: self.ast_id(param_spec),
}))
}
TypeParam::TypeVarTuple(type_var_tuple) => {
self.definitions
.type_parameters
.push(TypeParameter::TypeVarTuple(TypeParameterTypeVarTuple {
name: Name::new(&type_var_tuple.name),
ast_id: self.ast_id(type_var_tuple),
}))
}
}
}
fn lower_import(&mut self, _import: &StmtImport) {
// TODO
}
fn lower_import_from(&mut self, _import_from: &StmtImportFrom) {
// TODO
}
fn lower_global(&mut self, global: &StmtGlobal) -> GlobalId {
self.definitions.globals.push(Global {
ast_id: self.ast_id(global),
})
}
fn lower_non_local(&mut self, non_local: &StmtNonlocal) -> NonLocalId {
self.definitions.non_locals.push(NonLocal {
ast_id: self.ast_id(non_local),
})
}
fn lower_except_handler(&mut self, _except_handler: &ExceptHandlerExceptHandler) {
// TODO
}
fn lower_with_item(&mut self, _with_item: &WithItem) {
// TODO
}
fn lower_match_case(&mut self, _match_case: &MatchCase) {
// TODO
}
}
impl PreorderVisitor<'_> for DefinitionsVisitor<'_> {
fn visit_stmt(&mut self, stmt: &Stmt) {
match stmt {
// Definition statements
Stmt::FunctionDef(definition) => {
self.lower_function_def(definition);
self.visit_body(&definition.body);
}
Stmt::ClassDef(definition) => {
self.lower_class_def(definition);
self.visit_body(&definition.body);
}
Stmt::Assign(assignment) => {
self.lower_assignment(assignment);
}
Stmt::AnnAssign(annotated_assignment) => {
self.lower_annotated_assignment(annotated_assignment);
}
Stmt::TypeAlias(type_alias) => {
self.lower_type_alias(type_alias);
}
Stmt::Import(import) => self.lower_import(import),
Stmt::ImportFrom(import_from) => self.lower_import_from(import_from),
Stmt::Global(global) => {
self.lower_global(global);
}
Stmt::Nonlocal(non_local) => {
self.lower_non_local(non_local);
}
// Visit the compound statement bodies because they can contain other definitions.
Stmt::For(_)
| Stmt::While(_)
| Stmt::If(_)
| Stmt::With(_)
| Stmt::Match(_)
| Stmt::Try(_) => {
preorder::walk_stmt(self, stmt);
}
// Skip over simple statements because they can't contain any other definitions.
Stmt::Return(_)
| Stmt::Delete(_)
| Stmt::AugAssign(_)
| Stmt::Raise(_)
| Stmt::Assert(_)
| Stmt::Expr(_)
| Stmt::Pass(_)
| Stmt::Break(_)
| Stmt::Continue(_)
| Stmt::IpyEscapeCommand(_) => {
// No op
}
}
}
fn visit_expr(&mut self, _: &'_ Expr) {}
fn visit_decorator(&mut self, _decorator: &'_ Decorator) {}
fn visit_except_handler(&mut self, except_handler: &'_ ExceptHandler) {
match except_handler {
ExceptHandler::ExceptHandler(except_handler) => {
self.lower_except_handler(except_handler);
}
}
}
fn visit_with_item(&mut self, with_item: &'_ WithItem) {
self.lower_with_item(with_item);
}
fn visit_match_case(&mut self, match_case: &'_ MatchCase) {
self.lower_match_case(match_case);
self.visit_body(&match_case.body);
}
}

View File

@@ -1,108 +0,0 @@
use std::fmt::Formatter;
use std::hash::BuildHasherDefault;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use rustc_hash::{FxHashSet, FxHasher};
use crate::files::FileId;
pub mod ast_ids;
pub mod cache;
pub mod cancellation;
pub mod db;
pub mod files;
pub mod hir;
pub mod lint;
pub mod module;
mod parse;
pub mod program;
mod semantic;
pub mod source;
pub mod watch;
pub(crate) type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
#[allow(unused)]
pub(crate) type FxDashSet<V> = dashmap::DashSet<V, BuildHasherDefault<FxHasher>>;
pub(crate) type FxIndexSet<V> = indexmap::set::IndexSet<V, BuildHasherDefault<FxHasher>>;
#[derive(Debug, Clone)]
pub struct Workspace {
/// TODO this should be a resolved path. We should probably use a newtype wrapper that guarantees that
/// PATH is a UTF-8 path and is normalized.
root: PathBuf,
/// The files that are open in the workspace.
///
/// * Editor: The files that are actively being edited in the editor (the user has a tab open with the file).
/// * CLI: The resolved files passed as arguments to the CLI.
open_files: FxHashSet<FileId>,
}
impl Workspace {
pub fn new(root: PathBuf) -> Self {
Self {
root,
open_files: FxHashSet::default(),
}
}
pub fn root(&self) -> &Path {
self.root.as_path()
}
// TODO having the content in workspace feels wrong.
pub fn open_file(&mut self, file_id: FileId) {
self.open_files.insert(file_id);
}
pub fn close_file(&mut self, file_id: FileId) {
self.open_files.remove(&file_id);
}
// TODO introduce an `OpenFile` type instead of using an anonymous tuple.
pub fn open_files(&self) -> impl Iterator<Item = FileId> + '_ {
self.open_files.iter().copied()
}
pub fn is_file_open(&self, file_id: FileId) -> bool {
self.open_files.contains(&file_id)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Name(smol_str::SmolStr);
impl Name {
#[inline]
pub fn new(name: &str) -> Self {
Self(smol_str::SmolStr::new(name))
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl Deref for Name {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<T> From<T> for Name
where
T: Into<smol_str::SmolStr>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
impl std::fmt::Display for Name {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}

View File

@@ -1,332 +0,0 @@
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use std::time::Duration;
use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{ModModule, StringLiteral};
use ruff_python_parser::Parsed;
use crate::cache::KeyValueCache;
use crate::db::{LintDb, LintJar, QueryResult};
use crate::files::FileId;
use crate::module::{resolve_module, ModuleName};
use crate::parse::parse;
use crate::semantic::{infer_definition_type, infer_symbol_public_type, Type};
use crate::semantic::{
resolve_global_symbol, semantic_index, Definition, GlobalSymbolId, SemanticIndex, SymbolId,
};
use crate::source::{source_text, Source};
#[tracing::instrument(level = "debug", skip(db))]
pub(crate) fn lint_syntax(db: &dyn LintDb, file_id: FileId) -> QueryResult<Diagnostics> {
let lint_jar: &LintJar = db.jar()?;
let storage = &lint_jar.lint_syntax;
#[allow(clippy::print_stdout)]
if std::env::var("RED_KNOT_SLOW_LINT").is_ok() {
for i in 0..10 {
db.cancelled()?;
println!("RED_KNOT_SLOW_LINT is set, sleeping for {i}/10 seconds");
std::thread::sleep(Duration::from_secs(1));
}
}
storage.get(&file_id, |file_id| {
let mut diagnostics = Vec::new();
let source = source_text(db.upcast(), *file_id)?;
lint_lines(source.text(), &mut diagnostics);
let parsed = parse(db.upcast(), *file_id)?;
if parsed.errors().is_empty() {
let ast = parsed.syntax();
let mut visitor = SyntaxLintVisitor {
diagnostics,
source: source.text(),
};
visitor.visit_body(&ast.body);
diagnostics = visitor.diagnostics;
} else {
diagnostics.extend(parsed.errors().iter().map(std::string::ToString::to_string));
}
Ok(Diagnostics::from(diagnostics))
})
}
fn lint_lines(source: &str, diagnostics: &mut Vec<String>) {
for (line_number, line) in source.lines().enumerate() {
if line.len() < 88 {
continue;
}
let char_count = line.chars().count();
if char_count > 88 {
diagnostics.push(format!(
"Line {} is too long ({} characters)",
line_number + 1,
char_count
));
}
}
}
#[tracing::instrument(level = "debug", skip(db))]
pub(crate) fn lint_semantic(db: &dyn LintDb, file_id: FileId) -> QueryResult<Diagnostics> {
let lint_jar: &LintJar = db.jar()?;
let storage = &lint_jar.lint_semantic;
storage.get(&file_id, |file_id| {
let source = source_text(db.upcast(), *file_id)?;
let parsed = parse(db.upcast(), *file_id)?;
let semantic_index = semantic_index(db.upcast(), *file_id)?;
let context = SemanticLintContext {
file_id: *file_id,
source,
parsed: &parsed,
semantic_index,
db,
diagnostics: RefCell::new(Vec::new()),
};
lint_unresolved_imports(&context)?;
lint_bad_overrides(&context)?;
Ok(Diagnostics::from(context.diagnostics.take()))
})
}
fn lint_unresolved_imports(context: &SemanticLintContext) -> QueryResult<()> {
// TODO: Consider iterating over the dependencies (imports) only instead of all definitions.
for (symbol, definition) in context.semantic_index().symbol_table().all_definitions() {
match definition {
Definition::Import(import) => {
let ty = context.infer_symbol_public_type(symbol)?;
if ty.is_unknown() {
context.push_diagnostic(format!("Unresolved module {}", import.module));
}
}
Definition::ImportFrom(import) => {
let ty = context.infer_symbol_public_type(symbol)?;
if ty.is_unknown() {
let module_name = import.module().map(Deref::deref).unwrap_or_default();
let message = if import.level() > 0 {
format!(
"Unresolved relative import '{}' from {}{}",
import.name(),
".".repeat(import.level() as usize),
module_name
)
} else {
format!(
"Unresolved import '{}' from '{}'",
import.name(),
module_name
)
};
context.push_diagnostic(message);
}
}
_ => {}
}
}
Ok(())
}
fn lint_bad_overrides(context: &SemanticLintContext) -> QueryResult<()> {
// TODO we should have a special marker on the real typing module (from typeshed) so if you
// have your own "typing" module in your project, we don't consider it THE typing module (and
// same for other stdlib modules that our lint rules care about)
let Some(typing_override) = context.resolve_global_symbol("typing", "override")? else {
// TODO once we bundle typeshed, this should be unreachable!()
return Ok(());
};
// TODO we should maybe index definitions by type instead of iterating all, or else iterate all
// just once, match, and branch to all lint rules that care about a type of definition
for (symbol, definition) in context.semantic_index().symbol_table().all_definitions() {
if !matches!(definition, Definition::FunctionDef(_)) {
continue;
}
let ty = infer_definition_type(
context.db.upcast(),
GlobalSymbolId {
file_id: context.file_id,
symbol_id: symbol,
},
definition.clone(),
)?;
let Type::Function(func) = ty else {
unreachable!("type of a FunctionDef should always be a Function");
};
let Some(class) = func.get_containing_class(context.db.upcast())? else {
// not a method of a class
continue;
};
if func.has_decorator(context.db.upcast(), typing_override)? {
let method_name = func.name(context.db.upcast())?;
if class
.get_super_class_member(context.db.upcast(), &method_name)?
.is_none()
{
// TODO should have a qualname() method to support nested classes
context.push_diagnostic(
format!(
"Method {}.{} is decorated with `typing.override` but does not override any base class method",
class.name(context.db.upcast())?,
method_name,
));
}
}
}
Ok(())
}
pub struct SemanticLintContext<'a> {
file_id: FileId,
source: Source,
parsed: &'a Parsed<ModModule>,
semantic_index: Arc<SemanticIndex>,
db: &'a dyn LintDb,
diagnostics: RefCell<Vec<String>>,
}
impl<'a> SemanticLintContext<'a> {
pub fn source_text(&self) -> &str {
self.source.text()
}
pub fn file_id(&self) -> FileId {
self.file_id
}
pub fn ast(&self) -> &'a ModModule {
self.parsed.syntax()
}
pub fn semantic_index(&self) -> &SemanticIndex {
&self.semantic_index
}
pub fn infer_symbol_public_type(&self, symbol_id: SymbolId) -> QueryResult<Type> {
infer_symbol_public_type(
self.db.upcast(),
GlobalSymbolId {
file_id: self.file_id,
symbol_id,
},
)
}
pub fn push_diagnostic(&self, diagnostic: String) {
self.diagnostics.borrow_mut().push(diagnostic);
}
pub fn extend_diagnostics(&mut self, diagnostics: impl IntoIterator<Item = String>) {
self.diagnostics.get_mut().extend(diagnostics);
}
pub fn resolve_global_symbol(
&self,
module: &str,
symbol_name: &str,
) -> QueryResult<Option<GlobalSymbolId>> {
let Some(module) = resolve_module(self.db.upcast(), ModuleName::new(module))? else {
return Ok(None);
};
resolve_global_symbol(self.db.upcast(), module, symbol_name)
}
}
#[derive(Debug)]
struct SyntaxLintVisitor<'a> {
diagnostics: Vec<String>,
source: &'a str,
}
impl Visitor<'_> for SyntaxLintVisitor<'_> {
fn visit_string_literal(&mut self, string_literal: &'_ StringLiteral) {
// A very naive implementation of use double quotes
let text = &self.source[string_literal.range];
if text.starts_with('\'') {
self.diagnostics
.push("Use double quotes for strings".to_string());
}
}
}
#[derive(Debug, Clone)]
pub enum Diagnostics {
Empty,
List(Arc<Vec<String>>),
}
impl Diagnostics {
pub fn as_slice(&self) -> &[String] {
match self {
Diagnostics::Empty => &[],
Diagnostics::List(list) => list.as_slice(),
}
}
}
impl Deref for Diagnostics {
type Target = [String];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl From<Vec<String>> for Diagnostics {
fn from(value: Vec<String>) -> Self {
if value.is_empty() {
Diagnostics::Empty
} else {
Diagnostics::List(Arc::new(value))
}
}
}
#[derive(Default, Debug)]
pub struct LintSyntaxStorage(KeyValueCache<FileId, Diagnostics>);
impl Deref for LintSyntaxStorage {
type Target = KeyValueCache<FileId, Diagnostics>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LintSyntaxStorage {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Default, Debug)]
pub struct LintSemanticStorage(KeyValueCache<FileId, Diagnostics>);
impl Deref for LintSemanticStorage {
type Target = KeyValueCache<FileId, Diagnostics>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LintSemanticStorage {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@@ -0,0 +1,254 @@
//! Sets up logging for Red Knot
use anyhow::Context;
use colored::Colorize;
use std::fmt;
use std::fs::File;
use std::io::BufWriter;
use tracing::{Event, Subscriber};
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::EnvFilter;
/// Logging flags to `#[command(flatten)]` into your CLI
#[derive(clap::Args, Debug, Clone, Default)]
#[command(about = None, long_about = None)]
pub(crate) struct Verbosity {
#[arg(
long,
short = 'v',
help = "Use verbose output (or `-vv` and `-vvv` for more verbose output)",
action = clap::ArgAction::Count,
global = true,
)]
verbose: u8,
}
impl Verbosity {
/// Returns the verbosity level based on the number of `-v` flags.
///
/// Returns `None` if the user did not specify any verbosity flags.
pub(crate) fn level(&self) -> VerbosityLevel {
match self.verbose {
0 => VerbosityLevel::Default,
1 => VerbosityLevel::Verbose,
2 => VerbosityLevel::ExtraVerbose,
_ => VerbosityLevel::Trace,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) enum VerbosityLevel {
/// Default output level. Only shows Ruff and Red Knot events up to the [`WARN`](tracing::Level::WARN).
Default,
/// Enables verbose output. Emits Ruff and Red Knot events up to the [`INFO`](tracing::Level::INFO).
/// Corresponds to `-v`.
Verbose,
/// Enables a more verbose tracing format and emits Ruff and Red Knot events up to [`DEBUG`](tracing::Level::DEBUG).
/// Corresponds to `-vv`
ExtraVerbose,
/// Enables all tracing events and uses a tree-like output format. Corresponds to `-vvv`.
Trace,
}
impl VerbosityLevel {
const fn level_filter(self) -> LevelFilter {
match self {
VerbosityLevel::Default => LevelFilter::WARN,
VerbosityLevel::Verbose => LevelFilter::INFO,
VerbosityLevel::ExtraVerbose => LevelFilter::DEBUG,
VerbosityLevel::Trace => LevelFilter::TRACE,
}
}
pub(crate) const fn is_trace(self) -> bool {
matches!(self, VerbosityLevel::Trace)
}
pub(crate) const fn is_extra_verbose(self) -> bool {
matches!(self, VerbosityLevel::ExtraVerbose)
}
}
pub(crate) fn setup_tracing(level: VerbosityLevel) -> anyhow::Result<TracingGuard> {
use tracing_subscriber::prelude::*;
// The `RED_KNOT_LOG` environment variable overrides the default log level.
let filter = if let Ok(log_env_variable) = std::env::var("RED_KNOT_LOG") {
EnvFilter::builder()
.parse(log_env_variable)
.context("Failed to parse directives specified in RED_KNOT_LOG environment variable.")?
} else {
match level {
VerbosityLevel::Default => {
// Show warning traces
EnvFilter::default().add_directive(LevelFilter::WARN.into())
}
level => {
let level_filter = level.level_filter();
// Show info|debug|trace events, but allow `RED_KNOT_LOG` to override
let filter = EnvFilter::default().add_directive(
format!("red_knot={level_filter}")
.parse()
.expect("Hardcoded directive to be valid"),
);
filter.add_directive(
format!("ruff={level_filter}")
.parse()
.expect("Hardcoded directive to be valid"),
)
}
}
};
let (profiling_layer, guard) = setup_profile();
let registry = tracing_subscriber::registry()
.with(filter)
.with(profiling_layer);
if level.is_trace() {
let subscriber = registry.with(
tracing_tree::HierarchicalLayer::default()
.with_indent_lines(true)
.with_indent_amount(2)
.with_bracketed_fields(true)
.with_thread_ids(true)
.with_targets(true)
.with_writer(std::io::stderr)
.with_timer(tracing_tree::time::Uptime::default()),
);
subscriber.init();
} else {
let subscriber = registry.with(
tracing_subscriber::fmt::layer()
.event_format(RedKnotFormat {
display_level: true,
display_timestamp: level.is_extra_verbose(),
show_spans: false,
})
.with_writer(std::io::stderr),
);
subscriber.init();
}
Ok(TracingGuard {
_flame_guard: guard,
})
}
#[allow(clippy::type_complexity)]
fn setup_profile<S>() -> (
Option<tracing_flame::FlameLayer<S, BufWriter<File>>>,
Option<tracing_flame::FlushGuard<BufWriter<File>>>,
)
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
if let Ok("1" | "true") = std::env::var("RED_KNOT_LOG_PROFILE").as_deref() {
let (layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded")
.expect("Flame layer to be created");
(Some(layer), Some(guard))
} else {
(None, None)
}
}
pub(crate) struct TracingGuard {
_flame_guard: Option<tracing_flame::FlushGuard<BufWriter<File>>>,
}
struct RedKnotFormat {
display_timestamp: bool,
display_level: bool,
show_spans: bool,
}
/// See <https://docs.rs/tracing-subscriber/0.3.18/src/tracing_subscriber/fmt/format/mod.rs.html#1026-1156>
impl<S, N> FormatEvent<S, N> for RedKnotFormat
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
let meta = event.metadata();
let ansi = writer.has_ansi_escapes();
if self.display_timestamp {
let timestamp = chrono::Local::now()
.format("%Y-%m-%d %H:%M:%S.%f")
.to_string();
if ansi {
write!(writer, "{} ", timestamp.dimmed())?;
} else {
write!(
writer,
"{} ",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S.%f")
)?;
}
}
if self.display_level {
let level = meta.level();
// Same colors as tracing
if ansi {
let formatted_level = level.to_string();
match *level {
tracing::Level::TRACE => {
write!(writer, "{} ", formatted_level.purple().bold())?;
}
tracing::Level::DEBUG => write!(writer, "{} ", formatted_level.blue().bold())?,
tracing::Level::INFO => write!(writer, "{} ", formatted_level.green().bold())?,
tracing::Level::WARN => write!(writer, "{} ", formatted_level.yellow().bold())?,
tracing::Level::ERROR => write!(writer, "{} ", level.to_string().red().bold())?,
}
} else {
write!(writer, "{level} ")?;
}
}
if self.show_spans {
let span = event.parent();
let mut seen = false;
let span = span
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current());
let scope = span.into_iter().flat_map(|span| span.scope().from_root());
for span in scope {
seen = true;
if ansi {
write!(writer, "{}:", span.metadata().name().bold())?;
} else {
write!(writer, "{}:", span.metadata().name())?;
}
}
if seen {
writer.write_char(' ')?;
}
}
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer)
}
}

View File

@@ -1,65 +1,171 @@
#![allow(clippy::dbg_macro)]
use std::path::Path;
use std::process::{ExitCode, Termination};
use std::sync::Mutex;
use anyhow::{anyhow, Context};
use clap::Parser;
use colored::Colorize;
use crossbeam::channel as crossbeam_channel;
use tracing::subscriber::Interest;
use tracing::{Level, Metadata};
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::{Context, Filter, SubscriberExt};
use tracing_subscriber::{Layer, Registry};
use tracing_tree::time::Uptime;
use python_version::PythonVersion;
use red_knot_project::metadata::options::{EnvironmentOptions, Options};
use red_knot_project::watch;
use red_knot_project::watch::ProjectWatcher;
use red_knot_project::{ProjectDatabase, ProjectMetadata};
use red_knot_server::run_server;
use ruff_db::diagnostic::Diagnostic;
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
use salsa::plumbing::ZalsaDatabase;
use red_knot::db::{HasJar, ParallelDatabase, QueryError, SourceDb, SourceJar};
use red_knot::module::{set_module_search_paths, ModuleResolutionInputs};
use red_knot::program::check::ExecutionMode;
use red_knot::program::{FileWatcherChange, Program};
use red_knot::watch::FileWatcher;
use red_knot::Workspace;
use crate::logging::{setup_tracing, Verbosity};
mod logging;
mod python_version;
mod verbosity;
#[derive(Debug, Parser)]
#[command(
author,
name = "red-knot",
about = "An extremely fast Python type checker."
)]
#[command(version)]
struct Args {
#[command(subcommand)]
pub(crate) command: Option<Command>,
/// Run the command within the given project directory.
///
/// All `pyproject.toml` files will be discovered by walking up the directory tree from the given project directory,
/// as will the project's virtual environment (`.venv`) unless the `venv-path` option is set.
///
/// Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.
#[arg(long, value_name = "PROJECT")]
project: Option<SystemPathBuf>,
/// Path to the virtual environment the project uses.
///
/// If provided, red-knot will use the `site-packages` directory of this virtual environment
/// to resolve type information for the project's third-party dependencies.
#[arg(long, value_name = "PATH")]
venv_path: Option<SystemPathBuf>,
/// Custom directory to use for stdlib typeshed stubs.
#[arg(long, value_name = "PATH", alias = "custom-typeshed-dir")]
typeshed: Option<SystemPathBuf>,
/// Additional path to use as a module-resolution source (can be passed multiple times).
#[arg(long, value_name = "PATH")]
extra_search_path: Option<Vec<SystemPathBuf>>,
/// Python version to assume when resolving types.
#[arg(long, value_name = "VERSION", alias = "target-version")]
python_version: Option<PythonVersion>,
#[clap(flatten)]
verbosity: Verbosity,
/// Run in watch mode by re-running whenever files change.
#[arg(long, short = 'W')]
watch: bool,
}
impl Args {
fn to_options(&self, cli_cwd: &SystemPath) -> Options {
Options {
environment: Some(EnvironmentOptions {
python_version: self.python_version.map(Into::into),
venv_path: self
.venv_path
.as_ref()
.map(|venv_path| SystemPath::absolute(venv_path, cli_cwd)),
typeshed: self
.typeshed
.as_ref()
.map(|typeshed| SystemPath::absolute(typeshed, cli_cwd)),
extra_paths: self.extra_search_path.as_ref().map(|extra_search_paths| {
extra_search_paths
.iter()
.map(|path| SystemPath::absolute(path, cli_cwd))
.collect()
}),
..EnvironmentOptions::default()
}),
..Default::default()
}
}
}
#[derive(Debug, clap::Subcommand)]
pub enum Command {
/// Start the language server
Server,
}
#[allow(clippy::print_stdout, clippy::unnecessary_wraps, clippy::print_stderr)]
fn main() -> anyhow::Result<()> {
setup_tracing();
pub fn main() -> ExitStatus {
run().unwrap_or_else(|error| {
use std::io::Write;
let arguments: Vec<_> = std::env::args().collect();
// Use `writeln` instead of `eprintln` to avoid panicking when the stderr pipe is broken.
let mut stderr = std::io::stderr().lock();
if arguments.len() < 2 {
eprintln!("Usage: red_knot <path>");
return Err(anyhow::anyhow!("Invalid arguments"));
// This communicates that this isn't a linter error but Red Knot itself hard-errored for
// some reason (e.g. failed to resolve the configuration)
writeln!(stderr, "{}", "Red Knot failed".red().bold()).ok();
// Currently we generally only see one error, but e.g. with io errors when resolving
// the configuration it is help to chain errors ("resolving configuration failed" ->
// "failed to read file: subdir/pyproject.toml")
for cause in error.chain() {
writeln!(stderr, " {} {cause}", "Cause:".bold()).ok();
}
ExitStatus::Error
})
}
fn run() -> anyhow::Result<ExitStatus> {
let args = Args::parse_from(std::env::args());
if matches!(args.command, Some(Command::Server)) {
return run_server().map(|()| ExitStatus::Success);
}
let entry_point = Path::new(&arguments[1]);
let verbosity = args.verbosity.level();
countme::enable(verbosity.is_trace());
let _guard = setup_tracing(verbosity)?;
if !entry_point.exists() {
eprintln!("The entry point does not exist.");
return Err(anyhow::anyhow!("Invalid arguments"));
}
if !entry_point.is_file() {
eprintln!("The entry point is not a file.");
return Err(anyhow::anyhow!("Invalid arguments"));
}
let workspace_folder = entry_point.parent().unwrap();
let workspace = Workspace::new(workspace_folder.to_path_buf());
let workspace_search_path = workspace.root().to_path_buf();
let search_paths = ModuleResolutionInputs {
extra_paths: vec![],
workspace_root: workspace_search_path,
site_packages: None,
custom_typeshed: None,
// The base path to which all CLI arguments are relative to.
let cli_base_path = {
let cwd = std::env::current_dir().context("Failed to get the current working directory")?;
SystemPathBuf::from_path_buf(cwd)
.map_err(|path| {
anyhow!(
"The current working directory `{}` contains non-Unicode characters. Red Knot only supports Unicode paths.",
path.display()
)
})?
};
let mut program = Program::new(workspace);
set_module_search_paths(&mut program, search_paths);
let cwd = args
.project
.as_ref()
.map(|cwd| {
if cwd.as_std_path().is_dir() {
Ok(SystemPath::absolute(cwd, &cli_base_path))
} else {
Err(anyhow!("Provided project path `{cwd}` is not a directory"))
}
})
.transpose()?
.unwrap_or_else(|| cli_base_path.clone());
let entry_id = program.file_id(entry_point);
program.workspace_mut().open_file(entry_id);
let system = OsSystem::new(cwd.clone());
let cli_options = args.to_options(&cwd);
let mut workspace_metadata = ProjectMetadata::discover(system.current_directory(), &system)?;
workspace_metadata.apply_cli_options(cli_options.clone());
let (main_loop, main_loop_cancellation_token) = MainLoop::new();
let mut db = ProjectDatabase::new(workspace_metadata, system)?;
let (main_loop, main_loop_cancellation_token) = MainLoop::new(cli_options);
// Listen to Ctrl+C and abort the watch mode.
let main_loop_cancellation_token = Mutex::new(Some(main_loop_cancellation_token));
@@ -71,122 +177,162 @@ fn main() -> anyhow::Result<()> {
}
})?;
let file_changes_notifier = main_loop.file_changes_notifier();
let exit_status = if args.watch {
main_loop.watch(&mut db)?
} else {
main_loop.run(&mut db)
};
// Watch for file changes and re-trigger the analysis.
let mut file_watcher = FileWatcher::new(move |changes| {
file_changes_notifier.notify(changes);
})?;
tracing::trace!("Counts for entire CLI run:\n{}", countme::get_all());
file_watcher.watch_folder(workspace_folder)?;
std::mem::forget(db);
main_loop.run(&mut program);
Ok(exit_status)
}
let source_jar: &SourceJar = program.jar().unwrap();
#[derive(Copy, Clone)]
pub enum ExitStatus {
/// Checking was successful and there were no errors.
Success = 0,
dbg!(source_jar.parsed.statistics());
dbg!(source_jar.sources.statistics());
/// Checking was successful but there were errors.
Failure = 1,
Ok(())
/// Checking failed.
Error = 2,
}
impl Termination for ExitStatus {
fn report(self) -> ExitCode {
ExitCode::from(self as u8)
}
}
struct MainLoop {
orchestrator_sender: crossbeam_channel::Sender<OrchestratorMessage>,
main_loop_receiver: crossbeam_channel::Receiver<MainLoopMessage>,
/// Sender that can be used to send messages to the main loop.
sender: crossbeam_channel::Sender<MainLoopMessage>,
/// Receiver for the messages sent **to** the main loop.
receiver: crossbeam_channel::Receiver<MainLoopMessage>,
/// The file system watcher, if running in watch mode.
watcher: Option<ProjectWatcher>,
cli_options: Options,
}
impl MainLoop {
fn new() -> (Self, MainLoopCancellationToken) {
let (orchestrator_sender, orchestrator_receiver) = crossbeam_channel::bounded(1);
let (main_loop_sender, main_loop_receiver) = crossbeam_channel::bounded(1);
let mut orchestrator = Orchestrator {
receiver: orchestrator_receiver,
sender: main_loop_sender.clone(),
revision: 0,
};
std::thread::spawn(move || {
orchestrator.run();
});
fn new(cli_options: Options) -> (Self, MainLoopCancellationToken) {
let (sender, receiver) = crossbeam_channel::bounded(10);
(
Self {
orchestrator_sender,
main_loop_receiver,
},
MainLoopCancellationToken {
sender: main_loop_sender,
sender: sender.clone(),
receiver,
watcher: None,
cli_options,
},
MainLoopCancellationToken { sender },
)
}
fn file_changes_notifier(&self) -> FileChangesNotifier {
FileChangesNotifier {
sender: self.orchestrator_sender.clone(),
}
fn watch(mut self, db: &mut ProjectDatabase) -> anyhow::Result<ExitStatus> {
tracing::debug!("Starting watch mode");
let sender = self.sender.clone();
let watcher = watch::directory_watcher(move |event| {
sender.send(MainLoopMessage::ApplyChanges(event)).unwrap();
})?;
self.watcher = Some(ProjectWatcher::new(watcher, db));
self.run(db);
Ok(ExitStatus::Success)
}
fn run(self, program: &mut Program) {
self.orchestrator_sender
.send(OrchestratorMessage::Run)
.unwrap();
fn run(mut self, db: &mut ProjectDatabase) -> ExitStatus {
self.sender.send(MainLoopMessage::CheckWorkspace).unwrap();
for message in &self.main_loop_receiver {
tracing::trace!("Main Loop: Tick");
let result = self.main_loop(db);
tracing::debug!("Exiting main loop");
result
}
fn main_loop(&mut self, db: &mut ProjectDatabase) -> ExitStatus {
// Schedule the first check.
tracing::debug!("Starting main loop");
let mut revision = 0u64;
while let Ok(message) = self.receiver.recv() {
match message {
MainLoopMessage::CheckProgram { revision } => {
let program = program.snapshot();
let sender = self.orchestrator_sender.clone();
MainLoopMessage::CheckWorkspace => {
let db = db.clone();
let sender = self.sender.clone();
// Spawn a new task that checks the program. This needs to be done in a separate thread
// Spawn a new task that checks the project. This needs to be done in a separate thread
// to prevent blocking the main loop here.
rayon::spawn(move || match program.check(ExecutionMode::ThreadPool) {
Ok(result) => {
rayon::spawn(move || {
if let Ok(result) = db.check() {
// Send the result back to the main loop for printing.
sender
.send(OrchestratorMessage::CheckProgramCompleted {
diagnostics: result,
revision,
})
.send(MainLoopMessage::CheckCompleted { result, revision })
.unwrap();
}
Err(QueryError::Cancelled) => {}
});
}
MainLoopMessage::ApplyChanges(changes) => {
// Automatically cancels any pending queries and waits for them to complete.
program.apply_changes(changes);
MainLoopMessage::CheckCompleted {
result,
revision: check_revision,
} => {
let has_diagnostics = !result.is_empty();
if check_revision == revision {
#[allow(clippy::print_stdout)]
for diagnostic in result {
println!("{}", diagnostic.display(db));
}
} else {
tracing::debug!(
"Discarding check result for outdated revision: current: {revision}, result revision: {check_revision}"
);
}
if self.watcher.is_none() {
return if has_diagnostics {
ExitStatus::Failure
} else {
ExitStatus::Success
};
}
tracing::trace!("Counts after last check:\n{}", countme::get_all());
}
MainLoopMessage::CheckCompleted(diagnostics) => {
dbg!(diagnostics);
MainLoopMessage::ApplyChanges(changes) => {
revision += 1;
// Automatically cancels any pending queries and waits for them to complete.
db.apply_changes(changes, Some(&self.cli_options));
if let Some(watcher) = self.watcher.as_mut() {
watcher.update(db);
}
self.sender.send(MainLoopMessage::CheckWorkspace).unwrap();
}
MainLoopMessage::Exit => {
return;
// Cancel any pending queries and wait for them to complete.
// TODO: Don't use Salsa internal APIs
// [Zulip-Thread](https://salsa.zulipchat.com/#narrow/stream/333573-salsa-3.2E0/topic/Expose.20an.20API.20to.20cancel.20other.20queries)
let _ = db.zalsa_mut();
return ExitStatus::Success;
}
}
tracing::debug!("Waiting for next main loop message.");
}
}
}
impl Drop for MainLoop {
fn drop(&mut self) {
self.orchestrator_sender
.send(OrchestratorMessage::Shutdown)
.unwrap();
}
}
#[derive(Debug, Clone)]
struct FileChangesNotifier {
sender: crossbeam_channel::Sender<OrchestratorMessage>,
}
impl FileChangesNotifier {
fn notify(&self, changes: Vec<FileWatcherChange>) {
self.sender
.send(OrchestratorMessage::FileChanges(changes))
.unwrap();
ExitStatus::Success
}
}
@@ -201,164 +347,14 @@ impl MainLoopCancellationToken {
}
}
struct Orchestrator {
/// Sends messages to the main loop.
sender: crossbeam_channel::Sender<MainLoopMessage>,
/// Receives messages from the main loop.
receiver: crossbeam_channel::Receiver<OrchestratorMessage>,
revision: usize,
}
impl Orchestrator {
fn run(&mut self) {
while let Ok(message) = self.receiver.recv() {
match message {
OrchestratorMessage::Run => {
self.sender
.send(MainLoopMessage::CheckProgram {
revision: self.revision,
})
.unwrap();
}
OrchestratorMessage::CheckProgramCompleted {
diagnostics,
revision,
} => {
// Only take the diagnostics if they are for the latest revision.
if self.revision == revision {
self.sender
.send(MainLoopMessage::CheckCompleted(diagnostics))
.unwrap();
} else {
tracing::debug!("Discarding diagnostics for outdated revision {revision} (current: {}).", self.revision);
}
}
OrchestratorMessage::FileChanges(changes) => {
// Request cancellation, but wait until all analysis tasks have completed to
// avoid stale messages in the next main loop.
self.revision += 1;
self.debounce_changes(changes);
}
OrchestratorMessage::Shutdown => {
return self.shutdown();
}
}
}
}
fn debounce_changes(&self, mut changes: Vec<FileWatcherChange>) {
loop {
// Consume possibly incoming file change messages before running a new analysis, but don't wait for more than 100ms.
crossbeam_channel::select! {
recv(self.receiver) -> message => {
match message {
Ok(OrchestratorMessage::Shutdown) => {
return self.shutdown();
}
Ok(OrchestratorMessage::FileChanges(file_changes)) => {
changes.extend(file_changes);
}
Ok(OrchestratorMessage::CheckProgramCompleted { .. })=> {
// disregard any outdated completion message.
}
Ok(OrchestratorMessage::Run) => unreachable!("The orchestrator is already running."),
Err(_) => {
// There are no more senders, no point in waiting for more messages
return;
}
}
},
default(std::time::Duration::from_millis(10)) => {
// No more file changes after 10 ms, send the changes and schedule a new analysis
self.sender.send(MainLoopMessage::ApplyChanges(changes)).unwrap();
self.sender.send(MainLoopMessage::CheckProgram { revision: self.revision}).unwrap();
return;
}
}
}
}
#[allow(clippy::unused_self)]
fn shutdown(&self) {
tracing::trace!("Shutting down orchestrator.");
}
}
/// Message sent from the orchestrator to the main loop.
#[derive(Debug)]
enum MainLoopMessage {
CheckProgram { revision: usize },
CheckCompleted(Vec<String>),
ApplyChanges(Vec<FileWatcherChange>),
CheckWorkspace,
CheckCompleted {
result: Vec<Box<dyn Diagnostic>>,
revision: u64,
},
ApplyChanges(Vec<watch::ChangeEvent>),
Exit,
}
#[derive(Debug)]
enum OrchestratorMessage {
Run,
Shutdown,
CheckProgramCompleted {
diagnostics: Vec<String>,
revision: usize,
},
FileChanges(Vec<FileWatcherChange>),
}
fn setup_tracing() {
let subscriber = Registry::default().with(
tracing_tree::HierarchicalLayer::default()
.with_indent_lines(true)
.with_indent_amount(2)
.with_bracketed_fields(true)
.with_thread_ids(true)
.with_targets(true)
.with_writer(|| Box::new(std::io::stderr()))
.with_timer(Uptime::default())
.with_filter(LoggingFilter {
trace_level: Level::TRACE,
}),
);
tracing::subscriber::set_global_default(subscriber).unwrap();
}
struct LoggingFilter {
trace_level: Level,
}
impl LoggingFilter {
fn is_enabled(&self, meta: &Metadata<'_>) -> bool {
let filter = if meta.target().starts_with("red_knot") || meta.target().starts_with("ruff") {
self.trace_level
} else {
Level::INFO
};
meta.level() <= &filter
}
}
impl<S> Filter<S> for LoggingFilter {
fn enabled(&self, meta: &Metadata<'_>, _cx: &Context<'_, S>) -> bool {
self.is_enabled(meta)
}
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
if self.is_enabled(meta) {
Interest::always()
} else {
Interest::never()
}
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(LevelFilter::from_level(self.trace_level))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +0,0 @@
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use ruff_python_ast::ModModule;
use ruff_python_parser::Parsed;
use crate::cache::KeyValueCache;
use crate::db::{QueryResult, SourceDb};
use crate::files::FileId;
use crate::source::source_text;
#[tracing::instrument(level = "debug", skip(db))]
pub(crate) fn parse(db: &dyn SourceDb, file_id: FileId) -> QueryResult<Arc<Parsed<ModModule>>> {
let jar = db.jar()?;
jar.parsed.get(&file_id, |file_id| {
let source = source_text(db, *file_id)?;
Ok(Arc::new(ruff_python_parser::parse_unchecked_source(
source.text(),
source.kind().into(),
)))
})
}
#[derive(Debug, Default)]
pub struct ParsedStorage(KeyValueCache<FileId, Arc<Parsed<ModModule>>>);
impl Deref for ParsedStorage {
type Target = KeyValueCache<FileId, Arc<Parsed<ModModule>>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ParsedStorage {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@@ -1,413 +0,0 @@
use rayon::{current_num_threads, yield_local};
use rustc_hash::FxHashSet;
use crate::db::{Database, QueryError, QueryResult};
use crate::files::FileId;
use crate::lint::{lint_semantic, lint_syntax, Diagnostics};
use crate::module::{file_to_module, resolve_module};
use crate::program::Program;
use crate::semantic::{semantic_index, Dependency};
impl Program {
/// Checks all open files in the workspace and its dependencies.
#[tracing::instrument(level = "debug", skip_all)]
pub fn check(&self, mode: ExecutionMode) -> QueryResult<Vec<String>> {
self.cancelled()?;
let mut context = CheckContext::new(self);
match mode {
ExecutionMode::SingleThreaded => SingleThreadedExecutor.run(&mut context)?,
ExecutionMode::ThreadPool => ThreadPoolExecutor.run(&mut context)?,
};
Ok(context.finish())
}
#[tracing::instrument(level = "debug", skip(self, context))]
fn check_file(&self, file: FileId, context: &CheckFileContext) -> QueryResult<Diagnostics> {
self.cancelled()?;
let index = semantic_index(self, file)?;
let dependencies = index.symbol_table().dependencies();
if !dependencies.is_empty() {
let module = file_to_module(self, file)?;
// TODO scheduling all dependencies here is wasteful if we don't infer any types on them
// but I think that's unlikely, so it is okay?
// Anyway, we need to figure out a way to retrieve the dependencies of a module
// from the persistent cache. So maybe it should be a separate query after all.
for dependency in dependencies {
let dependency_name = match dependency {
Dependency::Module(name) => Some(name.clone()),
Dependency::Relative { .. } => match &module {
Some(module) => module.resolve_dependency(self, dependency)?,
None => None,
},
};
if let Some(dependency_name) = dependency_name {
// TODO We may want to have a different check functions for non-first-party
// files because we only need to index them and not check them.
// Supporting non-first-party code also requires supporting typing stubs.
if let Some(dependency) = resolve_module(self, dependency_name)? {
if dependency.path(self)?.root().kind().is_first_party() {
context.schedule_dependency(dependency.path(self)?.file());
}
}
}
}
}
let mut diagnostics = Vec::new();
if self.workspace().is_file_open(file) {
diagnostics.extend_from_slice(&lint_syntax(self, file)?);
diagnostics.extend_from_slice(&lint_semantic(self, file)?);
}
Ok(Diagnostics::from(diagnostics))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ExecutionMode {
SingleThreaded,
ThreadPool,
}
/// Context that stores state information about the entire check operation.
struct CheckContext<'a> {
/// IDs of the files that have been queued for checking.
///
/// Used to avoid queuing the same file twice.
scheduled_files: FxHashSet<FileId>,
/// Reference to the program that is checked.
program: &'a Program,
/// The aggregated diagnostics
diagnostics: Vec<String>,
}
impl<'a> CheckContext<'a> {
fn new(program: &'a Program) -> Self {
Self {
scheduled_files: FxHashSet::default(),
program,
diagnostics: Vec::new(),
}
}
/// Returns the tasks to check all open files in the workspace.
fn check_open_files(&mut self) -> Vec<CheckOpenFileTask> {
self.scheduled_files
.extend(self.program.workspace().open_files());
self.program
.workspace()
.open_files()
.map(|file_id| CheckOpenFileTask { file_id })
.collect()
}
/// Returns the task to check a dependency.
fn check_dependency(&mut self, file_id: FileId) -> Option<CheckDependencyTask> {
if self.scheduled_files.insert(file_id) {
Some(CheckDependencyTask { file_id })
} else {
None
}
}
/// Pushes the result for a single file check operation
fn push_diagnostics(&mut self, diagnostics: &Diagnostics) {
self.diagnostics.extend_from_slice(diagnostics);
}
/// Returns a reference to the program that is being checked.
fn program(&self) -> &'a Program {
self.program
}
/// Creates a task context that is used to check a single file.
fn task_context<'b, S>(&self, dependency_scheduler: &'b S) -> CheckTaskContext<'a, 'b, S>
where
S: ScheduleDependency,
{
CheckTaskContext {
program: self.program,
dependency_scheduler,
}
}
fn finish(self) -> Vec<String> {
self.diagnostics
}
}
/// Trait that abstracts away how a dependency of a file gets scheduled for checking.
trait ScheduleDependency {
/// Schedules the file with the given ID for checking.
fn schedule(&self, file_id: FileId);
}
impl<T> ScheduleDependency for T
where
T: Fn(FileId),
{
fn schedule(&self, file_id: FileId) {
let f = self;
f(file_id);
}
}
/// Context that is used to run a single file check task.
///
/// The task is generic over `S` because it is passed across thread boundaries and
/// we don't want to add the requirement that [`ScheduleDependency`] must be [`Send`].
struct CheckTaskContext<'a, 'scheduler, S>
where
S: ScheduleDependency,
{
dependency_scheduler: &'scheduler S,
program: &'a Program,
}
impl<'a, 'scheduler, S> CheckTaskContext<'a, 'scheduler, S>
where
S: ScheduleDependency,
{
fn as_file_context(&self) -> CheckFileContext<'scheduler> {
CheckFileContext {
dependency_scheduler: self.dependency_scheduler,
}
}
}
/// Context passed when checking a single file.
///
/// This is a trimmed down version of [`CheckTaskContext`] with the type parameter `S` erased
/// to avoid monomorphization of [`Program:check_file`].
struct CheckFileContext<'a> {
dependency_scheduler: &'a dyn ScheduleDependency,
}
impl<'a> CheckFileContext<'a> {
fn schedule_dependency(&self, file_id: FileId) {
self.dependency_scheduler.schedule(file_id);
}
}
#[derive(Debug)]
enum CheckFileTask {
OpenFile(CheckOpenFileTask),
Dependency(CheckDependencyTask),
}
impl CheckFileTask {
/// Runs the task and returns the results for checking this file.
fn run<S>(&self, context: &CheckTaskContext<S>) -> QueryResult<Diagnostics>
where
S: ScheduleDependency,
{
match self {
Self::OpenFile(task) => task.run(context),
Self::Dependency(task) => task.run(context),
}
}
fn file_id(&self) -> FileId {
match self {
CheckFileTask::OpenFile(task) => task.file_id,
CheckFileTask::Dependency(task) => task.file_id,
}
}
}
/// Task to check an open file.
#[derive(Debug)]
struct CheckOpenFileTask {
file_id: FileId,
}
impl CheckOpenFileTask {
fn run<S>(&self, context: &CheckTaskContext<S>) -> QueryResult<Diagnostics>
where
S: ScheduleDependency,
{
context
.program
.check_file(self.file_id, &context.as_file_context())
}
}
/// Task to check a dependency file.
#[derive(Debug)]
struct CheckDependencyTask {
file_id: FileId,
}
impl CheckDependencyTask {
fn run<S>(&self, context: &CheckTaskContext<S>) -> QueryResult<Diagnostics>
where
S: ScheduleDependency,
{
context
.program
.check_file(self.file_id, &context.as_file_context())
}
}
/// Executor that schedules the checking of individual program files.
trait CheckExecutor {
fn run(self, context: &mut CheckContext) -> QueryResult<()>;
}
/// Executor that runs all check operations on the current thread.
///
/// The executor does not schedule dependencies for checking.
/// The main motivation for scheduling dependencies
/// in a multithreaded environment is to parse and index the dependencies concurrently.
/// However, that doesn't make sense in a single threaded environment, because the dependencies then compute
/// with checking the open files. Checking dependencies in a single threaded environment is more likely
/// to hurt performance because we end up analyzing files in their entirety, even if we only need to type check parts of them.
#[derive(Debug, Default)]
struct SingleThreadedExecutor;
impl CheckExecutor for SingleThreadedExecutor {
fn run(self, context: &mut CheckContext) -> QueryResult<()> {
let mut queue = context.check_open_files();
let noop_schedule_dependency = |_| {};
while let Some(file) = queue.pop() {
context.program().cancelled()?;
let task_context = context.task_context(&noop_schedule_dependency);
context.push_diagnostics(&file.run(&task_context)?);
}
Ok(())
}
}
/// Executor that runs the check operations on a thread pool.
///
/// The executor runs each check operation as its own task using a thread pool.
///
/// Other than [`SingleThreadedExecutor`], this executor schedules dependencies for checking. It
/// even schedules dependencies for checking when the thread pool size is 1 for a better debugging experience.
#[derive(Debug, Default)]
struct ThreadPoolExecutor;
impl CheckExecutor for ThreadPoolExecutor {
fn run(self, context: &mut CheckContext) -> QueryResult<()> {
let num_threads = current_num_threads();
let single_threaded = num_threads == 1;
let span = tracing::trace_span!("ThreadPoolExecutor::run", num_threads);
let _ = span.enter();
let mut queue: Vec<_> = context
.check_open_files()
.into_iter()
.map(CheckFileTask::OpenFile)
.collect();
let (sender, receiver) = if single_threaded {
// Use an unbounded queue for single threaded execution to prevent deadlocks
// when a single file schedules multiple dependencies.
crossbeam::channel::unbounded()
} else {
// Use a bounded queue to apply backpressure when the orchestration thread isn't able to keep
// up processing messages from the worker threads.
crossbeam::channel::bounded(num_threads)
};
let schedule_sender = sender.clone();
let schedule_dependency = move |file_id| {
schedule_sender
.send(ThreadPoolMessage::ScheduleDependency(file_id))
.unwrap();
};
let result = rayon::in_place_scope(|scope| {
let mut pending = 0usize;
loop {
context.program().cancelled()?;
// 1. Try to get a queued message to ensure that we have always remaining space in the channel to prevent blocking the worker threads.
// 2. Try to process a queued file
// 3. If there's no queued file wait for the next incoming message.
// 4. Exit if there are no more messages and no senders.
let message = if let Ok(message) = receiver.try_recv() {
message
} else if let Some(task) = queue.pop() {
pending += 1;
let task_context = context.task_context(&schedule_dependency);
let sender = sender.clone();
let task_span = tracing::trace_span!(
parent: &span,
"CheckFileTask::run",
file_id = task.file_id().as_u32(),
);
scope.spawn(move |_| {
task_span.in_scope(|| match task.run(&task_context) {
Ok(result) => {
sender.send(ThreadPoolMessage::Completed(result)).unwrap();
}
Err(err) => sender.send(ThreadPoolMessage::Errored(err)).unwrap(),
});
});
// If this is a single threaded rayon thread pool, yield the current thread
// or we never start processing the work items.
if single_threaded {
yield_local();
}
continue;
} else if let Ok(message) = receiver.recv() {
message
} else {
break;
};
match message {
ThreadPoolMessage::ScheduleDependency(dependency) => {
if let Some(task) = context.check_dependency(dependency) {
queue.push(CheckFileTask::Dependency(task));
}
}
ThreadPoolMessage::Completed(diagnostics) => {
context.push_diagnostics(&diagnostics);
pending -= 1;
if pending == 0 && queue.is_empty() {
break;
}
}
ThreadPoolMessage::Errored(err) => {
return Err(err);
}
}
}
Ok(())
});
result
}
}
#[derive(Debug)]
enum ThreadPoolMessage {
ScheduleDependency(FileId),
Completed(Diagnostics),
Errored(QueryError),
}

View File

@@ -1,275 +0,0 @@
use std::collections::hash_map::Entry;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use rustc_hash::FxHashMap;
use crate::db::{
Database, Db, DbRuntime, DbWithJar, HasJar, HasJars, JarsStorage, LintDb, LintJar,
ParallelDatabase, QueryResult, SemanticDb, SemanticJar, Snapshot, SourceDb, SourceJar, Upcast,
};
use crate::files::{FileId, Files};
use crate::Workspace;
pub mod check;
#[derive(Debug)]
pub struct Program {
jars: JarsStorage<Program>,
files: Files,
workspace: Workspace,
}
impl Program {
pub fn new(workspace: Workspace) -> Self {
Self {
jars: JarsStorage::default(),
files: Files::default(),
workspace,
}
}
pub fn apply_changes<I>(&mut self, changes: I)
where
I: IntoIterator<Item = FileWatcherChange>,
{
let mut aggregated_changes = AggregatedChanges::default();
aggregated_changes.extend(changes.into_iter().map(|change| FileChange {
id: self.files.intern(&change.path),
kind: change.kind,
}));
let (source, semantic, lint) = self.jars_mut();
for change in aggregated_changes.iter() {
semantic.module_resolver.remove_module_by_file(change.id);
semantic.semantic_indices.remove(&change.id);
source.sources.remove(&change.id);
source.parsed.remove(&change.id);
// TODO: remove all dependent modules as well
semantic.type_store.remove_module(change.id);
lint.lint_syntax.remove(&change.id);
lint.lint_semantic.remove(&change.id);
}
}
pub fn files(&self) -> &Files {
&self.files
}
pub fn workspace(&self) -> &Workspace {
&self.workspace
}
pub fn workspace_mut(&mut self) -> &mut Workspace {
&mut self.workspace
}
}
impl SourceDb for Program {
fn file_id(&self, path: &Path) -> FileId {
self.files.intern(path)
}
fn file_path(&self, file_id: FileId) -> Arc<Path> {
self.files.path(file_id)
}
}
impl DbWithJar<SourceJar> for Program {}
impl SemanticDb for Program {}
impl DbWithJar<SemanticJar> for Program {}
impl LintDb for Program {}
impl DbWithJar<LintJar> for Program {}
impl Upcast<dyn SemanticDb> for Program {
fn upcast(&self) -> &(dyn SemanticDb + 'static) {
self
}
}
impl Upcast<dyn SourceDb> for Program {
fn upcast(&self) -> &(dyn SourceDb + 'static) {
self
}
}
impl Upcast<dyn LintDb> for Program {
fn upcast(&self) -> &(dyn LintDb + 'static) {
self
}
}
impl Db for Program {}
impl Database for Program {
fn runtime(&self) -> &DbRuntime {
self.jars.runtime()
}
fn runtime_mut(&mut self) -> &mut DbRuntime {
self.jars.runtime_mut()
}
}
impl ParallelDatabase for Program {
fn snapshot(&self) -> Snapshot<Self> {
Snapshot::new(Self {
jars: self.jars.snapshot(),
files: self.files.snapshot(),
workspace: self.workspace.clone(),
})
}
}
impl HasJars for Program {
type Jars = (SourceJar, SemanticJar, LintJar);
fn jars(&self) -> QueryResult<&Self::Jars> {
self.jars.jars()
}
fn jars_mut(&mut self) -> &mut Self::Jars {
self.jars.jars_mut()
}
}
impl HasJar<SourceJar> for Program {
fn jar(&self) -> QueryResult<&SourceJar> {
Ok(&self.jars()?.0)
}
fn jar_mut(&mut self) -> &mut SourceJar {
&mut self.jars_mut().0
}
}
impl HasJar<SemanticJar> for Program {
fn jar(&self) -> QueryResult<&SemanticJar> {
Ok(&self.jars()?.1)
}
fn jar_mut(&mut self) -> &mut SemanticJar {
&mut self.jars_mut().1
}
}
impl HasJar<LintJar> for Program {
fn jar(&self) -> QueryResult<&LintJar> {
Ok(&self.jars()?.2)
}
fn jar_mut(&mut self) -> &mut LintJar {
&mut self.jars_mut().2
}
}
#[derive(Clone, Debug)]
pub struct FileWatcherChange {
path: PathBuf,
kind: FileChangeKind,
}
impl FileWatcherChange {
pub fn new(path: PathBuf, kind: FileChangeKind) -> Self {
Self { path, kind }
}
}
#[derive(Copy, Clone, Debug)]
struct FileChange {
id: FileId,
kind: FileChangeKind,
}
impl FileChange {
fn file_id(self) -> FileId {
self.id
}
fn kind(self) -> FileChangeKind {
self.kind
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FileChangeKind {
Created,
Modified,
Deleted,
}
#[derive(Default, Debug)]
struct AggregatedChanges {
changes: FxHashMap<FileId, FileChangeKind>,
}
impl AggregatedChanges {
fn add(&mut self, change: FileChange) {
match self.changes.entry(change.file_id()) {
Entry::Occupied(mut entry) => {
let merged = entry.get_mut();
match (merged, change.kind()) {
(FileChangeKind::Created, FileChangeKind::Deleted) => {
// Deletion after creations means that ruff never saw the file.
entry.remove();
}
(FileChangeKind::Created, FileChangeKind::Modified) => {
// No-op, for ruff, modifying a file that it doesn't yet know that it exists is still considered a creation.
}
(FileChangeKind::Modified, FileChangeKind::Created) => {
// Uhh, that should probably not happen. Continue considering it a modification.
}
(FileChangeKind::Modified, FileChangeKind::Deleted) => {
*entry.get_mut() = FileChangeKind::Deleted;
}
(FileChangeKind::Deleted, FileChangeKind::Created) => {
*entry.get_mut() = FileChangeKind::Modified;
}
(FileChangeKind::Deleted, FileChangeKind::Modified) => {
// That's weird, but let's consider it a modification.
*entry.get_mut() = FileChangeKind::Modified;
}
(FileChangeKind::Created, FileChangeKind::Created)
| (FileChangeKind::Modified, FileChangeKind::Modified)
| (FileChangeKind::Deleted, FileChangeKind::Deleted) => {
// No-op transitions. Some of them should be impossible but we handle them anyway.
}
}
}
Entry::Vacant(entry) => {
entry.insert(change.kind());
}
}
}
fn extend<I>(&mut self, changes: I)
where
I: IntoIterator<Item = FileChange>,
{
let iter = changes.into_iter();
let (lower, _) = iter.size_hint();
self.changes.reserve(lower);
for change in iter {
self.add(change);
}
}
fn iter(&self) -> impl Iterator<Item = FileChange> + '_ {
self.changes.iter().map(|(id, kind)| FileChange {
id: *id,
kind: *kind,
})
}
}

View File

@@ -0,0 +1,68 @@
/// Enumeration of all supported Python versions
///
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default, clap::ValueEnum)]
pub enum PythonVersion {
#[value(name = "3.7")]
Py37,
#[value(name = "3.8")]
Py38,
#[default]
#[value(name = "3.9")]
Py39,
#[value(name = "3.10")]
Py310,
#[value(name = "3.11")]
Py311,
#[value(name = "3.12")]
Py312,
#[value(name = "3.13")]
Py313,
}
impl PythonVersion {
const fn as_str(self) -> &'static str {
match self {
Self::Py37 => "3.7",
Self::Py38 => "3.8",
Self::Py39 => "3.9",
Self::Py310 => "3.10",
Self::Py311 => "3.11",
Self::Py312 => "3.12",
Self::Py313 => "3.13",
}
}
}
impl std::fmt::Display for PythonVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl From<PythonVersion> for red_knot_python_semantic::PythonVersion {
fn from(value: PythonVersion) -> Self {
match value {
PythonVersion::Py37 => Self::PY37,
PythonVersion::Py38 => Self::PY38,
PythonVersion::Py39 => Self::PY39,
PythonVersion::Py310 => Self::PY310,
PythonVersion::Py311 => Self::PY311,
PythonVersion::Py312 => Self::PY312,
PythonVersion::Py313 => Self::PY313,
}
}
}
#[cfg(test)]
mod tests {
use crate::python_version::PythonVersion;
#[test]
fn same_default_as_python_version() {
assert_eq!(
red_knot_python_semantic::PythonVersion::from(PythonVersion::default()),
red_knot_python_semantic::PythonVersion::default()
);
}
}

View File

@@ -1,882 +0,0 @@
use std::num::NonZeroU32;
use ruff_python_ast as ast;
use ruff_python_ast::visitor::source_order::SourceOrderVisitor;
use ruff_python_ast::AstNode;
use crate::ast_ids::{NodeKey, TypedNodeKey};
use crate::cache::KeyValueCache;
use crate::db::{QueryResult, SemanticDb, SemanticJar};
use crate::files::FileId;
use crate::module::Module;
use crate::module::ModuleName;
use crate::parse::parse;
use crate::Name;
pub(crate) use definitions::Definition;
use definitions::{ImportDefinition, ImportFromDefinition};
pub(crate) use flow_graph::ConstrainedDefinition;
use flow_graph::{FlowGraph, FlowGraphBuilder, FlowNodeId, ReachableDefinitionsIterator};
use ruff_index::{newtype_index, IndexVec};
use rustc_hash::FxHashMap;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
pub(crate) use symbol_table::{Dependency, SymbolId};
use symbol_table::{ScopeId, ScopeKind, SymbolFlags, SymbolTable, SymbolTableBuilder};
pub(crate) use types::{infer_definition_type, infer_symbol_public_type, Type, TypeStore};
mod definitions;
mod flow_graph;
mod symbol_table;
mod types;
#[tracing::instrument(level = "debug", skip(db))]
pub fn semantic_index(db: &dyn SemanticDb, file_id: FileId) -> QueryResult<Arc<SemanticIndex>> {
let jar: &SemanticJar = db.jar()?;
jar.semantic_indices.get(&file_id, |_| {
let parsed = parse(db.upcast(), file_id)?;
Ok(Arc::from(SemanticIndex::from_ast(parsed.syntax())))
})
}
#[tracing::instrument(level = "debug", skip(db))]
pub fn resolve_global_symbol(
db: &dyn SemanticDb,
module: Module,
name: &str,
) -> QueryResult<Option<GlobalSymbolId>> {
let file_id = module.path(db)?.file();
let symbol_table = &semantic_index(db, file_id)?.symbol_table;
let Some(symbol_id) = symbol_table.root_symbol_id_by_name(name) else {
return Ok(None);
};
Ok(Some(GlobalSymbolId { file_id, symbol_id }))
}
#[newtype_index]
pub struct ExpressionId;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct GlobalSymbolId {
pub(crate) file_id: FileId,
pub(crate) symbol_id: SymbolId,
}
#[derive(Debug)]
pub struct SemanticIndex {
symbol_table: SymbolTable,
flow_graph: FlowGraph,
expressions: FxHashMap<NodeKey, ExpressionId>,
expressions_by_id: IndexVec<ExpressionId, NodeKey>,
}
impl SemanticIndex {
pub fn from_ast(module: &ast::ModModule) -> Self {
let root_scope_id = SymbolTable::root_scope_id();
let mut indexer = SemanticIndexer {
symbol_table_builder: SymbolTableBuilder::new(),
flow_graph_builder: FlowGraphBuilder::new(),
scopes: vec![ScopeState {
scope_id: root_scope_id,
current_flow_node_id: FlowGraph::start(),
}],
expressions: FxHashMap::default(),
expressions_by_id: IndexVec::default(),
current_definition: None,
};
indexer.visit_body(&module.body);
indexer.finish()
}
fn resolve_expression_id<'a>(
&self,
ast: &'a ast::ModModule,
expression_id: ExpressionId,
) -> ast::AnyNodeRef<'a> {
let node_key = self.expressions_by_id[expression_id];
node_key
.resolve(ast.as_any_node_ref())
.expect("node to resolve")
}
/// Return an iterator over all definitions of `symbol_id` reachable from `use_expr`. The value
/// of `symbol_id` in `use_expr` must originate from one of the iterated definitions (or from
/// an external reassignment of the name outside of this scope).
pub fn reachable_definitions(
&self,
symbol_id: SymbolId,
use_expr: &ast::Expr,
) -> ReachableDefinitionsIterator {
let expression_id = self.expression_id(use_expr);
ReachableDefinitionsIterator::new(
&self.flow_graph,
symbol_id,
self.flow_graph.for_expr(expression_id),
)
}
pub fn expression_id(&self, expression: &ast::Expr) -> ExpressionId {
self.expressions[&NodeKey::from_node(expression.into())]
}
pub fn symbol_table(&self) -> &SymbolTable {
&self.symbol_table
}
}
#[derive(Debug)]
struct ScopeState {
scope_id: ScopeId,
current_flow_node_id: FlowNodeId,
}
#[derive(Debug)]
struct SemanticIndexer {
symbol_table_builder: SymbolTableBuilder,
flow_graph_builder: FlowGraphBuilder,
scopes: Vec<ScopeState>,
/// the definition whose target(s) we are currently walking
current_definition: Option<Definition>,
expressions: FxHashMap<NodeKey, ExpressionId>,
expressions_by_id: IndexVec<ExpressionId, NodeKey>,
}
impl SemanticIndexer {
pub(crate) fn finish(mut self) -> SemanticIndex {
let SemanticIndexer {
flow_graph_builder,
symbol_table_builder,
..
} = self;
self.expressions.shrink_to_fit();
self.expressions_by_id.shrink_to_fit();
SemanticIndex {
flow_graph: flow_graph_builder.finish(),
symbol_table: symbol_table_builder.finish(),
expressions: self.expressions,
expressions_by_id: self.expressions_by_id,
}
}
fn set_current_flow_node(&mut self, new_flow_node_id: FlowNodeId) {
let scope_state = self.scopes.last_mut().expect("scope stack is never empty");
scope_state.current_flow_node_id = new_flow_node_id;
}
fn current_flow_node(&self) -> FlowNodeId {
self.scopes
.last()
.expect("scope stack is never empty")
.current_flow_node_id
}
fn add_or_update_symbol(&mut self, identifier: &str, flags: SymbolFlags) -> SymbolId {
self.symbol_table_builder
.add_or_update_symbol(self.cur_scope(), identifier, flags)
}
fn add_or_update_symbol_with_def(
&mut self,
identifier: &str,
definition: Definition,
) -> SymbolId {
let symbol_id = self.add_or_update_symbol(identifier, SymbolFlags::IS_DEFINED);
self.symbol_table_builder
.add_definition(symbol_id, definition.clone());
let new_flow_node_id =
self.flow_graph_builder
.add_definition(symbol_id, definition, self.current_flow_node());
self.set_current_flow_node(new_flow_node_id);
symbol_id
}
fn push_scope(
&mut self,
name: &str,
kind: ScopeKind,
definition: Option<Definition>,
defining_symbol: Option<SymbolId>,
) -> ScopeId {
let scope_id = self.symbol_table_builder.add_child_scope(
self.cur_scope(),
name,
kind,
definition,
defining_symbol,
);
self.scopes.push(ScopeState {
scope_id,
current_flow_node_id: FlowGraph::start(),
});
scope_id
}
fn pop_scope(&mut self) -> ScopeId {
self.scopes
.pop()
.expect("Scope stack should never be empty")
.scope_id
}
fn cur_scope(&self) -> ScopeId {
self.scopes
.last()
.expect("Scope stack should never be empty")
.scope_id
}
fn record_scope_for_node(&mut self, node_key: NodeKey, scope_id: ScopeId) {
self.symbol_table_builder
.record_scope_for_node(node_key, scope_id);
}
fn insert_constraint(&mut self, expr: &ast::Expr) {
let node_key = NodeKey::from_node(expr.into());
let expression_id = self.expressions[&node_key];
let constraint = self
.flow_graph_builder
.add_constraint(self.current_flow_node(), expression_id);
self.set_current_flow_node(constraint);
}
fn with_type_params(
&mut self,
name: &str,
params: &Option<Box<ast::TypeParams>>,
definition: Option<Definition>,
defining_symbol: Option<SymbolId>,
nested: impl FnOnce(&mut Self) -> ScopeId,
) -> ScopeId {
if let Some(type_params) = params {
self.push_scope(name, ScopeKind::Annotation, definition, defining_symbol);
for type_param in &type_params.type_params {
let name = match type_param {
ast::TypeParam::TypeVar(ast::TypeParamTypeVar { name, .. }) => name,
ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { name, .. }) => name,
ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { name, .. }) => name,
};
self.add_or_update_symbol(name, SymbolFlags::IS_DEFINED);
}
}
let scope_id = nested(self);
if params.is_some() {
self.pop_scope();
}
scope_id
}
}
impl SourceOrderVisitor<'_> for SemanticIndexer {
fn visit_expr(&mut self, expr: &ast::Expr) {
let node_key = NodeKey::from_node(expr.into());
let expression_id = self.expressions_by_id.push(node_key);
let flow_expression_id = self
.flow_graph_builder
.record_expr(self.current_flow_node());
debug_assert_eq!(expression_id, flow_expression_id);
let symbol_expression_id = self
.symbol_table_builder
.record_expression(self.cur_scope());
debug_assert_eq!(expression_id, symbol_expression_id);
self.expressions.insert(node_key, expression_id);
match expr {
ast::Expr::Name(ast::ExprName { id, ctx, .. }) => {
let flags = match ctx {
ast::ExprContext::Load => SymbolFlags::IS_USED,
ast::ExprContext::Store => SymbolFlags::IS_DEFINED,
ast::ExprContext::Del => SymbolFlags::IS_DEFINED,
ast::ExprContext::Invalid => SymbolFlags::empty(),
};
self.add_or_update_symbol(id, flags);
if flags.contains(SymbolFlags::IS_DEFINED) {
if let Some(curdef) = self.current_definition.clone() {
self.add_or_update_symbol_with_def(id, curdef);
}
}
ast::visitor::source_order::walk_expr(self, expr);
}
ast::Expr::Named(node) => {
debug_assert!(self.current_definition.is_none());
self.current_definition =
Some(Definition::NamedExpr(TypedNodeKey::from_node(node)));
// TODO walrus in comprehensions is implicitly nonlocal
self.visit_expr(&node.target);
self.current_definition = None;
self.visit_expr(&node.value);
}
ast::Expr::If(ast::ExprIf {
body, test, orelse, ..
}) => {
// TODO detect statically known truthy or falsy test (via type inference, not naive
// AST inspection, so we can't simplify here, need to record test expression in CFG
// for later checking)
self.visit_expr(test);
let if_branch = self.flow_graph_builder.add_branch(self.current_flow_node());
self.set_current_flow_node(if_branch);
self.insert_constraint(test);
self.visit_expr(body);
let post_body = self.current_flow_node();
self.set_current_flow_node(if_branch);
self.visit_expr(orelse);
let post_else = self
.flow_graph_builder
.add_phi(self.current_flow_node(), post_body);
self.set_current_flow_node(post_else);
}
_ => {
ast::visitor::source_order::walk_expr(self, expr);
}
}
}
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
// TODO need to capture more definition statements here
match stmt {
ast::Stmt::ClassDef(node) => {
let node_key = TypedNodeKey::from_node(node);
let def = Definition::ClassDef(node_key.clone());
let symbol_id = self.add_or_update_symbol_with_def(&node.name, def.clone());
for decorator in &node.decorator_list {
self.visit_decorator(decorator);
}
let scope_id = self.with_type_params(
&node.name,
&node.type_params,
Some(def.clone()),
Some(symbol_id),
|indexer| {
if let Some(arguments) = &node.arguments {
indexer.visit_arguments(arguments);
}
let scope_id = indexer.push_scope(
&node.name,
ScopeKind::Class,
Some(def.clone()),
Some(symbol_id),
);
indexer.visit_body(&node.body);
indexer.pop_scope();
scope_id
},
);
self.record_scope_for_node(*node_key.erased(), scope_id);
}
ast::Stmt::FunctionDef(node) => {
let node_key = TypedNodeKey::from_node(node);
let def = Definition::FunctionDef(node_key.clone());
let symbol_id = self.add_or_update_symbol_with_def(&node.name, def.clone());
for decorator in &node.decorator_list {
self.visit_decorator(decorator);
}
let scope_id = self.with_type_params(
&node.name,
&node.type_params,
Some(def.clone()),
Some(symbol_id),
|indexer| {
indexer.visit_parameters(&node.parameters);
for expr in &node.returns {
indexer.visit_annotation(expr);
}
let scope_id = indexer.push_scope(
&node.name,
ScopeKind::Function,
Some(def.clone()),
Some(symbol_id),
);
indexer.visit_body(&node.body);
indexer.pop_scope();
scope_id
},
);
self.record_scope_for_node(*node_key.erased(), scope_id);
}
ast::Stmt::Import(ast::StmtImport { names, .. }) => {
for alias in names {
let symbol_name = if let Some(asname) = &alias.asname {
asname.id.as_str()
} else {
alias.name.id.split('.').next().unwrap()
};
let module = ModuleName::new(&alias.name.id);
let def = Definition::Import(ImportDefinition {
module: module.clone(),
});
self.add_or_update_symbol_with_def(symbol_name, def);
self.symbol_table_builder
.add_dependency(Dependency::Module(module));
}
}
ast::Stmt::ImportFrom(ast::StmtImportFrom {
module,
names,
level,
..
}) => {
let module = module.as_ref().map(|m| ModuleName::new(&m.id));
for alias in names {
let symbol_name = if let Some(asname) = &alias.asname {
asname.id.as_str()
} else {
alias.name.id.as_str()
};
let def = Definition::ImportFrom(ImportFromDefinition {
module: module.clone(),
name: Name::new(&alias.name.id),
level: *level,
});
self.add_or_update_symbol_with_def(symbol_name, def);
}
let dependency = if let Some(module) = module {
match NonZeroU32::new(*level) {
Some(level) => Dependency::Relative {
level,
module: Some(module),
},
None => Dependency::Module(module),
}
} else {
Dependency::Relative {
level: NonZeroU32::new(*level)
.expect("Import without a module to have a level > 0"),
module,
}
};
self.symbol_table_builder.add_dependency(dependency);
}
ast::Stmt::Assign(node) => {
debug_assert!(self.current_definition.is_none());
self.visit_expr(&node.value);
self.current_definition =
Some(Definition::Assignment(TypedNodeKey::from_node(node)));
for expr in &node.targets {
self.visit_expr(expr);
}
self.current_definition = None;
}
ast::Stmt::If(node) => {
// TODO detect statically known truthy or falsy test (via type inference, not naive
// AST inspection, so we can't simplify here, need to record test expression in CFG
// for later checking)
// we visit the if "test" condition first regardless
self.visit_expr(&node.test);
// create branch node: does the if test pass or not?
let if_branch = self.flow_graph_builder.add_branch(self.current_flow_node());
// visit the body of the `if` clause
self.set_current_flow_node(if_branch);
self.insert_constraint(&node.test);
self.visit_body(&node.body);
// Flow node for the last if/elif condition branch; represents the "no branch
// taken yet" possibility (where "taking a branch" means that the condition in an
// if or elif evaluated to true and control flow went into that clause).
let mut prior_branch = if_branch;
// Flow node for the state after the prior if/elif/else clause; represents "we have
// taken one of the branches up to this point." Initially set to the post-if-clause
// state, later will be set to the phi node joining that possible path with the
// possibility that we took a later if/elif/else clause instead.
let mut post_prior_clause = self.current_flow_node();
// Flag to mark if the final clause is an "else" -- if so, that means the "match no
// clauses" path is not possible, we have to go through one of the clauses.
let mut last_branch_is_else = false;
for clause in &node.elif_else_clauses {
if let Some(test) = &clause.test {
self.visit_expr(test);
// This is an elif clause. Create a new branch node. Its predecessor is the
// previous branch node, because we can only take one branch in an entire
// if/elif/else chain, so if we take this branch, it can only be because we
// didn't take the previous one.
prior_branch = self.flow_graph_builder.add_branch(prior_branch);
self.set_current_flow_node(prior_branch);
self.insert_constraint(test);
} else {
// This is an else clause. No need to create a branch node; there's no
// branch here, if we haven't taken any previous branch, we definitely go
// into the "else" clause.
self.set_current_flow_node(prior_branch);
last_branch_is_else = true;
}
self.visit_elif_else_clause(clause);
// Update `post_prior_clause` to a new phi node joining the possibility that we
// took any of the previous branches with the possibility that we took the one
// just visited.
post_prior_clause = self
.flow_graph_builder
.add_phi(self.current_flow_node(), post_prior_clause);
}
if !last_branch_is_else {
// Final branch was not an "else", which means it's possible we took zero
// branches in the entire if/elif chain, so we need one more phi node to join
// the "no branches taken" possibility.
post_prior_clause = self
.flow_graph_builder
.add_phi(post_prior_clause, prior_branch);
}
// Onward, with current flow node set to our final Phi node.
self.set_current_flow_node(post_prior_clause);
}
_ => {
ast::visitor::source_order::walk_stmt(self, stmt);
}
}
}
}
#[derive(Debug, Default)]
pub struct SemanticIndexStorage(KeyValueCache<FileId, Arc<SemanticIndex>>);
impl Deref for SemanticIndexStorage {
type Target = KeyValueCache<FileId, Arc<SemanticIndex>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SemanticIndexStorage {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(test)]
mod tests {
use crate::semantic::symbol_table::{Symbol, SymbolIterator};
use ruff_python_ast as ast;
use ruff_python_ast::ModModule;
use ruff_python_parser::{Mode, Parsed};
use super::{Definition, ScopeKind, SemanticIndex, SymbolId};
fn parse(code: &str) -> Parsed<ModModule> {
ruff_python_parser::parse_unchecked(code, Mode::Module)
.try_into_module()
.unwrap()
}
fn names<I>(it: SymbolIterator<I>) -> Vec<&str>
where
I: Iterator<Item = SymbolId>,
{
let mut symbols: Vec<_> = it.map(Symbol::name).collect();
symbols.sort_unstable();
symbols
}
#[test]
fn empty() {
let parsed = parse("");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()).len(), 0);
}
#[test]
fn simple() {
let parsed = parse("x");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["x"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("x").unwrap())
.len(),
0
);
}
#[test]
fn annotation_only() {
let parsed = parse("x: int");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["int", "x"]);
// TODO record definition
}
#[test]
fn import() {
let parsed = parse("import foo");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["foo"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("foo").unwrap())
.len(),
1
);
}
#[test]
fn import_sub() {
let parsed = parse("import foo.bar");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["foo"]);
}
#[test]
fn import_as() {
let parsed = parse("import foo.bar as baz");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["baz"]);
}
#[test]
fn import_from() {
let parsed = parse("from bar import foo");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["foo"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("foo").unwrap())
.len(),
1
);
assert!(
table.root_symbol_id_by_name("foo").is_some_and(|sid| {
let s = sid.symbol(&table);
s.is_defined() || !s.is_used()
}),
"symbols that are defined get the defined flag"
);
}
#[test]
fn assign() {
let parsed = parse("x = foo");
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["foo", "x"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("x").unwrap())
.len(),
1
);
assert!(
table.root_symbol_id_by_name("foo").is_some_and(|sid| {
let s = sid.symbol(&table);
!s.is_defined() && s.is_used()
}),
"a symbol used but not defined in a scope should have only the used flag"
);
}
#[test]
fn class_scope() {
let parsed = parse(
"
class C:
x = 1
y = 2
",
);
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["C", "y"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
let c_scope = scopes[0].scope(&table);
assert_eq!(c_scope.kind(), ScopeKind::Class);
assert_eq!(c_scope.name(), "C");
assert_eq!(names(table.symbols_for_scope(scopes[0])), vec!["x"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("C").unwrap())
.len(),
1
);
}
#[test]
fn func_scope() {
let parsed = parse(
"
def func():
x = 1
y = 2
",
);
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["func", "y"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
let func_scope = scopes[0].scope(&table);
assert_eq!(func_scope.kind(), ScopeKind::Function);
assert_eq!(func_scope.name(), "func");
assert_eq!(names(table.symbols_for_scope(scopes[0])), vec!["x"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("func").unwrap())
.len(),
1
);
}
#[test]
fn dupes() {
let parsed = parse(
"
def func():
x = 1
def func():
y = 2
",
);
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["func"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 2);
let func_scope_1 = scopes[0].scope(&table);
let func_scope_2 = scopes[1].scope(&table);
assert_eq!(func_scope_1.kind(), ScopeKind::Function);
assert_eq!(func_scope_1.name(), "func");
assert_eq!(func_scope_2.kind(), ScopeKind::Function);
assert_eq!(func_scope_2.name(), "func");
assert_eq!(names(table.symbols_for_scope(scopes[0])), vec!["x"]);
assert_eq!(names(table.symbols_for_scope(scopes[1])), vec!["y"]);
assert_eq!(
table
.definitions(table.root_symbol_id_by_name("func").unwrap())
.len(),
2
);
}
#[test]
fn generic_func() {
let parsed = parse(
"
def func[T]():
x = 1
",
);
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["func"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
let ann_scope_id = scopes[0];
let ann_scope = ann_scope_id.scope(&table);
assert_eq!(ann_scope.kind(), ScopeKind::Annotation);
assert_eq!(ann_scope.name(), "func");
assert_eq!(names(table.symbols_for_scope(ann_scope_id)), vec!["T"]);
let scopes = table.child_scope_ids_of(ann_scope_id);
assert_eq!(scopes.len(), 1);
let func_scope_id = scopes[0];
let func_scope = func_scope_id.scope(&table);
assert_eq!(func_scope.kind(), ScopeKind::Function);
assert_eq!(func_scope.name(), "func");
assert_eq!(names(table.symbols_for_scope(func_scope_id)), vec!["x"]);
}
#[test]
fn generic_class() {
let parsed = parse(
"
class C[T]:
x = 1
",
);
let table = SemanticIndex::from_ast(parsed.syntax()).symbol_table;
assert_eq!(names(table.root_symbols()), vec!["C"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
let ann_scope_id = scopes[0];
let ann_scope = ann_scope_id.scope(&table);
assert_eq!(ann_scope.kind(), ScopeKind::Annotation);
assert_eq!(ann_scope.name(), "C");
assert_eq!(names(table.symbols_for_scope(ann_scope_id)), vec!["T"]);
assert!(
table
.symbol_by_name(ann_scope_id, "T")
.is_some_and(|s| s.is_defined() && !s.is_used()),
"type parameters are defined by the scope that introduces them"
);
let scopes = table.child_scope_ids_of(ann_scope_id);
assert_eq!(scopes.len(), 1);
let func_scope_id = scopes[0];
let func_scope = func_scope_id.scope(&table);
assert_eq!(func_scope.kind(), ScopeKind::Class);
assert_eq!(func_scope.name(), "C");
assert_eq!(names(table.symbols_for_scope(func_scope_id)), vec!["x"]);
}
#[test]
fn reachability_trivial() {
let parsed = parse("x = 1; x");
let ast = parsed.syntax();
let index = SemanticIndex::from_ast(ast);
let table = &index.symbol_table;
let x_sym = table
.root_symbol_id_by_name("x")
.expect("x symbol should exist");
let ast::Stmt::Expr(ast::StmtExpr { value: x_use, .. }) = &ast.body[1] else {
panic!("should be an expr")
};
let x_defs: Vec<_> = index
.reachable_definitions(x_sym, x_use)
.map(|constrained_definition| constrained_definition.definition)
.collect();
assert_eq!(x_defs.len(), 1);
let Definition::Assignment(node_key) = &x_defs[0] else {
panic!("def should be an assignment")
};
let Some(def_node) = node_key.resolve(ast.into()) else {
panic!("node key should resolve")
};
let ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(num),
..
}) = &*def_node.value
else {
panic!("should be a number literal")
};
assert_eq!(*num, 1);
}
#[test]
fn expression_scope() {
let parsed = parse("x = 1;\ndef test():\n y = 4");
let ast = parsed.syntax();
let index = SemanticIndex::from_ast(ast);
let table = &index.symbol_table;
let x_sym = table
.root_symbol_by_name("x")
.expect("x symbol should exist");
let x_stmt = ast.body[0].as_assign_stmt().unwrap();
let x_id = index.expression_id(&x_stmt.targets[0]);
assert_eq!(table.scope_of_expression(x_id).kind(), ScopeKind::Module);
assert_eq!(table.scope_id_of_expression(x_id), x_sym.scope_id());
let def = ast.body[1].as_function_def_stmt().unwrap();
let y_stmt = def.body[0].as_assign_stmt().unwrap();
let y_id = index.expression_id(&y_stmt.targets[0]);
assert_eq!(table.scope_of_expression(y_id).kind(), ScopeKind::Function);
}
}

View File

@@ -1,52 +0,0 @@
use crate::ast_ids::TypedNodeKey;
use crate::semantic::ModuleName;
use crate::Name;
use ruff_python_ast as ast;
// TODO storing TypedNodeKey for definitions means we have to search to find them again in the AST;
// this is at best O(log n). If looking up definitions is a bottleneck we should look for
// alternatives here.
// TODO intern Definitions in SymbolTable and reference using IDs?
#[derive(Clone, Debug)]
pub enum Definition {
// For the import cases, we don't need reference to any arbitrary AST subtrees (annotations,
// RHS), and referencing just the import statement node is imprecise (a single import statement
// can assign many symbols, we'd have to re-search for the one we care about), so we just copy
// the small amount of information we need from the AST.
Import(ImportDefinition),
ImportFrom(ImportFromDefinition),
ClassDef(TypedNodeKey<ast::StmtClassDef>),
FunctionDef(TypedNodeKey<ast::StmtFunctionDef>),
Assignment(TypedNodeKey<ast::StmtAssign>),
AnnotatedAssignment(TypedNodeKey<ast::StmtAnnAssign>),
NamedExpr(TypedNodeKey<ast::ExprNamed>),
/// represents the implicit initial definition of every name as "unbound"
Unbound,
// TODO with statements, except handlers, function args...
}
#[derive(Clone, Debug)]
pub struct ImportDefinition {
pub module: ModuleName,
}
#[derive(Clone, Debug)]
pub struct ImportFromDefinition {
pub module: Option<ModuleName>,
pub name: Name,
pub level: u32,
}
impl ImportFromDefinition {
pub fn module(&self) -> Option<&ModuleName> {
self.module.as_ref()
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn level(&self) -> u32 {
self.level
}
}

View File

@@ -1,270 +0,0 @@
use super::symbol_table::SymbolId;
use crate::semantic::{Definition, ExpressionId};
use ruff_index::{newtype_index, IndexVec};
use std::iter::FusedIterator;
use std::ops::Range;
#[newtype_index]
pub struct FlowNodeId;
#[derive(Debug)]
pub(crate) enum FlowNode {
Start,
Definition(DefinitionFlowNode),
Branch(BranchFlowNode),
Phi(PhiFlowNode),
Constraint(ConstraintFlowNode),
}
/// A point in control flow where a symbol is defined
#[derive(Debug)]
pub(crate) struct DefinitionFlowNode {
symbol_id: SymbolId,
definition: Definition,
predecessor: FlowNodeId,
}
/// A branch in control flow
#[derive(Debug)]
pub(crate) struct BranchFlowNode {
predecessor: FlowNodeId,
}
/// A join point where control flow paths come together
#[derive(Debug)]
pub(crate) struct PhiFlowNode {
first_predecessor: FlowNodeId,
second_predecessor: FlowNodeId,
}
/// A branch test which may apply constraints to a symbol's type
#[derive(Debug)]
pub(crate) struct ConstraintFlowNode {
predecessor: FlowNodeId,
test_expression: ExpressionId,
}
#[derive(Debug)]
pub struct FlowGraph {
flow_nodes_by_id: IndexVec<FlowNodeId, FlowNode>,
expression_map: IndexVec<ExpressionId, FlowNodeId>,
}
impl FlowGraph {
pub fn start() -> FlowNodeId {
FlowNodeId::from_usize(0)
}
pub fn for_expr(&self, expr: ExpressionId) -> FlowNodeId {
self.expression_map[expr]
}
}
#[derive(Debug)]
pub(crate) struct FlowGraphBuilder {
flow_graph: FlowGraph,
}
impl FlowGraphBuilder {
pub(crate) fn new() -> Self {
let mut graph = FlowGraph {
flow_nodes_by_id: IndexVec::default(),
expression_map: IndexVec::default(),
};
graph.flow_nodes_by_id.push(FlowNode::Start);
Self { flow_graph: graph }
}
pub(crate) fn add(&mut self, node: FlowNode) -> FlowNodeId {
self.flow_graph.flow_nodes_by_id.push(node)
}
pub(crate) fn add_definition(
&mut self,
symbol_id: SymbolId,
definition: Definition,
predecessor: FlowNodeId,
) -> FlowNodeId {
self.add(FlowNode::Definition(DefinitionFlowNode {
symbol_id,
definition,
predecessor,
}))
}
pub(crate) fn add_branch(&mut self, predecessor: FlowNodeId) -> FlowNodeId {
self.add(FlowNode::Branch(BranchFlowNode { predecessor }))
}
pub(crate) fn add_phi(
&mut self,
first_predecessor: FlowNodeId,
second_predecessor: FlowNodeId,
) -> FlowNodeId {
self.add(FlowNode::Phi(PhiFlowNode {
first_predecessor,
second_predecessor,
}))
}
pub(crate) fn add_constraint(
&mut self,
predecessor: FlowNodeId,
test_expression: ExpressionId,
) -> FlowNodeId {
self.add(FlowNode::Constraint(ConstraintFlowNode {
predecessor,
test_expression,
}))
}
pub(super) fn record_expr(&mut self, node_id: FlowNodeId) -> ExpressionId {
self.flow_graph.expression_map.push(node_id)
}
pub(super) fn finish(mut self) -> FlowGraph {
self.flow_graph.flow_nodes_by_id.shrink_to_fit();
self.flow_graph.expression_map.shrink_to_fit();
self.flow_graph
}
}
/// A definition, and the set of constraints between a use and the definition
#[derive(Debug, Clone)]
pub struct ConstrainedDefinition {
pub definition: Definition,
pub constraints: Vec<ExpressionId>,
}
/// A flow node and the constraints we passed through to reach it
#[derive(Debug)]
struct FlowState {
node_id: FlowNodeId,
constraints_range: Range<usize>,
}
#[derive(Debug)]
pub struct ReachableDefinitionsIterator<'a> {
flow_graph: &'a FlowGraph,
symbol_id: SymbolId,
pending: Vec<FlowState>,
constraints: Vec<ExpressionId>,
}
impl<'a> ReachableDefinitionsIterator<'a> {
pub fn new(flow_graph: &'a FlowGraph, symbol_id: SymbolId, start_node_id: FlowNodeId) -> Self {
Self {
flow_graph,
symbol_id,
pending: vec![FlowState {
node_id: start_node_id,
constraints_range: 0..0,
}],
constraints: vec![],
}
}
}
impl<'a> Iterator for ReachableDefinitionsIterator<'a> {
type Item = ConstrainedDefinition;
fn next(&mut self) -> Option<Self::Item> {
let FlowState {
mut node_id,
mut constraints_range,
} = self.pending.pop()?;
self.constraints.truncate(constraints_range.end + 1);
loop {
match &self.flow_graph.flow_nodes_by_id[node_id] {
FlowNode::Start => {
// constraints on unbound are irrelevant
return Some(ConstrainedDefinition {
definition: Definition::Unbound,
constraints: vec![],
});
}
FlowNode::Definition(def_node) => {
if def_node.symbol_id == self.symbol_id {
return Some(ConstrainedDefinition {
definition: def_node.definition.clone(),
constraints: self.constraints[constraints_range].to_vec(),
});
}
node_id = def_node.predecessor;
}
FlowNode::Branch(branch_node) => {
node_id = branch_node.predecessor;
}
FlowNode::Phi(phi_node) => {
self.pending.push(FlowState {
node_id: phi_node.first_predecessor,
constraints_range: constraints_range.clone(),
});
node_id = phi_node.second_predecessor;
}
FlowNode::Constraint(constraint_node) => {
node_id = constraint_node.predecessor;
self.constraints.push(constraint_node.test_expression);
constraints_range.end += 1;
}
}
}
}
}
impl<'a> FusedIterator for ReachableDefinitionsIterator<'a> {}
impl std::fmt::Display for FlowGraph {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "flowchart TD")?;
for (id, node) in self.flow_nodes_by_id.iter_enumerated() {
write!(f, " id{}", id.as_u32())?;
match node {
FlowNode::Start => writeln!(f, r"[\Start/]")?,
FlowNode::Definition(def_node) => {
writeln!(f, r"(Define symbol {})", def_node.symbol_id.as_u32())?;
writeln!(
f,
r" id{}-->id{}",
def_node.predecessor.as_u32(),
id.as_u32()
)?;
}
FlowNode::Branch(branch_node) => {
writeln!(f, r"{{Branch}}")?;
writeln!(
f,
r" id{}-->id{}",
branch_node.predecessor.as_u32(),
id.as_u32()
)?;
}
FlowNode::Phi(phi_node) => {
writeln!(f, r"((Phi))")?;
writeln!(
f,
r" id{}-->id{}",
phi_node.second_predecessor.as_u32(),
id.as_u32()
)?;
writeln!(
f,
r" id{}-->id{}",
phi_node.first_predecessor.as_u32(),
id.as_u32()
)?;
}
FlowNode::Constraint(constraint_node) => {
writeln!(f, r"((Constraint))")?;
writeln!(
f,
r" id{}-->id{}",
constraint_node.predecessor.as_u32(),
id.as_u32()
)?;
}
}
}
Ok(())
}
}

View File

@@ -1,560 +0,0 @@
#![allow(dead_code)]
use std::hash::{Hash, Hasher};
use std::iter::{Copied, DoubleEndedIterator, FusedIterator};
use std::num::NonZeroU32;
use bitflags::bitflags;
use hashbrown::hash_map::{Keys, RawEntryMut};
use rustc_hash::{FxHashMap, FxHasher};
use ruff_index::{newtype_index, IndexVec};
use crate::ast_ids::NodeKey;
use crate::module::ModuleName;
use crate::semantic::{Definition, ExpressionId};
use crate::Name;
type Map<K, V> = hashbrown::HashMap<K, V, ()>;
#[newtype_index]
pub struct ScopeId;
impl ScopeId {
pub fn scope(self, table: &SymbolTable) -> &Scope {
&table.scopes_by_id[self]
}
}
#[newtype_index]
pub struct SymbolId;
impl SymbolId {
pub fn symbol(self, table: &SymbolTable) -> &Symbol {
&table.symbols_by_id[self]
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ScopeKind {
Module,
Annotation,
Class,
Function,
}
#[derive(Debug)]
pub struct Scope {
name: Name,
kind: ScopeKind,
parent: Option<ScopeId>,
children: Vec<ScopeId>,
/// the definition (e.g. class or function) that created this scope
definition: Option<Definition>,
/// the symbol (e.g. class or function) that owns this scope
defining_symbol: Option<SymbolId>,
/// symbol IDs, hashed by symbol name
symbols_by_name: Map<SymbolId, ()>,
}
impl Scope {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn kind(&self) -> ScopeKind {
self.kind
}
pub fn definition(&self) -> Option<Definition> {
self.definition.clone()
}
pub fn defining_symbol(&self) -> Option<SymbolId> {
self.defining_symbol
}
}
#[derive(Debug)]
pub(crate) enum Kind {
FreeVar,
CellVar,
CellVarAssigned,
ExplicitGlobal,
ImplicitGlobal,
}
bitflags! {
#[derive(Copy,Clone,Debug)]
pub struct SymbolFlags: u8 {
const IS_USED = 1 << 0;
const IS_DEFINED = 1 << 1;
/// TODO: This flag is not yet set by anything
const MARKED_GLOBAL = 1 << 2;
/// TODO: This flag is not yet set by anything
const MARKED_NONLOCAL = 1 << 3;
}
}
#[derive(Debug)]
pub struct Symbol {
name: Name,
flags: SymbolFlags,
scope_id: ScopeId,
// kind: Kind,
}
impl Symbol {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn scope_id(&self) -> ScopeId {
self.scope_id
}
/// Is the symbol used in its containing scope?
pub fn is_used(&self) -> bool {
self.flags.contains(SymbolFlags::IS_USED)
}
/// Is the symbol defined in its containing scope?
pub fn is_defined(&self) -> bool {
self.flags.contains(SymbolFlags::IS_DEFINED)
}
// TODO: implement Symbol.kind 2-pass analysis to categorize as: free-var, cell-var,
// explicit-global, implicit-global and implement Symbol.kind by modifying the preorder
// traversal code
}
#[derive(Debug, Clone)]
pub enum Dependency {
Module(ModuleName),
Relative {
level: NonZeroU32,
module: Option<ModuleName>,
},
}
/// Table of all symbols in all scopes for a module.
#[derive(Debug)]
pub struct SymbolTable {
scopes_by_id: IndexVec<ScopeId, Scope>,
symbols_by_id: IndexVec<SymbolId, Symbol>,
/// the definitions for each symbol
defs: FxHashMap<SymbolId, Vec<Definition>>,
/// map of AST node (e.g. class/function def) to sub-scope it creates
scopes_by_node: FxHashMap<NodeKey, ScopeId>,
/// Maps expressions to their enclosing scope.
expression_scopes: IndexVec<ExpressionId, ScopeId>,
/// dependencies of this module
dependencies: Vec<Dependency>,
}
impl SymbolTable {
pub fn dependencies(&self) -> &[Dependency] {
&self.dependencies
}
pub const fn root_scope_id() -> ScopeId {
ScopeId::from_usize(0)
}
pub fn root_scope(&self) -> &Scope {
&self.scopes_by_id[SymbolTable::root_scope_id()]
}
pub fn symbol_ids_for_scope(&self, scope_id: ScopeId) -> Copied<Keys<SymbolId, ()>> {
self.scopes_by_id[scope_id].symbols_by_name.keys().copied()
}
pub fn symbols_for_scope(
&self,
scope_id: ScopeId,
) -> SymbolIterator<Copied<Keys<SymbolId, ()>>> {
SymbolIterator {
table: self,
ids: self.symbol_ids_for_scope(scope_id),
}
}
pub fn root_symbol_ids(&self) -> Copied<Keys<SymbolId, ()>> {
self.symbol_ids_for_scope(SymbolTable::root_scope_id())
}
pub fn root_symbols(&self) -> SymbolIterator<Copied<Keys<SymbolId, ()>>> {
self.symbols_for_scope(SymbolTable::root_scope_id())
}
pub fn child_scope_ids_of(&self, scope_id: ScopeId) -> &[ScopeId] {
&self.scopes_by_id[scope_id].children
}
pub fn child_scopes_of(&self, scope_id: ScopeId) -> ScopeIterator<&[ScopeId]> {
ScopeIterator {
table: self,
ids: self.child_scope_ids_of(scope_id),
}
}
pub fn root_child_scope_ids(&self) -> &[ScopeId] {
self.child_scope_ids_of(SymbolTable::root_scope_id())
}
pub fn root_child_scopes(&self) -> ScopeIterator<&[ScopeId]> {
self.child_scopes_of(SymbolTable::root_scope_id())
}
pub fn symbol_id_by_name(&self, scope_id: ScopeId, name: &str) -> Option<SymbolId> {
let scope = &self.scopes_by_id[scope_id];
let hash = SymbolTable::hash_name(name);
let name = Name::new(name);
Some(
*scope
.symbols_by_name
.raw_entry()
.from_hash(hash, |symid| self.symbols_by_id[*symid].name == name)?
.0,
)
}
pub fn symbol_by_name(&self, scope_id: ScopeId, name: &str) -> Option<&Symbol> {
Some(&self.symbols_by_id[self.symbol_id_by_name(scope_id, name)?])
}
pub fn root_symbol_id_by_name(&self, name: &str) -> Option<SymbolId> {
self.symbol_id_by_name(SymbolTable::root_scope_id(), name)
}
pub fn root_symbol_by_name(&self, name: &str) -> Option<&Symbol> {
self.symbol_by_name(SymbolTable::root_scope_id(), name)
}
pub fn scope_id_of_symbol(&self, symbol_id: SymbolId) -> ScopeId {
self.symbols_by_id[symbol_id].scope_id
}
pub fn scope_of_symbol(&self, symbol_id: SymbolId) -> &Scope {
&self.scopes_by_id[self.scope_id_of_symbol(symbol_id)]
}
pub fn scope_id_of_expression(&self, expression: ExpressionId) -> ScopeId {
self.expression_scopes[expression]
}
pub fn scope_of_expression(&self, expr_id: ExpressionId) -> &Scope {
&self.scopes_by_id[self.scope_id_of_expression(expr_id)]
}
pub fn parent_scopes(
&self,
scope_id: ScopeId,
) -> ScopeIterator<impl Iterator<Item = ScopeId> + '_> {
ScopeIterator {
table: self,
ids: std::iter::successors(Some(scope_id), |scope| self.scopes_by_id[*scope].parent),
}
}
pub fn parent_scope(&self, scope_id: ScopeId) -> Option<ScopeId> {
self.scopes_by_id[scope_id].parent
}
pub fn scope_id_for_node(&self, node_key: &NodeKey) -> ScopeId {
self.scopes_by_node[node_key]
}
pub fn definitions(&self, symbol_id: SymbolId) -> &[Definition] {
self.defs
.get(&symbol_id)
.map(std::vec::Vec::as_slice)
.unwrap_or_default()
}
pub fn all_definitions(&self) -> impl Iterator<Item = (SymbolId, &Definition)> + '_ {
self.defs
.iter()
.flat_map(|(sym_id, defs)| defs.iter().map(move |def| (*sym_id, def)))
}
fn hash_name(name: &str) -> u64 {
let mut hasher = FxHasher::default();
name.hash(&mut hasher);
hasher.finish()
}
}
pub struct SymbolIterator<'a, I> {
table: &'a SymbolTable,
ids: I,
}
impl<'a, I> Iterator for SymbolIterator<'a, I>
where
I: Iterator<Item = SymbolId>,
{
type Item = &'a Symbol;
fn next(&mut self) -> Option<Self::Item> {
let id = self.ids.next()?;
Some(&self.table.symbols_by_id[id])
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.ids.size_hint()
}
}
impl<'a, I> FusedIterator for SymbolIterator<'a, I> where
I: Iterator<Item = SymbolId> + FusedIterator
{
}
impl<'a, I> DoubleEndedIterator for SymbolIterator<'a, I>
where
I: Iterator<Item = SymbolId> + DoubleEndedIterator,
{
fn next_back(&mut self) -> Option<Self::Item> {
let id = self.ids.next_back()?;
Some(&self.table.symbols_by_id[id])
}
}
// TODO maybe get rid of this and just do all data access via methods on ScopeId?
pub struct ScopeIterator<'a, I> {
table: &'a SymbolTable,
ids: I,
}
/// iterate (`ScopeId`, `Scope`) pairs for given `ScopeId` iterator
impl<'a, I> Iterator for ScopeIterator<'a, I>
where
I: Iterator<Item = ScopeId>,
{
type Item = (ScopeId, &'a Scope);
fn next(&mut self) -> Option<Self::Item> {
let id = self.ids.next()?;
Some((id, &self.table.scopes_by_id[id]))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.ids.size_hint()
}
}
impl<'a, I> FusedIterator for ScopeIterator<'a, I> where I: Iterator<Item = ScopeId> + FusedIterator {}
impl<'a, I> DoubleEndedIterator for ScopeIterator<'a, I>
where
I: Iterator<Item = ScopeId> + DoubleEndedIterator,
{
fn next_back(&mut self) -> Option<Self::Item> {
let id = self.ids.next_back()?;
Some((id, &self.table.scopes_by_id[id]))
}
}
#[derive(Debug)]
pub(super) struct SymbolTableBuilder {
symbol_table: SymbolTable,
}
impl SymbolTableBuilder {
pub(super) fn new() -> Self {
let mut table = SymbolTable {
scopes_by_id: IndexVec::new(),
symbols_by_id: IndexVec::new(),
defs: FxHashMap::default(),
scopes_by_node: FxHashMap::default(),
expression_scopes: IndexVec::new(),
dependencies: Vec::new(),
};
table.scopes_by_id.push(Scope {
name: Name::new("<module>"),
kind: ScopeKind::Module,
parent: None,
children: Vec::new(),
definition: None,
defining_symbol: None,
symbols_by_name: Map::default(),
});
Self {
symbol_table: table,
}
}
pub(super) fn finish(self) -> SymbolTable {
let mut symbol_table = self.symbol_table;
symbol_table.scopes_by_id.shrink_to_fit();
symbol_table.symbols_by_id.shrink_to_fit();
symbol_table.defs.shrink_to_fit();
symbol_table.scopes_by_node.shrink_to_fit();
symbol_table.expression_scopes.shrink_to_fit();
symbol_table.dependencies.shrink_to_fit();
symbol_table
}
pub(super) fn add_or_update_symbol(
&mut self,
scope_id: ScopeId,
name: &str,
flags: SymbolFlags,
) -> SymbolId {
let hash = SymbolTable::hash_name(name);
let scope = &mut self.symbol_table.scopes_by_id[scope_id];
let name = Name::new(name);
let entry = scope
.symbols_by_name
.raw_entry_mut()
.from_hash(hash, |existing| {
self.symbol_table.symbols_by_id[*existing].name == name
});
match entry {
RawEntryMut::Occupied(entry) => {
if let Some(symbol) = self.symbol_table.symbols_by_id.get_mut(*entry.key()) {
symbol.flags.insert(flags);
};
*entry.key()
}
RawEntryMut::Vacant(entry) => {
let id = self.symbol_table.symbols_by_id.push(Symbol {
name,
flags,
scope_id,
});
entry.insert_with_hasher(hash, id, (), |symid| {
SymbolTable::hash_name(&self.symbol_table.symbols_by_id[*symid].name)
});
id
}
}
}
pub(super) fn add_definition(&mut self, symbol_id: SymbolId, definition: Definition) {
self.symbol_table
.defs
.entry(symbol_id)
.or_default()
.push(definition);
}
pub(super) fn add_child_scope(
&mut self,
parent_scope_id: ScopeId,
name: &str,
kind: ScopeKind,
definition: Option<Definition>,
defining_symbol: Option<SymbolId>,
) -> ScopeId {
let new_scope_id = self.symbol_table.scopes_by_id.push(Scope {
name: Name::new(name),
kind,
parent: Some(parent_scope_id),
children: Vec::new(),
definition,
defining_symbol,
symbols_by_name: Map::default(),
});
let parent_scope = &mut self.symbol_table.scopes_by_id[parent_scope_id];
parent_scope.children.push(new_scope_id);
new_scope_id
}
pub(super) fn record_scope_for_node(&mut self, node_key: NodeKey, scope_id: ScopeId) {
self.symbol_table.scopes_by_node.insert(node_key, scope_id);
}
pub(super) fn add_dependency(&mut self, dependency: Dependency) {
self.symbol_table.dependencies.push(dependency);
}
/// Records the scope for the current expression
pub(super) fn record_expression(&mut self, scope: ScopeId) -> ExpressionId {
self.symbol_table.expression_scopes.push(scope)
}
}
#[cfg(test)]
mod tests {
use super::{ScopeKind, SymbolFlags, SymbolTable, SymbolTableBuilder};
#[test]
fn insert_same_name_symbol_twice() {
let mut builder = SymbolTableBuilder::new();
let root_scope_id = SymbolTable::root_scope_id();
let symbol_id_1 =
builder.add_or_update_symbol(root_scope_id, "foo", SymbolFlags::IS_DEFINED);
let symbol_id_2 = builder.add_or_update_symbol(root_scope_id, "foo", SymbolFlags::IS_USED);
let table = builder.finish();
assert_eq!(symbol_id_1, symbol_id_2);
assert!(symbol_id_1.symbol(&table).is_used(), "flags must merge");
assert!(symbol_id_1.symbol(&table).is_defined(), "flags must merge");
}
#[test]
fn insert_different_named_symbols() {
let mut builder = SymbolTableBuilder::new();
let root_scope_id = SymbolTable::root_scope_id();
let symbol_id_1 = builder.add_or_update_symbol(root_scope_id, "foo", SymbolFlags::empty());
let symbol_id_2 = builder.add_or_update_symbol(root_scope_id, "bar", SymbolFlags::empty());
assert_ne!(symbol_id_1, symbol_id_2);
}
#[test]
fn add_child_scope_with_symbol() {
let mut builder = SymbolTableBuilder::new();
let root_scope_id = SymbolTable::root_scope_id();
let foo_symbol_top =
builder.add_or_update_symbol(root_scope_id, "foo", SymbolFlags::empty());
let c_scope = builder.add_child_scope(root_scope_id, "C", ScopeKind::Class, None, None);
let foo_symbol_inner = builder.add_or_update_symbol(c_scope, "foo", SymbolFlags::empty());
assert_ne!(foo_symbol_top, foo_symbol_inner);
}
#[test]
fn scope_from_id() {
let table = SymbolTableBuilder::new().finish();
let root_scope_id = SymbolTable::root_scope_id();
let scope = root_scope_id.scope(&table);
assert_eq!(scope.name.as_str(), "<module>");
assert_eq!(scope.kind, ScopeKind::Module);
}
#[test]
fn symbol_from_id() {
let mut builder = SymbolTableBuilder::new();
let root_scope_id = SymbolTable::root_scope_id();
let foo_symbol_id =
builder.add_or_update_symbol(root_scope_id, "foo", SymbolFlags::empty());
let table = builder.finish();
let symbol = foo_symbol_id.symbol(&table);
assert_eq!(symbol.name(), "foo");
}
#[test]
fn bigger_symbol_table() {
let mut builder = SymbolTableBuilder::new();
let root_scope_id = SymbolTable::root_scope_id();
let foo_symbol_id =
builder.add_or_update_symbol(root_scope_id, "foo", SymbolFlags::empty());
builder.add_or_update_symbol(root_scope_id, "bar", SymbolFlags::empty());
builder.add_or_update_symbol(root_scope_id, "baz", SymbolFlags::empty());
builder.add_or_update_symbol(root_scope_id, "qux", SymbolFlags::empty());
let table = builder.finish();
let foo_symbol_id_2 = table
.root_symbol_id_by_name("foo")
.expect("foo symbol to be found");
assert_eq!(foo_symbol_id_2, foo_symbol_id);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,762 +0,0 @@
#![allow(dead_code)]
use ruff_python_ast as ast;
use ruff_python_ast::AstNode;
use std::fmt::Debug;
use crate::db::{QueryResult, SemanticDb, SemanticJar};
use crate::module::{resolve_module, ModuleName};
use crate::parse::parse;
use crate::semantic::types::{ModuleTypeId, Type};
use crate::semantic::{
resolve_global_symbol, semantic_index, ConstrainedDefinition, Definition, GlobalSymbolId,
ImportDefinition, ImportFromDefinition,
};
use crate::{FileId, Name};
// FIXME: Figure out proper dead-lock free synchronisation now that this takes `&db` instead of `&mut db`.
/// Resolve the public-facing type for a symbol (the type seen by other scopes: other modules, or
/// nested functions). Because calls to nested functions and imports can occur anywhere in control
/// flow, this type must be conservative and consider all definitions of the symbol that could
/// possibly be seen by another scope. Currently we take the most conservative approach, which is
/// the union of all definitions. We may be able to narrow this in future to eliminate definitions
/// which can't possibly (or at least likely) be seen by any other scope, so that e.g. we could
/// infer `Literal["1"]` instead of `Literal[1] | Literal["1"]` for `x` in `x = x; x = str(x);`.
#[tracing::instrument(level = "trace", skip(db))]
pub fn infer_symbol_public_type(db: &dyn SemanticDb, symbol: GlobalSymbolId) -> QueryResult<Type> {
let index = semantic_index(db, symbol.file_id)?;
let defs = index.symbol_table().definitions(symbol.symbol_id).to_vec();
let jar: &SemanticJar = db.jar()?;
if let Some(ty) = jar.type_store.get_cached_symbol_public_type(symbol) {
return Ok(ty);
}
let ty = infer_type_from_definitions(db, symbol, defs.iter().cloned())?;
jar.type_store.cache_symbol_public_type(symbol, ty);
// TODO record dependencies
Ok(ty)
}
/// Infer type of a symbol as union of the given `Definitions`.
fn infer_type_from_definitions<T>(
db: &dyn SemanticDb,
symbol: GlobalSymbolId,
definitions: T,
) -> QueryResult<Type>
where
T: Debug + IntoIterator<Item = Definition>,
{
infer_type_from_constrained_definitions(
db,
symbol,
definitions
.into_iter()
.map(|definition| ConstrainedDefinition {
definition,
constraints: vec![],
}),
)
}
/// Infer type of a symbol as union of the given `ConstrainedDefinitions`.
fn infer_type_from_constrained_definitions<T>(
db: &dyn SemanticDb,
symbol: GlobalSymbolId,
constrained_definitions: T,
) -> QueryResult<Type>
where
T: IntoIterator<Item = ConstrainedDefinition>,
{
let jar: &SemanticJar = db.jar()?;
let mut tys = constrained_definitions
.into_iter()
.map(|def| infer_constrained_definition_type(db, symbol, def.clone()))
.peekable();
if let Some(first) = tys.next() {
if tys.peek().is_some() {
Ok(jar.type_store.add_union(
symbol.file_id,
&Iterator::chain(std::iter::once(first), tys).collect::<QueryResult<Vec<_>>>()?,
))
} else {
first
}
} else {
Ok(Type::Unknown)
}
}
/// Infer type for a ConstrainedDefinition (intersection of the definition type and the
/// constraints)
#[tracing::instrument(level = "trace", skip(db))]
pub fn infer_constrained_definition_type(
db: &dyn SemanticDb,
symbol: GlobalSymbolId,
constrained_definition: ConstrainedDefinition,
) -> QueryResult<Type> {
let ConstrainedDefinition {
definition,
constraints,
} = constrained_definition;
let index = semantic_index(db, symbol.file_id)?;
let parsed = parse(db.upcast(), symbol.file_id)?;
let mut intersected_types = vec![infer_definition_type(db, symbol, definition)?];
for constraint in constraints {
if let Some(constraint_type) = infer_constraint_type(
db,
symbol,
index.resolve_expression_id(parsed.syntax(), constraint),
)? {
intersected_types.push(constraint_type);
}
}
let jar: &SemanticJar = db.jar()?;
Ok(jar
.type_store
.add_intersection(symbol.file_id, &intersected_types, &[]))
}
/// Infer a type for a Definition
#[tracing::instrument(level = "trace", skip(db))]
pub fn infer_definition_type(
db: &dyn SemanticDb,
symbol: GlobalSymbolId,
definition: Definition,
) -> QueryResult<Type> {
let jar: &SemanticJar = db.jar()?;
let type_store = &jar.type_store;
let file_id = symbol.file_id;
match definition {
Definition::Unbound => Ok(Type::Unbound),
Definition::Import(ImportDefinition {
module: module_name,
}) => {
if let Some(module) = resolve_module(db, module_name.clone())? {
Ok(Type::Module(ModuleTypeId { module, file_id }))
} else {
Ok(Type::Unknown)
}
}
Definition::ImportFrom(ImportFromDefinition {
module,
name,
level,
}) => {
// TODO relative imports
assert!(matches!(level, 0));
let module_name = ModuleName::new(module.as_ref().expect("TODO relative imports"));
let Some(module) = resolve_module(db, module_name.clone())? else {
return Ok(Type::Unknown);
};
if let Some(remote_symbol) = resolve_global_symbol(db, module, &name)? {
infer_symbol_public_type(db, remote_symbol)
} else {
Ok(Type::Unknown)
}
}
Definition::ClassDef(node_key) => {
if let Some(ty) = type_store.get_cached_node_type(file_id, node_key.erased()) {
Ok(ty)
} else {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.syntax();
let index = semantic_index(db, file_id)?;
let node = node_key.resolve_unwrap(ast.as_any_node_ref());
let mut bases = Vec::with_capacity(node.bases().len());
for base in node.bases() {
bases.push(infer_expr_type(db, file_id, base)?);
}
let scope_id = index.symbol_table().scope_id_for_node(node_key.erased());
let ty = type_store.add_class(file_id, &node.name.id, scope_id, bases);
type_store.cache_node_type(file_id, *node_key.erased(), ty);
Ok(ty)
}
}
Definition::FunctionDef(node_key) => {
if let Some(ty) = type_store.get_cached_node_type(file_id, node_key.erased()) {
Ok(ty)
} else {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.syntax();
let index = semantic_index(db, file_id)?;
let node = node_key
.resolve(ast.as_any_node_ref())
.expect("node key should resolve");
let decorator_tys = node
.decorator_list
.iter()
.map(|decorator| infer_expr_type(db, file_id, &decorator.expression))
.collect::<QueryResult<_>>()?;
let scope_id = index.symbol_table().scope_id_for_node(node_key.erased());
let ty = type_store.add_function(
file_id,
&node.name.id,
symbol.symbol_id,
scope_id,
decorator_tys,
);
type_store.cache_node_type(file_id, *node_key.erased(), ty);
Ok(ty)
}
}
Definition::Assignment(node_key) => {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.syntax();
let node = node_key.resolve_unwrap(ast.as_any_node_ref());
// TODO handle unpacking assignment
infer_expr_type(db, file_id, &node.value)
}
Definition::AnnotatedAssignment(node_key) => {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.syntax();
let node = node_key.resolve_unwrap(ast.as_any_node_ref());
// TODO actually look at the annotation
let Some(value) = &node.value else {
return Ok(Type::Unknown);
};
// TODO handle unpacking assignment
infer_expr_type(db, file_id, value)
}
Definition::NamedExpr(node_key) => {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.syntax();
let node = node_key.resolve_unwrap(ast.as_any_node_ref());
infer_expr_type(db, file_id, &node.value)
}
}
}
/// Return the type that the given constraint (an expression from a control-flow test) requires the
/// given symbol to have. For example, returns the Type "~None" as the constraint type if given the
/// symbol ID for x and the expression ID for `x is not None`. Returns (Rust) None if the given
/// expression applies no constraints on the given symbol.
#[tracing::instrument(level = "trace", skip(db))]
fn infer_constraint_type(
db: &dyn SemanticDb,
symbol_id: GlobalSymbolId,
// TODO this should preferably take an &ast::Expr instead of AnyNodeRef
expression: ast::AnyNodeRef,
) -> QueryResult<Option<Type>> {
let file_id = symbol_id.file_id;
let index = semantic_index(db, file_id)?;
let jar: &SemanticJar = db.jar()?;
let symbol_name = symbol_id.symbol_id.symbol(&index.symbol_table).name();
// TODO narrowing attributes
// TODO narrowing dict keys
// TODO isinstance, ==/!=, type(...), literals, bools...
match expression {
ast::AnyNodeRef::ExprCompare(ast::ExprCompare {
left,
ops,
comparators,
..
}) => {
// TODO chained comparisons
match left.as_ref() {
ast::Expr::Name(ast::ExprName { id, .. }) if id == symbol_name => match ops[0] {
ast::CmpOp::Is | ast::CmpOp::IsNot => {
Ok(match infer_expr_type(db, file_id, &comparators[0])? {
Type::None => Some(Type::None),
_ => None,
}
.map(|ty| {
if matches!(ops[0], ast::CmpOp::IsNot) {
jar.type_store.add_intersection(file_id, &[], &[ty])
} else {
ty
}
}))
}
_ => Ok(None),
},
_ => Ok(None),
}
}
_ => Ok(None),
}
}
/// Infer type of the given expression.
fn infer_expr_type(db: &dyn SemanticDb, file_id: FileId, expr: &ast::Expr) -> QueryResult<Type> {
// TODO cache the resolution of the type on the node
let index = semantic_index(db, file_id)?;
match expr {
ast::Expr::NoneLiteral(_) => Ok(Type::None),
ast::Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
match value {
ast::Number::Int(n) => {
// TODO support big int literals
Ok(n.as_i64().map(Type::IntLiteral).unwrap_or(Type::Unknown))
}
// TODO builtins.float or builtins.complex
_ => Ok(Type::Unknown),
}
}
ast::Expr::Name(name) => {
// TODO look up in the correct scope, don't assume global
if let Some(symbol_id) = index.symbol_table().root_symbol_id_by_name(&name.id) {
infer_type_from_constrained_definitions(
db,
GlobalSymbolId { file_id, symbol_id },
index.reachable_definitions(symbol_id, expr),
)
} else {
Ok(Type::Unknown)
}
}
ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => {
let value_type = infer_expr_type(db, file_id, value)?;
let attr_name = &Name::new(&attr.id);
value_type
.get_member(db, attr_name)
.map(|ty| ty.unwrap_or(Type::Unknown))
}
ast::Expr::BinOp(ast::ExprBinOp {
left, op, right, ..
}) => {
let left_ty = infer_expr_type(db, file_id, left)?;
let right_ty = infer_expr_type(db, file_id, right)?;
// TODO add reverse bin op support if right <: left
left_ty.resolve_bin_op(db, *op, right_ty)
}
ast::Expr::Named(ast::ExprNamed { value, .. }) => infer_expr_type(db, file_id, value),
ast::Expr::If(ast::ExprIf { body, orelse, .. }) => {
// TODO detect statically known truthy or falsy test
let body_ty = infer_expr_type(db, file_id, body)?;
let else_ty = infer_expr_type(db, file_id, orelse)?;
let jar: &SemanticJar = db.jar()?;
Ok(jar.type_store.add_union(file_id, &[body_ty, else_ty]))
}
_ => todo!("expression type resolution for {:?}", expr),
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use crate::db::tests::TestDb;
use crate::db::{HasJar, SemanticJar};
use crate::module::{
resolve_module, set_module_search_paths, ModuleName, ModuleResolutionInputs,
};
use crate::semantic::{infer_symbol_public_type, resolve_global_symbol, Type};
use crate::Name;
// TODO with virtual filesystem we shouldn't have to write files to disk for these
// tests
struct TestCase {
temp_dir: tempfile::TempDir,
db: TestDb,
src: PathBuf,
}
fn create_test() -> std::io::Result<TestCase> {
let temp_dir = tempfile::tempdir()?;
let src = temp_dir.path().join("src");
std::fs::create_dir(&src)?;
let src = src.canonicalize()?;
let search_paths = ModuleResolutionInputs {
extra_paths: vec![],
workspace_root: src.clone(),
site_packages: None,
custom_typeshed: None,
};
let mut db = TestDb::default();
set_module_search_paths(&mut db, search_paths);
Ok(TestCase { temp_dir, db, src })
}
fn write_to_path(case: &TestCase, relative_path: &str, contents: &str) -> anyhow::Result<()> {
let path = case.src.join(relative_path);
std::fs::write(path, contents)?;
Ok(())
}
fn get_public_type(
case: &TestCase,
module_name: &str,
variable_name: &str,
) -> anyhow::Result<Type> {
let db = &case.db;
let module = resolve_module(db, ModuleName::new(module_name))?.expect("Module to exist");
let symbol = resolve_global_symbol(db, module, variable_name)?.expect("symbol to exist");
Ok(infer_symbol_public_type(db, symbol)?)
}
fn assert_public_type(
case: &TestCase,
module_name: &str,
variable_name: &str,
type_name: &str,
) -> anyhow::Result<()> {
let ty = get_public_type(case, module_name, variable_name)?;
let jar = HasJar::<SemanticJar>::jar(&case.db)?;
assert_eq!(format!("{}", ty.display(&jar.type_store)), type_name);
Ok(())
}
#[test]
fn follow_import_to_class() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(&case, "a.py", "from b import C as D; E = D")?;
write_to_path(&case, "b.py", "class C: pass")?;
assert_public_type(&case, "a", "E", "Literal[C]")
}
#[test]
fn resolve_base_class_by_name() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"mod.py",
"
class Base: pass
class Sub(Base): pass
",
)?;
let ty = get_public_type(&case, "mod", "Sub")?;
let Type::Class(class_id) = ty else {
panic!("Sub is not a Class")
};
let jar = HasJar::<SemanticJar>::jar(&case.db)?;
let base_names: Vec<_> = jar
.type_store
.get_class(class_id)
.bases()
.iter()
.map(|base_ty| format!("{}", base_ty.display(&jar.type_store)))
.collect();
assert_eq!(base_names, vec!["Literal[Base]"]);
Ok(())
}
#[test]
fn resolve_method() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"mod.py",
"
class C:
def f(self): pass
",
)?;
let ty = get_public_type(&case, "mod", "C")?;
let Type::Class(class_id) = ty else {
panic!("C is not a Class");
};
let member_ty = class_id
.get_own_class_member(&case.db, &Name::new("f"))
.expect("C.f to resolve");
let Some(Type::Function(func_id)) = member_ty else {
panic!("C.f is not a Function");
};
let jar = HasJar::<SemanticJar>::jar(&case.db)?;
let function = jar.type_store.get_function(func_id);
assert_eq!(function.name(), "f");
Ok(())
}
#[test]
fn resolve_module_member() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(&case, "a.py", "import b; D = b.C")?;
write_to_path(&case, "b.py", "class C: pass")?;
assert_public_type(&case, "a", "D", "Literal[C]")
}
#[test]
fn resolve_literal() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(&case, "a.py", "x = 1")?;
assert_public_type(&case, "a", "x", "Literal[1]")
}
#[test]
fn resolve_union() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
if flag:
x = 1
else:
x = 2
",
)?;
assert_public_type(&case, "a", "x", "Literal[1, 2]")
}
#[test]
fn resolve_visible_def() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(&case, "a.py", "y = 1; y = 2; x = y")?;
assert_public_type(&case, "a", "x", "Literal[2]")
}
#[test]
fn join_paths() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
y = 1
y = 2
if flag:
y = 3
x = y
",
)?;
assert_public_type(&case, "a", "x", "Literal[2, 3]")
}
#[test]
fn maybe_unbound() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
if flag:
y = 1
x = y
",
)?;
assert_public_type(&case, "a", "x", "Literal[1] | Unbound")
}
#[test]
fn if_elif_else() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
y = 1
y = 2
if flag:
y = 3
elif flag2:
y = 4
else:
r = y
y = 5
s = y
x = y
",
)?;
assert_public_type(&case, "a", "x", "Literal[3, 4, 5]")?;
assert_public_type(&case, "a", "r", "Literal[2]")?;
assert_public_type(&case, "a", "s", "Literal[5]")
}
#[test]
fn if_elif() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
y = 1
y = 2
if flag:
y = 3
elif flag2:
y = 4
x = y
",
)?;
assert_public_type(&case, "a", "x", "Literal[2, 3, 4]")
}
#[test]
fn literal_int_arithmetic() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
a = 2 + 1
b = a - 4
c = a * b
d = c / 3
e = 5 % 3
",
)?;
assert_public_type(&case, "a", "a", "Literal[3]")?;
assert_public_type(&case, "a", "b", "Literal[-1]")?;
assert_public_type(&case, "a", "c", "Literal[-3]")?;
assert_public_type(&case, "a", "d", "Literal[-1]")?;
assert_public_type(&case, "a", "e", "Literal[2]")
}
#[test]
fn walrus() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
x = (y := 1) + 1
",
)?;
assert_public_type(&case, "a", "x", "Literal[2]")?;
assert_public_type(&case, "a", "y", "Literal[1]")
}
#[test]
fn ifexpr() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
x = 1 if flag else 2
",
)?;
assert_public_type(&case, "a", "x", "Literal[1, 2]")
}
#[test]
fn ifexpr_walrus() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
y = z = 0
x = (y := 1) if flag else (z := 2)
a = y
b = z
",
)?;
assert_public_type(&case, "a", "x", "Literal[1, 2]")?;
assert_public_type(&case, "a", "a", "Literal[0, 1]")?;
assert_public_type(&case, "a", "b", "Literal[0, 2]")
}
#[test]
fn ifexpr_walrus_2() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
y = 0
(y := 1) if flag else (y := 2)
a = y
",
)?;
assert_public_type(&case, "a", "a", "Literal[1, 2]")
}
#[test]
fn ifexpr_nested() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
x = 1 if flag else 2 if flag2 else 3
",
)?;
assert_public_type(&case, "a", "x", "Literal[1, 2, 3]")
}
#[test]
fn none() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
x = 1 if flag else None
",
)?;
assert_public_type(&case, "a", "x", "Literal[1] | None")
}
#[test]
fn narrow_none() -> anyhow::Result<()> {
let case = create_test()?;
write_to_path(
&case,
"a.py",
"
x = 1 if flag else None
y = 0
if x is not None:
y = x
z = y
",
)?;
// TODO normalization of unions and intersections: this type is technically correct but
// begging for normalization
assert_public_type(&case, "a", "z", "Literal[0] | Literal[1] | None & ~None")
}
}

View File

@@ -1,105 +0,0 @@
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use ruff_notebook::Notebook;
use ruff_python_ast::PySourceType;
use crate::cache::KeyValueCache;
use crate::db::{QueryResult, SourceDb};
use crate::files::FileId;
#[tracing::instrument(level = "debug", skip(db))]
pub(crate) fn source_text(db: &dyn SourceDb, file_id: FileId) -> QueryResult<Source> {
let jar = db.jar()?;
let sources = &jar.sources;
sources.get(&file_id, |file_id| {
let path = db.file_path(*file_id);
let source_text = std::fs::read_to_string(&path).unwrap_or_else(|err| {
tracing::error!("Failed to read file '{path:?}: {err}'. Falling back to empty text");
String::new()
});
let python_ty = PySourceType::from(&path);
let kind = match python_ty {
PySourceType::Python => {
SourceKind::Python(Arc::from(source_text))
}
PySourceType::Stub => SourceKind::Stub(Arc::from(source_text)),
PySourceType::Ipynb => {
let notebook = Notebook::from_source_code(&source_text).unwrap_or_else(|err| {
// TODO should this be changed to never fail?
// or should we instead add a diagnostic somewhere? But what would we return in this case?
tracing::error!(
"Failed to parse notebook '{path:?}: {err}'. Falling back to an empty notebook"
);
Notebook::from_source_code("").unwrap()
});
SourceKind::IpyNotebook(Arc::new(notebook))
}
};
Ok(Source { kind })
})
}
#[derive(Debug, Clone, PartialEq)]
pub enum SourceKind {
Python(Arc<str>),
Stub(Arc<str>),
IpyNotebook(Arc<Notebook>),
}
impl<'a> From<&'a SourceKind> for PySourceType {
fn from(value: &'a SourceKind) -> Self {
match value {
SourceKind::Python(_) => PySourceType::Python,
SourceKind::Stub(_) => PySourceType::Stub,
SourceKind::IpyNotebook(_) => PySourceType::Ipynb,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Source {
kind: SourceKind,
}
impl Source {
pub fn python<T: Into<Arc<str>>>(source: T) -> Self {
Self {
kind: SourceKind::Python(source.into()),
}
}
pub fn kind(&self) -> &SourceKind {
&self.kind
}
pub fn text(&self) -> &str {
match &self.kind {
SourceKind::Python(text) => text,
SourceKind::Stub(text) => text,
SourceKind::IpyNotebook(notebook) => notebook.source_code(),
}
}
}
#[derive(Debug, Default)]
pub struct SourceStorage(pub(crate) KeyValueCache<FileId, Source>);
impl Deref for SourceStorage {
type Target = KeyValueCache<FileId, Source>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SourceStorage {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@@ -0,0 +1 @@

View File

@@ -1,77 +0,0 @@
use std::path::Path;
use anyhow::Context;
use notify::event::{CreateKind, RemoveKind};
use notify::{recommended_watcher, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use crate::program::{FileChangeKind, FileWatcherChange};
pub struct FileWatcher {
watcher: RecommendedWatcher,
}
pub trait EventHandler: Send + 'static {
fn handle(&self, changes: Vec<FileWatcherChange>);
}
impl<F> EventHandler for F
where
F: Fn(Vec<FileWatcherChange>) + Send + 'static,
{
fn handle(&self, changes: Vec<FileWatcherChange>) {
let f = self;
f(changes);
}
}
impl FileWatcher {
pub fn new<E>(handler: E) -> anyhow::Result<Self>
where
E: EventHandler,
{
Self::from_handler(Box::new(handler))
}
fn from_handler(handler: Box<dyn EventHandler>) -> anyhow::Result<Self> {
let watcher = recommended_watcher(move |changes: notify::Result<Event>| {
match changes {
Ok(event) => {
// TODO verify that this handles all events correctly
let change_kind = match event.kind {
EventKind::Create(CreateKind::File) => FileChangeKind::Created,
EventKind::Modify(_) => FileChangeKind::Modified,
EventKind::Remove(RemoveKind::File) => FileChangeKind::Deleted,
_ => {
return;
}
};
let mut changes = Vec::new();
for path in event.paths {
if path.is_file() {
changes.push(FileWatcherChange::new(path, change_kind));
}
}
if !changes.is_empty() {
handler.handle(changes);
}
}
// TODO proper error handling
Err(err) => {
panic!("Error: {err}");
}
}
})
.context("Failed to create file watcher.")?;
Ok(Self { watcher })
}
pub fn watch_folder(&mut self, path: &Path) -> anyhow::Result<()> {
self.watcher.watch(path, RecursiveMode::Recursive)?;
Ok(())
}
}

View File

@@ -0,0 +1,60 @@
use anyhow::Context;
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
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.
#[test]
fn test_config_override() -> anyhow::Result<()> {
let tempdir = TempDir::new()?;
std::fs::write(
tempdir.path().join("pyproject.toml"),
r#"
[tool.knot.environment]
python-version = "3.11"
"#,
)
.context("Failed to write settings")?;
std::fs::write(
tempdir.path().join("test.py"),
r#"
import sys
# Access `sys.last_exc` that was only added in Python 3.12
print(sys.last_exc)
"#,
)
.context("Failed to write test.py")?;
insta::with_settings!({filters => vec![(&*tempdir_filter(&tempdir), "<temp_dir>/")]}, {
assert_cmd_snapshot!(knot().arg("--project").arg(tempdir.path()), @r"
success: false
exit_code: 1
----- stdout -----
error[lint:unresolved-attribute] <temp_dir>/test.py:5:7 Type `<module 'sys'>` has no attribute `last_exc`
----- stderr -----
");
});
assert_cmd_snapshot!(knot().arg("--project").arg(tempdir.path()).arg("--python-version").arg("3.12"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
");
Ok(())
}
fn knot() -> Command {
Command::new(get_cargo_bin("red_knot"))
}
fn tempdir_filter(tempdir: &TempDir) -> String {
format!(r"{}\\?/?", regex::escape(tempdir.path().to_str().unwrap()))
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,156 +0,0 @@
use ruff_db::Upcast;
use crate::resolver::{
file_to_module,
internal::{ModuleNameIngredient, ModuleResolverSearchPaths},
resolve_module_query,
};
#[salsa::jar(db=Db)]
pub struct Jar(
ModuleNameIngredient<'_>,
ModuleResolverSearchPaths,
resolve_module_query,
file_to_module,
);
pub trait Db: salsa::DbWithJar<Jar> + ruff_db::Db + Upcast<dyn ruff_db::Db> {}
pub(crate) mod tests {
use std::sync;
use salsa::DebugWithDb;
use ruff_db::file_system::{FileSystem, MemoryFileSystem, OsFileSystem};
use ruff_db::vfs::Vfs;
use super::*;
#[salsa::db(Jar, ruff_db::Jar)]
pub(crate) struct TestDb {
storage: salsa::Storage<Self>,
file_system: TestFileSystem,
events: sync::Arc<sync::Mutex<Vec<salsa::Event>>>,
vfs: Vfs,
}
impl TestDb {
#[allow(unused)]
pub(crate) fn new() -> Self {
Self {
storage: salsa::Storage::default(),
file_system: TestFileSystem::Memory(MemoryFileSystem::default()),
events: sync::Arc::default(),
vfs: Vfs::with_stubbed_vendored(),
}
}
/// Returns the memory file system.
///
/// ## Panics
/// If this test db isn't using a memory file system.
#[allow(unused)]
pub(crate) fn memory_file_system(&self) -> &MemoryFileSystem {
if let TestFileSystem::Memory(fs) = &self.file_system {
fs
} else {
panic!("The test db is not using a memory file system");
}
}
/// Uses the real file system instead of the memory file system.
///
/// This useful for testing advanced file system features like permissions, symlinks, etc.
///
/// Note that any files written to the memory file system won't be copied over.
#[allow(unused)]
pub(crate) fn with_os_file_system(&mut self) {
self.file_system = TestFileSystem::Os(OsFileSystem);
}
#[allow(unused)]
pub(crate) fn vfs_mut(&mut self) -> &mut Vfs {
&mut self.vfs
}
/// Takes the salsa events.
///
/// ## Panics
/// If there are any pending salsa snapshots.
#[allow(unused)]
pub(crate) fn take_salsa_events(&mut self) -> Vec<salsa::Event> {
let inner = sync::Arc::get_mut(&mut self.events).expect("no pending salsa snapshots");
let events = inner.get_mut().unwrap();
std::mem::take(&mut *events)
}
/// Clears the salsa events.
///
/// ## Panics
/// If there are any pending salsa snapshots.
#[allow(unused)]
pub(crate) fn clear_salsa_events(&mut self) {
self.take_salsa_events();
}
}
impl Upcast<dyn ruff_db::Db> for TestDb {
fn upcast(&self) -> &(dyn ruff_db::Db + 'static) {
self
}
}
impl ruff_db::Db for TestDb {
fn file_system(&self) -> &dyn ruff_db::file_system::FileSystem {
self.file_system.inner()
}
fn vfs(&self) -> &ruff_db::vfs::Vfs {
&self.vfs
}
}
impl Db for TestDb {}
impl salsa::Database for TestDb {
fn salsa_event(&self, event: salsa::Event) {
tracing::trace!("event: {:?}", event.debug(self));
let mut events = self.events.lock().unwrap();
events.push(event);
}
}
impl salsa::ParallelDatabase for TestDb {
fn snapshot(&self) -> salsa::Snapshot<Self> {
salsa::Snapshot::new(Self {
storage: self.storage.snapshot(),
file_system: self.file_system.snapshot(),
events: self.events.clone(),
vfs: self.vfs.snapshot(),
})
}
}
enum TestFileSystem {
Memory(MemoryFileSystem),
#[allow(unused)]
Os(OsFileSystem),
}
impl TestFileSystem {
fn inner(&self) -> &dyn FileSystem {
match self {
Self::Memory(inner) => inner,
Self::Os(inner) => inner,
}
}
fn snapshot(&self) -> Self {
match self {
Self::Memory(inner) => Self::Memory(inner.snapshot()),
Self::Os(inner) => Self::Os(inner.snapshot()),
}
}
}
}

View File

@@ -1,9 +0,0 @@
mod db;
mod module;
mod resolver;
mod typeshed;
pub use db::{Db, Jar};
pub use module::{ModuleKind, ModuleName};
pub use resolver::{resolve_module, set_module_resolution_settings, ModuleResolutionSettings};
pub use typeshed::versions::TypeshedVersions;

View File

@@ -1,346 +0,0 @@
use std::fmt::Formatter;
use std::ops::Deref;
use std::sync::Arc;
use ruff_db::file_system::FileSystemPath;
use ruff_db::vfs::{VfsFile, VfsPath};
use ruff_python_stdlib::identifiers::is_identifier;
use crate::Db;
/// A module name, e.g. `foo.bar`.
///
/// Always normalized to the absolute form (never a relative module name, i.e., never `.foo`).
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct ModuleName(smol_str::SmolStr);
impl ModuleName {
/// Creates a new module name for `name`. Returns `Some` if `name` is a valid, absolute
/// module name and `None` otherwise.
///
/// The module name is invalid if:
///
/// * The name is empty
/// * The name is relative
/// * The name ends with a `.`
/// * The name contains a sequence of multiple dots
/// * A component of a name (the part between two dots) isn't a valid python identifier.
#[inline]
pub fn new(name: &str) -> Option<Self> {
Self::new_from_smol(smol_str::SmolStr::new(name))
}
/// Creates a new module name for `name` where `name` is a static string.
/// Returns `Some` if `name` is a valid, absolute module name and `None` otherwise.
///
/// The module name is invalid if:
///
/// * The name is empty
/// * The name is relative
/// * The name ends with a `.`
/// * The name contains a sequence of multiple dots
/// * A component of a name (the part between two dots) isn't a valid python identifier.
///
/// ## Examples
///
/// ```
/// use red_knot_module_resolver::ModuleName;
///
/// assert_eq!(ModuleName::new_static("foo.bar").as_deref(), Some("foo.bar"));
/// assert_eq!(ModuleName::new_static(""), None);
/// assert_eq!(ModuleName::new_static("..foo"), None);
/// assert_eq!(ModuleName::new_static(".foo"), None);
/// assert_eq!(ModuleName::new_static("foo."), None);
/// assert_eq!(ModuleName::new_static("foo..bar"), None);
/// assert_eq!(ModuleName::new_static("2000"), None);
/// ```
#[inline]
pub fn new_static(name: &'static str) -> Option<Self> {
Self::new_from_smol(smol_str::SmolStr::new_static(name))
}
fn new_from_smol(name: smol_str::SmolStr) -> Option<Self> {
if name.is_empty() {
return None;
}
if name.split('.').all(is_identifier) {
Some(Self(name))
} else {
None
}
}
/// An iterator over the components of the module name:
///
/// # Examples
///
/// ```
/// use red_knot_module_resolver::ModuleName;
///
/// assert_eq!(ModuleName::new_static("foo.bar.baz").unwrap().components().collect::<Vec<_>>(), vec!["foo", "bar", "baz"]);
/// ```
pub fn components(&self) -> impl DoubleEndedIterator<Item = &str> {
self.0.split('.')
}
/// The name of this module's immediate parent, if it has a parent.
///
/// # Examples
///
/// ```
/// use red_knot_module_resolver::ModuleName;
///
/// assert_eq!(ModuleName::new_static("foo.bar").unwrap().parent(), Some(ModuleName::new_static("foo").unwrap()));
/// assert_eq!(ModuleName::new_static("foo.bar.baz").unwrap().parent(), Some(ModuleName::new_static("foo.bar").unwrap()));
/// assert_eq!(ModuleName::new_static("root").unwrap().parent(), None);
/// ```
pub fn parent(&self) -> Option<ModuleName> {
let (parent, _) = self.0.rsplit_once('.')?;
Some(Self(smol_str::SmolStr::new(parent)))
}
/// Returns `true` if the name starts with `other`.
///
/// This is equivalent to checking if `self` is a sub-module of `other`.
///
/// # Examples
///
/// ```
/// use red_knot_module_resolver::ModuleName;
///
/// assert!(ModuleName::new_static("foo.bar").unwrap().starts_with(&ModuleName::new_static("foo").unwrap()));
///
/// assert!(!ModuleName::new_static("foo.bar").unwrap().starts_with(&ModuleName::new_static("bar").unwrap()));
/// assert!(!ModuleName::new_static("foo_bar").unwrap().starts_with(&ModuleName::new_static("foo").unwrap()));
/// ```
pub fn starts_with(&self, other: &ModuleName) -> bool {
let mut self_components = self.components();
let other_components = other.components();
for other_component in other_components {
if self_components.next() != Some(other_component) {
return false;
}
}
true
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
pub(crate) fn from_relative_path(path: &FileSystemPath) -> Option<Self> {
let path = if path.ends_with("__init__.py") || path.ends_with("__init__.pyi") {
path.parent()?
} else {
path
};
let name = if let Some(parent) = path.parent() {
let mut name = String::with_capacity(path.as_str().len());
for component in parent.components() {
name.push_str(component.as_os_str().to_str()?);
name.push('.');
}
// SAFETY: Unwrap is safe here or `parent` would have returned `None`.
name.push_str(path.file_stem().unwrap());
smol_str::SmolStr::from(name)
} else {
smol_str::SmolStr::new(path.file_stem()?)
};
Some(Self(name))
}
}
impl Deref for ModuleName {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl PartialEq<str> for ModuleName {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<ModuleName> for str {
fn eq(&self, other: &ModuleName) -> bool {
self == other.as_str()
}
}
impl std::fmt::Display for ModuleName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
/// Representation of a Python module.
#[derive(Clone, PartialEq, Eq)]
pub struct Module {
inner: Arc<ModuleInner>,
}
impl Module {
pub(crate) fn new(
name: ModuleName,
kind: ModuleKind,
search_path: ModuleSearchPath,
file: VfsFile,
) -> Self {
Self {
inner: Arc::new(ModuleInner {
name,
kind,
search_path,
file,
}),
}
}
/// The absolute name of the module (e.g. `foo.bar`)
pub fn name(&self) -> &ModuleName {
&self.inner.name
}
/// The file to the source code that defines this module
pub fn file(&self) -> VfsFile {
self.inner.file
}
/// The search path from which the module was resolved.
pub fn search_path(&self) -> &ModuleSearchPath {
&self.inner.search_path
}
/// Determine whether this module is a single-file module or a package
pub fn kind(&self) -> ModuleKind {
self.inner.kind
}
}
impl std::fmt::Debug for Module {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Module")
.field("name", &self.name())
.field("kind", &self.kind())
.field("file", &self.file())
.field("search_path", &self.search_path())
.finish()
}
}
impl salsa::DebugWithDb<dyn Db> for Module {
fn fmt(&self, f: &mut Formatter<'_>, db: &dyn Db) -> std::fmt::Result {
f.debug_struct("Module")
.field("name", &self.name())
.field("kind", &self.kind())
.field("file", &self.file().debug(db.upcast()))
.field("search_path", &self.search_path())
.finish()
}
}
#[derive(PartialEq, Eq)]
struct ModuleInner {
name: ModuleName,
kind: ModuleKind,
search_path: ModuleSearchPath,
file: VfsFile,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ModuleKind {
/// A single-file module (e.g. `foo.py` or `foo.pyi`)
Module,
/// A python package (`foo/__init__.py` or `foo/__init__.pyi`)
Package,
}
/// A search path in which to search modules.
/// Corresponds to a path in [`sys.path`](https://docs.python.org/3/library/sys_path_init.html) at runtime.
///
/// Cloning a search path is cheap because it's an `Arc`.
#[derive(Clone, PartialEq, Eq)]
pub struct ModuleSearchPath {
inner: Arc<ModuleSearchPathInner>,
}
impl ModuleSearchPath {
pub fn new<P>(path: P, kind: ModuleSearchPathKind) -> Self
where
P: Into<VfsPath>,
{
Self {
inner: Arc::new(ModuleSearchPathInner {
path: path.into(),
kind,
}),
}
}
/// Determine whether this is a first-party, third-party or standard-library search path
pub fn kind(&self) -> ModuleSearchPathKind {
self.inner.kind
}
/// Return the location of the search path on the file system
pub fn path(&self) -> &VfsPath {
&self.inner.path
}
}
impl std::fmt::Debug for ModuleSearchPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ModuleSearchPath")
.field("path", &self.inner.path)
.field("kind", &self.kind())
.finish()
}
}
#[derive(Eq, PartialEq)]
struct ModuleSearchPathInner {
path: VfsPath,
kind: ModuleSearchPathKind,
}
/// Enumeration of the different kinds of search paths type checkers are expected to support.
///
/// N.B. Although we don't implement `Ord` for this enum, they are ordered in terms of the
/// priority that we want to give these modules when resolving them.
/// This is roughly [the order given in the typing spec], but typeshed's stubs
/// for the standard library are moved higher up to match Python's semantics at runtime.
///
/// [the order given in the typing spec]: https://typing.readthedocs.io/en/latest/spec/distributing.html#import-resolution-ordering
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ModuleSearchPathKind {
/// "Extra" paths provided by the user in a config file, env var or CLI flag.
/// E.g. mypy's `MYPYPATH` env var, or pyright's `stubPath` configuration setting
Extra,
/// Files in the project we're directly being invoked on
FirstParty,
/// The `stdlib` directory of typeshed (either vendored or custom)
StandardLibrary,
/// Stubs or runtime modules installed in site-packages
SitePackagesThirdParty,
/// Vendored third-party stubs from typeshed
VendoredThirdParty,
}

View File

@@ -1,938 +0,0 @@
use salsa::DebugWithDb;
use std::ops::Deref;
use ruff_db::file_system::{FileSystem, FileSystemPath, FileSystemPathBuf};
use ruff_db::vfs::{system_path_to_file, vfs_path_to_file, VfsFile, VfsPath};
use crate::module::{Module, ModuleKind, ModuleName, ModuleSearchPath, ModuleSearchPathKind};
use crate::resolver::internal::ModuleResolverSearchPaths;
use crate::Db;
const TYPESHED_STDLIB_DIRECTORY: &str = "stdlib";
/// Configures the module search paths for the module resolver.
///
/// Must be called before calling any other module resolution functions.
pub fn set_module_resolution_settings(db: &mut dyn Db, config: ModuleResolutionSettings) {
// There's no concurrency issue here because we hold a `&mut dyn Db` reference. No other
// thread can mutate the `Db` while we're in this call, so using `try_get` to test if
// the settings have already been set is safe.
if let Some(existing) = ModuleResolverSearchPaths::try_get(db) {
existing
.set_search_paths(db)
.to(config.into_ordered_search_paths());
} else {
ModuleResolverSearchPaths::new(db, config.into_ordered_search_paths());
}
}
/// Resolves a module name to a module.
pub fn resolve_module(db: &dyn Db, module_name: ModuleName) -> Option<Module> {
let interned_name = internal::ModuleNameIngredient::new(db, module_name);
resolve_module_query(db, interned_name)
}
/// Salsa query that resolves an interned [`ModuleNameIngredient`] to a module.
///
/// This query should not be called directly. Instead, use [`resolve_module`]. It only exists
/// because Salsa requires the module name to be an ingredient.
#[salsa::tracked]
pub(crate) fn resolve_module_query<'db>(
db: &'db dyn Db,
module_name: internal::ModuleNameIngredient<'db>,
) -> Option<Module> {
let _ = tracing::trace_span!("resolve_module", module_name = ?module_name.debug(db)).enter();
let name = module_name.name(db);
let (search_path, module_file, kind) = resolve_name(db, name)?;
let module = Module::new(name.clone(), kind, search_path, module_file);
Some(module)
}
/// Resolves the module for the given path.
///
/// Returns `None` if the path is not a module locatable via `sys.path`.
#[tracing::instrument(level = "debug", skip(db))]
pub fn path_to_module(db: &dyn Db, path: &VfsPath) -> Option<Module> {
// It's not entirely clear on first sight why this method calls `file_to_module` instead of
// it being the other way round, considering that the first thing that `file_to_module` does
// is to retrieve the file's path.
//
// The reason is that `file_to_module` is a tracked Salsa query and salsa queries require that
// all arguments are Salsa ingredients (something stored in Salsa). `Path`s aren't salsa ingredients but
// `VfsFile` is. So what we do here is to retrieve the `path`'s `VfsFile` so that we can make
// use of Salsa's caching and invalidation.
let file = vfs_path_to_file(db.upcast(), path)?;
file_to_module(db, file)
}
/// Resolves the module for the file with the given id.
///
/// Returns `None` if the file is not a module locatable via `sys.path`.
#[salsa::tracked]
#[allow(unused)]
pub(crate) fn file_to_module(db: &dyn Db, file: VfsFile) -> Option<Module> {
let _ = tracing::trace_span!("file_to_module", file = ?file.debug(db.upcast())).enter();
let path = file.path(db.upcast());
let search_paths = module_search_paths(db);
let relative_path = search_paths
.iter()
.find_map(|root| match (root.path(), path) {
(VfsPath::FileSystem(root_path), VfsPath::FileSystem(path)) => {
let relative_path = path.strip_prefix(root_path).ok()?;
Some(relative_path)
}
(VfsPath::Vendored(_), VfsPath::Vendored(_)) => {
todo!("Add support for vendored modules")
}
(VfsPath::Vendored(_), VfsPath::FileSystem(_))
| (VfsPath::FileSystem(_), VfsPath::Vendored(_)) => None,
})?;
let module_name = ModuleName::from_relative_path(relative_path)?;
// Resolve the module name to see if Python would resolve the name to the same path.
// If it doesn't, then that means that multiple modules have the same name in different
// root paths, but that the module corresponding to `path` is in a lower priority search path,
// in which case we ignore it.
let module = resolve_module(db, module_name)?;
if file == module.file() {
Some(module)
} else {
// This path is for a module with the same name but with a different precedence. For example:
// ```
// src/foo.py
// src/foo/__init__.py
// ```
// The module name of `src/foo.py` is `foo`, but the module loaded by Python is `src/foo/__init__.py`.
// That means we need to ignore `src/foo.py` even though it resolves to the same module name.
None
}
}
/// Configures the search paths that are used to resolve modules.
#[derive(Eq, PartialEq, Debug)]
pub struct ModuleResolutionSettings {
/// List of user-provided paths that should take first priority in the module resolution.
/// Examples in other type checkers are mypy's MYPYPATH environment variable,
/// or pyright's stubPath configuration setting.
pub extra_paths: Vec<FileSystemPathBuf>,
/// The root of the workspace, used for finding first-party modules.
pub workspace_root: FileSystemPathBuf,
/// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed.
pub site_packages: Option<FileSystemPathBuf>,
/// Optional path to standard-library typeshed stubs.
/// Currently this has to be a directory that exists on disk.
///
/// (TODO: fall back to vendored stubs if no custom directory is provided.)
pub custom_typeshed: Option<FileSystemPathBuf>,
}
impl ModuleResolutionSettings {
/// Implementation of PEP 561's module resolution order
/// (with some small, deliberate, differences)
fn into_ordered_search_paths(self) -> OrderedSearchPaths {
let ModuleResolutionSettings {
extra_paths,
workspace_root,
site_packages,
custom_typeshed,
} = self;
let mut paths: Vec<_> = extra_paths
.into_iter()
.map(|path| ModuleSearchPath::new(path, ModuleSearchPathKind::Extra))
.collect();
paths.push(ModuleSearchPath::new(
workspace_root,
ModuleSearchPathKind::FirstParty,
));
// TODO fallback to vendored typeshed stubs if no custom typeshed directory is provided by the user
if let Some(custom_typeshed) = custom_typeshed {
paths.push(ModuleSearchPath::new(
custom_typeshed.join(TYPESHED_STDLIB_DIRECTORY),
ModuleSearchPathKind::StandardLibrary,
));
}
// TODO vendor typeshed's third-party stubs as well as the stdlib and fallback to them as a final step
if let Some(site_packages) = site_packages {
paths.push(ModuleSearchPath::new(
site_packages,
ModuleSearchPathKind::SitePackagesThirdParty,
));
}
OrderedSearchPaths(paths)
}
}
/// A resolved module resolution order, implementing PEP 561
/// (with some small, deliberate differences)
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(crate) struct OrderedSearchPaths(Vec<ModuleSearchPath>);
impl Deref for OrderedSearchPaths {
type Target = [ModuleSearchPath];
fn deref(&self) -> &Self::Target {
&self.0
}
}
// The singleton methods generated by salsa are all `pub` instead of `pub(crate)` which triggers
// `unreachable_pub`. Work around this by creating a module and allow `unreachable_pub` for it.
// Salsa also generates uses to `_db` variables for `interned` which triggers `clippy::used_underscore_binding`. Suppress that too
// TODO(micha): Contribute a fix for this upstream where the singleton methods have the same visibility as the struct.
#[allow(unreachable_pub, clippy::used_underscore_binding)]
pub(crate) mod internal {
use crate::module::ModuleName;
use crate::resolver::OrderedSearchPaths;
#[salsa::input(singleton)]
pub(crate) struct ModuleResolverSearchPaths {
#[return_ref]
pub(super) search_paths: OrderedSearchPaths,
}
/// A thin wrapper around `ModuleName` to make it a Salsa ingredient.
///
/// This is needed because Salsa requires that all query arguments are salsa ingredients.
#[salsa::interned]
pub(crate) struct ModuleNameIngredient<'db> {
#[return_ref]
pub(super) name: ModuleName,
}
}
fn module_search_paths(db: &dyn Db) -> &[ModuleSearchPath] {
ModuleResolverSearchPaths::get(db).search_paths(db)
}
/// Given a module name and a list of search paths in which to lookup modules,
/// attempt to resolve the module name
fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(ModuleSearchPath, VfsFile, ModuleKind)> {
let search_paths = module_search_paths(db);
for search_path in search_paths {
let mut components = name.components();
let module_name = components.next_back()?;
let VfsPath::FileSystem(fs_search_path) = search_path.path() else {
todo!("Vendored search paths are not yet supported");
};
match resolve_package(db.file_system(), fs_search_path, components) {
Ok(resolved_package) => {
let mut package_path = resolved_package.path;
package_path.push(module_name);
// Must be a `__init__.pyi` or `__init__.py` or it isn't a package.
let kind = if db.file_system().is_directory(&package_path) {
package_path.push("__init__");
ModuleKind::Package
} else {
ModuleKind::Module
};
// TODO Implement full https://peps.python.org/pep-0561/#type-checker-module-resolution-order resolution
let stub = package_path.with_extension("pyi");
if let Some(stub) = system_path_to_file(db.upcast(), &stub) {
return Some((search_path.clone(), stub, kind));
}
let module = package_path.with_extension("py");
if let Some(module) = system_path_to_file(db.upcast(), &module) {
return Some((search_path.clone(), module, kind));
}
// For regular packages, don't search the next search path. All files of that
// package must be in the same location
if resolved_package.kind.is_regular_package() {
return None;
}
}
Err(parent_kind) => {
if parent_kind.is_regular_package() {
// For regular packages, don't search the next search path.
return None;
}
}
}
}
None
}
fn resolve_package<'a, I>(
fs: &dyn FileSystem,
module_search_path: &FileSystemPath,
components: I,
) -> Result<ResolvedPackage, PackageKind>
where
I: Iterator<Item = &'a str>,
{
let mut package_path = module_search_path.to_path_buf();
// `true` if inside a folder that is a namespace package (has no `__init__.py`).
// Namespace packages are special because they can be spread across multiple search paths.
// https://peps.python.org/pep-0420/
let mut in_namespace_package = false;
// `true` if resolving a sub-package. For example, `true` when resolving `bar` of `foo.bar`.
let mut in_sub_package = false;
// For `foo.bar.baz`, test that `foo` and `baz` both contain a `__init__.py`.
for folder in components {
package_path.push(folder);
let has_init_py = fs.is_file(&package_path.join("__init__.py"))
|| fs.is_file(&package_path.join("__init__.pyi"));
if has_init_py {
in_namespace_package = false;
} else if fs.is_directory(&package_path) {
// A directory without an `__init__.py` is a namespace package, continue with the next folder.
in_namespace_package = true;
} else if in_namespace_package {
// Package not found but it is part of a namespace package.
return Err(PackageKind::Namespace);
} else if in_sub_package {
// A regular sub package wasn't found.
return Err(PackageKind::Regular);
} else {
// We couldn't find `foo` for `foo.bar.baz`, search the next search path.
return Err(PackageKind::Root);
}
in_sub_package = true;
}
let kind = if in_namespace_package {
PackageKind::Namespace
} else if in_sub_package {
PackageKind::Regular
} else {
PackageKind::Root
};
Ok(ResolvedPackage {
kind,
path: package_path,
})
}
#[derive(Debug)]
struct ResolvedPackage {
path: FileSystemPathBuf,
kind: PackageKind,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum PackageKind {
/// A root package or module. E.g. `foo` in `foo.bar.baz` or just `foo`.
Root,
/// A regular sub-package where the parent contains an `__init__.py`.
///
/// For example, `bar` in `foo.bar` when the `foo` directory contains an `__init__.py`.
Regular,
/// A sub-package in a namespace package. A namespace package is a package without an `__init__.py`.
///
/// For example, `bar` in `foo.bar` if the `foo` directory contains no `__init__.py`.
Namespace,
}
impl PackageKind {
const fn is_regular_package(self) -> bool {
matches!(self, PackageKind::Regular)
}
}
#[cfg(test)]
mod tests {
use ruff_db::file_system::{FileSystemPath, FileSystemPathBuf};
use ruff_db::vfs::{system_path_to_file, VfsFile, VfsPath};
use crate::db::tests::TestDb;
use crate::module::{ModuleKind, ModuleName};
use super::{
path_to_module, resolve_module, set_module_resolution_settings, ModuleResolutionSettings,
TYPESHED_STDLIB_DIRECTORY,
};
struct TestCase {
db: TestDb,
src: FileSystemPathBuf,
custom_typeshed: FileSystemPathBuf,
site_packages: FileSystemPathBuf,
}
fn create_resolver() -> std::io::Result<TestCase> {
let mut db = TestDb::new();
let src = FileSystemPath::new("src").to_path_buf();
let site_packages = FileSystemPath::new("site_packages").to_path_buf();
let custom_typeshed = FileSystemPath::new("typeshed").to_path_buf();
let fs = db.memory_file_system();
fs.create_directory_all(&src)?;
fs.create_directory_all(&site_packages)?;
fs.create_directory_all(&custom_typeshed)?;
let settings = ModuleResolutionSettings {
extra_paths: vec![],
workspace_root: src.clone(),
site_packages: Some(site_packages.clone()),
custom_typeshed: Some(custom_typeshed.clone()),
};
set_module_resolution_settings(&mut db, settings);
Ok(TestCase {
db,
src,
custom_typeshed,
site_packages,
})
}
#[test]
fn first_party_module() -> anyhow::Result<()> {
let TestCase { db, src, .. } = create_resolver()?;
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_path = src.join("foo.py");
db.memory_file_system()
.write_file(&foo_path, "print('Hello, world!')")?;
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
assert_eq!(
Some(&foo_module),
resolve_module(&db, foo_module_name.clone()).as_ref()
);
assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path().path());
assert_eq!(ModuleKind::Module, foo_module.kind());
assert_eq!(&foo_path, foo_module.file().path(&db));
assert_eq!(
Some(foo_module),
path_to_module(&db, &VfsPath::FileSystem(foo_path))
);
Ok(())
}
#[test]
fn stdlib() -> anyhow::Result<()> {
let TestCase {
db,
custom_typeshed,
..
} = create_resolver()?;
let stdlib_dir = custom_typeshed.join(TYPESHED_STDLIB_DIRECTORY);
let functools_path = stdlib_dir.join("functools.py");
db.memory_file_system()
.write_file(&functools_path, "def update_wrapper(): ...")?;
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(
Some(&functools_module),
resolve_module(&db, functools_module_name).as_ref()
);
assert_eq!(&stdlib_dir, functools_module.search_path().path());
assert_eq!(ModuleKind::Module, functools_module.kind());
assert_eq!(&functools_path.clone(), functools_module.file().path(&db));
assert_eq!(
Some(functools_module),
path_to_module(&db, &VfsPath::FileSystem(functools_path))
);
Ok(())
}
#[test]
fn first_party_precedence_over_stdlib() -> anyhow::Result<()> {
let TestCase {
db,
src,
custom_typeshed,
..
} = create_resolver()?;
let stdlib_dir = custom_typeshed.join(TYPESHED_STDLIB_DIRECTORY);
let stdlib_functools_path = stdlib_dir.join("functools.py");
let first_party_functools_path = src.join("functools.py");
db.memory_file_system().write_files([
(&stdlib_functools_path, "def update_wrapper(): ..."),
(&first_party_functools_path, "def update_wrapper(): ..."),
])?;
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(
Some(&functools_module),
resolve_module(&db, functools_module_name).as_ref()
);
assert_eq!(&src, functools_module.search_path().path());
assert_eq!(ModuleKind::Module, functools_module.kind());
assert_eq!(
&first_party_functools_path.clone(),
functools_module.file().path(&db)
);
assert_eq!(
Some(functools_module),
path_to_module(&db, &VfsPath::FileSystem(first_party_functools_path))
);
Ok(())
}
// TODO: Port typeshed test case. Porting isn't possible at the moment because the vendored zip
// is part of the red knot crate
// #[test]
// fn typeshed_zip_created_at_build_time() -> anyhow::Result<()> {
// // The file path here is hardcoded in this crate's `build.rs` script.
// // Luckily this crate will fail to build if this file isn't available at build time.
// const TYPESHED_ZIP_BYTES: &[u8] =
// include_bytes!(concat!(env!("OUT_DIR"), "/zipped_typeshed.zip"));
// assert!(!TYPESHED_ZIP_BYTES.is_empty());
// let mut typeshed_zip_archive = ZipArchive::new(Cursor::new(TYPESHED_ZIP_BYTES))?;
//
// let path_to_functools = Path::new("stdlib").join("functools.pyi");
// let mut functools_module_stub = typeshed_zip_archive
// .by_name(path_to_functools.to_str().unwrap())
// .unwrap();
// assert!(functools_module_stub.is_file());
//
// let mut functools_module_stub_source = String::new();
// functools_module_stub.read_to_string(&mut functools_module_stub_source)?;
//
// assert!(functools_module_stub_source.contains("def update_wrapper("));
// Ok(())
// }
#[test]
fn resolve_package() -> anyhow::Result<()> {
let TestCase { src, db, .. } = create_resolver()?;
let foo_dir = src.join("foo");
let foo_path = foo_dir.join("__init__.py");
db.memory_file_system()
.write_file(&foo_path, "print('Hello, world!')")?;
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path().path());
assert_eq!(&foo_path, foo_module.file().path(&db));
assert_eq!(
Some(&foo_module),
path_to_module(&db, &VfsPath::FileSystem(foo_path)).as_ref()
);
// Resolving by directory doesn't resolve to the init file.
assert_eq!(None, path_to_module(&db, &VfsPath::FileSystem(foo_dir)));
Ok(())
}
#[test]
fn package_priority_over_module() -> anyhow::Result<()> {
let TestCase { db, src, .. } = create_resolver()?;
let foo_dir = src.join("foo");
let foo_init = foo_dir.join("__init__.py");
db.memory_file_system()
.write_file(&foo_init, "print('Hello, world!')")?;
let foo_py = src.join("foo.py");
db.memory_file_system()
.write_file(&foo_py, "print('Hello, world!')")?;
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!(&src, foo_module.search_path().path());
assert_eq!(&foo_init, foo_module.file().path(&db));
assert_eq!(ModuleKind::Package, foo_module.kind());
assert_eq!(
Some(foo_module),
path_to_module(&db, &VfsPath::FileSystem(foo_init))
);
assert_eq!(None, path_to_module(&db, &VfsPath::FileSystem(foo_py)));
Ok(())
}
#[test]
fn typing_stub_over_module() -> anyhow::Result<()> {
let TestCase { db, src, .. } = create_resolver()?;
let foo_stub = src.join("foo.pyi");
let foo_py = src.join("foo.py");
db.memory_file_system()
.write_files([(&foo_stub, "x: int"), (&foo_py, "print('Hello, world!')")])?;
let foo = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!(&src, foo.search_path().path());
assert_eq!(&foo_stub, foo.file().path(&db));
assert_eq!(
Some(foo),
path_to_module(&db, &VfsPath::FileSystem(foo_stub))
);
assert_eq!(None, path_to_module(&db, &VfsPath::FileSystem(foo_py)));
Ok(())
}
#[test]
fn sub_packages() -> anyhow::Result<()> {
let TestCase { db, src, .. } = create_resolver()?;
let foo = src.join("foo");
let bar = foo.join("bar");
let baz = bar.join("baz.py");
db.memory_file_system().write_files([
(&foo.join("__init__.py"), ""),
(&bar.join("__init__.py"), ""),
(&baz, "print('Hello, world!')"),
])?;
let baz_module =
resolve_module(&db, ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
assert_eq!(&src, baz_module.search_path().path());
assert_eq!(&baz, baz_module.file().path(&db));
assert_eq!(
Some(baz_module),
path_to_module(&db, &VfsPath::FileSystem(baz))
);
Ok(())
}
#[test]
fn namespace_package() -> anyhow::Result<()> {
let TestCase {
db,
src,
site_packages,
..
} = create_resolver()?;
// From [PEP420](https://peps.python.org/pep-0420/#nested-namespace-packages).
// But uses `src` for `project1` and `site_packages2` for `project2`.
// ```
// src
// parent
// child
// one.py
// site_packages
// parent
// child
// two.py
// ```
let parent1 = src.join("parent");
let child1 = parent1.join("child");
let one = child1.join("one.py");
let parent2 = site_packages.join("parent");
let child2 = parent2.join("child");
let two = child2.join("two.py");
db.memory_file_system().write_files([
(&one, "print('Hello, world!')"),
(&two, "print('Hello, world!')"),
])?;
let one_module =
resolve_module(&db, ModuleName::new_static("parent.child.one").unwrap()).unwrap();
assert_eq!(
Some(one_module),
path_to_module(&db, &VfsPath::FileSystem(one))
);
let two_module =
resolve_module(&db, ModuleName::new_static("parent.child.two").unwrap()).unwrap();
assert_eq!(
Some(two_module),
path_to_module(&db, &VfsPath::FileSystem(two))
);
Ok(())
}
#[test]
fn regular_package_in_namespace_package() -> anyhow::Result<()> {
let TestCase {
db,
src,
site_packages,
..
} = create_resolver()?;
// Adopted test case from the [PEP420 examples](https://peps.python.org/pep-0420/#nested-namespace-packages).
// The `src/parent/child` package is a regular package. Therefore, `site_packages/parent/child/two.py` should not be resolved.
// ```
// src
// parent
// child
// one.py
// site_packages
// parent
// child
// two.py
// ```
let parent1 = src.join("parent");
let child1 = parent1.join("child");
let one = child1.join("one.py");
let parent2 = site_packages.join("parent");
let child2 = parent2.join("child");
let two = child2.join("two.py");
db.memory_file_system().write_files([
(&child1.join("__init__.py"), "print('Hello, world!')"),
(&one, "print('Hello, world!')"),
(&two, "print('Hello, world!')"),
])?;
let one_module =
resolve_module(&db, ModuleName::new_static("parent.child.one").unwrap()).unwrap();
assert_eq!(
Some(one_module),
path_to_module(&db, &VfsPath::FileSystem(one))
);
assert_eq!(
None,
resolve_module(&db, ModuleName::new_static("parent.child.two").unwrap())
);
Ok(())
}
#[test]
fn module_search_path_priority() -> anyhow::Result<()> {
let TestCase {
db,
src,
site_packages,
..
} = create_resolver()?;
let foo_src = src.join("foo.py");
let foo_site_packages = site_packages.join("foo.py");
db.memory_file_system()
.write_files([(&foo_src, ""), (&foo_site_packages, "")])?;
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!(&src, foo_module.search_path().path());
assert_eq!(&foo_src, foo_module.file().path(&db));
assert_eq!(
Some(foo_module),
path_to_module(&db, &VfsPath::FileSystem(foo_src))
);
assert_eq!(
None,
path_to_module(&db, &VfsPath::FileSystem(foo_site_packages))
);
Ok(())
}
#[test]
#[cfg(target_family = "unix")]
fn symlink() -> anyhow::Result<()> {
let TestCase {
mut db,
src,
site_packages,
custom_typeshed,
} = create_resolver()?;
db.with_os_file_system();
let temp_dir = tempfile::tempdir()?;
let root = FileSystemPath::from_std_path(temp_dir.path()).unwrap();
let src = root.join(src);
let site_packages = root.join(site_packages);
let custom_typeshed = root.join(custom_typeshed);
let foo = src.join("foo.py");
let bar = src.join("bar.py");
std::fs::create_dir_all(src.as_std_path())?;
std::fs::create_dir_all(site_packages.as_std_path())?;
std::fs::create_dir_all(custom_typeshed.as_std_path())?;
std::fs::write(foo.as_std_path(), "")?;
std::os::unix::fs::symlink(foo.as_std_path(), bar.as_std_path())?;
let settings = ModuleResolutionSettings {
extra_paths: vec![],
workspace_root: src.clone(),
site_packages: Some(site_packages),
custom_typeshed: Some(custom_typeshed),
};
set_module_resolution_settings(&mut db, settings);
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
let bar_module = resolve_module(&db, ModuleName::new_static("bar").unwrap()).unwrap();
assert_ne!(foo_module, bar_module);
assert_eq!(&src, foo_module.search_path().path());
assert_eq!(&foo, foo_module.file().path(&db));
// `foo` and `bar` shouldn't resolve to the same file
assert_eq!(&src, bar_module.search_path().path());
assert_eq!(&bar, bar_module.file().path(&db));
assert_eq!(&foo, foo_module.file().path(&db));
assert_ne!(&foo_module, &bar_module);
assert_eq!(
Some(foo_module),
path_to_module(&db, &VfsPath::FileSystem(foo))
);
assert_eq!(
Some(bar_module),
path_to_module(&db, &VfsPath::FileSystem(bar))
);
Ok(())
}
#[test]
fn deleting_an_unrealted_file_doesnt_change_module_resolution() -> anyhow::Result<()> {
let TestCase { mut db, src, .. } = create_resolver()?;
let foo_path = src.join("foo.py");
let bar_path = src.join("bar.py");
db.memory_file_system()
.write_files([(&foo_path, "x = 1"), (&bar_path, "y = 2")])?;
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let bar = system_path_to_file(&db, &bar_path).expect("bar.py to exist");
db.clear_salsa_events();
// Delete `bar.py`
db.memory_file_system().remove_file(&bar_path)?;
bar.touch(&mut db);
// Re-query the foo module. The foo module should still be cached because `bar.py` isn't relevant
// for resolving `foo`.
let foo_module2 = resolve_module(&db, foo_module_name);
assert!(!db
.take_salsa_events()
.iter()
.any(|event| { matches!(event.kind, salsa::EventKind::WillExecute { .. }) }));
assert_eq!(Some(foo_module), foo_module2);
Ok(())
}
#[test]
fn adding_a_file_on_which_the_module_resolution_depends_on_invalidates_the_query(
) -> anyhow::Result<()> {
let TestCase { mut db, src, .. } = create_resolver()?;
let foo_path = src.join("foo.py");
let foo_module_name = ModuleName::new_static("foo").unwrap();
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
// Now write the foo file
db.memory_file_system().write_file(&foo_path, "x = 1")?;
VfsFile::touch_path(&mut db, &VfsPath::FileSystem(foo_path.clone()));
let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist");
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
assert_eq!(foo_file, foo_module.file());
Ok(())
}
#[test]
fn removing_a_file_that_the_module_resolution_depends_on_invalidates_the_query(
) -> anyhow::Result<()> {
let TestCase { mut db, src, .. } = create_resolver()?;
let foo_path = src.join("foo.py");
let foo_init_path = src.join("foo/__init__.py");
db.memory_file_system()
.write_files([(&foo_path, "x = 1"), (&foo_init_path, "x = 2")])?;
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).expect("foo module to exist");
assert_eq!(&foo_init_path, foo_module.file().path(&db));
// Delete `foo/__init__.py` and the `foo` folder. `foo` should now resolve to `foo.py`
db.memory_file_system().remove_file(&foo_init_path)?;
db.memory_file_system()
.remove_directory(foo_init_path.parent().unwrap())?;
VfsFile::touch_path(&mut db, &VfsPath::FileSystem(foo_init_path.clone()));
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
assert_eq!(&foo_path, foo_module.file().path(&db));
Ok(())
}
}

View File

@@ -1 +0,0 @@
114409d49b43ba62a179ebb856fa70a5161f751e

View File

@@ -1,90 +0,0 @@
import sys
from _typeshed import SupportsWrite
from collections.abc import Iterable, Iterator
from typing import Any, Final, Literal
from typing_extensions import TypeAlias
__version__: Final[str]
QUOTE_ALL: Literal[1]
QUOTE_MINIMAL: Literal[0]
QUOTE_NONE: Literal[3]
QUOTE_NONNUMERIC: Literal[2]
if sys.version_info >= (3, 12):
QUOTE_STRINGS: Literal[4]
QUOTE_NOTNULL: Literal[5]
# Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC`
# However, using literals in situations like these can cause false-positives (see #7258)
_QuotingType: TypeAlias = int
class Error(Exception): ...
class Dialect:
delimiter: str
quotechar: str | None
escapechar: str | None
doublequote: bool
skipinitialspace: bool
lineterminator: str
quoting: _QuotingType
strict: bool
def __init__(self) -> None: ...
_DialectLike: TypeAlias = str | Dialect | type[Dialect]
class _reader(Iterator[list[str]]):
@property
def dialect(self) -> Dialect: ...
line_num: int
def __next__(self) -> list[str]: ...
class _writer:
@property
def dialect(self) -> Dialect: ...
def writerow(self, row: Iterable[Any]) -> Any: ...
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
def writer(
csvfile: SupportsWrite[str],
dialect: _DialectLike = "excel",
*,
delimiter: str = ",",
quotechar: str | None = '"',
escapechar: str | None = None,
doublequote: bool = True,
skipinitialspace: bool = False,
lineterminator: str = "\r\n",
quoting: _QuotingType = 0,
strict: bool = False,
) -> _writer: ...
def reader(
csvfile: Iterable[str],
dialect: _DialectLike = "excel",
*,
delimiter: str = ",",
quotechar: str | None = '"',
escapechar: str | None = None,
doublequote: bool = True,
skipinitialspace: bool = False,
lineterminator: str = "\r\n",
quoting: _QuotingType = 0,
strict: bool = False,
) -> _reader: ...
def register_dialect(
name: str,
dialect: type[Dialect] = ...,
*,
delimiter: str = ",",
quotechar: str | None = '"',
escapechar: str | None = None,
doublequote: bool = True,
skipinitialspace: bool = False,
lineterminator: str = "\r\n",
quoting: _QuotingType = 0,
strict: bool = False,
) -> None: ...
def unregister_dialect(name: str) -> None: ...
def get_dialect(name: str) -> Dialect: ...
def list_dialects() -> list[str]: ...
def field_size_limit(new_limit: int = ...) -> int: ...

View File

@@ -1,211 +0,0 @@
import sys
from _typeshed import ReadableBuffer, WriteableBuffer
from abc import abstractmethod
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
from ctypes import CDLL, ArgumentError as ArgumentError
from typing import Any, ClassVar, Generic, TypeVar, overload
from typing_extensions import Self, TypeAlias
if sys.version_info >= (3, 9):
from types import GenericAlias
_T = TypeVar("_T")
_CT = TypeVar("_CT", bound=_CData)
FUNCFLAG_CDECL: int
FUNCFLAG_PYTHONAPI: int
FUNCFLAG_USE_ERRNO: int
FUNCFLAG_USE_LASTERROR: int
RTLD_GLOBAL: int
RTLD_LOCAL: int
if sys.version_info >= (3, 11):
CTYPES_MAX_ARGCOUNT: int
if sys.version_info >= (3, 12):
SIZEOF_TIME_T: int
if sys.platform == "win32":
# Description, Source, HelpFile, HelpContext, scode
_COMError_Details: TypeAlias = tuple[str | None, str | None, str | None, int | None, int | None]
class COMError(Exception):
hresult: int
text: str | None
details: _COMError_Details
def __init__(self, hresult: int, text: str | None, details: _COMError_Details) -> None: ...
def CopyComPointer(src: _PointerLike, dst: _PointerLike | _CArgObject) -> int: ...
FUNCFLAG_HRESULT: int
FUNCFLAG_STDCALL: int
def FormatError(code: int = ...) -> str: ...
def get_last_error() -> int: ...
def set_last_error(value: int) -> int: ...
def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ...
def FreeLibrary(handle: int, /) -> None: ...
class _CDataMeta(type):
# By default mypy complains about the following two methods, because strictly speaking cls
# might not be a Type[_CT]. However this can never actually happen, because the only class that
# uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here.
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc]
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc]
class _CData(metaclass=_CDataMeta):
_b_base_: int
_b_needsfree_: bool
_objects: Mapping[Any, int] | None
# At runtime the following classmethods are available only on classes, not
# on instances. This can't be reflected properly in the type system:
#
# Structure.from_buffer(...) # valid at runtime
# Structure(...).from_buffer(...) # invalid at runtime
#
@classmethod
def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ...
@classmethod
def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ...
@classmethod
def from_address(cls, address: int) -> Self: ...
@classmethod
def from_param(cls, obj: Any) -> Self | _CArgObject: ...
@classmethod
def in_dll(cls, library: CDLL, name: str) -> Self: ...
def __buffer__(self, flags: int, /) -> memoryview: ...
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
class _SimpleCData(_CData, Generic[_T]):
value: _T
# The TypeVar can be unsolved here,
# but we can't use overloads without creating many, many mypy false-positive errors
def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
class _CanCastTo(_CData): ...
class _PointerLike(_CanCastTo): ...
class _Pointer(_PointerLike, _CData, Generic[_CT]):
_type_: type[_CT]
contents: _CT
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, arg: _CT) -> None: ...
@overload
def __getitem__(self, key: int, /) -> Any: ...
@overload
def __getitem__(self, key: slice, /) -> list[Any]: ...
def __setitem__(self, key: int, value: Any, /) -> None: ...
def POINTER(type: type[_CT]) -> type[_Pointer[_CT]]: ...
def pointer(arg: _CT, /) -> _Pointer[_CT]: ...
class _CArgObject: ...
def byref(obj: _CData, offset: int = ...) -> _CArgObject: ...
_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData]
_PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
class CFuncPtr(_PointerLike, _CData):
restype: type[_CData] | Callable[[int], Any] | None
argtypes: Sequence[type[_CData]]
errcheck: _ECT
# Abstract attribute that must be defined on subclasses
_flags_: ClassVar[int]
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, address: int, /) -> None: ...
@overload
def __init__(self, callable: Callable[..., Any], /) -> None: ...
@overload
def __init__(self, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> None: ...
if sys.platform == "win32":
@overload
def __init__(
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., /
) -> None: ...
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
_GetT = TypeVar("_GetT")
_SetT = TypeVar("_SetT")
class _CField(Generic[_CT, _GetT, _SetT]):
offset: int
size: int
@overload
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
@overload
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
def __set__(self, instance: Any, value: _SetT, /) -> None: ...
class _StructUnionMeta(_CDataMeta):
_fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]]
_pack_: int
_anonymous_: Sequence[str]
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
class _StructUnionBase(_CData, metaclass=_StructUnionMeta):
def __init__(self, *args: Any, **kw: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
def __setattr__(self, name: str, value: Any) -> None: ...
class Union(_StructUnionBase): ...
class Structure(_StructUnionBase): ...
class Array(_CData, Generic[_CT]):
@property
@abstractmethod
def _length_(self) -> int: ...
@_length_.setter
def _length_(self, value: int) -> None: ...
@property
@abstractmethod
def _type_(self) -> type[_CT]: ...
@_type_.setter
def _type_(self, value: type[_CT]) -> None: ...
# Note: only available if _CT == c_char
@property
def raw(self) -> bytes: ...
@raw.setter
def raw(self, value: ReadableBuffer) -> None: ...
value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise
# TODO These methods cannot be annotated correctly at the moment.
# All of these "Any"s stand for the array's element type, but it's not possible to use _CT
# here, because of a special feature of ctypes.
# By default, when accessing an element of an Array[_CT], the returned object has type _CT.
# However, when _CT is a "simple type" like c_int, ctypes automatically "unboxes" the object
# and converts it to the corresponding Python primitive. For example, when accessing an element
# of an Array[c_int], a Python int object is returned, not a c_int.
# This behavior does *not* apply to subclasses of "simple types".
# If MyInt is a subclass of c_int, then accessing an element of an Array[MyInt] returns
# a MyInt, not an int.
# This special behavior is not easy to model in a stub, so for now all places where
# the array element type would belong are annotated with Any instead.
def __init__(self, *args: Any) -> None: ...
@overload
def __getitem__(self, key: int, /) -> Any: ...
@overload
def __getitem__(self, key: slice, /) -> list[Any]: ...
@overload
def __setitem__(self, key: int, value: Any, /) -> None: ...
@overload
def __setitem__(self, key: slice, value: Iterable[Any], /) -> None: ...
def __iter__(self) -> Iterator[Any]: ...
# Can't inherit from Sized because the metaclass conflict between
# Sized and _CData prevents using _CDataMeta.
def __len__(self) -> int: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
def addressof(obj: _CData) -> int: ...
def alignment(obj_or_type: _CData | type[_CData]) -> int: ...
def get_errno() -> int: ...
def resize(obj: _CData, size: int) -> None: ...
def set_errno(value: int) -> int: ...
def sizeof(obj_or_type: _CData | type[_CData]) -> int: ...

View File

@@ -1,164 +0,0 @@
import sys
from _thread import _excepthook, _ExceptHookArgs
from _typeshed import ProfileFunction, TraceFunction
from collections.abc import Callable, Iterable, Mapping
from types import TracebackType
from typing import Any, TypeVar
_T = TypeVar("_T")
__all__ = [
"get_ident",
"active_count",
"Condition",
"current_thread",
"enumerate",
"main_thread",
"TIMEOUT_MAX",
"Event",
"Lock",
"RLock",
"Semaphore",
"BoundedSemaphore",
"Thread",
"Barrier",
"BrokenBarrierError",
"Timer",
"ThreadError",
"setprofile",
"settrace",
"local",
"stack_size",
"ExceptHookArgs",
"excepthook",
]
def active_count() -> int: ...
def current_thread() -> Thread: ...
def currentThread() -> Thread: ...
def get_ident() -> int: ...
def enumerate() -> list[Thread]: ...
def main_thread() -> Thread: ...
def settrace(func: TraceFunction) -> None: ...
def setprofile(func: ProfileFunction | None) -> None: ...
def stack_size(size: int | None = None) -> int: ...
TIMEOUT_MAX: float
class ThreadError(Exception): ...
class local:
def __getattribute__(self, name: str) -> Any: ...
def __setattr__(self, name: str, value: Any) -> None: ...
def __delattr__(self, name: str) -> None: ...
class Thread:
name: str
daemon: bool
@property
def ident(self) -> int | None: ...
def __init__(
self,
group: None = None,
target: Callable[..., object] | None = None,
name: str | None = None,
args: Iterable[Any] = (),
kwargs: Mapping[str, Any] | None = None,
*,
daemon: bool | None = None,
) -> None: ...
def start(self) -> None: ...
def run(self) -> None: ...
def join(self, timeout: float | None = None) -> None: ...
def getName(self) -> str: ...
def setName(self, name: str) -> None: ...
@property
def native_id(self) -> int | None: ... # only available on some platforms
def is_alive(self) -> bool: ...
if sys.version_info < (3, 9):
def isAlive(self) -> bool: ...
def isDaemon(self) -> bool: ...
def setDaemon(self, daemonic: bool) -> None: ...
class _DummyThread(Thread): ...
class Lock:
def __enter__(self) -> bool: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> bool | None: ...
def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
def release(self) -> None: ...
def locked(self) -> bool: ...
class _RLock:
def __enter__(self) -> bool: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> bool | None: ...
def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ...
def release(self) -> None: ...
RLock = _RLock
class Condition:
def __init__(self, lock: Lock | _RLock | None = None) -> None: ...
def __enter__(self) -> bool: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> bool | None: ...
def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
def release(self) -> None: ...
def wait(self, timeout: float | None = None) -> bool: ...
def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ...
def notify(self, n: int = 1) -> None: ...
def notify_all(self) -> None: ...
def notifyAll(self) -> None: ...
class Semaphore:
def __init__(self, value: int = 1) -> None: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> bool | None: ...
def acquire(self, blocking: bool = True, timeout: float | None = None) -> bool: ...
def __enter__(self, blocking: bool = True, timeout: float | None = None) -> bool: ...
if sys.version_info >= (3, 9):
def release(self, n: int = ...) -> None: ...
else:
def release(self) -> None: ...
class BoundedSemaphore(Semaphore): ...
class Event:
def is_set(self) -> bool: ...
def set(self) -> None: ...
def clear(self) -> None: ...
def wait(self, timeout: float | None = None) -> bool: ...
excepthook = _excepthook
ExceptHookArgs = _ExceptHookArgs
class Timer(Thread):
def __init__(
self,
interval: float,
function: Callable[..., object],
args: Iterable[Any] | None = None,
kwargs: Mapping[str, Any] | None = None,
) -> None: ...
def cancel(self) -> None: ...
class Barrier:
@property
def parties(self) -> int: ...
@property
def n_waiting(self) -> int: ...
@property
def broken(self) -> bool: ...
def __init__(self, parties: int, action: Callable[[], None] | None = None, timeout: float | None = None) -> None: ...
def wait(self, timeout: float | None = None) -> int: ...
def reset(self) -> None: ...
def abort(self) -> None: ...
class BrokenBarrierError(RuntimeError): ...

View File

@@ -1,100 +0,0 @@
import sys
from _typeshed import StrPath
from collections.abc import Mapping
LC_CTYPE: int
LC_COLLATE: int
LC_TIME: int
LC_MONETARY: int
LC_NUMERIC: int
LC_ALL: int
CHAR_MAX: int
def setlocale(category: int, locale: str | None = None, /) -> str: ...
def localeconv() -> Mapping[str, int | str | list[int]]: ...
if sys.version_info >= (3, 11):
def getencoding() -> str: ...
def strcoll(os1: str, os2: str, /) -> int: ...
def strxfrm(string: str, /) -> str: ...
# native gettext functions
# https://docs.python.org/3/library/locale.html#access-to-message-catalogs
# https://github.com/python/cpython/blob/f4c03484da59049eb62a9bf7777b963e2267d187/Modules/_localemodule.c#L626
if sys.platform != "win32":
LC_MESSAGES: int
ABDAY_1: int
ABDAY_2: int
ABDAY_3: int
ABDAY_4: int
ABDAY_5: int
ABDAY_6: int
ABDAY_7: int
ABMON_1: int
ABMON_2: int
ABMON_3: int
ABMON_4: int
ABMON_5: int
ABMON_6: int
ABMON_7: int
ABMON_8: int
ABMON_9: int
ABMON_10: int
ABMON_11: int
ABMON_12: int
DAY_1: int
DAY_2: int
DAY_3: int
DAY_4: int
DAY_5: int
DAY_6: int
DAY_7: int
ERA: int
ERA_D_T_FMT: int
ERA_D_FMT: int
ERA_T_FMT: int
MON_1: int
MON_2: int
MON_3: int
MON_4: int
MON_5: int
MON_6: int
MON_7: int
MON_8: int
MON_9: int
MON_10: int
MON_11: int
MON_12: int
CODESET: int
D_T_FMT: int
D_FMT: int
T_FMT: int
T_FMT_AMPM: int
AM_STR: int
PM_STR: int
RADIXCHAR: int
THOUSEP: int
YESEXPR: int
NOEXPR: int
CRNCYSTR: int
ALT_DIGITS: int
def nl_langinfo(key: int, /) -> str: ...
# This is dependent on `libintl.h` which is a part of `gettext`
# system dependency. These functions might be missing.
# But, we always say that they are present.
def gettext(msg: str, /) -> str: ...
def dgettext(domain: str | None, msg: str, /) -> str: ...
def dcgettext(domain: str | None, msg: str, category: int, /) -> str: ...
def textdomain(domain: str | None, /) -> str: ...
def bindtextdomain(domain: str, dir: StrPath | None, /) -> str: ...
def bind_textdomain_codeset(domain: str, codeset: str | None, /) -> str | None: ...

View File

@@ -1,117 +0,0 @@
import sys
from typing import Literal
SF_APPEND: Literal[0x00040000]
SF_ARCHIVED: Literal[0x00010000]
SF_IMMUTABLE: Literal[0x00020000]
SF_NOUNLINK: Literal[0x00100000]
SF_SNAPSHOT: Literal[0x00200000]
ST_MODE: Literal[0]
ST_INO: Literal[1]
ST_DEV: Literal[2]
ST_NLINK: Literal[3]
ST_UID: Literal[4]
ST_GID: Literal[5]
ST_SIZE: Literal[6]
ST_ATIME: Literal[7]
ST_MTIME: Literal[8]
ST_CTIME: Literal[9]
S_IFIFO: Literal[0o010000]
S_IFLNK: Literal[0o120000]
S_IFREG: Literal[0o100000]
S_IFSOCK: Literal[0o140000]
S_IFBLK: Literal[0o060000]
S_IFCHR: Literal[0o020000]
S_IFDIR: Literal[0o040000]
# These are 0 on systems that don't support the specific kind of file.
# Example: Linux doesn't support door files, so S_IFDOOR is 0 on linux.
S_IFDOOR: int
S_IFPORT: int
S_IFWHT: int
S_ISUID: Literal[0o4000]
S_ISGID: Literal[0o2000]
S_ISVTX: Literal[0o1000]
S_IRWXU: Literal[0o0700]
S_IRUSR: Literal[0o0400]
S_IWUSR: Literal[0o0200]
S_IXUSR: Literal[0o0100]
S_IRWXG: Literal[0o0070]
S_IRGRP: Literal[0o0040]
S_IWGRP: Literal[0o0020]
S_IXGRP: Literal[0o0010]
S_IRWXO: Literal[0o0007]
S_IROTH: Literal[0o0004]
S_IWOTH: Literal[0o0002]
S_IXOTH: Literal[0o0001]
S_ENFMT: Literal[0o2000]
S_IREAD: Literal[0o0400]
S_IWRITE: Literal[0o0200]
S_IEXEC: Literal[0o0100]
UF_APPEND: Literal[0x00000004]
UF_COMPRESSED: Literal[0x00000020] # OS X 10.6+ only
UF_HIDDEN: Literal[0x00008000] # OX X 10.5+ only
UF_IMMUTABLE: Literal[0x00000002]
UF_NODUMP: Literal[0x00000001]
UF_NOUNLINK: Literal[0x00000010]
UF_OPAQUE: Literal[0x00000008]
def S_IMODE(mode: int, /) -> int: ...
def S_IFMT(mode: int, /) -> int: ...
def S_ISBLK(mode: int, /) -> bool: ...
def S_ISCHR(mode: int, /) -> bool: ...
def S_ISDIR(mode: int, /) -> bool: ...
def S_ISDOOR(mode: int, /) -> bool: ...
def S_ISFIFO(mode: int, /) -> bool: ...
def S_ISLNK(mode: int, /) -> bool: ...
def S_ISPORT(mode: int, /) -> bool: ...
def S_ISREG(mode: int, /) -> bool: ...
def S_ISSOCK(mode: int, /) -> bool: ...
def S_ISWHT(mode: int, /) -> bool: ...
def filemode(mode: int, /) -> str: ...
if sys.platform == "win32":
IO_REPARSE_TAG_SYMLINK: int
IO_REPARSE_TAG_MOUNT_POINT: int
IO_REPARSE_TAG_APPEXECLINK: int
if sys.platform == "win32":
FILE_ATTRIBUTE_ARCHIVE: Literal[32]
FILE_ATTRIBUTE_COMPRESSED: Literal[2048]
FILE_ATTRIBUTE_DEVICE: Literal[64]
FILE_ATTRIBUTE_DIRECTORY: Literal[16]
FILE_ATTRIBUTE_ENCRYPTED: Literal[16384]
FILE_ATTRIBUTE_HIDDEN: Literal[2]
FILE_ATTRIBUTE_INTEGRITY_STREAM: Literal[32768]
FILE_ATTRIBUTE_NORMAL: Literal[128]
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Literal[8192]
FILE_ATTRIBUTE_NO_SCRUB_DATA: Literal[131072]
FILE_ATTRIBUTE_OFFLINE: Literal[4096]
FILE_ATTRIBUTE_READONLY: Literal[1]
FILE_ATTRIBUTE_REPARSE_POINT: Literal[1024]
FILE_ATTRIBUTE_SPARSE_FILE: Literal[512]
FILE_ATTRIBUTE_SYSTEM: Literal[4]
FILE_ATTRIBUTE_TEMPORARY: Literal[256]
FILE_ATTRIBUTE_VIRTUAL: Literal[65536]
if sys.version_info >= (3, 13):
SF_SETTABLE: Literal[0x3FFF0000]
# https://github.com/python/cpython/issues/114081#issuecomment-2119017790
# SF_RESTRICTED: Literal[0x00080000]
SF_FIRMLINK: Literal[0x00800000]
SF_DATALESS: Literal[0x40000000]
SF_SUPPORTED: Literal[0x9F0000]
SF_SYNTHETIC: Literal[0xC0000000]
UF_TRACKED: Literal[0x00000040]
UF_DATAVAULT: Literal[0x00000080]
UF_SETTABLE: Literal[0x0000FFFF]

View File

@@ -1,59 +0,0 @@
import sys
from _typeshed import structseq
from collections.abc import Callable
from threading import Thread
from types import TracebackType
from typing import Any, Final, NoReturn, final, overload
from typing_extensions import TypeVarTuple, Unpack
_Ts = TypeVarTuple("_Ts")
error = RuntimeError
def _count() -> int: ...
@final
class LockType:
def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
def release(self) -> None: ...
def locked(self) -> bool: ...
def __enter__(self) -> bool: ...
def __exit__(
self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
) -> None: ...
@overload
def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> int: ...
@overload
def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> int: ...
def interrupt_main() -> None: ...
def exit() -> NoReturn: ...
def allocate_lock() -> LockType: ...
def get_ident() -> int: ...
def stack_size(size: int = ...) -> int: ...
TIMEOUT_MAX: float
def get_native_id() -> int: ... # only available on some platforms
@final
class _ExceptHookArgs(structseq[Any], tuple[type[BaseException], BaseException | None, TracebackType | None, Thread | None]):
if sys.version_info >= (3, 10):
__match_args__: Final = ("exc_type", "exc_value", "exc_traceback", "thread")
@property
def exc_type(self) -> type[BaseException]: ...
@property
def exc_value(self) -> BaseException | None: ...
@property
def exc_traceback(self) -> TracebackType | None: ...
@property
def thread(self) -> Thread | None: ...
_excepthook: Callable[[_ExceptHookArgs], Any]
if sys.version_info >= (3, 12):
def daemon_threads_allowed() -> bool: ...
class _local:
def __getattribute__(self, name: str, /) -> Any: ...
def __setattr__(self, name: str, value: Any, /) -> None: ...
def __delattr__(self, name: str, /) -> None: ...

View File

@@ -1,41 +0,0 @@
import sys
from collections.abc import Callable
from typing import Any, Generic, TypeVar, final, overload
from typing_extensions import Self
if sys.version_info >= (3, 9):
from types import GenericAlias
_C = TypeVar("_C", bound=Callable[..., Any])
_T = TypeVar("_T")
@final
class CallableProxyType(Generic[_C]): # "weakcallableproxy"
def __eq__(self, value: object, /) -> bool: ...
def __getattr__(self, attr: str) -> Any: ...
__call__: _C
@final
class ProxyType(Generic[_T]): # "weakproxy"
def __eq__(self, value: object, /) -> bool: ...
def __getattr__(self, attr: str) -> Any: ...
class ReferenceType(Generic[_T]):
__callback__: Callable[[ReferenceType[_T]], Any]
def __new__(cls, o: _T, callback: Callable[[ReferenceType[_T]], Any] | None = ..., /) -> Self: ...
def __call__(self) -> _T | None: ...
def __eq__(self, value: object, /) -> bool: ...
def __hash__(self) -> int: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
ref = ReferenceType
def getweakrefcount(object: Any, /) -> int: ...
def getweakrefs(object: Any, /) -> list[Any]: ...
# Return CallableProxyType if object is callable, ProxyType otherwise
@overload
def proxy(object: _C, callback: Callable[[_C], Any] | None = None, /) -> CallableProxyType[_C]: ...
@overload
def proxy(object: _T, callback: Callable[[_T], Any] | None = None, /) -> Any: ...

View File

@@ -1,255 +0,0 @@
import sys
from _typeshed import ReadableBuffer
from collections.abc import Sequence
from typing import Any, Literal, NoReturn, final, overload
if sys.platform == "win32":
ABOVE_NORMAL_PRIORITY_CLASS: Literal[0x8000]
BELOW_NORMAL_PRIORITY_CLASS: Literal[0x4000]
CREATE_BREAKAWAY_FROM_JOB: Literal[0x1000000]
CREATE_DEFAULT_ERROR_MODE: Literal[0x4000000]
CREATE_NO_WINDOW: Literal[0x8000000]
CREATE_NEW_CONSOLE: Literal[0x10]
CREATE_NEW_PROCESS_GROUP: Literal[0x200]
DETACHED_PROCESS: Literal[8]
DUPLICATE_CLOSE_SOURCE: Literal[1]
DUPLICATE_SAME_ACCESS: Literal[2]
ERROR_ALREADY_EXISTS: Literal[183]
ERROR_BROKEN_PIPE: Literal[109]
ERROR_IO_PENDING: Literal[997]
ERROR_MORE_DATA: Literal[234]
ERROR_NETNAME_DELETED: Literal[64]
ERROR_NO_DATA: Literal[232]
ERROR_NO_SYSTEM_RESOURCES: Literal[1450]
ERROR_OPERATION_ABORTED: Literal[995]
ERROR_PIPE_BUSY: Literal[231]
ERROR_PIPE_CONNECTED: Literal[535]
ERROR_SEM_TIMEOUT: Literal[121]
FILE_FLAG_FIRST_PIPE_INSTANCE: Literal[0x80000]
FILE_FLAG_OVERLAPPED: Literal[0x40000000]
FILE_GENERIC_READ: Literal[1179785]
FILE_GENERIC_WRITE: Literal[1179926]
FILE_MAP_ALL_ACCESS: Literal[983071]
FILE_MAP_COPY: Literal[1]
FILE_MAP_EXECUTE: Literal[32]
FILE_MAP_READ: Literal[4]
FILE_MAP_WRITE: Literal[2]
FILE_TYPE_CHAR: Literal[2]
FILE_TYPE_DISK: Literal[1]
FILE_TYPE_PIPE: Literal[3]
FILE_TYPE_REMOTE: Literal[32768]
FILE_TYPE_UNKNOWN: Literal[0]
GENERIC_READ: Literal[0x80000000]
GENERIC_WRITE: Literal[0x40000000]
HIGH_PRIORITY_CLASS: Literal[0x80]
INFINITE: Literal[0xFFFFFFFF]
# Ignore the Flake8 error -- flake8-pyi assumes
# most numbers this long will be implementation details,
# but here we can see that it's a power of 2
INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054
IDLE_PRIORITY_CLASS: Literal[0x40]
NORMAL_PRIORITY_CLASS: Literal[0x20]
REALTIME_PRIORITY_CLASS: Literal[0x100]
NMPWAIT_WAIT_FOREVER: Literal[0xFFFFFFFF]
MEM_COMMIT: Literal[0x1000]
MEM_FREE: Literal[0x10000]
MEM_IMAGE: Literal[0x1000000]
MEM_MAPPED: Literal[0x40000]
MEM_PRIVATE: Literal[0x20000]
MEM_RESERVE: Literal[0x2000]
NULL: Literal[0]
OPEN_EXISTING: Literal[3]
PIPE_ACCESS_DUPLEX: Literal[3]
PIPE_ACCESS_INBOUND: Literal[1]
PIPE_READMODE_MESSAGE: Literal[2]
PIPE_TYPE_MESSAGE: Literal[4]
PIPE_UNLIMITED_INSTANCES: Literal[255]
PIPE_WAIT: Literal[0]
PAGE_EXECUTE: Literal[0x10]
PAGE_EXECUTE_READ: Literal[0x20]
PAGE_EXECUTE_READWRITE: Literal[0x40]
PAGE_EXECUTE_WRITECOPY: Literal[0x80]
PAGE_GUARD: Literal[0x100]
PAGE_NOACCESS: Literal[0x1]
PAGE_NOCACHE: Literal[0x200]
PAGE_READONLY: Literal[0x2]
PAGE_READWRITE: Literal[0x4]
PAGE_WRITECOMBINE: Literal[0x400]
PAGE_WRITECOPY: Literal[0x8]
PROCESS_ALL_ACCESS: Literal[0x1FFFFF]
PROCESS_DUP_HANDLE: Literal[0x40]
SEC_COMMIT: Literal[0x8000000]
SEC_IMAGE: Literal[0x1000000]
SEC_LARGE_PAGES: Literal[0x80000000]
SEC_NOCACHE: Literal[0x10000000]
SEC_RESERVE: Literal[0x4000000]
SEC_WRITECOMBINE: Literal[0x40000000]
STARTF_USESHOWWINDOW: Literal[0x1]
STARTF_USESTDHANDLES: Literal[0x100]
STD_ERROR_HANDLE: Literal[0xFFFFFFF4]
STD_OUTPUT_HANDLE: Literal[0xFFFFFFF5]
STD_INPUT_HANDLE: Literal[0xFFFFFFF6]
STILL_ACTIVE: Literal[259]
SW_HIDE: Literal[0]
SYNCHRONIZE: Literal[0x100000]
WAIT_ABANDONED_0: Literal[128]
WAIT_OBJECT_0: Literal[0]
WAIT_TIMEOUT: Literal[258]
if sys.version_info >= (3, 10):
LOCALE_NAME_INVARIANT: str
LOCALE_NAME_MAX_LENGTH: int
LOCALE_NAME_SYSTEM_DEFAULT: str
LOCALE_NAME_USER_DEFAULT: str | None
LCMAP_FULLWIDTH: int
LCMAP_HALFWIDTH: int
LCMAP_HIRAGANA: int
LCMAP_KATAKANA: int
LCMAP_LINGUISTIC_CASING: int
LCMAP_LOWERCASE: int
LCMAP_SIMPLIFIED_CHINESE: int
LCMAP_TITLECASE: int
LCMAP_TRADITIONAL_CHINESE: int
LCMAP_UPPERCASE: int
if sys.version_info >= (3, 12):
COPYFILE2_CALLBACK_CHUNK_STARTED: Literal[1]
COPYFILE2_CALLBACK_CHUNK_FINISHED: Literal[2]
COPYFILE2_CALLBACK_STREAM_STARTED: Literal[3]
COPYFILE2_CALLBACK_STREAM_FINISHED: Literal[4]
COPYFILE2_CALLBACK_POLL_CONTINUE: Literal[5]
COPYFILE2_CALLBACK_ERROR: Literal[6]
COPYFILE2_PROGRESS_CONTINUE: Literal[0]
COPYFILE2_PROGRESS_CANCEL: Literal[1]
COPYFILE2_PROGRESS_STOP: Literal[2]
COPYFILE2_PROGRESS_QUIET: Literal[3]
COPYFILE2_PROGRESS_PAUSE: Literal[4]
COPY_FILE_FAIL_IF_EXISTS: Literal[0x1]
COPY_FILE_RESTARTABLE: Literal[0x2]
COPY_FILE_OPEN_SOURCE_FOR_WRITE: Literal[0x4]
COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Literal[0x8]
COPY_FILE_COPY_SYMLINK: Literal[0x800]
COPY_FILE_NO_BUFFERING: Literal[0x1000]
COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Literal[0x2000]
COPY_FILE_RESUME_FROM_PAUSE: Literal[0x4000]
COPY_FILE_NO_OFFLOAD: Literal[0x40000]
COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Literal[0x10000000]
ERROR_ACCESS_DENIED: Literal[5]
ERROR_PRIVILEGE_NOT_HELD: Literal[1314]
def CloseHandle(handle: int, /) -> None: ...
@overload
def ConnectNamedPipe(handle: int, overlapped: Literal[True]) -> Overlapped: ...
@overload
def ConnectNamedPipe(handle: int, overlapped: Literal[False] = False) -> None: ...
@overload
def ConnectNamedPipe(handle: int, overlapped: bool) -> Overlapped | None: ...
def CreateFile(
file_name: str,
desired_access: int,
share_mode: int,
security_attributes: int,
creation_disposition: int,
flags_and_attributes: int,
template_file: int,
/,
) -> int: ...
def CreateJunction(src_path: str, dst_path: str, /) -> None: ...
def CreateNamedPipe(
name: str,
open_mode: int,
pipe_mode: int,
max_instances: int,
out_buffer_size: int,
in_buffer_size: int,
default_timeout: int,
security_attributes: int,
/,
) -> int: ...
def CreatePipe(pipe_attrs: Any, size: int, /) -> tuple[int, int]: ...
def CreateProcess(
application_name: str | None,
command_line: str | None,
proc_attrs: Any,
thread_attrs: Any,
inherit_handles: bool,
creation_flags: int,
env_mapping: dict[str, str],
current_directory: str | None,
startup_info: Any,
/,
) -> tuple[int, int, int, int]: ...
def DuplicateHandle(
source_process_handle: int,
source_handle: int,
target_process_handle: int,
desired_access: int,
inherit_handle: bool,
options: int = 0,
/,
) -> int: ...
def ExitProcess(ExitCode: int, /) -> NoReturn: ...
def GetACP() -> int: ...
def GetFileType(handle: int) -> int: ...
def GetCurrentProcess() -> int: ...
def GetExitCodeProcess(process: int, /) -> int: ...
def GetLastError() -> int: ...
def GetModuleFileName(module_handle: int, /) -> str: ...
def GetStdHandle(std_handle: int, /) -> int: ...
def GetVersion() -> int: ...
def OpenProcess(desired_access: int, inherit_handle: bool, process_id: int, /) -> int: ...
def PeekNamedPipe(handle: int, size: int = 0, /) -> tuple[int, int] | tuple[bytes, int, int]: ...
if sys.version_info >= (3, 10):
def LCMapStringEx(locale: str, flags: int, src: str) -> str: ...
def UnmapViewOfFile(address: int, /) -> None: ...
@overload
def ReadFile(handle: int, size: int, overlapped: Literal[True]) -> tuple[Overlapped, int]: ...
@overload
def ReadFile(handle: int, size: int, overlapped: Literal[False] = False) -> tuple[bytes, int]: ...
@overload
def ReadFile(handle: int, size: int, overlapped: int | bool) -> tuple[Any, int]: ...
def SetNamedPipeHandleState(
named_pipe: int, mode: int | None, max_collection_count: int | None, collect_data_timeout: int | None, /
) -> None: ...
def TerminateProcess(handle: int, exit_code: int, /) -> None: ...
def WaitForMultipleObjects(handle_seq: Sequence[int], wait_flag: bool, milliseconds: int = 0xFFFFFFFF, /) -> int: ...
def WaitForSingleObject(handle: int, milliseconds: int, /) -> int: ...
def WaitNamedPipe(name: str, timeout: int, /) -> None: ...
@overload
def WriteFile(handle: int, buffer: ReadableBuffer, overlapped: Literal[True]) -> tuple[Overlapped, int]: ...
@overload
def WriteFile(handle: int, buffer: ReadableBuffer, overlapped: Literal[False] = False) -> tuple[int, int]: ...
@overload
def WriteFile(handle: int, buffer: ReadableBuffer, overlapped: int | bool) -> tuple[Any, int]: ...
@final
class Overlapped:
event: int
def GetOverlappedResult(self, wait: bool, /) -> tuple[int, int]: ...
def cancel(self) -> None: ...
def getbuffer(self) -> bytes | None: ...
if sys.version_info >= (3, 12):
def CopyFile2(existing_file_name: str, new_file_name: str, flags: int, progress_routine: int | None = None) -> int: ...
def NeedCurrentDirectoryForExePath(exe_name: str, /) -> bool: ...

View File

@@ -1,370 +0,0 @@
import os
import sys
from _ast import *
from _typeshed import ReadableBuffer, Unused
from collections.abc import Iterator
from typing import Any, Literal, TypeVar as _TypeVar, overload
from typing_extensions import deprecated
class _ABC(type):
if sys.version_info >= (3, 9):
def __init__(cls, *args: Unused) -> None: ...
@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14")
class Num(Constant, metaclass=_ABC):
value: int | float | complex
@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14")
class Str(Constant, metaclass=_ABC):
value: str
# Aliases for value, for backwards compatibility
s: str
@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14")
class Bytes(Constant, metaclass=_ABC):
value: bytes
# Aliases for value, for backwards compatibility
s: bytes
@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14")
class NameConstant(Constant, metaclass=_ABC): ...
@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14")
class Ellipsis(Constant, metaclass=_ABC): ...
if sys.version_info >= (3, 9):
class slice(AST): ...
class ExtSlice(slice): ...
class Index(slice): ...
class Suite(mod): ...
class AugLoad(expr_context): ...
class AugStore(expr_context): ...
class Param(expr_context): ...
class NodeVisitor:
def visit(self, node: AST) -> Any: ...
def generic_visit(self, node: AST) -> Any: ...
def visit_Module(self, node: Module) -> Any: ...
def visit_Interactive(self, node: Interactive) -> Any: ...
def visit_Expression(self, node: Expression) -> Any: ...
def visit_FunctionDef(self, node: FunctionDef) -> Any: ...
def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any: ...
def visit_ClassDef(self, node: ClassDef) -> Any: ...
def visit_Return(self, node: Return) -> Any: ...
def visit_Delete(self, node: Delete) -> Any: ...
def visit_Assign(self, node: Assign) -> Any: ...
def visit_AugAssign(self, node: AugAssign) -> Any: ...
def visit_AnnAssign(self, node: AnnAssign) -> Any: ...
def visit_For(self, node: For) -> Any: ...
def visit_AsyncFor(self, node: AsyncFor) -> Any: ...
def visit_While(self, node: While) -> Any: ...
def visit_If(self, node: If) -> Any: ...
def visit_With(self, node: With) -> Any: ...
def visit_AsyncWith(self, node: AsyncWith) -> Any: ...
def visit_Raise(self, node: Raise) -> Any: ...
def visit_Try(self, node: Try) -> Any: ...
def visit_Assert(self, node: Assert) -> Any: ...
def visit_Import(self, node: Import) -> Any: ...
def visit_ImportFrom(self, node: ImportFrom) -> Any: ...
def visit_Global(self, node: Global) -> Any: ...
def visit_Nonlocal(self, node: Nonlocal) -> Any: ...
def visit_Expr(self, node: Expr) -> Any: ...
def visit_Pass(self, node: Pass) -> Any: ...
def visit_Break(self, node: Break) -> Any: ...
def visit_Continue(self, node: Continue) -> Any: ...
def visit_Slice(self, node: Slice) -> Any: ...
def visit_BoolOp(self, node: BoolOp) -> Any: ...
def visit_BinOp(self, node: BinOp) -> Any: ...
def visit_UnaryOp(self, node: UnaryOp) -> Any: ...
def visit_Lambda(self, node: Lambda) -> Any: ...
def visit_IfExp(self, node: IfExp) -> Any: ...
def visit_Dict(self, node: Dict) -> Any: ...
def visit_Set(self, node: Set) -> Any: ...
def visit_ListComp(self, node: ListComp) -> Any: ...
def visit_SetComp(self, node: SetComp) -> Any: ...
def visit_DictComp(self, node: DictComp) -> Any: ...
def visit_GeneratorExp(self, node: GeneratorExp) -> Any: ...
def visit_Await(self, node: Await) -> Any: ...
def visit_Yield(self, node: Yield) -> Any: ...
def visit_YieldFrom(self, node: YieldFrom) -> Any: ...
def visit_Compare(self, node: Compare) -> Any: ...
def visit_Call(self, node: Call) -> Any: ...
def visit_FormattedValue(self, node: FormattedValue) -> Any: ...
def visit_JoinedStr(self, node: JoinedStr) -> Any: ...
def visit_Constant(self, node: Constant) -> Any: ...
def visit_NamedExpr(self, node: NamedExpr) -> Any: ...
def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ...
def visit_Attribute(self, node: Attribute) -> Any: ...
def visit_Subscript(self, node: Subscript) -> Any: ...
def visit_Starred(self, node: Starred) -> Any: ...
def visit_Name(self, node: Name) -> Any: ...
def visit_List(self, node: List) -> Any: ...
def visit_Tuple(self, node: Tuple) -> Any: ...
def visit_Del(self, node: Del) -> Any: ...
def visit_Load(self, node: Load) -> Any: ...
def visit_Store(self, node: Store) -> Any: ...
def visit_And(self, node: And) -> Any: ...
def visit_Or(self, node: Or) -> Any: ...
def visit_Add(self, node: Add) -> Any: ...
def visit_BitAnd(self, node: BitAnd) -> Any: ...
def visit_BitOr(self, node: BitOr) -> Any: ...
def visit_BitXor(self, node: BitXor) -> Any: ...
def visit_Div(self, node: Div) -> Any: ...
def visit_FloorDiv(self, node: FloorDiv) -> Any: ...
def visit_LShift(self, node: LShift) -> Any: ...
def visit_Mod(self, node: Mod) -> Any: ...
def visit_Mult(self, node: Mult) -> Any: ...
def visit_MatMult(self, node: MatMult) -> Any: ...
def visit_Pow(self, node: Pow) -> Any: ...
def visit_RShift(self, node: RShift) -> Any: ...
def visit_Sub(self, node: Sub) -> Any: ...
def visit_Invert(self, node: Invert) -> Any: ...
def visit_Not(self, node: Not) -> Any: ...
def visit_UAdd(self, node: UAdd) -> Any: ...
def visit_USub(self, node: USub) -> Any: ...
def visit_Eq(self, node: Eq) -> Any: ...
def visit_Gt(self, node: Gt) -> Any: ...
def visit_GtE(self, node: GtE) -> Any: ...
def visit_In(self, node: In) -> Any: ...
def visit_Is(self, node: Is) -> Any: ...
def visit_IsNot(self, node: IsNot) -> Any: ...
def visit_Lt(self, node: Lt) -> Any: ...
def visit_LtE(self, node: LtE) -> Any: ...
def visit_NotEq(self, node: NotEq) -> Any: ...
def visit_NotIn(self, node: NotIn) -> Any: ...
def visit_comprehension(self, node: comprehension) -> Any: ...
def visit_ExceptHandler(self, node: ExceptHandler) -> Any: ...
def visit_arguments(self, node: arguments) -> Any: ...
def visit_arg(self, node: arg) -> Any: ...
def visit_keyword(self, node: keyword) -> Any: ...
def visit_alias(self, node: alias) -> Any: ...
def visit_withitem(self, node: withitem) -> Any: ...
if sys.version_info >= (3, 10):
def visit_Match(self, node: Match) -> Any: ...
def visit_match_case(self, node: match_case) -> Any: ...
def visit_MatchValue(self, node: MatchValue) -> Any: ...
def visit_MatchSequence(self, node: MatchSequence) -> Any: ...
def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ...
def visit_MatchStar(self, node: MatchStar) -> Any: ...
def visit_MatchMapping(self, node: MatchMapping) -> Any: ...
def visit_MatchClass(self, node: MatchClass) -> Any: ...
def visit_MatchAs(self, node: MatchAs) -> Any: ...
def visit_MatchOr(self, node: MatchOr) -> Any: ...
if sys.version_info >= (3, 11):
def visit_TryStar(self, node: TryStar) -> Any: ...
if sys.version_info >= (3, 12):
def visit_TypeVar(self, node: TypeVar) -> Any: ...
def visit_ParamSpec(self, node: ParamSpec) -> Any: ...
def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ...
def visit_TypeAlias(self, node: TypeAlias) -> Any: ...
# visit methods for deprecated nodes
def visit_ExtSlice(self, node: ExtSlice) -> Any: ...
def visit_Index(self, node: Index) -> Any: ...
def visit_Suite(self, node: Suite) -> Any: ...
def visit_AugLoad(self, node: AugLoad) -> Any: ...
def visit_AugStore(self, node: AugStore) -> Any: ...
def visit_Param(self, node: Param) -> Any: ...
def visit_Num(self, node: Num) -> Any: ...
def visit_Str(self, node: Str) -> Any: ...
def visit_Bytes(self, node: Bytes) -> Any: ...
def visit_NameConstant(self, node: NameConstant) -> Any: ...
def visit_Ellipsis(self, node: Ellipsis) -> Any: ...
class NodeTransformer(NodeVisitor):
def generic_visit(self, node: AST) -> AST: ...
# TODO: Override the visit_* methods with better return types.
# The usual return type is AST | None, but Iterable[AST]
# is also allowed in some cases -- this needs to be mapped.
_T = _TypeVar("_T", bound=AST)
if sys.version_info >= (3, 13):
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: Literal["exec"] = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Module: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["eval"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["func_type"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["single"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["eval"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["func_type"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["single"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: str = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> AST: ...
else:
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: Literal["exec"] = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Module: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["eval"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["func_type"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["single"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["eval"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["func_type"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["single"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: str = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> AST: ...
if sys.version_info >= (3, 9):
def unparse(ast_obj: AST) -> str: ...
def copy_location(new_node: _T, old_node: AST) -> _T: ...
if sys.version_info >= (3, 13):
def dump(
node: AST,
annotate_fields: bool = True,
include_attributes: bool = False,
*,
indent: int | str | None = None,
show_empty: bool = False,
) -> str: ...
elif sys.version_info >= (3, 9):
def dump(
node: AST, annotate_fields: bool = True, include_attributes: bool = False, *, indent: int | str | None = None
) -> str: ...
else:
def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False) -> str: ...
def fix_missing_locations(node: _T) -> _T: ...
def get_docstring(node: AsyncFunctionDef | FunctionDef | ClassDef | Module, clean: bool = True) -> str | None: ...
def increment_lineno(node: _T, n: int = 1) -> _T: ...
def iter_child_nodes(node: AST) -> Iterator[AST]: ...
def iter_fields(node: AST) -> Iterator[tuple[str, Any]]: ...
def literal_eval(node_or_string: str | AST) -> Any: ...
def get_source_segment(source: str, node: AST, *, padded: bool = False) -> str | None: ...
def walk(node: AST) -> Iterator[AST]: ...
if sys.version_info >= (3, 9):
def main() -> None: ...
if sys.version_info >= (3, 14):
def compare(left: AST, right: AST, /, *, compare_attributes: bool = False) -> bool: ...

View File

@@ -1,41 +0,0 @@
import sys
from collections.abc import Awaitable, Coroutine, Generator
from typing import Any, TypeVar
from typing_extensions import TypeAlias
# As at runtime, this depends on all submodules defining __all__ accurately.
from .base_events import *
from .coroutines import *
from .events import *
from .exceptions import *
from .futures import *
from .locks import *
from .protocols import *
from .queues import *
from .runners import *
from .streams import *
from .subprocess import *
from .tasks import *
from .transports import *
if sys.version_info >= (3, 9):
from .threads import *
if sys.version_info >= (3, 11):
from .taskgroups import *
from .timeouts import *
if sys.platform == "win32":
from .windows_events import *
else:
from .unix_events import *
_T_co = TypeVar("_T_co", covariant=True)
# Aliases imported by multiple submodules in typeshed
if sys.version_info >= (3, 12):
_AwaitableLike: TypeAlias = Awaitable[_T_co] # noqa: Y047
_CoroutineLike: TypeAlias = Coroutine[Any, Any, _T_co] # noqa: Y047
else:
_AwaitableLike: TypeAlias = Generator[Any, None, _T_co] | Awaitable[_T_co]
_CoroutineLike: TypeAlias = Generator[Any, None, _T_co] | Coroutine[Any, Any, _T_co]

View File

@@ -1,20 +0,0 @@
import enum
import sys
from typing import Literal
LOG_THRESHOLD_FOR_CONNLOST_WRITES: Literal[5]
ACCEPT_RETRY_DELAY: Literal[1]
DEBUG_STACK_DEPTH: Literal[10]
SSL_HANDSHAKE_TIMEOUT: float
SENDFILE_FALLBACK_READBUFFER_SIZE: Literal[262144]
if sys.version_info >= (3, 11):
SSL_SHUTDOWN_TIMEOUT: float
FLOW_CONTROL_HIGH_WATER_SSL_READ: Literal[256]
FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Literal[512]
if sys.version_info >= (3, 12):
THREAD_JOIN_TIMEOUT: Literal[300]
class _SendfileMode(enum.Enum):
UNSUPPORTED = 1
TRY_NATIVE = 2
FALLBACK = 3

View File

@@ -1,20 +0,0 @@
import functools
import traceback
from collections.abc import Iterable
from types import FrameType, FunctionType
from typing import Any, overload
from typing_extensions import TypeAlias
class _HasWrapper:
__wrapper__: _HasWrapper | FunctionType
_FuncType: TypeAlias = FunctionType | _HasWrapper | functools.partial[Any] | functools.partialmethod[Any]
@overload
def _get_function_source(func: _FuncType) -> tuple[str, int]: ...
@overload
def _get_function_source(func: object) -> tuple[str, int] | None: ...
def _format_callback_source(func: object, args: Iterable[Any]) -> str: ...
def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any]) -> str: ...
def _format_callback(func: object, args: Iterable[Any], kwargs: dict[str, Any], suffix: str = "") -> str: ...
def extract_stack(f: FrameType | None = None, limit: int | None = None) -> traceback.StackSummary: ...

View File

@@ -1,57 +0,0 @@
import sys
from collections.abc import Awaitable, Callable, Generator, Iterable
from concurrent.futures._base import Future as _ConcurrentFuture
from contextvars import Context
from typing import Any, Literal, TypeVar
from typing_extensions import Self, TypeIs
from .events import AbstractEventLoop
if sys.version_info >= (3, 9):
from types import GenericAlias
__all__ = ("Future", "wrap_future", "isfuture")
_T = TypeVar("_T")
# asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py
# but it leads to circular import error in pytype tool.
# That's why the import order is reversed.
def isfuture(obj: object) -> TypeIs[Future[Any]]: ...
class Future(Awaitable[_T], Iterable[_T]):
_state: str
@property
def _exception(self) -> BaseException | None: ...
_blocking: bool
@property
def _log_traceback(self) -> bool: ...
@_log_traceback.setter
def _log_traceback(self, val: Literal[False]) -> None: ...
_asyncio_future_blocking: bool # is a part of duck-typing contract for `Future`
def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ...
def __del__(self) -> None: ...
def get_loop(self) -> AbstractEventLoop: ...
@property
def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ...
def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ...
if sys.version_info >= (3, 9):
def cancel(self, msg: Any | None = None) -> bool: ...
else:
def cancel(self) -> bool: ...
def cancelled(self) -> bool: ...
def done(self) -> bool: ...
def result(self) -> _T: ...
def exception(self) -> BaseException | None: ...
def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ...
def set_result(self, result: _T, /) -> None: ...
def set_exception(self, exception: type | BaseException, /) -> None: ...
def __iter__(self) -> Generator[Any, None, _T]: ...
def __await__(self) -> Generator[Any, None, _T]: ...
@property
def _loop(self) -> AbstractEventLoop: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ...

View File

@@ -1,196 +0,0 @@
import sys
import types
from abc import ABCMeta, abstractmethod
from collections.abc import Callable
from typing import Literal
from typing_extensions import Self, TypeVarTuple, Unpack, deprecated
from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy
from .selector_events import BaseSelectorEventLoop
_Ts = TypeVarTuple("_Ts")
# This is also technically not available on Win,
# but other parts of typeshed need this definition.
# So, it is special cased.
if sys.version_info >= (3, 12):
@deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
class AbstractChildWatcher:
@abstractmethod
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
@abstractmethod
def remove_child_handler(self, pid: int) -> bool: ...
@abstractmethod
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
@abstractmethod
def close(self) -> None: ...
@abstractmethod
def __enter__(self) -> Self: ...
@abstractmethod
def __exit__(
self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None
) -> None: ...
@abstractmethod
def is_active(self) -> bool: ...
else:
class AbstractChildWatcher:
@abstractmethod
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
@abstractmethod
def remove_child_handler(self, pid: int) -> bool: ...
@abstractmethod
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
@abstractmethod
def close(self) -> None: ...
@abstractmethod
def __enter__(self) -> Self: ...
@abstractmethod
def __exit__(
self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None
) -> None: ...
@abstractmethod
def is_active(self) -> bool: ...
if sys.platform != "win32":
if sys.version_info >= (3, 9):
__all__ = (
"SelectorEventLoop",
"AbstractChildWatcher",
"SafeChildWatcher",
"FastChildWatcher",
"PidfdChildWatcher",
"MultiLoopChildWatcher",
"ThreadedChildWatcher",
"DefaultEventLoopPolicy",
)
else:
__all__ = (
"SelectorEventLoop",
"AbstractChildWatcher",
"SafeChildWatcher",
"FastChildWatcher",
"MultiLoopChildWatcher",
"ThreadedChildWatcher",
"DefaultEventLoopPolicy",
)
# Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub.
# See discussion in #7412
class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta):
def close(self) -> None: ...
def is_active(self) -> bool: ...
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
if sys.version_info >= (3, 12):
@deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
class SafeChildWatcher(BaseChildWatcher):
def __enter__(self) -> Self: ...
def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
@deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
class FastChildWatcher(BaseChildWatcher):
def __enter__(self) -> Self: ...
def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
else:
class SafeChildWatcher(BaseChildWatcher):
def __enter__(self) -> Self: ...
def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
class FastChildWatcher(BaseChildWatcher):
def __enter__(self) -> Self: ...
def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
class _UnixSelectorEventLoop(BaseSelectorEventLoop): ...
class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy):
if sys.version_info >= (3, 12):
@deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
def get_child_watcher(self) -> AbstractChildWatcher: ...
@deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ...
else:
def get_child_watcher(self) -> AbstractChildWatcher: ...
def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ...
SelectorEventLoop = _UnixSelectorEventLoop
DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
if sys.version_info >= (3, 12):
@deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
class MultiLoopChildWatcher(AbstractChildWatcher):
def is_active(self) -> bool: ...
def close(self) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None
) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
else:
class MultiLoopChildWatcher(AbstractChildWatcher):
def is_active(self) -> bool: ...
def close(self) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None
) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
class ThreadedChildWatcher(AbstractChildWatcher):
def is_active(self) -> Literal[True]: ...
def close(self) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None
) -> None: ...
def __del__(self) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
if sys.version_info >= (3, 9):
class PidfdChildWatcher(AbstractChildWatcher):
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None
) -> None: ...
def is_active(self) -> bool: ...
def close(self) -> None: ...
def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
def add_child_handler(
self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
) -> None: ...
def remove_child_handler(self, pid: int) -> bool: ...

View File

@@ -1,32 +0,0 @@
from ._base import (
ALL_COMPLETED as ALL_COMPLETED,
FIRST_COMPLETED as FIRST_COMPLETED,
FIRST_EXCEPTION as FIRST_EXCEPTION,
BrokenExecutor as BrokenExecutor,
CancelledError as CancelledError,
Executor as Executor,
Future as Future,
InvalidStateError as InvalidStateError,
TimeoutError as TimeoutError,
as_completed as as_completed,
wait as wait,
)
from .process import ProcessPoolExecutor as ProcessPoolExecutor
from .thread import ThreadPoolExecutor as ThreadPoolExecutor
__all__ = (
"FIRST_COMPLETED",
"FIRST_EXCEPTION",
"ALL_COMPLETED",
"CancelledError",
"TimeoutError",
"BrokenExecutor",
"Future",
"Executor",
"wait",
"as_completed",
"ProcessPoolExecutor",
"ThreadPoolExecutor",
)
def __dir__() -> tuple[str, ...]: ...

View File

@@ -1,16 +0,0 @@
from typing import Any, TypeVar
__all__ = ["Error", "copy", "deepcopy"]
_T = TypeVar("_T")
# None in CPython but non-None in Jython
PyStringMap: Any
# Note: memo and _nil are internal kwargs.
def deepcopy(x: _T, memo: dict[int, Any] | None = None, _nil: Any = []) -> _T: ...
def copy(x: _T) -> _T: ...
class Error(Exception): ...
error = Error

View File

@@ -1,12 +0,0 @@
import sys
if sys.platform != "win32":
class _Method: ...
METHOD_CRYPT: _Method
METHOD_MD5: _Method
METHOD_SHA256: _Method
METHOD_SHA512: _Method
METHOD_BLOWFISH: _Method
methods: list[_Method]
def mksalt(method: _Method | None = None, *, rounds: int | None = None) -> str: ...
def crypt(word: str, salt: str | _Method | None = None) -> str: ...

View File

@@ -1,22 +0,0 @@
from _curses import *
from _curses import _CursesWindow as _CursesWindow
from collections.abc import Callable
from typing import TypeVar
from typing_extensions import Concatenate, ParamSpec
# NOTE: The _curses module is ordinarily only available on Unix, but the
# windows-curses package makes it available on Windows as well with the same
# contents.
_T = TypeVar("_T")
_P = ParamSpec("_P")
# available after calling `curses.initscr()`
LINES: int
COLS: int
# available after calling `curses.start_color()`
COLORS: int
COLOR_PAIRS: int
def wrapper(func: Callable[Concatenate[_CursesWindow, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ...

View File

@@ -1,22 +0,0 @@
from _curses import _CursesWindow
version: str
class _Curses_Panel: # type is <class '_curses_panel.curses panel'> (note the space in the class name)
def above(self) -> _Curses_Panel: ...
def below(self) -> _Curses_Panel: ...
def bottom(self) -> None: ...
def hidden(self) -> bool: ...
def hide(self) -> None: ...
def move(self, y: int, x: int) -> None: ...
def replace(self, win: _CursesWindow) -> None: ...
def set_userptr(self, obj: object) -> None: ...
def show(self) -> None: ...
def top(self) -> None: ...
def userptr(self) -> object: ...
def window(self) -> _CursesWindow: ...
def bottom_panel() -> _Curses_Panel: ...
def new_panel(win: _CursesWindow, /) -> _Curses_Panel: ...
def top_panel() -> _Curses_Panel: ...
def update_panels() -> _Curses_Panel: ...

View File

@@ -1,2 +0,0 @@
from _decimal import *
from _decimal import __libmpdec_version__ as __libmpdec_version__, __version__ as __version__

View File

@@ -1,99 +0,0 @@
from _typeshed import BytesPath, Incomplete, StrOrBytesPath, StrPath, Unused
from abc import abstractmethod
from collections.abc import Callable, Iterable
from distutils.dist import Distribution
from distutils.file_util import _BytesPathT, _StrPathT
from typing import Any, ClassVar, Literal, overload
class Command:
distribution: Distribution
# Any to work around variance issues
sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]]
def __init__(self, dist: Distribution) -> None: ...
@abstractmethod
def initialize_options(self) -> None: ...
@abstractmethod
def finalize_options(self) -> None: ...
@abstractmethod
def run(self) -> None: ...
def announce(self, msg: str, level: int = 1) -> None: ...
def debug_print(self, msg: str) -> None: ...
def ensure_string(self, option: str, default: str | None = None) -> None: ...
def ensure_string_list(self, option: str | list[str]) -> None: ...
def ensure_filename(self, option: str) -> None: ...
def ensure_dirname(self, option: str) -> None: ...
def get_command_name(self) -> str: ...
def set_undefined_options(self, src_cmd: str, *option_pairs: tuple[str, str]) -> None: ...
def get_finalized_command(self, command: str, create: bool | Literal[0, 1] = 1) -> Command: ...
def reinitialize_command(self, command: Command | str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ...
def run_command(self, command: str) -> None: ...
def get_sub_commands(self) -> list[str]: ...
def warn(self, msg: str) -> None: ...
def execute(self, func: Callable[..., object], args: Iterable[Any], msg: str | None = None, level: int = 1) -> None: ...
def mkpath(self, name: str, mode: int = 0o777) -> None: ...
@overload
def copy_file(
self,
infile: StrPath,
outfile: _StrPathT,
preserve_mode: bool | Literal[0, 1] = 1,
preserve_times: bool | Literal[0, 1] = 1,
link: str | None = None,
level: Unused = 1,
) -> tuple[_StrPathT | str, bool]: ...
@overload
def copy_file(
self,
infile: BytesPath,
outfile: _BytesPathT,
preserve_mode: bool | Literal[0, 1] = 1,
preserve_times: bool | Literal[0, 1] = 1,
link: str | None = None,
level: Unused = 1,
) -> tuple[_BytesPathT | bytes, bool]: ...
def copy_tree(
self,
infile: StrPath,
outfile: str,
preserve_mode: bool | Literal[0, 1] = 1,
preserve_times: bool | Literal[0, 1] = 1,
preserve_symlinks: bool | Literal[0, 1] = 0,
level: Unused = 1,
) -> list[str]: ...
@overload
def move_file(self, src: StrPath, dst: _StrPathT, level: Unused = 1) -> _StrPathT | str: ...
@overload
def move_file(self, src: BytesPath, dst: _BytesPathT, level: Unused = 1) -> _BytesPathT | bytes: ...
def spawn(self, cmd: Iterable[str], search_path: bool | Literal[0, 1] = 1, level: Unused = 1) -> None: ...
@overload
def make_archive(
self,
base_name: str,
format: str,
root_dir: StrOrBytesPath | None = None,
base_dir: str | None = None,
owner: str | None = None,
group: str | None = None,
) -> str: ...
@overload
def make_archive(
self,
base_name: StrPath,
format: str,
root_dir: StrOrBytesPath,
base_dir: str | None = None,
owner: str | None = None,
group: str | None = None,
) -> str: ...
def make_file(
self,
infiles: str | list[str] | tuple[str, ...],
outfile: StrOrBytesPath,
func: Callable[..., object],
args: list[Any],
exec_msg: str | None = None,
skip_msg: str | None = None,
level: Unused = 1,
) -> None: ...
def ensure_finalized(self) -> None: ...
def dump_options(self, header: Incomplete | None = None, indent: str = "") -> None: ...

View File

@@ -1,25 +0,0 @@
from typing import Any
from ..cmd import Command
def show_formats() -> None: ...
class bdist(Command):
description: str
user_options: Any
boolean_options: Any
help_options: Any
no_format_option: Any
default_format: Any
format_commands: Any
format_command: Any
bdist_base: Any
plat_name: Any
formats: Any
dist_dir: Any
skip_build: int
group: Any
owner: Any
def initialize_options(self) -> None: ...
def finalize_options(self) -> None: ...
def run(self) -> None: ...

View File

@@ -1,21 +0,0 @@
from typing import Any
from ..cmd import Command
class bdist_dumb(Command):
description: str
user_options: Any
boolean_options: Any
default_format: Any
bdist_dir: Any
plat_name: Any
format: Any
keep_temp: int
dist_dir: Any
skip_build: Any
relative: int
owner: Any
group: Any
def initialize_options(self) -> None: ...
def finalize_options(self) -> None: ...
def run(self) -> None: ...

View File

@@ -1,52 +0,0 @@
from typing import Any
from ..cmd import Command
class bdist_rpm(Command):
description: str
user_options: Any
boolean_options: Any
negative_opt: Any
bdist_base: Any
rpm_base: Any
dist_dir: Any
python: Any
fix_python: Any
spec_only: Any
binary_only: Any
source_only: Any
use_bzip2: Any
distribution_name: Any
group: Any
release: Any
serial: Any
vendor: Any
packager: Any
doc_files: Any
changelog: Any
icon: Any
prep_script: Any
build_script: Any
install_script: Any
clean_script: Any
verify_script: Any
pre_install: Any
post_install: Any
pre_uninstall: Any
post_uninstall: Any
prep: Any
provides: Any
requires: Any
conflicts: Any
build_requires: Any
obsoletes: Any
keep_temp: int
use_rpm_opt_flags: int
rpm3_mode: int
no_autoreq: int
force_arch: Any
quiet: int
def initialize_options(self) -> None: ...
def finalize_options(self) -> None: ...
def finalize_package_data(self) -> None: ...
def run(self) -> None: ...

View File

@@ -1,27 +0,0 @@
from typing import Any
from ..cmd import Command
def show_compilers() -> None: ...
class build_clib(Command):
description: str
user_options: Any
boolean_options: Any
help_options: Any
build_clib: Any
build_temp: Any
libraries: Any
include_dirs: Any
define: Any
undef: Any
debug: Any
force: int
compiler: Any
def initialize_options(self) -> None: ...
def finalize_options(self) -> None: ...
def run(self) -> None: ...
def check_library_list(self, libraries) -> None: ...
def get_library_names(self): ...
def get_source_files(self): ...
def build_libraries(self, libraries) -> None: ...

View File

@@ -1,50 +0,0 @@
from typing import Any
from ..cmd import Command
extension_name_re: Any
def show_compilers() -> None: ...
class build_ext(Command):
description: str
sep_by: Any
user_options: Any
boolean_options: Any
help_options: Any
extensions: Any
build_lib: Any
plat_name: Any
build_temp: Any
inplace: int
package: Any
include_dirs: Any
define: Any
undef: Any
libraries: Any
library_dirs: Any
rpath: Any
link_objects: Any
debug: Any
force: Any
compiler: Any
swig: Any
swig_cpp: Any
swig_opts: Any
user: Any
parallel: Any
def initialize_options(self) -> None: ...
def finalize_options(self) -> None: ...
def run(self) -> None: ...
def check_extensions_list(self, extensions) -> None: ...
def get_source_files(self): ...
def get_outputs(self): ...
def build_extensions(self) -> None: ...
def build_extension(self, ext) -> None: ...
def swig_sources(self, sources, extension): ...
def find_swig(self): ...
def get_ext_fullpath(self, ext_name: str) -> str: ...
def get_ext_fullname(self, ext_name: str) -> str: ...
def get_ext_filename(self, ext_name: str) -> str: ...
def get_export_symbols(self, ext): ...
def get_libraries(self, ext): ...

View File

@@ -1,17 +0,0 @@
from typing import Any
from ..cmd import Command
class clean(Command):
description: str
user_options: Any
boolean_options: Any
build_base: Any
build_lib: Any
build_temp: Any
build_scripts: Any
bdist_base: Any
all: Any
def initialize_options(self) -> None: ...
def finalize_options(self) -> None: ...
def run(self) -> None: ...

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