Skip to content
rlsbl.lint.python_ast
On this page

Python linter using tree-sitter AST parsing to detect library boundary violations including forbidden imports and stdout/logging usage.

#rlsbl.lint.python_ast

#rlsbl.lint.python_ast

Python linter using tree-sitter AST parsing to detect library boundary violations including forbidden imports and stdout/logging usage.

#ImportRecord

Immutable record of a single Python import with location and context metadata.

#_make_parser

python
def _make_parser()

Create a tree-sitter parser configured for Python.

#_node_line

python
def _node_line(node)

Return 1-based line number for a tree-sitter node.

#_is_in_try_except_import_error

python
def _is_in_try_except_import_error(node)

Check if an import node is inside a try body protected by except ImportError/ModuleNotFoundError.

Walks up the parent chain looking for a try_statement ancestor where:

  1. The import is in the try body (not inside an except_clause's block)
  2. At least one except_clause catches ImportError or ModuleNotFoundError

Returns True if ANY ancestor try_statement satisfies both conditions.

#_try_catches_import_error

python
def _try_catches_import_error(try_node, error_names)

Check if a try_statement has an except_clause catching ImportError/ModuleNotFoundError.

#_is_in_type_checking_block

python
def _is_in_type_checking_block(node)

Check if an import node is inside an if TYPE_CHECKING: block.

Walks up the parent chain looking for an if_statement ancestor whose condition is:

  • An identifier node with text TYPE_CHECKING, OR
  • An attribute node whose last identifier is TYPE_CHECKING

(for typing.TYPE_CHECKING)

Returns True if found, False otherwise.

#_collect_all_imports

python
def _collect_all_imports(tree, filepath)

Walk AST and collect all imported module names with full paths.

Returns a set of ImportRecord with top_level, full_path, filepath, line, guarded, and type_checking fields. top_level is the first component of the dotted path (e.g., "orxt"). full_path is the complete dotted module path (e.g., "orxt.protocols"). Imports inside try/except ImportError blocks are marked guarded=True. Imports inside if TYPE_CHECKING: blocks are marked type_checking=True.

#_check_forbidden_imports

python
def _check_forbidden_imports(tree, filepath, config)

Walk AST for import_statement and import_from_statement nodes.

#child_by_field

python
def child_by_field(node, field_name)

Find the module name in an import_from_statement.

tree-sitter-python represents 'from foo import bar' with the module as a dotted_name or relative_import child before the 'import' keyword.

#_check_stdout

python
def _check_stdout(tree, filepath, config)

Detect print(), sys.stdout/stderr.write(), and logging.* calls.

#_check_entry_points

python
def _check_entry_points(project_path, config)

Check pyproject.toml for CLI entry point declarations.

#_always_terminates

python
def _always_terminates(node)

Return True if the given node unconditionally terminates its enclosing block.

A node always terminates if:

  • It is a return, raise, break, or continue statement.
  • It is a block whose last child always terminates.
  • It is an if_statement with an else_clause where the if body, all elif

bodies, and the else body all always terminate.

#_terminator_label

python
def _terminator_label(node)

Return a human-readable label for the terminating construct.

#_check_unreachable_code

python
def _check_unreachable_code(tree, filepath)

Detect unreachable statements in block nodes.

Walks all block nodes in the tree. Within each block, if a child statement unconditionally terminates (return, raise, break, continue, or exhaustive if/else with all branches terminating), any subsequent sibling statements are flagged as unreachable.

Skips nested function and class definitions -- a return inside a nested function does not make the outer code unreachable.

#PythonAstLinter

Python linter using tree-sitter AST analysis.

#lint

python
def lint(self, project_path: str, config: LanguageLintConfig) -> list[LintResult]

Run forbidden-import, stdout, and unreachable-code checks on Python files.

#scan_imports

python
def scan_imports(self, project_path: str, exclude_dirs: list[str] | None=None) -> set[ImportRecord]

Collect all imported module names from Python files.

Returns a set of ImportRecord with top_level, full_path, filepath, line, guarded, and type_checking fields. Guarded imports are those inside try/except ImportError blocks. TYPE_CHECKING imports are those inside if TYPE_CHECKING: blocks.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search