Skip to main content

Pylint Plugin

CI Python CI ruff pylint mypy PyPI python Topics license

Opinionated pylint checkers that encode review preferences beyond ruff, self-linting this repository.

Table of Contents

About

Ruff with lint.select = ["ALL"] covers lint hygiene but leaves review-time preferences unenforced: no docstrings on functions, mandatory AAA section markers inside tests, Final annotations on module-level constants, and similar project-specific rules. Restating these in code review is repetitive and lossy.

This plugin encodes those preferences as 14 pylint checkers and runs them against its own source tree. make pylint self-lints the repository with the same rules, so a regression in the codebase fails the same gate that defines the rule.

Key features

  • 14 enforced checkers: docstrings, AAA test markers, blank-line discipline, imports, naming, Final annotations, frozenset constants, and contextlib.suppress.
  • Test-scoped rules: the gajaguar-test-* checkers activate only on files under tests/, files matching test_*.py or *_test.py, or any parent named test or tests. Production code paths stay unaffected.
  • Configurable section markers: tune the AAA markers via pylint option or environment variable without editing the plugin source.
  • One runtime dependency: the wheel requires only pylint (pylint>=4.0).
  • Self-linting: the plugin runs against itself in CI and in pre-commit; a rule violation fails the same gate that defines it.

Requirements

Python 3.14 or higher — the package declares requires-python = ">=3.14". Install uv for dependency management and virtual environments. Install pnpm (or another Node package manager) only if you plan to run the Markdown lint and spell targets.

Usage

Run pylint with the plugin against your source tree:

uv run pylint --load-plugins=pylint_gajaguar \
  --disable=all --enable=gajaguar src tests

The pylint_gajaguar argument is the module the wheel installs (see Architecture). The --disable=all flag is deliberate: pylint's built-in rules overlap with ruff (line length, import placement), and missing-module-docstring conflicts with gajaguar-no-docstrings. --enable=gajaguar selects only the plugin's own rules: every checker registers under the single name gajaguar, so rules added in a later release are enabled without touching your configuration.

To run a subset, enable rules by message name instead, for example --enable=gajaguar-no-docstrings,gajaguar-require-final.

For the local development loop in this repository:

make check   # read-only gate: lint, format, mypy, pyright, md-lint, spell, pylint
make fix     # apply safe auto-fixes
make test    # run the test suite
make help    # list every target

Scope a target to specific files:

make lint FILES="src/pylint_gajaguar/scopes.py"
make pylint FILES="src"

Override the AAA section markers without editing config:

TEST_SECTION_MARKERS="Given When Then" make pylint

Getting started

Use in another project

Install the plugin from PyPI as a dev dependency:

uv add --dev pylint-gajaguar

or with pip:

pip install pylint-gajaguar

Then run pylint with the plugin loaded (see Usage for the --enable=gajaguar command).

Develop this repository

Clone the repository and install everything (toolchain, Python deps, Node toolchain, git hook):

git clone https://github.com/gajaguar/pylint-gajaguar.git
cd pylint-gajaguar
make install

Architecture

pylint --load-plugins=pylint_gajaguar
  -> pylint_gajaguar/__init__.py (re-exports register)
    -> pylint_gajaguar/_register.py: register(linter)
      -> instantiates and registers each Checker class
         -> pylint messages
scopes.py -> shared section-marker option + test-scoping helpers,
             consumed by the gajaguar-test-* checkers

The wheel ships a single top-level package, pylint_gajaguar: hatchling finds src/pylint_gajaguar/ from the project name and installs it as pylint_gajaguar, without the src/ prefix. That is why --load-plugins=pylint_gajaguar resolves: pylint calls register from the package's __init__.py.

pylint_gajaguar/__init__.py re-exports register from _register.py, which instantiates and registers each checker with the pylint linter. scopes.py provides the shared section-marker option and test-scoping helpers that the gajaguar-test-* checkers consume.

Built with

Configuration

Rule reference

gajaguar-smoke registers with pylint but carries no messages; it exists to verify the registration wiring. The remaining 14 rules each carry a message code and report violations.

Rule (name) Code Enforces
gajaguar-no-docstrings W9001 No docstrings on functions, methods, or classes (use comments)
gajaguar-test-aaa-markers W9002 test_* bodies contain # Arrange, # Act, # Assert
gajaguar-test-no-blank-lines W9003 No blank lines inside test method bodies
gajaguar-unused-arg-use-del W9004 Use del arg at body top, not a _-prefixed arg
gajaguar-module-const-naming C9005 Module-level names are SCREAMING_SNAKE_CASE
gajaguar-no-file-level-disable W9006 No standalone # pylint: disable=; use inline / disable-next
gajaguar-no-inline-imports W9008 Imports at module top, not inside functions
gajaguar-no-relative-imports W9009 Absolute imports only
gajaguar-use-contextlib-suppress W9012 contextlib.suppress(...) over try/except/pass
gajaguar-frozenset-constant W9013 Module-level set constants use frozenset(...)
gajaguar-require-final C9014 Module-level constants carry a Final annotation
gajaguar-test-no-extra-comments W9015 Test bodies carry only the configured section markers
gajaguar-test-partial-assertion W9016 Field assertions without a whole-object assertion (advisory)
gajaguar-test-name-implementation-detail W9017 Test names naming mocks, patches, internals (advisory)

gajaguar-test-partial-assertion and gajaguar-test-name-implementation-detail use heuristics with a measurable false-positive rate; treat their output as advisory, not a hard gate.

Options

Option Type Default Description
--test-section-markers csv Arrange,Act,Assert Section names a test body must carry, in order, without their leading # comment marker.
TEST_SECTION_MARKERS env var (unset) Space-separated section names. Overrides --test-section-markers when set.

Resolution order: TEST_SECTION_MARKERS (env) → --test-section-markers (linter config) → the default tuple. Set the env var to experiment with markers without rewriting the linter config.

In pyproject.toml the option lives under the checker name:

[tool.pylint.gajaguar]
test-section-markers = ["Given", "When", "Then"]

Test scoping

The gajaguar-test-* rules activate only on files matching one of these:

  • Stem starts with test_ (e.g. test_module.py)
  • Stem ends with _test (e.g. module_test.py)
  • Any parent directory is named test or tests

Files outside those paths are skipped entirely. Function-scoping applies on top: a test_* function inside a test file is checked; a helper function in the same file is not.

Make variables

Variable Behavior Examples
FILES Scope by path FILES="src/pylint_gajaguar/scopes.py" or FILES="src/pylint_gajaguar/*.py"
check* Read-only gate make check, make lint, make pylint, make mypy, make md-lint, make spell
fix* Mutates in place make fix, make lint-fix, make format, make md-fix

Contributing

Contributions optimize this plugin. Fork the repository, create a feature branch, commit your change, push, and open a Pull Request.

See CONTRIBUTING.md for the local setup, the check vs fix convention, and the three-file procedure for adding a new checker.

Security

Report vulnerabilities privately by email to dev@gajaguar.com. Do not open a public issue, pull request, or discussion.

See SECURITY.md for supported versions and the reporting process.

Open items

  • Dynamic checker discovery to remove the three-file registration step.
  • CHANGELOG.md.

License

Distributed under the MIT License. See LICENSE for the full text.

Metadata

Release files for pylint-gajaguar 2.0.0

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

Source distribution (sdist)

Source distribution for pylint-gajaguar 2.0.0
File Size Uploaded
pylint_gajaguar-2.0.0.tar.gz 87.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pylint-gajaguar 2.0.0
File Interpreter ABI Platform
pylint_gajaguar-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 107.9 kB

Release files / pylint_gajaguar-2.0.0.tar.gz

Download URL pylint_gajaguar-2.0.0.tar.gz
Size 87.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f706aabbc311b87290315edefc22d74dd05c05ca2f16fef95e3ab3d0a9a0da52
BLAKE2b-256 checksum
How to use checksums
88b5466c4d0fbfdd0e2521de5c9d921d22c7b864423c2aebe9f0e8c2fde74907
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 29, 2026.

Transparency log

Release files / pylint_gajaguar-2.0.0-py3-none-any.whl

Download URL pylint_gajaguar-2.0.0-py3-none-any.whl
Size 20.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3b6c098ebcfd6d0fe78ab4e82c09bc337d5d5cae48e45bea7375274fa2f0642
BLAKE2b-256 checksum
How to use checksums
b1494ee594c712e5e3a47cbd6dc92a3c0ca9aec47aa0df1f387655b4db394234
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 29, 2026.

Transparency log

Release history Release notifications | RSS feed

2.0.1

2 release files

This release

2.0.0 This release

2 release files

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