Skip to content

Commit 01a5b3a

Browse files
committed
Merge branch 'main' into dcreager/two-phase-binding
* main: (26 commits) Use the common `OperatorPrecedence` for the parser (#16747) [red-knot] Check subtype relation between callable types (#16804) [red-knot] Check whether two callable types are equivalent (#16698) [red-knot] Ban most `Type::Instance` types in type expressions (#16872) Special-case value-expression inference of special form subscriptions (#16877) [syntax-errors] Fix star annotation before Python 3.11 (#16878) Recognize `SyntaxError:` as an error code for ecosystem checks (#16879) [red-knot] add test cases result in false positive errors (#16856) Bump 0.11.1 (#16871) Allow discovery of venv in VIRTUAL_ENV env variable (#16853) Split git pathspecs in change determination onto separate lines (#16869) Use the correct base commit for change determination (#16857) Separate `BitXorOr` into `BitXor` and `BitOr` precedence (#16844) Server: Allow `FixAll` action in presence of version-specific syntax errors (#16848) [`refurb`] Fix starred expressions fix (`FURB161`) (#16550) [`flake8-executable`] Add pytest and uv run to help message for `shebang-missing-python` (`EXE003`) (#16855) Show more precise messages in invalid type expressions (#16850) [`flake8-executables`] Allow `uv run` in shebang line for `shebang-missing-python` (`EXE003`) (#16849) Add `--exit-non-zero-on-format` (#16009) [red-knot] Ban list literals in most contexts in type expressions (#16847) ...
2 parents 2794b03 + 2a4d835 commit 01a5b3a

File tree

78 files changed

+2893
-819
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+2893
-819
lines changed

.github/workflows/ci.yaml

+73-6
Original file line numberDiff line numberDiff line change
@@ -45,55 +45,122 @@ jobs:
4545
fetch-depth: 0
4646
persist-credentials: false
4747

48+
- name: Determine merge base
49+
id: merge_base
50+
env:
51+
BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }}
52+
run: |
53+
sha=$(git merge-base HEAD "origin/${BASE_REF}")
54+
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
55+
4856
- name: Check if the parser code changed
4957
id: check_parser
58+
env:
59+
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
5060
run: |
51-
if git diff --quiet ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD -- ':Cargo.toml' ':Cargo.lock' ':crates/ruff_python_trivia/**' ':crates/ruff_source_file/**' ':crates/ruff_text_size/**' ':crates/ruff_python_ast/**' ':crates/ruff_python_parser/**' ':python/py-fuzzer/**' ':.github/workflows/ci.yaml'; then
61+
if git diff --quiet "${MERGE_BASE}...HEAD" -- \
62+
':Cargo.toml' \
63+
':Cargo.lock' \
64+
':crates/ruff_python_trivia/**' \
65+
':crates/ruff_source_file/**' \
66+
':crates/ruff_text_size/**' \
67+
':crates/ruff_python_ast/**' \
68+
':crates/ruff_python_parser/**' \
69+
':python/py-fuzzer/**' \
70+
':.github/workflows/ci.yaml' \
71+
; then
5272
echo "changed=false" >> "$GITHUB_OUTPUT"
5373
else
5474
echo "changed=true" >> "$GITHUB_OUTPUT"
5575
fi
5676
5777
- name: Check if the linter code changed
5878
id: check_linter
79+
env:
80+
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
5981
run: |
60-
if git diff --quiet ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD -- ':Cargo.toml' ':Cargo.lock' ':crates/**' ':!crates/red_knot*/**' ':!crates/ruff_python_formatter/**' ':!crates/ruff_formatter/**' ':!crates/ruff_dev/**' ':!crates/ruff_db/**' ':scripts/*' ':python/**' ':.github/workflows/ci.yaml'; then
82+
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \
83+
':Cargo.lock' \
84+
':crates/**' \
85+
':!crates/red_knot*/**' \
86+
':!crates/ruff_python_formatter/**' \
87+
':!crates/ruff_formatter/**' \
88+
':!crates/ruff_dev/**' \
89+
':!crates/ruff_db/**' \
90+
':scripts/*' \
91+
':python/**' \
92+
':.github/workflows/ci.yaml' \
93+
; then
6194
echo "changed=false" >> "$GITHUB_OUTPUT"
6295
else
6396
echo "changed=true" >> "$GITHUB_OUTPUT"
6497
fi
6598
6699
- name: Check if the formatter code changed
67100
id: check_formatter
101+
env:
102+
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
68103
run: |
69-
if git diff --quiet ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD -- ':Cargo.toml' ':Cargo.lock' ':crates/ruff_python_formatter/**' ':crates/ruff_formatter/**' ':crates/ruff_python_trivia/**' ':crates/ruff_python_ast/**' ':crates/ruff_source_file/**' ':crates/ruff_python_index/**' ':crates/ruff_python_index/**' ':crates/ruff_text_size/**' ':crates/ruff_python_parser/**' ':scripts/*' ':python/**' ':.github/workflows/ci.yaml'; then
104+
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \
105+
':Cargo.lock' \
106+
':crates/ruff_python_formatter/**' \
107+
':crates/ruff_formatter/**' \
108+
':crates/ruff_python_trivia/**' \
109+
':crates/ruff_python_ast/**' \
110+
':crates/ruff_source_file/**' \
111+
':crates/ruff_python_index/**' \
112+
':crates/ruff_python_index/**' \
113+
':crates/ruff_text_size/**' \
114+
':crates/ruff_python_parser/**' \
115+
':scripts/*' \
116+
':python/**' \
117+
':.github/workflows/ci.yaml' \
118+
; then
70119
echo "changed=false" >> "$GITHUB_OUTPUT"
71120
else
72121
echo "changed=true" >> "$GITHUB_OUTPUT"
73122
fi
74123
75124
- name: Check if the fuzzer code changed
76125
id: check_fuzzer
126+
env:
127+
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
77128
run: |
78-
if git diff --quiet ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD -- ':Cargo.toml' ':Cargo.lock' ':fuzz/fuzz_targets/**' ':.github/workflows/ci.yaml'; then
129+
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \
130+
':Cargo.lock' \
131+
':fuzz/fuzz_targets/**' \
132+
':.github/workflows/ci.yaml' \
133+
; then
79134
echo "changed=false" >> "$GITHUB_OUTPUT"
80135
else
81136
echo "changed=true" >> "$GITHUB_OUTPUT"
82137
fi
83138
84139
- name: Check if there was any code related change
85140
id: check_code
141+
env:
142+
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
86143
run: |
87-
if git diff --quiet ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD -- ':**/*' ':!**/*.md' ':crates/red_knot_python_semantic/resources/mdtest/**/*.md' ':!docs/**' ':!assets/**' ':.github/workflows/ci.yaml'; then
144+
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':**/*' \
145+
':!**/*.md' \
146+
':crates/red_knot_python_semantic/resources/mdtest/**/*.md' \
147+
':!docs/**' \
148+
':!assets/**' \
149+
':.github/workflows/ci.yaml' \
150+
; then
88151
echo "changed=false" >> "$GITHUB_OUTPUT"
89152
else
90153
echo "changed=true" >> "$GITHUB_OUTPUT"
91154
fi
92155
93156
- name: Check if there was any playground related change
94157
id: check_playground
158+
env:
159+
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
95160
run: |
96-
if git diff --quiet ${{ github.event.pull_request.base.sha || 'origin/main' }}...HEAD -- ':playground/**'; then
161+
if git diff --quiet "${MERGE_BASE}...HEAD" -- \
162+
':playground/**' \
163+
; then
97164
echo "changed=false" >> "$GITHUB_OUTPUT"
98165
else
99166
echo "changed=true" >> "$GITHUB_OUTPUT"

.github/workflows/release.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ on:
5050
jobs:
5151
# Run 'dist plan' (or host) to determine what tasks we need to do
5252
plan:
53-
runs-on: "ubuntu-20.04"
53+
runs-on: "depot-ubuntu-latest-4"
5454
outputs:
5555
val: ${{ steps.plan.outputs.manifest }}
5656
tag: ${{ (inputs.tag != 'dry-run' && inputs.tag) || '' }}
@@ -116,7 +116,7 @@ jobs:
116116
- plan
117117
- custom-build-binaries
118118
- custom-build-docker
119-
runs-on: "ubuntu-20.04"
119+
runs-on: "depot-ubuntu-latest-4"
120120
env:
121121
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122122
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
@@ -167,7 +167,7 @@ jobs:
167167
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }}
168168
env:
169169
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
170-
runs-on: "ubuntu-20.04"
170+
runs-on: "depot-ubuntu-latest-4"
171171
outputs:
172172
val: ${{ steps.host.outputs.manifest }}
173173
steps:
@@ -242,7 +242,7 @@ jobs:
242242
# still allowing individual publish jobs to skip themselves (for prereleases).
243243
# "host" however must run to completion, no skipping allowed!
244244
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') }}
245-
runs-on: "ubuntu-20.04"
245+
runs-on: "depot-ubuntu-latest-4"
246246
env:
247247
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
248248
steps:

CHANGELOG.md

+37-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
# Changelog
22

3+
## 0.11.1
4+
5+
### Preview features
6+
7+
- \[`airflow`\] Add `chain`, `chain_linear` and `cross_downstream` for `AIR302` ([#16647](https://github.com/astral-sh/ruff/pull/16647))
8+
- [syntax-errors] Improve error message and range for pre-PEP-614 decorator syntax errors ([#16581](https://github.com/astral-sh/ruff/pull/16581))
9+
- [syntax-errors] PEP 701 f-strings before Python 3.12 ([#16543](https://github.com/astral-sh/ruff/pull/16543))
10+
- [syntax-errors] Parenthesized context managers before Python 3.9 ([#16523](https://github.com/astral-sh/ruff/pull/16523))
11+
- [syntax-errors] Star annotations before Python 3.11 ([#16545](https://github.com/astral-sh/ruff/pull/16545))
12+
- [syntax-errors] Star expression in index before Python 3.11 ([#16544](https://github.com/astral-sh/ruff/pull/16544))
13+
- [syntax-errors] Unparenthesized assignment expressions in sets and indexes ([#16404](https://github.com/astral-sh/ruff/pull/16404))
14+
15+
### Bug fixes
16+
17+
- Server: Allow `FixAll` action in presence of version-specific syntax errors ([#16848](https://github.com/astral-sh/ruff/pull/16848))
18+
- \[`flake8-bandit`\] Allow raw strings in `suspicious-mark-safe-usage` (`S308`) #16702 ([#16770](https://github.com/astral-sh/ruff/pull/16770))
19+
- \[`refurb`\] Avoid panicking `unwrap` in `verbose-decimal-constructor` (`FURB157`) ([#16777](https://github.com/astral-sh/ruff/pull/16777))
20+
- \[`refurb`\] Fix starred expressions fix (`FURB161`) ([#16550](https://github.com/astral-sh/ruff/pull/16550))
21+
- Fix `--statistics` reporting for unsafe fixes ([#16756](https://github.com/astral-sh/ruff/pull/16756))
22+
23+
### Rule changes
24+
25+
- \[`flake8-executables`\] Allow `uv run` in shebang line for `shebang-missing-python` (`EXE003`) ([#16849](https://github.com/astral-sh/ruff/pull/16849),[#16855](https://github.com/astral-sh/ruff/pull/16855))
26+
27+
### CLI
28+
29+
- Add `--exit-non-zero-on-format` ([#16009](https://github.com/astral-sh/ruff/pull/16009))
30+
31+
### Documentation
32+
33+
- Update Ruff tutorial to avoid non-existent fix in `__init__.py` ([#16818](https://github.com/astral-sh/ruff/pull/16818))
34+
- \[`flake8-gettext`\] Swap `format-` and `printf-in-get-text-func-call` examples (`INT002`, `INT003`) ([#16769](https://github.com/astral-sh/ruff/pull/16769))
35+
336
## 0.11.0
437

538
This is a follow-up to release 0.10.0. Because of a mistake in the release process, the `requires-python` inference changes were not included in that release. Ruff 0.11.0 now includes this change as well as the stabilization of the preview behavior for `PGH004`.
@@ -56,7 +89,7 @@ See also, the "Remapped rules" section which may result in disabled rules.
5689

5790
- **More robust noqa parsing** ([#16483](https://github.com/astral-sh/ruff/pull/16483))
5891

59-
The syntax for both file-level and in-line suppression comments has been unified and made more robust to certain errors. In most cases, this will result in more suppression comments being read by Ruff, but there are a few instances where previously read comments will now log an error to the user instead. Please refer to the documentation on [_Error suppression_](https://docs.astral.sh/ruff/linter/#error-suppression) for the full specification.
92+
The syntax for both file-level and in-line suppression comments has been unified and made more robust to certain errors. In most cases, this will result in more suppression comments being read by Ruff, but there are a few instances where previously read comments will now log an error to the user instead. Please refer to the documentation on [*Error suppression*](https://docs.astral.sh/ruff/linter/#error-suppression) for the full specification.
6093

6194
- **Avoid unnecessary parentheses around with statements with a single context manager and a trailing comment** ([#14005](https://github.com/astral-sh/ruff/pull/14005))
6295

@@ -1468,7 +1501,7 @@ The following fixes have been stabilized:
14681501

14691502
## 0.5.6
14701503

1471-
Ruff 0.5.6 automatically enables linting and formatting of notebooks in _preview mode_.
1504+
Ruff 0.5.6 automatically enables linting and formatting of notebooks in *preview mode*.
14721505
You can opt-out of this behavior by adding `*.ipynb` to the `extend-exclude` setting.
14731506

14741507
```toml
@@ -2221,7 +2254,7 @@ To setup `ruff server` with your editor, refer to the [README.md](https://github
22212254

22222255
### Server
22232256

2224-
_This section is devoted to updates for our new language server, written in Rust._
2257+
*This section is devoted to updates for our new language server, written in Rust.*
22252258

22262259
- Enable ruff-specific source actions ([#10916](https://github.com/astral-sh/ruff/pull/10916))
22272260
- Refreshes diagnostics for open files when file configuration is changed ([#10988](https://github.com/astral-sh/ruff/pull/10988))
@@ -3628,7 +3661,7 @@ Read Ruff's new [versioning policy](https://docs.astral.sh/ruff/versioning/).
36283661
- \[`refurb`\] Add `single-item-membership-test` (`FURB171`) ([#7815](https://github.com/astral-sh/ruff/pull/7815))
36293662
- \[`pylint`\] Add `and-or-ternary` (`R1706`) ([#7811](https://github.com/astral-sh/ruff/pull/7811))
36303663

3631-
_New rules are added in [preview](https://docs.astral.sh/ruff/preview/)._
3664+
*New rules are added in [preview](https://docs.astral.sh/ruff/preview/).*
36323665

36333666
### Configuration
36343667

Cargo.lock

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,9 @@ github-custom-job-permissions = { "build-docker" = { packages = "write", content
327327
install-updater = false
328328
# Path that installers should place binaries in
329329
install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"]
330+
# Temporarily allow changes to the `release` workflow, in which we pin actions
331+
# to a SHA instead of a tag (https://github.com/astral-sh/uv/issues/12253)
332+
allow-dirty = ["ci"]
333+
334+
[workspace.metadata.dist.github-custom-runners]
335+
global = "depot-ubuntu-latest-4"

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
149149
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
150150

151151
# For a specific version.
152-
curl -LsSf https://astral.sh/ruff/0.11.0/install.sh | sh
153-
powershell -c "irm https://astral.sh/ruff/0.11.0/install.ps1 | iex"
152+
curl -LsSf https://astral.sh/ruff/0.11.1/install.sh | sh
153+
powershell -c "irm https://astral.sh/ruff/0.11.1/install.ps1 | iex"
154154
```
155155

156156
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -183,7 +183,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
183183
```yaml
184184
- repo: https://github.com/astral-sh/ruff-pre-commit
185185
# Ruff version.
186-
rev: v0.11.0
186+
rev: v0.11.1
187187
hooks:
188188
# Run the linter.
189189
- id: ruff

crates/red_knot/src/args.rs

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub(crate) struct CheckCommand {
5050

5151
/// Path to the Python installation from which Red Knot resolves type information and third-party dependencies.
5252
///
53+
/// If not specified, Red Knot will look at the `VIRTUAL_ENV` environment variable.
54+
///
5355
/// Red Knot will search in the path's `site-packages` directories for type information and
5456
/// third-party imports.
5557
///

crates/red_knot_project/src/metadata/options.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,14 @@ impl Options {
106106
custom_typeshed: typeshed.map(|path| path.absolute(project_root, system)),
107107
python_path: python
108108
.map(|python_path| {
109-
PythonPath::SysPrefix(python_path.absolute(project_root, system))
109+
PythonPath::from_cli_flag(python_path.absolute(project_root, system))
110110
})
111-
.unwrap_or(PythonPath::KnownSitePackages(vec![])),
111+
.or_else(|| {
112+
std::env::var("VIRTUAL_ENV")
113+
.ok()
114+
.map(PythonPath::from_virtual_env_var)
115+
})
116+
.unwrap_or_else(|| PythonPath::KnownSitePackages(vec![])),
112117
}
113118
}
114119

crates/red_knot_python_semantic/resources/mdtest/annotations/annotated.md

+4-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ It is invalid to parameterize `Annotated` with less than two arguments.
2929
```py
3030
from typing_extensions import Annotated
3131

32-
# error: [invalid-type-form] "`Annotated` requires at least two arguments when used in an annotation or type expression"
32+
# error: [invalid-type-form] "`typing.Annotated` requires at least two arguments when used in a type expression"
3333
def _(x: Annotated):
3434
reveal_type(x) # revealed: Unknown
3535

@@ -39,11 +39,11 @@ def _(flag: bool):
3939
else:
4040
X = bool
4141

42-
# error: [invalid-type-form] "`Annotated` requires at least two arguments when used in an annotation or type expression"
42+
# error: [invalid-type-form] "`typing.Annotated` requires at least two arguments when used in a type expression"
4343
def f(y: X):
4444
reveal_type(y) # revealed: Unknown | bool
4545

46-
# error: [invalid-type-form] "`Annotated` requires at least two arguments when used in an annotation or type expression"
46+
# error: [invalid-type-form] "`typing.Annotated` requires at least two arguments when used in a type expression"
4747
def _(x: Annotated | bool):
4848
reveal_type(x) # revealed: Unknown | bool
4949

@@ -73,12 +73,10 @@ Inheriting from `Annotated[T, ...]` is equivalent to inheriting from `T` itself.
7373
```py
7474
from typing_extensions import Annotated
7575

76-
# TODO: False positive
77-
# error: [invalid-base]
7876
class C(Annotated[int, "foo"]): ...
7977

8078
# TODO: Should be `tuple[Literal[C], Literal[int], Literal[object]]`
81-
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Unknown, Literal[object]]
79+
reveal_type(C.__mro__) # revealed: tuple[Literal[C], @Todo(Inference of subscript on special form), Literal[object]]
8280
```
8381

8482
### Not parameterized

0 commit comments

Comments
 (0)