pyastq 0.1.2__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
Binary file
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyastq
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Classifier: Environment :: Console
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Rust
|
|
7
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
8
|
+
Summary: Structural Python search and lightweight AST-based rule checking
|
|
9
|
+
Keywords: ast,cli,linting,python,structural-search
|
|
10
|
+
Author: Petar Mishov
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
13
|
+
Project-URL: Repository, https://github.com/PetarMishov/pyastq
|
|
14
|
+
|
|
15
|
+
# pyastq
|
|
16
|
+
|
|
17
|
+
`pyastq` is a structural Python searcher and lightweight rule runner. It searches
|
|
18
|
+
the Python AST rather than raw text, making it suitable for local scripts,
|
|
19
|
+
pre-commit hooks, and CI checks.
|
|
20
|
+
|
|
21
|
+
## Build
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
cargo build --release
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The binary is written to `target/release/pyastq`.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
`pyastq` is a Rust executable distributed as the `pyastq` Python package. Install
|
|
32
|
+
it as an isolated command-line tool:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
uv tool install pyastq
|
|
36
|
+
# or
|
|
37
|
+
pipx install pyastq
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For local development:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
uv tool install .
|
|
44
|
+
pyastq --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The PyPI package is only a distribution mechanism for the executable; it does
|
|
48
|
+
not provide an importable Python module.
|
|
49
|
+
|
|
50
|
+
### Versions
|
|
51
|
+
|
|
52
|
+
Release tags are the source of truth for published package versions. To publish
|
|
53
|
+
a new release:
|
|
54
|
+
|
|
55
|
+
1. Tag the commit, for example `git tag v0.2.0`.
|
|
56
|
+
2. Push the tag with `git push origin v0.2.0`.
|
|
57
|
+
|
|
58
|
+
The release workflow validates the tag, changes the package version in its
|
|
59
|
+
temporary checkout, builds wheels for Linux, macOS, and Windows, publishes them
|
|
60
|
+
to PyPI, and creates a GitHub release. `Cargo.toml` may therefore still show the
|
|
61
|
+
development version from the tagged commit.
|
|
62
|
+
|
|
63
|
+
An existing tag can be released from GitHub Actions by running the `Release`
|
|
64
|
+
workflow manually and entering the tag.
|
|
65
|
+
|
|
66
|
+
PyPI retains previously published versions, so users can select one explicitly:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
uv tool install 'pyastq==0.1.0'
|
|
70
|
+
pipx install 'pyastq==0.1.0'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Uploading another build with the same version is not a replacement mechanism.
|
|
74
|
+
Fixes require a new version such as `0.1.1`.
|
|
75
|
+
|
|
76
|
+
## Structural Search
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
pyastq find src 'call:eval'
|
|
80
|
+
pyastq find src 'class:* -> function:regex:^[A-Z]'
|
|
81
|
+
pyastq find src 'call:request AND argument:timeout:>30'
|
|
82
|
+
pyastq find src 'function:* AND descendant(call:open) AND NOT descendant(call:close)'
|
|
83
|
+
pyastq find src 'call:print AND ancestor(function:*)'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The first pattern is the node reported as the finding. Conditions inspect its
|
|
87
|
+
structure:
|
|
88
|
+
|
|
89
|
+
- `pattern` and `descendant(pattern)` search all nested nodes.
|
|
90
|
+
- `child(pattern)` searches direct AST children.
|
|
91
|
+
- `ancestor(pattern)` and `inside(pattern)` search enclosing nodes.
|
|
92
|
+
- `->` is shorthand for `AND descendant`.
|
|
93
|
+
- `AND`, `OR`, `NOT`, and parentheses compose conditions.
|
|
94
|
+
|
|
95
|
+
Supported node patterns:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
call:<value>
|
|
99
|
+
class:<value>
|
|
100
|
+
function:<value>
|
|
101
|
+
import:<value>
|
|
102
|
+
argument:<key>:<value>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Argument keys are keyword names, zero-based positional indexes, or `*`:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
argument:timeout:30
|
|
109
|
+
argument:0:"input.txt"
|
|
110
|
+
argument:*:None
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Value predicates:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
* any value
|
|
117
|
+
User exact value
|
|
118
|
+
exact:User exact value
|
|
119
|
+
contains:User substring
|
|
120
|
+
starts_with:test_ prefix
|
|
121
|
+
ends_with:_unsafe suffix
|
|
122
|
+
regex:^[A-Z] regular expression
|
|
123
|
+
>3 >=3 <10 <=10 numeric comparison
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Quote a complete query when invoking it from a shell. Quotes inside a value
|
|
127
|
+
allow spaces, for example `argument:0:"hello world"`.
|
|
128
|
+
|
|
129
|
+
## Automation
|
|
130
|
+
|
|
131
|
+
`find` returns `0` unless parsing or execution fails. Use `--fail-on-match` to
|
|
132
|
+
make matches return `1`:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
pyastq find . 'call:eval' --fail-on-match --quiet
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Exit codes:
|
|
139
|
+
|
|
140
|
+
- `0`: successful and clean
|
|
141
|
+
- `1`: findings were detected when failure-on-match applies
|
|
142
|
+
- `2`: invalid query, configuration, or execution error
|
|
143
|
+
|
|
144
|
+
Output and filtering options:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
pyastq find . 'call:eval' --format json
|
|
148
|
+
pyastq find . 'call:eval' --format jsonl
|
|
149
|
+
pyastq find . 'call:eval' --format sarif
|
|
150
|
+
pyastq find . 'call:eval' --include 'src/**/*.py' --exclude '**/generated/**'
|
|
151
|
+
pyastq find . 'call:eval' --changed --max-matches 10
|
|
152
|
+
pyastq find . 'call:eval' --no-cache
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`--changed` includes staged, unstaged, and untracked Python files reported by
|
|
156
|
+
Git.
|
|
157
|
+
|
|
158
|
+
Directory searches store one content hash per file and findings per query or
|
|
159
|
+
rule in `.pyastq-cache.json`. Unchanged files reuse cached findings. Changed files
|
|
160
|
+
are read, hashed, and parsed once, then all applicable rules run against the
|
|
161
|
+
same syntax tree. Use `--no-cache` to force a full scan. Cache failures fall
|
|
162
|
+
back to a full scan, and `--changed` or `--max-matches` searches do not use the
|
|
163
|
+
cache.
|
|
164
|
+
|
|
165
|
+
## Rule Files
|
|
166
|
+
|
|
167
|
+
Rules use TOML. See [`pyastq.example.toml`](pyastq.example.toml).
|
|
168
|
+
|
|
169
|
+
```toml
|
|
170
|
+
exclude = ["**/generated/**"]
|
|
171
|
+
|
|
172
|
+
[[rules]]
|
|
173
|
+
id = "no-eval"
|
|
174
|
+
query = "call:eval"
|
|
175
|
+
message = "Avoid eval(); parse the expected input explicitly."
|
|
176
|
+
severity = "error"
|
|
177
|
+
include = ["src/**/*.py"]
|
|
178
|
+
valid = ["parse(value)"]
|
|
179
|
+
invalid = ["eval(value)"]
|
|
180
|
+
|
|
181
|
+
[[rules]]
|
|
182
|
+
id = "method-name-case"
|
|
183
|
+
query = "class:* -> function:regex:^[A-Z]"
|
|
184
|
+
message = "Method names must start with a lowercase letter."
|
|
185
|
+
severity = "warning"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Run rules:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
pyastq check . --rules pyastq.toml
|
|
192
|
+
pyastq check . --rules pyastq.toml --format json --changed
|
|
193
|
+
pyastq test-rules --rules pyastq.toml
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`check` returns `1` when any rule matches. `test-rules` verifies that each
|
|
197
|
+
`valid` example does not match and each `invalid` example does.
|
|
198
|
+
|
|
199
|
+
Rules can also live in `pyproject.toml`:
|
|
200
|
+
|
|
201
|
+
```toml
|
|
202
|
+
[tool.pyastq]
|
|
203
|
+
exclude = ["generated/**", "migrations/**"]
|
|
204
|
+
|
|
205
|
+
[[tool.pyastq.rules]]
|
|
206
|
+
id = "no-eval"
|
|
207
|
+
query = "call:eval"
|
|
208
|
+
message = "Avoid eval(); parse the expected input explicitly."
|
|
209
|
+
severity = "error"
|
|
210
|
+
valid = ["parse(value)"]
|
|
211
|
+
invalid = ["eval(value)"]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Alternatively, reference a standalone rule file:
|
|
215
|
+
|
|
216
|
+
```toml
|
|
217
|
+
[tool.pyastq]
|
|
218
|
+
rules-file = "config/pyastq.toml"
|
|
219
|
+
exclude = ["build/**"]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`rules-file` is resolved relative to `pyproject.toml`. External and inline
|
|
223
|
+
rules may be used together: external rules are loaded first, inline rules are
|
|
224
|
+
appended, and exclusions from both configurations are combined. Rule IDs must
|
|
225
|
+
remain unique across both sources.
|
|
226
|
+
|
|
227
|
+
When `--rules` is omitted, `check` searches from the analyzed path toward the
|
|
228
|
+
filesystem root for a `pyproject.toml` containing `[tool.pyastq]`:
|
|
229
|
+
|
|
230
|
+
```sh
|
|
231
|
+
pyastq check .
|
|
232
|
+
pyastq test-rules
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Passing `--rules` continues to support standalone rule files and explicit
|
|
236
|
+
`pyproject.toml` files:
|
|
237
|
+
|
|
238
|
+
```sh
|
|
239
|
+
pyastq check . --rules pyastq.toml
|
|
240
|
+
pyastq check . --rules pyproject.toml
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
SARIF 2.1.0 output is suitable for code-scanning systems:
|
|
244
|
+
|
|
245
|
+
```sh
|
|
246
|
+
pyastq check . --format sarif > pyastq.sarif
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Suppressions
|
|
250
|
+
|
|
251
|
+
Suppress one line, the following line, or an entire file:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
eval(value) # pyastq: ignore no-eval
|
|
255
|
+
|
|
256
|
+
# pyastq: ignore no-eval
|
|
257
|
+
eval(value)
|
|
258
|
+
|
|
259
|
+
# pyastq: ignore-file no-eval
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Omitting the rule ID suppresses every rule at that location. Multiple IDs can
|
|
263
|
+
be separated by spaces or commas.
|
|
264
|
+
|
|
265
|
+
## Pre-commit Example
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
repos:
|
|
269
|
+
- repo: local
|
|
270
|
+
hooks:
|
|
271
|
+
- id: pyastq
|
|
272
|
+
name: pyastq structural rules
|
|
273
|
+
entry: target/release/pyastq check . --rules pyastq.toml --changed
|
|
274
|
+
language: system
|
|
275
|
+
pass_filenames: false
|
|
276
|
+
```
|
|
277
|
+
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
pyastq-0.1.2.data\scripts\pyastq.exe,sha256=iPfc--2C1WTtQWR6py8G6uJOpR-2yZLP7QhxPVWLhog,3711488
|
|
2
|
+
pyastq-0.1.2.dist-info\METADATA,sha256=O5pOrQTd-e1iKAMsmwej1fRzR_GTvP3x_gM5LxLn4FA,7471
|
|
3
|
+
pyastq-0.1.2.dist-info\WHEEL,sha256=jsSEiVNsW1dJj5gDaReR40i7mhgBjWtms6nAD6EViXU,94
|
|
4
|
+
pyastq-0.1.2.dist-info\RECORD,,
|