Skip to content

Conversation

w0nder1ng
Copy link
Contributor

Summary

This change adds an auto-fix for manual dict comprehensions. It also copies many of the improvements from #13919 (and associated PRs fixing issues with it), and moves some of the utility functions from manual_list_comprehension.rs into a separate helpers.rs to be used in both.

Test Plan

I added a preview test case to showcase the new fix and added a test case in PERF403.py to make sure lines with semicolons function. I didn't yet make similar tests to the ones I added earlier to PERF401.py, but the logic is the same, so it might be good to add those to make sure they work.

Copy link
Contributor

github-actions bot commented Mar 14, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+6 -8 violations, +0 -0 fixes in 3 projects; 52 projects unchanged)

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

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

+ providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py:141:17: PERF403 Use `dict.update` instead of a for-loop
- providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py:141:17: PERF403 Use a dictionary comprehension instead of a for-loop
+ providers/exasol/src/airflow/providers/exasol/hooks/exasol.py:70:17: PERF403 Use `dict.update` instead of a for-loop
- providers/exasol/src/airflow/providers/exasol/hooks/exasol.py:70:17: PERF403 Use a dictionary comprehension instead of a for-loop
+ providers/mysql/src/airflow/providers/mysql/hooks/mysql.py:191:17: PERF403 Use `dict.update` instead of a for-loop
- providers/mysql/src/airflow/providers/mysql/hooks/mysql.py:191:17: PERF403 Use a dictionary comprehension instead of a for-loop
+ providers/postgres/src/airflow/providers/postgres/hooks/postgres.py:171:17: PERF403 Use `dict.update` instead of a for-loop
- providers/postgres/src/airflow/providers/postgres/hooks/postgres.py:171:17: PERF403 Use a dictionary comprehension instead of a for-loop

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

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

+ superset/migrations/versions/2021-04-12_12-38_fc3a3a8ff221_migrate_filter_sets_to_new_format.py:122:21: PERF403 Use `dict.update` instead of a for-loop
- superset/migrations/versions/2021-04-12_12-38_fc3a3a8ff221_migrate_filter_sets_to_new_format.py:122:21: PERF403 Use a dictionary comprehension instead of a for-loop
+ superset/migrations/versions/2022-04-01_14-38_a9422eeaae74_new_dataset_models_take_2.py:795:21: PERF403 Use `dict.update` instead of a for-loop
- superset/migrations/versions/2022-04-01_14-38_a9422eeaae74_new_dataset_models_take_2.py:795:21: PERF403 Use a dictionary comprehension instead of a for-loop

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

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

- pandas/tests/arrays/sparse/test_accessor.py:247:30: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw
- pandas/tests/arrays/sparse/test_accessor.py:96:50: RUF043 Pattern passed to `match=` contains metacharacters but is neither escaped nor raw

Changes by rule (2 rules affected)

code total + violation - violation + fix - fix
PERF403 12 6 6 0 0
RUF043 2 0 2 0 0

@MichaReiser MichaReiser added the fixes Related to suggested fixes for violations label Mar 14, 2025
@Skylion007
Copy link
Contributor

Skylion007 commented Mar 15, 2025

Slight nit: had a situation where it didn't simplify an annotated dict = dict() builtin to a single line comprehension. Still kept a sepereate call to the dict() builtin function followed by update. Manually fixed that.

    node_to_step: dict[BaseSchedulerNode, int] = dict()
    for step, node in enumerate(nodes):
        node_to_step[node] = step

got transformed to

     node_to_step: dict[BaseSchedulerNode, int] = dict()
     node_to_step.update(...)

@w0nder1ng
Copy link
Contributor Author

I had forgotten about using a function call to make a dict, since it's not typically something I do in my own code. I've now modified the "is empty dict" check to allow builtin function calls for both PERF403 and PERF401.

@Skylion007
Copy link
Contributor

Title is wrong, it's PERF403, right?

@w0nder1ng w0nder1ng changed the title [perflint] implement quick-fix for manual-dict-comprehension (PERF404) [perflint] implement quick-fix for manual-dict-comprehension (PERF403) Mar 17, 2025
@w0nder1ng
Copy link
Contributor Author

Do you know why the tests aren't running? The test jobs in my fork of the repo are timing out while waiting for a runner.

@ntBre
Copy link
Contributor

ntBre commented Mar 24, 2025

I think the most recent commits came in while CI was disabled. You can try pushing another (possibly empty) commit, or closing and reopening the PR to retrigger CI.

@Skylion007
Copy link
Contributor

@ntBre CI is run now.

@ntBre
Copy link
Contributor

ntBre commented Apr 3, 2025

Thanks for the ping, I'll try to review this tomorrow.

@ntBre ntBre self-assigned this Apr 3, 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 for all of this work! It looks good, and most of my inline comments are just stylistic nits.

For tests, I think I'd like to see ~1 test for each of the (very helpful) comments that document some constraint, if that's not too hard. Is that what you had in mind in the PR summary?

@w0nder1ng
Copy link
Contributor Author

@ntBre I think I've addressed all of your feedback, so please let me know if you need anything else changed.

Also, do you know why the configuration.md output test is failing? I don't think I touched any files that affect it.

@ntBre
Copy link
Contributor

ntBre commented Apr 7, 2025

Also, do you know why the configuration.md output test is failing? I don't think I touched any files that affect it.

At the very top of the error message it says

Error: docs/configuration.md changed, please run cargo dev generate-all:

I think adding a fix probably changed the docs slightly, and you just need to run cargo dev generate-all to fix it. Some of the diffs look kind of weird, though, so you may also need to merge main or something, but try generate-all first.

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! I'd still like to avoid the expect calls, and I added one more suggestion for Diagnostic::with_fix but this looks good to me otherwise, once the tests are passing.

@w0nder1ng
Copy link
Contributor Author

Turns out I just needed to merge with main. Did you also want the .expect() in convert_to_dict_comprehension removed?

@ntBre
Copy link
Contributor

ntBre commented Apr 7, 2025

Turns out I just needed to merge with main. Did you also want the .expect() in convert_to_dict_comprehension removed?

Yes please!

@w0nder1ng
Copy link
Contributor Author

@ntBre let me know if any other changes are necessary

@ntBre ntBre changed the title [perflint] implement quick-fix for manual-dict-comprehension (PERF403) [perflint] Implement fix for manual-dict-comprehension (PERF403) Apr 16, 2025
Comment on lines 270 to 282
list_binding_value.is_some_and(|binding_value| match binding_value {
// `value = []`
Expr::List(list_expr) => list_expr.is_empty(),
// `value = list()`
// This is probably be linted against, but turning it into a list comprehension will also remove it
Expr::Call(call) => {
checker
.semantic()
.resolve_builtin_symbol(&call.func)
.is_some_and(|name| name == "list")
&& call.arguments.is_empty()
}
_ => false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for not noticing this earlier (or for bringing it up again if we already discussed it), but should this be included here? I think these changes (and the corresponding ones on the dict side) are causing the differences in the ecosystem check, and I would kind of like to limit this PR strictly to the autofix.

I would have at least expected some PERF401 snapshots to change with this, since it's obviously affecting ecosystem packages.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can split it off in a different PR, but it's an ecosystem bug we noticed when testing.

@ntBre ntBre added the preview Related to preview mode features label Apr 18, 2025
@ntBre
Copy link
Contributor

ntBre commented Apr 18, 2025

Thanks, I thought we had to revert the Call check in the dict too, but that's in the new preview part anyway.

@ntBre ntBre merged commit 0822145 into astral-sh:main Apr 18, 2025
22 checks passed
@Skylion007
Copy link
Contributor

@W0ndering Mind opening a new PR for that ecosystem fix to list Calls as well?

ntBre pushed a commit that referenced this pull request Apr 21, 2025
…nsion (`PERF401`) (#17519)

This is an implementation of the discussion from #16719. 

This change will allow list function calls to be replaced with
comprehensions:

```python
result = list()
for i in range(3):
    result.append(i + 1)
# becomes
result = [i + 1 for i in range(3)]
```

I added a new test to `PERF401.py` to verify that this fix will now work
for `list()`.
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.

4 participants