fileaudit.python_check module

fileaudit.python_check module#

License MPL-2.0 (C) 2026 Created by Maikel Mardjan - https://nocomplexity.com/ FileAudit Security Checker - Checks whether a Python file is syntactically valid.

exception fileaudit.python_check.PythonValidationError(message)[source]#

Bases: Exception

Custom exception for Python/AST validation failures in FileAudit.

fileaudit.python_check.validate_python(func_or_path=None, max_file_size=None, max_lines=100000, max_line_length=10000, max_ast_nodes=500000, allowed_base_dir=None, allow_symlinks=False, parse_timeout=10)[source]#

Validate Python source files via decorator or direct call.

This function operates in two modes:

  • Direct call / CLI mode – When called with a file path (str or pathlib.Path), the file is validated immediately. Returns True on success.

    Raises : `PythonValidationError` on any validation failure. If an error occurs, an exception is raised. You can catch it or let it propagate depending on your needs.

  • Decorator mode – When used as @validate_python, @validate_python(), or @validate_python("arg_name"), the decorated function’s selected str/Path argument is validated before the function body executes. On failure a PythonValidationError is raised.

Existing public calling conventions are preserved.

Security checks performed (before and during AST parsing):

  • File existence and regular-file type

  • Symlink rejection (unless allow_symlinks=True)

  • Directory containment / path-traversal guard (via allowed_base_dir)

  • File size limit (checked before reading into memory)

  • Only .py extension is accepted

  • UTF-8 BOM detection and stripping

  • Strict UTF-8 decoding

  • Rejection of null bytes (\0)

  • Line count and per-line length limits

  • Safe ast.parse with explicit handling of SyntaxError, ValueError, MemoryError and RecursionError

  • Optional parse timeout via SIGALRM (Unix only)

  • Post-parse AST node count limit

Parameters:
  • func_or_path (callable, str, pathlib.Path, or None) –

    Determines the operating mode.

    • None – Returns a decorator factory (@validate_python() or with keyword arguments).

    • A callable – Bare decorator usage (@validate_python).

    • A str that looks like a file path, or a pathlib.Path – Direct validation of that file.

    • A str that is a valid Python identifier – Treated as the name of the argument to validate in decorator mode (e.g. @validate_python("source_path")).

  • max_file_size (int or None) – Maximum allowed file size in bytes. Falls back to DEFAULT_MAX_FILE_SIZE when omitted.

  • max_lines (int) – Maximum number of lines allowed in the source file.

  • max_line_length (int) – Maximum number of characters allowed per line.

  • max_ast_nodes (int) – Maximum number of AST nodes allowed after parsing.

  • allowed_base_dir (str, pathlib.Path, or None) – If set, the resolved path of the file must lie inside this directory (path-traversal protection).

  • allow_symlinks (bool) – If False (default), symbolic links are rejected.

  • parse_timeout (int) – Seconds allowed for ast.parse before aborting. Uses SIGALRM and is effective only on Unix-like systems. A value of 0 disables the timeout.

Returns:

  • In direct-call mode: True if validation succeeds.

  • In decorator mode: the wrapped function (or a decorator factory when func_or_path is None).

Return type:

Union[bool, callable]

Raises:
  • PythonValidationError – Raised in the following cases: * Validation of the target file fails (missing file, wrong type/extension, size/line/AST limits exceeded, encoding error, null byte, syntax error, timeout, etc.). * In decorator mode: the decorated function has no arguments, the designated target argument is missing, or the argument is not of type str or pathlib.Path. * The function signature cannot be inspected.

  • ValueError – Raised when an invalid value is supplied for allow_symlinks or allowed_base_dir.

Examples

Direct validation:

validate_python("path/to/file.py")
validate_python(Path("path/to/file.py"), max_file_size=5000)

Decorator usage:

@validate_python
def process(path):
    ...

@validate_python()
def process(path):
    ...

@validate_python("source")
def process(source, other):
    ...

@validate_python(max_lines=10_000, allow_symlinks=True)
def process(path):
    ...