[perflint] Optimize extend suggestions and fix identity false negatives (PERF401)#23008
[perflint] Optimize extend suggestions and fix identity false negatives (PERF401)#23008danparizher wants to merge 1 commit intoastral-sh:mainfrom
perflint] Optimize extend suggestions and fix identity false negatives (PERF401)#23008Conversation
|
| code | total | + violation | - violation | + fix | - fix |
|---|---|---|---|---|---|
| PERF401 | 73 | 8 | 65 | 0 | 0 |
| RUF100 | 14 | 14 | 0 | 0 | 0 |
Linter (preview)
ℹ️ ecosystem check detected linter changes. (+22 -65 violations, +0 -0 fixes in 8 projects; 47 projects unchanged)
apache/airflow (+2 -37 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL
- airflow-core/docs/conf.py:168:13: PERF401 Use `list.extend` to create a transformed list - airflow-core/docs/conf.py:172:13: PERF401 Use `list.extend` to create a transformed list - airflow-core/src/airflow/cli/commands/dag_command.py:506:13: PERF401 Use `list.extend` to create a transformed list - airflow-core/src/airflow/dag_processing/manager.py:1020:21: PERF401 Use `list.extend` to create a transformed list - airflow-core/tests/integration/otel/test_otel.py:218:9: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/commands/ci_commands.py:362:25: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/commands/sbom_commands.py:1086:13: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/utils/airflow_release_validator.py:180:29: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/utils/airflow_release_validator.py:211:29: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/utils/publish_docs_to_s3.py:260:17: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/utils/reproducible.py:115:17: PERF401 Use `list.extend` to create a transformed list + dev/breeze/src/airflow_breeze/utils/run_tests.py:554:13: PERF401 Use `list.extend` to create a transformed list - dev/breeze/src/airflow_breeze/utils/selective_checks.py:1330:17: PERF401 Use `list.extend` to create a transformed list - dev/stats/get_important_pr_candidates.py:908:17: PERF401 Use `list.extend` to create a transformed list - devel-common/src/sphinx_exts/docs_build/fetch_inventories.py:128:9: PERF401 Use `list.extend` to create a transformed list - devel-common/src/sphinx_exts/docs_build/fetch_inventories.py:136:9: PERF401 Use `list.extend` to create a transformed list - providers/amazon/src/airflow/providers/amazon/aws/hooks/s3.py:500:21: PERF401 Use `list.extend` to create a transformed list - providers/amazon/src/airflow/providers/amazon/aws/hooks/s3.py:706:21: PERF401 Use `list.extend` to create a transformed list - providers/amazon/src/airflow/providers/amazon/aws/log/s3_task_handler.py:163:17: PERF401 Use `list.extend` to create a transformed list - providers/amazon/src/airflow/providers/amazon/aws/operators/sagemaker.py:1262:17: PERF401 Use `list.extend` to create a transformed list - providers/amazon/src/airflow/providers/amazon/aws/operators/sagemaker.py:390:17: PERF401 Use `list.extend` to create a transformed list - providers/amazon/src/airflow/providers/amazon/aws/operators/sagemaker.py:396:17: PERF401 Use `list.extend` to create a transformed list ... 17 additional changes omitted for project
apache/superset (+1 -21 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL
- scripts/benchmark_migration.py:128:21: PERF401 Use `list.extend` to create a transformed list + superset/commands/dashboard/update.py:140:21: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lib.py:224:9: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lib.py:614:9: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lib.py:620:9: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lib.py:630:9: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lib.py:648:9: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lib.py:659:9: PERF401 Use `list.extend` to create a transformed list - superset/db_engine_specs/lint_metadata.py:576:9: PERF401 Use `list.extend` to create a transformed list - superset/mcp_service/chart/validation/dataset_validator.py:180:17: PERF401 Use `list.extend` to create a transformed list - superset/mcp_service/chart/validation/dataset_validator.py:213:13: PERF401 Use `list.extend` to create a transformed list - superset/mcp_service/chart/validation/runtime/format_validator.py:119:21: PERF401 Use `list.extend` to create a transformed list ... 10 additional changes omitted for project
bokeh/bokeh (+1 -4 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL
- src/bokeh/layouts.py:475:25: PERF401 Use `list.extend` to create a transformed list + src/bokeh/server/contexts.py:88:17: PERF401 Use `list.extend` to create a transformed list - src/bokeh/settings.py:780:21: PERF401 Use `list.extend` to create a transformed list - src/bokeh/settings.py:791:21: PERF401 Use `list.extend` to create a transformed list - tests/codebase/test_windows_reserved_filenames.py:43:17: PERF401 Use `list.extend` to create a transformed list
langchain-ai/langchain (+1 -0 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview
+ libs/langchain/tests/unit_tests/agents/test_agent.py:524:47: RUF100 [*] Unused `noqa` directive (unused: `PERF401`)
latchbio/latch (+1 -3 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview
+ src/latch_cli/menus.py:15:13: PERF401 Use `list.extend` to create a transformed list - src/latch_cli/snakemake/config/utils.py:288:9: PERF401 Use `list.extend` to create a transformed list - src/latch_cli/snakemake/workflow.py:155:21: PERF401 Use `list.extend` to create a transformed list - src/latch_cli/snakemake/workflow.py:158:21: PERF401 Use `list.extend` to create a transformed list
mlflow/mlflow (+2 -0 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview
+ tests/dspy/test_save.py:511:13: PERF401 Use `list.extend` to create a transformed list + tests/tracing/test_fluent.py:529:13: PERF401 Use `list.extend` to create a transformed list
indico/indico (+13 -0 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview
+ indico/core/celery/core.py:181:88: RUF100 [*] Unused `noqa` directive (unused: `PERF401`) + indico/core/celery/core.py:184:83: RUF100 [*] Unused `noqa` directive (unused: `PERF401`) + indico/modules/events/registration/lists.py:123:93: RUF100 [*] Unused `noqa` directive (unused: `PERF401`) + indico/modules/rb/models/rooms.py:512:31: RUF100 [*] Unused `noqa` directive (unused: `PERF401`) + indico/modules/rb/models/rooms.py:519:31: RUF100 [*] Unused `noqa` directive (unused: `PERF401`) + indico/modules/rb/models/rooms.py:530:40: RUF100 [*] Unused `noqa` directive (unused: `PERF401`) ... 7 additional changes omitted for rule RUF100 + indico/web/http_api/hooks/base.py:155:21: PERF401 Use `list.extend` to create a transformed list ... 6 additional changes omitted for project
python-trio/trio (+1 -0 violations, +0 -0 fixes)
ruff check --no-cache --exit-zero --no-fix --output-format concise --preview
+ src/trio/_tools/gen_exports.py:96:52: RUF100 [*] Unused `noqa` directive (unused: `PERF401`)
Changes by rule (2 rules affected)
| code | total | + violation | - violation | + fix | - fix |
|---|---|---|---|---|---|
| PERF401 | 73 | 8 | 65 | 0 | 0 |
| RUF100 | 14 | 14 | 0 | 0 | 0 |
|
The ecosystem check findings (decreased PERF401 violations, increased RUF100 violations) are expected, as we are no longer flagging the "slow" cases that users may have suppressed with |
| if arg | ||
| .as_name_expr() | ||
| .is_some_and(|arg| arg.id == *for_stmt_target_id) |
There was a problem hiding this comment.
I should have noticed this earlier, but as the comment notes, this is an intentional "false negative" to avoid a duplicate diagnostic with manual-list-copy (PERF402). On this branch:
❯ just run check --select PERF --preview - <<EOF
∙ def main1():
ret = []
for x in range(8):
for i in range(x):
ret.append(i)
∙ EOF
PERF402 Use `list` or `list.copy` to create a copy of a list
--> -:5:13
|
3 | for x in range(8):
4 | for i in range(x):
5 | ret.append(i)
| ^^^^^^^^^^^^^
|
PERF401 Use `list.extend` to create a transformed list
--> -:5:13
|
3 | for x in range(8):
4 | for i in range(x):
5 | ret.append(i)
| ^^^^^^^^^^^^^
|
help: Replace for loop with list.extendvs a released branch:
PERF402 Use `list` or `list.copy` to create a copy of a list
--> -:5:13
|
3 | for x in range(8):
4 | for i in range(x):
5 | ret.append(i)
| ^^^^^^^^^^^^^
|
Found 1 error.However, the PERF402 diagnostic is the subject of another bug report: #8070, so I would actually be supportive of moving this case from PERF402 to PERF401 in preview. I would also suggest adding a test case where both rules are active to avoid any regressions here in the future.
I think we should handle that change and the perf-related extend changes in separate PRs to make review easier. I also think if we're modifying the extend behavior for PERF401, we should do the same for PERF403. I know the
needs-decision
Summary
Modifies PERF401 (
manual-list-comprehension) to:extendsuggestions when the loop is an identity transformation (e.g.,for x in y: ret.append(x)->ret.extend(y)). This was previously a false negative.extendsuggestions when the loop involves a transformation or filter (e.g.,for x in y: ret.append(str(x))), as the resultingextend(generator)is often slower than the loop. This was a performance regression.Fixes #21891.
Problem
PERF401 previously ignored
appendcalls where the argument was identical to the loop target (leaving them to PERF402, which only suggestslist()creation, notextendon existing lists).Conversely, PERF401 suggested replacing
appendloops withextend(generator)even when the generator overhead made it slower than the original loop.Approach
manual_list_comprehensionto allowComprehensionType::Extendonly for identity transformations without filters.convert_to_list_extendto generateextend(iterable)instead ofextend(x for x in iterable)for identity cases.ComprehensionType::ListComprehensionfor identity cases (deferring to PERF402).Test Plan
crates/ruff_linter/resources/test/fixtures/perflint/PERF401.py.cargo test -p ruff_linter perflint.