lintex 0.1.0__tar.gz
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.
- lintex-0.1.0/.github/workflows/ci.yml +41 -0
- lintex-0.1.0/.github/workflows/release.yml +73 -0
- lintex-0.1.0/.gitignore +13 -0
- lintex-0.1.0/.pre-commit-hooks.yaml +7 -0
- lintex-0.1.0/CHANGELOG.md +47 -0
- lintex-0.1.0/CONTRIBUTING.md +71 -0
- lintex-0.1.0/LICENSE +21 -0
- lintex-0.1.0/PKG-INFO +420 -0
- lintex-0.1.0/README.md +390 -0
- lintex-0.1.0/SECURITY.md +15 -0
- lintex-0.1.0/pyproject.toml +88 -0
- lintex-0.1.0/src/lintex/__init__.py +22 -0
- lintex-0.1.0/src/lintex/__main__.py +3 -0
- lintex-0.1.0/src/lintex/cli.py +475 -0
- lintex-0.1.0/src/lintex/config.py +291 -0
- lintex-0.1.0/src/lintex/engine.py +304 -0
- lintex-0.1.0/src/lintex/hooks/__init__.py +13 -0
- lintex-0.1.0/src/lintex/hooks/claude_code.py +119 -0
- lintex-0.1.0/src/lintex/hooks/git.py +124 -0
- lintex-0.1.0/src/lintex/hooks/statusline.py +119 -0
- lintex-0.1.0/src/lintex/knowledge/__init__.py +164 -0
- lintex-0.1.0/src/lintex/knowledge/__main__.py +3 -0
- lintex-0.1.0/src/lintex/knowledge/curated.toml +742 -0
- lintex-0.1.0/src/lintex/knowledge/sync.py +251 -0
- lintex-0.1.0/src/lintex/knowledge/typeshed.json +1248 -0
- lintex-0.1.0/src/lintex/ledger.py +292 -0
- lintex-0.1.0/src/lintex/models.py +66 -0
- lintex-0.1.0/src/lintex/py.typed +0 -0
- lintex-0.1.0/src/lintex/reporters.py +315 -0
- lintex-0.1.0/src/lintex/rules/__init__.py +116 -0
- lintex-0.1.0/src/lintex/rules/agent_artifacts.py +180 -0
- lintex-0.1.0/src/lintex/rules/banned_patterns.py +529 -0
- lintex-0.1.0/src/lintex/rules/base.py +226 -0
- lintex-0.1.0/src/lintex/rules/bloat.py +275 -0
- lintex-0.1.0/src/lintex/rules/hallucinations.py +410 -0
- lintex-0.1.0/tests/conftest.py +29 -0
- lintex-0.1.0/tests/test_changed.py +114 -0
- lintex-0.1.0/tests/test_cli.py +148 -0
- lintex-0.1.0/tests/test_config.py +75 -0
- lintex-0.1.0/tests/test_engine.py +97 -0
- lintex-0.1.0/tests/test_knowledge.py +182 -0
- lintex-0.1.0/tests/test_ledger.py +199 -0
- lintex-0.1.0/tests/test_reporters.py +107 -0
- lintex-0.1.0/tests/test_rules_agent_artifacts.py +59 -0
- lintex-0.1.0/tests/test_rules_banned.py +98 -0
- lintex-0.1.0/tests/test_rules_bloat.py +72 -0
- lintex-0.1.0/tests/test_rules_hallucinations.py +115 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
# Pinned: `ubuntu-latest` moved to 26.04, which has no Python 3.9 build for
|
|
15
|
+
# actions/setup-python, and we support 3.9.
|
|
16
|
+
os: [ubuntu-24.04]
|
|
17
|
+
python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
include:
|
|
19
|
+
- os: windows-latest
|
|
20
|
+
python: "3.13"
|
|
21
|
+
- os: macos-latest
|
|
22
|
+
python: "3.13"
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python }}
|
|
28
|
+
- run: python -m pip install -e . pytest pyyaml
|
|
29
|
+
- run: python -m pytest
|
|
30
|
+
|
|
31
|
+
lint:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: actions/setup-python@v5
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.13"
|
|
38
|
+
- run: python -m pip install -e . ruff
|
|
39
|
+
- run: ruff check src tests
|
|
40
|
+
- run: ruff format --check src tests
|
|
41
|
+
- run: lintex check . --format github
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a version tag (e.g. v0.1.0) is pushed, or to TestPyPI
|
|
4
|
+
# (or PyPI) on demand from the Actions tab. Uses trusted publishing, so no
|
|
5
|
+
# tokens are stored: configure a trusted publisher for this repository and
|
|
6
|
+
# workflow at https://pypi.org/manage/account/publishing/ (environment `pypi`)
|
|
7
|
+
# and, for rehearsals, at https://test.pypi.org/manage/account/publishing/
|
|
8
|
+
# (environment `testpypi`).
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags: ["v*"]
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
target:
|
|
15
|
+
description: "Where to publish"
|
|
16
|
+
type: choice
|
|
17
|
+
options: [testpypi, pypi]
|
|
18
|
+
default: testpypi
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.13"
|
|
28
|
+
- run: python -m pip install build twine
|
|
29
|
+
- name: Check the tag matches the package version
|
|
30
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
31
|
+
run: |
|
|
32
|
+
version=$(python -c "import re,pathlib;print(re.search(r'__version__ = \"([^\"]+)\"', pathlib.Path('src/lintex/__init__.py').read_text()).group(1))")
|
|
33
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
34
|
+
if [ "$version" != "$tag" ]; then
|
|
35
|
+
echo "tag v$tag does not match __version__ $version" >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
- run: python -m build
|
|
39
|
+
- run: python -m twine check --strict dist/*
|
|
40
|
+
- name: Smoke-test the wheel in a clean environment
|
|
41
|
+
run: |
|
|
42
|
+
python -m venv /tmp/smoke && /tmp/smoke/bin/pip install -q dist/*.whl
|
|
43
|
+
/tmp/smoke/bin/lintex --version
|
|
44
|
+
echo 'from collections import Mapping' > /tmp/smoke/x.py
|
|
45
|
+
# lintex exits 1 when it finds something, which is what we expect here.
|
|
46
|
+
# Capture the status so `bash -e` (the Actions default) doesn't abort on it.
|
|
47
|
+
status=0
|
|
48
|
+
/tmp/smoke/bin/lintex check /tmp/smoke/x.py --format concise --no-color || status=$?
|
|
49
|
+
test "$status" -eq 1
|
|
50
|
+
- uses: actions/upload-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
publish:
|
|
56
|
+
needs: build
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment: ${{ github.event_name == 'workflow_dispatch' && inputs.target || 'pypi' }}
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
- name: Publish to TestPyPI
|
|
67
|
+
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
69
|
+
with:
|
|
70
|
+
repository-url: https://test.pypi.org/legacy/
|
|
71
|
+
- name: Publish to PyPI
|
|
72
|
+
if: github.event_name != 'workflow_dispatch' || inputs.target == 'pypi'
|
|
73
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
lintex-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to lintex are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/). While the version is 0.x, rule defaults and
|
|
6
|
+
severities may change between minor versions as they are tuned on real codebases.
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-10-23
|
|
11
|
+
|
|
12
|
+
First release.
|
|
13
|
+
|
|
14
|
+
### Rules
|
|
15
|
+
|
|
16
|
+
- **LX001** syntax-error: the file does not parse (often a truncated edit).
|
|
17
|
+
- **LX101** hallucinated-api: removed, deprecated or Python-2-only APIs, from a curated pack
|
|
18
|
+
and a pack generated from typeshed. Stdlib removals are judged against the project's minimum
|
|
19
|
+
Python (`requires-python` or `target-version`).
|
|
20
|
+
- **LX102** nonexistent-stdlib-name: names that don't exist in a stdlib module, with
|
|
21
|
+
"did you mean" suggestions (Python 3.10+).
|
|
22
|
+
- **LX103** unresolvable-import (opt-in): imports that aren't installed, stdlib or local.
|
|
23
|
+
- **LX201** sql-string-interpolation: SQL built with f-strings, `.format()`, `%` or `+`.
|
|
24
|
+
- **LX202** raw-sql (opt-in): any raw SQL, for ORM-only projects.
|
|
25
|
+
- **LX203** dangerous-call: `eval`/`exec`, unsafe deserialization, `yaml.load` without a safe
|
|
26
|
+
loader, `os.system`, `shell=True`, `verify=False`.
|
|
27
|
+
- **LX204** insecure-file-operation: `tempfile.mktemp`, world-writable modes, tar
|
|
28
|
+
`extractall()` without `filter=`, hard-coded `/tmp` paths.
|
|
29
|
+
- **LX205** banned-api: project-specific banned modules and functions with custom messages.
|
|
30
|
+
- **LX301–LX305** agent bloat: function length, cyclomatic complexity, parameter count,
|
|
31
|
+
nesting depth, module length.
|
|
32
|
+
- **LX401** placeholder-code: comments standing in for elided code, stub markers.
|
|
33
|
+
- **LX402** swallowed-exception, **LX403** bare-except.
|
|
34
|
+
|
|
35
|
+
### Tooling
|
|
36
|
+
|
|
37
|
+
- `lintex check` with `--format pretty|full|concise|github|json`, `--staged`, `--changed`,
|
|
38
|
+
`--select`/`--ignore`/`--enable`, `--fail-on`, inline `# lintex: ignore[CODE]` and
|
|
39
|
+
`# lintex: skip-file`.
|
|
40
|
+
- Configuration in `pyproject.toml` `[tool.lintex]`, `.lintex.toml` or `.lintex.yaml`.
|
|
41
|
+
- Claude Code `PostToolUse` hook that feeds findings (with `why` and `fix`) back to the agent.
|
|
42
|
+
- Session ledger with `lintex report` and a Claude Code status line (`lintex statusline`).
|
|
43
|
+
- Git pre-commit hook (`lintex install-hook git`) and a `.pre-commit-hooks.yaml`.
|
|
44
|
+
- `python -m lintex.knowledge.sync` to regenerate the typeshed knowledge pack.
|
|
45
|
+
|
|
46
|
+
[Unreleased]: https://github.com/700799/lintex/compare/v0.1.0...HEAD
|
|
47
|
+
[0.1.0]: https://github.com/700799/lintex/releases/tag/v0.1.0
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Contributing to lintex
|
|
2
|
+
|
|
3
|
+
Thanks for helping. The most valuable contributions, in order, are: a false positive with a
|
|
4
|
+
minimal reproduction, a new API knowledge entry, and a new rule.
|
|
5
|
+
|
|
6
|
+
## Set up
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/700799/lintex && cd lintex
|
|
10
|
+
python -m venv .venv && . .venv/bin/activate
|
|
11
|
+
pip install -e . pytest pyyaml ruff mypy
|
|
12
|
+
pytest # the suite runs in well under a second
|
|
13
|
+
ruff check src tests && ruff format --check src tests
|
|
14
|
+
lintex check . # lintex lints itself and must stay clean
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
CI runs the same three commands on Python 3.9–3.13, Windows and macOS.
|
|
18
|
+
|
|
19
|
+
## Report a false positive
|
|
20
|
+
|
|
21
|
+
Open an issue with the smallest snippet that triggers it, the lintex version, and your
|
|
22
|
+
Python version. Precision matters more than recall here: a rule that cries wolf gets turned
|
|
23
|
+
off, so a confirmed false positive is a bug.
|
|
24
|
+
|
|
25
|
+
## Add an API knowledge entry
|
|
26
|
+
|
|
27
|
+
Removed, deprecated and Python-2-only names for LX101 live in
|
|
28
|
+
`src/lintex/knowledge/curated.toml`. An entry is:
|
|
29
|
+
|
|
30
|
+
```toml
|
|
31
|
+
[[api]]
|
|
32
|
+
name = "requests.packages.urllib3"
|
|
33
|
+
status = "removed" # removed | deprecated | py2
|
|
34
|
+
package = "requests" # omit for the standard library
|
|
35
|
+
version = "2.16"
|
|
36
|
+
fix = "Use `import urllib3` directly."
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Write the `fix` as something an agent can paste: the replacement, not a description of it.
|
|
40
|
+
Add a line to `tests/test_rules_hallucinations.py` showing it fires. Stdlib module removals and
|
|
41
|
+
`@deprecated` names are generated into `typeshed.json`; don't edit that file, regenerate it
|
|
42
|
+
with `python -m lintex.knowledge.sync --from-mypy` (stdlib) or `--typeshed PATH` (with
|
|
43
|
+
third-party stubs). A curated entry overrides a generated one for the same name, which is the
|
|
44
|
+
way to give a generated entry a better fix.
|
|
45
|
+
|
|
46
|
+
## Add a rule
|
|
47
|
+
|
|
48
|
+
1. Subclass `lintex.rules.base.Rule` in the module for its family (`hallucinations`,
|
|
49
|
+
`banned_patterns`, `bloat`, `agent_artifacts`). Set `code`, `name`, `summary`,
|
|
50
|
+
`explanation`, `default_severity`, `node_types` and, if configurable, `options`.
|
|
51
|
+
2. Implement `check_node` (called for each node whose type is in `node_types`) and/or
|
|
52
|
+
`check_file`. Use `ctx.resolve(node)` to turn a Name/Attribute chain into an
|
|
53
|
+
import-qualified name, and `is_compat_guarded` to skip `try/except ImportError` blocks.
|
|
54
|
+
3. Every violation needs a `message` (what), a `why` (the failure mode) and a `fix` (what to
|
|
55
|
+
write instead). Assume the reader is an LLM with no other context.
|
|
56
|
+
4. Register it in `ALL_RULES` in `src/lintex/rules/__init__.py` and add tests with both a
|
|
57
|
+
triggering case and a clean case that looks similar.
|
|
58
|
+
5. Run it over a large real codebase before opening the PR (the CPython stdlib is a good
|
|
59
|
+
known-good corpus: `lintex check $(python -c "import sysconfig;print(sysconfig.get_paths()['stdlib'])") --select LXnnn`).
|
|
60
|
+
New rules default to `warning` unless the finding is almost never wrong.
|
|
61
|
+
|
|
62
|
+
## Keep the engine fast
|
|
63
|
+
|
|
64
|
+
Rules run inside a single AST walk, dispatched by node type. Don't re-walk the tree in a rule;
|
|
65
|
+
keep per-file state in `ctx.state[self.code]` and finish in `check_file`. Anything that reads
|
|
66
|
+
files or imports modules must be cached across files.
|
|
67
|
+
|
|
68
|
+
## Commit messages and PRs
|
|
69
|
+
|
|
70
|
+
Explain the *why* in the commit body; the diff already shows the what. Keep PRs to one change.
|
|
71
|
+
A PR that adds a rule should include the false-positive numbers from the corpus run.
|
lintex-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lintex contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|