Skip to content

Lint

Static checks for the rules of hooks. pn lint is a thin wrapper around this module: lint_paths finds and checks the files, and lint_source checks one module's text. Both return Finding objects, so you can run the same checks from a test or an editor integration.

from pythonnative.lint import lint_paths

for finding in lint_paths(["app"]):
    print(finding.format())

See the Linting guide for what each rule catches and how to suppress a finding.

Static checks for the rules of hooks, behind pn lint.

The linter parses Python source with the standard library's ast module, so it has no dependencies and never imports or runs the code it checks. It reports four rules:

  • PN101: a hook is called conditionally: inside an if, a loop, a try, a match, a comprehension, a lambda, a conditional expression, the right side of and/or, a nested function, or after an early return.
  • PN102: a hook is called outside a @component function or a use_* custom hook (including at module level).
  • PN103: an effect, memo, or callback reads a local name that its literal dependency list leaves out.
  • PN104: key= is passed to a module-level @component; use .with_key(...) on the returned element instead.

A syntax error is reported as PN000. A trailing # pn: ignore comment silences every finding on its line, and # pn: ignore[PN101,PN103] silences only the listed codes.

Example
from pythonnative.lint import lint_source

for finding in lint_source(source, "app/main.py"):
    print(finding.format())

Classes:

Name Description
Finding

One problem found by the linter.

Functions:

Name Description
lint_source

Lint one module's source text.

lint_paths

Lint every Python file under paths.

Attributes:

Name Type Description
CODES dict[str, str]

Every code pn lint can report, with a one-line summary.

CODES module-attribute

CODES: dict[str, str] = {
    "PN000": "the file could not be parsed",
    "PN101": "hook called conditionally, in a loop, or in a nested function",
    "PN102": "hook called outside a component or custom hook",
    "PN103": "dependency list is missing a name the callback reads",
    "PN104": "key= passed to a user component",
}

Every code pn lint can report, with a one-line summary.

Finding dataclass

Finding(
    path: str, line: int, col: int, code: str, message: str
)

One problem found by the linter.

Attributes:

Name Type Description
path str

The file the finding is in, as it was passed to the linter.

line int

1-based line number.

col int

1-based column number.

code str

Rule code, such as "PN101".

message str

Human-readable explanation.

Methods:

Name Description
format

Return the finding as path:line:col: CODE message.

to_dict

Return the finding as a JSON-serializable dictionary.

format

format() -> str

Return the finding as path:line:col: CODE message.

to_dict

to_dict() -> dict[str, Any]

Return the finding as a JSON-serializable dictionary.

lint_source

lint_source(source: str, filename: str) -> list[Finding]

Lint one module's source text.

Parameters:

Name Type Description Default
source str

The Python source to check.

required
filename str

The path reported in each finding. The file isn't read.

required

Returns:

Type Description
list[Finding]

The findings, sorted by position, with suppressed ones removed.

list[Finding]

A syntax error yields a single PN000 finding.

lint_paths

lint_paths(
    paths: Iterable[str | PathLike[str]],
) -> list[Finding]

Lint every Python file under paths.

Directories are searched recursively for *.py files, skipping .git, build, dist, .venv*, __pycache__, node_modules, and site. A file named directly is linted whatever its suffix.

Parameters:

Name Type Description Default
paths Iterable[str | PathLike[str]]

Files and directories to check.

required

Returns:

Type Description
list[Finding]

All findings, ordered by path and then by position.

Raises:

Type Description
FileNotFoundError

If one of paths doesn't exist.

Next steps

  • Run the checks from the command line with pn lint: see CLI (pn).
  • Read why the rules exist in Hooks.