Skip to content

[ruff] Ban pytest autouse fixtures (RUF076) - #25477

Merged
ntBre merged 19 commits into
astral-sh:mainfrom
Kilo59:ban-pytest-fixture-autouse
Jun 4, 2026
Merged

[ruff] Ban pytest autouse fixtures (RUF076)#25477
ntBre merged 19 commits into
astral-sh:mainfrom
Kilo59:ban-pytest-fixture-autouse

Conversation

@Kilo59

@Kilo59 Kilo59 commented May 30, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a new Ruff-specific lint rule (RUF076) in the preview namespace to ban pytest fixtures that set the parameter autouse=True in the decorator constructor.

Implicitly running autouse fixtures is discouraged because it hides test dependencies and can introduce subtle side effects (especially when defined in shared conftest.py files), making the test suite harder to reason about and debug. Explicit parameter injection is preferred.

Note

A default banning of all autouse fixtures is opinionated; we may want to wait for further configuration settings (such as filtering by scope or restricting to conftest.py files) and appropriate defaults before taking this rule out of preview.

Examples

Basic

# Avoid
@pytest.fixture(autouse=True)
def my_fixture():
    ...

# Prefer
@pytest.fixture()
def my_fixture():
    ...

def test_foo(my_fixture):
    ...

Advanced (Combining Fixtures)

Instead of using autouse=True to avoid repetitive declarations in test parameters, multiple related fixtures can be aggregated into a single high-level fixture and requested explicitly:

# Avoid
@pytest.fixture(autouse=True)
def db():
    return Database()

@pytest.fixture(autouse=True)
def cache():
    return Cache()

@pytest.fixture(autouse=True)
def mock_email_client():
    return MockEmailClient()

def test_user_creation():
    ...

# Prefer
@pytest.fixture
def db():
    return Database()

@pytest.fixture
def cache():
    return Cache()

@pytest.fixture
def mock_email_client():
    return MockEmailClient()

# Combine related dependencies into a single high-level fixture
@pytest.fixture
def app_context(db, cache, mock_email_client):
    return AppContext(db=db, cache=cache, email=mock_email_client)

# Declare only the combining fixture in the test
def test_user_creation(app_context):
    ...

Future Enhancements

Developer TODO comments have been added to the checker function to consider adding settings for:

  1. Only flagging this rule when the fixture is defined inside a conftest.py file.
  2. Only flagging this rule when the fixture has a specific scope (e.g. function scope, which is the default and typically the most costly), supporting a whitelist or blacklist approach.

References

Test Plan

Added comprehensive test cases in a new test fixture file RUF076.py. The rule has been registered under preview_rules in crates/ruff_linter/src/rules/ruff/mod.rs and verified using snapshot testing:

CARGO_PROFILE_DEV_OPT_LEVEL=1 INSTA_FORCE_PASS=1 INSTA_UPDATE=always CARGO_PROFILE_DEV_DEBUG="line-tables-only" MDTEST_UPDATE_SNAPSHOTS=1 cargo test -p ruff_linter --rules pytest_fixture_autouse

We also ran:

  • cargo dev generate-all to generate updated linter schemas and documentation.
  • cargo clippy -p ruff_linter --all-targets --all-features -- -D warnings to verify code quality.
  • uvx prek on all changed files to ensure compliance with the repository's formatting/linting hooks.

next release will be 0.15.16 not 0.15.15
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.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 great to me overall, I just had a few minor suggestions. I'll also approve the CI workflow so we can get a sense for the ecosystem impact. I'm kind of expecting a lot of diagnostics.

Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/resources/test/fixtures/ruff/RUF076.py Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs
@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Jun 1, 2026
Kilo59 and others added 4 commits June 2, 2026 21:11
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Centralizes the logic for identifying `pytest.fixture` decorators into a single helper function within `flake8_pytest_style`. This allows other rules, like `RUF076`, to consume the same helper, reducing code duplication.
Migrates the `pytest-fixture-autouse` (`RUF076`) test to the markdown format
for improved documentation and snapshot testing.

Updates the rule's documentation to clarify its pedantic nature and provide
guidance on using `per-file-ignores` for targeted application,
especially for `conftest.py` files.
@Kilo59
Kilo59 requested a review from ntBre June 3, 2026 01:44
@astral-sh-bot

astral-sh-bot Bot commented Jun 4, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

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

RasaHQ/rasa (+6 -0 violations, +0 -0 fixes)

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

+ tests/conftest.py:823:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:837:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:881:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_telemetry.py:27:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_telemetry.py:37:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/utils/test_common.py:29:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

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

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

+ airflow-core/tests/conftest.py:166:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/system/conftest.py:34:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/always/test_providers_manager.py:346:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/always/test_providers_manager.py:93:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api/common/test_trigger_dag.py:37:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/auth/test_tokens.py:329:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/common/db/test_dags.py:45:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/common/test_dagbag.py:47:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/common/test_exceptions.py:109:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_asset_store.py:56:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:1459:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:247:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py:704:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
... 511 additional changes omitted for project

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

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

+ tests/integration_tests/celery_tests.py:70:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/charts/api_tests.py:84:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/charts/data/api_tests.py:91:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/conftest.py:119:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dao/base_dao_test.py:71:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dao/conftest.py:46:33: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dashboards/filter_state/api_tests.py:56:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/dashboards/update_tabs_test.py:39:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/explore/api_tests.py:65:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/integration_tests/explore/form_data/api_tests.py:67:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
... 51 additional changes omitted for project

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

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

+ tests/test_examples.py:114:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

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

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

+ ibis/backends/flink/tests/conftest.py:201:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ ibis/backends/pyspark/tests/conftest.py:407:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ ibis/backends/pyspark/tests/conftest.py:444:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ ibis/backends/pyspark/tests/conftest.py:475:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ ibis/conftest.py:25:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

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

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

+ libs/core/tests/unit_tests/conftest.py:12:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/language_models/chat_models/test_rate_limiting.py:12:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/load/test_secret_injection.py:35:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/core/tests/unit_tests/runnables/test_tracing_interops.py:303:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/langchain_v1/tests/unit_tests/conftest.py:20:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/partners/fireworks/tests/integration_tests/conftest.py:35:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/partners/fireworks/tests/unit_tests/test_chat_models.py:629:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/partners/openai/tests/unit_tests/chat_models/test_client_utils.py:27:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/partners/openrouter/tests/unit_tests/test_chat_models.py:760:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ libs/standard-tests/langchain_tests/_langsmith_plugin.py:87:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
... 2 additional changes omitted for project

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

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

+ tests/conftest.py:80:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/unit/test_fiat_providers.py:60:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

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

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

+ tests/orm/conftest.py:83:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_async_milvus_client.py:23:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_connection_manager.py:102:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_milvus_client.py:28:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/test_repro_issues.py:26:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

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

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

+ pandas/conftest.py:279:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/conftest.py:288:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/arithmetic/test_numeric.py:33:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/config/test_config.py:13:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/extension/base/dim2.py:24:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/extension/base/setitem.py:40:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/extension/test_masked.py:167:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/extension/test_string.py:287:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/frame/test_arithmetic.py:50:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ pandas/tests/frame/test_query_eval.py:1634:21: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
... 6 additional changes omitted for project

pypa/build (+4 -0 violations, +0 -0 fixes)

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

+ tests/conftest.py:102:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:109:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:115:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:94:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

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

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

+ test/conftest.py:127:34: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ unit_test/main_tests/conftest.py:34:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ unit_test/main_tests/conftest.py:58:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ unit_test/main_tests/conftest.py:63:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ unit_test/main_tests/main_requires_python_test.py:18:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators

python-poetry/poetry (+27 -0 violations, +0 -0 fixes)

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

+ tests/conftest.py:234:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:265:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:271:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:279:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:298:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:309:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:464:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/conftest.py:500:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/console/commands/debug/test_resolve.py:23:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
+ tests/console/commands/env/test_info.py:20:17: RUF076 Avoid using `autouse=True` in `pytest.fixture` decorators
... 17 additional changes omitted for project

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

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
RUF076 1919 1919 0 0 0

@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.

Thank you! I had a few even smaller docs nits, and CI is flagging a docs formatting issue, but I think this is otherwise ready to go.

Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
Comment thread crates/ruff_linter/src/rules/ruff/rules/pytest_fixture_autouse.rs Outdated
@ntBre ntBre changed the title [ruff] Ban pytest autouse fixtures (RUF076) [ruff] Ban pytest autouse fixtures (RUF076) Jun 4, 2026
Kilo59 and others added 2 commits June 4, 2026 16:21
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
@Kilo59
Kilo59 requested a review from ntBre June 4, 2026 20:24

@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.

Thank you!

@ntBre
ntBre merged commit 43fefba into astral-sh:main Jun 4, 2026
45 checks passed
@Kilo59
Kilo59 deleted the ban-pytest-fixture-autouse branch June 4, 2026 22:05
ntBre added a commit that referenced this pull request Jun 23, 2026
ntBre added a commit that referenced this pull request Jun 25, 2026
Summary
--

This PR removes `RUF076` that was added in #25477 and closes #26178. It
should also reopen #12491.

The reason for the removal is that this rule is very opinionated. I knew
that going in, but I failed
to consider the effect of adding a pedantic rule to the `RUF` selector,
which many preview users
enable as a whole.

The implementation was okay otherwise and can be reinstated once we have
a better category for the
rule, which #1774 should help with.

We initially opted for a full revert, but marking the rule as removed
both avoids an invalid configuration error for projects that already
`ignore` the rule (although they will still see a warning) and prevents
another rule in the future from reusing the code.

I don't believe we've done this before, but we can also reinstate the
rule simply by switching its metadata back from `removed_since` to
`preview_since` (and updating the mdtests, which I also opted to
preserve).
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 request - ban pytest.fixture(autouse=True)

2 participants