mrkeyoor.com_
Sun 20 Sept 15:55 UTC
PyPICLI & Toolingupdated 20 Sept 2026

pylint review

Our Pylint 4.0.7 install gave us a typed, pure-Python command-line analyzer that infers what names can refer to through Astroid before reporting errors, suspicious behavior, design smells, and style violations. That inference finds problems in code with few type annotations, but it also makes a full run slower and more sensitive to dynamic framework behavior than syntax-only linters. Version 4.0.7 is a correction release: it fixes TypedDict instance names being treated as class names, stops several analyzer crashes, repairs unsafe `nested-min-max` suggestions, excludes PEP 695 type parameters from local-variable counts, and corrects several string-format and comparison findings.

Verdict

Pylint 4.0.7 installed in 0.3 seconds, used 5 MB, and imported in 0.07 seconds in our sandbox with 0 audit findings. Use it for inference checks that faster syntax linters miss, but pin it and choose a deliberate message set instead of treating every default warning as policy.

We installed it

Lab card: what happened when we installed pylintScreenshot of pylint documentation
Install✓ · 0.3s7 packages on disk · 5 MB
Importimport pylint in 0.07s · pure Python · py.typed · requires Python >=3.10.0
Known vulns0(pip-audit)

Answers from our run

Does pylint install cleanly?

Yes. In a fresh container with an empty cache, pip install pylint finished in 0.3s, leaving 7 packages and 5 MB on disk. pip-audit reported no known vulnerabilities.

What does pylint need to run?

Python >=3.10.0, and nothing compiled: it is pure Python. In our run import pylint succeeded in 0.07s, and the package ships py.typed for type checkers.

pylint or ruff: which should you use?

ruff: Choose it for very fast linting, broad rule coverage, formatting, and automatic fixes. Pylint 4.0.7 installed in 0.3 seconds, used 5 MB, and imported in 0.07 seconds in our sandbox with 0 audit findings.

When should you not use pylint?

Fast feedback is the priority: Pylint's README says inference makes it slower, while Ruff is built for the quick edit-and-fix loop

API stability3/5The `pylint` command, message IDs, category letters, inline pragmas, and main configuration mechanisms are established. CI behavior still changes when inference gets more accurate. Release 4.0.7 alters reports for TypedDict values, PEP 695 parameters, nested min/max calls, literal comparisons, and percent formatting. Those are fixes, yet each can change a repository's message count or score without an application code change.
Docs5/5The user guide explains installation, message control, configuration discovery, plugins, reporters, CI use, and adoption on existing projects. Individual message pages include examples and rationale, while each release has a hand-written list of false positives and crashes fixed. The README is unusually candid about inference cost and recommends Ruff, type checkers, security tools, formatters, and import organizers alongside Pylint rather than claiming one tool covers every job.
Maintenance5/5Pylint 4.0.7 was uploaded on 2026-08-09, the repository was pushed on 2026-08-24, and GitHub reports 1,085 open issues and pull requests. That queue is large, but the current release closes specific reports across naming, generics, context managers, formatting, comparisons, and analyzer crashes. Version 4.0.6 shipped less than two months earlier with another long set of false-positive and stability corrections.
Ecosystem5/5Pylint has 5,714 GitHub stars, millions of weekly PyPI downloads, editor integrations, pre-commit support, and third-party plugins for frameworks that generate attributes dynamically. It also includes `pyreverse` and `symilar`. Ruff now covers many everyday lint rules faster, but the two tools can coexist: Ruff handles immediate syntax-pattern feedback while Pylint contributes whole-program inference and custom checker APIs.

Use it if

  • An untyped or partly typed Python codebase needs inference-based checks such as no-member, used-before-assignment, and wrong call arguments
  • CI needs a bug-focused second pass after a faster formatter or linter
  • Your organization wants custom Python checkers with named messages and configuration
  • Class and package diagrams from the bundled `pyreverse` command would help document an inherited codebase
Skip it if

Setup reality

Our clean pylint==4.0.7 install completed in 0.3 seconds. It left 7 packages occupying 5 MB, and pip-audit found 0 known vulnerabilities. The measured package data reports 13 direct dependencies, Python 3.10.0 or newer, pure Python code, and an unknown license value. It ships a py.typed marker, and import pylint worked in 0.07 seconds. PyPI separately declares GPL-2.0-or-later, which matters if you import and redistribute its internals.

Install Pylint inside the same environment as the application. Astroid follows imports, so a global or isolated tool environment sees third-party modules as missing unless you reproduce those dependencies or provide stubs and plugins. This is a common pre-commit surprise: an isolated hook may flag every project import. A local language: system hook uses the active project environment, while additional_dependencies can populate an isolated hook at the cost of duplicating dependency declarations.

Configuration can live in pyproject.toml, .pylintrc, pylintrc, or other supported locations. A nearer file can win over the one you edited, so use --verbose or generate configuration when results look inexplicable. Start a previously unlinted repository with --errors-only, then enable warnings and selected refactor or convention checks. Pylint returns a bit-encoded nonzero status when messages are present; decide whether CI should use --fail-on, --fail-under, or --exit-zero instead of assuming every message deserves the same gate.

Whole-package analysis gives Astroid more context than separate file invocations. -j 0 uses available CPUs, but parallel workers and CI sharding can weaken cross-file inference or duplicate-code checks. Dynamic libraries may need plugins such as pylint-django or generated-member configuration. Version 4.0.7 reduces false reports for TypedDict instances and PEP 695 generics, but it does not make inference perfect. Keep suppressions narrow, name the message, and enable useless-suppression periodically so obsolete comments do not accumulate.

Patterns

Check only error-category messages run-errors-only

pylint --errors-only src/my_package

This is the practical first pass on an existing codebase. Pass the package directory so import and cross-module inference see more context than a list of isolated files.

Introduce warnings before style policy adopt-in-stages

# First deployment
pylint --errors-only src/my_package

# Add warnings while leaving convention and refactor checks off
pylint --disable=C,R src/my_package

Turn individual convention and refactor messages back on after the team agrees they catch something worth changing. A single all-default run often creates an ignored backlog.

Store a focused policy in pyproject.toml configure-pyproject

[tool.pylint.main]
py-version = "3.12"
jobs = 0
ignore-paths = ["^migrations/", "^tests/fixtures/"]

[tool.pylint."MESSAGES CONTROL"]
disable = ["missing-module-docstring", "too-few-public-methods"]

[tool.pylint.design]
max-args = 8

The quoted MESSAGES CONTROL section keeps its space. Check for a nearer `.pylintrc` if edits here appear to have no effect.

Limit a suppression to the line that needs it suppress-one-message

import optional_backend  # pylint: disable=import-error

def handler(event, context):  # pylint: disable=unused-argument
    return event['id']

Use symbolic names so reviewers can see the reason. A standalone disable inside a block continues through that block unless you enable it again.

Fail CI on selected message classes gate-specific-errors

pylint --disable=all --enable=E,F --fail-on=E,F src/my_package

--fail-on makes the named messages fatal even when another score setting would pass. Pin Pylint so an inference update cannot change the gate unexpectedly.

Require a minimum Pylint score gate-by-score

pylint --fail-under=9.0 src/my_package

The score changes as files and enabled messages change. A list of fatal message IDs is easier to reason about when the goal is preventing bugs rather than tracking style debt.

Write structured results for CI tooling emit-json-report

pylint --output-format=json2 src/my_package > pylint-report.json

json2 includes messages plus run statistics. Keep normal text or colorized output for local work where clickable paths and concise explanations are easier to scan.

Use detected CPU cores for a full run use-parallel-workers

pylint -j 0 src/my_package

Parallel analysis can change cross-file inference compared with a serial whole-package run. Keep the same worker policy locally and in CI when reproducible results matter.

Teach inference about a dynamic framework load-framework-plugin

pip install pylint-django
pylint --load-plugins=pylint_django src/my_project

Install the plugin in the same environment as Pylint. Put load-plugins in project configuration so editor, terminal, and CI runs do not disagree.

Register a small organization-specific checker write-custom-checker

from astroid import nodes
from pylint.checkers import BaseChecker

class NoPrintChecker(BaseChecker):
    name = 'no-print'
    msgs = {'W9001': ('Use the application logger', 'forbidden-print', 'Keep output in structured logs.')}

    def visit_call(self, node: nodes.Call) -> None:
        if isinstance(node.func, nodes.Name) and node.func.name == 'print':
            self.add_message('forbidden-print', node=node)

def register(linter) -> None:
    linter.register_checker(NoPrintChecker(linter))

Third-party checkers use message numbers in the 9000 range. The plugin module must be importable everywhere Pylint runs and must be listed in load-plugins.

Run Pylint in the project environment from pre-commit configure-pre-commit

repos:
  - repo: local
    hooks:
      - id: pylint
        name: pylint
        entry: pylint --errors-only
        language: system
        types: [python]
        require_serial: true

language: system lets Pylint see installed application dependencies. The tradeoff is that contributors must activate the correct environment before running pre-commit.

Draw package and class relationships with pyreverse generate-class-diagram

pyreverse -o svg -p billing src/billing
# produces classes_billing.svg and packages_billing.svg

pyreverse -o mmd -p billing src/billing

SVG output needs Graphviz installed. Mermaid output is text and can be embedded in compatible Markdown renderers without a local Graphviz executable.

Alternatives

PackageRegistryPick it when
ruffPyPIChoose it for very fast linting, broad rule coverage, formatting, and automatic fixes.
mypyPyPIChoose it when annotated type correctness is the main requirement rather than style and design checks.
flake8PyPIChoose it for a smaller AST-based checker with a familiar plugin model and less inference.

More cli & tooling guides

commander · chalk · typescript · esbuild · yargs · click · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.