Skip to content

[ruff] Add unnecessary-regular-expression-compile (RUF078) - #25533

Open
anishgirianish wants to merge 9 commits into
astral-sh:mainfrom
anishgirianish:ruff-unnecessary-regex-compile
Open

anishgirianish wants to merge 9 commits into
astral-sh:mainfrom
anishgirianish:ruff-unnecessary-regex-compile

Conversation

@anishgirianish

@anishgirianish anishgirianish commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds unnecessary-regular-expression-compile (RUF076, preview): flags a re.compile() used exactly once inline (re.compile(p).match(s)) or via a single-use local, since the top-level re functions are equivalent and skip
the throwaway object. Reused patterns (module/class-level, or locals read more than once) are left alone.

Closes #14691.

Test Plan

cargo test -p ruff_mdtest --test mdtest -- unnecessary-regular-expression-compile

@astral-sh-bot
astral-sh-bot Bot requested a review from ntBre June 1, 2026 18:27
@anishgirianish
anishgirianish marked this pull request as draft June 1, 2026 18:28
@astral-sh-bot

astral-sh-bot Bot commented Jun 1, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+83 -0 violations, +0 -0 fixes in 21 projects; 38 projects unchanged)

DisnakeDev/disnake (+4 -0 violations, +0 -0 fixes)

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

+ disnake/ext/commands/context.py:252:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ disnake/ext/commands/converter.py:1057:37: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ disnake/ext/commands/converter.py:392:17: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ disnake/ext/commands/converter.py:392:45: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ .github/scripts/citation_updater.py:66:29: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ airflow-core/src/airflow/dag_processing/bundles/manager.py:627:24: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ airflow-core/tests/unit/utils/test_db.py:234:15: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ dev/breeze/src/airflow_breeze/commands/ci_commands.py:645:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ dev/breeze/src/airflow_breeze/commands/kubernetes_commands.py:531:21: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ dev/breeze/src/airflow_breeze/commands/workflow_commands.py:242:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ dev/breeze/src/airflow_breeze/utils/constraints_version_check.py:603:13: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ dev/breeze/src/airflow_breeze/utils/docker_command_utils.py:372:29: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ kubernetes-tests/tests/kubernetes_tests/test_base.py:134:18: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ kubernetes-tests/tests/kubernetes_tests/test_base.py:142:18: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ kubernetes-tests/tests/kubernetes_tests/test_base.py:391:18: unnecessary-regular-expression-compile Compiled regular expression is used only once
... 7 additional changes omitted for project

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

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

+ superset/daos/database.py:302:21: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ superset/db_engine_specs/base.py:1339:18: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ superset/db_engine_specs/presto.py:119:13: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ tests/unit_tests/models/helpers_test.py:4019:12: unnecessary-regular-expression-compile Compiled regular expression is used only once

binary-husky/gpt_academic (+16 -0 violations, +0 -0 fixes)

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

+ crazy_functions/agent_fns/python_comment_agent.py:276:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/agent_fns/python_comment_agent.py:277:19: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_actions.py:137:21: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_actions.py:141:25: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_toolbox.py:163:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_toolbox.py:178:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_toolbox.py:196:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_toolbox.py:222:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ crazy_functions/latex_fns/latex_toolbox.py:442:17: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ multi_language.py:132:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
... 6 additional changes omitted for project

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

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

+ scripts/hooks/protect_branches.py:13:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ src/bokeh/embed/util.py:373:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ src/bokeh/embed/util.py:388:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ src/bokeh/util/version.py:65:17: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ src/bokeh/util/version.py:78:13: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ tests/unit/bokeh/core/test_templates.py:84:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ tests/unit/bokeh/embed/test_elements.py:71:18: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ tests/unit/bokeh/embed/test_elements.py:76:18: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ tests/unit/bokeh/test___init__.py:83:12: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ ibis/backends/sql/ddl.py:26:17: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ libs/core/langchain_core/runnables/graph_mermaid.py:440:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ libs/langchain/tests/unit_tests/callbacks/test_file.py:44:12: unnecessary-regular-expression-compile Compiled regular expression is used only once

milvus-io/pymilvus (+1 -0 violations, +0 -0 fixes)

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

+ pymilvus/client/__init__.py:25:13: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ pandas/_config/localization.py:177:13: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ pandas/core/dtypes/dtypes.py:2029:13: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ pandas/tests/generic/test_finalize.py:368:9: unnecessary-regular-expression-compile Compiled regular expression is used only once

prefecthq/prefect (+1 -0 violations, +0 -0 fixes)

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

+ src/integrations/prefect-docker/prefect_docker/types.py:35:13: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ packages/reflex-release/src/reflex_release/dist.py:166:20: unnecessary-regular-expression-compile Compiled regular expression is used only once

rotki/rotki (+2 -0 violations, +0 -0 fixes)

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

+ rotkehlchen/globaldb/upgrades/v3_v4.py:320:38: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ rotkehlchen/tests/unit/test_bitcoin.py:633:13: unnecessary-regular-expression-compile Compiled regular expression is used only once

scikit-build/scikit-build-core (+1 -0 violations, +0 -0 fixes)

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

+ src/scikit_build_core/settings/skbuild_overrides.py:102:17: unnecessary-regular-expression-compile Compiled regular expression is used only once

sphinx-doc/sphinx (+2 -0 violations, +0 -0 fixes)

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

+ sphinx/domains/cpp/__init__.py:241:16: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ tests/test_ext_napoleon/test_ext_napoleon_docstring.py:2771:24: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ zerver/lib/markdown/fenced_code.py:161:15: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ zerver/lib/markdown/fenced_code.py:413:13: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ zerver/lib/test_fixtures.py:297:12: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ zerver/lib/typed_endpoint_validators.py:107:23: unnecessary-regular-expression-compile Compiled regular expression is used only once
+ zerver/webhooks/jira/view.py:91:21: unnecessary-regular-expression-compile Compiled regular expression is used only once

indico/indico (+2 -0 violations, +0 -0 fixes)

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

+ indico/util/mdx_latex.py:534:15: RUF078 Compiled regular expression is used only once
+ indico/util/mdx_latex.py:537:16: RUF078 Compiled regular expression is used only once

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

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

+ tests/test_virtualenv.py:452:20: unnecessary-regular-expression-compile Compiled regular expression is used only once

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

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

+ testing/python/approx.py:84:20: unnecessary-regular-expression-compile Compiled regular expression is used only once

... Truncated remaining completed project reports due to GitHub comment length restrictions

Changes by rule (2 rules affected)

code total + violation - violation + fix - fix
unnecessary-regular-expression-compile 81 81 0 0 0
RUF078 2 2 0 0 0

@anishgirianish
anishgirianish marked this pull request as ready for review June 1, 2026 20:00
…gex-compile

# Conflicts:
#	crates/ruff_linter/src/codes.rs

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks, this looks good to me overall! As usual, Codex came up with some edge cases that might be worth looking into.

First, I believe the binding.references() count is only counting textual references, so a case like this:

p = re.compile(...)
for x in range(100):
    p.match(...)

only looks like one reference. This occurs in a couple of ecosystem projects:

Somewhat similarly, I don't think the rule is accounting for rebindings currently. So a conditional case like this can be incorrect:

pattern = Matcher()
if condition:
    pattern = re.compile("a")
return pattern.match(value)

This one is the least problematic, or at least seems the most rare, but we might also want to check for side effects in the re.compile arguments because these will only be evaluated once if the rule is followed. Something like this:

re.compile(get_pattern()).match(get_string())

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Jun 24, 2026
@anishgirianish anishgirianish changed the title [ruff] Add unnecessary-regular-expression-compile (RUF076) [ruff] Add unnecessary-regular-expression-compile (RUF077) Jun 25, 2026

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

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 found a few more edge cases and a possible small simplification.

Comment thread crates/ruff_linter/src/rules/ruff/rules/unnecessary_regular_expression_compile.rs Outdated
@ntBre ntBre changed the title [ruff] Add unnecessary-regular-expression-compile (RUF077) [ruff] Add unnecessary-regular-expression-compile (RUF077) Jul 31, 2026
…gex-compile

# Conflicts:
#	crates/ruff_linter/src/codes.rs
@anishgirianish anishgirianish changed the title [ruff] Add unnecessary-regular-expression-compile (RUF077) [ruff] Add unnecessary-regular-expression-compile (RUF078) Sep 9, 2026
@anishgirianish

Copy link
Copy Markdown
Contributor Author

Thanks, this is looking good. I found a few more edge cases and a possible small simplification.

Hi @ntBre , thank you so much for the review, I have updated pr resolving the feedbacks. Would like to request you for re-review, when ever you get a chance.

Thank you

@ntBre

ntBre commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Thanks for working on this and dealing with all my comments! I feel bad mentioning this in light of all the work you've put in, but what do you think about restricting the rule to the inline case rather than trying to track assignments and loops and such? This occurred to me today when reviewing again for a few reasons:

  • this is the case described in the original issue
  • tracking assignments and control flow introduces a lot of complexity into the rule that is hard to get right (Codex found another small bug today)
  • Codex pointed out that the re module actually caches compiled regex patterns, even across calls (https://github.com/python/cpython/blob/ebf955df7a89ed0c7968f79faec1de49f61ed7cb/Lib/re/__init__.py#L321-L330). This means the performance justification for the rule is weaker than I thought. It really is more about complexity or style.
  • applying the rule to the single-use case seems more opinionated. I think more people would be interested in the inline case, whereas a separate re.compile call feels more intentional

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rule idea: unncessary-compiled-regular-expression

2 participants