As an example, if you have `single` as your preferred style, we'll now allow this:
```py
assert s.to_python(123) == (
"123 info=SerializationInfo(include=None, exclude=None, mode='python', by_alias=True, exclude_unset=False, "
"exclude_defaults=False, exclude_none=False, round_trip=False)"
)
```
Previously, the second line of the implicit string concatenation would be flagged as invalid, despite the _first_ line requiring double quotes. (Note that we'll accept either single or double quotes for that second line.)
Mechanically, this required that we process sequences of `Tok::String` rather than a single `Tok::String` at a time. Prior to iterating over the strings in the sequence, we check if any of them require the non-preferred quote style; if so, we let _any_ of them use it.
Closes#2400.
This is a followup to #2361. The isort check still had an issue in a rather specific case: files with a multiline import, indented with tabs, and not containing any indented blocks.
The root cause is this: [`Stylist`'s indentation detection](ad8693e3de/src/source_code/stylist.rs (L163-L172)) works by finding `Indent` tokens to determine the type of indentation used by a file. This works for indented code blocks (loops/classes/functions/etc) but does not work for multiline values, so falls back to 4 spaces if the file doesn't contain code blocks.
I considered a few possible solutions:
1. Fix `detect_indentation` to avoid tokenizing and instead use some other heuristic to determine indentation. This would have the benefit of working in other places where this is potentially an issue, but would still fail if the file doesn't contain any indentation at all, and would need to fall back to option 2 anyways.
2. Add an option for specifying the default indentation in Ruff's config. I think this would confusing, since it wouldn't affect the detection behavior and only operate as a fallback, has no other current application and would probably end up being overloaded for other things.
3. Relax the isort check by comparing the expected and actual code's lexed tokens. This would require an additional lexing step.
4. Relax the isort check by comparing the expected and actual code modulo whitespace at the start of lines.
This PR does approach 4, which in addition to being the simplest option, has the (expected, although I didn't benchmark) added benefit of improved performance, since the check no longer needs to do two allocations for the two `dedent` calls. I also believe that the check is still correct enough for all practical purposes.
We now only trigger `logging-exc-info` and `logging-redundant-exc-info` when in an exception handler, with an `exc_info` that isn't `true` or `sys.exc_info()`.
Closes#2356.
This PR fixes two related issues with using isort on files using tabs for indentation:
- Multiline imports are never considered correctly formatted, since the comparison with the generated code will always fail.
- Using autofix generates code that can have mixed indentation in the same line, for imports that are within nested blocks.
I think we've never run into this case, since it's rare to import `*` from a module _and_ import some other member explicitly. But we were deviating from `isort` by placing the `*` after other members, rather than up-top.
Closes#2318.
`RUF100` does not take into account a rule ignored for a file via a `per-file-ignores` configuration. To see this, try the following pyproject.toml:
```toml
[tool.ruff.per-file-ignores]
"test.py" = ["F401"]
```
and this test.py file:
```python
import itertools # noqa: F401
```
Running `ruff --extend-select RUF100 test.py`, we should expect to get this error:
```
test.py:1:19: RUF100 Unused `noqa` directive (unused: `F401`)
```
The issue is that the per-file-ignores diagnostics are filtered out after the noqa checks, rather than before.
Accessed attributes that are Python constants should be considered for yoda-conditions
```py
## Error
JediOrder.YODA == age # SIM300
## OK
age == JediOrder.YODA
```
~~PS: This PR will fail CI, as the `main` branch currently failing.~~
SIM300 currently doesn't take Python constants into account when looking for Yoda conditions, this PR fixes that behavior.
```python
# Errors
YODA == age # SIM300
YODA > age # SIM300
YODA >= age # SIM300
# OK
age == YODA
age < YODA
age <= YODA
```
Ref: <https://github.com/home-assistant/core/pull/86793>
This isn't super consistent with some other rules, but... if you have a lone body, with a `pass`, followed by a comment, it's probably surprising if it gets removed. Let's retain the comment.
Closes#2231.