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-onlyspecified)1: Findings detected2: 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
testthat nopytest.fixturedecorator 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| assert_pytest_class_holds_state-20260907154656.tar.gz | 26.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.