Compare commits
32 Commits
v0.0.274
...
charlie/do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fbec8e6a2 | ||
|
|
ac146e11f0 | ||
|
|
1229600e1d | ||
|
|
ccf34aae8c | ||
|
|
341b12d918 | ||
|
|
3d7411bfaf | ||
|
|
1eccbbb60e | ||
|
|
e71f044f0d | ||
|
|
f194572be8 | ||
|
|
62e2c46f98 | ||
|
|
9419d3f9c8 | ||
|
|
9b5fb8f38f | ||
|
|
d7c7484618 | ||
|
|
bc63cc9b3c | ||
|
|
bf1a94ee54 | ||
|
|
c792c10eaa | ||
|
|
f9ffb3d50d | ||
|
|
2b76d88bd3 | ||
|
|
41ef17b007 | ||
|
|
0aa21277c6 | ||
|
|
ecf61d49fa | ||
|
|
d99b3bf661 | ||
|
|
e47aa468d5 | ||
|
|
6155fd647d | ||
|
|
4634560c80 | ||
|
|
10885d09a1 | ||
|
|
44156f6962 | ||
|
|
f551c9aad2 | ||
|
|
653dbb6d17 | ||
|
|
db301c14bd | ||
|
|
1336ca601b | ||
|
|
3973836420 |
39
.github/workflows/release.yaml
vendored
39
.github/workflows/release.yaml
vendored
@@ -423,8 +423,8 @@ jobs:
|
||||
echo "Releasing ${git_sha}"
|
||||
fi
|
||||
|
||||
release:
|
||||
name: Release
|
||||
upload-release:
|
||||
name: Upload to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- macos-universal
|
||||
@@ -442,8 +442,6 @@ jobs:
|
||||
permissions:
|
||||
# For pypi trusted publishing
|
||||
id-token: write
|
||||
# For GitHub release publishing
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -455,10 +453,18 @@ jobs:
|
||||
skip-existing: true
|
||||
packages-dir: wheels
|
||||
verbose: true
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
|
||||
tag-release:
|
||||
name: Tag release
|
||||
runs-on: ubuntu-latest
|
||||
needs: upload-release
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
permissions:
|
||||
# For git tag
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: git tag
|
||||
run: |
|
||||
git config user.email "hey@astral.sh"
|
||||
@@ -467,10 +473,25 @@ jobs:
|
||||
# If there is duplicate tag, this will fail. The publish to pypi action will have been a noop (due to skip
|
||||
# existing), so we make a non-destructive exit here
|
||||
git push --tags
|
||||
|
||||
publish-release:
|
||||
name: Publish to GitHub
|
||||
runs-on: ubuntu-latest
|
||||
needs: tag-release
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
permissions:
|
||||
# For GitHub release publishing
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
draft: false
|
||||
files: binaries/*
|
||||
tag_name: v${{ inputs.tag }}
|
||||
|
||||
|
||||
131
CONTRIBUTING.md
131
CONTRIBUTING.md
@@ -12,7 +12,7 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
|
||||
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
|
||||
- [MkDocs](#mkdocs)
|
||||
- [Release Process](#release-process)
|
||||
- [Benchmarks](#benchmarks)
|
||||
- [Benchmarks](#benchmarking-and-profiling)
|
||||
|
||||
## The Basics
|
||||
|
||||
@@ -307,7 +307,15 @@ downloading the [`known-github-tomls.json`](https://github.com/akx/ruff-usage-ag
|
||||
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](https://github.com/astral-sh/ruff/blob/main/scripts/Dockerfile.ecosystem).
|
||||
Note that this check will take a while to run.
|
||||
|
||||
## Benchmarks
|
||||
## Benchmarking and Profiling
|
||||
|
||||
We have several ways of benchmarking and profiling Ruff:
|
||||
|
||||
- Our main performance benchmark comparing Ruff with other tools on the CPython codebase
|
||||
- Microbenchmarks which the linter or the formatter on individual files. There run on pull requests.
|
||||
- Profiling the linter on either the microbenchmarks or entire projects
|
||||
|
||||
### CPython Benchmark
|
||||
|
||||
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
|
||||
which makes it a good target for benchmarking.
|
||||
@@ -386,9 +394,9 @@ Summary
|
||||
159.43 ± 2.48 times faster than 'pycodestyle crates/ruff/resources/test/cpython'
|
||||
```
|
||||
|
||||
You can run `poetry install` from `./scripts` to create a working environment for the above. All
|
||||
reported benchmarks were computed using the versions specified by `./scripts/pyproject.toml`
|
||||
on Python 3.11.
|
||||
You can run `poetry install` from `./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.
|
||||
|
||||
To benchmark Pylint, remove the following files from the CPython repository:
|
||||
|
||||
@@ -429,3 +437,116 @@ Benchmark 1: find . -type f -name "*.py" | xargs -P 0 pyupgrade --py311-plus
|
||||
Time (mean ± σ): 30.119 s ± 0.195 s [User: 28.638 s, System: 0.390 s]
|
||||
Range (min … max): 29.813 s … 30.356 s 10 runs
|
||||
```
|
||||
|
||||
## Microbenchmarks
|
||||
|
||||
The `ruff_benchmark` crate benchmarks the linter and the formatter on individual files.
|
||||
|
||||
You can run the benchmarks with
|
||||
|
||||
```shell
|
||||
cargo benchmark
|
||||
```
|
||||
|
||||
### Benchmark driven Development
|
||||
|
||||
Ruff uses [Criterion.rs](https://bheisler.github.io/criterion.rs/book/) for benchmarks. You can use
|
||||
`--save-baseline=<name>` to store an initial baseline benchmark (e.g. on `main`) and then use
|
||||
`--benchmark=<name>` to compare against that benchmark. Criterion will print a message telling you
|
||||
if the benchmark improved/regressed compared to that baseline.
|
||||
|
||||
```shell
|
||||
# Run once on your "baseline" code
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# Then iterate with
|
||||
cargo benchmark --baseline=main
|
||||
```
|
||||
|
||||
### PR Summary
|
||||
|
||||
You can use `--save-baseline` and `critcmp` to get a pretty comparison between two recordings.
|
||||
This is useful to illustrate the improvements of a PR.
|
||||
|
||||
```shell
|
||||
# On main
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# After applying your changes
|
||||
cargo benchmark --save-baseline=pr
|
||||
|
||||
critcmp main pr
|
||||
```
|
||||
|
||||
You must install [`critcmp`](https://github.com/BurntSushi/critcmp) for the comparison.
|
||||
|
||||
```bash
|
||||
cargo install critcmp
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
- Use `cargo benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark linter/pydantic`
|
||||
to only run the pydantic tests.
|
||||
- Use `cargo benchmark --quiet` for a more cleaned up output (without statistical relevance)
|
||||
- Use `cargo benchmark --quick` to get faster results (more prone to noise)
|
||||
|
||||
## Profiling Projects
|
||||
|
||||
You can either use the microbenchmarks from above or a project directory for benchmarking. There
|
||||
are a lot of profiling tools out there,
|
||||
[The Rust Performance Book](https://nnethercote.github.io/perf-book/profiling.html) lists some
|
||||
examples.
|
||||
|
||||
### Linux
|
||||
|
||||
Install `perf` and build `ruff_benchmark` with the `release-debug` profile and then run it with perf
|
||||
|
||||
```shell
|
||||
cargo bench -p ruff_benchmark --no-run --profile=release-debug && perf record -g -F 9999 cargo bench -p ruff_benchmark --profile=release-debug -- --profile-time=1
|
||||
```
|
||||
|
||||
You can also use the `ruff_dev` launcher to run `ruff check` multiple times on a repository to
|
||||
gather enough samples for a good flamegraph (change the 999, the sample rate, and the 30, the number
|
||||
of checks, to your liking)
|
||||
|
||||
```shell
|
||||
cargo build --bin ruff_dev --profile=release-debug
|
||||
perf record -g -F 999 target/release-debug/ruff_dev repeat --repeat 30 --exit-zero --no-cache path/to/cpython > /dev/null
|
||||
```
|
||||
|
||||
Then convert the recorded profile
|
||||
|
||||
```shell
|
||||
perf script -F +pid > /tmp/test.perf
|
||||
```
|
||||
|
||||
You can now view the converted file with [firefox profiler](https://profiler.firefox.com/), with a
|
||||
more in-depth guide [here](https://profiler.firefox.com/docs/#/./guide-perf-profiling)
|
||||
|
||||
An alternative is to convert the perf data to `flamegraph.svg` using
|
||||
[flamegraph](https://github.com/flamegraph-rs/flamegraph) (`cargo install flamegraph`):
|
||||
|
||||
```shell
|
||||
flamegraph --perfdata perf.data
|
||||
```
|
||||
|
||||
### Mac
|
||||
|
||||
Install [`cargo-instruments`](https://crates.io/crates/cargo-instruments):
|
||||
|
||||
```shell
|
||||
cargo install cargo-instruments
|
||||
```
|
||||
|
||||
Then run the profiler with
|
||||
|
||||
```shell
|
||||
cargo instruments -t time --bench linter --profile release-debug -p ruff_benchmark -- --profile-time=1
|
||||
```
|
||||
|
||||
- `-t`: Specifies what to profile. Useful options are `time` to profile the wall time and `alloc`
|
||||
for profiling the allocations.
|
||||
- You may want to pass an additional filter to run a single test file
|
||||
|
||||
Otherwise, follow the instructions from the linux section.
|
||||
|
||||
@@ -14,9 +14,9 @@ An extremely fast Python linter, written in Rust.
|
||||
|
||||
<p align="center">
|
||||
<picture align="center">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1309177/212613422-7faaf278-706b-4294-ad92-236ffcab3430.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1309177/232603514-c95e9b0f-6b31-43de-9a80-9e844173fd6a.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1309177/232603516-4fb4892d-585c-4b20-b810-3db9161831e4.svg">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/232603516-4fb4892d-585c-4b20-b810-3db9161831e4.svg">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import typing
|
||||
from typing import ClassVar, Sequence
|
||||
from typing import ClassVar, Sequence, Final
|
||||
|
||||
KNOWINGLY_MUTABLE_DEFAULT = []
|
||||
|
||||
@@ -10,6 +10,7 @@ class A:
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: typing.ClassVar[list[int]] = []
|
||||
final_variable: typing.Final[list[int]] = []
|
||||
|
||||
|
||||
class B:
|
||||
@@ -18,6 +19,7 @@ class B:
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
@@ -31,3 +33,17 @@ class C:
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class D(BaseModel):
|
||||
mutable_default: list[int] = []
|
||||
immutable_annotation: Sequence[int] = []
|
||||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
@@ -18,19 +18,19 @@ def f(arg: object = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int = None): # RUF011
|
||||
def f(arg: int = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: str = None): # RUF011
|
||||
def f(arg: str = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.List[str] = None): # RUF011
|
||||
def f(arg: typing.List[str] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Tuple[str] = None): # RUF011
|
||||
def f(arg: Tuple[str] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
@@ -64,15 +64,15 @@ def f(arg: Union[int, str, Any] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union = None): # RUF011
|
||||
def f(arg: Union = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[int, str] = None): # RUF011
|
||||
def f(arg: Union[int, str] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.Union[int, str] = None): # RUF011
|
||||
def f(arg: typing.Union[int, str] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
@@ -91,11 +91,11 @@ def f(arg: int | float | str | None = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int | float = None): # RUF011
|
||||
def f(arg: int | float = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: int | float | str | bytes = None): # RUF011
|
||||
def f(arg: int | float | str | bytes = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
@@ -110,11 +110,11 @@ def f(arg: Literal[1, 2, None, 3] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Literal[1, "foo"] = None): # RUF011
|
||||
def f(arg: Literal[1, "foo"] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: typing.Literal[1, "foo", True] = None): # RUF011
|
||||
def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
@@ -133,11 +133,11 @@ def f(arg: Annotated[Any, ...] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Annotated[int, ...] = None): # RUF011
|
||||
def f(arg: Annotated[int, ...] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011
|
||||
def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
@@ -153,9 +153,9 @@ def f(
|
||||
|
||||
|
||||
def f(
|
||||
arg1: int = None, # RUF011
|
||||
arg2: Union[int, float] = None, # RUF011
|
||||
arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
arg1: int = None, # RUF013
|
||||
arg2: Union[int, float] = None, # RUF013
|
||||
arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -183,20 +183,41 @@ def f(arg: Union[Annotated[int, ...], Annotated[Optional[float], ...]] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011
|
||||
def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
# Quoted
|
||||
|
||||
|
||||
def f(arg: "int" = None):
|
||||
def f(arg: "int" = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: "str" = None):
|
||||
def f(arg: "str" = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: "st" "r" = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: "Optional[int]" = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union["int", "str"] = None): # RUF013
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union["int", "None"] = None):
|
||||
pass
|
||||
|
||||
|
||||
def f(arg: Union["No" "ne", "int"] = None):
|
||||
pass
|
||||
|
||||
|
||||
# Avoid flagging when there's a parse error in the forward reference
|
||||
def f(arg: Union["<>", "int"] = None):
|
||||
pass
|
||||
|
||||
@@ -3852,6 +3852,9 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
// Store the existing binding, if any.
|
||||
let existing_id = self.semantic.lookup(name);
|
||||
|
||||
// Add the bound exception name to the scope.
|
||||
let binding_id = self.add_binding(
|
||||
name,
|
||||
@@ -3862,14 +3865,6 @@ where
|
||||
|
||||
walk_except_handler(self, except_handler);
|
||||
|
||||
// Remove it from the scope immediately after.
|
||||
self.add_binding(
|
||||
name,
|
||||
range,
|
||||
BindingKind::UnboundException,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
|
||||
// If the exception name wasn't used in the scope, emit a diagnostic.
|
||||
if !self.semantic.is_used(binding_id) {
|
||||
if self.enabled(Rule::UnusedVariable) {
|
||||
@@ -3889,6 +3884,13 @@ where
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
self.add_binding(
|
||||
name,
|
||||
range,
|
||||
BindingKind::UnboundException(existing_id),
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
}
|
||||
None => walk_except_handler(self, except_handler),
|
||||
}
|
||||
@@ -4236,7 +4238,7 @@ impl<'a> Checker<'a> {
|
||||
let shadowed = &self.semantic.bindings[shadowed_id];
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Builtin | BindingKind::Deletion | BindingKind::UnboundException,
|
||||
BindingKind::Builtin | BindingKind::Deletion | BindingKind::UnboundException(_),
|
||||
) {
|
||||
let references = shadowed.references.clone();
|
||||
let is_global = shadowed.is_global();
|
||||
|
||||
@@ -157,7 +157,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
|
||||
// pylint
|
||||
(Pylint, "C0414") => (RuleGroup::Unspecified, rules::pylint::rules::UselessImportAlias),
|
||||
(Pylint, "C1901") => (RuleGroup::Unspecified, rules::pylint::rules::CompareToEmptyString),
|
||||
(Pylint, "C1901") => (RuleGroup::Nursery, rules::pylint::rules::CompareToEmptyString),
|
||||
(Pylint, "C3002") => (RuleGroup::Unspecified, rules::pylint::rules::UnnecessaryDirectLambdaCall),
|
||||
(Pylint, "C0208") => (RuleGroup::Unspecified, rules::pylint::rules::IterationOverSet),
|
||||
(Pylint, "E0100") => (RuleGroup::Unspecified, rules::pylint::rules::YieldInInit),
|
||||
|
||||
@@ -78,7 +78,7 @@ fn detect_package_root_with_cache<'a>(
|
||||
current
|
||||
}
|
||||
|
||||
/// Return a mapping from Python file to its package root.
|
||||
/// Return a mapping from Python package to its package root.
|
||||
pub fn detect_package_roots<'a>(
|
||||
files: &[&'a Path],
|
||||
resolver: &'a Resolver,
|
||||
|
||||
@@ -251,7 +251,7 @@ impl Renamer {
|
||||
| BindingKind::ClassDefinition
|
||||
| BindingKind::FunctionDefinition
|
||||
| BindingKind::Deletion
|
||||
| BindingKind::UnboundException => {
|
||||
| BindingKind::UnboundException(_) => {
|
||||
Some(Edit::range_replacement(target.to_string(), binding.range))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,5 @@ pub(super) fn convert_inplace_argument_to_assignment(
|
||||
false,
|
||||
)
|
||||
.ok()?;
|
||||
#[allow(deprecated)]
|
||||
Some(Fix::unspecified_edits(insert_assignment, [remove_argument]))
|
||||
Some(Fix::suggested_edits(insert_assignment, [remove_argument]))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,21 @@ use crate::settings::Settings;
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// For flowing long blocks of text (docstrings or comments), overlong lines
|
||||
/// can hurt readability.
|
||||
/// can hurt readability. [PEP 8], for example, recommends that such lines be
|
||||
/// limited to 72 characters.
|
||||
///
|
||||
/// In the context of this rule, a "doc line" is defined as a line consisting
|
||||
/// of either a standalone comment or a standalone string, like a docstring.
|
||||
///
|
||||
/// In the interest of pragmatism, this rule makes a few exceptions when
|
||||
/// determining whether a line is overlong. Namely, it ignores lines that
|
||||
/// consist of a single "word" (i.e., without any whitespace between its
|
||||
/// characters), and lines that end with a URL (as long as the URL starts
|
||||
/// before the line-length threshold).
|
||||
///
|
||||
/// If `pycodestyle.ignore_overlong_task_comments` is `true`, this rule will
|
||||
/// also ignore comments that start with any of the specified `task-tags`
|
||||
/// (e.g., `# TODO:`).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -26,6 +40,13 @@ use crate::settings::Settings;
|
||||
/// Duis auctor purus ut ex fermentum, at maximus est hendrerit.
|
||||
/// """
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ## Options
|
||||
/// - `task-tags`
|
||||
/// - `pycodestyle.ignore-overlong-task-comments`
|
||||
///
|
||||
/// [PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length
|
||||
#[violation]
|
||||
pub struct DocLineTooLong(pub usize, pub usize);
|
||||
|
||||
|
||||
@@ -9,7 +9,18 @@ use crate::settings::Settings;
|
||||
/// Checks for lines that exceed the specified maximum character length.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Overlong lines can hurt readability.
|
||||
/// Overlong lines can hurt readability. [PEP 8], for example, recommends
|
||||
/// limiting lines to 79 characters.
|
||||
///
|
||||
/// In the interest of pragmatism, this rule makes a few exceptions when
|
||||
/// determining whether a line is overlong. Namely, it ignores lines that
|
||||
/// consist of a single "word" (i.e., without any whitespace between its
|
||||
/// characters), and lines that end with a URL (as long as the URL starts
|
||||
/// before the line-length threshold).
|
||||
///
|
||||
/// If `pycodestyle.ignore_overlong_task_comments` is `true`, this rule will
|
||||
/// also ignore comments that start with any of the specified `task-tags`
|
||||
/// (e.g., `# TODO:`).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -26,6 +37,9 @@ use crate::settings::Settings;
|
||||
///
|
||||
/// ## Options
|
||||
/// - `task-tags`
|
||||
/// - `pycodestyle.ignore-overlong-task-comments`
|
||||
///
|
||||
/// [PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length
|
||||
#[violation]
|
||||
pub struct LineTooLong(pub usize, pub usize);
|
||||
|
||||
|
||||
@@ -353,9 +353,59 @@ mod tests {
|
||||
except Exception as x:
|
||||
pass
|
||||
|
||||
# No error here, though it should arguably be an F821 error. `x` will
|
||||
# be unbound after the `except` block (assuming an exception is raised
|
||||
# and caught).
|
||||
print(x)
|
||||
"#,
|
||||
"print_after_shadowing_except"
|
||||
"print_in_body_after_shadowing_except"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
try:
|
||||
1 / 0
|
||||
except ValueError as x:
|
||||
pass
|
||||
except ImportError as x:
|
||||
pass
|
||||
|
||||
# No error here, though it should arguably be an F821 error. `x` will
|
||||
# be unbound after the `except` block (assuming an exception is raised
|
||||
# and caught).
|
||||
print(x)
|
||||
"#,
|
||||
"print_in_body_after_double_shadowing_except"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
def f():
|
||||
try:
|
||||
x = 3
|
||||
except ImportError as x:
|
||||
print(x)
|
||||
else:
|
||||
print(x)
|
||||
"#,
|
||||
"print_in_try_else_after_shadowing_except"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
def f():
|
||||
list = [1, 2, 3]
|
||||
|
||||
for e in list:
|
||||
if e % 2 == 0:
|
||||
try:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(e)
|
||||
else:
|
||||
print(e)
|
||||
"#,
|
||||
"print_in_if_else_after_shadowing_except"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
@@ -366,6 +416,79 @@ mod tests {
|
||||
"#,
|
||||
"double_del"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
x = 1
|
||||
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
except ValueError as x:
|
||||
pass
|
||||
|
||||
# This should resolve to the `x` in `x = 1`.
|
||||
print(x)
|
||||
"#,
|
||||
"load_after_unbind_from_module_scope"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
x = 1
|
||||
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
except ValueError as x:
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except ValueError as x:
|
||||
pass
|
||||
|
||||
# This should resolve to the `x` in `x = 1`.
|
||||
print(x)
|
||||
"#,
|
||||
"load_after_multiple_unbinds_from_module_scope"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
x = 1
|
||||
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
except ValueError as x:
|
||||
pass
|
||||
|
||||
def g():
|
||||
try:
|
||||
pass
|
||||
except ValueError as x:
|
||||
pass
|
||||
|
||||
# This should resolve to the `x` in `x = 1`.
|
||||
print(x)
|
||||
"#,
|
||||
"load_after_unbind_from_nested_module_scope"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
class C:
|
||||
x = 1
|
||||
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
except ValueError as x:
|
||||
pass
|
||||
|
||||
# This should raise an F821 error, rather than resolving to the
|
||||
# `x` in `x = 1`.
|
||||
print(x)
|
||||
"#,
|
||||
"load_after_unbind_from_class_scope"
|
||||
)]
|
||||
fn contents(contents: &str, snapshot: &str) {
|
||||
let diagnostics = test_snippet(contents, &Settings::for_rules(&Linter::Pyflakes));
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
|
||||
@@ -89,8 +89,7 @@ fn fix_f_string_missing_placeholders(
|
||||
checker: &mut Checker,
|
||||
) -> Fix {
|
||||
let content = &checker.locator.contents()[TextRange::new(prefix_range.end(), tok_range.end())];
|
||||
#[allow(deprecated)]
|
||||
Fix::unspecified(Edit::replacement(
|
||||
Fix::automatic(Edit::replacement(
|
||||
unescape_f_string(content),
|
||||
prefix_range.start(),
|
||||
tok_range.end(),
|
||||
|
||||
@@ -11,7 +11,7 @@ F541.py:6:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
3 3 | b = f"ghi{'jkl'}"
|
||||
4 4 |
|
||||
5 5 | # Errors
|
||||
@@ -32,7 +32,7 @@ F541.py:7:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
4 4 |
|
||||
5 5 | # Errors
|
||||
6 6 | c = f"def"
|
||||
@@ -53,7 +53,7 @@ F541.py:9:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
6 6 | c = f"def"
|
||||
7 7 | d = f"def" + "ghi"
|
||||
8 8 | e = (
|
||||
@@ -74,7 +74,7 @@ F541.py:13:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
10 10 | "ghi"
|
||||
11 11 | )
|
||||
12 12 | f = (
|
||||
@@ -95,7 +95,7 @@ F541.py:14:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
11 11 | )
|
||||
12 12 | f = (
|
||||
13 13 | f"a"
|
||||
@@ -116,7 +116,7 @@ F541.py:16:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
13 13 | f"a"
|
||||
14 14 | F"b"
|
||||
15 15 | "c"
|
||||
@@ -137,7 +137,7 @@ F541.py:17:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
14 14 | F"b"
|
||||
15 15 | "c"
|
||||
16 16 | rf"d"
|
||||
@@ -158,7 +158,7 @@ F541.py:19:5: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
16 16 | rf"d"
|
||||
17 17 | fr"e"
|
||||
18 18 | )
|
||||
@@ -178,7 +178,7 @@ F541.py:25:13: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
22 22 | g = f"ghi{123:{45}}"
|
||||
23 23 |
|
||||
24 24 | # Error
|
||||
@@ -198,7 +198,7 @@ F541.py:34:7: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
31 31 | f"{f'{v:0.2f}'}"
|
||||
32 32 |
|
||||
33 33 | # Errors
|
||||
@@ -219,7 +219,7 @@ F541.py:35:4: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
32 32 |
|
||||
33 33 | # Errors
|
||||
34 34 | f"{v:{f'0.2f'}}"
|
||||
@@ -240,7 +240,7 @@ F541.py:36:1: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
33 33 | # Errors
|
||||
34 34 | f"{v:{f'0.2f'}}"
|
||||
35 35 | f"{f''}"
|
||||
@@ -261,7 +261,7 @@ F541.py:37:1: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
34 34 | f"{v:{f'0.2f'}}"
|
||||
35 35 | f"{f''}"
|
||||
36 36 | f"{{test}}"
|
||||
@@ -281,7 +281,7 @@ F541.py:38:1: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
35 35 | f"{f''}"
|
||||
36 36 | f"{{test}}"
|
||||
37 37 | f'{{ 40 }}'
|
||||
@@ -302,7 +302,7 @@ F541.py:39:1: F541 [*] f-string without any placeholders
|
||||
|
|
||||
= help: Remove extraneous `f` prefix
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
36 36 | f"{{test}}"
|
||||
37 37 | f'{{ 40 }}'
|
||||
38 38 | f"{{a {{x}}"
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
<filename>:7:26: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
5 | try:
|
||||
6 | pass
|
||||
7 | except ValueError as x:
|
||||
| ^ F841
|
||||
8 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
4 4 | def f():
|
||||
5 5 | try:
|
||||
6 6 | pass
|
||||
7 |- except ValueError as x:
|
||||
7 |+ except ValueError:
|
||||
8 8 | pass
|
||||
9 9 |
|
||||
10 10 | try:
|
||||
|
||||
<filename>:12:26: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
10 | try:
|
||||
11 | pass
|
||||
12 | except ValueError as x:
|
||||
| ^ F841
|
||||
13 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
9 9 |
|
||||
10 10 | try:
|
||||
11 11 | pass
|
||||
12 |- except ValueError as x:
|
||||
12 |+ except ValueError:
|
||||
13 13 | pass
|
||||
14 14 |
|
||||
15 15 | # This should resolve to the `x` in `x = 1`.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
<filename>:8:30: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
6 | try:
|
||||
7 | pass
|
||||
8 | except ValueError as x:
|
||||
| ^ F841
|
||||
9 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
5 5 | def f():
|
||||
6 6 | try:
|
||||
7 7 | pass
|
||||
8 |- except ValueError as x:
|
||||
8 |+ except ValueError:
|
||||
9 9 | pass
|
||||
10 10 |
|
||||
11 11 | # This should raise an F821 error, rather than resolving to the
|
||||
|
||||
<filename>:13:15: F821 Undefined name `x`
|
||||
|
|
||||
11 | # This should raise an F821 error, rather than resolving to the
|
||||
12 | # `x` in `x = 1`.
|
||||
13 | print(x)
|
||||
| ^ F821
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
<filename>:7:26: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
5 | try:
|
||||
6 | pass
|
||||
7 | except ValueError as x:
|
||||
| ^ F841
|
||||
8 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
4 4 | def f():
|
||||
5 5 | try:
|
||||
6 6 | pass
|
||||
7 |- except ValueError as x:
|
||||
7 |+ except ValueError:
|
||||
8 8 | pass
|
||||
9 9 |
|
||||
10 10 | # This should resolve to the `x` in `x = 1`.
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
<filename>:7:26: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
5 | try:
|
||||
6 | pass
|
||||
7 | except ValueError as x:
|
||||
| ^ F841
|
||||
8 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
4 4 | def f():
|
||||
5 5 | try:
|
||||
6 6 | pass
|
||||
7 |- except ValueError as x:
|
||||
7 |+ except ValueError:
|
||||
8 8 | pass
|
||||
9 9 |
|
||||
10 10 | def g():
|
||||
|
||||
<filename>:13:30: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
11 | try:
|
||||
12 | pass
|
||||
13 | except ValueError as x:
|
||||
| ^ F841
|
||||
14 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
10 10 | def g():
|
||||
11 11 | try:
|
||||
12 12 | pass
|
||||
13 |- except ValueError as x:
|
||||
13 |+ except ValueError:
|
||||
14 14 | pass
|
||||
15 15 |
|
||||
16 16 | # This should resolve to the `x` in `x = 1`.
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
<filename>:7:26: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
5 | try:
|
||||
6 | 1 / 0
|
||||
7 | except ValueError as x:
|
||||
| ^ F841
|
||||
8 | pass
|
||||
9 | except ImportError as x:
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
4 4 |
|
||||
5 5 | try:
|
||||
6 6 | 1 / 0
|
||||
7 |- except ValueError as x:
|
||||
7 |+ except ValueError:
|
||||
8 8 | pass
|
||||
9 9 | except ImportError as x:
|
||||
10 10 | pass
|
||||
|
||||
<filename>:9:27: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
7 | except ValueError as x:
|
||||
8 | pass
|
||||
9 | except ImportError as x:
|
||||
| ^ F841
|
||||
10 | pass
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
ℹ Fix
|
||||
6 6 | 1 / 0
|
||||
7 7 | except ValueError as x:
|
||||
8 8 | pass
|
||||
9 |- except ImportError as x:
|
||||
9 |+ except ImportError:
|
||||
10 10 | pass
|
||||
11 11 |
|
||||
12 12 | # No error here, though it should arguably be an F821 error. `x` will
|
||||
|
||||
|
||||
@@ -19,14 +19,6 @@ source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
7 |+ except Exception:
|
||||
8 8 | pass
|
||||
9 9 |
|
||||
10 10 | print(x)
|
||||
|
||||
<filename>:10:11: F821 Undefined name `x`
|
||||
|
|
||||
8 | pass
|
||||
9 |
|
||||
10 | print(x)
|
||||
| ^ F821
|
||||
|
|
||||
10 10 | # No error here, though it should arguably be an F821 error. `x` will
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
|
||||
@@ -7,49 +7,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub(crate) enum EmptyStringCmpOp {
|
||||
Is,
|
||||
IsNot,
|
||||
Eq,
|
||||
NotEq,
|
||||
}
|
||||
|
||||
impl TryFrom<&CmpOp> for EmptyStringCmpOp {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &CmpOp) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
CmpOp::Is => Ok(Self::Is),
|
||||
CmpOp::IsNot => Ok(Self::IsNot),
|
||||
CmpOp::Eq => Ok(Self::Eq),
|
||||
CmpOp::NotEq => Ok(Self::NotEq),
|
||||
_ => bail!("{value:?} cannot be converted to EmptyStringCmpOp"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyStringCmpOp {
|
||||
pub(crate) fn into_unary(self) -> &'static str {
|
||||
match self {
|
||||
Self::Is | Self::Eq => "not ",
|
||||
Self::IsNot | Self::NotEq => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EmptyStringCmpOp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let repr = match self {
|
||||
Self::Is => "is",
|
||||
Self::IsNot => "is not",
|
||||
Self::Eq => "==",
|
||||
Self::NotEq => "!=",
|
||||
};
|
||||
write!(f, "{repr}")
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for comparisons to empty strings.
|
||||
///
|
||||
@@ -83,13 +40,15 @@ pub struct CompareToEmptyString {
|
||||
impl Violation for CompareToEmptyString {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"`{}` can be simplified to `{}` as an empty string is falsey",
|
||||
self.existing, self.replacement,
|
||||
)
|
||||
let CompareToEmptyString {
|
||||
existing,
|
||||
replacement,
|
||||
} = self;
|
||||
format!("`{existing}` can be simplified to `{replacement}` as an empty string is falsey",)
|
||||
}
|
||||
}
|
||||
|
||||
/// PLC1901
|
||||
pub(crate) fn compare_to_empty_string(
|
||||
checker: &mut Checker,
|
||||
left: &Expr,
|
||||
@@ -98,10 +57,12 @@ pub(crate) fn compare_to_empty_string(
|
||||
) {
|
||||
// Omit string comparison rules within subscripts. This is mostly commonly used within
|
||||
// DataFrame and np.ndarray indexing.
|
||||
for parent in checker.semantic().expr_ancestors() {
|
||||
if matches!(parent, Expr::Subscript(_)) {
|
||||
return;
|
||||
}
|
||||
if checker
|
||||
.semantic()
|
||||
.expr_ancestors()
|
||||
.any(|parent| parent.is_subscript_expr())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
@@ -153,3 +114,46 @@ pub(crate) fn compare_to_empty_string(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
enum EmptyStringCmpOp {
|
||||
Is,
|
||||
IsNot,
|
||||
Eq,
|
||||
NotEq,
|
||||
}
|
||||
|
||||
impl TryFrom<&CmpOp> for EmptyStringCmpOp {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &CmpOp) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
CmpOp::Is => Ok(Self::Is),
|
||||
CmpOp::IsNot => Ok(Self::IsNot),
|
||||
CmpOp::Eq => Ok(Self::Eq),
|
||||
CmpOp::NotEq => Ok(Self::NotEq),
|
||||
_ => bail!("{value:?} cannot be converted to EmptyStringCmpOp"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyStringCmpOp {
|
||||
fn into_unary(self) -> &'static str {
|
||||
match self {
|
||||
Self::Is | Self::Eq => "not ",
|
||||
Self::IsNot | Self::NotEq => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EmptyStringCmpOp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let repr = match self {
|
||||
Self::Is => "is",
|
||||
Self::IsNot => "is not",
|
||||
Self::Eq => "==",
|
||||
Self::NotEq => "!=",
|
||||
};
|
||||
write!(f, "{repr}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,8 +191,7 @@ pub(crate) fn invalid_string_characters(locator: &Locator, range: TextRange) ->
|
||||
let location = range.start() + TextSize::try_from(column).unwrap();
|
||||
let range = TextRange::at(location, c.text_len());
|
||||
|
||||
#[allow(deprecated)]
|
||||
diagnostics.push(Diagnostic::new(rule, range).with_fix(Fix::unspecified(
|
||||
diagnostics.push(Diagnostic::new(rule, range).with_fix(Fix::automatic(
|
||||
Edit::range_replacement(replacement.to_string(), range),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -157,8 +157,7 @@ pub(crate) fn nested_min_max(
|
||||
keywords: keywords.to_owned(),
|
||||
range: TextRange::default(),
|
||||
});
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
checker.generator().expr(&flattened_expr),
|
||||
expr.range(),
|
||||
)));
|
||||
|
||||
@@ -82,8 +82,7 @@ pub(crate) fn sys_exit_alias(checker: &mut Checker, func: &Expr) {
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, func.range());
|
||||
#[allow(deprecated)]
|
||||
Ok(Fix::unspecified_edits(import_edit, [reference_edit]))
|
||||
Ok(Fix::suggested_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -49,8 +49,7 @@ pub(crate) fn useless_import_alias(checker: &mut Checker, alias: &Alias) {
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UselessImportAlias, alias.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
#[allow(deprecated)]
|
||||
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
asname.to_string(),
|
||||
alias.range(),
|
||||
)));
|
||||
|
||||
@@ -12,7 +12,7 @@ invalid_characters.py:15:6: PLE2510 [*] Invalid unescaped character backspace, u
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
12 12 | # (Pylint, "C0414") => Rule::UselessImportAlias,
|
||||
13 13 | # (Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
|
||||
14 14 | #foo = 'hi'
|
||||
|
||||
@@ -12,7 +12,7 @@ invalid_characters.py:21:12: PLE2512 [*] Invalid unescaped character SUB, use "\
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
18 18 |
|
||||
19 19 | cr_ok = '\\r'
|
||||
20 20 |
|
||||
|
||||
@@ -12,7 +12,7 @@ invalid_characters.py:25:16: PLE2513 [*] Invalid unescaped character ESC, use "\
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
22 22 |
|
||||
23 23 | sub_ok = '\x1a'
|
||||
24 24 |
|
||||
|
||||
Binary file not shown.
@@ -12,7 +12,7 @@ invalid_characters.py:34:13: PLE2515 [*] Invalid unescaped character zero-width-
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
31 31 |
|
||||
32 32 | nul_ok = '\0'
|
||||
33 33 |
|
||||
@@ -32,7 +32,7 @@ invalid_characters.py:38:36: PLE2515 [*] Invalid unescaped character zero-width-
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
35 35 |
|
||||
36 36 | zwsp_ok = '\u200b'
|
||||
37 37 |
|
||||
@@ -48,7 +48,7 @@ invalid_characters.py:39:60: PLE2515 [*] Invalid unescaped character zero-width-
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
36 36 | zwsp_ok = '\u200b'
|
||||
37 37 |
|
||||
38 38 | zwsp_after_multibyte_character = "ಫ"
|
||||
@@ -63,7 +63,7 @@ invalid_characters.py:39:61: PLE2515 [*] Invalid unescaped character zero-width-
|
||||
|
|
||||
= help: Replace with escape sequence
|
||||
|
||||
ℹ Suggested fix
|
||||
ℹ Fix
|
||||
36 36 | zwsp_ok = '\u200b'
|
||||
37 37 |
|
||||
38 38 | zwsp_after_multibyte_character = "ಫ"
|
||||
|
||||
@@ -11,6 +11,22 @@ use crate::rules::ruff::rules::confusables::CONFUSABLES;
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::settings::Settings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for ambiguous unicode characters in strings.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The use of ambiguous unicode characters can confuse readers and cause
|
||||
/// subtle bugs.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// print("Ηello, world!") # "Η" is the Greek eta (`U+0397`).
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// print("Hello, world!") # "H" is the Latin capital H (`U+0048`).
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct AmbiguousUnicodeCharacterString {
|
||||
confusable: char,
|
||||
@@ -44,6 +60,22 @@ impl AlwaysAutofixableViolation for AmbiguousUnicodeCharacterString {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for ambiguous unicode characters in docstrings.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The use of ambiguous unicode characters can confuse readers and cause
|
||||
/// subtle bugs.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// """A lovely docstring (with a `U+FF09` parenthesis)."""
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// """A lovely docstring (with no strange parentheses)."""
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct AmbiguousUnicodeCharacterDocstring {
|
||||
confusable: char,
|
||||
@@ -77,6 +109,22 @@ impl AlwaysAutofixableViolation for AmbiguousUnicodeCharacterDocstring {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for ambiguous unicode characters in comments.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The use of ambiguous unicode characters can confuse readers and cause
|
||||
/// subtle bugs.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// foo() # nоqa # "о" is Cyrillic (`U+043E`)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// foo() # noqa # "o" is Latin (`U+006F`)
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct AmbiguousUnicodeCharacterComment {
|
||||
confusable: char,
|
||||
|
||||
@@ -13,6 +13,36 @@ pub struct CollectionLiteralConcatenation {
|
||||
expr: String,
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of the `+` operator to concatenate collections.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In Python, the `+` operator can be used to concatenate collections (e.g.,
|
||||
/// `x + y` to concatenate the lists `x` and `y`).
|
||||
///
|
||||
/// However, collections can be concatenated more efficiently using the
|
||||
/// unpacking operator (e.g., `[*x, *y]` to concatenate `x` and `y`).
|
||||
///
|
||||
/// Prefer the unpacking operator to concatenate collections, as it is more
|
||||
/// readable and flexible. The `*` operator can unpack any iterable, whereas
|
||||
/// `+` operates only on particular sequences which, in many cases, must be of
|
||||
/// the same type.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// foo = [2, 3, 4]
|
||||
/// bar = [1] + foo + [5, 6]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// foo = [2, 3, 4]
|
||||
/// bar = [1, *foo, 5, 6]
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 448 – Additional Unpacking Generalizations](https://peps.python.org/pep-0448/)
|
||||
/// - [Python docs: Sequence Types — `list`, `tuple`, `range`](https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range)
|
||||
impl Violation for CollectionLiteralConcatenation {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ use crate::rules::ruff::rules::helpers::{
|
||||
/// - `flake8-bugbear.extend-immutable-calls`
|
||||
#[violation]
|
||||
pub struct FunctionCallInDataclassDefaultArgument {
|
||||
pub name: Option<String>,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for FunctionCallInDataclassDefaultArgument {
|
||||
|
||||
@@ -18,6 +18,14 @@ pub(super) fn is_class_var_annotation(annotation: &Expr, semantic: &SemanticMode
|
||||
semantic.match_typing_expr(value, "ClassVar")
|
||||
}
|
||||
|
||||
/// Returns `true` if the given [`Expr`] is a `typing.Final` annotation.
|
||||
pub(super) fn is_final_annotation(annotation: &Expr, semantic: &SemanticModel) -> bool {
|
||||
let Expr::Subscript(ast::ExprSubscript { value, .. }) = &annotation else {
|
||||
return false;
|
||||
};
|
||||
semantic.match_typing_expr(value, "Final")
|
||||
}
|
||||
|
||||
/// Returns `true` if the given class is a dataclass.
|
||||
pub(super) fn is_dataclass(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
|
||||
class_def.decorator_list.iter().any(|decorator| {
|
||||
@@ -28,3 +36,12 @@ pub(super) fn is_dataclass(class_def: &ast::StmtClassDef, semantic: &SemanticMod
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the given class is a Pydantic `BaseModel`.
|
||||
pub(super) fn is_pydantic_model(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
|
||||
class_def.bases.iter().any(|expr| {
|
||||
semantic.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
matches!(call_path.as_slice(), ["pydantic", "BaseModel"])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ use anyhow::Result;
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::{self, ArgWithDefault, Arguments, Constant, Expr, Operator, Ranged};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_const_none;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::typing::parse_type_annotation;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -65,14 +67,16 @@ pub struct ImplicitOptional {
|
||||
conversion_type: ConversionType,
|
||||
}
|
||||
|
||||
impl AlwaysAutofixableViolation for ImplicitOptional {
|
||||
impl Violation for ImplicitOptional {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("PEP 484 prohibits implicit `Optional`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
format!("Convert to `{}`", self.conversion_type)
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
Some(format!("Convert to `{}`", self.conversion_type))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,15 +146,15 @@ enum TypingTarget<'a> {
|
||||
None,
|
||||
Any,
|
||||
Object,
|
||||
ForwardReference,
|
||||
Optional,
|
||||
ForwardReference(Expr),
|
||||
Union(Vec<&'a Expr>),
|
||||
Literal(Vec<&'a Expr>),
|
||||
Annotated(&'a Expr),
|
||||
}
|
||||
|
||||
impl<'a> TypingTarget<'a> {
|
||||
fn try_from_expr(expr: &'a Expr, semantic: &SemanticModel) -> Option<Self> {
|
||||
fn try_from_expr(expr: &'a Expr, semantic: &SemanticModel, locator: &Locator) -> Option<Self> {
|
||||
match expr {
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
if semantic.match_typing_expr(value, "Optional") {
|
||||
@@ -177,9 +181,14 @@ impl<'a> TypingTarget<'a> {
|
||||
..
|
||||
}) => Some(TypingTarget::None),
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: Constant::Str(_),
|
||||
value: Constant::Str(string),
|
||||
range,
|
||||
..
|
||||
}) => Some(TypingTarget::ForwardReference),
|
||||
}) => parse_type_annotation(string, *range, locator)
|
||||
// In case of a parse error, we return `Any` to avoid false positives.
|
||||
.map_or(Some(TypingTarget::Any), |(expr, _)| {
|
||||
Some(TypingTarget::ForwardReference(expr))
|
||||
}),
|
||||
_ => semantic.resolve_call_path(expr).and_then(|call_path| {
|
||||
if semantic.match_typing_call_path(&call_path, "Any") {
|
||||
Some(TypingTarget::Any)
|
||||
@@ -193,44 +202,42 @@ impl<'a> TypingTarget<'a> {
|
||||
}
|
||||
|
||||
/// Check if the [`TypingTarget`] explicitly allows `None`.
|
||||
fn contains_none(&self, semantic: &SemanticModel) -> bool {
|
||||
fn contains_none(&self, semantic: &SemanticModel, locator: &Locator) -> bool {
|
||||
match self {
|
||||
TypingTarget::None
|
||||
| TypingTarget::Optional
|
||||
| TypingTarget::Any
|
||||
| TypingTarget::Object => true,
|
||||
TypingTarget::Literal(elements) => elements.iter().any(|element| {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(element, semantic) else {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(element, semantic, locator) else {
|
||||
return false;
|
||||
};
|
||||
// Literal can only contain `None`, a literal value, other `Literal`
|
||||
// or an enum value.
|
||||
match new_target {
|
||||
TypingTarget::None => true,
|
||||
TypingTarget::Literal(_) => new_target.contains_none(semantic),
|
||||
TypingTarget::Literal(_) => new_target.contains_none(semantic, locator),
|
||||
_ => false,
|
||||
}
|
||||
}),
|
||||
TypingTarget::Union(elements) => elements.iter().any(|element| {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(element, semantic) else {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(element, semantic, locator) else {
|
||||
return false;
|
||||
};
|
||||
match new_target {
|
||||
TypingTarget::None => true,
|
||||
_ => new_target.contains_none(semantic),
|
||||
}
|
||||
new_target.contains_none(semantic, locator)
|
||||
}),
|
||||
TypingTarget::Annotated(element) => {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(element, semantic) else {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(element, semantic, locator) else {
|
||||
return false;
|
||||
};
|
||||
match new_target {
|
||||
TypingTarget::None => true,
|
||||
_ => new_target.contains_none(semantic),
|
||||
}
|
||||
new_target.contains_none(semantic, locator)
|
||||
}
|
||||
TypingTarget::ForwardReference(expr) => {
|
||||
let Some(new_target) = TypingTarget::try_from_expr(expr, semantic, locator) else {
|
||||
return false;
|
||||
};
|
||||
new_target.contains_none(semantic, locator)
|
||||
}
|
||||
// TODO(charlie): Add support for forward references (quoted annotations).
|
||||
TypingTarget::ForwardReference => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,8 +252,9 @@ impl<'a> TypingTarget<'a> {
|
||||
fn type_hint_explicitly_allows_none<'a>(
|
||||
annotation: &'a Expr,
|
||||
semantic: &SemanticModel,
|
||||
locator: &Locator,
|
||||
) -> Option<&'a Expr> {
|
||||
let Some(target) = TypingTarget::try_from_expr(annotation, semantic) else {
|
||||
let Some(target) = TypingTarget::try_from_expr(annotation, semantic, locator) else {
|
||||
return Some(annotation);
|
||||
};
|
||||
match target {
|
||||
@@ -256,9 +264,9 @@ fn type_hint_explicitly_allows_none<'a>(
|
||||
// return the inner type if it doesn't allow `None`. If `Annotated`
|
||||
// is found nested inside another type, then the outer type should
|
||||
// be returned.
|
||||
TypingTarget::Annotated(expr) => type_hint_explicitly_allows_none(expr, semantic),
|
||||
TypingTarget::Annotated(expr) => type_hint_explicitly_allows_none(expr, semantic, locator),
|
||||
_ => {
|
||||
if target.contains_none(semantic) {
|
||||
if target.contains_none(semantic, locator) {
|
||||
None
|
||||
} else {
|
||||
Some(annotation)
|
||||
@@ -333,15 +341,42 @@ pub(crate) fn implicit_optional(checker: &mut Checker, arguments: &Arguments) {
|
||||
let Some(annotation) = &def.annotation else {
|
||||
continue
|
||||
};
|
||||
let Some(expr) = type_hint_explicitly_allows_none(annotation, checker.semantic()) else {
|
||||
continue;
|
||||
};
|
||||
let conversion_type = checker.settings.target_version.into();
|
||||
|
||||
let mut diagnostic = Diagnostic::new(ImplicitOptional { conversion_type }, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| generate_fix(checker, conversion_type, expr));
|
||||
if let Expr::Constant(ast::ExprConstant {
|
||||
range,
|
||||
value: Constant::Str(string),
|
||||
..
|
||||
}) = annotation.as_ref()
|
||||
{
|
||||
// Quoted annotation.
|
||||
if let Ok((annotation, kind)) = parse_type_annotation(string, *range, checker.locator) {
|
||||
let Some(expr) = type_hint_explicitly_allows_none(&annotation, checker.semantic(), checker.locator) else {
|
||||
continue;
|
||||
};
|
||||
let conversion_type = checker.settings.target_version.into();
|
||||
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(ImplicitOptional { conversion_type }, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if kind.is_simple() {
|
||||
diagnostic.try_set_fix(|| generate_fix(checker, conversion_type, expr));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
} else {
|
||||
// Unquoted annotation.
|
||||
let Some(expr) = type_hint_explicitly_allows_none(annotation, checker.semantic(), checker.locator) else {
|
||||
continue;
|
||||
};
|
||||
let conversion_type = checker.settings.target_version.into();
|
||||
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(ImplicitOptional { conversion_type }, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| generate_fix(checker, conversion_type, expr));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::ruff::rules::helpers::{is_class_var_annotation, is_dataclass};
|
||||
use crate::rules::ruff::rules::helpers::{
|
||||
is_class_var_annotation, is_dataclass, is_final_annotation, is_pydantic_model,
|
||||
};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for mutable default values in class attributes.
|
||||
@@ -54,9 +56,15 @@ pub(crate) fn mutable_class_default(checker: &mut Checker, class_def: &ast::Stmt
|
||||
}) => {
|
||||
if is_mutable_expr(value, checker.semantic())
|
||||
&& !is_class_var_annotation(annotation, checker.semantic())
|
||||
&& !is_final_annotation(annotation, checker.semantic())
|
||||
&& !is_immutable_annotation(annotation, checker.semantic())
|
||||
&& !is_dataclass(class_def, checker.semantic())
|
||||
{
|
||||
// Avoid Pydantic models, which end up copying defaults on instance creation.
|
||||
if is_pydantic_model(class_def, checker.semantic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(MutableClassDefault, value.range()));
|
||||
@@ -64,6 +72,11 @@ pub(crate) fn mutable_class_default(checker: &mut Checker, class_def: &ast::Stmt
|
||||
}
|
||||
Stmt::Assign(ast::StmtAssign { value, .. }) => {
|
||||
if is_mutable_expr(value, checker.semantic()) {
|
||||
// Avoid Pydantic models, which end up copying defaults on instance creation.
|
||||
if is_pydantic_model(class_def, checker.semantic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(MutableClassDefault, value.range()));
|
||||
|
||||
@@ -6,6 +6,32 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for use of `zip()` to iterate over successive pairs of elements.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// When iterating over successive pairs of elements, prefer
|
||||
/// `itertools.pairwise()` over `zip()`.
|
||||
///
|
||||
/// `itertools.pairwise()` is more readable and conveys the intent of the code
|
||||
/// more clearly.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// letters = "ABCD"
|
||||
/// zip(letters, letters[1:]) # ("A", "B"), ("B", "C"), ("C", "D")
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from itertools import pairwise
|
||||
///
|
||||
/// letters = "ABCD"
|
||||
/// pairwise(letters) # ("A", "B"), ("B", "C"), ("C", "D")
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `itertools.pairwise`](https://docs.python.org/3/library/itertools.html#itertools.pairwise)
|
||||
#[violation]
|
||||
pub struct PairwiseOverZipped;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ source: crates/ruff/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
21 | def f(arg: int = None): # RUF011
|
||||
21 | def f(arg: int = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
22 | pass
|
||||
|
|
||||
@@ -13,15 +13,15 @@ RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
18 18 | pass
|
||||
19 19 |
|
||||
20 20 |
|
||||
21 |-def f(arg: int = None): # RUF011
|
||||
21 |+def f(arg: Optional[int] = None): # RUF011
|
||||
21 |-def f(arg: int = None): # RUF013
|
||||
21 |+def f(arg: Optional[int] = None): # RUF013
|
||||
22 22 | pass
|
||||
23 23 |
|
||||
24 24 |
|
||||
|
||||
RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
25 | def f(arg: str = None): # RUF011
|
||||
25 | def f(arg: str = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
26 | pass
|
||||
|
|
||||
@@ -31,15 +31,15 @@ RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
22 22 | pass
|
||||
23 23 |
|
||||
24 24 |
|
||||
25 |-def f(arg: str = None): # RUF011
|
||||
25 |+def f(arg: Optional[str] = None): # RUF011
|
||||
25 |-def f(arg: str = None): # RUF013
|
||||
25 |+def f(arg: Optional[str] = None): # RUF013
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
|
||||
RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
29 | def f(arg: typing.List[str] = None): # RUF011
|
||||
29 | def f(arg: typing.List[str] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^ RUF013
|
||||
30 | pass
|
||||
|
|
||||
@@ -49,15 +49,15 @@ RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
29 |-def f(arg: typing.List[str] = None): # RUF011
|
||||
29 |+def f(arg: Optional[typing.List[str]] = None): # RUF011
|
||||
29 |-def f(arg: typing.List[str] = None): # RUF013
|
||||
29 |+def f(arg: Optional[typing.List[str]] = None): # RUF013
|
||||
30 30 | pass
|
||||
31 31 |
|
||||
32 32 |
|
||||
|
||||
RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
33 | def f(arg: Tuple[str] = None): # RUF011
|
||||
33 | def f(arg: Tuple[str] = None): # RUF013
|
||||
| ^^^^^^^^^^ RUF013
|
||||
34 | pass
|
||||
|
|
||||
@@ -67,15 +67,15 @@ RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
30 30 | pass
|
||||
31 31 |
|
||||
32 32 |
|
||||
33 |-def f(arg: Tuple[str] = None): # RUF011
|
||||
33 |+def f(arg: Optional[Tuple[str]] = None): # RUF011
|
||||
33 |-def f(arg: Tuple[str] = None): # RUF013
|
||||
33 |+def f(arg: Optional[Tuple[str]] = None): # RUF013
|
||||
34 34 | pass
|
||||
35 35 |
|
||||
36 36 |
|
||||
|
||||
RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
67 | def f(arg: Union = None): # RUF011
|
||||
67 | def f(arg: Union = None): # RUF013
|
||||
| ^^^^^ RUF013
|
||||
68 | pass
|
||||
|
|
||||
@@ -85,15 +85,15 @@ RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
64 64 | pass
|
||||
65 65 |
|
||||
66 66 |
|
||||
67 |-def f(arg: Union = None): # RUF011
|
||||
67 |+def f(arg: Optional[Union] = None): # RUF011
|
||||
67 |-def f(arg: Union = None): # RUF013
|
||||
67 |+def f(arg: Optional[Union] = None): # RUF013
|
||||
68 68 | pass
|
||||
69 69 |
|
||||
70 70 |
|
||||
|
||||
RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
71 | def f(arg: Union[int, str] = None): # RUF011
|
||||
71 | def f(arg: Union[int, str] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^ RUF013
|
||||
72 | pass
|
||||
|
|
||||
@@ -103,15 +103,15 @@ RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
68 68 | pass
|
||||
69 69 |
|
||||
70 70 |
|
||||
71 |-def f(arg: Union[int, str] = None): # RUF011
|
||||
71 |+def f(arg: Optional[Union[int, str]] = None): # RUF011
|
||||
71 |-def f(arg: Union[int, str] = None): # RUF013
|
||||
71 |+def f(arg: Optional[Union[int, str]] = None): # RUF013
|
||||
72 72 | pass
|
||||
73 73 |
|
||||
74 74 |
|
||||
|
||||
RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
75 | def f(arg: typing.Union[int, str] = None): # RUF011
|
||||
75 | def f(arg: typing.Union[int, str] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
76 | pass
|
||||
|
|
||||
@@ -121,15 +121,15 @@ RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
72 72 | pass
|
||||
73 73 |
|
||||
74 74 |
|
||||
75 |-def f(arg: typing.Union[int, str] = None): # RUF011
|
||||
75 |+def f(arg: Optional[typing.Union[int, str]] = None): # RUF011
|
||||
75 |-def f(arg: typing.Union[int, str] = None): # RUF013
|
||||
75 |+def f(arg: Optional[typing.Union[int, str]] = None): # RUF013
|
||||
76 76 | pass
|
||||
77 77 |
|
||||
78 78 |
|
||||
|
||||
RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
94 | def f(arg: int | float = None): # RUF011
|
||||
94 | def f(arg: int | float = None): # RUF013
|
||||
| ^^^^^^^^^^^ RUF013
|
||||
95 | pass
|
||||
|
|
||||
@@ -139,15 +139,15 @@ RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
91 91 | pass
|
||||
92 92 |
|
||||
93 93 |
|
||||
94 |-def f(arg: int | float = None): # RUF011
|
||||
94 |+def f(arg: Optional[int | float] = None): # RUF011
|
||||
94 |-def f(arg: int | float = None): # RUF013
|
||||
94 |+def f(arg: Optional[int | float] = None): # RUF013
|
||||
95 95 | pass
|
||||
96 96 |
|
||||
97 97 |
|
||||
|
||||
RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
98 | def f(arg: int | float | str | bytes = None): # RUF011
|
||||
98 | def f(arg: int | float | str | bytes = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
99 | pass
|
||||
|
|
||||
@@ -157,15 +157,15 @@ RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
95 95 | pass
|
||||
96 96 |
|
||||
97 97 |
|
||||
98 |-def f(arg: int | float | str | bytes = None): # RUF011
|
||||
98 |+def f(arg: Optional[int | float | str | bytes] = None): # RUF011
|
||||
98 |-def f(arg: int | float | str | bytes = None): # RUF013
|
||||
98 |+def f(arg: Optional[int | float | str | bytes] = None): # RUF013
|
||||
99 99 | pass
|
||||
100 100 |
|
||||
101 101 |
|
||||
|
||||
RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
113 | def f(arg: Literal[1, "foo"] = None): # RUF011
|
||||
113 | def f(arg: Literal[1, "foo"] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^ RUF013
|
||||
114 | pass
|
||||
|
|
||||
@@ -175,15 +175,15 @@ RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
110 110 | pass
|
||||
111 111 |
|
||||
112 112 |
|
||||
113 |-def f(arg: Literal[1, "foo"] = None): # RUF011
|
||||
113 |+def f(arg: Optional[Literal[1, "foo"]] = None): # RUF011
|
||||
113 |-def f(arg: Literal[1, "foo"] = None): # RUF013
|
||||
113 |+def f(arg: Optional[Literal[1, "foo"]] = None): # RUF013
|
||||
114 114 | pass
|
||||
115 115 |
|
||||
116 116 |
|
||||
|
||||
RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF011
|
||||
117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
118 | pass
|
||||
|
|
||||
@@ -193,15 +193,15 @@ RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
114 114 | pass
|
||||
115 115 |
|
||||
116 116 |
|
||||
117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF011
|
||||
117 |+def f(arg: Optional[typing.Literal[1, "foo", True]] = None): # RUF011
|
||||
117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
|
||||
117 |+def f(arg: Optional[typing.Literal[1, "foo", True]] = None): # RUF013
|
||||
118 118 | pass
|
||||
119 119 |
|
||||
120 120 |
|
||||
|
||||
RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
136 | def f(arg: Annotated[int, ...] = None): # RUF011
|
||||
136 | def f(arg: Annotated[int, ...] = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
137 | pass
|
||||
|
|
||||
@@ -211,15 +211,15 @@ RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
133 133 | pass
|
||||
134 134 |
|
||||
135 135 |
|
||||
136 |-def f(arg: Annotated[int, ...] = None): # RUF011
|
||||
136 |+def f(arg: Annotated[Optional[int], ...] = None): # RUF011
|
||||
136 |-def f(arg: Annotated[int, ...] = None): # RUF013
|
||||
136 |+def f(arg: Annotated[Optional[int], ...] = None): # RUF013
|
||||
137 137 | pass
|
||||
138 138 |
|
||||
139 139 |
|
||||
|
||||
RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011
|
||||
140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
|
||||
| ^^^^^^^^^ RUF013
|
||||
141 | pass
|
||||
|
|
||||
@@ -229,8 +229,8 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
137 137 | pass
|
||||
138 138 |
|
||||
139 139 |
|
||||
140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011
|
||||
140 |+def f(arg: Annotated[Annotated[Optional[int | str], ...], ...] = None): # RUF011
|
||||
140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
|
||||
140 |+def f(arg: Annotated[Annotated[Optional[int | str], ...], ...] = None): # RUF013
|
||||
141 141 | pass
|
||||
142 142 |
|
||||
143 143 |
|
||||
@@ -238,10 +238,10 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
155 | def f(
|
||||
156 | arg1: int = None, # RUF011
|
||||
156 | arg1: int = None, # RUF013
|
||||
| ^^^ RUF013
|
||||
157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
|
|
||||
= help: Convert to `Optional[T]`
|
||||
|
||||
@@ -249,19 +249,19 @@ RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
153 153 |
|
||||
154 154 |
|
||||
155 155 | def f(
|
||||
156 |- arg1: int = None, # RUF011
|
||||
156 |+ arg1: Optional[int] = None, # RUF011
|
||||
157 157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
156 |- arg1: int = None, # RUF013
|
||||
156 |+ arg1: Optional[int] = None, # RUF013
|
||||
157 157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
159 159 | ):
|
||||
|
||||
RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
155 | def f(
|
||||
156 | arg1: int = None, # RUF011
|
||||
157 | arg2: Union[int, float] = None, # RUF011
|
||||
156 | arg1: int = None, # RUF013
|
||||
157 | arg2: Union[int, float] = None, # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^ RUF013
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
159 | ):
|
||||
|
|
||||
= help: Convert to `Optional[T]`
|
||||
@@ -269,18 +269,18 @@ RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
ℹ Suggested fix
|
||||
154 154 |
|
||||
155 155 | def f(
|
||||
156 156 | arg1: int = None, # RUF011
|
||||
157 |- arg2: Union[int, float] = None, # RUF011
|
||||
157 |+ arg2: Optional[Union[int, float]] = None, # RUF011
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
156 156 | arg1: int = None, # RUF013
|
||||
157 |- arg2: Union[int, float] = None, # RUF013
|
||||
157 |+ arg2: Optional[Union[int, float]] = None, # RUF013
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
159 159 | ):
|
||||
160 160 | pass
|
||||
|
||||
RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
156 | arg1: int = None, # RUF011
|
||||
157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
156 | arg1: int = None, # RUF013
|
||||
157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
| ^^^^^^^^^^^^^^^^ RUF013
|
||||
159 | ):
|
||||
160 | pass
|
||||
@@ -289,17 +289,17 @@ RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
||||
ℹ Suggested fix
|
||||
155 155 | def f(
|
||||
156 156 | arg1: int = None, # RUF011
|
||||
157 157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 |- arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
158 |+ arg3: Optional[Literal[1, 2, 3]] = None, # RUF011
|
||||
156 156 | arg1: int = None, # RUF013
|
||||
157 157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 |- arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
158 |+ arg3: Optional[Literal[1, 2, 3]] = None, # RUF013
|
||||
159 159 | ):
|
||||
160 160 | pass
|
||||
161 161 |
|
||||
|
||||
RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011
|
||||
186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
187 | pass
|
||||
|
|
||||
@@ -309,10 +309,72 @@ RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
183 183 | pass
|
||||
184 184 |
|
||||
185 185 |
|
||||
186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011
|
||||
186 |+def f(arg: Optional[Union[Annotated[int, ...], Union[str, bytes]]] = None): # RUF011
|
||||
186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
|
||||
186 |+def f(arg: Optional[Union[Annotated[int, ...], Union[str, bytes]]] = None): # RUF013
|
||||
187 187 | pass
|
||||
188 188 |
|
||||
189 189 |
|
||||
|
||||
RUF013_0.py:193:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
193 | def f(arg: "int" = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
194 | pass
|
||||
|
|
||||
= help: Convert to `Optional[T]`
|
||||
|
||||
ℹ Suggested fix
|
||||
190 190 | # Quoted
|
||||
191 191 |
|
||||
192 192 |
|
||||
193 |-def f(arg: "int" = None): # RUF013
|
||||
193 |+def f(arg: "Optional[int]" = None): # RUF013
|
||||
194 194 | pass
|
||||
195 195 |
|
||||
196 196 |
|
||||
|
||||
RUF013_0.py:197:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
197 | def f(arg: "str" = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
198 | pass
|
||||
|
|
||||
= help: Convert to `Optional[T]`
|
||||
|
||||
ℹ Suggested fix
|
||||
194 194 | pass
|
||||
195 195 |
|
||||
196 196 |
|
||||
197 |-def f(arg: "str" = None): # RUF013
|
||||
197 |+def f(arg: "Optional[str]" = None): # RUF013
|
||||
198 198 | pass
|
||||
199 199 |
|
||||
200 200 |
|
||||
|
||||
RUF013_0.py:201:12: RUF013 PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
201 | def f(arg: "st" "r" = None): # RUF013
|
||||
| ^^^^^^^^ RUF013
|
||||
202 | pass
|
||||
|
|
||||
= help: Convert to `Optional[T]`
|
||||
|
||||
RUF013_0.py:209:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
209 | def f(arg: Union["int", "str"] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
210 | pass
|
||||
|
|
||||
= help: Convert to `Optional[T]`
|
||||
|
||||
ℹ Suggested fix
|
||||
206 206 | pass
|
||||
207 207 |
|
||||
208 208 |
|
||||
209 |-def f(arg: Union["int", "str"] = None): # RUF013
|
||||
209 |+def f(arg: Optional[Union["int", "str"]] = None): # RUF013
|
||||
210 210 | pass
|
||||
211 211 |
|
||||
212 212 |
|
||||
|
||||
|
||||
|
||||
@@ -20,33 +20,33 @@ RUF012.py:10:26: RUF012 Mutable class attributes should be annotated with `typin
|
||||
12 | class_variable: typing.ClassVar[list[int]] = []
|
||||
|
|
||||
|
||||
RUF012.py:16:34: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
RUF012.py:17:34: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
|
|
||||
15 | class B:
|
||||
16 | mutable_default: list[int] = []
|
||||
16 | class B:
|
||||
17 | mutable_default: list[int] = []
|
||||
| ^^ RUF012
|
||||
17 | immutable_annotation: Sequence[int] = []
|
||||
18 | without_annotation = []
|
||||
18 | immutable_annotation: Sequence[int] = []
|
||||
19 | without_annotation = []
|
||||
|
|
||||
|
||||
RUF012.py:18:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
RUF012.py:19:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
|
|
||||
16 | mutable_default: list[int] = []
|
||||
17 | immutable_annotation: Sequence[int] = []
|
||||
18 | without_annotation = []
|
||||
17 | mutable_default: list[int] = []
|
||||
18 | immutable_annotation: Sequence[int] = []
|
||||
19 | without_annotation = []
|
||||
| ^^ RUF012
|
||||
19 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
20 | class_variable: ClassVar[list[int]] = []
|
||||
20 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
21 | class_variable: ClassVar[list[int]] = []
|
||||
|
|
||||
|
||||
RUF012.py:30:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
RUF012.py:32:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
|
|
||||
28 | mutable_default: list[int] = []
|
||||
29 | immutable_annotation: Sequence[int] = []
|
||||
30 | without_annotation = []
|
||||
30 | mutable_default: list[int] = []
|
||||
31 | immutable_annotation: Sequence[int] = []
|
||||
32 | without_annotation = []
|
||||
| ^^ RUF012
|
||||
31 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
32 | perfectly_fine: list[int] = field(default_factory=list)
|
||||
33 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
34 | perfectly_fine: list[int] = field(default_factory=list)
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ source: crates/ruff/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
21 | def f(arg: int = None): # RUF011
|
||||
21 | def f(arg: int = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
22 | pass
|
||||
|
|
||||
@@ -13,15 +13,15 @@ RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
18 18 | pass
|
||||
19 19 |
|
||||
20 20 |
|
||||
21 |-def f(arg: int = None): # RUF011
|
||||
21 |+def f(arg: int | None = None): # RUF011
|
||||
21 |-def f(arg: int = None): # RUF013
|
||||
21 |+def f(arg: int | None = None): # RUF013
|
||||
22 22 | pass
|
||||
23 23 |
|
||||
24 24 |
|
||||
|
||||
RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
25 | def f(arg: str = None): # RUF011
|
||||
25 | def f(arg: str = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
26 | pass
|
||||
|
|
||||
@@ -31,15 +31,15 @@ RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
22 22 | pass
|
||||
23 23 |
|
||||
24 24 |
|
||||
25 |-def f(arg: str = None): # RUF011
|
||||
25 |+def f(arg: str | None = None): # RUF011
|
||||
25 |-def f(arg: str = None): # RUF013
|
||||
25 |+def f(arg: str | None = None): # RUF013
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
|
||||
RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
29 | def f(arg: typing.List[str] = None): # RUF011
|
||||
29 | def f(arg: typing.List[str] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^ RUF013
|
||||
30 | pass
|
||||
|
|
||||
@@ -49,15 +49,15 @@ RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
29 |-def f(arg: typing.List[str] = None): # RUF011
|
||||
29 |+def f(arg: typing.List[str] | None = None): # RUF011
|
||||
29 |-def f(arg: typing.List[str] = None): # RUF013
|
||||
29 |+def f(arg: typing.List[str] | None = None): # RUF013
|
||||
30 30 | pass
|
||||
31 31 |
|
||||
32 32 |
|
||||
|
||||
RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
33 | def f(arg: Tuple[str] = None): # RUF011
|
||||
33 | def f(arg: Tuple[str] = None): # RUF013
|
||||
| ^^^^^^^^^^ RUF013
|
||||
34 | pass
|
||||
|
|
||||
@@ -67,15 +67,15 @@ RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
30 30 | pass
|
||||
31 31 |
|
||||
32 32 |
|
||||
33 |-def f(arg: Tuple[str] = None): # RUF011
|
||||
33 |+def f(arg: Tuple[str] | None = None): # RUF011
|
||||
33 |-def f(arg: Tuple[str] = None): # RUF013
|
||||
33 |+def f(arg: Tuple[str] | None = None): # RUF013
|
||||
34 34 | pass
|
||||
35 35 |
|
||||
36 36 |
|
||||
|
||||
RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
67 | def f(arg: Union = None): # RUF011
|
||||
67 | def f(arg: Union = None): # RUF013
|
||||
| ^^^^^ RUF013
|
||||
68 | pass
|
||||
|
|
||||
@@ -85,15 +85,15 @@ RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
64 64 | pass
|
||||
65 65 |
|
||||
66 66 |
|
||||
67 |-def f(arg: Union = None): # RUF011
|
||||
67 |+def f(arg: Union | None = None): # RUF011
|
||||
67 |-def f(arg: Union = None): # RUF013
|
||||
67 |+def f(arg: Union | None = None): # RUF013
|
||||
68 68 | pass
|
||||
69 69 |
|
||||
70 70 |
|
||||
|
||||
RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
71 | def f(arg: Union[int, str] = None): # RUF011
|
||||
71 | def f(arg: Union[int, str] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^ RUF013
|
||||
72 | pass
|
||||
|
|
||||
@@ -103,15 +103,15 @@ RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
68 68 | pass
|
||||
69 69 |
|
||||
70 70 |
|
||||
71 |-def f(arg: Union[int, str] = None): # RUF011
|
||||
71 |+def f(arg: Union[int, str] | None = None): # RUF011
|
||||
71 |-def f(arg: Union[int, str] = None): # RUF013
|
||||
71 |+def f(arg: Union[int, str] | None = None): # RUF013
|
||||
72 72 | pass
|
||||
73 73 |
|
||||
74 74 |
|
||||
|
||||
RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
75 | def f(arg: typing.Union[int, str] = None): # RUF011
|
||||
75 | def f(arg: typing.Union[int, str] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
76 | pass
|
||||
|
|
||||
@@ -121,15 +121,15 @@ RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
72 72 | pass
|
||||
73 73 |
|
||||
74 74 |
|
||||
75 |-def f(arg: typing.Union[int, str] = None): # RUF011
|
||||
75 |+def f(arg: typing.Union[int, str] | None = None): # RUF011
|
||||
75 |-def f(arg: typing.Union[int, str] = None): # RUF013
|
||||
75 |+def f(arg: typing.Union[int, str] | None = None): # RUF013
|
||||
76 76 | pass
|
||||
77 77 |
|
||||
78 78 |
|
||||
|
||||
RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
94 | def f(arg: int | float = None): # RUF011
|
||||
94 | def f(arg: int | float = None): # RUF013
|
||||
| ^^^^^^^^^^^ RUF013
|
||||
95 | pass
|
||||
|
|
||||
@@ -139,15 +139,15 @@ RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
91 91 | pass
|
||||
92 92 |
|
||||
93 93 |
|
||||
94 |-def f(arg: int | float = None): # RUF011
|
||||
94 |+def f(arg: int | float | None = None): # RUF011
|
||||
94 |-def f(arg: int | float = None): # RUF013
|
||||
94 |+def f(arg: int | float | None = None): # RUF013
|
||||
95 95 | pass
|
||||
96 96 |
|
||||
97 97 |
|
||||
|
||||
RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
98 | def f(arg: int | float | str | bytes = None): # RUF011
|
||||
98 | def f(arg: int | float | str | bytes = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
99 | pass
|
||||
|
|
||||
@@ -157,15 +157,15 @@ RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
95 95 | pass
|
||||
96 96 |
|
||||
97 97 |
|
||||
98 |-def f(arg: int | float | str | bytes = None): # RUF011
|
||||
98 |+def f(arg: int | float | str | bytes | None = None): # RUF011
|
||||
98 |-def f(arg: int | float | str | bytes = None): # RUF013
|
||||
98 |+def f(arg: int | float | str | bytes | None = None): # RUF013
|
||||
99 99 | pass
|
||||
100 100 |
|
||||
101 101 |
|
||||
|
||||
RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
113 | def f(arg: Literal[1, "foo"] = None): # RUF011
|
||||
113 | def f(arg: Literal[1, "foo"] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^ RUF013
|
||||
114 | pass
|
||||
|
|
||||
@@ -175,15 +175,15 @@ RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
110 110 | pass
|
||||
111 111 |
|
||||
112 112 |
|
||||
113 |-def f(arg: Literal[1, "foo"] = None): # RUF011
|
||||
113 |+def f(arg: Literal[1, "foo"] | None = None): # RUF011
|
||||
113 |-def f(arg: Literal[1, "foo"] = None): # RUF013
|
||||
113 |+def f(arg: Literal[1, "foo"] | None = None): # RUF013
|
||||
114 114 | pass
|
||||
115 115 |
|
||||
116 116 |
|
||||
|
||||
RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF011
|
||||
117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
118 | pass
|
||||
|
|
||||
@@ -193,15 +193,15 @@ RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
114 114 | pass
|
||||
115 115 |
|
||||
116 116 |
|
||||
117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF011
|
||||
117 |+def f(arg: typing.Literal[1, "foo", True] | None = None): # RUF011
|
||||
117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
|
||||
117 |+def f(arg: typing.Literal[1, "foo", True] | None = None): # RUF013
|
||||
118 118 | pass
|
||||
119 119 |
|
||||
120 120 |
|
||||
|
||||
RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
136 | def f(arg: Annotated[int, ...] = None): # RUF011
|
||||
136 | def f(arg: Annotated[int, ...] = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
137 | pass
|
||||
|
|
||||
@@ -211,15 +211,15 @@ RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
133 133 | pass
|
||||
134 134 |
|
||||
135 135 |
|
||||
136 |-def f(arg: Annotated[int, ...] = None): # RUF011
|
||||
136 |+def f(arg: Annotated[int | None, ...] = None): # RUF011
|
||||
136 |-def f(arg: Annotated[int, ...] = None): # RUF013
|
||||
136 |+def f(arg: Annotated[int | None, ...] = None): # RUF013
|
||||
137 137 | pass
|
||||
138 138 |
|
||||
139 139 |
|
||||
|
||||
RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011
|
||||
140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
|
||||
| ^^^^^^^^^ RUF013
|
||||
141 | pass
|
||||
|
|
||||
@@ -229,8 +229,8 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
137 137 | pass
|
||||
138 138 |
|
||||
139 139 |
|
||||
140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011
|
||||
140 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None): # RUF011
|
||||
140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
|
||||
140 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None): # RUF013
|
||||
141 141 | pass
|
||||
142 142 |
|
||||
143 143 |
|
||||
@@ -238,10 +238,10 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
155 | def f(
|
||||
156 | arg1: int = None, # RUF011
|
||||
156 | arg1: int = None, # RUF013
|
||||
| ^^^ RUF013
|
||||
157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
|
|
||||
= help: Convert to `T | None`
|
||||
|
||||
@@ -249,19 +249,19 @@ RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
153 153 |
|
||||
154 154 |
|
||||
155 155 | def f(
|
||||
156 |- arg1: int = None, # RUF011
|
||||
156 |+ arg1: int | None = None, # RUF011
|
||||
157 157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
156 |- arg1: int = None, # RUF013
|
||||
156 |+ arg1: int | None = None, # RUF013
|
||||
157 157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
159 159 | ):
|
||||
|
||||
RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
155 | def f(
|
||||
156 | arg1: int = None, # RUF011
|
||||
157 | arg2: Union[int, float] = None, # RUF011
|
||||
156 | arg1: int = None, # RUF013
|
||||
157 | arg2: Union[int, float] = None, # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^ RUF013
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
159 | ):
|
||||
|
|
||||
= help: Convert to `T | None`
|
||||
@@ -269,18 +269,18 @@ RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
ℹ Suggested fix
|
||||
154 154 |
|
||||
155 155 | def f(
|
||||
156 156 | arg1: int = None, # RUF011
|
||||
157 |- arg2: Union[int, float] = None, # RUF011
|
||||
157 |+ arg2: Union[int, float] | None = None, # RUF011
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
156 156 | arg1: int = None, # RUF013
|
||||
157 |- arg2: Union[int, float] = None, # RUF013
|
||||
157 |+ arg2: Union[int, float] | None = None, # RUF013
|
||||
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
159 159 | ):
|
||||
160 160 | pass
|
||||
|
||||
RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
156 | arg1: int = None, # RUF011
|
||||
157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
156 | arg1: int = None, # RUF013
|
||||
157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 | arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
| ^^^^^^^^^^^^^^^^ RUF013
|
||||
159 | ):
|
||||
160 | pass
|
||||
@@ -289,17 +289,17 @@ RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
||||
ℹ Suggested fix
|
||||
155 155 | def f(
|
||||
156 156 | arg1: int = None, # RUF011
|
||||
157 157 | arg2: Union[int, float] = None, # RUF011
|
||||
158 |- arg3: Literal[1, 2, 3] = None, # RUF011
|
||||
158 |+ arg3: Literal[1, 2, 3] | None = None, # RUF011
|
||||
156 156 | arg1: int = None, # RUF013
|
||||
157 157 | arg2: Union[int, float] = None, # RUF013
|
||||
158 |- arg3: Literal[1, 2, 3] = None, # RUF013
|
||||
158 |+ arg3: Literal[1, 2, 3] | None = None, # RUF013
|
||||
159 159 | ):
|
||||
160 160 | pass
|
||||
161 161 |
|
||||
|
||||
RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011
|
||||
186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
187 | pass
|
||||
|
|
||||
@@ -309,10 +309,72 @@ RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
183 183 | pass
|
||||
184 184 |
|
||||
185 185 |
|
||||
186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011
|
||||
186 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None): # RUF011
|
||||
186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
|
||||
186 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None): # RUF013
|
||||
187 187 | pass
|
||||
188 188 |
|
||||
189 189 |
|
||||
|
||||
RUF013_0.py:193:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
193 | def f(arg: "int" = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
194 | pass
|
||||
|
|
||||
= help: Convert to `T | None`
|
||||
|
||||
ℹ Suggested fix
|
||||
190 190 | # Quoted
|
||||
191 191 |
|
||||
192 192 |
|
||||
193 |-def f(arg: "int" = None): # RUF013
|
||||
193 |+def f(arg: "int | None" = None): # RUF013
|
||||
194 194 | pass
|
||||
195 195 |
|
||||
196 196 |
|
||||
|
||||
RUF013_0.py:197:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
197 | def f(arg: "str" = None): # RUF013
|
||||
| ^^^ RUF013
|
||||
198 | pass
|
||||
|
|
||||
= help: Convert to `T | None`
|
||||
|
||||
ℹ Suggested fix
|
||||
194 194 | pass
|
||||
195 195 |
|
||||
196 196 |
|
||||
197 |-def f(arg: "str" = None): # RUF013
|
||||
197 |+def f(arg: "str | None" = None): # RUF013
|
||||
198 198 | pass
|
||||
199 199 |
|
||||
200 200 |
|
||||
|
||||
RUF013_0.py:201:12: RUF013 PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
201 | def f(arg: "st" "r" = None): # RUF013
|
||||
| ^^^^^^^^ RUF013
|
||||
202 | pass
|
||||
|
|
||||
= help: Convert to `T | None`
|
||||
|
||||
RUF013_0.py:209:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
||||
|
|
||||
209 | def f(arg: Union["int", "str"] = None): # RUF013
|
||||
| ^^^^^^^^^^^^^^^^^^^ RUF013
|
||||
210 | pass
|
||||
|
|
||||
= help: Convert to `T | None`
|
||||
|
||||
ℹ Suggested fix
|
||||
206 206 | pass
|
||||
207 207 |
|
||||
208 208 |
|
||||
209 |-def f(arg: Union["int", "str"] = None): # RUF013
|
||||
209 |+def f(arg: Union["int", "str"] | None = None): # RUF013
|
||||
210 210 | pass
|
||||
211 211 |
|
||||
212 212 |
|
||||
|
||||
|
||||
|
||||
@@ -1,91 +1,5 @@
|
||||
# Ruff Micro-benchmarks
|
||||
# Ruff Benchmarks
|
||||
|
||||
Benchmarks for the different Ruff-tools.
|
||||
The `ruff_benchmark` crate benchmarks the linter and the formatter on individual files.
|
||||
|
||||
## Run Benchmark
|
||||
|
||||
You can run the benchmarks with
|
||||
|
||||
```shell
|
||||
cargo benchmark
|
||||
```
|
||||
|
||||
## Benchmark driven Development
|
||||
|
||||
You can use `--save-baseline=<name>` to store an initial baseline benchmark (e.g. on `main`) and
|
||||
then use `--benchmark=<name>` to compare against that benchmark. Criterion will print a message
|
||||
telling you if the benchmark improved/regressed compared to that baseline.
|
||||
|
||||
```shell
|
||||
# Run once on your "baseline" code
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# Then iterate with
|
||||
cargo benchmark --baseline=main
|
||||
```
|
||||
|
||||
## PR Summary
|
||||
|
||||
You can use `--save-baseline` and `critcmp` to get a pretty comparison between two recordings.
|
||||
This is useful to illustrate the improvements of a PR.
|
||||
|
||||
```shell
|
||||
# On main
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# After applying your changes
|
||||
cargo benchmark --save-baseline=pr
|
||||
|
||||
critcmp main pr
|
||||
```
|
||||
|
||||
You must install [`critcmp`](https://github.com/BurntSushi/critcmp) for the comparison.
|
||||
|
||||
```bash
|
||||
cargo install critcmp
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Use `cargo benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark linter/pydantic`
|
||||
to only run the pydantic tests.
|
||||
- Use `cargo benchmark --quiet` for a more cleaned up output (without statistical relevance)
|
||||
- Use `cargo benchmark --quick` to get faster results (more prone to noise)
|
||||
|
||||
## Profiling
|
||||
|
||||
### Linux
|
||||
|
||||
Install `perf` and build `ruff_benchmark` with the `release-debug` profile and then run it with perf
|
||||
|
||||
```shell
|
||||
cargo bench -p ruff_benchmark --no-run --profile=release-debug && perf record -g -F 9999 cargo bench -p ruff_benchmark --profile=release-debug -- --profile-time=1
|
||||
```
|
||||
|
||||
Then convert the recorded profile
|
||||
|
||||
```shell
|
||||
perf script -F +pid > /tmp/test.perf
|
||||
```
|
||||
|
||||
You can now view the converted file with [firefox profiler](https://profiler.firefox.com/)
|
||||
|
||||
You can find a more in-depth guide [here](https://profiler.firefox.com/docs/#/./guide-perf-profiling)
|
||||
|
||||
### Mac
|
||||
|
||||
Install [`cargo-instruments`](https://crates.io/crates/cargo-instruments):
|
||||
|
||||
```shell
|
||||
cargo install cargo-instruments
|
||||
```
|
||||
|
||||
Then run the profiler with
|
||||
|
||||
```shell
|
||||
cargo instruments -t time --bench linter --profile release-debug -p ruff_benchmark -- --profile-time=1
|
||||
```
|
||||
|
||||
- `-t`: Specifies what to profile. Useful options are `time` to profile the wall time and `alloc`
|
||||
for profiling the allocations.
|
||||
- You may want to pass an additional filter to run a single test file
|
||||
See [CONTRIBUTING.md](../../CONTRIBUTING.md) on how to use these benchmarks.
|
||||
|
||||
@@ -81,17 +81,18 @@ pub(crate) fn run(
|
||||
// Load the caches.
|
||||
let caches = bool::from(cache).then(|| {
|
||||
package_roots
|
||||
.values()
|
||||
.flatten()
|
||||
.dedup()
|
||||
.map(|package_root| {
|
||||
let settings = resolver.resolve_all(package_root, pyproject_config);
|
||||
.iter()
|
||||
.map(|(package, package_root)| package_root.unwrap_or(package))
|
||||
.unique()
|
||||
.par_bridge()
|
||||
.map(|cache_root| {
|
||||
let settings = resolver.resolve_all(cache_root, pyproject_config);
|
||||
let cache = Cache::open(
|
||||
&settings.cli.cache_dir,
|
||||
package_root.to_path_buf(),
|
||||
cache_root.to_path_buf(),
|
||||
&settings.lib,
|
||||
);
|
||||
(&**package_root, cache)
|
||||
(cache_root, cache)
|
||||
})
|
||||
.collect::<HashMap<&Path, Cache>>()
|
||||
});
|
||||
@@ -109,8 +110,16 @@ pub(crate) fn run(
|
||||
.and_then(|package| *package);
|
||||
|
||||
let settings = resolver.resolve_all(path, pyproject_config);
|
||||
let package_root = package.unwrap_or_else(|| path.parent().unwrap_or(path));
|
||||
let cache = caches.as_ref().and_then(|caches| caches.get(&package_root));
|
||||
|
||||
let cache_root = package.unwrap_or_else(|| path.parent().unwrap_or(path));
|
||||
let cache = caches.as_ref().and_then(|caches| {
|
||||
if let Some(cache) = caches.get(&cache_root) {
|
||||
Some(cache)
|
||||
} else {
|
||||
debug!("No cache found for {}", cache_root.display());
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
lint_path(path, package, settings, cache, noqa, autofix).map_err(|e| {
|
||||
(Some(path.to_owned()), {
|
||||
|
||||
@@ -528,7 +528,22 @@ impl<Context> Format<Context> for LineSuffixBoundary {
|
||||
/// use ruff_formatter::prelude::*;
|
||||
/// use ruff_formatter::{format, write, LineWidth};
|
||||
///
|
||||
/// enum SomeLabelId {}
|
||||
/// #[derive(Debug, Copy, Clone)]
|
||||
/// enum MyLabels {
|
||||
/// Main
|
||||
/// }
|
||||
///
|
||||
/// impl tag::LabelDefinition for MyLabels {
|
||||
/// fn value(&self) -> u64 {
|
||||
/// *self as u64
|
||||
/// }
|
||||
///
|
||||
/// fn name(&self) -> &'static str {
|
||||
/// match self {
|
||||
/// Self::Main => "Main"
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> FormatResult<()> {
|
||||
/// let formatted = format!(
|
||||
@@ -537,24 +552,24 @@ impl<Context> Format<Context> for LineSuffixBoundary {
|
||||
/// let mut recording = f.start_recording();
|
||||
/// write!(recording, [
|
||||
/// labelled(
|
||||
/// LabelId::of::<SomeLabelId>(),
|
||||
/// LabelId::of(MyLabels::Main),
|
||||
/// &text("'I have a label'")
|
||||
/// )
|
||||
/// ])?;
|
||||
///
|
||||
/// let recorded = recording.stop();
|
||||
///
|
||||
/// let is_labelled = recorded.first().map_or(false, |element| element.has_label(LabelId::of::<SomeLabelId>()));
|
||||
/// let is_labelled = recorded.first().map_or(false, |element| element.has_label(LabelId::of(MyLabels::Main)));
|
||||
///
|
||||
/// if is_labelled {
|
||||
/// write!(f, [text(" has label SomeLabelId")])
|
||||
/// write!(f, [text(" has label `Main`")])
|
||||
/// } else {
|
||||
/// write!(f, [text(" doesn't have label SomeLabelId")])
|
||||
/// write!(f, [text(" doesn't have label `Main`")])
|
||||
/// }
|
||||
/// })]
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!("'I have a label' has label SomeLabelId", formatted.print()?.as_code());
|
||||
/// assert_eq!("'I have a label' has label `Main`", formatted.print()?.as_code());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use crate::format_element::PrintMode;
|
||||
use crate::{GroupId, TextSize};
|
||||
#[cfg(debug_assertions)]
|
||||
use std::any::type_name;
|
||||
use std::any::TypeId;
|
||||
use std::cell::Cell;
|
||||
use std::num::NonZeroU8;
|
||||
|
||||
@@ -235,37 +232,48 @@ impl Align {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
#[derive(Debug, Eq, Copy, Clone)]
|
||||
pub struct LabelId {
|
||||
id: TypeId,
|
||||
value: u64,
|
||||
#[cfg(debug_assertions)]
|
||||
label: &'static str,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl std::fmt::Debug for LabelId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.label)
|
||||
}
|
||||
}
|
||||
impl PartialEq for LabelId {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let is_equal = self.value == other.value;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
impl std::fmt::Debug for LabelId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::write!(f, "#{:?}", self.id)
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if is_equal {
|
||||
assert_eq!(self.name, other.name, "Two `LabelId`s with different names have the same `value`. Are you mixing labels of two different `LabelDefinition` or are the values returned by the `LabelDefinition` not unique?");
|
||||
}
|
||||
}
|
||||
|
||||
is_equal
|
||||
}
|
||||
}
|
||||
|
||||
impl LabelId {
|
||||
pub fn of<T: ?Sized + 'static>() -> Self {
|
||||
pub fn of<T: LabelDefinition>(label: T) -> Self {
|
||||
Self {
|
||||
id: TypeId::of::<T>(),
|
||||
value: label.value(),
|
||||
#[cfg(debug_assertions)]
|
||||
label: type_name::<T>(),
|
||||
name: label.name(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the valid labels of a language. You want to have at most one implementation per formatter
|
||||
/// project.
|
||||
pub trait LabelDefinition {
|
||||
/// Returns the `u64` uniquely identifying this specific label.
|
||||
fn value(&self) -> u64;
|
||||
|
||||
/// Returns the name of the label that is shown in debug builds.
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
pub enum VerbatimKind {
|
||||
Bogus,
|
||||
|
||||
@@ -192,7 +192,8 @@ fn rules_by_prefix(
|
||||
|
||||
for (code, rule) in rules {
|
||||
// Nursery rules have to be explicitly selected, so we ignore them when looking at
|
||||
// prefixes.
|
||||
// prefix-level selectors (e.g., `--select SIM10`), but add the rule itself under
|
||||
// its fully-qualified code (e.g., `--select SIM101`).
|
||||
if is_nursery(&rule.group) {
|
||||
rules_by_prefix.insert(code.clone(), vec![(rule.path.clone(), rule.attrs.clone())]);
|
||||
continue;
|
||||
@@ -329,10 +330,17 @@ fn generate_iter_impl(
|
||||
) -> TokenStream {
|
||||
let mut linter_into_iter_match_arms = quote!();
|
||||
for (linter, map) in linter_to_rules {
|
||||
let rule_paths = map.values().map(|Rule { attrs, path, .. }| {
|
||||
let rule_name = path.segments.last().unwrap();
|
||||
quote!(#(#attrs)* Rule::#rule_name)
|
||||
});
|
||||
let rule_paths = map
|
||||
.values()
|
||||
.filter(|rule| {
|
||||
// Nursery rules have to be explicitly selected, so we ignore them when looking at
|
||||
// linter-level selectors (e.g., `--select SIM`).
|
||||
!is_nursery(&rule.group)
|
||||
})
|
||||
.map(|Rule { attrs, path, .. }| {
|
||||
let rule_name = path.segments.last().unwrap();
|
||||
quote!(#(#attrs)* Rule::#rule_name)
|
||||
});
|
||||
linter_into_iter_match_arms.extend(quote! {
|
||||
Linter::#linter => vec![#(#rule_paths,)*].into_iter(),
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pub mod preorder;
|
||||
|
||||
use rustpython_ast::{ArgWithDefault, Decorator};
|
||||
use rustpython_ast::Decorator;
|
||||
use rustpython_parser::ast::{
|
||||
self, Alias, Arg, Arguments, BoolOp, CmpOp, Comprehension, Constant, ExceptHandler, Expr,
|
||||
ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, UnaryOp, WithItem,
|
||||
@@ -61,9 +61,6 @@ pub trait Visitor<'a> {
|
||||
fn visit_arg(&mut self, arg: &'a Arg) {
|
||||
walk_arg(self, arg);
|
||||
}
|
||||
fn visit_arg_with_default(&mut self, arg_with_default: &'a ArgWithDefault) {
|
||||
walk_arg_with_default(self, arg_with_default);
|
||||
}
|
||||
fn visit_keyword(&mut self, keyword: &'a Keyword) {
|
||||
walk_keyword(self, keyword);
|
||||
}
|
||||
@@ -99,10 +96,10 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
returns,
|
||||
..
|
||||
}) => {
|
||||
visitor.visit_arguments(args);
|
||||
for decorator in decorator_list {
|
||||
visitor.visit_decorator(decorator);
|
||||
}
|
||||
visitor.visit_arguments(args);
|
||||
for expr in returns {
|
||||
visitor.visit_annotation(expr);
|
||||
}
|
||||
@@ -115,10 +112,10 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
returns,
|
||||
..
|
||||
}) => {
|
||||
visitor.visit_arguments(args);
|
||||
for decorator in decorator_list {
|
||||
visitor.visit_decorator(decorator);
|
||||
}
|
||||
visitor.visit_arguments(args);
|
||||
for expr in returns {
|
||||
visitor.visit_annotation(expr);
|
||||
}
|
||||
@@ -131,15 +128,15 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
decorator_list,
|
||||
..
|
||||
}) => {
|
||||
for decorator in decorator_list {
|
||||
visitor.visit_decorator(decorator);
|
||||
}
|
||||
for expr in bases {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
for keyword in keywords {
|
||||
visitor.visit_keyword(keyword);
|
||||
}
|
||||
for decorator in decorator_list {
|
||||
visitor.visit_decorator(decorator);
|
||||
}
|
||||
visitor.visit_body(body);
|
||||
}
|
||||
Stmt::Return(ast::StmtReturn {
|
||||
@@ -180,10 +177,10 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
value,
|
||||
..
|
||||
}) => {
|
||||
visitor.visit_annotation(annotation);
|
||||
if let Some(expr) = value {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
visitor.visit_annotation(annotation);
|
||||
visitor.visit_expr(target);
|
||||
}
|
||||
Stmt::For(ast::StmtFor {
|
||||
@@ -606,17 +603,34 @@ pub fn walk_except_handler<'a, V: Visitor<'a> + ?Sized>(
|
||||
}
|
||||
|
||||
pub fn walk_arguments<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, arguments: &'a Arguments) {
|
||||
// Defaults are evaluated before annotations.
|
||||
for arg in &arguments.posonlyargs {
|
||||
visitor.visit_arg_with_default(arg);
|
||||
if let Some(default) = &arg.default {
|
||||
visitor.visit_expr(default);
|
||||
}
|
||||
}
|
||||
for arg in &arguments.args {
|
||||
visitor.visit_arg_with_default(arg);
|
||||
if let Some(default) = &arg.default {
|
||||
visitor.visit_expr(default);
|
||||
}
|
||||
}
|
||||
for arg in &arguments.kwonlyargs {
|
||||
if let Some(default) = &arg.default {
|
||||
visitor.visit_expr(default);
|
||||
}
|
||||
}
|
||||
|
||||
for arg in &arguments.posonlyargs {
|
||||
visitor.visit_arg(&arg.def);
|
||||
}
|
||||
for arg in &arguments.args {
|
||||
visitor.visit_arg(&arg.def);
|
||||
}
|
||||
if let Some(arg) = &arguments.vararg {
|
||||
visitor.visit_arg(arg);
|
||||
}
|
||||
for arg in &arguments.kwonlyargs {
|
||||
visitor.visit_arg_with_default(arg);
|
||||
visitor.visit_arg(&arg.def);
|
||||
}
|
||||
if let Some(arg) = &arguments.kwarg {
|
||||
visitor.visit_arg(arg);
|
||||
@@ -629,16 +643,6 @@ pub fn walk_arg<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, arg: &'a Arg) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_arg_with_default<'a, V: Visitor<'a> + ?Sized>(
|
||||
visitor: &mut V,
|
||||
arg_with_default: &'a ArgWithDefault,
|
||||
) {
|
||||
visitor.visit_arg(&arg_with_default.def);
|
||||
if let Some(expr) = &arg_with_default.default {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_keyword<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, keyword: &'a Keyword) {
|
||||
visitor.visit_expr(&keyword.value);
|
||||
}
|
||||
|
||||
29
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/attribute.py
vendored
Normal file
29
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/attribute.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
(
|
||||
a
|
||||
# comment
|
||||
.b # trailing comment
|
||||
)
|
||||
|
||||
(
|
||||
a
|
||||
# comment
|
||||
.b # trailing dot comment # trailing identifier comment
|
||||
)
|
||||
|
||||
(
|
||||
a
|
||||
# comment
|
||||
.b # trailing identifier comment
|
||||
)
|
||||
|
||||
|
||||
(
|
||||
a
|
||||
# comment
|
||||
. # trailing dot comment
|
||||
# in between
|
||||
b # trailing identifier comment
|
||||
)
|
||||
|
||||
|
||||
aaaaaaaaaaaaaaaaaaaaa.lllllllllllllllllllllllllllloooooooooong.chaaaaaaaaaaaaaaaaaaaaaaiiiiiiiiiiiiiiiiiiiiiiinnnnnnnn.ooooooooooooooooooooooooofffffffff.aaaaaaaaaattr
|
||||
@@ -52,3 +52,138 @@ if (
|
||||
ccccccccccc
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Left only breaks
|
||||
if [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
] & aaaaaaaaaaaaaaaaaaaaaaaaaa:
|
||||
...
|
||||
|
||||
if [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
] & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
|
||||
...
|
||||
|
||||
# Right only can break
|
||||
if aaaaaaaaaaaaaaaaaaaaaaaaaa & [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
]:
|
||||
...
|
||||
|
||||
if aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa & [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
]:
|
||||
...
|
||||
|
||||
|
||||
# Left or right can break
|
||||
if [2222, 333] & [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
]:
|
||||
...
|
||||
|
||||
if [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
] & [2222, 333]:
|
||||
...
|
||||
|
||||
if [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
] & [fffffffffffffffff, gggggggggggggggggggg, hhhhhhhhhhhhhhhhhhhhh, iiiiiiiiiiiiiiii, jjjjjjjjjjjjj]:
|
||||
...
|
||||
|
||||
if (
|
||||
# comment
|
||||
[
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
]
|
||||
) & [
|
||||
fffffffffffffffff,
|
||||
gggggggggggggggggggg,
|
||||
hhhhhhhhhhhhhhhhhhhhh,
|
||||
iiiiiiiiiiiiiiii,
|
||||
jjjjjjjjjjjjj,
|
||||
]:
|
||||
pass
|
||||
|
||||
...
|
||||
|
||||
# Nesting
|
||||
if (aaaa + b) & [
|
||||
fffffffffffffffff,
|
||||
gggggggggggggggggggg,
|
||||
hhhhhhhhhhhhhhhhhhhhh,
|
||||
iiiiiiiiiiiiiiii,
|
||||
jjjjjjjjjjjjj,
|
||||
]:
|
||||
...
|
||||
|
||||
if [
|
||||
fffffffffffffffff,
|
||||
gggggggggggggggggggg,
|
||||
hhhhhhhhhhhhhhhhhhhhh,
|
||||
iiiiiiiiiiiiiiii,
|
||||
jjjjjjjjjjjjj,
|
||||
] & (a + b):
|
||||
...
|
||||
|
||||
|
||||
if [
|
||||
fffffffffffffffff,
|
||||
gggggggggggggggggggg,
|
||||
hhhhhhhhhhhhhhhhhhhhh,
|
||||
iiiiiiiiiiiiiiii,
|
||||
jjjjjjjjjjjjj,
|
||||
] & (
|
||||
# comment
|
||||
a
|
||||
+ b
|
||||
):
|
||||
...
|
||||
|
||||
if (
|
||||
[
|
||||
fffffffffffffffff,
|
||||
gggggggggggggggggggg,
|
||||
hhhhhhhhhhhhhhhhhhhhh,
|
||||
iiiiiiiiiiiiiiii,
|
||||
jjjjjjjjjjjjj,
|
||||
]
|
||||
&
|
||||
# comment
|
||||
a + b
|
||||
):
|
||||
...
|
||||
|
||||
64
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/boolean_operation.py
vendored
Normal file
64
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/boolean_operation.py
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
if (
|
||||
self._proc
|
||||
# has the child process finished?
|
||||
and self._returncode
|
||||
# the child process has finished, but the
|
||||
# transport hasn't been notified yet?
|
||||
and self._proc.poll()
|
||||
):
|
||||
pass
|
||||
|
||||
if (
|
||||
self._proc
|
||||
and self._returncode
|
||||
and self._proc.poll()
|
||||
and self._proc
|
||||
and self._returncode
|
||||
and self._proc.poll()
|
||||
):
|
||||
...
|
||||
|
||||
if (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
and aaaaaaaaaaaaaaaaa
|
||||
and aaaaaaaaaaaaaaaaaaaaaa
|
||||
and aaaaaaaaaaaaaaaaaaaaaaaa
|
||||
and aaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
and aaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
):
|
||||
...
|
||||
|
||||
|
||||
if (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas
|
||||
and aaaaaaaaaaaaaaaaa
|
||||
):
|
||||
...
|
||||
|
||||
|
||||
if [2222, 333] and [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
]:
|
||||
...
|
||||
|
||||
if [
|
||||
aaaaaaaaaaaaa,
|
||||
bbbbbbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccccc,
|
||||
dddddddddddddddddddd,
|
||||
eeeeeeeeee,
|
||||
] and [2222, 333]:
|
||||
pass
|
||||
|
||||
# Break right only applies for boolean operations with a left and right side
|
||||
if (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
and bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
and ccccccccccccccccc
|
||||
and [dddddddddddddd, eeeeeeeeee, fffffffffffffff]
|
||||
):
|
||||
pass
|
||||
82
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py
vendored
Normal file
82
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Handle comments both when lower and upper exist and when they don't
|
||||
a1 = "a"[
|
||||
# a
|
||||
1 # b
|
||||
: # c
|
||||
2 # d
|
||||
]
|
||||
a2 = "a"[
|
||||
# a
|
||||
# b
|
||||
: # c
|
||||
# d
|
||||
]
|
||||
|
||||
# Check all places where comments can exist
|
||||
b1 = "b"[ # a
|
||||
# b
|
||||
1 # c
|
||||
# d
|
||||
: # e
|
||||
# f
|
||||
2 # g
|
||||
# h
|
||||
: # i
|
||||
# j
|
||||
3 # k
|
||||
# l
|
||||
]
|
||||
|
||||
# Handle the spacing from the colon correctly with upper leading comments
|
||||
c1 = "c"[
|
||||
1
|
||||
: # e
|
||||
# f
|
||||
2
|
||||
]
|
||||
c2 = "c"[
|
||||
1
|
||||
: # e
|
||||
2
|
||||
]
|
||||
c3 = "c"[
|
||||
1
|
||||
:
|
||||
# f
|
||||
2
|
||||
]
|
||||
c4 = "c"[
|
||||
1
|
||||
: # f
|
||||
2
|
||||
]
|
||||
|
||||
# End of line comments
|
||||
d1 = "d"[ # comment
|
||||
:
|
||||
]
|
||||
d2 = "d"[ # comment
|
||||
1:
|
||||
]
|
||||
d3 = "d"[
|
||||
1 # comment
|
||||
:
|
||||
]
|
||||
|
||||
# Spacing around the colon(s)
|
||||
def a():
|
||||
...
|
||||
|
||||
e00 = "e"[:]
|
||||
e01 = "e"[:1]
|
||||
e02 = "e"[: a()]
|
||||
e10 = "e"[1:]
|
||||
e11 = "e"[1:1]
|
||||
e12 = "e"[1 : a()]
|
||||
e20 = "e"[a() :]
|
||||
e21 = "e"[a() : 1]
|
||||
e22 = "e"[a() : a()]
|
||||
e200 = "e"[a() :: ]
|
||||
e201 = "e"[a() :: 1]
|
||||
e202 = "e"[a() :: a()]
|
||||
e210 = "e"[a() : 1 :]
|
||||
138
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/unary.py
vendored
Normal file
138
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/unary.py
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
if not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:
|
||||
pass
|
||||
|
||||
a = True
|
||||
not a
|
||||
|
||||
b = 10
|
||||
-b
|
||||
+b
|
||||
|
||||
## Leading operand comments
|
||||
|
||||
if not (
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
if ~(
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb):
|
||||
pass
|
||||
|
||||
if -(
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb):
|
||||
pass
|
||||
|
||||
|
||||
if +(
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb):
|
||||
pass
|
||||
|
||||
if (
|
||||
not
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
if (
|
||||
~
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb):
|
||||
pass
|
||||
|
||||
if (
|
||||
-
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb):
|
||||
pass
|
||||
|
||||
|
||||
if (
|
||||
+
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb):
|
||||
pass
|
||||
|
||||
## Parentheses
|
||||
|
||||
if (
|
||||
# unary comment
|
||||
not
|
||||
# operand comment
|
||||
(
|
||||
# comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
if (not (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
if aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa & (not (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
if (
|
||||
not (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
)
|
||||
& aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
## Trailing operator comments
|
||||
|
||||
if (
|
||||
not # comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
if (
|
||||
~ # comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
):
|
||||
pass
|
||||
|
||||
if (
|
||||
- # comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
if (
|
||||
+ # comment
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
## Varia
|
||||
|
||||
if not \
|
||||
a:
|
||||
pass
|
||||
34
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/for.py
vendored
Normal file
34
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/for.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
for x in y: # trailing test comment
|
||||
pass # trailing last statement comment
|
||||
|
||||
# trailing for body comment
|
||||
|
||||
# leading else comment
|
||||
|
||||
else: # trailing else comment
|
||||
pass
|
||||
|
||||
# trailing else body comment
|
||||
|
||||
|
||||
for aVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn in anotherVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn: # trailing comment
|
||||
pass
|
||||
|
||||
else:
|
||||
...
|
||||
|
||||
for (
|
||||
x,
|
||||
y,
|
||||
) in z: # comment
|
||||
...
|
||||
|
||||
|
||||
# remove brackets around x,y but keep them around z,w
|
||||
for (x, y) in (z, w):
|
||||
...
|
||||
|
||||
|
||||
# type comment
|
||||
for x in (): # type: int
|
||||
...
|
||||
@@ -97,3 +97,137 @@ def foo(
|
||||
b=3 + 2 # comment
|
||||
):
|
||||
...
|
||||
|
||||
|
||||
# Comments on the slash or the star, both of which don't have a node
|
||||
def f11(
|
||||
a,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
b,
|
||||
):
|
||||
pass
|
||||
|
||||
def f12(
|
||||
a=1,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
b=2,
|
||||
):
|
||||
pass
|
||||
|
||||
def f13(
|
||||
a,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
):
|
||||
pass
|
||||
|
||||
def f21(
|
||||
a=1,
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
b=2,
|
||||
):
|
||||
pass
|
||||
|
||||
def f22(
|
||||
a,
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
b,
|
||||
):
|
||||
pass
|
||||
|
||||
def f23(
|
||||
a,
|
||||
# keyword only comment, leading
|
||||
*args, # keyword only comment, trailing
|
||||
b,
|
||||
):
|
||||
pass
|
||||
|
||||
def f24(
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
a
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def f31(
|
||||
a=1,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
b=2,
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
c=3,
|
||||
):
|
||||
pass
|
||||
|
||||
def f32(
|
||||
a,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
b,
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
c,
|
||||
):
|
||||
pass
|
||||
|
||||
def f33(
|
||||
a,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
# keyword only comment, leading
|
||||
*args, # keyword only comment, trailing
|
||||
c,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def f34(
|
||||
a,
|
||||
# positional only comment, leading
|
||||
/, # positional only comment, trailing
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
c,
|
||||
):
|
||||
pass
|
||||
|
||||
def f35(
|
||||
# keyword only comment, leading
|
||||
*, # keyword only comment, trailing
|
||||
c,
|
||||
):
|
||||
pass
|
||||
|
||||
# Multiple trailing comments
|
||||
def f41(
|
||||
a,
|
||||
/ # 1
|
||||
, # 2
|
||||
# 3
|
||||
* # 4
|
||||
, # 5
|
||||
c,
|
||||
):
|
||||
pass
|
||||
|
||||
# Multiple trailing comments strangely places. The goal here is only stable formatting,
|
||||
# the comments are placed to strangely to keep their relative position intact
|
||||
def f42(
|
||||
a,
|
||||
/ # 1
|
||||
# 2
|
||||
, # 3
|
||||
# 4
|
||||
* # 5
|
||||
# 6
|
||||
, # 7
|
||||
c,
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -35,3 +35,30 @@ elif aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
|
||||
|
||||
else:
|
||||
...
|
||||
|
||||
# Regression test: Don't drop the trailing comment by associating it with the elif
|
||||
# instead of the else.
|
||||
# Originally found in https://github.com/python/cpython/blob/ab3823a97bdeefb0266b3c8d493f7f6223ce3686/Lib/dataclasses.py#L539
|
||||
|
||||
if "if 1":
|
||||
pass
|
||||
elif "elif 1":
|
||||
pass
|
||||
# Don't drop this comment 1
|
||||
x = 1
|
||||
|
||||
if "if 2":
|
||||
pass
|
||||
elif "elif 2":
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
# Don't drop this comment 2
|
||||
x = 2
|
||||
|
||||
if "if 3":
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
# Don't drop this comment 3
|
||||
x = 3
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Debug for DebugComment<'_> {
|
||||
|
||||
strut
|
||||
.field("text", &self.comment.slice.text(self.source_code))
|
||||
.field("position", &self.comment.position);
|
||||
.field("position", &self.comment.line_position);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
strut.field("formatted", &self.comment.formatted.get());
|
||||
@@ -177,7 +177,7 @@ impl Debug for DebugNodeCommentSlice<'_> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::comments::map::MultiMap;
|
||||
use crate::comments::{CommentTextPosition, Comments, CommentsMap, SourceComment};
|
||||
use crate::comments::{CommentLinePosition, Comments, CommentsMap, SourceComment};
|
||||
use insta::assert_debug_snapshot;
|
||||
use ruff_formatter::SourceCode;
|
||||
use ruff_python_ast::node::AnyNode;
|
||||
@@ -208,7 +208,7 @@ break;
|
||||
continue_statement.as_ref().into(),
|
||||
SourceComment::new(
|
||||
source_code.slice(TextRange::at(TextSize::new(0), TextSize::new(17))),
|
||||
CommentTextPosition::OwnLine,
|
||||
CommentLinePosition::OwnLine,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -216,7 +216,7 @@ break;
|
||||
continue_statement.as_ref().into(),
|
||||
SourceComment::new(
|
||||
source_code.slice(TextRange::at(TextSize::new(28), TextSize::new(10))),
|
||||
CommentTextPosition::EndOfLine,
|
||||
CommentLinePosition::EndOfLine,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -224,7 +224,7 @@ break;
|
||||
break_statement.as_ref().into(),
|
||||
SourceComment::new(
|
||||
source_code.slice(TextRange::at(TextSize::new(39), TextSize::new(15))),
|
||||
CommentTextPosition::OwnLine,
|
||||
CommentLinePosition::OwnLine,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
||||
{
|
||||
let slice = trailing.slice();
|
||||
|
||||
has_trailing_own_line_comment |= trailing.position().is_own_line();
|
||||
has_trailing_own_line_comment |= trailing.line_position().is_own_line();
|
||||
|
||||
if has_trailing_own_line_comment {
|
||||
let lines_before_comment = lines_before(slice.start(), f.context().contents());
|
||||
@@ -208,7 +208,7 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
||||
.iter()
|
||||
.filter(|comment| comment.is_unformatted())
|
||||
{
|
||||
if first && comment.position().is_end_of_line() {
|
||||
if first && comment.line_position().is_end_of_line() {
|
||||
write!(f, [space(), space()])?;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,14 +117,14 @@ pub(crate) struct SourceComment {
|
||||
slice: SourceCodeSlice,
|
||||
/// Whether the comment has been formatted or not.
|
||||
formatted: Cell<bool>,
|
||||
position: CommentTextPosition,
|
||||
line_position: CommentLinePosition,
|
||||
}
|
||||
|
||||
impl SourceComment {
|
||||
fn new(slice: SourceCodeSlice, position: CommentTextPosition) -> Self {
|
||||
fn new(slice: SourceCodeSlice, position: CommentLinePosition) -> Self {
|
||||
Self {
|
||||
slice,
|
||||
position,
|
||||
line_position: position,
|
||||
formatted: Cell::new(false),
|
||||
}
|
||||
}
|
||||
@@ -135,8 +135,8 @@ impl SourceComment {
|
||||
&self.slice
|
||||
}
|
||||
|
||||
pub(crate) const fn position(&self) -> CommentTextPosition {
|
||||
self.position
|
||||
pub(crate) const fn line_position(&self) -> CommentLinePosition {
|
||||
self.line_position
|
||||
}
|
||||
|
||||
/// Marks the comment as formatted
|
||||
@@ -163,7 +163,7 @@ impl SourceComment {
|
||||
|
||||
/// The position of a comment in the source text.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum CommentTextPosition {
|
||||
pub(crate) enum CommentLinePosition {
|
||||
/// A comment that is on the same line as the preceding token and is separated by at least one line break from the following token.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -176,7 +176,7 @@ pub(crate) enum CommentTextPosition {
|
||||
/// ```
|
||||
///
|
||||
/// `# comment` is an end of line comments because it is separated by at least one line break from the following token `b`.
|
||||
/// Comments that not only end, but also start on a new line are [`OwnLine`](CommentTextPosition::OwnLine) comments.
|
||||
/// Comments that not only end, but also start on a new line are [`OwnLine`](CommentLinePosition::OwnLine) comments.
|
||||
EndOfLine,
|
||||
|
||||
/// A Comment that is separated by at least one line break from the preceding token.
|
||||
@@ -193,13 +193,13 @@ pub(crate) enum CommentTextPosition {
|
||||
OwnLine,
|
||||
}
|
||||
|
||||
impl CommentTextPosition {
|
||||
impl CommentLinePosition {
|
||||
pub(crate) const fn is_own_line(self) -> bool {
|
||||
matches!(self, CommentTextPosition::OwnLine)
|
||||
matches!(self, CommentLinePosition::OwnLine)
|
||||
}
|
||||
|
||||
pub(crate) const fn is_end_of_line(self) -> bool {
|
||||
matches!(self, CommentTextPosition::EndOfLine)
|
||||
matches!(self, CommentLinePosition::EndOfLine)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ impl<'a> Comments<'a> {
|
||||
{
|
||||
self.trailing_comments(node)
|
||||
.iter()
|
||||
.any(|comment| comment.position().is_own_line())
|
||||
.any(|comment| comment.line_position().is_own_line())
|
||||
}
|
||||
|
||||
/// Returns an iterator over the [leading](self#leading-comments) and [trailing comments](self#trailing-comments) of `node`.
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use rustpython_parser::ast::Ranged;
|
||||
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use crate::comments::visitor::{CommentPlacement, DecoratedComment};
|
||||
use crate::expression::expr_slice::{assign_comment_in_slice, ExprSliceCommentSection};
|
||||
use crate::other::arguments::{
|
||||
assign_argument_separator_comment_placement, find_argument_separators,
|
||||
};
|
||||
use crate::trivia::{first_non_trivia_token_rev, SimpleTokenizer, Token, TokenKind};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::whitespace;
|
||||
use ruff_python_whitespace::{PythonWhitespace, UniversalNewlines};
|
||||
|
||||
use crate::comments::visitor::{CommentPlacement, DecoratedComment};
|
||||
use crate::comments::CommentTextPosition;
|
||||
use crate::trivia::{SimpleTokenizer, Token, TokenKind};
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::{Expr, ExprSlice, Ranged};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Implements the custom comment placement logic.
|
||||
pub(super) fn place_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
mut comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
handle_in_between_except_handlers_or_except_handler_and_else_or_finally_comment(
|
||||
comment, locator,
|
||||
)
|
||||
.or_else(|comment| handle_match_comment(comment, locator))
|
||||
.or_else(|comment| handle_in_between_bodies_own_line_comment(comment, locator))
|
||||
.or_else(|comment| handle_in_between_bodies_end_of_line_comment(comment, locator))
|
||||
.or_else(|comment| handle_trailing_body_comment(comment, locator))
|
||||
.or_else(handle_trailing_end_of_line_body_comment)
|
||||
.or_else(|comment| handle_trailing_end_of_line_condition_comment(comment, locator))
|
||||
.or_else(|comment| {
|
||||
handle_module_level_own_line_comment_before_class_or_function_comment(comment, locator)
|
||||
})
|
||||
.or_else(|comment| handle_positional_only_arguments_separator_comment(comment, locator))
|
||||
.or_else(|comment| handle_trailing_binary_expression_left_or_operator_comment(comment, locator))
|
||||
.or_else(handle_leading_function_with_decorators_comment)
|
||||
.or_else(|comment| handle_dict_unpacking_comment(comment, locator))
|
||||
static HANDLERS: &[for<'a> fn(DecoratedComment<'a>, &Locator) -> CommentPlacement<'a>] = &[
|
||||
handle_in_between_except_handlers_or_except_handler_and_else_or_finally_comment,
|
||||
handle_match_comment,
|
||||
handle_in_between_bodies_own_line_comment,
|
||||
handle_in_between_bodies_end_of_line_comment,
|
||||
handle_trailing_body_comment,
|
||||
handle_trailing_end_of_line_body_comment,
|
||||
handle_trailing_end_of_line_condition_comment,
|
||||
handle_module_level_own_line_comment_before_class_or_function_comment,
|
||||
handle_arguments_separator_comment,
|
||||
handle_trailing_binary_expression_left_or_operator_comment,
|
||||
handle_leading_function_with_decorators_comment,
|
||||
handle_dict_unpacking_comment,
|
||||
handle_slice_comments,
|
||||
handle_attribute_comment,
|
||||
];
|
||||
for handler in HANDLERS {
|
||||
comment = match handler(comment, locator) {
|
||||
CommentPlacement::Default(comment) => comment,
|
||||
placement => return placement,
|
||||
};
|
||||
}
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
/// Handles leading comments in front of a match case or a trailing comment of the `match` statement.
|
||||
@@ -49,7 +56,7 @@ fn handle_match_comment<'a>(
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
// Must be an own line comment after the last statement in a match case
|
||||
if comment.text_position().is_end_of_line() || comment.following_node().is_some() {
|
||||
if comment.line_position().is_end_of_line() || comment.following_node().is_some() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -147,7 +154,7 @@ fn handle_in_between_except_handlers_or_except_handler_and_else_or_finally_comme
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
if comment.text_position().is_end_of_line() || comment.following_node().is_none() {
|
||||
if comment.line_position().is_end_of_line() || comment.following_node().is_none() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -201,7 +208,7 @@ fn handle_in_between_bodies_own_line_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
if !comment.text_position().is_own_line() {
|
||||
if !comment.line_position().is_own_line() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -310,7 +317,7 @@ fn handle_in_between_bodies_end_of_line_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
if !comment.text_position().is_end_of_line() {
|
||||
if !comment.line_position().is_end_of_line() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -393,12 +400,16 @@ fn handle_trailing_body_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
if comment.text_position().is_end_of_line() {
|
||||
if comment.line_position().is_end_of_line() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
// Only do something if the preceding node has a body (has indented statements).
|
||||
let Some(last_child) = comment.preceding_node().and_then(last_child_in_body) else {
|
||||
let Some(preceding_node) = comment.preceding_node() else {
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
|
||||
let Some(last_child) = last_child_in_body(preceding_node) else {
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
|
||||
@@ -411,8 +422,24 @@ fn handle_trailing_body_comment<'a>(
|
||||
// the indent-level doesn't depend on the tab width (the indent level must be the same if the tab width is 1 or 8).
|
||||
let comment_indentation_len = comment_indentation.len();
|
||||
|
||||
// Keep the comment on the entire statement in case it's a trailing comment
|
||||
// ```python
|
||||
// if "first if":
|
||||
// pass
|
||||
// elif "first elif":
|
||||
// pass
|
||||
// # Trailing if comment
|
||||
// ```
|
||||
// Here we keep the comment a trailing comment of the `if`
|
||||
let Some(preceding_node_indentation) = whitespace::indentation_at_offset(locator, preceding_node.start()) else {
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
if comment_indentation_len == preceding_node_indentation.len() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
let mut current_child = last_child;
|
||||
let mut parent_body = comment.preceding_node();
|
||||
let mut parent_body = Some(preceding_node);
|
||||
let mut grand_parent_body = None;
|
||||
|
||||
loop {
|
||||
@@ -483,9 +510,12 @@ fn handle_trailing_body_comment<'a>(
|
||||
/// if something.changed:
|
||||
/// do.stuff() # trailing comment
|
||||
/// ```
|
||||
fn handle_trailing_end_of_line_body_comment(comment: DecoratedComment<'_>) -> CommentPlacement<'_> {
|
||||
fn handle_trailing_end_of_line_body_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
_locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
// Must be an end of line comment
|
||||
if comment.text_position().is_own_line() {
|
||||
if comment.line_position().is_own_line() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -526,7 +556,7 @@ fn handle_trailing_end_of_line_condition_comment<'a>(
|
||||
use ruff_python_ast::prelude::*;
|
||||
|
||||
// Must be an end of line comment
|
||||
if comment.text_position().is_own_line() {
|
||||
if comment.line_position().is_own_line() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -602,18 +632,11 @@ fn handle_trailing_end_of_line_condition_comment<'a>(
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
/// Attaches comments for the positional-only arguments separator `/` as trailing comments to the
|
||||
/// enclosing [`Arguments`] node.
|
||||
/// Attaches comments for the positional only arguments separator `/` or the keywords only arguments
|
||||
/// separator `*` as dangling comments to the enclosing [`Arguments`] node.
|
||||
///
|
||||
/// ```python
|
||||
/// def test(
|
||||
/// a,
|
||||
/// # Positional arguments only after here
|
||||
/// /, # trailing positional argument comment.
|
||||
/// b,
|
||||
/// ): pass
|
||||
/// ```
|
||||
fn handle_positional_only_arguments_separator_comment<'a>(
|
||||
/// See [`assign_argument_separator_comment_placement`]
|
||||
fn handle_arguments_separator_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
@@ -621,45 +644,19 @@ fn handle_positional_only_arguments_separator_comment<'a>(
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
|
||||
// Using the `/` without any leading arguments is a syntax error.
|
||||
let Some(last_argument_or_default) = comment.preceding_node() else {
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
|
||||
let is_last_positional_argument =
|
||||
are_same_optional(last_argument_or_default, arguments.posonlyargs.last());
|
||||
|
||||
if !is_last_positional_argument {
|
||||
return CommentPlacement::Default(comment);
|
||||
let (slash, star) = find_argument_separators(locator.contents(), arguments);
|
||||
let comment_range = comment.slice().range();
|
||||
let placement = assign_argument_separator_comment_placement(
|
||||
slash.as_ref(),
|
||||
star.as_ref(),
|
||||
comment_range,
|
||||
comment.line_position(),
|
||||
);
|
||||
if placement.is_some() {
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
|
||||
let trivia_end = comment
|
||||
.following_node()
|
||||
.map_or(arguments.end(), |following| following.start());
|
||||
let trivia_range = TextRange::new(last_argument_or_default.end(), trivia_end);
|
||||
|
||||
if let Some(slash_offset) = find_pos_only_slash_offset(trivia_range, locator) {
|
||||
let comment_start = comment.slice().range().start();
|
||||
let is_slash_comment = match comment.text_position() {
|
||||
CommentTextPosition::EndOfLine => {
|
||||
let preceding_end_line = locator.line_end(last_argument_or_default.end());
|
||||
let slash_comments_start = preceding_end_line.min(slash_offset);
|
||||
|
||||
comment_start >= slash_comments_start
|
||||
&& locator.line_end(slash_offset) > comment_start
|
||||
}
|
||||
CommentTextPosition::OwnLine => comment_start < slash_offset,
|
||||
};
|
||||
|
||||
if is_slash_comment {
|
||||
CommentPlacement::dangling(comment.enclosing_node(), comment)
|
||||
} else {
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
} else {
|
||||
// Should not happen, but let's go with it
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
/// Handles comments between the left side and the operator of a binary expression (trailing comments of the left),
|
||||
@@ -711,7 +708,7 @@ fn handle_trailing_binary_expression_left_or_operator_comment<'a>(
|
||||
// )
|
||||
// ```
|
||||
CommentPlacement::trailing(AnyNodeRef::from(binary_expression.left.as_ref()), comment)
|
||||
} else if comment.text_position().is_end_of_line() {
|
||||
} else if comment.line_position().is_end_of_line() {
|
||||
// Is the operator on its own line.
|
||||
if locator.contains_line_break(TextRange::new(
|
||||
binary_expression.left.end(),
|
||||
@@ -800,7 +797,7 @@ fn handle_module_level_own_line_comment_before_class_or_function_comment<'a>(
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
// Only applies for own line comments on the module level...
|
||||
if !comment.text_position().is_own_line() || !comment.enclosing_node().is_module() {
|
||||
if !comment.line_position().is_own_line() || !comment.enclosing_node().is_module() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
@@ -829,33 +826,85 @@ fn handle_module_level_own_line_comment_before_class_or_function_comment<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the offset of the `/` that separates the positional only and arguments from the other arguments.
|
||||
/// Returns `None` if the positional only separator `/` isn't present in the specified range.
|
||||
fn find_pos_only_slash_offset(
|
||||
between_arguments_range: TextRange,
|
||||
/// Handles the attaching comments left or right of the colon in a slice as trailing comment of the
|
||||
/// preceding node or leading comment of the following node respectively.
|
||||
/// ```python
|
||||
/// a = "input"[
|
||||
/// 1 # c
|
||||
/// # d
|
||||
/// :2
|
||||
/// ]
|
||||
/// ```
|
||||
fn handle_slice_comments<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> Option<TextSize> {
|
||||
let mut tokens =
|
||||
SimpleTokenizer::new(locator.contents(), between_arguments_range).skip_trivia();
|
||||
|
||||
if let Some(comma) = tokens.next() {
|
||||
debug_assert_eq!(comma.kind(), TokenKind::Comma);
|
||||
|
||||
if let Some(maybe_slash) = tokens.next() {
|
||||
if maybe_slash.kind() == TokenKind::Slash {
|
||||
return Some(maybe_slash.start());
|
||||
) -> CommentPlacement<'a> {
|
||||
let expr_slice = match comment.enclosing_node() {
|
||||
AnyNodeRef::ExprSlice(expr_slice) => expr_slice,
|
||||
AnyNodeRef::ExprSubscript(expr_subscript) => {
|
||||
if expr_subscript.value.end() < expr_subscript.slice.start() {
|
||||
if let Expr::Slice(expr_slice) = expr_subscript.slice.as_ref() {
|
||||
expr_slice
|
||||
} else {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
} else {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
debug_assert_eq!(
|
||||
maybe_slash.kind(),
|
||||
TokenKind::RParen,
|
||||
"{:?}",
|
||||
maybe_slash.kind()
|
||||
);
|
||||
}
|
||||
_ => return CommentPlacement::Default(comment),
|
||||
};
|
||||
|
||||
let ExprSlice {
|
||||
range: _,
|
||||
lower,
|
||||
upper,
|
||||
step,
|
||||
} = expr_slice;
|
||||
|
||||
// Check for `foo[ # comment`, but only if they are on the same line
|
||||
let after_lbracket = matches!(
|
||||
first_non_trivia_token_rev(comment.slice().start(), locator.contents()),
|
||||
Some(Token {
|
||||
kind: TokenKind::LBracket,
|
||||
..
|
||||
})
|
||||
);
|
||||
if comment.line_position().is_end_of_line() && after_lbracket {
|
||||
// Keep comments after the opening bracket there by formatting them outside the
|
||||
// soft block indent
|
||||
// ```python
|
||||
// "a"[ # comment
|
||||
// 1:
|
||||
// ]
|
||||
// ```
|
||||
debug_assert!(
|
||||
matches!(comment.enclosing_node(), AnyNodeRef::ExprSubscript(_)),
|
||||
"{:?}",
|
||||
comment.enclosing_node()
|
||||
);
|
||||
return CommentPlacement::dangling(comment.enclosing_node(), comment);
|
||||
}
|
||||
|
||||
None
|
||||
let assignment =
|
||||
assign_comment_in_slice(comment.slice().range(), locator.contents(), expr_slice);
|
||||
let node = match assignment {
|
||||
ExprSliceCommentSection::Lower => lower,
|
||||
ExprSliceCommentSection::Upper => upper,
|
||||
ExprSliceCommentSection::Step => step,
|
||||
};
|
||||
|
||||
if let Some(node) = node {
|
||||
if comment.slice().start() < node.start() {
|
||||
CommentPlacement::leading(node.as_ref().into(), comment)
|
||||
} else {
|
||||
// If a trailing comment is an end of line comment that's fine because we have a node
|
||||
// ahead of it
|
||||
CommentPlacement::trailing(node.as_ref().into(), comment)
|
||||
}
|
||||
} else {
|
||||
CommentPlacement::dangling(expr_slice.as_any_node_ref(), comment)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles own line comments between the last function decorator and the *header* of the function.
|
||||
@@ -868,7 +917,10 @@ fn find_pos_only_slash_offset(
|
||||
/// def test():
|
||||
/// ...
|
||||
/// ```
|
||||
fn handle_leading_function_with_decorators_comment(comment: DecoratedComment) -> CommentPlacement {
|
||||
fn handle_leading_function_with_decorators_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
_locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
let is_preceding_decorator = comment
|
||||
.preceding_node()
|
||||
.map_or(false, |node| node.is_decorator());
|
||||
@@ -877,7 +929,7 @@ fn handle_leading_function_with_decorators_comment(comment: DecoratedComment) ->
|
||||
.following_node()
|
||||
.map_or(false, |node| node.is_arguments());
|
||||
|
||||
if comment.text_position().is_own_line() && is_preceding_decorator && is_following_arguments {
|
||||
if comment.line_position().is_own_line() && is_preceding_decorator && is_following_arguments {
|
||||
CommentPlacement::dangling(comment.enclosing_node(), comment)
|
||||
} else {
|
||||
CommentPlacement::Default(comment)
|
||||
@@ -954,6 +1006,43 @@ fn handle_dict_unpacking_comment<'a>(
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
|
||||
// Own line comments coming after the node are always dangling comments
|
||||
// ```python
|
||||
// (
|
||||
// a
|
||||
// # trailing a comment
|
||||
// . # dangling comment
|
||||
// # or this
|
||||
// b
|
||||
// )
|
||||
// ```
|
||||
fn handle_attribute_comment<'a>(
|
||||
comment: DecoratedComment<'a>,
|
||||
locator: &Locator,
|
||||
) -> CommentPlacement<'a> {
|
||||
let Some(attribute) = comment.enclosing_node().expr_attribute() else {
|
||||
return CommentPlacement::Default(comment);
|
||||
};
|
||||
|
||||
// It must be a comment AFTER the name
|
||||
if comment.preceding_node().is_none() {
|
||||
return CommentPlacement::Default(comment);
|
||||
}
|
||||
|
||||
let between_value_and_attr = TextRange::new(attribute.value.end(), attribute.attr.start());
|
||||
|
||||
let dot = SimpleTokenizer::new(locator.contents(), between_value_and_attr)
|
||||
.skip_trivia()
|
||||
.next()
|
||||
.expect("Expected the `.` character after the value");
|
||||
|
||||
if TextRange::new(dot.end(), attribute.attr.start()).contains(comment.slice().start()) {
|
||||
CommentPlacement::dangling(attribute.into(), comment)
|
||||
} else {
|
||||
CommentPlacement::Default(comment)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `right` is `Some` and `left` and `right` are referentially equal.
|
||||
fn are_same_optional<'a, T>(left: AnyNodeRef, right: Option<T>) -> bool
|
||||
where
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::comments::node_key::NodeRefEqualityKey;
|
||||
use crate::comments::placement::place_comment;
|
||||
use crate::comments::{CommentTextPosition, CommentsMap, SourceComment};
|
||||
use crate::comments::{CommentLinePosition, CommentsMap, SourceComment};
|
||||
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::prelude::*;
|
||||
@@ -66,7 +66,7 @@ impl<'a> CommentsVisitor<'a> {
|
||||
preceding: self.preceding_node,
|
||||
following: Some(node),
|
||||
parent: self.parents.iter().rev().nth(1).copied(),
|
||||
text_position: text_position(*comment_range, self.source_code),
|
||||
line_position: text_position(*comment_range, self.source_code),
|
||||
slice: self.source_code.slice(*comment_range),
|
||||
};
|
||||
|
||||
@@ -125,7 +125,7 @@ impl<'a> CommentsVisitor<'a> {
|
||||
preceding: self.preceding_node,
|
||||
parent: self.parents.last().copied(),
|
||||
following: None,
|
||||
text_position: text_position(*comment_range, self.source_code),
|
||||
line_position: text_position(*comment_range, self.source_code),
|
||||
slice: self.source_code.slice(*comment_range),
|
||||
};
|
||||
|
||||
@@ -280,7 +280,7 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast> {
|
||||
}
|
||||
}
|
||||
|
||||
fn text_position(comment_range: TextRange, source_code: SourceCode) -> CommentTextPosition {
|
||||
fn text_position(comment_range: TextRange, source_code: SourceCode) -> CommentLinePosition {
|
||||
let before = &source_code.as_str()[TextRange::up_to(comment_range.start())];
|
||||
|
||||
for c in before.chars().rev() {
|
||||
@@ -289,11 +289,11 @@ fn text_position(comment_range: TextRange, source_code: SourceCode) -> CommentTe
|
||||
break;
|
||||
}
|
||||
c if is_python_whitespace(c) => continue,
|
||||
_ => return CommentTextPosition::EndOfLine,
|
||||
_ => return CommentLinePosition::EndOfLine,
|
||||
}
|
||||
}
|
||||
|
||||
CommentTextPosition::OwnLine
|
||||
CommentLinePosition::OwnLine
|
||||
}
|
||||
|
||||
/// A comment decorated with additional information about its surrounding context in the source document.
|
||||
@@ -305,7 +305,7 @@ pub(super) struct DecoratedComment<'a> {
|
||||
preceding: Option<AnyNodeRef<'a>>,
|
||||
following: Option<AnyNodeRef<'a>>,
|
||||
parent: Option<AnyNodeRef<'a>>,
|
||||
text_position: CommentTextPosition,
|
||||
line_position: CommentLinePosition,
|
||||
slice: SourceCodeSlice,
|
||||
}
|
||||
|
||||
@@ -443,14 +443,14 @@ impl<'a> DecoratedComment<'a> {
|
||||
}
|
||||
|
||||
/// The position of the comment in the text.
|
||||
pub(super) fn text_position(&self) -> CommentTextPosition {
|
||||
self.text_position
|
||||
pub(super) fn line_position(&self) -> CommentLinePosition {
|
||||
self.line_position
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DecoratedComment<'_>> for SourceComment {
|
||||
fn from(decorated: DecoratedComment) -> Self {
|
||||
Self::new(decorated.slice, decorated.text_position)
|
||||
Self::new(decorated.slice, decorated.line_position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,18 +615,6 @@ impl<'a> CommentPlacement<'a> {
|
||||
comment: comment.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the placement if it isn't [`CommentPlacement::Default`], otherwise calls `f` and returns the result.
|
||||
#[inline]
|
||||
pub(super) fn or_else<F>(self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(DecoratedComment<'a>) -> CommentPlacement<'a>,
|
||||
{
|
||||
match self {
|
||||
CommentPlacement::Default(comment) => f(comment),
|
||||
placement => placement,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
@@ -659,8 +647,8 @@ impl<'a> CommentsBuilder<'a> {
|
||||
self.push_dangling_comment(node, comment);
|
||||
}
|
||||
CommentPlacement::Default(comment) => {
|
||||
match comment.text_position() {
|
||||
CommentTextPosition::EndOfLine => {
|
||||
match comment.line_position() {
|
||||
CommentLinePosition::EndOfLine => {
|
||||
match (comment.preceding_node(), comment.following_node()) {
|
||||
(Some(preceding), Some(_)) => {
|
||||
// Attach comments with both preceding and following node to the preceding
|
||||
@@ -682,7 +670,7 @@ impl<'a> CommentsBuilder<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
CommentTextPosition::OwnLine => {
|
||||
CommentLinePosition::OwnLine => {
|
||||
match (comment.preceding_node(), comment.following_node()) {
|
||||
// Following always wins for a leading comment
|
||||
// ```python
|
||||
|
||||
215
crates/ruff_python_formatter/src/expression/binary_like.rs
Normal file
215
crates/ruff_python_formatter/src/expression/binary_like.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
//! This module provides helper utilities to format an expression that has a left side, an operator,
|
||||
//! and a right side (binary like).
|
||||
|
||||
use crate::expression::parentheses::Parentheses;
|
||||
use crate::prelude::*;
|
||||
use ruff_formatter::{format_args, write};
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
/// Trait to implement a binary like syntax that has a left operand, an operator, and a right operand.
|
||||
pub(super) trait FormatBinaryLike<'ast> {
|
||||
/// The type implementing the formatting of the operator.
|
||||
type FormatOperator: Format<PyFormatContext<'ast>>;
|
||||
|
||||
/// Formats the binary like expression to `f`.
|
||||
fn fmt_binary(
|
||||
&self,
|
||||
parentheses: Option<Parentheses>,
|
||||
f: &mut PyFormatter<'ast, '_>,
|
||||
) -> FormatResult<()> {
|
||||
let left = self.left()?;
|
||||
let operator = self.operator();
|
||||
let right = self.right()?;
|
||||
|
||||
let layout = if parentheses == Some(Parentheses::Custom) {
|
||||
self.binary_layout()
|
||||
} else {
|
||||
BinaryLayout::Default
|
||||
};
|
||||
|
||||
match layout {
|
||||
BinaryLayout::Default => self.fmt_default(f),
|
||||
BinaryLayout::ExpandLeft => {
|
||||
let left = left.format().memoized();
|
||||
let right = right.format().memoized();
|
||||
write!(
|
||||
f,
|
||||
[best_fitting![
|
||||
// Everything on a single line
|
||||
format_args![group(&left), space(), operator, space(), right],
|
||||
// Break the left over multiple lines, keep the right flat
|
||||
format_args![
|
||||
group(&left).should_expand(true),
|
||||
space(),
|
||||
operator,
|
||||
space(),
|
||||
right
|
||||
],
|
||||
// The content doesn't fit, indent the content and break before the operator.
|
||||
format_args![
|
||||
text("("),
|
||||
block_indent(&format_args![
|
||||
left,
|
||||
hard_line_break(),
|
||||
operator,
|
||||
space(),
|
||||
right
|
||||
]),
|
||||
text(")")
|
||||
]
|
||||
]
|
||||
.with_mode(BestFittingMode::AllLines)]
|
||||
)
|
||||
}
|
||||
BinaryLayout::ExpandRight => {
|
||||
let left_group = f.group_id("BinaryLeft");
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
// Wrap the left in a group and gives it an id. The printer first breaks the
|
||||
// right side if `right` contains any line break because the printer breaks
|
||||
// sequences of groups from right to left.
|
||||
// Indents the left side if the group breaks.
|
||||
group(&format_args![
|
||||
if_group_breaks(&text("(")),
|
||||
indent_if_group_breaks(
|
||||
&format_args![
|
||||
soft_line_break(),
|
||||
left.format(),
|
||||
soft_line_break_or_space(),
|
||||
operator,
|
||||
space()
|
||||
],
|
||||
left_group
|
||||
)
|
||||
])
|
||||
.with_group_id(Some(left_group)),
|
||||
// Wrap the right in a group and indents its content but only if the left side breaks
|
||||
group(&indent_if_group_breaks(&right.format(), left_group)),
|
||||
// If the left side breaks, insert a hard line break to finish the indent and close the open paren.
|
||||
if_group_breaks(&format_args![hard_line_break(), text(")")])
|
||||
.with_group_id(Some(left_group))
|
||||
]
|
||||
)
|
||||
}
|
||||
BinaryLayout::ExpandRightThenLeft => {
|
||||
// The formatter expands group-sequences from right to left, and expands both if
|
||||
// there isn't enough space when expanding only one of them.
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
group(&left.format()),
|
||||
space(),
|
||||
operator,
|
||||
space(),
|
||||
group(&right.format())
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines which binary layout to use.
|
||||
fn binary_layout(&self) -> BinaryLayout {
|
||||
if let (Ok(left), Ok(right)) = (self.left(), self.right()) {
|
||||
BinaryLayout::from_left_right(left, right)
|
||||
} else {
|
||||
BinaryLayout::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the node according to the default layout.
|
||||
fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()>;
|
||||
|
||||
/// Returns the left operator
|
||||
fn left(&self) -> FormatResult<&Expr>;
|
||||
|
||||
/// Returns the right operator.
|
||||
fn right(&self) -> FormatResult<&Expr>;
|
||||
|
||||
/// Returns the object that formats the operator.
|
||||
fn operator(&self) -> Self::FormatOperator;
|
||||
}
|
||||
|
||||
fn can_break_expr(expr: &Expr) -> bool {
|
||||
use ruff_python_ast::prelude::*;
|
||||
|
||||
match expr {
|
||||
Expr::Tuple(ExprTuple {
|
||||
elts: expressions, ..
|
||||
})
|
||||
| Expr::List(ExprList {
|
||||
elts: expressions, ..
|
||||
})
|
||||
| Expr::Set(ExprSet {
|
||||
elts: expressions, ..
|
||||
})
|
||||
| Expr::Dict(ExprDict {
|
||||
values: expressions,
|
||||
..
|
||||
}) => !expressions.is_empty(),
|
||||
Expr::Call(ExprCall { args, keywords, .. }) => !(args.is_empty() && keywords.is_empty()),
|
||||
Expr::ListComp(_) | Expr::SetComp(_) | Expr::DictComp(_) | Expr::GeneratorExp(_) => true,
|
||||
Expr::UnaryOp(ExprUnaryOp { operand, .. }) => match operand.as_ref() {
|
||||
Expr::BinOp(_) => true,
|
||||
_ => can_break_expr(operand.as_ref()),
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub(super) enum BinaryLayout {
|
||||
/// Put each operand on their own line if either side expands
|
||||
Default,
|
||||
|
||||
/// Try to expand the left to make it fit. Add parentheses if the left or right don't fit.
|
||||
///
|
||||
///```python
|
||||
/// [
|
||||
/// a,
|
||||
/// b
|
||||
/// ] & c
|
||||
///```
|
||||
ExpandLeft,
|
||||
|
||||
/// Try to expand the right to make it fix. Add parentheses if the left or right don't fit.
|
||||
///
|
||||
/// ```python
|
||||
/// a & [
|
||||
/// b,
|
||||
/// c
|
||||
/// ]
|
||||
/// ```
|
||||
ExpandRight,
|
||||
|
||||
/// Both the left and right side can be expanded. Try in the following order:
|
||||
/// * expand the right side
|
||||
/// * expand the left side
|
||||
/// * expand both sides
|
||||
///
|
||||
/// to make the expression fit
|
||||
///
|
||||
/// ```python
|
||||
/// [
|
||||
/// a,
|
||||
/// b
|
||||
/// ] & [
|
||||
/// c,
|
||||
/// d
|
||||
/// ]
|
||||
/// ```
|
||||
ExpandRightThenLeft,
|
||||
}
|
||||
|
||||
impl BinaryLayout {
|
||||
pub(super) fn from_left_right(left: &Expr, right: &Expr) -> BinaryLayout {
|
||||
match (can_break_expr(left), can_break_expr(right)) {
|
||||
(false, false) => BinaryLayout::Default,
|
||||
(true, false) => BinaryLayout::ExpandLeft,
|
||||
(false, true) => BinaryLayout::ExpandRight,
|
||||
(true, true) => BinaryLayout::ExpandRightThenLeft,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::comments::Comments;
|
||||
use crate::comments::{leading_comments, trailing_comments, Comments};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::{not_yet_implemented_custom_text, FormatNodeRule};
|
||||
use crate::FormatNodeRule;
|
||||
use ruff_formatter::write;
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprAttribute, ExprConstant};
|
||||
|
||||
@@ -15,11 +15,11 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||
let ExprAttribute {
|
||||
value,
|
||||
range: _,
|
||||
attr: _,
|
||||
attr,
|
||||
ctx: _,
|
||||
} = item;
|
||||
|
||||
let requires_space = matches!(
|
||||
let needs_parentheses = matches!(
|
||||
value.as_ref(),
|
||||
Expr::Constant(ExprConstant {
|
||||
value: Constant::Int(_) | Constant::Float(_),
|
||||
@@ -27,16 +27,45 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||
})
|
||||
);
|
||||
|
||||
if needs_parentheses {
|
||||
value.format().with_options(Parenthesize::Always).fmt(f)?;
|
||||
} else {
|
||||
value.format().fmt(f)?;
|
||||
}
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
|
||||
if comments.has_trailing_own_line_comments(value.as_ref()) {
|
||||
hard_line_break().fmt(f)?;
|
||||
}
|
||||
|
||||
let dangling_comments = comments.dangling_comments(item);
|
||||
|
||||
let leading_attribute_comments_start =
|
||||
dangling_comments.partition_point(|comment| comment.line_position().is_end_of_line());
|
||||
let (trailing_dot_comments, leading_attribute_comments) =
|
||||
dangling_comments.split_at(leading_attribute_comments_start);
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
item.value.format(),
|
||||
requires_space.then_some(space()),
|
||||
text("."),
|
||||
not_yet_implemented_custom_text("NOT_IMPLEMENTED_attr")
|
||||
trailing_comments(trailing_dot_comments),
|
||||
(!leading_attribute_comments.is_empty()).then_some(hard_line_break()),
|
||||
leading_comments(leading_attribute_comments),
|
||||
attr.format()
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn fmt_dangling_comments(
|
||||
&self,
|
||||
_node: &ExprAttribute,
|
||||
_f: &mut PyFormatter,
|
||||
) -> FormatResult<()> {
|
||||
// handle in `fmt_fields`
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses for ExprAttribute {
|
||||
@@ -46,6 +75,9 @@ impl NeedsParentheses for ExprAttribute {
|
||||
source: &str,
|
||||
comments: &Comments,
|
||||
) -> Parentheses {
|
||||
default_expression_needs_parentheses(self.into(), parenthesize, source, comments)
|
||||
match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) {
|
||||
Parentheses::Optional => Parentheses::Never,
|
||||
parentheses => parentheses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::comments::{trailing_comments, Comments};
|
||||
use crate::expression::binary_like::{BinaryLayout, FormatBinaryLike};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parenthesize,
|
||||
};
|
||||
use crate::expression::Parentheses;
|
||||
use crate::prelude::*;
|
||||
use crate::FormatNodeRule;
|
||||
use ruff_formatter::{
|
||||
format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions,
|
||||
};
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions};
|
||||
use rustpython_parser::ast::{
|
||||
Constant, Expr, ExprAttribute, ExprBinOp, ExprConstant, ExprUnaryOp, Operator, UnaryOp,
|
||||
};
|
||||
@@ -29,76 +27,7 @@ impl FormatRuleWithOptions<ExprBinOp, PyFormatContext<'_>> for FormatExprBinOp {
|
||||
|
||||
impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
|
||||
fn fmt_fields(&self, item: &ExprBinOp, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let ExprBinOp {
|
||||
left,
|
||||
right,
|
||||
op,
|
||||
range: _,
|
||||
} = item;
|
||||
|
||||
let should_break_right = self.parentheses == Some(Parentheses::Custom);
|
||||
|
||||
if should_break_right {
|
||||
let left_group = f.group_id("BinaryLeft");
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
// Wrap the left in a group and gives it an id. The printer first breaks the
|
||||
// right side if `right` contains any line break because the printer breaks
|
||||
// sequences of groups from right to left.
|
||||
// Indents the left side if the group breaks.
|
||||
group(&format_args![
|
||||
if_group_breaks(&text("(")),
|
||||
indent_if_group_breaks(
|
||||
&format_args![
|
||||
soft_line_break(),
|
||||
left.format(),
|
||||
soft_line_break_or_space(),
|
||||
op.format(),
|
||||
space()
|
||||
],
|
||||
left_group
|
||||
)
|
||||
])
|
||||
.with_group_id(Some(left_group)),
|
||||
// Wrap the right in a group and indents its content but only if the left side breaks
|
||||
group(&indent_if_group_breaks(&right.format(), left_group)),
|
||||
// If the left side breaks, insert a hard line break to finish the indent and close the open paren.
|
||||
if_group_breaks(&format_args![hard_line_break(), text(")")])
|
||||
.with_group_id(Some(left_group))
|
||||
]
|
||||
)
|
||||
} else {
|
||||
let comments = f.context().comments().clone();
|
||||
let operator_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||
let needs_space = !is_simple_power_expression(item);
|
||||
|
||||
let before_operator_space = if needs_space {
|
||||
soft_line_break_or_space()
|
||||
} else {
|
||||
soft_line_break()
|
||||
};
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
left.format(),
|
||||
before_operator_space,
|
||||
op.format(),
|
||||
trailing_comments(operator_comments),
|
||||
]
|
||||
)?;
|
||||
|
||||
// Format the operator on its own line if the right side has any leading comments.
|
||||
if comments.has_leading_comments(right.as_ref()) {
|
||||
write!(f, [hard_line_break()])?;
|
||||
} else if needs_space {
|
||||
write!(f, [space()])?;
|
||||
}
|
||||
|
||||
write!(f, [group(&right.format())])
|
||||
}
|
||||
item.fmt_binary(self.parentheses, f)
|
||||
}
|
||||
|
||||
fn fmt_dangling_comments(&self, _node: &ExprBinOp, _f: &mut PyFormatter) -> FormatResult<()> {
|
||||
@@ -107,6 +36,60 @@ impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> FormatBinaryLike<'ast> for ExprBinOp {
|
||||
type FormatOperator = FormatOwnedWithRule<Operator, FormatOperator, PyFormatContext<'ast>>;
|
||||
|
||||
fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()> {
|
||||
let ExprBinOp {
|
||||
range: _,
|
||||
left,
|
||||
op,
|
||||
right,
|
||||
} = self;
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let operator_comments = comments.dangling_comments(self);
|
||||
let needs_space = !is_simple_power_expression(self);
|
||||
|
||||
let before_operator_space = if needs_space {
|
||||
soft_line_break_or_space()
|
||||
} else {
|
||||
soft_line_break()
|
||||
};
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
left.format(),
|
||||
before_operator_space,
|
||||
op.format(),
|
||||
trailing_comments(operator_comments),
|
||||
]
|
||||
)?;
|
||||
|
||||
// Format the operator on its own line if the right side has any leading comments.
|
||||
if comments.has_leading_comments(right.as_ref()) {
|
||||
write!(f, [hard_line_break()])?;
|
||||
} else if needs_space {
|
||||
write!(f, [space()])?;
|
||||
}
|
||||
|
||||
write!(f, [group(&right.format())])
|
||||
}
|
||||
|
||||
fn left(&self) -> FormatResult<&Expr> {
|
||||
Ok(&self.left)
|
||||
}
|
||||
|
||||
fn right(&self) -> FormatResult<&Expr> {
|
||||
Ok(&self.right)
|
||||
}
|
||||
|
||||
fn operator(&self) -> Self::FormatOperator {
|
||||
self.op.into_format()
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_simple_power_expression(expr: &ExprBinOp) -> bool {
|
||||
expr.op.is_pow() && is_simple_power_operand(&expr.left) && is_simple_power_operand(&expr.right)
|
||||
}
|
||||
@@ -179,42 +162,16 @@ impl NeedsParentheses for ExprBinOp {
|
||||
) -> Parentheses {
|
||||
match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) {
|
||||
Parentheses::Optional => {
|
||||
if should_binary_break_right_side_first(self) {
|
||||
Parentheses::Custom
|
||||
} else {
|
||||
if self.binary_layout() == BinaryLayout::Default
|
||||
|| comments.has_leading_comments(self.right.as_ref())
|
||||
|| comments.has_dangling_comments(self)
|
||||
{
|
||||
Parentheses::Optional
|
||||
} else {
|
||||
Parentheses::Custom
|
||||
}
|
||||
}
|
||||
parentheses => parentheses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn should_binary_break_right_side_first(expr: &ExprBinOp) -> bool {
|
||||
use ruff_python_ast::prelude::*;
|
||||
|
||||
if expr.left.is_bin_op_expr() {
|
||||
false
|
||||
} else {
|
||||
match expr.right.as_ref() {
|
||||
Expr::Tuple(ExprTuple {
|
||||
elts: expressions, ..
|
||||
})
|
||||
| Expr::List(ExprList {
|
||||
elts: expressions, ..
|
||||
})
|
||||
| Expr::Set(ExprSet {
|
||||
elts: expressions, ..
|
||||
})
|
||||
| Expr::Dict(ExprDict {
|
||||
values: expressions,
|
||||
..
|
||||
}) => !expressions.is_empty(),
|
||||
Expr::Call(ExprCall { args, keywords, .. }) => !args.is_empty() && !keywords.is_empty(),
|
||||
Expr::ListComp(_) | Expr::SetComp(_) | Expr::DictComp(_) | Expr::GeneratorExp(_) => {
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,87 @@
|
||||
use crate::comments::Comments;
|
||||
use crate::comments::{leading_comments, Comments};
|
||||
use crate::expression::binary_like::{BinaryLayout, FormatBinaryLike};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||
};
|
||||
use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter};
|
||||
use ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprBoolOp;
|
||||
use crate::prelude::*;
|
||||
use ruff_formatter::{
|
||||
write, FormatError, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions,
|
||||
};
|
||||
use rustpython_parser::ast::{BoolOp, Expr, ExprBoolOp};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprBoolOp;
|
||||
pub struct FormatExprBoolOp {
|
||||
parentheses: Option<Parentheses>,
|
||||
}
|
||||
|
||||
impl FormatRuleWithOptions<ExprBoolOp, PyFormatContext<'_>> for FormatExprBoolOp {
|
||||
type Options = Option<Parentheses>;
|
||||
fn with_options(mut self, options: Self::Options) -> Self {
|
||||
self.parentheses = options;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatNodeRule<ExprBoolOp> for FormatExprBoolOp {
|
||||
fn fmt_fields(&self, _item: &ExprBoolOp, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
write!(
|
||||
f,
|
||||
[not_yet_implemented_custom_text(
|
||||
"NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2"
|
||||
)]
|
||||
)
|
||||
fn fmt_fields(&self, item: &ExprBoolOp, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
item.fmt_binary(self.parentheses, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> FormatBinaryLike<'ast> for ExprBoolOp {
|
||||
type FormatOperator = FormatOwnedWithRule<BoolOp, FormatBoolOp, PyFormatContext<'ast>>;
|
||||
|
||||
fn binary_layout(&self) -> BinaryLayout {
|
||||
match self.values.as_slice() {
|
||||
[left, right] => BinaryLayout::from_left_right(left, right),
|
||||
[..] => BinaryLayout::Default,
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()> {
|
||||
let ExprBoolOp {
|
||||
range: _,
|
||||
op,
|
||||
values,
|
||||
} = self;
|
||||
|
||||
let mut values = values.iter();
|
||||
let comments = f.context().comments().clone();
|
||||
|
||||
let Some(first) = values.next() else {
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
write!(f, [group(&first.format())])?;
|
||||
|
||||
for value in values {
|
||||
let leading_value_comments = comments.leading_comments(value);
|
||||
// Format the expressions leading comments **before** the operator
|
||||
if leading_value_comments.is_empty() {
|
||||
write!(f, [soft_line_break_or_space()])?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
[hard_line_break(), leading_comments(leading_value_comments)]
|
||||
)?;
|
||||
}
|
||||
|
||||
write!(f, [op.format(), space(), group(&value.format())])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn left(&self) -> FormatResult<&Expr> {
|
||||
self.values.first().ok_or(FormatError::SyntaxError)
|
||||
}
|
||||
|
||||
fn right(&self) -> FormatResult<&Expr> {
|
||||
self.values.last().ok_or(FormatError::SyntaxError)
|
||||
}
|
||||
|
||||
fn operator(&self) -> Self::FormatOperator {
|
||||
self.op.into_format()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +92,53 @@ impl NeedsParentheses for ExprBoolOp {
|
||||
source: &str,
|
||||
comments: &Comments,
|
||||
) -> Parentheses {
|
||||
default_expression_needs_parentheses(self.into(), parenthesize, source, comments)
|
||||
match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) {
|
||||
Parentheses::Optional => match self.binary_layout() {
|
||||
BinaryLayout::Default => Parentheses::Optional,
|
||||
|
||||
BinaryLayout::ExpandRight
|
||||
| BinaryLayout::ExpandLeft
|
||||
| BinaryLayout::ExpandRightThenLeft
|
||||
if self
|
||||
.values
|
||||
.last()
|
||||
.map_or(false, |right| comments.has_leading_comments(right)) =>
|
||||
{
|
||||
Parentheses::Optional
|
||||
}
|
||||
_ => Parentheses::Custom,
|
||||
},
|
||||
parentheses => parentheses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FormatBoolOp;
|
||||
|
||||
impl<'ast> AsFormat<PyFormatContext<'ast>> for BoolOp {
|
||||
type Format<'a> = FormatRefWithRule<'a, BoolOp, FormatBoolOp, PyFormatContext<'ast>>;
|
||||
|
||||
fn format(&self) -> Self::Format<'_> {
|
||||
FormatRefWithRule::new(self, FormatBoolOp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> IntoFormat<PyFormatContext<'ast>> for BoolOp {
|
||||
type Format = FormatOwnedWithRule<BoolOp, FormatBoolOp, PyFormatContext<'ast>>;
|
||||
|
||||
fn into_format(self) -> Self::Format {
|
||||
FormatOwnedWithRule::new(self, FormatBoolOp)
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatRule<BoolOp, PyFormatContext<'_>> for FormatBoolOp {
|
||||
fn fmt(&self, item: &BoolOp, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||
let operator = match item {
|
||||
BoolOp::And => "and",
|
||||
BoolOp::Or => "or",
|
||||
};
|
||||
|
||||
text(operator).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::comments::{dangling_comments, CommentTextPosition, Comments};
|
||||
use crate::comments::{dangling_comments, CommentLinePosition, Comments};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||
};
|
||||
@@ -30,11 +30,12 @@ impl FormatNodeRule<ExprList> for FormatExprList {
|
||||
// ```
|
||||
// In all other cases comments get assigned to a list element
|
||||
if elts.is_empty() {
|
||||
let end_of_line_split = dangling
|
||||
.partition_point(|comment| comment.position() == CommentTextPosition::EndOfLine);
|
||||
let end_of_line_split = dangling.partition_point(|comment| {
|
||||
comment.line_position() == CommentLinePosition::EndOfLine
|
||||
});
|
||||
debug_assert!(dangling[end_of_line_split..]
|
||||
.iter()
|
||||
.all(|comment| comment.position() == CommentTextPosition::OwnLine));
|
||||
.all(|comment| comment.line_position() == CommentLinePosition::OwnLine));
|
||||
return write!(
|
||||
f,
|
||||
[group(&format_args![
|
||||
|
||||
@@ -1,26 +1,264 @@
|
||||
use crate::comments::{dangling_comments, Comments, SourceComment};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||
};
|
||||
use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter};
|
||||
|
||||
use crate::comments::Comments;
|
||||
use ruff_formatter::{write, Buffer, FormatResult};
|
||||
use crate::trivia::Token;
|
||||
use crate::trivia::{first_non_trivia_token, TokenKind};
|
||||
use crate::{AsFormat, FormatNodeRule, PyFormatter};
|
||||
use ruff_formatter::prelude::{hard_line_break, line_suffix_boundary, space, text};
|
||||
use ruff_formatter::{write, Buffer, Format, FormatError, FormatResult};
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::prelude::{Expr, Ranged};
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::ExprSlice;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprSlice;
|
||||
|
||||
impl FormatNodeRule<ExprSlice> for FormatExprSlice {
|
||||
fn fmt_fields(&self, _item: &ExprSlice, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
write!(
|
||||
f,
|
||||
[not_yet_implemented_custom_text(
|
||||
"NOT_IMPLEMENTED_start:NOT_IMPLEMENTED_end"
|
||||
)]
|
||||
)
|
||||
/// This implementation deviates from black in that comments are attached to the section of the
|
||||
/// slice they originate in
|
||||
fn fmt_fields(&self, item: &ExprSlice, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
// `[lower:upper:step]`
|
||||
let ExprSlice {
|
||||
range,
|
||||
lower,
|
||||
upper,
|
||||
step,
|
||||
} = item;
|
||||
|
||||
let (first_colon, second_colon) =
|
||||
find_colons(f.context().contents(), *range, lower, upper)?;
|
||||
|
||||
// Handle comment placement
|
||||
// In placements.rs, we marked comment for None nodes a dangling and associated all others
|
||||
// as leading or dangling wrt to a node. That means we either format a node and only have
|
||||
// to handle newlines and spacing, or the node is None and we insert the corresponding
|
||||
// slice of dangling comments
|
||||
let comments = f.context().comments().clone();
|
||||
let slice_dangling_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||
// Put the dangling comments (where the nodes are missing) into buckets
|
||||
let first_colon_partition_index = slice_dangling_comments
|
||||
.partition_point(|x| x.slice().start() < first_colon.range.start());
|
||||
let (dangling_lower_comments, dangling_upper_step_comments) =
|
||||
slice_dangling_comments.split_at(first_colon_partition_index);
|
||||
let (dangling_upper_comments, dangling_step_comments) =
|
||||
if let Some(second_colon) = &second_colon {
|
||||
let second_colon_partition_index = dangling_upper_step_comments
|
||||
.partition_point(|x| x.slice().start() < second_colon.range.start());
|
||||
dangling_upper_step_comments.split_at(second_colon_partition_index)
|
||||
} else {
|
||||
// Without a second colon they remaining dangling comments belong between the first
|
||||
// colon and the closing parentheses
|
||||
(dangling_upper_step_comments, [].as_slice())
|
||||
};
|
||||
|
||||
// Ensure there a no dangling comments for a node if the node is present
|
||||
debug_assert!(lower.is_none() || dangling_lower_comments.is_empty());
|
||||
debug_assert!(upper.is_none() || dangling_upper_comments.is_empty());
|
||||
debug_assert!(step.is_none() || dangling_step_comments.is_empty());
|
||||
|
||||
// Handle spacing around the colon(s)
|
||||
// https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices
|
||||
let lower_simple = lower.as_ref().map_or(true, |expr| is_simple_expr(expr));
|
||||
let upper_simple = upper.as_ref().map_or(true, |expr| is_simple_expr(expr));
|
||||
let step_simple = step.as_ref().map_or(true, |expr| is_simple_expr(expr));
|
||||
let all_simple = lower_simple && upper_simple && step_simple;
|
||||
|
||||
// lower
|
||||
if let Some(lower) = lower {
|
||||
write!(f, [lower.format(), line_suffix_boundary()])?;
|
||||
} else {
|
||||
dangling_comments(dangling_lower_comments).fmt(f)?;
|
||||
}
|
||||
|
||||
// First colon
|
||||
// The spacing after the colon depends on both the lhs and the rhs:
|
||||
// ```
|
||||
// e00 = x[:]
|
||||
// e01 = x[:1]
|
||||
// e02 = x[: a()]
|
||||
// e10 = x[1:]
|
||||
// e11 = x[1:1]
|
||||
// e12 = x[1 : a()]
|
||||
// e20 = x[a() :]
|
||||
// e21 = x[a() : 1]
|
||||
// e22 = x[a() : a()]
|
||||
// e200 = "e"[a() : :]
|
||||
// e201 = "e"[a() :: 1]
|
||||
// e202 = "e"[a() :: a()]
|
||||
// ```
|
||||
if !all_simple {
|
||||
space().fmt(f)?;
|
||||
}
|
||||
text(":").fmt(f)?;
|
||||
// No upper node, no need for a space, e.g. `x[a() :]`
|
||||
if !all_simple && upper.is_some() {
|
||||
space().fmt(f)?;
|
||||
}
|
||||
|
||||
// Upper
|
||||
if let Some(upper) = upper {
|
||||
let upper_leading_comments = comments.leading_comments(upper.as_ref());
|
||||
leading_comments_spacing(f, upper_leading_comments)?;
|
||||
write!(f, [upper.format(), line_suffix_boundary()])?;
|
||||
} else {
|
||||
if let Some(first) = dangling_upper_comments.first() {
|
||||
// Here the spacing for end-of-line comments works but own line comments need
|
||||
// explicit spacing
|
||||
if first.line_position().is_own_line() {
|
||||
hard_line_break().fmt(f)?;
|
||||
}
|
||||
}
|
||||
dangling_comments(dangling_upper_comments).fmt(f)?;
|
||||
}
|
||||
|
||||
// (optionally) step
|
||||
if second_colon.is_some() {
|
||||
// Same spacing rules as for the first colon, except for the strange case when the
|
||||
// second colon exists, but neither upper nor step
|
||||
// ```
|
||||
// e200 = "e"[a() : :]
|
||||
// e201 = "e"[a() :: 1]
|
||||
// e202 = "e"[a() :: a()]
|
||||
// ```
|
||||
if !all_simple && (upper.is_some() || step.is_none()) {
|
||||
space().fmt(f)?;
|
||||
}
|
||||
text(":").fmt(f)?;
|
||||
// No step node, no need for a space
|
||||
if !all_simple && step.is_some() {
|
||||
space().fmt(f)?;
|
||||
}
|
||||
if let Some(step) = step {
|
||||
let step_leading_comments = comments.leading_comments(step.as_ref());
|
||||
leading_comments_spacing(f, step_leading_comments)?;
|
||||
step.format().fmt(f)?;
|
||||
} else {
|
||||
if !dangling_step_comments.is_empty() {
|
||||
// Put the colon and comments on their own lines
|
||||
write!(
|
||||
f,
|
||||
[hard_line_break(), dangling_comments(dangling_step_comments)]
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug_assert!(step.is_none(), "step can't exist without a second colon");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// We're in a slice, so we know there's a first colon, but with have to look into the source
|
||||
/// to find out whether there is a second one, too, e.g. `[1:2]` and `[1:10:2]`.
|
||||
///
|
||||
/// Returns the first and optionally the second colon.
|
||||
pub(crate) fn find_colons(
|
||||
contents: &str,
|
||||
range: TextRange,
|
||||
lower: &Option<Box<Expr>>,
|
||||
upper: &Option<Box<Expr>>,
|
||||
) -> FormatResult<(Token, Option<Token>)> {
|
||||
let after_lower = lower
|
||||
.as_ref()
|
||||
.map_or(range.start(), |lower| lower.range().end());
|
||||
let first_colon =
|
||||
first_non_trivia_token(after_lower, contents).ok_or(FormatError::SyntaxError)?;
|
||||
if first_colon.kind != TokenKind::Colon {
|
||||
return Err(FormatError::SyntaxError);
|
||||
}
|
||||
|
||||
let after_upper = upper
|
||||
.as_ref()
|
||||
.map_or(first_colon.end(), |upper| upper.range().end());
|
||||
// At least the closing bracket must exist, so there must be a token there
|
||||
let next_token =
|
||||
first_non_trivia_token(after_upper, contents).ok_or(FormatError::SyntaxError)?;
|
||||
let second_colon = if next_token.kind == TokenKind::Colon {
|
||||
debug_assert!(
|
||||
next_token.range.start() < range.end(),
|
||||
"The next token in a slice must either be a colon or the closing bracket"
|
||||
);
|
||||
Some(next_token)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((first_colon, second_colon))
|
||||
}
|
||||
|
||||
/// Determines whether this expression needs a space around the colon
|
||||
/// <https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices>
|
||||
fn is_simple_expr(expr: &Expr) -> bool {
|
||||
matches!(expr, Expr::Constant(_) | Expr::Name(_))
|
||||
}
|
||||
|
||||
pub(crate) enum ExprSliceCommentSection {
|
||||
Lower,
|
||||
Upper,
|
||||
Step,
|
||||
}
|
||||
|
||||
/// Assigns a comment to lower/upper/step in `[lower:upper:step]`.
|
||||
///
|
||||
/// ```python
|
||||
/// "sliceable"[
|
||||
/// # lower comment
|
||||
/// :
|
||||
/// # upper comment
|
||||
/// :
|
||||
/// # step comment
|
||||
/// ]
|
||||
/// ```
|
||||
pub(crate) fn assign_comment_in_slice(
|
||||
comment: TextRange,
|
||||
contents: &str,
|
||||
expr_slice: &ExprSlice,
|
||||
) -> ExprSliceCommentSection {
|
||||
let ExprSlice {
|
||||
range,
|
||||
lower,
|
||||
upper,
|
||||
step: _,
|
||||
} = expr_slice;
|
||||
|
||||
let (first_colon, second_colon) = find_colons(contents, *range, lower, upper)
|
||||
.expect("SyntaxError when trying to parse slice");
|
||||
|
||||
if comment.start() < first_colon.range.start() {
|
||||
ExprSliceCommentSection::Lower
|
||||
} else {
|
||||
// We are to the right of the first colon
|
||||
if let Some(second_colon) = second_colon {
|
||||
if comment.start() < second_colon.range.start() {
|
||||
ExprSliceCommentSection::Upper
|
||||
} else {
|
||||
ExprSliceCommentSection::Step
|
||||
}
|
||||
} else {
|
||||
// No second colon means there is no step
|
||||
ExprSliceCommentSection::Upper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manual spacing for the leading comments of upper and step
|
||||
fn leading_comments_spacing(
|
||||
f: &mut PyFormatter,
|
||||
leading_comments: &[SourceComment],
|
||||
) -> FormatResult<()> {
|
||||
if let Some(first) = leading_comments.first() {
|
||||
if first.line_position().is_own_line() {
|
||||
// Insert a newline after the colon so the comment ends up on its own line
|
||||
hard_line_break().fmt(f)?;
|
||||
} else {
|
||||
// Insert the two spaces between the colon and the end-of-line comment after the colon
|
||||
write!(f, [space(), space()])?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl NeedsParentheses for ExprSlice {
|
||||
fn needs_parentheses(
|
||||
&self,
|
||||
|
||||
@@ -1,24 +1,52 @@
|
||||
use crate::comments::{trailing_comments, Comments};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||
};
|
||||
use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter};
|
||||
|
||||
use crate::comments::Comments;
|
||||
use ruff_formatter::{write, Buffer, FormatResult};
|
||||
use crate::{AsFormat, FormatNodeRule, PyFormatter};
|
||||
use ruff_formatter::prelude::{group, soft_block_indent, text};
|
||||
use ruff_formatter::{format_args, write, Buffer, FormatResult};
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use rustpython_parser::ast::ExprSubscript;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprSubscript;
|
||||
|
||||
impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
||||
fn fmt_fields(&self, _item: &ExprSubscript, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
fn fmt_fields(&self, item: &ExprSubscript, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let ExprSubscript {
|
||||
range: _,
|
||||
value,
|
||||
slice,
|
||||
ctx: _,
|
||||
} = item;
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||
debug_assert!(
|
||||
dangling_comments.len() <= 1,
|
||||
"The subscript expression must have at most a single comment, the one after the bracket"
|
||||
);
|
||||
|
||||
write!(
|
||||
f,
|
||||
[not_yet_implemented_custom_text(
|
||||
"NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]"
|
||||
)]
|
||||
[group(&format_args![
|
||||
value.format(),
|
||||
text("["),
|
||||
trailing_comments(dangling_comments),
|
||||
soft_block_indent(&slice.format()),
|
||||
text("]")
|
||||
])]
|
||||
)
|
||||
}
|
||||
|
||||
fn fmt_dangling_comments(
|
||||
&self,
|
||||
_node: &ExprSubscript,
|
||||
_f: &mut PyFormatter,
|
||||
) -> FormatResult<()> {
|
||||
// Handled inside of `fmt_fields`
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses for ExprSubscript {
|
||||
@@ -28,6 +56,9 @@ impl NeedsParentheses for ExprSubscript {
|
||||
source: &str,
|
||||
comments: &Comments,
|
||||
) -> Parentheses {
|
||||
default_expression_needs_parentheses(self.into(), parenthesize, source, comments)
|
||||
match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) {
|
||||
Parentheses::Optional => Parentheses::Never,
|
||||
parentheses => parentheses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,48 @@ use ruff_formatter::formatter::Formatter;
|
||||
use ruff_formatter::prelude::{
|
||||
block_indent, group, if_group_breaks, soft_block_indent, soft_line_break_or_space, text,
|
||||
};
|
||||
use ruff_formatter::{format_args, write, Buffer, Format, FormatResult};
|
||||
use ruff_formatter::{format_args, write, Buffer, Format, FormatResult, FormatRuleWithOptions};
|
||||
use ruff_python_ast::prelude::{Expr, Ranged};
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::ExprTuple;
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Default)]
|
||||
pub enum TupleParentheses {
|
||||
/// Effectively `None` in `Option<Parentheses>`
|
||||
#[default]
|
||||
Default,
|
||||
/// Effectively `Some(Parentheses)` in `Option<Parentheses>`
|
||||
Expr(Parentheses),
|
||||
/// Handle the special case where we remove parentheses even if they were initially present
|
||||
///
|
||||
/// Normally, black keeps parentheses, but in the case of loops it formats
|
||||
/// ```python
|
||||
/// for (a, b) in x:
|
||||
/// pass
|
||||
/// ```
|
||||
/// to
|
||||
/// ```python
|
||||
/// for a, b in x:
|
||||
/// pass
|
||||
/// ```
|
||||
/// Black still does use parentheses in this position if the group breaks or magic trailing
|
||||
/// comma is used.
|
||||
StripInsideForLoop,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprTuple;
|
||||
pub struct FormatExprTuple {
|
||||
parentheses: TupleParentheses,
|
||||
}
|
||||
|
||||
impl FormatRuleWithOptions<ExprTuple, PyFormatContext<'_>> for FormatExprTuple {
|
||||
type Options = TupleParentheses;
|
||||
|
||||
fn with_options(mut self, options: Self::Options) -> Self {
|
||||
self.parentheses = options;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
||||
fn fmt_fields(&self, item: &ExprTuple, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
@@ -74,9 +109,14 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
||||
&text(")"),
|
||||
]
|
||||
)?;
|
||||
} else if is_parenthesized(*range, elts, f) {
|
||||
// If the tuple has parentheses, keep them. Note that unlike other expr parentheses,
|
||||
// those are actually part of the range
|
||||
} else if is_parenthesized(*range, elts, f)
|
||||
&& self.parentheses != TupleParentheses::StripInsideForLoop
|
||||
{
|
||||
// If the tuple has parentheses, we generally want to keep them. The exception are for
|
||||
// loops, see `TupleParentheses::StripInsideForLoop` doc comment.
|
||||
//
|
||||
// Unlike other expression parentheses, tuple parentheses are part of the range of the
|
||||
// tuple itself.
|
||||
write!(
|
||||
f,
|
||||
[group(&format_args![
|
||||
|
||||
@@ -1,17 +1,68 @@
|
||||
use crate::comments::Comments;
|
||||
use crate::comments::{trailing_comments, Comments};
|
||||
use crate::expression::parentheses::{
|
||||
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||
};
|
||||
use crate::{not_yet_implemented, FormatNodeRule, PyFormatter};
|
||||
use ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprUnaryOp;
|
||||
use crate::trivia::{SimpleTokenizer, TokenKind};
|
||||
use crate::{AsFormat, FormatNodeRule, PyFormatter};
|
||||
use ruff_formatter::prelude::{hard_line_break, space, text};
|
||||
use ruff_formatter::{Format, FormatContext, FormatResult};
|
||||
use ruff_python_ast::prelude::UnaryOp;
|
||||
use ruff_text_size::{TextLen, TextRange};
|
||||
use rustpython_parser::ast::{ExprUnaryOp, Ranged};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprUnaryOp;
|
||||
|
||||
impl FormatNodeRule<ExprUnaryOp> for FormatExprUnaryOp {
|
||||
fn fmt_fields(&self, item: &ExprUnaryOp, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
write!(f, [not_yet_implemented(item)])
|
||||
let ExprUnaryOp {
|
||||
range: _,
|
||||
op,
|
||||
operand,
|
||||
} = item;
|
||||
|
||||
let operator = match op {
|
||||
UnaryOp::Invert => "~",
|
||||
UnaryOp::Not => "not",
|
||||
UnaryOp::UAdd => "+",
|
||||
UnaryOp::USub => "-",
|
||||
};
|
||||
|
||||
text(operator).fmt(f)?;
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
|
||||
// Split off the comments that follow after the operator and format them as trailing comments.
|
||||
// ```python
|
||||
// (not # comment
|
||||
// a)
|
||||
// ```
|
||||
let leading_operand_comments = comments.leading_comments(operand.as_ref());
|
||||
let trailing_operator_comments_end =
|
||||
leading_operand_comments.partition_point(|p| p.line_position().is_end_of_line());
|
||||
let (trailing_operator_comments, leading_operand_comments) =
|
||||
leading_operand_comments.split_at(trailing_operator_comments_end);
|
||||
|
||||
if !trailing_operator_comments.is_empty() {
|
||||
trailing_comments(trailing_operator_comments).fmt(f)?;
|
||||
}
|
||||
|
||||
// Insert a line break if the operand has comments but itself is not parenthesized.
|
||||
// ```python
|
||||
// if (
|
||||
// not
|
||||
// # comment
|
||||
// a)
|
||||
// ```
|
||||
if !leading_operand_comments.is_empty()
|
||||
&& !is_operand_parenthesized(item, f.context().source_code().as_str())
|
||||
{
|
||||
hard_line_break().fmt(f)?;
|
||||
} else if op.is_not() {
|
||||
space().fmt(f)?;
|
||||
}
|
||||
|
||||
operand.format().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +73,37 @@ impl NeedsParentheses for ExprUnaryOp {
|
||||
source: &str,
|
||||
comments: &Comments,
|
||||
) -> Parentheses {
|
||||
default_expression_needs_parentheses(self.into(), parenthesize, source, comments)
|
||||
match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) {
|
||||
Parentheses::Optional => {
|
||||
// We preserve the parentheses of the operand. It should not be necessary to break this expression.
|
||||
if is_operand_parenthesized(self, source) {
|
||||
Parentheses::Never
|
||||
} else {
|
||||
Parentheses::Optional
|
||||
}
|
||||
}
|
||||
parentheses => parentheses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_operand_parenthesized(unary: &ExprUnaryOp, source: &str) -> bool {
|
||||
let operator_len = match unary.op {
|
||||
UnaryOp::Invert => '~'.text_len(),
|
||||
UnaryOp::Not => "not".text_len(),
|
||||
UnaryOp::UAdd => '+'.text_len(),
|
||||
UnaryOp::USub => '-'.text_len(),
|
||||
};
|
||||
|
||||
let trivia_range = TextRange::new(unary.range.start() + operator_len, unary.operand.start());
|
||||
|
||||
if let Some(token) = SimpleTokenizer::new(source, trivia_range)
|
||||
.skip_trivia()
|
||||
.next()
|
||||
{
|
||||
debug_assert_eq!(token.kind(), TokenKind::LParen);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::comments::Comments;
|
||||
use crate::context::NodeLevel;
|
||||
use crate::expression::expr_tuple::TupleParentheses;
|
||||
use crate::expression::parentheses::{NeedsParentheses, Parentheses, Parenthesize};
|
||||
use crate::prelude::*;
|
||||
use ruff_formatter::{
|
||||
@@ -7,6 +8,7 @@ use ruff_formatter::{
|
||||
};
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
mod binary_like;
|
||||
pub(crate) mod expr_attribute;
|
||||
pub(crate) mod expr_await;
|
||||
pub(crate) mod expr_bin_op;
|
||||
@@ -59,7 +61,7 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||
);
|
||||
|
||||
let format_expr = format_with(|f| match item {
|
||||
Expr::BoolOp(expr) => expr.format().fmt(f),
|
||||
Expr::BoolOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f),
|
||||
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
||||
Expr::BinOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f),
|
||||
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
||||
@@ -84,7 +86,10 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||
Expr::Starred(expr) => expr.format().fmt(f),
|
||||
Expr::Name(expr) => expr.format().fmt(f),
|
||||
Expr::List(expr) => expr.format().fmt(f),
|
||||
Expr::Tuple(expr) => expr.format().fmt(f),
|
||||
Expr::Tuple(expr) => expr
|
||||
.format()
|
||||
.with_options(TupleParentheses::Expr(parentheses))
|
||||
.fmt(f),
|
||||
Expr::Slice(expr) => expr.format().fmt(f),
|
||||
});
|
||||
|
||||
|
||||
@@ -23,8 +23,12 @@ pub(super) fn default_expression_needs_parentheses(
|
||||
"Should only be called for expressions"
|
||||
);
|
||||
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
if parenthesize.is_always() {
|
||||
Parentheses::Always
|
||||
}
|
||||
// `Optional` or `Preserve` and expression has parentheses in source code.
|
||||
if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
|
||||
else if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
|
||||
Parentheses::Always
|
||||
}
|
||||
// `Optional` or `IfBreaks`: Add parentheses if the expression doesn't fit on a line but enforce
|
||||
@@ -42,7 +46,7 @@ pub(super) fn default_expression_needs_parentheses(
|
||||
}
|
||||
|
||||
/// Configures if the expression should be parenthesized.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub enum Parenthesize {
|
||||
/// Parenthesize the expression if it has parenthesis in the source.
|
||||
#[default]
|
||||
@@ -53,14 +57,20 @@ pub enum Parenthesize {
|
||||
|
||||
/// Parenthesizes the expression only if it doesn't fit on a line.
|
||||
IfBreaks,
|
||||
|
||||
Always,
|
||||
}
|
||||
|
||||
impl Parenthesize {
|
||||
const fn is_if_breaks(self) -> bool {
|
||||
pub(crate) const fn is_always(self) -> bool {
|
||||
matches!(self, Parenthesize::Always)
|
||||
}
|
||||
|
||||
pub(crate) const fn is_if_breaks(self) -> bool {
|
||||
matches!(self, Parenthesize::IfBreaks)
|
||||
}
|
||||
|
||||
const fn is_preserve(self) -> bool {
|
||||
pub(crate) const fn is_preserve(self) -> bool {
|
||||
matches!(self, Parenthesize::Preserve)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +80,8 @@ impl Parenthesize {
|
||||
/// whether there are parentheses in the source code or not.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Parentheses {
|
||||
/// Always create parentheses
|
||||
/// Always set parentheses regardless if the expression breaks or if they were
|
||||
/// present in the source.
|
||||
Always,
|
||||
|
||||
/// Only add parentheses when necessary because the expression breaks over multiple lines.
|
||||
|
||||
@@ -273,36 +273,7 @@ if True:
|
||||
|
||||
let formatted_code = printed.as_code();
|
||||
|
||||
let reformatted = match format_module(formatted_code) {
|
||||
Ok(reformatted) => reformatted,
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Expected formatted code to be valid syntax: {err}:\
|
||||
\n---\n{formatted_code}---\n",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if reformatted.as_code() != formatted_code {
|
||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||
.unified_diff()
|
||||
.header("Formatted once", "Formatted twice")
|
||||
.to_string();
|
||||
panic!(
|
||||
r#"Reformatting the formatted code a second time resulted in formatting changes.
|
||||
---
|
||||
{diff}---
|
||||
|
||||
Formatted once:
|
||||
---
|
||||
{formatted_code}---
|
||||
|
||||
Formatted twice:
|
||||
---
|
||||
{}---"#,
|
||||
reformatted.as_code()
|
||||
);
|
||||
}
|
||||
ensure_stability_when_formatting_twice(formatted_code);
|
||||
|
||||
if formatted_code == expected_output {
|
||||
// Black and Ruff formatting matches. Delete any existing snapshot files because the Black output
|
||||
@@ -374,6 +345,8 @@ Formatted twice:
|
||||
let reformatted =
|
||||
format_module(formatted_code).unwrap_or_else(|err| panic!("Expected formatted code to be valid syntax but it contains syntax errors: {err}\n{formatted_code}"));
|
||||
|
||||
ensure_stability_when_formatting_twice(formatted_code);
|
||||
|
||||
if reformatted.as_code() != formatted_code {
|
||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||
.unified_diff()
|
||||
@@ -406,16 +379,49 @@ Formatted twice:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Format another time and make sure that there are no changes anymore
|
||||
fn ensure_stability_when_formatting_twice(formatted_code: &str) {
|
||||
let reformatted = match format_module(formatted_code) {
|
||||
Ok(reformatted) => reformatted,
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Expected formatted code to be valid syntax: {err}:\
|
||||
\n---\n{formatted_code}---\n",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if reformatted.as_code() != formatted_code {
|
||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||
.unified_diff()
|
||||
.header("Formatted once", "Formatted twice")
|
||||
.to_string();
|
||||
panic!(
|
||||
r#"Reformatting the formatted code a second time resulted in formatting changes.
|
||||
---
|
||||
{diff}---
|
||||
|
||||
Formatted once:
|
||||
---
|
||||
{formatted_code}---
|
||||
|
||||
Formatted twice:
|
||||
---
|
||||
{}---"#,
|
||||
reformatted.as_code()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this test to debug the formatting of some snipped
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn quick_test() {
|
||||
let src = r#"
|
||||
|
||||
def foo(
|
||||
b=3
|
||||
+ 2 # comment
|
||||
):
|
||||
if [
|
||||
aaaaaa,
|
||||
BBBB,ccccccccc,ddddddd,eeeeeeeeee,ffffff
|
||||
] & bbbbbbbbbbbbbbbbbbddddddddddddddddddddddddddddbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:
|
||||
...
|
||||
"#;
|
||||
// Tokenize once
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::prelude::*;
|
||||
use crate::FormatNodeRule;
|
||||
use ruff_formatter::write;
|
||||
use ruff_text_size::{TextLen, TextRange};
|
||||
use rustpython_parser::ast::Arg;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -10,21 +9,13 @@ pub struct FormatArg;
|
||||
impl FormatNodeRule<Arg> for FormatArg {
|
||||
fn fmt_fields(&self, item: &Arg, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let Arg {
|
||||
range,
|
||||
range: _,
|
||||
arg,
|
||||
annotation,
|
||||
type_comment: _,
|
||||
} = item;
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
// The name of the argument
|
||||
source_text_slice(
|
||||
TextRange::at(range.start(), arg.text_len()),
|
||||
ContainsNewlines::No
|
||||
)
|
||||
]
|
||||
)?;
|
||||
|
||||
arg.format().fmt(f)?;
|
||||
|
||||
if let Some(annotation) = annotation {
|
||||
write!(f, [text(":"), space(), annotation.format()])?;
|
||||
|
||||
@@ -5,11 +5,15 @@ use rustpython_parser::ast::{Arguments, Ranged};
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
|
||||
use crate::comments::{dangling_node_comments, leading_node_comments};
|
||||
use crate::comments::{
|
||||
dangling_comments, leading_comments, leading_node_comments, trailing_comments,
|
||||
CommentLinePosition, SourceComment,
|
||||
};
|
||||
use crate::context::NodeLevel;
|
||||
use crate::prelude::*;
|
||||
use crate::trivia::{first_non_trivia_token, SimpleTokenizer, Token, TokenKind};
|
||||
use crate::FormatNodeRule;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatArguments;
|
||||
@@ -28,6 +32,10 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||
let saved_level = f.context().node_level();
|
||||
f.context_mut().set_node_level(NodeLevel::Expression);
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling_comments(item);
|
||||
let (slash, star) = find_argument_separators(f.context().contents(), item);
|
||||
|
||||
let format_inner = format_with(|f: &mut PyFormatter| {
|
||||
let separator = format_with(|f| write!(f, [text(","), soft_line_break_or_space()]));
|
||||
let mut joiner = f.join_with(separator);
|
||||
@@ -39,9 +47,29 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||
last_node = Some(arg_with_default.into());
|
||||
}
|
||||
|
||||
if !posonlyargs.is_empty() {
|
||||
joiner.entry(&text("/"));
|
||||
}
|
||||
let slash_comments_end = if posonlyargs.is_empty() {
|
||||
0
|
||||
} else {
|
||||
let slash_comments_end = dangling.partition_point(|comment| {
|
||||
let assignment = assign_argument_separator_comment_placement(
|
||||
slash.as_ref(),
|
||||
star.as_ref(),
|
||||
comment.slice().range(),
|
||||
comment.line_position(),
|
||||
)
|
||||
.expect("Unexpected dangling comment type in function arguments");
|
||||
matches!(
|
||||
assignment,
|
||||
ArgumentSeparatorCommentLocation::SlashLeading
|
||||
| ArgumentSeparatorCommentLocation::SlashTrailing
|
||||
)
|
||||
});
|
||||
joiner.entry(&CommentsAroundText {
|
||||
text: "/",
|
||||
comments: &dangling[..slash_comments_end],
|
||||
});
|
||||
slash_comments_end
|
||||
};
|
||||
|
||||
for arg_with_default in args {
|
||||
joiner.entry(&arg_with_default.format());
|
||||
@@ -60,7 +88,26 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||
]);
|
||||
last_node = Some(vararg.as_any_node_ref());
|
||||
} else if !kwonlyargs.is_empty() {
|
||||
joiner.entry(&text("*"));
|
||||
// Given very strange comment placement, comments here may not actually have been
|
||||
// marked as `StarLeading`/`StarTrailing`, but that's fine since we still produce
|
||||
// a stable formatting in this case
|
||||
// ```python
|
||||
// def f42(
|
||||
// a,
|
||||
// / # 1
|
||||
// # 2
|
||||
// , # 3
|
||||
// # 4
|
||||
// * # 5
|
||||
// , # 6
|
||||
// c,
|
||||
// ):
|
||||
// pass
|
||||
// ```
|
||||
joiner.entry(&CommentsAroundText {
|
||||
text: "*",
|
||||
comments: &dangling[slash_comments_end..],
|
||||
});
|
||||
}
|
||||
|
||||
for arg_with_default in kwonlyargs {
|
||||
@@ -127,7 +174,7 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||
f,
|
||||
[
|
||||
text("("),
|
||||
block_indent(&dangling_node_comments(item)),
|
||||
block_indent(&dangling_comments(dangling)),
|
||||
text(")")
|
||||
]
|
||||
)?;
|
||||
@@ -152,3 +199,367 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct CommentsAroundText<'a> {
|
||||
text: &'static str,
|
||||
comments: &'a [SourceComment],
|
||||
}
|
||||
|
||||
impl Format<PyFormatContext<'_>> for CommentsAroundText<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||
if self.comments.is_empty() {
|
||||
text(self.text).fmt(f)
|
||||
} else {
|
||||
// There might be own line comments in trailing, but those are weird and we can kinda
|
||||
// ignore them
|
||||
// ```python
|
||||
// def f42(
|
||||
// a,
|
||||
// # leading comment (own line)
|
||||
// / # first trailing comment (end-of-line)
|
||||
// # trailing own line comment
|
||||
// ,
|
||||
// c,
|
||||
// ):
|
||||
// ```
|
||||
let (leading, trailing) = self.comments.split_at(
|
||||
self.comments
|
||||
.partition_point(|comment| comment.line_position().is_own_line()),
|
||||
);
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
leading_comments(leading),
|
||||
text(self.text),
|
||||
trailing_comments(trailing)
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `/` and `*` in a function signature
|
||||
///
|
||||
/// ```text
|
||||
/// def f(arg_a, /, arg_b, *, arg_c): pass
|
||||
/// ^ ^ ^ ^ ^ ^ slash preceding end
|
||||
/// ^ ^ ^ ^ ^ slash (a separator)
|
||||
/// ^ ^ ^ ^ slash following start
|
||||
/// ^ ^ ^ star preceding end
|
||||
/// ^ ^ star (a separator)
|
||||
/// ^ star following start
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ArgumentSeparator {
|
||||
/// The end of the last node or separator before this separator
|
||||
pub(crate) preceding_end: TextSize,
|
||||
/// The range of the separator itself
|
||||
pub(crate) separator: TextRange,
|
||||
/// The start of the first node or separator following this separator
|
||||
pub(crate) following_start: TextSize,
|
||||
}
|
||||
|
||||
/// Finds slash and star in `f(a, /, b, *, c)`
|
||||
///
|
||||
/// Returns slash and star
|
||||
pub(crate) fn find_argument_separators(
|
||||
contents: &str,
|
||||
arguments: &Arguments,
|
||||
) -> (Option<ArgumentSeparator>, Option<ArgumentSeparator>) {
|
||||
// We only compute preceding_end and token location here since following_start depends on the
|
||||
// star location, but the star location depends on slash's position
|
||||
let slash = if let Some(preceding_end) = arguments.posonlyargs.last().map(Ranged::end) {
|
||||
// ```text
|
||||
// def f(a1=1, a2=2, /, a3, a4): pass
|
||||
// ^^^^^^^^^^^ the range (defaults)
|
||||
// def f(a1, a2, /, a3, a4): pass
|
||||
// ^^^^^^^^^^^^ the range (no default)
|
||||
// ```
|
||||
let range = TextRange::new(preceding_end, arguments.end());
|
||||
let mut tokens = SimpleTokenizer::new(contents, range).skip_trivia();
|
||||
|
||||
let comma = tokens
|
||||
.next()
|
||||
.expect("The function definition can't end here");
|
||||
debug_assert!(comma.kind() == TokenKind::Comma, "{comma:?}");
|
||||
let slash = tokens
|
||||
.next()
|
||||
.expect("The function definition can't end here");
|
||||
debug_assert!(slash.kind() == TokenKind::Slash, "{slash:?}");
|
||||
|
||||
Some((preceding_end, slash.range))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If we have a vararg we have a node that the comments attach to
|
||||
let star = if arguments.vararg.is_some() {
|
||||
// When the vararg is present the comments attach there and we don't need to do manual
|
||||
// formatting
|
||||
None
|
||||
} else if let Some(first_keyword_argument) = arguments.kwonlyargs.first() {
|
||||
// Check in that order:
|
||||
// * `f(a, /, b, *, c)` and `f(a=1, /, b=2, *, c)`
|
||||
// * `f(a, /, *, b)`
|
||||
// * `f(*, b)` (else branch)
|
||||
let after_arguments = arguments
|
||||
.args
|
||||
.last()
|
||||
.map(|arg| arg.range.end())
|
||||
.or(slash.map(|(_, slash)| slash.end()));
|
||||
if let Some(preceding_end) = after_arguments {
|
||||
let range = TextRange::new(preceding_end, arguments.end());
|
||||
let mut tokens = SimpleTokenizer::new(contents, range).skip_trivia();
|
||||
|
||||
let comma = tokens
|
||||
.next()
|
||||
.expect("The function definition can't end here");
|
||||
debug_assert!(comma.kind() == TokenKind::Comma, "{comma:?}");
|
||||
let star = tokens
|
||||
.next()
|
||||
.expect("The function definition can't end here");
|
||||
debug_assert!(star.kind() == TokenKind::Star, "{star:?}");
|
||||
|
||||
Some(ArgumentSeparator {
|
||||
preceding_end,
|
||||
separator: star.range,
|
||||
following_start: first_keyword_argument.start(),
|
||||
})
|
||||
} else {
|
||||
let mut tokens = SimpleTokenizer::new(contents, arguments.range).skip_trivia();
|
||||
|
||||
let lparen = tokens
|
||||
.next()
|
||||
.expect("The function definition can't end here");
|
||||
debug_assert!(lparen.kind() == TokenKind::LParen, "{lparen:?}");
|
||||
let star = tokens
|
||||
.next()
|
||||
.expect("The function definition can't end here");
|
||||
debug_assert!(star.kind() == TokenKind::Star, "{star:?}");
|
||||
Some(ArgumentSeparator {
|
||||
preceding_end: arguments.range.start(),
|
||||
separator: star.range,
|
||||
following_start: first_keyword_argument.start(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Now that we have star, compute how long slash trailing comments can go
|
||||
// Check in that order:
|
||||
// * `f(a, /, b)`
|
||||
// * `f(a, /, *b)`
|
||||
// * `f(a, /, *, b)`
|
||||
// * `f(a, /)`
|
||||
let slash_following_start = arguments
|
||||
.args
|
||||
.first()
|
||||
.map(Ranged::start)
|
||||
.or(arguments.vararg.as_ref().map(|first| first.start()))
|
||||
.or(star.as_ref().map(|star| star.separator.start()))
|
||||
.unwrap_or(arguments.end());
|
||||
let slash = slash.map(|(preceding_end, slash)| ArgumentSeparator {
|
||||
preceding_end,
|
||||
separator: slash,
|
||||
following_start: slash_following_start,
|
||||
});
|
||||
|
||||
(slash, star)
|
||||
}
|
||||
|
||||
/// Locates positional only arguments separator `/` or the keywords only arguments
|
||||
/// separator `*` comments.
|
||||
///
|
||||
/// ```python
|
||||
/// def test(
|
||||
/// a,
|
||||
/// # Positional only arguments after here
|
||||
/// /, # trailing positional argument comment.
|
||||
/// b,
|
||||
/// ):
|
||||
/// pass
|
||||
/// ```
|
||||
/// or
|
||||
/// ```python
|
||||
/// def f(
|
||||
/// a="",
|
||||
/// # Keyword only arguments only after here
|
||||
/// *, # trailing keyword argument comment.
|
||||
/// b="",
|
||||
/// ):
|
||||
/// pass
|
||||
/// ```
|
||||
/// or
|
||||
/// ```python
|
||||
/// def f(
|
||||
/// a,
|
||||
/// # positional only comment, leading
|
||||
/// /, # positional only comment, trailing
|
||||
/// b,
|
||||
/// # keyword only comment, leading
|
||||
/// *, # keyword only comment, trailing
|
||||
/// c,
|
||||
/// ):
|
||||
/// pass
|
||||
/// ```
|
||||
/// Notably, the following is possible:
|
||||
/// ```python
|
||||
/// def f32(
|
||||
/// a,
|
||||
/// # positional only comment, leading
|
||||
/// /, # positional only comment, trailing
|
||||
/// # keyword only comment, leading
|
||||
/// *, # keyword only comment, trailing
|
||||
/// c,
|
||||
/// ):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ## Background
|
||||
///
|
||||
/// ```text
|
||||
/// def f(a1, a2): pass
|
||||
/// ^^^^^^ arguments (args)
|
||||
/// ```
|
||||
/// Use a star to separate keyword only arguments:
|
||||
/// ```text
|
||||
/// def f(a1, a2, *, a3, a4): pass
|
||||
/// ^^^^^^ arguments (args)
|
||||
/// ^^^^^^ keyword only arguments (kwargs)
|
||||
/// ```
|
||||
/// Use a slash to separate positional only arguments. Note that this changes the arguments left
|
||||
/// of the slash while the star change the arguments right of it:
|
||||
/// ```text
|
||||
/// def f(a1, a2, /, a3, a4): pass
|
||||
/// ^^^^^^ positional only arguments (posonlyargs)
|
||||
/// ^^^^^^ arguments (args)
|
||||
/// ```
|
||||
/// You can combine both:
|
||||
/// ```text
|
||||
/// def f(a1, a2, /, a3, a4, *, a5, a6): pass
|
||||
/// ^^^^^^ positional only arguments (posonlyargs)
|
||||
/// ^^^^^^ arguments (args)
|
||||
/// ^^^^^^ keyword only arguments (kwargs)
|
||||
/// ```
|
||||
/// They can all have defaults, meaning that the preceding node ends at the default instead of the
|
||||
/// argument itself:
|
||||
/// ```text
|
||||
/// def f(a1=1, a2=2, /, a3=3, a4=4, *, a5=5, a6=6): pass
|
||||
/// ^ ^ ^ ^ ^ ^ defaults
|
||||
/// ^^^^^^^^^^ positional only arguments (posonlyargs)
|
||||
/// ^^^^^^^^^^ arguments (args)
|
||||
/// ^^^^^^^^^^ keyword only arguments (kwargs)
|
||||
/// ```
|
||||
/// An especially difficult case is having no regular arguments, so comments from both slash and
|
||||
/// star will attach to either a2 or a3 and the next token is incorrect.
|
||||
/// ```text
|
||||
/// def f(a1, a2, /, *, a3, a4): pass
|
||||
/// ^^^^^^ positional only arguments (posonlyargs)
|
||||
/// ^^^^^^ keyword only arguments (kwargs)
|
||||
/// ```
|
||||
pub(crate) fn assign_argument_separator_comment_placement(
|
||||
slash: Option<&ArgumentSeparator>,
|
||||
star: Option<&ArgumentSeparator>,
|
||||
comment_range: TextRange,
|
||||
text_position: CommentLinePosition,
|
||||
) -> Option<ArgumentSeparatorCommentLocation> {
|
||||
if let Some(ArgumentSeparator {
|
||||
preceding_end,
|
||||
separator: slash,
|
||||
following_start,
|
||||
}) = slash
|
||||
{
|
||||
// ```python
|
||||
// def f(
|
||||
// # start too early
|
||||
// a, # not own line
|
||||
// # this is the one
|
||||
// /, # too late (handled later)
|
||||
// b,
|
||||
// )
|
||||
// ```
|
||||
if comment_range.start() > *preceding_end
|
||||
&& comment_range.start() < slash.start()
|
||||
&& text_position.is_own_line()
|
||||
{
|
||||
return Some(ArgumentSeparatorCommentLocation::SlashLeading);
|
||||
}
|
||||
|
||||
// ```python
|
||||
// def f(
|
||||
// a,
|
||||
// # too early (handled above)
|
||||
// /, # this is the one
|
||||
// # not end-of-line
|
||||
// b,
|
||||
// )
|
||||
// ```
|
||||
if comment_range.start() > slash.end()
|
||||
&& comment_range.start() < *following_start
|
||||
&& text_position.is_end_of_line()
|
||||
{
|
||||
return Some(ArgumentSeparatorCommentLocation::SlashTrailing);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ArgumentSeparator {
|
||||
preceding_end,
|
||||
separator: star,
|
||||
following_start,
|
||||
}) = star
|
||||
{
|
||||
// ```python
|
||||
// def f(
|
||||
// # start too early
|
||||
// a, # not own line
|
||||
// # this is the one
|
||||
// *, # too late (handled later)
|
||||
// b,
|
||||
// )
|
||||
// ```
|
||||
if comment_range.start() > *preceding_end
|
||||
&& comment_range.start() < star.start()
|
||||
&& text_position.is_own_line()
|
||||
{
|
||||
return Some(ArgumentSeparatorCommentLocation::StarLeading);
|
||||
}
|
||||
|
||||
// ```python
|
||||
// def f(
|
||||
// a,
|
||||
// # too early (handled above)
|
||||
// *, # this is the one
|
||||
// # not end-of-line
|
||||
// b,
|
||||
// )
|
||||
// ```
|
||||
if comment_range.start() > star.end()
|
||||
&& comment_range.start() < *following_start
|
||||
&& text_position.is_end_of_line()
|
||||
{
|
||||
return Some(ArgumentSeparatorCommentLocation::StarTrailing);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// ```python
|
||||
/// def f(
|
||||
/// a,
|
||||
/// # before slash
|
||||
/// /, # after slash
|
||||
/// b,
|
||||
/// # before star
|
||||
/// *, # after star
|
||||
/// c,
|
||||
/// ):
|
||||
/// pass
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ArgumentSeparatorCommentLocation {
|
||||
SlashLeading,
|
||||
SlashTrailing,
|
||||
StarLeading,
|
||||
StarTrailing,
|
||||
}
|
||||
|
||||
28
crates/ruff_python_formatter/src/other/identifier.rs
Normal file
28
crates/ruff_python_formatter/src/other/identifier.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use crate::prelude::*;
|
||||
use crate::AsFormat;
|
||||
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule};
|
||||
use rustpython_parser::ast::{Identifier, Ranged};
|
||||
|
||||
pub struct FormatIdentifier;
|
||||
|
||||
impl FormatRule<Identifier, PyFormatContext<'_>> for FormatIdentifier {
|
||||
fn fmt(&self, item: &Identifier, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
source_text_slice(item.range(), ContainsNewlines::No).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> AsFormat<PyFormatContext<'ast>> for Identifier {
|
||||
type Format<'a> = FormatRefWithRule<'a, Identifier, FormatIdentifier, PyFormatContext<'ast>>;
|
||||
|
||||
fn format(&self) -> Self::Format<'_> {
|
||||
FormatRefWithRule::new(self, FormatIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> IntoFormat<PyFormatContext<'ast>> for Identifier {
|
||||
type Format = FormatOwnedWithRule<Identifier, FormatIdentifier, PyFormatContext<'ast>>;
|
||||
|
||||
fn into_format(self) -> Self::Format {
|
||||
FormatOwnedWithRule::new(self, FormatIdentifier)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ pub(crate) mod arguments;
|
||||
pub(crate) mod comprehension;
|
||||
pub(crate) mod decorator;
|
||||
pub(crate) mod except_handler_except_handler;
|
||||
pub(crate) mod identifier;
|
||||
pub(crate) mod keyword;
|
||||
pub(crate) mod match_case;
|
||||
pub(crate) mod type_ignore_type_ignore;
|
||||
|
||||
@@ -55,27 +55,25 @@ y = 100(no)
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = 1. .NOT_IMPLEMENTED_attr
|
||||
+x = 1E+1 .NOT_IMPLEMENTED_attr
|
||||
+x = 1E-1 .NOT_IMPLEMENTED_attr
|
||||
+x = (1.).imag
|
||||
+x = (1E+1).imag
|
||||
+x = (1E-1).real
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = 123456789.123456789E123456789 .NOT_IMPLEMENTED_attr
|
||||
+x = (123456789.123456789E123456789).real
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = 123456789J.NOT_IMPLEMENTED_attr
|
||||
+x = 123456789J.real
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = 0O777 .NOT_IMPLEMENTED_attr
|
||||
+x = (0O777).real
|
||||
+x = NOT_IMPLEMENTED_call()
|
||||
+x = NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+x = -100.0000J
|
||||
|
||||
-if (10).real:
|
||||
+if 10 .NOT_IMPLEMENTED_attr:
|
||||
if (10).real:
|
||||
...
|
||||
|
||||
-y = 100[no]
|
||||
y = 100[no]
|
||||
-y = 100(no)
|
||||
+y = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+y = NOT_IMPLEMENTED_call()
|
||||
```
|
||||
|
||||
@@ -85,24 +83,24 @@ y = 100(no)
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = 1. .NOT_IMPLEMENTED_attr
|
||||
x = 1E+1 .NOT_IMPLEMENTED_attr
|
||||
x = 1E-1 .NOT_IMPLEMENTED_attr
|
||||
x = (1.).imag
|
||||
x = (1E+1).imag
|
||||
x = (1E-1).real
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = 123456789.123456789E123456789 .NOT_IMPLEMENTED_attr
|
||||
x = (123456789.123456789E123456789).real
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = 123456789J.NOT_IMPLEMENTED_attr
|
||||
x = 123456789J.real
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = 0O777 .NOT_IMPLEMENTED_attr
|
||||
x = (0O777).real
|
||||
x = NOT_IMPLEMENTED_call()
|
||||
x = NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
x = -100.0000J
|
||||
|
||||
if 10 .NOT_IMPLEMENTED_attr:
|
||||
if (10).real:
|
||||
...
|
||||
|
||||
y = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
y = 100[no]
|
||||
y = NOT_IMPLEMENTED_call()
|
||||
```
|
||||
|
||||
|
||||
@@ -19,12 +19,14 @@ lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,4 +1,3 @@
|
||||
@@ -1,4 +1,6 @@
|
||||
-for ((x in {}) or {})["a"] in x:
|
||||
- pass
|
||||
+for ((NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right) or {})[
|
||||
+ "NOT_YET_IMPLEMENTED_STRING"
|
||||
+] in x:
|
||||
pass
|
||||
-pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip())
|
||||
-lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+pem_spam = lambda x: True
|
||||
+lambda x: True
|
||||
```
|
||||
@@ -32,7 +34,10 @@ lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x
|
||||
## Ruff Output
|
||||
|
||||
```py
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for ((NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right) or {})[
|
||||
"NOT_YET_IMPLEMENTED_STRING"
|
||||
] in x:
|
||||
pass
|
||||
pem_spam = lambda x: True
|
||||
lambda x: True
|
||||
```
|
||||
|
||||
@@ -84,7 +84,7 @@ if True:
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,99 +1,56 @@
|
||||
@@ -1,61 +1,40 @@
|
||||
-import core, time, a
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
|
||||
@@ -164,12 +164,10 @@ if True:
|
||||
+NOT_YET_IMPLEMENTED_StmtAssert
|
||||
|
||||
# looping over a 1-tuple should also not get wrapped
|
||||
-for x in (1,):
|
||||
- pass
|
||||
-for (x,) in (1,), (2,), (3,):
|
||||
- pass
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
for x in (1,):
|
||||
@@ -63,14 +42,10 @@
|
||||
for (x,) in (1,), (2,), (3,):
|
||||
pass
|
||||
|
||||
-[
|
||||
- 1,
|
||||
@@ -183,13 +181,9 @@ if True:
|
||||
+NOT_IMPLEMENTED_call()
|
||||
|
||||
if True:
|
||||
- IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = (
|
||||
- Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING
|
||||
- | {pylons.controllers.WSGIController}
|
||||
- )
|
||||
+ IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = Config.NOT_IMPLEMENTED_attr | {
|
||||
+ pylons.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr,
|
||||
+ }
|
||||
IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = (
|
||||
@@ -79,21 +54,6 @@
|
||||
)
|
||||
|
||||
if True:
|
||||
- ec2client.get_waiter("instance_stopped").wait(
|
||||
@@ -257,8 +251,10 @@ y = {
|
||||
NOT_YET_IMPLEMENTED_StmtAssert
|
||||
|
||||
# looping over a 1-tuple should also not get wrapped
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for x in (1,):
|
||||
pass
|
||||
for (x,) in (1,), (2,), (3,):
|
||||
pass
|
||||
|
||||
[1, 2, 3]
|
||||
|
||||
@@ -266,9 +262,10 @@ division_result_tuple = (6 / 2,)
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
if True:
|
||||
IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = Config.NOT_IMPLEMENTED_attr | {
|
||||
pylons.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr,
|
||||
}
|
||||
IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = (
|
||||
Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING
|
||||
| {pylons.controllers.WSGIController}
|
||||
)
|
||||
|
||||
if True:
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
@@ -227,7 +227,7 @@ instruction()#comment with bad spacing
|
||||
]
|
||||
|
||||
not_shareables = [
|
||||
@@ -37,50 +33,51 @@
|
||||
@@ -37,31 +33,35 @@
|
||||
# builtin types and objects
|
||||
type,
|
||||
object,
|
||||
@@ -263,47 +263,39 @@ instruction()#comment with bad spacing
|
||||
def inline_comments_in_brackets_ruin_everything():
|
||||
if typedargslist:
|
||||
- parameters.children = [children[0], body, children[-1]] # (1 # )1
|
||||
- parameters.children = [
|
||||
- children[0],
|
||||
+ parameters.NOT_IMPLEMENTED_attr = [
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (1
|
||||
parameters.children = [
|
||||
+ children[0], # (1
|
||||
+ body,
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )1
|
||||
+ children[-1], # )1
|
||||
+ ]
|
||||
+ parameters.NOT_IMPLEMENTED_attr = [
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key],
|
||||
+ parameters.children = [
|
||||
children[0],
|
||||
body,
|
||||
- children[-1], # type: ignore
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: ignore
|
||||
]
|
||||
else:
|
||||
- parameters.children = [
|
||||
- parameters.children[0], # (2 what if this was actually long
|
||||
+ parameters.NOT_IMPLEMENTED_attr = [
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (2 what if this was actually long
|
||||
children[-1], # type: ignore
|
||||
@@ -72,14 +72,18 @@
|
||||
body,
|
||||
- parameters.children[-1], # )2
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )2
|
||||
parameters.children[-1], # )2
|
||||
]
|
||||
- parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore
|
||||
- if (
|
||||
- self._proc is not None
|
||||
- # has the child process finished?
|
||||
- and self._returncode is None
|
||||
- # the child process has finished, but the
|
||||
- # transport hasn't been notified yet?
|
||||
- and self._proc.poll() is None
|
||||
- ):
|
||||
+ parameters.NOT_IMPLEMENTED_attr = [
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key],
|
||||
+ parameters.children = [
|
||||
+ parameters.what_if_this_was_actually_long.children[0],
|
||||
+ body,
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key],
|
||||
+ parameters.children[-1],
|
||||
+ ] # type: ignore
|
||||
+ if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if (
|
||||
- self._proc is not None
|
||||
+ NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
# has the child process finished?
|
||||
- and self._returncode is None
|
||||
+ and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
# the child process has finished, but the
|
||||
# transport hasn't been notified yet?
|
||||
- and self._proc.poll() is None
|
||||
+ and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
):
|
||||
pass
|
||||
# no newline before or after
|
||||
short = [
|
||||
@@ -91,48 +88,14 @@
|
||||
@@ -91,48 +95,14 @@
|
||||
]
|
||||
|
||||
# no newline after
|
||||
@@ -357,7 +349,7 @@ instruction()#comment with bad spacing
|
||||
while True:
|
||||
if False:
|
||||
continue
|
||||
@@ -141,25 +104,13 @@
|
||||
@@ -141,25 +111,13 @@
|
||||
# and round and round we go
|
||||
|
||||
# let's return
|
||||
@@ -386,7 +378,7 @@ instruction()#comment with bad spacing
|
||||
|
||||
|
||||
#######################
|
||||
@@ -167,7 +118,7 @@
|
||||
@@ -167,7 +125,7 @@
|
||||
#######################
|
||||
|
||||
|
||||
@@ -458,28 +450,35 @@ else:
|
||||
# Comment before function.
|
||||
def inline_comments_in_brackets_ruin_everything():
|
||||
if typedargslist:
|
||||
parameters.NOT_IMPLEMENTED_attr = [
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (1
|
||||
parameters.children = [
|
||||
children[0], # (1
|
||||
body,
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )1
|
||||
children[-1], # )1
|
||||
]
|
||||
parameters.NOT_IMPLEMENTED_attr = [
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key],
|
||||
parameters.children = [
|
||||
children[0],
|
||||
body,
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: ignore
|
||||
children[-1], # type: ignore
|
||||
]
|
||||
else:
|
||||
parameters.NOT_IMPLEMENTED_attr = [
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (2 what if this was actually long
|
||||
parameters.children = [
|
||||
parameters.children[0], # (2 what if this was actually long
|
||||
body,
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )2
|
||||
parameters.children[-1], # )2
|
||||
]
|
||||
parameters.NOT_IMPLEMENTED_attr = [
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key],
|
||||
parameters.children = [
|
||||
parameters.what_if_this_was_actually_long.children[0],
|
||||
body,
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key],
|
||||
parameters.children[-1],
|
||||
] # type: ignore
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if (
|
||||
NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
# has the child process finished?
|
||||
and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
# the child process has finished, but the
|
||||
# transport hasn't been notified yet?
|
||||
and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
):
|
||||
pass
|
||||
# no newline before or after
|
||||
short = [
|
||||
|
||||
@@ -61,7 +61,7 @@ def func():
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -3,46 +3,15 @@
|
||||
@@ -3,46 +3,17 @@
|
||||
|
||||
# %%
|
||||
def func():
|
||||
@@ -82,7 +82,7 @@ def func():
|
||||
- if isinstance(exc_value, MultiError):
|
||||
+ if NOT_IMPLEMENTED_call():
|
||||
embedded = []
|
||||
- for exc in exc_value.exceptions:
|
||||
for exc in exc_value.exceptions:
|
||||
- if exc not in _seen:
|
||||
- embedded.append(
|
||||
- # This should be left alone (before)
|
||||
@@ -97,7 +97,8 @@ def func():
|
||||
- )
|
||||
- # This should be left alone (after)
|
||||
- )
|
||||
+ NOT_YET_IMPLEMENTED_StmtFor
|
||||
+ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
|
||||
# everything is fine if the expression isn't nested
|
||||
- traceback.TracebackException.from_exception(
|
||||
@@ -128,7 +129,9 @@ def func():
|
||||
# Capture each of the exceptions in the MultiError along with each of their causes and contexts
|
||||
if NOT_IMPLEMENTED_call():
|
||||
embedded = []
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for exc in exc_value.exceptions:
|
||||
if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
# everything is fine if the expression isn't nested
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
@@ -86,37 +86,38 @@ if __name__ == "__main__":
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,33 +1,20 @@
|
||||
@@ -1,6 +1,6 @@
|
||||
while True:
|
||||
- if something.changed:
|
||||
if something.changed:
|
||||
- do.stuff() # trailing comment
|
||||
+ if something.NOT_IMPLEMENTED_attr:
|
||||
+ NOT_IMPLEMENTED_call() # trailing comment
|
||||
# Comment belongs to the `if` block.
|
||||
# This one belongs to the `while` block.
|
||||
|
||||
# Should this one, too? I guess so.
|
||||
@@ -8,26 +8,20 @@
|
||||
|
||||
# This one is properly standalone now.
|
||||
-
|
||||
-for i in range(100):
|
||||
- # first we do this
|
||||
- if i % 33 == 0:
|
||||
- break
|
||||
|
||||
- # then we do this
|
||||
-for i in range(100):
|
||||
+for i in NOT_IMPLEMENTED_call():
|
||||
# first we do this
|
||||
- if i % 33 == 0:
|
||||
+ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
break
|
||||
|
||||
# then we do this
|
||||
- print(i)
|
||||
- # and finally we loop around
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
# and finally we loop around
|
||||
|
||||
-with open(some_temp_file) as f:
|
||||
- data = f.read()
|
||||
-
|
||||
+NOT_YET_IMPLEMENTED_StmtWith
|
||||
|
||||
-try:
|
||||
- with open(some_other_file) as w:
|
||||
- w.write(data)
|
||||
+NOT_YET_IMPLEMENTED_StmtWith
|
||||
|
||||
-
|
||||
-except OSError:
|
||||
- print("problems")
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
@@ -126,7 +127,7 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
# leading function comment
|
||||
@@ -42,7 +29,7 @@
|
||||
@@ -42,7 +36,7 @@
|
||||
# leading 1
|
||||
@deco1
|
||||
# leading 2
|
||||
@@ -135,7 +136,7 @@ if __name__ == "__main__":
|
||||
# leading 3
|
||||
@deco3
|
||||
def decorated1():
|
||||
@@ -52,7 +39,7 @@
|
||||
@@ -52,7 +46,7 @@
|
||||
# leading 1
|
||||
@deco1
|
||||
# leading 2
|
||||
@@ -144,7 +145,7 @@ if __name__ == "__main__":
|
||||
# leading function comment
|
||||
def decorated1():
|
||||
...
|
||||
@@ -69,5 +56,5 @@
|
||||
@@ -69,5 +63,5 @@
|
||||
...
|
||||
|
||||
|
||||
@@ -158,7 +159,7 @@ if __name__ == "__main__":
|
||||
|
||||
```py
|
||||
while True:
|
||||
if something.NOT_IMPLEMENTED_attr:
|
||||
if something.changed:
|
||||
NOT_IMPLEMENTED_call() # trailing comment
|
||||
# Comment belongs to the `if` block.
|
||||
# This one belongs to the `while` block.
|
||||
@@ -167,7 +168,14 @@ while True:
|
||||
|
||||
# This one is properly standalone now.
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for i in NOT_IMPLEMENTED_call():
|
||||
# first we do this
|
||||
if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
break
|
||||
|
||||
# then we do this
|
||||
NOT_IMPLEMENTED_call()
|
||||
# and finally we loop around
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtWith
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
|
||||
|
||||
|
||||
def f(
|
||||
@@ -49,10 +49,8 @@
|
||||
@@ -49,10 +49,11 @@
|
||||
element = 0 # type: int
|
||||
another_element = 1 # type: float
|
||||
another_element_with_long_name = 2 # type: int
|
||||
@@ -146,16 +146,16 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
|
||||
- ) # type: int
|
||||
- an_element_with_a_long_value = calls() or more_calls() and more() # type: bool
|
||||
+ another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = 3 # type: int
|
||||
+ an_element_with_a_long_value = NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2 # type: bool
|
||||
+ an_element_with_a_long_value = (
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ or NOT_IMPLEMENTED_call() and NOT_IMPLEMENTED_call()
|
||||
+ ) # type: bool
|
||||
|
||||
tup = (
|
||||
another_element,
|
||||
@@ -84,35 +82,22 @@
|
||||
|
||||
|
||||
@@ -86,33 +87,20 @@
|
||||
def func(
|
||||
- a=some_list[0], # type: int
|
||||
+ a=NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: int
|
||||
a=some_list[0], # type: int
|
||||
): # type: () -> int
|
||||
- c = call(
|
||||
- 0.0123,
|
||||
@@ -255,7 +255,10 @@ def f(
|
||||
another_element = 1 # type: float
|
||||
another_element_with_long_name = 2 # type: int
|
||||
another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = 3 # type: int
|
||||
an_element_with_a_long_value = NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2 # type: bool
|
||||
an_element_with_a_long_value = (
|
||||
NOT_IMPLEMENTED_call()
|
||||
or NOT_IMPLEMENTED_call() and NOT_IMPLEMENTED_call()
|
||||
) # type: bool
|
||||
|
||||
tup = (
|
||||
another_element,
|
||||
@@ -287,7 +290,7 @@ def f(
|
||||
|
||||
|
||||
def func(
|
||||
a=NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: int
|
||||
a=some_list[0], # type: int
|
||||
): # type: () -> int
|
||||
c = NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
@@ -109,38 +109,35 @@ async def wat():
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -4,24 +4,15 @@
|
||||
@@ -4,21 +4,15 @@
|
||||
#
|
||||
# Has many lines. Many, many lines.
|
||||
# Many, many, many lines.
|
||||
-"""Module docstring.
|
||||
+"NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
-
|
||||
-Possibly also many, many lines.
|
||||
-"""
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
+"NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
-import os.path
|
||||
-import sys
|
||||
-
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
|
||||
-import a
|
||||
-from b.c import X # some noqa comment
|
||||
-
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
+NOT_YET_IMPLEMENTED_StmtImportFrom # some noqa comment
|
||||
|
||||
-try:
|
||||
- import fast
|
||||
-except ImportError:
|
||||
- import slow as fast
|
||||
-
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
+NOT_YET_IMPLEMENTED_StmtImportFrom # some noqa comment
|
||||
|
||||
-# Some comment before a function.
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
y = 1
|
||||
(
|
||||
# some strings
|
||||
@@ -30,67 +21,50 @@
|
||||
|
||||
|
||||
# Some comment before a function.
|
||||
@@ -30,67 +24,50 @@
|
||||
|
||||
|
||||
def function(default=None):
|
||||
@@ -177,17 +174,17 @@ async def wat():
|
||||
|
||||
# Another comment!
|
||||
# This time two lines.
|
||||
-
|
||||
-
|
||||
|
||||
|
||||
-class Foo:
|
||||
- """Docstring for class Foo. Example from Sphinx docs."""
|
||||
|
||||
-
|
||||
- #: Doc comment for class attribute Foo.bar.
|
||||
- #: It can have multiple lines.
|
||||
- bar = 1
|
||||
-
|
||||
- flox = 1.5 #: Doc comment for Foo.flox. One line only.
|
||||
|
||||
-
|
||||
- baz = 2
|
||||
- """Docstring for class attribute Foo.baz."""
|
||||
-
|
||||
@@ -245,6 +242,9 @@ NOT_YET_IMPLEMENTED_StmtImport
|
||||
NOT_YET_IMPLEMENTED_StmtImportFrom # some noqa comment
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
|
||||
# Some comment before a function.
|
||||
y = 1
|
||||
(
|
||||
# some strings
|
||||
|
||||
@@ -105,7 +105,7 @@ def g():
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,89 +1,70 @@
|
||||
@@ -1,59 +1,46 @@
|
||||
-"""Docstring."""
|
||||
+"NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
@@ -119,12 +119,9 @@ def g():
|
||||
+ SPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
+ DOUBLESPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
- t = leaf.type
|
||||
- p = leaf.parent # trailing comment
|
||||
- v = leaf.value
|
||||
+ t = leaf.NOT_IMPLEMENTED_attr
|
||||
+ p = leaf.NOT_IMPLEMENTED_attr # trailing comment
|
||||
+ v = leaf.NOT_IMPLEMENTED_attr
|
||||
t = leaf.type
|
||||
p = leaf.parent # trailing comment
|
||||
v = leaf.value
|
||||
|
||||
- if t in ALWAYS_NO_SPACE:
|
||||
+ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
@@ -136,14 +133,12 @@ def g():
|
||||
- assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
|
||||
+ NOT_YET_IMPLEMENTED_StmtAssert
|
||||
|
||||
- prev = leaf.prev_sibling
|
||||
- if not prev:
|
||||
prev = leaf.prev_sibling
|
||||
if not prev:
|
||||
- prevp = preceding_leaf(p)
|
||||
- if not prevp or prevp.type in OPENING_BRACKETS:
|
||||
+ prev = leaf.NOT_IMPLEMENTED_attr
|
||||
+ if NOT_YET_IMPLEMENTED_ExprUnaryOp:
|
||||
+ prevp = NOT_IMPLEMENTED_call()
|
||||
+ if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ if not prevp or NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
|
||||
- if prevp.type == token.EQUAL:
|
||||
@@ -155,7 +150,7 @@ def g():
|
||||
- syms.argument,
|
||||
- }:
|
||||
+ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
+ if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ if prevp.parent and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
|
||||
- elif prevp.type == token.DOUBLESTAR:
|
||||
@@ -167,7 +162,7 @@ def g():
|
||||
- syms.dictsetmaker,
|
||||
- }:
|
||||
+ elif NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
+ if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ if prevp.parent and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
|
||||
|
||||
@@ -184,12 +179,9 @@ def g():
|
||||
+ SPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
+ DOUBLESPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
- t = leaf.type
|
||||
- p = leaf.parent
|
||||
- v = leaf.value
|
||||
+ t = leaf.NOT_IMPLEMENTED_attr
|
||||
+ p = leaf.NOT_IMPLEMENTED_attr
|
||||
+ v = leaf.NOT_IMPLEMENTED_attr
|
||||
t = leaf.type
|
||||
p = leaf.parent
|
||||
@@ -61,29 +48,23 @@
|
||||
|
||||
# Comment because comments
|
||||
|
||||
@@ -204,15 +196,13 @@ def g():
|
||||
- assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
|
||||
+ NOT_YET_IMPLEMENTED_StmtAssert
|
||||
|
||||
- prev = leaf.prev_sibling
|
||||
- if not prev:
|
||||
prev = leaf.prev_sibling
|
||||
if not prev:
|
||||
- prevp = preceding_leaf(p)
|
||||
+ prev = leaf.NOT_IMPLEMENTED_attr
|
||||
+ if NOT_YET_IMPLEMENTED_ExprUnaryOp:
|
||||
+ prevp = NOT_IMPLEMENTED_call()
|
||||
|
||||
- if not prevp or prevp.type in OPENING_BRACKETS:
|
||||
+ if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ if not prevp or NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
# Start of the line or a bracketed expression.
|
||||
# More than one line for the comment.
|
||||
return NO
|
||||
@@ -226,7 +216,7 @@ def g():
|
||||
- syms.argument,
|
||||
- }:
|
||||
+ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
+ if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ if prevp.parent and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
```
|
||||
|
||||
@@ -242,9 +232,9 @@ def f():
|
||||
SPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
DOUBLESPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
t = leaf.NOT_IMPLEMENTED_attr
|
||||
p = leaf.NOT_IMPLEMENTED_attr # trailing comment
|
||||
v = leaf.NOT_IMPLEMENTED_attr
|
||||
t = leaf.type
|
||||
p = leaf.parent # trailing comment
|
||||
v = leaf.value
|
||||
|
||||
if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
pass
|
||||
@@ -253,18 +243,18 @@ def f():
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtAssert
|
||||
|
||||
prev = leaf.NOT_IMPLEMENTED_attr
|
||||
if NOT_YET_IMPLEMENTED_ExprUnaryOp:
|
||||
prev = leaf.prev_sibling
|
||||
if not prev:
|
||||
prevp = NOT_IMPLEMENTED_call()
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if not prevp or NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
|
||||
if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if prevp.parent and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
|
||||
elif NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if prevp.parent and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
|
||||
|
||||
@@ -277,9 +267,9 @@ def g():
|
||||
SPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
DOUBLESPACE = "NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
t = leaf.NOT_IMPLEMENTED_attr
|
||||
p = leaf.NOT_IMPLEMENTED_attr
|
||||
v = leaf.NOT_IMPLEMENTED_attr
|
||||
t = leaf.type
|
||||
p = leaf.parent
|
||||
v = leaf.value
|
||||
|
||||
# Comment because comments
|
||||
|
||||
@@ -291,17 +281,17 @@ def g():
|
||||
# Another comment because more comments
|
||||
NOT_YET_IMPLEMENTED_StmtAssert
|
||||
|
||||
prev = leaf.NOT_IMPLEMENTED_attr
|
||||
if NOT_YET_IMPLEMENTED_ExprUnaryOp:
|
||||
prev = leaf.prev_sibling
|
||||
if not prev:
|
||||
prevp = NOT_IMPLEMENTED_call()
|
||||
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if not prevp or NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
# Start of the line or a bracketed expression.
|
||||
# More than one line for the comment.
|
||||
return NO
|
||||
|
||||
if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if prevp.parent and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
return NO
|
||||
```
|
||||
|
||||
|
||||
@@ -276,45 +276,10 @@ last_call()
|
||||
Name
|
||||
None
|
||||
True
|
||||
@@ -7,226 +8,225 @@
|
||||
1
|
||||
1.0
|
||||
1j
|
||||
-True or False
|
||||
-True or False or None
|
||||
-True and False
|
||||
-True and False and None
|
||||
-(Name1 and Name2) or Name3
|
||||
-Name1 and Name2 or Name3
|
||||
-Name1 or (Name2 and Name3)
|
||||
-Name1 or Name2 and Name3
|
||||
-(Name1 and Name2) or (Name3 and Name4)
|
||||
-Name1 and Name2 or Name3 and Name4
|
||||
-Name1 or (Name2 and Name3) or Name4
|
||||
-Name1 or Name2 and Name3 or Name4
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
v1 << 2
|
||||
1 >> v2
|
||||
1 % finished
|
||||
1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
|
||||
((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
|
||||
-not great
|
||||
-~great
|
||||
-+value
|
||||
--1
|
||||
-~int and not v1 ^ 123 + v2 | True
|
||||
-(~int) and (not ((v1 ^ (123 + v2)) | True))
|
||||
@@ -30,98 +31,90 @@
|
||||
-1
|
||||
~int and not v1 ^ 123 + v2 | True
|
||||
(~int) and (not ((v1 ^ (123 + v2)) | True))
|
||||
-+(really ** -(confusing ** ~(operator**-precedence)))
|
||||
-flags & ~select.EPOLLIN and waiters.write_task is not None
|
||||
-lambda arg: None
|
||||
@@ -339,14 +304,8 @@ last_call()
|
||||
-)
|
||||
-{"2.7": dead, "3.7": (long_live or die_hard)}
|
||||
-{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}}
|
||||
+NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
++really ** -confusing ** ~operator**-precedence
|
||||
+flags & ~select.EPOLLIN and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+lambda x: True
|
||||
+lambda x: True
|
||||
+lambda x: True
|
||||
@@ -362,15 +321,11 @@ last_call()
|
||||
+(NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false)
|
||||
+{
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": (
|
||||
+ NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+ ),
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": (long_live or die_hard),
|
||||
+}
|
||||
+{
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": (
|
||||
+ NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+ ),
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": (long_live or die_hard),
|
||||
+ **{"NOT_YET_IMPLEMENTED_STRING": verygood},
|
||||
+}
|
||||
{**a, **b, **c}
|
||||
@@ -384,43 +339,42 @@ last_call()
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ (NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false),
|
||||
+}
|
||||
+NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
+(
|
||||
+ {"NOT_YET_IMPLEMENTED_STRING": "NOT_YET_IMPLEMENTED_STRING"},
|
||||
+ (True or False),
|
||||
+ (+value),
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ b"NOT_YET_IMPLEMENTED_BYTE_STRING",
|
||||
+) or None
|
||||
()
|
||||
(1,)
|
||||
(1, 2)
|
||||
(1, 2, 3)
|
||||
[]
|
||||
-[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
||||
-[
|
||||
- 1,
|
||||
- 2,
|
||||
- 3,
|
||||
-]
|
||||
-[*a]
|
||||
-[*range(10)]
|
||||
-[
|
||||
- *a,
|
||||
4,
|
||||
5,
|
||||
-]
|
||||
-[
|
||||
- 4,
|
||||
- *a,
|
||||
- 5,
|
||||
+ 6,
|
||||
+ 7,
|
||||
+ 8,
|
||||
+ 9,
|
||||
+ (NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2),
|
||||
+ (NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2),
|
||||
+ (NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2),
|
||||
]
|
||||
-]
|
||||
+[1, 2, 3]
|
||||
+[NOT_YET_IMPLEMENTED_ExprStarred]
|
||||
+[NOT_YET_IMPLEMENTED_ExprStarred]
|
||||
+[NOT_YET_IMPLEMENTED_ExprStarred, 4, 5]
|
||||
+[4, NOT_YET_IMPLEMENTED_ExprStarred, 5]
|
||||
[
|
||||
- 4,
|
||||
- *a,
|
||||
- 5,
|
||||
-]
|
||||
-[
|
||||
this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||
element,
|
||||
another,
|
||||
@@ -439,6 +393,28 @@ last_call()
|
||||
-{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))}
|
||||
-{a: b * 2 for a, b in dictionary.items()}
|
||||
-{a: b * -2 for a, b in dictionary.items()}
|
||||
-{
|
||||
- k: v
|
||||
- for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension
|
||||
-}
|
||||
-Python3 > Python2 > COBOL
|
||||
-Life is Life
|
||||
-call()
|
||||
-call(arg)
|
||||
-call(kwarg="hey")
|
||||
-call(arg, kwarg="hey")
|
||||
-call(arg, another, kwarg="hey", **kwargs)
|
||||
-call(
|
||||
- this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||
- arg,
|
||||
- another,
|
||||
- kwarg="hey",
|
||||
- **kwargs,
|
||||
-) # note: no trailing comma pre-3.6
|
||||
-call(*gidgets[:2])
|
||||
-call(a, *gidgets[:2])
|
||||
-call(**self.screen_kwargs)
|
||||
-call(b, **self.screen_kwargs)
|
||||
+NOT_YET_IMPLEMENTED_ExprSetComp
|
||||
+NOT_YET_IMPLEMENTED_ExprSetComp
|
||||
+NOT_YET_IMPLEMENTED_ExprSetComp
|
||||
@@ -464,81 +440,16 @@ last_call()
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+lukasz.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+1 .NOT_IMPLEMENTED_attr
|
||||
+1.0 .NOT_IMPLEMENTED_attr
|
||||
+....NOT_IMPLEMENTED_attr
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign # type: ignore
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false
|
||||
{
|
||||
- k: v
|
||||
- for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
}
|
||||
-Python3 > Python2 > COBOL
|
||||
-Life is Life
|
||||
-call()
|
||||
-call(arg)
|
||||
-call(kwarg="hey")
|
||||
-call(arg, kwarg="hey")
|
||||
-call(arg, another, kwarg="hey", **kwargs)
|
||||
-call(
|
||||
- this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||
- arg,
|
||||
- another,
|
||||
- kwarg="hey",
|
||||
- **kwargs,
|
||||
-) # note: no trailing comma pre-3.6
|
||||
-call(*gidgets[:2])
|
||||
-call(a, *gidgets[:2])
|
||||
-call(**self.screen_kwargs)
|
||||
-call(b, **self.screen_kwargs)
|
||||
-lukasz.langa.pl
|
||||
lukasz.langa.pl
|
||||
-call.me(maybe)
|
||||
-(1).real
|
||||
-(1.0).real
|
||||
-....__class__
|
||||
-list[str]
|
||||
-dict[str, int]
|
||||
-tuple[str, ...]
|
||||
-tuple[str, int, float, dict[str, int]]
|
||||
-tuple[
|
||||
+NOT_IMPLEMENTED_call()
|
||||
(1).real
|
||||
(1.0).real
|
||||
....__class__
|
||||
@@ -130,34 +123,28 @@
|
||||
tuple[str, ...]
|
||||
tuple[str, int, float, dict[str, int]]
|
||||
tuple[
|
||||
- str,
|
||||
- int,
|
||||
- float,
|
||||
@@ -546,27 +457,12 @@ last_call()
|
||||
-]
|
||||
-very_long_variable_name_filters: t.List[
|
||||
- t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
||||
+{
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
+}
|
||||
+[
|
||||
+ 1,
|
||||
+ 2,
|
||||
+ 3,
|
||||
+ 4,
|
||||
+ 5,
|
||||
+ 6,
|
||||
+ 7,
|
||||
+ 8,
|
||||
+ 9,
|
||||
+ NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
+ NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
+ NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
+ (
|
||||
+ str,
|
||||
+ int,
|
||||
+ float,
|
||||
+ dict[str, int],
|
||||
+ )
|
||||
]
|
||||
-xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
|
||||
- sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
||||
@@ -577,36 +473,51 @@ last_call()
|
||||
-xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod(
|
||||
- sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
||||
-) # type: ignore
|
||||
-slice[0]
|
||||
-slice[0:1]
|
||||
-slice[0:1:2]
|
||||
-slice[:]
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign # type: ignore
|
||||
slice[0]
|
||||
slice[0:1]
|
||||
slice[0:1:2]
|
||||
slice[:]
|
||||
-slice[:-1]
|
||||
-slice[1:]
|
||||
+slice[ : -1]
|
||||
slice[1:]
|
||||
-slice[::-1]
|
||||
-slice[d :: d + 1]
|
||||
-slice[:c, c - 1]
|
||||
-numpy[:, 0:1]
|
||||
+slice[ :: -1]
|
||||
slice[d :: d + 1]
|
||||
slice[:c, c - 1]
|
||||
numpy[:, 0:1]
|
||||
-numpy[:, :-1]
|
||||
-numpy[0, :]
|
||||
-numpy[:, i]
|
||||
-numpy[0, :2]
|
||||
-numpy[:N, 0]
|
||||
-numpy[:2, :4]
|
||||
-numpy[2:4, 1:5]
|
||||
-numpy[4:, 2:]
|
||||
-numpy[:, (0, 1, 2, 5)]
|
||||
-numpy[0, [0]]
|
||||
-numpy[:, [i]]
|
||||
-numpy[1 : c + 1, c]
|
||||
-numpy[-(c + 1) :, d]
|
||||
-numpy[:, l[-2]]
|
||||
+numpy[:, : -1]
|
||||
numpy[0, :]
|
||||
numpy[:, i]
|
||||
numpy[0, :2]
|
||||
@@ -171,62 +158,58 @@
|
||||
numpy[1 : c + 1, c]
|
||||
numpy[-(c + 1) :, d]
|
||||
numpy[:, l[-2]]
|
||||
-numpy[:, ::-1]
|
||||
-numpy[np.newaxis, :]
|
||||
+numpy[:, :: -1]
|
||||
numpy[np.newaxis, :]
|
||||
-(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)
|
||||
-{"2.7": dead, "3.7": long_live or die_hard}
|
||||
-{"2.7", "3.6", "3.7", "3.8", "3.9", "4.0" if gilectomy else "3.10"}
|
||||
-[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||
+NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false
|
||||
+{
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": long_live or die_hard,
|
||||
+}
|
||||
+{
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
+}
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||
(SomeName)
|
||||
SomeName
|
||||
(Good, Bad, Ugly)
|
||||
@@ -644,7 +555,13 @@ last_call()
|
||||
-g = 1, *"ten"
|
||||
-what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(
|
||||
- vars_to_remove
|
||||
-)
|
||||
+e = NOT_IMPLEMENTED_call()
|
||||
+f = 1, NOT_YET_IMPLEMENTED_ExprStarred
|
||||
+g = 1, NOT_YET_IMPLEMENTED_ExprStarred
|
||||
+what_is_up_with_those_new_coord_names = (
|
||||
+ (coord_names + NOT_IMPLEMENTED_call())
|
||||
+ + NOT_IMPLEMENTED_call()
|
||||
)
|
||||
-what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set(
|
||||
- vars_to_remove
|
||||
-)
|
||||
@@ -655,13 +572,7 @@ last_call()
|
||||
- )
|
||||
- .order_by(models.Customer.id.asc())
|
||||
- .all()
|
||||
+e = NOT_IMPLEMENTED_call()
|
||||
+f = 1, NOT_YET_IMPLEMENTED_ExprStarred
|
||||
+g = 1, NOT_YET_IMPLEMENTED_ExprStarred
|
||||
+what_is_up_with_those_new_coord_names = (
|
||||
+ (coord_names + NOT_IMPLEMENTED_call())
|
||||
+ + NOT_IMPLEMENTED_call()
|
||||
)
|
||||
-)
|
||||
-result = (
|
||||
- session.query(models.Customer.id)
|
||||
- .filter(
|
||||
@@ -684,7 +595,7 @@ last_call()
|
||||
mapping = {
|
||||
A: 0.25 * (10.0 / 12),
|
||||
B: 0.1 * (10.0 / 12),
|
||||
@@ -236,65 +236,35 @@
|
||||
@@ -236,31 +219,29 @@
|
||||
|
||||
|
||||
def gen():
|
||||
@@ -711,38 +622,31 @@ last_call()
|
||||
- force=False
|
||||
-), "Short message"
|
||||
-assert parens is TooMany
|
||||
-for (x,) in (1,), (2,), (3,):
|
||||
- ...
|
||||
-for y in ():
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+NOT_IMPLEMENTED_call()
|
||||
+NOT_YET_IMPLEMENTED_StmtAssert
|
||||
+NOT_YET_IMPLEMENTED_StmtAssert
|
||||
+NOT_YET_IMPLEMENTED_StmtAssert
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+while NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
for (x,) in (1,), (2,), (3,):
|
||||
...
|
||||
for y in ():
|
||||
...
|
||||
-for z in (i for i in (1, 2, 3)):
|
||||
- ...
|
||||
+for z in (i for i in []):
|
||||
...
|
||||
-for i in call():
|
||||
- ...
|
||||
-for j in 1 + (2 + 3):
|
||||
- ...
|
||||
-while this and that:
|
||||
- ...
|
||||
-for (
|
||||
- addr_family,
|
||||
- addr_type,
|
||||
- addr_proto,
|
||||
- addr_canonname,
|
||||
- addr_sockaddr,
|
||||
+for i in NOT_IMPLEMENTED_call():
|
||||
...
|
||||
for j in 1 + (2 + 3):
|
||||
...
|
||||
@@ -272,28 +253,16 @@
|
||||
addr_proto,
|
||||
addr_canonname,
|
||||
addr_sockaddr,
|
||||
-) in socket.getaddrinfo("google.com", "http"):
|
||||
- pass
|
||||
+) in NOT_IMPLEMENTED_call():
|
||||
pass
|
||||
-a = (
|
||||
- aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp
|
||||
- in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
|
||||
@@ -759,84 +663,54 @@ last_call()
|
||||
- aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp
|
||||
- is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
|
||||
-)
|
||||
-if (
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
if (
|
||||
- threading.current_thread() != threading.main_thread()
|
||||
- and threading.current_thread() != threading.main_thread()
|
||||
- or signal.getsignal(signal.SIGINT) != signal.default_int_handler
|
||||
-):
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+ and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+ or NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
):
|
||||
return True
|
||||
if (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
@@ -327,24 +297,44 @@
|
||||
@@ -327,13 +296,18 @@
|
||||
):
|
||||
return True
|
||||
if (
|
||||
- ~aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e
|
||||
- | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n
|
||||
+ NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+ + aaaa.NOT_IMPLEMENTED_attr
|
||||
+ - aaaa.NOT_IMPLEMENTED_attr * aaaa.NOT_IMPLEMENTED_attr / aaaa.NOT_IMPLEMENTED_attr
|
||||
+ | aaaa.NOT_IMPLEMENTED_attr
|
||||
+ & aaaa.NOT_IMPLEMENTED_attr % aaaa.NOT_IMPLEMENTED_attr
|
||||
+ ^ aaaa.NOT_IMPLEMENTED_attr
|
||||
+ << aaaa.NOT_IMPLEMENTED_attr
|
||||
+ >> aaaa.NOT_IMPLEMENTED_attr**aaaa.NOT_IMPLEMENTED_attr // aaaa.NOT_IMPLEMENTED_attr
|
||||
+ ~aaaa.a
|
||||
+ + aaaa.b
|
||||
+ - aaaa.c * aaaa.d / aaaa.e
|
||||
| aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n
|
||||
):
|
||||
return True
|
||||
if (
|
||||
- ~aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e
|
||||
- | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h
|
||||
- ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n
|
||||
+ NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+ + aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ - aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ @ aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ / aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ | aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ & aaaaaaaa.NOT_IMPLEMENTED_attr % aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ ^ aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ << aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ >> aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ **aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ // aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ ~aaaaaaaa.a
|
||||
+ + aaaaaaaa.b
|
||||
+ - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e
|
||||
+ | aaaaaaaa.f
|
||||
+ & aaaaaaaa.g % aaaaaaaa.h
|
||||
^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n
|
||||
):
|
||||
return True
|
||||
if (
|
||||
- ~aaaaaaaaaaaaaaaa.a
|
||||
- + aaaaaaaaaaaaaaaa.b
|
||||
- - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e
|
||||
@@ -341,7 +315,8 @@
|
||||
~aaaaaaaaaaaaaaaa.a
|
||||
+ aaaaaaaaaaaaaaaa.b
|
||||
- aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e
|
||||
- | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h
|
||||
- ^ aaaaaaaaaaaaaaaa.i
|
||||
- << aaaaaaaaaaaaaaaa.k
|
||||
- >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||
+ NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+ + aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ - aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ * aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ @ aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ | aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ & aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr % aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ ^ aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ << aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ >> aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ **aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
+ // aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
):
|
||||
return True
|
||||
(
|
||||
@@ -363,8 +353,9 @@
|
||||
bbbb >> bbbb * bbbb
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
- ^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ ^ bbbb.NOT_IMPLEMENTED_attr
|
||||
+ & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ | aaaaaaaaaaaaaaaa.f
|
||||
+ & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h
|
||||
^ aaaaaaaaaaaaaaaa.i
|
||||
<< aaaaaaaaaaaaaaaa.k
|
||||
>> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||
@@ -366,5 +341,5 @@
|
||||
^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
^ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
)
|
||||
-last_call()
|
||||
@@ -857,31 +731,31 @@ False
|
||||
1
|
||||
1.0
|
||||
1j
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
True or False
|
||||
True or False or None
|
||||
True and False
|
||||
True and False and None
|
||||
(Name1 and Name2) or Name3
|
||||
Name1 and Name2 or Name3
|
||||
Name1 or (Name2 and Name3)
|
||||
Name1 or Name2 and Name3
|
||||
(Name1 and Name2) or (Name3 and Name4)
|
||||
Name1 and Name2 or Name3 and Name4
|
||||
Name1 or (Name2 and Name3) or Name4
|
||||
Name1 or Name2 and Name3 or Name4
|
||||
v1 << 2
|
||||
1 >> v2
|
||||
1 % finished
|
||||
1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
|
||||
((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
not great
|
||||
~great
|
||||
+value
|
||||
-1
|
||||
~int and not v1 ^ 123 + v2 | True
|
||||
(~int) and (not ((v1 ^ (123 + v2)) | True))
|
||||
+really ** -confusing ** ~operator**-precedence
|
||||
flags & ~select.EPOLLIN and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
lambda x: True
|
||||
lambda x: True
|
||||
lambda x: True
|
||||
@@ -897,15 +771,11 @@ NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false
|
||||
(NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false)
|
||||
{
|
||||
"NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
"NOT_YET_IMPLEMENTED_STRING": (
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
),
|
||||
"NOT_YET_IMPLEMENTED_STRING": (long_live or die_hard),
|
||||
}
|
||||
{
|
||||
"NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
"NOT_YET_IMPLEMENTED_STRING": (
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
),
|
||||
"NOT_YET_IMPLEMENTED_STRING": (long_live or die_hard),
|
||||
**{"NOT_YET_IMPLEMENTED_STRING": verygood},
|
||||
}
|
||||
{**a, **b, **c}
|
||||
@@ -917,26 +787,19 @@ NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false
|
||||
"NOT_YET_IMPLEMENTED_STRING",
|
||||
(NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false),
|
||||
}
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2
|
||||
(
|
||||
{"NOT_YET_IMPLEMENTED_STRING": "NOT_YET_IMPLEMENTED_STRING"},
|
||||
(True or False),
|
||||
(+value),
|
||||
"NOT_YET_IMPLEMENTED_STRING",
|
||||
b"NOT_YET_IMPLEMENTED_BYTE_STRING",
|
||||
) or None
|
||||
()
|
||||
(1,)
|
||||
(1, 2)
|
||||
(1, 2, 3)
|
||||
[]
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
(NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2),
|
||||
(NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2),
|
||||
(NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2),
|
||||
]
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
||||
[1, 2, 3]
|
||||
[NOT_YET_IMPLEMENTED_ExprStarred]
|
||||
[NOT_YET_IMPLEMENTED_ExprStarred]
|
||||
@@ -973,50 +836,57 @@ NOT_IMPLEMENTED_call()
|
||||
NOT_IMPLEMENTED_call()
|
||||
NOT_IMPLEMENTED_call()
|
||||
NOT_IMPLEMENTED_call()
|
||||
lukasz.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr
|
||||
lukasz.langa.pl
|
||||
NOT_IMPLEMENTED_call()
|
||||
1 .NOT_IMPLEMENTED_attr
|
||||
1.0 .NOT_IMPLEMENTED_attr
|
||||
....NOT_IMPLEMENTED_attr
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
(1).real
|
||||
(1.0).real
|
||||
....__class__
|
||||
list[str]
|
||||
dict[str, int]
|
||||
tuple[str, ...]
|
||||
tuple[str, int, float, dict[str, int]]
|
||||
tuple[
|
||||
(
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
dict[str, int],
|
||||
)
|
||||
]
|
||||
NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
NOT_YET_IMPLEMENTED_StmtAnnAssign # type: ignore
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
slice[0]
|
||||
slice[0:1]
|
||||
slice[0:1:2]
|
||||
slice[:]
|
||||
slice[ : -1]
|
||||
slice[1:]
|
||||
slice[ :: -1]
|
||||
slice[d :: d + 1]
|
||||
slice[:c, c - 1]
|
||||
numpy[:, 0:1]
|
||||
numpy[:, : -1]
|
||||
numpy[0, :]
|
||||
numpy[:, i]
|
||||
numpy[0, :2]
|
||||
numpy[:N, 0]
|
||||
numpy[:2, :4]
|
||||
numpy[2:4, 1:5]
|
||||
numpy[4:, 2:]
|
||||
numpy[:, (0, 1, 2, 5)]
|
||||
numpy[0, [0]]
|
||||
numpy[:, [i]]
|
||||
numpy[1 : c + 1, c]
|
||||
numpy[-(c + 1) :, d]
|
||||
numpy[:, l[-2]]
|
||||
numpy[:, :: -1]
|
||||
numpy[np.newaxis, :]
|
||||
NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false
|
||||
{
|
||||
"NOT_YET_IMPLEMENTED_STRING": dead,
|
||||
"NOT_YET_IMPLEMENTED_STRING": NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
"NOT_YET_IMPLEMENTED_STRING": long_live or die_hard,
|
||||
}
|
||||
{
|
||||
"NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -1026,20 +896,7 @@ NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false
|
||||
"NOT_YET_IMPLEMENTED_STRING",
|
||||
NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
}
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2,
|
||||
]
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||
(SomeName)
|
||||
SomeName
|
||||
(Good, Bad, Ugly)
|
||||
@@ -1101,19 +958,35 @@ NOT_IMPLEMENTED_call()
|
||||
NOT_YET_IMPLEMENTED_StmtAssert
|
||||
NOT_YET_IMPLEMENTED_StmtAssert
|
||||
NOT_YET_IMPLEMENTED_StmtAssert
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
while NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
for (x,) in (1,), (2,), (3,):
|
||||
...
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for y in ():
|
||||
...
|
||||
for z in (i for i in []):
|
||||
...
|
||||
for i in NOT_IMPLEMENTED_call():
|
||||
...
|
||||
for j in 1 + (2 + 3):
|
||||
...
|
||||
while this and that:
|
||||
...
|
||||
for (
|
||||
addr_family,
|
||||
addr_type,
|
||||
addr_proto,
|
||||
addr_canonname,
|
||||
addr_sockaddr,
|
||||
) in NOT_IMPLEMENTED_call():
|
||||
pass
|
||||
a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if (
|
||||
NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
or NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
):
|
||||
return True
|
||||
if (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
@@ -1146,44 +1019,30 @@ if (
|
||||
):
|
||||
return True
|
||||
if (
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+ aaaa.NOT_IMPLEMENTED_attr
|
||||
- aaaa.NOT_IMPLEMENTED_attr * aaaa.NOT_IMPLEMENTED_attr / aaaa.NOT_IMPLEMENTED_attr
|
||||
| aaaa.NOT_IMPLEMENTED_attr
|
||||
& aaaa.NOT_IMPLEMENTED_attr % aaaa.NOT_IMPLEMENTED_attr
|
||||
^ aaaa.NOT_IMPLEMENTED_attr
|
||||
<< aaaa.NOT_IMPLEMENTED_attr
|
||||
>> aaaa.NOT_IMPLEMENTED_attr**aaaa.NOT_IMPLEMENTED_attr // aaaa.NOT_IMPLEMENTED_attr
|
||||
~aaaa.a
|
||||
+ aaaa.b
|
||||
- aaaa.c * aaaa.d / aaaa.e
|
||||
| aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n
|
||||
):
|
||||
return True
|
||||
if (
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+ aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
- aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
@ aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
/ aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
| aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
& aaaaaaaa.NOT_IMPLEMENTED_attr % aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
^ aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
<< aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
>> aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
**aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
// aaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
~aaaaaaaa.a
|
||||
+ aaaaaaaa.b
|
||||
- aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e
|
||||
| aaaaaaaa.f
|
||||
& aaaaaaaa.g % aaaaaaaa.h
|
||||
^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n
|
||||
):
|
||||
return True
|
||||
if (
|
||||
NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+ aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
- aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
* aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
@ aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
| aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
& aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr % aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
^ aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
<< aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
>> aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
**aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
// aaaaaaaaaaaaaaaa.NOT_IMPLEMENTED_attr
|
||||
~aaaaaaaaaaaaaaaa.a
|
||||
+ aaaaaaaaaaaaaaaa.b
|
||||
- aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e
|
||||
| aaaaaaaaaaaaaaaa.f
|
||||
& aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h
|
||||
^ aaaaaaaaaaaaaaaa.i
|
||||
<< aaaaaaaaaaaaaaaa.k
|
||||
>> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||
):
|
||||
return True
|
||||
(
|
||||
@@ -1202,8 +1061,7 @@ aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
||||
bbbb >> bbbb * bbbb
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
^ bbbb.NOT_IMPLEMENTED_attr
|
||||
& aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
^ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
)
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
@@ -134,7 +134,7 @@ elif unformatted:
|
||||
return True
|
||||
# yapf: enable
|
||||
elif b:
|
||||
@@ -39,49 +21,27 @@
|
||||
@@ -39,49 +21,29 @@
|
||||
# Regression test for https://github.com/psf/black/issues/2567.
|
||||
if True:
|
||||
# fmt: off
|
||||
@@ -142,7 +142,9 @@ elif unformatted:
|
||||
- # fmt: on
|
||||
- print ( "This won't be formatted" )
|
||||
- print ( "This won't be formatted either" )
|
||||
+ NOT_YET_IMPLEMENTED_StmtFor
|
||||
+ for _ in NOT_IMPLEMENTED_call():
|
||||
+ # fmt: on
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
else:
|
||||
- print("This will be formatted")
|
||||
@@ -220,7 +222,9 @@ def test_func():
|
||||
# Regression test for https://github.com/psf/black/issues/2567.
|
||||
if True:
|
||||
# fmt: off
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for _ in NOT_IMPLEMENTED_call():
|
||||
# fmt: on
|
||||
NOT_IMPLEMENTED_call()
|
||||
NOT_IMPLEMENTED_call()
|
||||
else:
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
@@ -222,7 +222,7 @@ d={'a':1,
|
||||
# Comment 1
|
||||
|
||||
# Comment 2
|
||||
@@ -18,30 +16,51 @@
|
||||
@@ -18,30 +16,53 @@
|
||||
|
||||
# fmt: off
|
||||
def func_no_args():
|
||||
@@ -241,7 +241,9 @@ d={'a':1,
|
||||
+ NOT_YET_IMPLEMENTED_StmtRaise
|
||||
+ if False:
|
||||
+ ...
|
||||
+ NOT_YET_IMPLEMENTED_StmtFor
|
||||
+ for i in NOT_IMPLEMENTED_call():
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ continue
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ return None
|
||||
+
|
||||
@@ -251,19 +253,18 @@ d={'a':1,
|
||||
- async with some_connection() as conn:
|
||||
- await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2)
|
||||
- await asyncio.sleep(1)
|
||||
-@asyncio.coroutine
|
||||
+ "NOT_YET_IMPLEMENTED_STRING"
|
||||
+ NOT_YET_IMPLEMENTED_StmtAsyncWith
|
||||
+ await NOT_IMPLEMENTED_call()
|
||||
+
|
||||
+
|
||||
@asyncio.coroutine
|
||||
-@some_decorator(
|
||||
-with_args=True,
|
||||
-many_args=[1,2,3]
|
||||
-)
|
||||
-def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str:
|
||||
- return text[number:-1]
|
||||
+ "NOT_YET_IMPLEMENTED_STRING"
|
||||
+ NOT_YET_IMPLEMENTED_StmtAsyncWith
|
||||
+ await NOT_IMPLEMENTED_call()
|
||||
+
|
||||
+
|
||||
+@asyncio.NOT_IMPLEMENTED_attr
|
||||
+@NOT_IMPLEMENTED_call()
|
||||
+def function_signature_stress_test(
|
||||
+ number: int,
|
||||
@@ -273,7 +274,7 @@ d={'a':1,
|
||||
+ debug: bool = False,
|
||||
+ **kwargs,
|
||||
+) -> str:
|
||||
+ return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+ return text[number : -1]
|
||||
+
|
||||
+
|
||||
# fmt: on
|
||||
@@ -286,7 +287,7 @@ d={'a':1,
|
||||
+ c=[],
|
||||
+ d={},
|
||||
+ e=True,
|
||||
+ f=NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
+ f=-1,
|
||||
+ g=NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
+ h="NOT_YET_IMPLEMENTED_STRING",
|
||||
+ i="NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -296,15 +297,13 @@ d={'a':1,
|
||||
|
||||
|
||||
def spaces_types(
|
||||
@@ -50,77 +69,62 @@
|
||||
c: list = [],
|
||||
@@ -51,76 +72,71 @@
|
||||
d: dict = {},
|
||||
e: bool = True,
|
||||
- f: int = -1,
|
||||
f: int = -1,
|
||||
- g: int = 1 if False else 2,
|
||||
- h: str = "",
|
||||
- i: str = r"",
|
||||
+ f: int = NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
+ g: int = NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
+ h: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ i: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -325,15 +324,22 @@ d={'a':1,
|
||||
|
||||
|
||||
def subscriptlist():
|
||||
- atom[
|
||||
- # fmt: off
|
||||
atom[
|
||||
# fmt: off
|
||||
- 'some big and',
|
||||
- 'complex subscript',
|
||||
- # fmt: on
|
||||
- goes + here,
|
||||
- andhere,
|
||||
- ]
|
||||
+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+ (
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ # fmt: on
|
||||
+ goes
|
||||
+ + here,
|
||||
+ andhere,
|
||||
+ )
|
||||
]
|
||||
|
||||
|
||||
def import_as_names():
|
||||
@@ -392,7 +398,7 @@ d={'a':1,
|
||||
# fmt: off
|
||||
|
||||
# hey, that won't work
|
||||
@@ -130,13 +134,15 @@
|
||||
@@ -130,13 +146,13 @@
|
||||
|
||||
|
||||
def on_and_off_broken():
|
||||
@@ -407,13 +413,11 @@ d={'a':1,
|
||||
+ this = NOT_IMPLEMENTED_call()
|
||||
+ and_ = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ (
|
||||
+ now.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr
|
||||
+ )
|
||||
+ now.considers.multiple.fmt.directives.within.one.prefix
|
||||
# fmt: on
|
||||
# fmt: off
|
||||
# ...but comments still get reformatted even though they should not be
|
||||
@@ -145,80 +151,21 @@
|
||||
@@ -145,80 +161,21 @@
|
||||
|
||||
def long_lines():
|
||||
if True:
|
||||
@@ -532,7 +536,9 @@ def func_no_args():
|
||||
NOT_YET_IMPLEMENTED_StmtRaise
|
||||
if False:
|
||||
...
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for i in NOT_IMPLEMENTED_call():
|
||||
NOT_IMPLEMENTED_call()
|
||||
continue
|
||||
NOT_IMPLEMENTED_call()
|
||||
return None
|
||||
|
||||
@@ -543,7 +549,7 @@ async def coroutine(arg, exec=False):
|
||||
await NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
@asyncio.NOT_IMPLEMENTED_attr
|
||||
@asyncio.coroutine
|
||||
@NOT_IMPLEMENTED_call()
|
||||
def function_signature_stress_test(
|
||||
number: int,
|
||||
@@ -553,7 +559,7 @@ def function_signature_stress_test(
|
||||
debug: bool = False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
return text[number : -1]
|
||||
|
||||
|
||||
# fmt: on
|
||||
@@ -563,7 +569,7 @@ def spaces(
|
||||
c=[],
|
||||
d={},
|
||||
e=True,
|
||||
f=NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
f=-1,
|
||||
g=NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
h="NOT_YET_IMPLEMENTED_STRING",
|
||||
i="NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -578,7 +584,7 @@ def spaces_types(
|
||||
c: list = [],
|
||||
d: dict = {},
|
||||
e: bool = True,
|
||||
f: int = NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
f: int = -1,
|
||||
g: int = NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
h: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
i: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -597,7 +603,17 @@ something = {
|
||||
|
||||
|
||||
def subscriptlist():
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
atom[
|
||||
# fmt: off
|
||||
(
|
||||
"NOT_YET_IMPLEMENTED_STRING",
|
||||
"NOT_YET_IMPLEMENTED_STRING",
|
||||
# fmt: on
|
||||
goes
|
||||
+ here,
|
||||
andhere,
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def import_as_names():
|
||||
@@ -649,9 +665,7 @@ def on_and_off_broken():
|
||||
this = NOT_IMPLEMENTED_call()
|
||||
and_ = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
NOT_IMPLEMENTED_call()
|
||||
(
|
||||
now.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr
|
||||
)
|
||||
now.considers.multiple.fmt.directives.within.one.prefix
|
||||
# fmt: on
|
||||
# fmt: off
|
||||
# ...but comments still get reformatted even though they should not be
|
||||
|
||||
@@ -22,15 +22,17 @@ else:
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,9 +1,5 @@
|
||||
@@ -1,9 +1,9 @@
|
||||
a, b, c = 3, 4, 5
|
||||
-if (
|
||||
if (
|
||||
- a == 3
|
||||
- and b != 9 # fmt: skip
|
||||
- and c is not None
|
||||
-):
|
||||
+ NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
+ and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right # fmt: skip
|
||||
+ and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
):
|
||||
- print("I'm good!")
|
||||
+if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
else:
|
||||
- print("I'm bad")
|
||||
@@ -41,7 +43,11 @@ else:
|
||||
|
||||
```py
|
||||
a, b, c = 3, 4, 5
|
||||
if NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2:
|
||||
if (
|
||||
NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right # fmt: skip
|
||||
and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
):
|
||||
NOT_IMPLEMENTED_call()
|
||||
else:
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
@@ -75,7 +75,7 @@ async def test_async_with():
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,62 +1,46 @@
|
||||
@@ -1,62 +1,47 @@
|
||||
# Make sure a leading comment is not removed.
|
||||
-def some_func( unformatted, args ): # fmt: skip
|
||||
- print("I am some_func")
|
||||
@@ -99,13 +99,13 @@ async def test_async_with():
|
||||
- def some_method( self, unformatted, args ): # fmt: skip
|
||||
- print("I am some_method")
|
||||
- return 0
|
||||
-
|
||||
+NOT_YET_IMPLEMENTED_StmtClassDef
|
||||
|
||||
- async def some_async_method( self, unformatted, args ): # fmt: skip
|
||||
- print("I am some_async_method")
|
||||
- await asyncio.sleep(1)
|
||||
+NOT_YET_IMPLEMENTED_StmtClassDef
|
||||
|
||||
|
||||
-
|
||||
# Make sure a leading comment is not removed.
|
||||
-if unformatted_call( args ): # fmt: skip
|
||||
- print("First branch")
|
||||
@@ -130,7 +130,8 @@ async def test_async_with():
|
||||
|
||||
-for i in some_iter( unformatted, args ): # fmt: skip
|
||||
- print("Do something")
|
||||
+NOT_YET_IMPLEMENTED_StmtFor # fmt: skip
|
||||
+for i in NOT_IMPLEMENTED_call(): # fmt: skip
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
async def test_async_for():
|
||||
@@ -193,7 +194,8 @@ while NOT_IMPLEMENTED_call(): # fmt: skip
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtFor # fmt: skip
|
||||
for i in NOT_IMPLEMENTED_call(): # fmt: skip
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
async def test_async_for():
|
||||
|
||||
@@ -116,17 +116,17 @@ def __await__(): return (yield)
|
||||
+NOT_YET_IMPLEMENTED_StmtImport
|
||||
|
||||
-from third_party import X, Y, Z
|
||||
-
|
||||
-from library import some_connection, some_decorator
|
||||
+NOT_YET_IMPLEMENTED_StmtImportFrom
|
||||
|
||||
-from library import some_connection, some_decorator
|
||||
-
|
||||
-f"trigger 3.6 mode"
|
||||
+NOT_YET_IMPLEMENTED_StmtImportFrom
|
||||
+NOT_YET_IMPLEMENTED_ExprJoinedStr
|
||||
|
||||
|
||||
def func_no_args():
|
||||
@@ -14,39 +13,46 @@
|
||||
@@ -14,39 +13,48 @@
|
||||
b
|
||||
c
|
||||
if True:
|
||||
@@ -136,9 +136,10 @@ def __await__(): return (yield)
|
||||
...
|
||||
- for i in range(10):
|
||||
- print(i)
|
||||
- continue
|
||||
+ for i in NOT_IMPLEMENTED_call():
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
continue
|
||||
- exec("new-style exec", {}, {})
|
||||
+ NOT_YET_IMPLEMENTED_StmtFor
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
return None
|
||||
|
||||
@@ -153,9 +154,8 @@ def __await__(): return (yield)
|
||||
+ await NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
-@asyncio.coroutine
|
||||
@asyncio.coroutine
|
||||
-@some_decorator(with_args=True, many_args=[1, 2, 3])
|
||||
+@asyncio.NOT_IMPLEMENTED_attr
|
||||
+@NOT_IMPLEMENTED_call()
|
||||
def function_signature_stress_test(
|
||||
number: int,
|
||||
@@ -167,7 +167,7 @@ def __await__(): return (yield)
|
||||
**kwargs,
|
||||
) -> str:
|
||||
- return text[number:-1]
|
||||
+ return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+ return text[number : -1]
|
||||
|
||||
|
||||
-def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""):
|
||||
@@ -179,7 +179,7 @@ def __await__(): return (yield)
|
||||
+ c=[],
|
||||
+ d={},
|
||||
+ e=True,
|
||||
+ f=NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
+ f=-1,
|
||||
+ g=NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
+ h="NOT_YET_IMPLEMENTED_STRING",
|
||||
+ i="NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -189,15 +189,13 @@ def __await__(): return (yield)
|
||||
|
||||
|
||||
def spaces_types(
|
||||
@@ -55,71 +61,27 @@
|
||||
c: list = [],
|
||||
@@ -56,70 +64,26 @@
|
||||
d: dict = {},
|
||||
e: bool = True,
|
||||
- f: int = -1,
|
||||
f: int = -1,
|
||||
- g: int = 1 if False else 2,
|
||||
- h: str = "",
|
||||
- i: str = r"",
|
||||
+ f: int = NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
+ g: int = NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
+ h: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
+ i: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -271,7 +269,7 @@ def __await__(): return (yield)
|
||||
|
||||
|
||||
def trailing_comma():
|
||||
@@ -135,14 +97,8 @@
|
||||
@@ -135,14 +99,8 @@
|
||||
a,
|
||||
**kwargs,
|
||||
) -> A:
|
||||
@@ -311,7 +309,9 @@ def func_no_args():
|
||||
NOT_YET_IMPLEMENTED_StmtRaise
|
||||
if False:
|
||||
...
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for i in NOT_IMPLEMENTED_call():
|
||||
NOT_IMPLEMENTED_call()
|
||||
continue
|
||||
NOT_IMPLEMENTED_call()
|
||||
return None
|
||||
|
||||
@@ -322,7 +322,7 @@ async def coroutine(arg, exec=False):
|
||||
await NOT_IMPLEMENTED_call()
|
||||
|
||||
|
||||
@asyncio.NOT_IMPLEMENTED_attr
|
||||
@asyncio.coroutine
|
||||
@NOT_IMPLEMENTED_call()
|
||||
def function_signature_stress_test(
|
||||
number: int,
|
||||
@@ -332,7 +332,7 @@ def function_signature_stress_test(
|
||||
debug: bool = False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
return text[number : -1]
|
||||
|
||||
|
||||
def spaces(
|
||||
@@ -341,7 +341,7 @@ def spaces(
|
||||
c=[],
|
||||
d={},
|
||||
e=True,
|
||||
f=NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
f=-1,
|
||||
g=NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
h="NOT_YET_IMPLEMENTED_STRING",
|
||||
i="NOT_YET_IMPLEMENTED_STRING",
|
||||
@@ -356,7 +356,7 @@ def spaces_types(
|
||||
c: list = [],
|
||||
d: dict = {},
|
||||
e: bool = True,
|
||||
f: int = NOT_YET_IMPLEMENTED_ExprUnaryOp,
|
||||
f: int = -1,
|
||||
g: int = NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false,
|
||||
h: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
i: str = "NOT_YET_IMPLEMENTED_STRING",
|
||||
|
||||
@@ -94,7 +94,7 @@ some_module.some_function(
|
||||
}
|
||||
tup = (
|
||||
1,
|
||||
@@ -24,45 +24,18 @@
|
||||
@@ -24,45 +24,23 @@
|
||||
def f(
|
||||
a: int = 1,
|
||||
):
|
||||
@@ -106,7 +106,9 @@ some_module.some_function(
|
||||
- call2(
|
||||
- arg=[1, 2, 3],
|
||||
- )
|
||||
- x = {
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
x = {
|
||||
- "a": 1,
|
||||
- "b": 2,
|
||||
- }["a"]
|
||||
@@ -123,9 +125,11 @@ some_module.some_function(
|
||||
- "h": 8,
|
||||
- }["a"]
|
||||
- ):
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
+ x = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": 1,
|
||||
+ "NOT_YET_IMPLEMENTED_STRING": 2,
|
||||
+ }[
|
||||
+ "NOT_YET_IMPLEMENTED_STRING"
|
||||
+ ]
|
||||
+ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
pass
|
||||
|
||||
@@ -133,7 +137,7 @@ some_module.some_function(
|
||||
-def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (
|
||||
- Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
|
||||
-):
|
||||
+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]:
|
||||
+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set["NOT_YET_IMPLEMENTED_STRING"]:
|
||||
json = {
|
||||
- "k": {
|
||||
- "k2": {
|
||||
@@ -148,7 +152,7 @@ some_module.some_function(
|
||||
}
|
||||
|
||||
|
||||
@@ -80,35 +53,16 @@
|
||||
@@ -80,35 +58,16 @@
|
||||
pass
|
||||
|
||||
|
||||
@@ -221,12 +225,17 @@ def f(
|
||||
):
|
||||
NOT_IMPLEMENTED_call()
|
||||
NOT_IMPLEMENTED_call()
|
||||
x = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
x = {
|
||||
"NOT_YET_IMPLEMENTED_STRING": 1,
|
||||
"NOT_YET_IMPLEMENTED_STRING": 2,
|
||||
}[
|
||||
"NOT_YET_IMPLEMENTED_STRING"
|
||||
]
|
||||
if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right:
|
||||
pass
|
||||
|
||||
|
||||
def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]:
|
||||
def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set["NOT_YET_IMPLEMENTED_STRING"]:
|
||||
json = {
|
||||
"NOT_YET_IMPLEMENTED_STRING": {
|
||||
"NOT_YET_IMPLEMENTED_STRING": {"NOT_YET_IMPLEMENTED_STRING": [1]},
|
||||
|
||||
@@ -62,7 +62,7 @@ __all__ = (
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,64 +1,42 @@
|
||||
@@ -1,54 +1,32 @@
|
||||
-"""The asyncio package, tracking PEP 3156."""
|
||||
+"NOT_YET_IMPLEMENTED_STRING"
|
||||
|
||||
@@ -136,27 +136,7 @@ __all__ = (
|
||||
+NOT_YET_IMPLEMENTED_StmtImportFrom
|
||||
|
||||
__all__ = (
|
||||
- base_events.__all__
|
||||
- + coroutines.__all__
|
||||
- + events.__all__
|
||||
- + futures.__all__
|
||||
- + locks.__all__
|
||||
- + protocols.__all__
|
||||
- + runners.__all__
|
||||
- + queues.__all__
|
||||
- + streams.__all__
|
||||
- + tasks.__all__
|
||||
+ base_events.NOT_IMPLEMENTED_attr
|
||||
+ + coroutines.NOT_IMPLEMENTED_attr
|
||||
+ + events.NOT_IMPLEMENTED_attr
|
||||
+ + futures.NOT_IMPLEMENTED_attr
|
||||
+ + locks.NOT_IMPLEMENTED_attr
|
||||
+ + protocols.NOT_IMPLEMENTED_attr
|
||||
+ + runners.NOT_IMPLEMENTED_attr
|
||||
+ + queues.NOT_IMPLEMENTED_attr
|
||||
+ + streams.NOT_IMPLEMENTED_attr
|
||||
+ + tasks.NOT_IMPLEMENTED_attr
|
||||
)
|
||||
base_events.__all__
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
@@ -193,16 +173,16 @@ NOT_YET_IMPLEMENTED_StmtImportFrom
|
||||
NOT_YET_IMPLEMENTED_StmtImportFrom
|
||||
|
||||
__all__ = (
|
||||
base_events.NOT_IMPLEMENTED_attr
|
||||
+ coroutines.NOT_IMPLEMENTED_attr
|
||||
+ events.NOT_IMPLEMENTED_attr
|
||||
+ futures.NOT_IMPLEMENTED_attr
|
||||
+ locks.NOT_IMPLEMENTED_attr
|
||||
+ protocols.NOT_IMPLEMENTED_attr
|
||||
+ runners.NOT_IMPLEMENTED_attr
|
||||
+ queues.NOT_IMPLEMENTED_attr
|
||||
+ streams.NOT_IMPLEMENTED_attr
|
||||
+ tasks.NOT_IMPLEMENTED_attr
|
||||
base_events.__all__
|
||||
+ coroutines.__all__
|
||||
+ events.__all__
|
||||
+ futures.__all__
|
||||
+ locks.__all__
|
||||
+ protocols.__all__
|
||||
+ runners.__all__
|
||||
+ queues.__all__
|
||||
+ streams.__all__
|
||||
+ tasks.__all__
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -25,25 +25,28 @@ list_of_types = [tuple[int,],]
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,22 +1,12 @@
|
||||
@@ -1,22 +1,17 @@
|
||||
# We should not treat the trailing comma
|
||||
# in a single-element subscript.
|
||||
-a: tuple[int,]
|
||||
-b = tuple[int,]
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+b = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+b = tuple[(int,)]
|
||||
|
||||
# The magic comma still applies to multi-element subscripts.
|
||||
-c: tuple[
|
||||
- int,
|
||||
- int,
|
||||
-]
|
||||
-d = tuple[
|
||||
- int,
|
||||
- int,
|
||||
-]
|
||||
+NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
+d = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
d = tuple[
|
||||
- int,
|
||||
- int,
|
||||
+ (
|
||||
+ int,
|
||||
+ int,
|
||||
+ )
|
||||
]
|
||||
|
||||
# Magic commas still work as expected for non-subscripts.
|
||||
-small_list = [
|
||||
@@ -53,7 +56,7 @@ list_of_types = [tuple[int,],]
|
||||
- tuple[int,],
|
||||
-]
|
||||
+small_list = [1]
|
||||
+list_of_types = [NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]]
|
||||
+list_of_types = [tuple[(int,)]]
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
@@ -62,15 +65,20 @@ list_of_types = [tuple[int,],]
|
||||
# We should not treat the trailing comma
|
||||
# in a single-element subscript.
|
||||
NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
b = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
b = tuple[(int,)]
|
||||
|
||||
# The magic comma still applies to multi-element subscripts.
|
||||
NOT_YET_IMPLEMENTED_StmtAnnAssign
|
||||
d = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
d = tuple[
|
||||
(
|
||||
int,
|
||||
int,
|
||||
)
|
||||
]
|
||||
|
||||
# Magic commas still work as expected for non-subscripts.
|
||||
small_list = [1]
|
||||
list_of_types = [NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]]
|
||||
list_of_types = [tuple[(int,)]]
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
@@ -76,32 +76,28 @@ return np.divide(
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -11,53 +11,46 @@
|
||||
{**a, **b, **c}
|
||||
@@ -12,52 +12,45 @@
|
||||
|
||||
|
||||
-a = 5**~4
|
||||
a = 5**~4
|
||||
-b = 5 ** f()
|
||||
-c = -(5**2)
|
||||
+b = 5 ** NOT_IMPLEMENTED_call()
|
||||
c = -(5**2)
|
||||
-d = 5 ** f["hi"]
|
||||
-e = lazy(lambda **kwargs: 5)
|
||||
-f = f() ** 5
|
||||
-g = a.b**c.d
|
||||
+d = 5 ** f["NOT_YET_IMPLEMENTED_STRING"]
|
||||
+e = NOT_IMPLEMENTED_call()
|
||||
+f = NOT_IMPLEMENTED_call() ** 5
|
||||
g = a.b**c.d
|
||||
-h = 5 ** funcs.f()
|
||||
-i = funcs.f() ** 5
|
||||
-j = super().name ** 5
|
||||
-k = [(2**idx, value) for idx, value in pairs]
|
||||
-l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001)
|
||||
+a = 5**NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+b = 5 ** NOT_IMPLEMENTED_call()
|
||||
+c = NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+d = 5 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+e = NOT_IMPLEMENTED_call()
|
||||
+f = NOT_IMPLEMENTED_call() ** 5
|
||||
+g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr
|
||||
+h = 5 ** NOT_IMPLEMENTED_call()
|
||||
+i = NOT_IMPLEMENTED_call() ** 5
|
||||
+j = NOT_IMPLEMENTED_call().NOT_IMPLEMENTED_attr ** 5
|
||||
+j = NOT_IMPLEMENTED_call().name ** 5
|
||||
+k = [i for i in []]
|
||||
+l = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
m = [([2**63], [1, 2**63])]
|
||||
@@ -115,28 +111,25 @@ return np.divide(
|
||||
+q = [i for i in []]
|
||||
r = x**y
|
||||
|
||||
-a = 5.0**~4.0
|
||||
a = 5.0**~4.0
|
||||
-b = 5.0 ** f()
|
||||
-c = -(5.0**2.0)
|
||||
+b = 5.0 ** NOT_IMPLEMENTED_call()
|
||||
c = -(5.0**2.0)
|
||||
-d = 5.0 ** f["hi"]
|
||||
-e = lazy(lambda **kwargs: 5)
|
||||
-f = f() ** 5.0
|
||||
-g = a.b**c.d
|
||||
+d = 5.0 ** f["NOT_YET_IMPLEMENTED_STRING"]
|
||||
+e = NOT_IMPLEMENTED_call()
|
||||
+f = NOT_IMPLEMENTED_call() ** 5.0
|
||||
g = a.b**c.d
|
||||
-h = 5.0 ** funcs.f()
|
||||
-i = funcs.f() ** 5.0
|
||||
-j = super().name ** 5.0
|
||||
-k = [(2.0**idx, value) for idx, value in pairs]
|
||||
-l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001)
|
||||
+a = 5.0**NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+b = 5.0 ** NOT_IMPLEMENTED_call()
|
||||
+c = NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
+d = 5.0 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
+e = NOT_IMPLEMENTED_call()
|
||||
+f = NOT_IMPLEMENTED_call() ** 5.0
|
||||
+g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr
|
||||
+h = 5.0 ** NOT_IMPLEMENTED_call()
|
||||
+i = NOT_IMPLEMENTED_call() ** 5.0
|
||||
+j = NOT_IMPLEMENTED_call().NOT_IMPLEMENTED_attr ** 5.0
|
||||
+j = NOT_IMPLEMENTED_call().name ** 5.0
|
||||
+k = [i for i in []]
|
||||
+l = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
m = [([2.0**63.0], [1.0, 2**63.0])]
|
||||
@@ -183,16 +176,16 @@ def function_dont_replace_spaces():
|
||||
{**a, **b, **c}
|
||||
|
||||
|
||||
a = 5**NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
a = 5**~4
|
||||
b = 5 ** NOT_IMPLEMENTED_call()
|
||||
c = NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
d = 5 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
c = -(5**2)
|
||||
d = 5 ** f["NOT_YET_IMPLEMENTED_STRING"]
|
||||
e = NOT_IMPLEMENTED_call()
|
||||
f = NOT_IMPLEMENTED_call() ** 5
|
||||
g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr
|
||||
g = a.b**c.d
|
||||
h = 5 ** NOT_IMPLEMENTED_call()
|
||||
i = NOT_IMPLEMENTED_call() ** 5
|
||||
j = NOT_IMPLEMENTED_call().NOT_IMPLEMENTED_attr ** 5
|
||||
j = NOT_IMPLEMENTED_call().name ** 5
|
||||
k = [i for i in []]
|
||||
l = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
m = [([2**63], [1, 2**63])]
|
||||
@@ -202,16 +195,16 @@ p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_
|
||||
q = [i for i in []]
|
||||
r = x**y
|
||||
|
||||
a = 5.0**NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
a = 5.0**~4.0
|
||||
b = 5.0 ** NOT_IMPLEMENTED_call()
|
||||
c = NOT_YET_IMPLEMENTED_ExprUnaryOp
|
||||
d = 5.0 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]
|
||||
c = -(5.0**2.0)
|
||||
d = 5.0 ** f["NOT_YET_IMPLEMENTED_STRING"]
|
||||
e = NOT_IMPLEMENTED_call()
|
||||
f = NOT_IMPLEMENTED_call() ** 5.0
|
||||
g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr
|
||||
g = a.b**c.d
|
||||
h = 5.0 ** NOT_IMPLEMENTED_call()
|
||||
i = NOT_IMPLEMENTED_call() ** 5.0
|
||||
j = NOT_IMPLEMENTED_call().NOT_IMPLEMENTED_attr ** 5.0
|
||||
j = NOT_IMPLEMENTED_call().name ** 5.0
|
||||
k = [i for i in []]
|
||||
l = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right
|
||||
m = [([2.0**63.0], [1.0, 2**63.0])]
|
||||
|
||||
@@ -40,7 +40,7 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx
|
||||
-xxxxxxxxx_yyy_zzzzzzzz[
|
||||
- xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)
|
||||
-] = 1
|
||||
+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] = 1
|
||||
+xxxxxxxxx_yyy_zzzzzzzz[NOT_IMPLEMENTED_call(), NOT_IMPLEMENTED_call()] = 1
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
@@ -61,7 +61,7 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx
|
||||
|
||||
# Make when when the left side of assignment plus the opening paren "... = (" is
|
||||
# exactly line length limit + 1, it won't be split like that.
|
||||
NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] = 1
|
||||
xxxxxxxxx_yyy_zzzzzzzz[NOT_IMPLEMENTED_call(), NOT_IMPLEMENTED_call()] = 1
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
@@ -48,28 +48,31 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,42 +1,9 @@
|
||||
@@ -1,42 +1,17 @@
|
||||
# These brackets are redundant, therefore remove.
|
||||
-try:
|
||||
- a.something
|
||||
-except AttributeError as err:
|
||||
- raise err
|
||||
-
|
||||
-# This is tuple of exceptions.
|
||||
-# Although this could be replaced with just the exception,
|
||||
-# we do not remove brackets to preserve AST.
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
# This is tuple of exceptions.
|
||||
# Although this could be replaced with just the exception,
|
||||
# we do not remove brackets to preserve AST.
|
||||
-try:
|
||||
- a.something
|
||||
-except (AttributeError,) as err:
|
||||
- raise err
|
||||
-
|
||||
-# This is a tuple of exceptions. Do not remove brackets.
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
# This is a tuple of exceptions. Do not remove brackets.
|
||||
-try:
|
||||
- a.something
|
||||
-except (AttributeError, ValueError) as err:
|
||||
- raise err
|
||||
-
|
||||
-# Test long variants.
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
# Test long variants.
|
||||
-try:
|
||||
- a.something
|
||||
-except (
|
||||
@@ -77,9 +80,6 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov
|
||||
-) as err:
|
||||
- raise err
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
+NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
-try:
|
||||
- a.something
|
||||
@@ -104,8 +104,16 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov
|
||||
```py
|
||||
# These brackets are redundant, therefore remove.
|
||||
NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
# This is tuple of exceptions.
|
||||
# Although this could be replaced with just the exception,
|
||||
# we do not remove brackets to preserve AST.
|
||||
NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
# This is a tuple of exceptions. Do not remove brackets.
|
||||
NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
# Test long variants.
|
||||
NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtTry
|
||||
|
||||
@@ -32,25 +32,28 @@ for (((((k, v))))) in d.items():
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,27 +1,13 @@
|
||||
@@ -1,27 +1,22 @@
|
||||
# Only remove tuple brackets after `for`
|
||||
-for k, v in d.items():
|
||||
- print(k, v)
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+for k, v in NOT_IMPLEMENTED_call():
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
|
||||
# Don't touch tuple brackets after `in`
|
||||
-for module in (core, _unicodefun):
|
||||
for module in (core, _unicodefun):
|
||||
- if hasattr(module, "_verify_python3_env"):
|
||||
- module._verify_python3_env = lambda: None
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+ if NOT_IMPLEMENTED_call():
|
||||
+ module._verify_python3_env = lambda x: True
|
||||
|
||||
# Brackets remain for long for loop lines
|
||||
-for (
|
||||
- why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long,
|
||||
- i_dont_know_but_we_should_still_check_the_behaviour_if_they_do,
|
||||
for (
|
||||
why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long,
|
||||
i_dont_know_but_we_should_still_check_the_behaviour_if_they_do,
|
||||
-) in d.items():
|
||||
- print(k, v)
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+) in NOT_IMPLEMENTED_call():
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
|
||||
-for (
|
||||
- k,
|
||||
@@ -59,30 +62,41 @@ for (((((k, v))))) in d.items():
|
||||
- dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items()
|
||||
-):
|
||||
- print(k, v)
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+for k, v in NOT_IMPLEMENTED_call():
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
|
||||
# Test deeply nested brackets
|
||||
-for k, v in d.items():
|
||||
- print(k, v)
|
||||
+NOT_YET_IMPLEMENTED_StmtFor
|
||||
+for k, v in NOT_IMPLEMENTED_call():
|
||||
+ NOT_IMPLEMENTED_call()
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```py
|
||||
# Only remove tuple brackets after `for`
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for k, v in NOT_IMPLEMENTED_call():
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
# Don't touch tuple brackets after `in`
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for module in (core, _unicodefun):
|
||||
if NOT_IMPLEMENTED_call():
|
||||
module._verify_python3_env = lambda x: True
|
||||
|
||||
# Brackets remain for long for loop lines
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for (
|
||||
why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long,
|
||||
i_dont_know_but_we_should_still_check_the_behaviour_if_they_do,
|
||||
) in NOT_IMPLEMENTED_call():
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for k, v in NOT_IMPLEMENTED_call():
|
||||
NOT_IMPLEMENTED_call()
|
||||
|
||||
# Test deeply nested brackets
|
||||
NOT_YET_IMPLEMENTED_StmtFor
|
||||
for k, v in NOT_IMPLEMENTED_call():
|
||||
NOT_IMPLEMENTED_call()
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user