Skip to content

Conversation

@junhsonjb
Copy link
Contributor

Summary

As a follow-up to #18949 (suggested here), this PR implements auto-fix logic for PLC0207.

Test Plan

Existing tests pass, with updates to the snapshot so that it expects the new output that comes along with the auto-fix.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 16, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+62 -62 violations, +0 -0 fixes in 13 projects; 42 projects unchanged)

PlasmaPy/PlasmaPy (+1 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- noxfile.py:453:16: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ noxfile.py:453:16: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.

apache/airflow (+11 -11 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview --select ALL

- dev/backport/update_backport_status.py:78:21: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ dev/backport/update_backport_status.py:78:21: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- dev/breeze/src/airflow_breeze/commands/common_options.py:502:26: PLC0207 Pass `maxsplit=1` into `str.split()`
+ dev/breeze/src/airflow_breeze/commands/common_options.py:502:26: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- dev/breeze/src/airflow_breeze/commands/release_management_commands.py:2633:50: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ dev/breeze/src/airflow_breeze/commands/release_management_commands.py:2633:50: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py:714:34: PLC0207 Pass `maxsplit=1` into `str.split()`
+ dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py:714:34: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- dev/breeze/src/airflow_breeze/utils/run_utils.py:120:25: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ dev/breeze/src/airflow_breeze/utils/run_utils.py:120:25: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
... 12 additional changes omitted for project

apache/superset (+2 -2 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview --select ALL

- tests/common/query_context_generator.py:214:29: PLC0207 Pass `maxsplit=1` into `str.split()`
+ tests/common/query_context_generator.py:214:29: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- tests/common/query_context_generator.py:255:22: PLC0207 Pass `maxsplit=1` into `str.split()`
+ tests/common/query_context_generator.py:255:22: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

bokeh/bokeh (+3 -3 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview --select ALL

- src/bokeh/io/notebook.py:663:26: PLC0207 Pass `maxsplit=1` into `str.split()`
+ src/bokeh/io/notebook.py:663:26: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- src/bokeh/util/token.py:142:41: PLC0207 Pass `maxsplit=1` into `str.split()`
+ src/bokeh/util/token.py:142:41: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- src/bokeh/util/token.py:155:41: PLC0207 Pass `maxsplit=1` into `str.split()`
+ src/bokeh/util/token.py:155:41: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

freedomofpress/securedrop (+1 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- securedrop/store.py:345:49: PLC0207 Pass `maxsplit=1` into `str.split()`
+ securedrop/store.py:345:49: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

ibis-project/ibis (+1 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- ibis/examples/gen_registry.py:125:52: PLC0207 Pass `maxsplit=1` into `str.split()`
+ ibis/examples/gen_registry.py:125:52: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

langchain-ai/langchain (+22 -22 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- libs/core/langchain_core/_api/deprecation.py:360:17: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ libs/core/langchain_core/_api/deprecation.py:360:17: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- libs/core/langchain_core/_api/deprecation.py:360:56: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ libs/core/langchain_core/_api/deprecation.py:360:56: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- libs/core/langchain_core/_api/deprecation.py:368:17: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ libs/core/langchain_core/_api/deprecation.py:368:17: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- libs/core/langchain_core/_api/deprecation.py:369:16: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ libs/core/langchain_core/_api/deprecation.py:369:16: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- libs/core/langchain_core/_api/deprecation.py:473:24: PLC0207 Pass `maxsplit=1` into `str.split()`
+ libs/core/langchain_core/_api/deprecation.py:473:24: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- libs/core/langchain_core/_api/deprecation.py:492:27: PLC0207 Pass `maxsplit=1` into `str.split()`
+ libs/core/langchain_core/_api/deprecation.py:492:27: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- libs/core/langchain_core/runnables/graph_mermaid.py:157:24: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ libs/core/langchain_core/runnables/graph_mermaid.py:157:24: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- libs/core/langchain_core/utils/mustache.py:85:19: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ libs/core/langchain_core/utils/mustache.py:85:19: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- libs/core/langchain_core/utils/utils.py:140:32: PLC0207 Pass `maxsplit=1` into `str.split()`
... 27 additional changes omitted for project

pandas-dev/pandas (+1 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- pandas/compat/_optional.py:165:14: PLC0207 Pass `maxsplit=1` into `str.split()`
+ pandas/compat/_optional.py:165:14: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

pypa/cibuildwheel (+3 -3 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- bin/update_pythons.py:158:22: PLC0207 Pass `maxsplit=1` into `str.split()`
+ bin/update_pythons.py:158:22: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- cibuildwheel/platforms/macos.py:153:31: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ cibuildwheel/platforms/macos.py:153:31: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- cibuildwheel/selector.py:79:26: PLC0207 Pass `maxsplit=1` into `str.split()`
+ cibuildwheel/selector.py:79:26: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

zulip/zulip (+12 -12 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview --select ALL

- zerver/lib/send_email.py:287:16: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ zerver/lib/send_email.py:287:16: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- zerver/lib/send_email.py:429:21: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ zerver/lib/send_email.py:429:21: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- zerver/lib/upload/local.py:67:17: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ zerver/lib/upload/local.py:67:17: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- zerver/lib/upload/s3.py:176:25: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ zerver/lib/upload/s3.py:176:25: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
- zerver/lib/webhooks/common.py:249:26: PLC0207 Pass `maxsplit=1` into `str.split()`
+ zerver/lib/webhooks/common.py:249:26: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
... 14 additional changes omitted for project

wntrblm/nox (+1 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- tests/test_sessions.py:1199:44: PLC0207 Instead of `str.split()`, call `str.rsplit()` and pass `maxsplit=1`
+ tests/test_sessions.py:1199:44: PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.

pytest-dev/pytest (+3 -3 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- src/_pytest/config/findpaths.py:160:16: PLC0207 Pass `maxsplit=1` into `str.split()`
+ src/_pytest/config/findpaths.py:160:16: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- src/_pytest/terminal.py:1012:40: PLC0207 Pass `maxsplit=1` into `str.split()`
+ src/_pytest/terminal.py:1012:40: PLC0207 [*] Replace with `split(..., maxsplit=1)`.
- src/_pytest/terminal.py:462:41: PLC0207 Pass `maxsplit=1` into `str.split()`
+ src/_pytest/terminal.py:462:41: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

astropy/astropy (+1 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --no-fix --output-format concise --preview

- astropy/io/ascii/latex.py:438:16: PLC0207 Pass `maxsplit=1` into `str.split()`
+ astropy/io/ascii/latex.py:438:16: PLC0207 [*] Replace with `split(..., maxsplit=1)`.

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
PLC0207 124 62 62 0 0

@ntBre ntBre self-requested a review July 16, 2025 19:12
ntBre added a commit that referenced this pull request Jul 17, 2025
## Summary

This came up on
[Discord](https://discord.com/channels/1039017663004942429/1343692072921731082/1395447082520678440)
and also in #19387, but on macOS the tmp directory is a symlink to
`/private/tmp`, which breaks this filter. I'm still not quite sure why
only these tests are affected when we use the `tempdir_filter`
elsewhere, but hopefully this fixes the immediate issue. Just
`tempdir.path().canonicalize()` also worked, but I used `dunce` since
that's what I saw in other tests (I guess it's not _just_ these tests).

Some related links from uv:
-
https://github.com/astral-sh/uv/blob/1b2f212e8b2f91069b858cb7f5905589c9d15add/crates/uv/tests/it/common/mod.rs#L1161-L1178
-
https://github.com/astral-sh/uv/blob/1b2f212e8b2f91069b858cb7f5905589c9d15add/crates/uv/tests/it/common/mod.rs#L424-L438
- astral-sh/uv#14290

Thanks to @zanieb for those!

## Test Plan

I tested the `main` branch on my MacBook and reproduced the test
failure, then confirmed that the tests pass after the change. Now to
make sure it passes on Windows, which caused most of the trouble in the
first PR!
@junhsonjb junhsonjb force-pushed the jjb-maxsplit-autofix branch from 0ecc0ed to d5d89c2 Compare July 18, 2025 11:37
@ntBre ntBre added fixes Related to suggested fixes for violations preview Related to preview mode features labels Jul 21, 2025
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is looking good! I just had some ideas/questions about the diagnostic messages and fix safety.

Comment on lines 74 to 78
if actual_split_type == suggested_split_type {
format!("Pass `maxsplit=1` into `str.{actual_split_type}()`")
} else {
format!("Use `str.{suggested_split_type}()` and pass `maxsplit=1`")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These look quite repetitive with the message above, especially the first case, which is exactly the same. How hard would it be to make a message like:

Replace with `{suggested_split_type}(..., maxsplit=1)`.

I don't really love that either, but I don't think we should duplicate the message. Ideally we could include the actual variable name and pattern, but that would require a bit of extra work.

Another option might be changing message to something more general instead. I think the more detailed suggestion is a better fit here in fix_title, especially since it's AlwaysFixable.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the message and fix_title are redundant and that it's better to have more detail in the fix_title. I also agree that including the actual variable name and pattern is ideal, but I think that might fall out of the scope for this PR. Perhaps we go with a more general message and leave that as a potential follow-up?

I think since this is AlwaysFixable we can get away with a general message especially because the fix_title will always be there to provide more detail. How does that sound to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me!

SliceBoundary::Last => "rsplit",
};

let maxsplit_argument_edit = fix::edits::add_argument(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was checking if other calls to add_argument were safe or not, and I found a few examples like this:

add_argument(
"check=False",
&call.arguments,
checker.comment_ranges(),
checker.locator().contents(),
),
// If the function call contains `**kwargs`, mark the fix as unsafe.
if call
.arguments
.keywords
.iter()
.any(|keyword| keyword.arg.is_none())
{
Applicability::Unsafe
} else {
Applicability::Safe
},

I think we should probably do the same here and also add a ## Fix safety section to the docs. I can't think of another reason it would be unsafe because we shouldn't be able to delete any comments with these edits.

Here's a PR adding one of these, for a ## Fix safety example too:

https://github.com/astral-sh/ruff/pull/9176/files

@junhsonjb
Copy link
Contributor Author

Thanks for the comments, in addition to the updated message that we discussed, I added the ## Fix Safety like you suggested and marked the fix as unsafe when **kwargs is present! Let me know what you think of the changes!

@junhsonjb junhsonjb requested a review from ntBre July 25, 2025 14:11
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@ntBre ntBre merged commit 6d0f3ef into astral-sh:main Jul 28, 2025
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fixes Related to suggested fixes for violations preview Related to preview mode features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants