Skip to main content

Structural Python search and lightweight AST-based rule checking

Project description

pyastq

pyastq is a structural Python searcher and lightweight rule runner. It searches the Python AST rather than raw text, making it suitable for local scripts, pre-commit hooks, and CI checks.

Build

cargo build --release

The binary is written to target/release/pyastq.

Install

pyastq is a Rust executable distributed as the pyastq Python package. Install it as an isolated command-line tool:

uv tool install pyastq
# or
pipx install pyastq

For local development:

uv tool install .
pyastq --help

The PyPI package is only a distribution mechanism for the executable; it does not provide an importable Python module.

Versions

Release tags are the source of truth for published package versions. To publish a new release:

  1. Tag the commit, for example git tag v0.2.0.
  2. Push the tag with git push origin v0.2.0.

The release workflow validates the tag, changes the package version in its temporary checkout, builds wheels for Linux, macOS, and Windows, publishes them to PyPI, and creates a GitHub release. Cargo.toml may therefore still show the development version from the tagged commit.

An existing tag can be released from GitHub Actions by running the Release workflow manually and entering the tag.

PyPI retains previously published versions, so users can select one explicitly:

uv tool install 'pyastq==0.1.0'
pipx install 'pyastq==0.1.0'

Uploading another build with the same version is not a replacement mechanism. Fixes require a new version such as 0.1.1.

Structural Search

pyastq find src 'call:eval'
pyastq find src 'class:* -> function:regex:^[A-Z]'
pyastq find src 'call:request AND argument:timeout:>30'
pyastq find src 'function:* AND descendant(call:open) AND NOT descendant(call:close)'
pyastq find src 'call:print AND ancestor(function:*)'

The first pattern is the node reported as the finding. Conditions inspect its structure:

  • pattern and descendant(pattern) search all nested nodes.
  • child(pattern) searches direct AST children.
  • ancestor(pattern) and inside(pattern) search enclosing nodes.
  • -> is shorthand for AND descendant.
  • AND, OR, NOT, and parentheses compose conditions.

Supported node patterns:

call:<value>
class:<value>
function:<value>
import:<value>
argument:<key>:<value>

Argument keys are keyword names, zero-based positional indexes, or *:

argument:timeout:30
argument:0:"input.txt"
argument:*:None

Value predicates:

*                         any value
User                      exact value
exact:User                exact value
contains:User             substring
starts_with:test_         prefix
ends_with:_unsafe         suffix
regex:^[A-Z]              regular expression
>3  >=3  <10  <=10        numeric comparison

Quote a complete query when invoking it from a shell. Quotes inside a value allow spaces, for example argument:0:"hello world".

Automation

find returns 0 unless parsing or execution fails. Use --fail-on-match to make matches return 1:

pyastq find . 'call:eval' --fail-on-match --quiet

Exit codes:

  • 0: successful and clean
  • 1: findings were detected when failure-on-match applies
  • 2: invalid query, configuration, or execution error

Output and filtering options:

pyastq find . 'call:eval' --format json
pyastq find . 'call:eval' --format jsonl
pyastq find . 'call:eval' --format sarif
pyastq find . 'call:eval' --include 'src/**/*.py' --exclude '**/generated/**'
pyastq find . 'call:eval' --changed --max-matches 10
pyastq find . 'call:eval' --no-cache

--changed includes staged, unstaged, and untracked Python files reported by Git.

Directory searches store one content hash per file and findings per query or rule in .pyastq-cache.json. Unchanged files reuse cached findings. Changed files are read, hashed, and parsed once, then all applicable rules run against the same syntax tree. Use --no-cache to force a full scan. Cache failures fall back to a full scan, and --changed or --max-matches searches do not use the cache.

Rule Files

Rules use TOML. See pyastq.example.toml.

exclude = ["**/generated/**"]

[[rules]]
id = "no-eval"
query = "call:eval"
message = "Avoid eval(); parse the expected input explicitly."
severity = "error"
include = ["src/**/*.py"]
valid = ["parse(value)"]
invalid = ["eval(value)"]

[[rules]]
id = "method-name-case"
query = "class:* -> function:regex:^[A-Z]"
message = "Method names must start with a lowercase letter."
severity = "warning"

Run rules:

pyastq check . --rules pyastq.toml
pyastq check . --rules pyastq.toml --format json --changed
pyastq test-rules --rules pyastq.toml

check returns 1 when any rule matches. test-rules verifies that each valid example does not match and each invalid example does.

Rules can also live in pyproject.toml:

[tool.pyastq]
exclude = ["generated/**", "migrations/**"]

[[tool.pyastq.rules]]
id = "no-eval"
query = "call:eval"
message = "Avoid eval(); parse the expected input explicitly."
severity = "error"
valid = ["parse(value)"]
invalid = ["eval(value)"]

Alternatively, reference a standalone rule file:

[tool.pyastq]
rules-file = "config/pyastq.toml"
exclude = ["build/**"]

rules-file is resolved relative to pyproject.toml. External and inline rules may be used together: external rules are loaded first, inline rules are appended, and exclusions from both configurations are combined. Rule IDs must remain unique across both sources.

When --rules is omitted, check searches from the analyzed path toward the filesystem root for a pyproject.toml containing [tool.pyastq]:

pyastq check .
pyastq test-rules

Passing --rules continues to support standalone rule files and explicit pyproject.toml files:

pyastq check . --rules pyastq.toml
pyastq check . --rules pyproject.toml

SARIF 2.1.0 output is suitable for code-scanning systems:

pyastq check . --format sarif > pyastq.sarif

Suppressions

Suppress one line, the following line, or an entire file:

eval(value)  # pyastq: ignore no-eval

# pyastq: ignore no-eval
eval(value)

# pyastq: ignore-file no-eval

Omitting the rule ID suppresses every rule at that location. Multiple IDs can be separated by spaces or commas.

Pre-commit Example

repos:
  - repo: local
    hooks:
      - id: pyastq
        name: pyastq structural rules
        entry: target/release/pyastq check . --rules pyastq.toml --changed
        language: system
        pass_filenames: false

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyastq-0.1.2-py3-none-win_amd64.whl (1.4 MB view details)

Uploaded Python 3Windows x86-64

pyastq-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

pyastq-0.1.2-py3-none-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

pyastq-0.1.2-py3-none-macosx_10_12_x86_64.whl (1.4 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file pyastq-0.1.2-py3-none-win_amd64.whl.

File metadata

  • Download URL: pyastq-0.1.2-py3-none-win_amd64.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyastq-0.1.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 4f04d054f206bd1686282645c42a29b9803fd6e78e1a13922d9be90db10fac44
MD5 7be77b28f953143999808858d564c9bc
BLAKE2b-256 ed1525d42c0d32d97417fbce75fa6e6cca479484acbad35b9e78d79c8d21cc90

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyastq-0.1.2-py3-none-win_amd64.whl:

Publisher: release.yml on PetarMishov/pyastq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyastq-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyastq-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2525a75eec329bcff09a519a13e556692d1b7d9e672f6b2282b516f90b80f398
MD5 48cd3a2f0ecdbbb450fb6d77a925cc2a
BLAKE2b-256 57c1abe9aa567b1de0779bc6cde86338c498c1763510507fda2414c448e5f73a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyastq-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on PetarMishov/pyastq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyastq-0.1.2-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyastq-0.1.2-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6600e221271278aeba58f2dd95f7c7a5fd2c20f735e0cde20c824e0011fd3e92
MD5 cf7709c7b5fc5069d94c5686e82064cf
BLAKE2b-256 a5d937c5045b176d258b4cec13aea0097c6cbddc0242832153e8b9810d8f9b61

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyastq-0.1.2-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on PetarMishov/pyastq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyastq-0.1.2-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pyastq-0.1.2-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 ed61b3cc8c8fb2d91974863ad31c607e0503d0c80b73ccf02e037d1f7ce7fd7f
MD5 45a4cb0e25f957c361bbc051ecb4ca3c
BLAKE2b-256 049d3a3b0522c5d77ff78c9929ffe11b5ba2b587dc24ff6a44be5b2a6590ed7b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyastq-0.1.2-py3-none-macosx_10_12_x86_64.whl:

Publisher: release.yml on PetarMishov/pyastq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page