`logging.basicConfig` should not be called at a global module scope,
as that produces a race condition to configure logging based on which
module gets imported first. Logging should instead be initialized
in an entrypoint to the program, either in a `main()` or in the
typical `if __name__ == "__main__"` block.
## Summary
We use this profile in uv to create success, as an optimization for the
iterative test loop. We include `opt-level=1` because it ends up being
"worth it" for testing (empirically), even though it means the build is
actually a big slower than `dev` (if you remove `opt-level=1`, clean
compile is about 22% faster than `dev`).
Here are some benchmarks I generated with Claude -- the main motivator
here is the incremental testing for `ty_python_semantic` which is 2.4x
faster:
### `ty_python_semantic`
Full test suite (471 tests):
| Scenario | dev | fast-test | Improvement |
|-------------|-------|------------|-------------|
| Clean | 53s | 49s | 8% faster |
| Incremental | 17.8s | 6.8s | 2.4x faster |
Single test:
| Scenario | dev | fast-test | Improvement |
|-------------|-------|------------|-------------|
| Clean | 42.5s | 55.3s | 30% slower |
| Incremental | 6.5s | 6.1s | ~same |
### `ruff_linter`
Full test suite (2622 tests):
| Scenario | dev | fast-test | Improvement |
|-------------|-------|------------|-------------|
| Clean | 31s | 41s | 32% slower |
| Incremental | 11.9s | 10.5s | 12% faster |
Single test:
| Scenario | dev | fast-test | Improvement |
|-------------|------|------------|-------------|
| Clean | 26s | 36.5s | 40% slower |
| Incremental | 4.5s | 5.5s | 22% slower |
Snapshot tests recently started reporting this warning:
> Snapshot test passes but the existing value is in a legacy format.
> Please run cargo insta test --force-update-snapshots to update to a
> newer format.
This PR is the result of that forced update.
One file (crates/ruff_db/src/diagnostic/render/full.rs) seems to get
corrupted, because it contains strings with unprintable characters that
trigger some bug in cargo-insta. I've manually reverted that file, and
also manually reverted the `input_file:` lines, which we like.
## Summary
Decorators are now called with the class as an argument, and the return
type becomes the class's type. This mirrors how function decorators
already work.
Closes https://github.com/astral-sh/ty/issues/2313.
## Summary
This PR closes#21692. `PLR1714` will no longer flag if all members are
identical. I iterate through the equality comparisons and if they are
all equal the rule does not flag.
## Test Plan
Additional tests were added with identical members.
Summary
--
This PR fixes#14900 by:
- Restricting the diagnostic range from the whole `for` loop to only the
`target in iter` part
- Adding secondary annotations to each use of the `dict[key]` accesses
- Adding a `fix_title` suggesting to use `for key in dict.items()`
I thought this approach sounded slightly nicer than the alternative of
renaming the rule to focus on each indexing operation mentioned in
https://github.com/astral-sh/ruff/issues/14900#issuecomment-2543923625,
but I don't feel too strongly. This was easy to implement with our new
diagnostic infrastructure too.
This produces an example annotation like this:
```
PLC0206 Extracting value from dictionary without calling `.items()`
--> dict_index_missing_items.py:59:5
|
58 | # A case with multiple uses of the value to show off the secondary annotations
59 | for instrument in ORCHESTRA:
| ^^^^^^^^^^^^^^^^^^^^^^^
60 | data = json.dumps(
61 | {
62 | "instrument": instrument,
63 | "section": ORCHESTRA[instrument],
| ---------------------
64 | }
65 | )
66 |
67 | print(f"saving data for {instrument} in {ORCHESTRA[instrument]}")
| ---------------------
68 |
69 | with open(f"{instrument}/{ORCHESTRA[instrument]}.txt", "w") as f:
| ---------------------
70 | f.write(data)
|
help: Use `for instrument, value in ORCHESTRA.items()` instead
```
which I think is a big improvement over:
```
PLC0206 Extracting value from dictionary without calling `.items()`
--> dict_index_missing_items.py:59:1
|
58 | # A case with multiple uses of the value to show off the secondary annotations
59 | / for instrument in ORCHESTRA:
60 | | data = json.dumps(
61 | | {
62 | | "instrument": instrument,
63 | | "section": ORCHESTRA[instrument],
64 | | }
65 | | )
66 | |
67 | | print(f"saving data for {instrument} in {ORCHESTRA[instrument]}")
68 | |
69 | | with open(f"{instrument}/{ORCHESTRA[instrument]}.txt", "w") as f:
70 | | f.write(data)
| |_____________________^
|
```
The secondary annotation feels a bit bare without a message, but I
thought it
might be too busy to include one. Something like `value extracted here`
or
`indexed here` might work if we do want to include a brief message.
To avoid collecting a `Vec` of annotation ranges, I added a `&Checker`
to the
rule's visitor to emit diagnostics as we go instead of at the end.
Test Plan
--
Existing tests, plus a new case showing off multiple secondary
annotations
## Summary
Fixes false positive in ARG001 when `**kwargs` is passed to
`typing.TypeVar`
Closes#22178
When `**kwargs` is used in a `typing.TypeVar` call, the checker was not
recognizing it as a usage, leading to false positive "unused function
argument" warnings.
### Root Cause
In the AST, keyword arguments are represented by the `Keyword` struct
with an `arg` field of type `Option<Identifier>`:
- Named keywords like `bound=int` have `arg = Some("bound")`
- Dictionary unpacking like `**kwargs` has `arg = None`
The existing code only handled the `Some(id)` case, never visiting the
expression when `arg` was `None`, so `**kwargs` was never marked as
used.
### Changes
Added an `else` branch to handle `**kwargs` unpacking by calling
`visit_non_type_definition(value)` when `arg` is `None`. This ensures
the `kwargs` variable is properly visited and marked as used by the
semantic model.
## Test Plan
Tested with the following code:
```python
import typing
def f(
*args: object,
default: object = None,
**kwargs: object,
) -> None:
typing.TypeVar(*args, **kwargs)
```
Before :
`ARG001 Unused function argument: kwargs
`
After :
`All checks passed!`
Run the example with the following command(from the root of ruff and
please change the path to the module that contains the code example):
`cargo run -p ruff -- check /path/to/file.py --isolated --select=ARG
--no-cache`