Skip to main content

assert-pytest-class-holds-state

Assert that every pytest class holds a fixture, an attribute, a helper or a base.

Why Must a Class Hold Something?

A pytest class earns its place three ways. It holds a fixture, whose scope is then the class. It holds an attribute or a helper that its methods reach through self. Or it derives from a shared suite, collecting one set of tests for many subjects.

A class that does none of those is a namespace. It costs an unused self on every signature, a level of indentation on every body, a prefix on every node ID, and it suggests a setup that is not there. The methods belong at module level, where pytest collects them just as readily.

no-self-use is the mechanical signal and the wrong instrument. It reports on a method that happens not to touch self, so it flags an autouse fixture sitting in the class that is its scope, and it flags a kept class's one stateless method. This tool asks what the class is for instead, so a class that holds anything at all is silent rather than waived.

Installation

pip install assert-pytest-class-holds-state

Usage

Command Line

# Scan specific files
assert-pytest-class-holds-state test_example.py

# Scan directories recursively
assert-pytest-class-holds-state test/

# Use glob patterns
assert-pytest-class-holds-state "test/**/test_*.py"

# Exclude patterns
assert-pytest-class-holds-state test/ --exclude "**/conftest.py"

# Verbose output
assert-pytest-class-holds-state test/ --verbose

# Fail fast (exit on first finding)
assert-pytest-class-holds-state test/ --fail-fast

# Warn only (always exit 0)
assert-pytest-class-holds-state test/ --warn-only

GitHub Actions

- uses: 10U-Labs/assert-pytest-class-holds-state@latest
  with:
    files: "test/"
    verbose: "true"

As a Python Module

python -m assert_pytest_class_holds_state test/

Output Format

Default output shows one finding per line:

path/to/test_file.py:10:TestExample: the test methods belong at module level

Format: file_path:line_number:class_name, followed by the reason. The line number is the class statement.

Exit Codes

  • 0: No findings (or --warn-only specified)
  • 1: Findings detected
  • 2: Error (missing files, syntax errors, etc.)

What Counts as Holding Something?

A class whose name begins with Test is refused only when all four of these are true:

  • it has no base and no class keyword
  • it carries no decorator
  • its body holds no assignment
  • every function in its body is a test — a name beginning with test that no pytest.fixture decorator files as a fixture

Anything else passes. An inheritance stub passes on its base, even with an empty body. A fixture holder passes on the fixture, autouse or not. A class holding a @staticmethod helper, a plain helper, or a setup_method passes on the non-test function. A class carrying @pytest.mark.integration passes on the decorator, and one carrying region = "us-east-2" passes on the assignment.

A decorator on a method is not a decorator on the class, so @pytest.mark.parametrize on every test does not save a namespace.

What Is Never Examined

A class defined inside a function is a suite the function produces, not a namespace someone wrote around loose methods. Its methods close over the function's arguments and cannot move to module level, and pytest never collects it until another module inherits from it. The tool does not descend into function bodies at all.

Only files pytest would collect are scanned: test_*.py and *_test.py.

Options

Option Description
--exclude PATTERNS Glob patterns to exclude (comma-separated)
--quiet Suppress all output (exit code only)
--count Output only the number of findings
--verbose Show detailed processing information
--fail-fast Exit after first finding
--warn-only Always exit 0, even with findings

License

Apache 2.0 - See LICENSE.txt

Metadata

Release files for assert-pytest-class-holds-state 20260907154656

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for assert-pytest-class-holds-state 20260907154656
File Size Uploaded Provenance
assert_pytest_class_holds_state-20260907154656.tar.gz 26.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for assert-pytest-class-holds-state 20260907154656
File Interpreter ABI Platform
assert_pytest_class_holds_state-20260907154656-py3-none-any.whl Python 3 none any Details

Total release size: 38.0 kB

Release files / assert_pytest_class_holds_state-20260907154656.tar.gz

Download URL assert_pytest_class_holds_state-20260907154656.tar.gz
Size 26.3 kB
Tags Source
SHA-256 checksum
How to use checksums
ab789107109d775e992e5e60da1200fbfc8c3c8a24e8ba3013111562bd60d10c
BLAKE2b-256 checksum
How to use checksums
266226cf73fdd573a3c88a70b0fc933f3680a5ee2b8068224ddf1e5f7bf46857
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / assert_pytest_class_holds_state-20260907154656-py3-none-any.whl

Download URL assert_pytest_class_holds_state-20260907154656-py3-none-any.whl
Size 11.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9947a24be4aac5bdf038898957fa146f5bd97ca07ec464f057b1fa4c2f2c4629
BLAKE2b-256 checksum
How to use checksums
33ad5549e6407411af8229944a64b5f9d4da211e87504f8092b72cc2f4688f7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Security for assert-pytest-class-holds-state 20260907154656

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for assert_pytest_class_holds_state-20260907154656.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page