python-constricter 0.2.9__tar.gz → 0.3.1__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.
- python_constricter-0.3.1/.gitignore +21 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/PKG-INFO +8 -9
- {python_constricter-0.2.9 → python_constricter-0.3.1}/README.md +1 -1
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/__init__.py +1 -1
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/overloads.py +3 -2
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/signatures.py +21 -4
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/stdlib.py +4 -20
- python_constricter-0.3.1/constricter/fix/tables/source.json +22 -0
- python_constricter-0.3.1/hatch_build.py +78 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/pyproject.toml +37 -12
- python_constricter-0.3.1/stdlib_tables/__init__.py +16 -0
- python_constricter-0.3.1/stdlib_tables/__main__.py +8 -0
- python_constricter-0.3.1/stdlib_tables/generate.py +777 -0
- python_constricter-0.3.1/stdlib_tables/overloads.py +889 -0
- python_constricter-0.3.1/stdlib_tables/partial.json +974 -0
- python_constricter-0.3.1/stdlib_tables/reading.py +764 -0
- python_constricter-0.3.1/stdlib_tables/stubs.py +525 -0
- python_constricter-0.3.1/stdlib_tables/templates.py +261 -0
- python_constricter-0.2.9/constricter/fix/tables/source.json +0 -4
- {python_constricter-0.2.9 → python_constricter-0.3.1}/LICENSE.md +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/__main__.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/__init__.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/baseline.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/collecting.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/command.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/config.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/explain.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/guard.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/hints.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/options.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/paths.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/protocol.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/report.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/runs.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/schedule.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/workers.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/__init__.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/atoms.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/callers.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/classnames.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/declared.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/doubts.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/fills.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/fixes.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/guesses.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/hinted.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/imports.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/inference.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/installed.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/known.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/library.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/members.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/modules.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/narrowed.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/opened.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/order.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/project.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/returned.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/returns.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/stubbed.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/aliases.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/attributes.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/bases.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/classes.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/generic_attributes.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/method_overloads.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/method_signatures.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/methods.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/overloads.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/returns.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/scalar_members.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/scalars.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/subscriptable.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/type_parameters.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/variables.json +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/targets.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/jsonc.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/noqa.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/notebook.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/offences.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/plugins/__init__.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/plugins/flake8.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/plugins/pylint.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/py.typed +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/__init__.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/annotations.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/binding.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/calls.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/checker.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/flow.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/late.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/narrowing.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/parsed.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/rebinding.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/redundant.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/scope.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/syntax.py +0 -0
- {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/walked.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Everything generated locally (the dev venv, tool caches, coverage data, build output) lives under local/.
|
|
2
|
+
local/
|
|
3
|
+
|
|
4
|
+
# Written next to the sources by Python.
|
|
5
|
+
__pycache__/
|
|
6
|
+
|
|
7
|
+
# Generated by a build (hatch_build.py, or `python -m stdlib_tables`): the standard-library tables, and
|
|
8
|
+
# the tests' list of what only some platforms and Pythons have.
|
|
9
|
+
constricter/fix/tables/
|
|
10
|
+
stdlib_tables/partial.json
|
|
11
|
+
|
|
12
|
+
# npm installs of the pinned CI Markdown tools (.github/package.json)
|
|
13
|
+
.github/node_modules/
|
|
14
|
+
|
|
15
|
+
.hypothesis
|
|
16
|
+
|
|
17
|
+
# Claude Code's agent worktrees: other checkouts of this repository.
|
|
18
|
+
.claude/worktrees/
|
|
19
|
+
|
|
20
|
+
# Backups merge tools leave next to a conflicted file.
|
|
21
|
+
*.orig
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-constricter
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Import-Name: constricter
|
|
4
5
|
Summary: Lint rules: every local variable is typed where it's first bound. A flake8 plugin, a pylint plugin and a standalone CLI.
|
|
5
6
|
Author: Ivy Duggan
|
|
6
|
-
Requires-Python: >=3.11
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
7
|
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE.md
|
|
9
9
|
Classifier: Framework :: Flake8
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -18,12 +18,12 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
19
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
20
|
Classifier: Typing :: Typed
|
|
21
|
-
|
|
22
|
-
Requires-Dist: flake8>=7 ; extra == "flake8"
|
|
23
|
-
Requires-Dist: pylint>=3 ; extra == "pylint"
|
|
21
|
+
Requires-Python: >=3.11
|
|
24
22
|
Provides-Extra: flake8
|
|
23
|
+
Requires-Dist: flake8>=7; extra == 'flake8'
|
|
25
24
|
Provides-Extra: pylint
|
|
26
|
-
|
|
25
|
+
Requires-Dist: pylint>=3; extra == 'pylint'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
28
28
|
# python-con`strict`er
|
|
29
29
|
|
|
@@ -264,7 +264,7 @@ pre-commit, after ruff's hooks (or `constricter-fix`, which runs `--fix` first):
|
|
|
264
264
|
|
|
265
265
|
```yaml
|
|
266
266
|
- repo: https://github.com/ivylikethevine/python-constricter
|
|
267
|
-
rev: v0.
|
|
267
|
+
rev: v0.3.1
|
|
268
268
|
hooks:
|
|
269
269
|
- id: constricter
|
|
270
270
|
```
|
|
@@ -378,4 +378,3 @@ I have used generative AI to write large parts of this code. All of the code her
|
|
|
378
378
|
_responsibility_ regardless: AI is a tool, not an owner of a project. I have personally understood,
|
|
379
379
|
reviewed, and approved all of the AI-generated code in this repository, and **mainline releases**
|
|
380
380
|
carry the same accountability to me as anything I write and publish myself.
|
|
381
|
-
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Standard-library calls whose arguments decide their type: the signatures a call may match.
|
|
3
3
|
|
|
4
4
|
`tables/overloads.json` holds each such function's signatures, in order, as each platform and Python
|
|
5
|
-
version reads them (see `
|
|
5
|
+
version reads them (see `stdlib_tables/overloads.py`): every parameter's kind, whether it has a
|
|
6
6
|
default, and which argument types (`SCALARS`) it certainly takes or refuses; and the return, as a
|
|
7
7
|
template naming classes by their dotted paths and type variables by their names.
|
|
8
8
|
|
|
@@ -31,6 +31,7 @@ from constricter.fix.signatures import (
|
|
|
31
31
|
Constant,
|
|
32
32
|
Parameter,
|
|
33
33
|
ReadSignature,
|
|
34
|
+
Variant,
|
|
34
35
|
)
|
|
35
36
|
from constricter.rules.walked import walk
|
|
36
37
|
|
|
@@ -308,7 +309,7 @@ def _signatures(name: str) -> tuple[tuple[ReadSignature, ...], ...]:
|
|
|
308
309
|
Each variant's signatures.
|
|
309
310
|
|
|
310
311
|
"""
|
|
311
|
-
variants: list[
|
|
312
|
+
variants: list[Variant] = stdlib.OVERLOADS.get(name) or stdlib.method_signatures()[name]
|
|
312
313
|
return tuple(
|
|
313
314
|
tuple(
|
|
314
315
|
ReadSignature(
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
2
|
"""The signatures `--fix` matches a call against: the standard library's tables', and installed packages'.
|
|
3
3
|
|
|
4
|
-
What each parameter takes (`Accepts`) and
|
|
5
|
-
|
|
6
|
-
and `constricter.fix.stubbed`, which reads an
|
|
4
|
+
What each parameter takes (`Accepts`), and a signature as the tables hold it (`Signature`) and as
|
|
5
|
+
it's matched (`ReadSignature`); see `constricter.fix.overloads`, which matches them,
|
|
6
|
+
`constricter.fix.stdlib`, whose tables hold them, and `constricter.fix.stubbed`, which reads an
|
|
7
|
+
installed package's.
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
|
-
from typing import Final, NamedTuple, Required, TypeAlias, TypedDict
|
|
10
|
+
from typing import Final, NamedTuple, NotRequired, Required, TypeAlias, TypedDict
|
|
10
11
|
|
|
11
12
|
# `Accepts`' keys for an installed package's parameter (see `constricter.fix.stubbed`): a class passed
|
|
12
13
|
# as the argument, and a builtin container.
|
|
@@ -58,6 +59,22 @@ class Accepts(TypedDict, total=False):
|
|
|
58
59
|
Parameter: TypeAlias = tuple[str, str, bool, Accepts | None]
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
class Signature(TypedDict):
|
|
63
|
+
"""One signature of a function whose arguments decide its type: its parameters, and its return.
|
|
64
|
+
|
|
65
|
+
The return is a template (see `constricter.fix.overloads`), or `None` if `--fix` can't write it.
|
|
66
|
+
`self`: for a generic class's method declaring its instance's type (`self: Pattern[str]`), the
|
|
67
|
+
type arguments that instance must have.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
params: list[Parameter | str]
|
|
71
|
+
returns: str | None
|
|
72
|
+
self: NotRequired[list[str]]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
Variant: TypeAlias = list[Signature] # one configuration's signatures, in order
|
|
76
|
+
|
|
77
|
+
|
|
61
78
|
class ReadSignature(NamedTuple):
|
|
62
79
|
"""One signature, read: its parameters in full, its return template, and its `self`'s type arguments."""
|
|
63
80
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
2
|
"""Standard-library functions and classes whose types `--fix` knows, and how a module names them.
|
|
3
3
|
|
|
4
|
-
The tables are generated from typeshed's stubs into `tables
|
|
5
|
-
`
|
|
4
|
+
The tables are generated from typeshed's stubs into `tables/` when the package is built, one JSON
|
|
5
|
+
file each (see `stdlib_tables/generate.py`): `RETURNS` holds functions returning the same builtin type
|
|
6
6
|
whatever their arguments; `OVERLOADS` those whose arguments decide it (`str` in, `str` out;
|
|
7
7
|
`bytes` in, `bytes` out), so they're typed only when those are known; `CLASSES` holds
|
|
8
8
|
non-generic classes, and functions returning one; `library_member` types those classes' methods
|
|
@@ -18,29 +18,13 @@ from collections.abc import Iterable, Mapping
|
|
|
18
18
|
from functools import cache, lru_cache
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from types import MappingProxyType
|
|
21
|
-
from typing import Final, NamedTuple,
|
|
21
|
+
from typing import Final, NamedTuple, TypeAlias, cast
|
|
22
22
|
|
|
23
23
|
from constricter.fix.known import ImportPlan, Inference, Known
|
|
24
|
-
from constricter.fix.signatures import
|
|
24
|
+
from constricter.fix.signatures import Variant
|
|
25
25
|
from constricter.rules.syntax import import_bindings
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class Signature(TypedDict):
|
|
29
|
-
"""One signature of a function whose arguments decide its type: its parameters, and its return.
|
|
30
|
-
|
|
31
|
-
The return is a template (see `constricter.fix.overloads`), or `None` if `--fix` can't write it.
|
|
32
|
-
`self`: for a generic class's method declaring its instance's type (`self: Pattern[str]`), the
|
|
33
|
-
type arguments that instance must have.
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
params: list[Parameter | str]
|
|
37
|
-
returns: str | None
|
|
38
|
-
self: NotRequired[list[str]]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Variant: TypeAlias = list[Signature] # one configuration's signatures, in order
|
|
42
|
-
|
|
43
|
-
|
|
44
28
|
def _table(name: str) -> object:
|
|
45
29
|
"""Read one of the tables (`tables/<name>.json`), plain JSON as generated.
|
|
46
30
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"basedpyright": "1.40.1",
|
|
3
|
+
"digest": "4ccf811c7737d0379e2ccbd9a361889ae27ea04c1cc4ee0450f31ea3e2ef44ae",
|
|
4
|
+
"tables": [
|
|
5
|
+
"aliases",
|
|
6
|
+
"attributes",
|
|
7
|
+
"bases",
|
|
8
|
+
"classes",
|
|
9
|
+
"generic_attributes",
|
|
10
|
+
"method_overloads",
|
|
11
|
+
"method_signatures",
|
|
12
|
+
"methods",
|
|
13
|
+
"overloads",
|
|
14
|
+
"returns",
|
|
15
|
+
"scalar_members",
|
|
16
|
+
"scalars",
|
|
17
|
+
"subscriptable",
|
|
18
|
+
"type_parameters",
|
|
19
|
+
"variables"
|
|
20
|
+
],
|
|
21
|
+
"typeshed": "289e5d3568961c8bcd33d01eef5b7ec5e1ad33ad"
|
|
22
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Hatchling's build hook: generate the standard-library tables where they're missing or stale.
|
|
3
|
+
|
|
4
|
+
They aren't tracked (see `stdlib_tables.generate`). A build from a checkout generates them, and asks
|
|
5
|
+
for the basedpyright version `uv.lock` pins to read typeshed's stubs from; one from an sdist, which
|
|
6
|
+
carries them, asks for nothing.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import importlib
|
|
10
|
+
import sys
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from typing import TYPE_CHECKING, Protocol, cast
|
|
13
|
+
|
|
14
|
+
from hatchling.builders.config import BuilderConfig
|
|
15
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from typing_extensions import override # `typing.override` is 3.12+
|
|
19
|
+
else:
|
|
20
|
+
|
|
21
|
+
def override(func: object) -> object:
|
|
22
|
+
"""Mark an override (for type checkers only).
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
`func`.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
return func
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _Generator(Protocol): # pylint: disable=too-few-public-methods # a module's functions
|
|
32
|
+
"""What the hook uses of `stdlib_tables.generate`."""
|
|
33
|
+
|
|
34
|
+
STUBS: str
|
|
35
|
+
current: Callable[[], bool] # whether the tables are all there, and current
|
|
36
|
+
pinned: Callable[[], str] # the basedpyright version the tables are read from
|
|
37
|
+
ensure: Callable[[], bool] # generate the tables if they're missing or stale
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _typed(generator: object) -> _Generator:
|
|
41
|
+
"""Type the generator's module by what the hook uses of it.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
It.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
return cast("_Generator", generator)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TablesHook(BuildHookInterface[BuilderConfig]):
|
|
51
|
+
"""Generate the tables before a build, and ask for basedpyright only when it must."""
|
|
52
|
+
|
|
53
|
+
def _generator(self) -> _Generator:
|
|
54
|
+
"""Import `stdlib_tables.generate` from the project being built.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
It.
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
if self.root not in sys.path:
|
|
61
|
+
sys.path.insert(0, self.root)
|
|
62
|
+
return _typed(importlib.import_module("stdlib_tables.generate"))
|
|
63
|
+
|
|
64
|
+
@override
|
|
65
|
+
def dependencies(self) -> list[str]:
|
|
66
|
+
"""Ask for the pinned basedpyright, if the tables must be generated.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
It, or nothing.
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
generator: _Generator = self._generator()
|
|
73
|
+
return [] if generator.current() else [f"{generator.STUBS}=={generator.pinned()}"]
|
|
74
|
+
|
|
75
|
+
@override
|
|
76
|
+
def initialize(self, version: str, build_data: dict[str, object]) -> None:
|
|
77
|
+
"""Generate the tables, if they're missing or stale."""
|
|
78
|
+
_ = self._generator().ensure()
|
|
@@ -7,6 +7,7 @@ license = "MIT"
|
|
|
7
7
|
license-files = ["LICENSE.md"]
|
|
8
8
|
authors = [{ name = "Ivy Duggan" }]
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
|
+
import-names = ["constricter"]
|
|
10
11
|
dependencies = []
|
|
11
12
|
classifiers = [
|
|
12
13
|
"Framework :: Flake8",
|
|
@@ -29,11 +30,19 @@ pylint = ["pylint>=3"]
|
|
|
29
30
|
|
|
30
31
|
[dependency-groups]
|
|
31
32
|
# Locked with hashes in uv.lock (see README, Development).
|
|
33
|
+
# What an editable install needs, with no build isolation: the build backend (and what it asks for
|
|
34
|
+
# an editable one), and the basedpyright whose typeshed stubs the standard-library tables are
|
|
35
|
+
# generated from (see hatch_build.py).
|
|
36
|
+
build = [
|
|
37
|
+
"basedpyright",
|
|
38
|
+
"editables",
|
|
39
|
+
"hatchling>=1.27,<2",
|
|
40
|
+
]
|
|
32
41
|
# What the tests need: pure Python, so it installs on PyPy and free-threaded builds too.
|
|
33
42
|
test = [
|
|
43
|
+
{ include-group = "build" },
|
|
34
44
|
"fastjsonschema", # validates the SARIF output against its schema
|
|
35
45
|
"flake8>=7",
|
|
36
|
-
"flit_core>=4.1,<5",
|
|
37
46
|
"hypothesis",
|
|
38
47
|
"pylint>=3",
|
|
39
48
|
"pytest",
|
|
@@ -44,8 +53,8 @@ test = [
|
|
|
44
53
|
# runtime-validation library and a data library, each a different style of real-world Python.
|
|
45
54
|
# Pure Python 2 code (Twisted 12.3.0) can't be installed on 3.11+: tests/corpus/corpus_sources.py fetches it.
|
|
46
55
|
corpus = [
|
|
56
|
+
{ include-group = "build" }, # for the setup action's editable `-e .` install
|
|
47
57
|
"django==5.2.17", # 6.x needs Python 3.12+; 5.2 is the current LTS and still supports 3.11
|
|
48
|
-
"flit_core>=4.1,<5", # build backend, needed for the setup action's editable `-e .` install
|
|
49
58
|
"pandas==3.0.6", # a large, partly annotated data library: overloads, generics, TYPE_CHECKING imports
|
|
50
59
|
"pydantic==2.13.5",
|
|
51
60
|
"sqlalchemy==2.0.54",
|
|
@@ -73,11 +82,25 @@ constricter = "constricter.cli.command:main"
|
|
|
73
82
|
LVA = "constricter.plugins.flake8:ConstricterChecker"
|
|
74
83
|
|
|
75
84
|
[build-system]
|
|
76
|
-
requires = ["
|
|
77
|
-
build-backend = "
|
|
85
|
+
requires = ["hatchling>=1.27,<2"]
|
|
86
|
+
build-backend = "hatchling.build"
|
|
87
|
+
|
|
88
|
+
[tool.hatch.version]
|
|
89
|
+
path = "constricter/__init__.py"
|
|
90
|
+
|
|
91
|
+
[tool.hatch.build]
|
|
92
|
+
# Generated where they're missing or stale (hatch_build.py), not tracked. `partial.json` is the tests',
|
|
93
|
+
# in the sdist only: a wheel built from it counts the tables current only with it there.
|
|
94
|
+
artifacts = ["constricter/fix/tables/*.json", "stdlib_tables/partial.json"]
|
|
95
|
+
|
|
96
|
+
[tool.hatch.build.hooks.custom]
|
|
97
|
+
|
|
98
|
+
[tool.hatch.build.targets.wheel]
|
|
99
|
+
packages = ["constricter"]
|
|
78
100
|
|
|
79
|
-
[tool.
|
|
80
|
-
|
|
101
|
+
[tool.hatch.build.targets.sdist]
|
|
102
|
+
# The generator too: a wheel built from the sdist checks the tables it carries are current.
|
|
103
|
+
only-include = ["constricter", "stdlib_tables", "hatch_build.py", "README.md", "LICENSE.md"]
|
|
81
104
|
|
|
82
105
|
[tool.constricter]
|
|
83
106
|
# Drafts for other projects (Trunk, conda-forge); Trunk's `basic.in.py` is untyped on purpose. The
|
|
@@ -116,6 +139,10 @@ notice-rgx = "SPDX-License-Identifier: MIT"
|
|
|
116
139
|
# `--infer-with`'s tests start its guard and a fake server, and a constricter to kill.
|
|
117
140
|
"tests/cli/test_hints.py" = ["assert", "suspicious-subprocess-import", "subprocess-without-shell-equals-true"]
|
|
118
141
|
"tests/corpus/corpus_suite.py" = ["suspicious-subprocess-import", "subprocess-without-shell-equals-true", "start-process-with-partial-path"]
|
|
142
|
+
# Hatchling's build hook interface: generating the tables needs neither the version nor the build data.
|
|
143
|
+
"hatch_build.py" = ["unused-method-argument"]
|
|
144
|
+
# The generator imports `constricter`'s modules without its `__init__`: its tests import it apart.
|
|
145
|
+
"tests/fix/test_stdlib_tables.py" = ["assert", "suspicious-subprocess-import", "subprocess-without-shell-equals-true"]
|
|
119
146
|
# The census of what `--fix` can't type runs this checkout's constricter on each corpus.
|
|
120
147
|
"tests/corpus/corpus_untyped.py" = ["suspicious-subprocess-import", "subprocess-without-shell-equals-true"]
|
|
121
148
|
# The local CI runner runs the workflow's own steps (and git) as subprocesses.
|
|
@@ -126,11 +153,13 @@ notice-rgx = "SPDX-License-Identifier: MIT"
|
|
|
126
153
|
# `--infer-with` starts the type checker's language server, from a fixed table of commands (and
|
|
127
154
|
# asks it its version, to see it runs).
|
|
128
155
|
"constricter/cli/{hints,guard,protocol}.py" = ["suspicious-subprocess-import", "subprocess-without-shell-equals-true"]
|
|
156
|
+
# The generator's modules import `constricter`'s without its `__init__`, which loads the tables.
|
|
157
|
+
"stdlib_tables/__init__.py" = ["non-empty-init-module"]
|
|
129
158
|
"tests/corpus/corpus_sources.py" = ["suspicious-url-open-usage"] # downloads only its pinned https URLs
|
|
130
159
|
"tests/cli/test_cli.py" = ["suspicious-url-open-usage"] # fetches the SARIF schema from its pinned URL
|
|
131
160
|
|
|
132
161
|
[tool.mypy]
|
|
133
|
-
files = ["constricter", "tests"]
|
|
162
|
+
files = ["constricter", "stdlib_tables", "tests", "hatch_build.py"]
|
|
134
163
|
python_version = "3.11"
|
|
135
164
|
cache_dir = "local/.mypy_cache"
|
|
136
165
|
strict = true
|
|
@@ -219,12 +248,8 @@ module = ["numpy", "numpy.*"]
|
|
|
219
248
|
follow_imports = "skip"
|
|
220
249
|
follow_imports_for_stubs = true # numpy ships `.pyi` stubs, which `follow_imports` alone doesn't cover
|
|
221
250
|
|
|
251
|
+
# What only basedpyright has: pyrightconfig.json, which both it and Pyright read, extends this.
|
|
222
252
|
[tool.basedpyright]
|
|
223
|
-
include = ["constricter", "tests"]
|
|
224
|
-
# The dev venv (see README, Development).
|
|
225
|
-
venvPath = "local"
|
|
226
|
-
venv = ".venv"
|
|
227
|
-
pythonVersion = "3.11"
|
|
228
253
|
typeCheckingMode = "all"
|
|
229
254
|
# astroid (pylint's AST) and fastjsonschema (the SARIF test's validator) ship no annotations.
|
|
230
255
|
allowedUntypedLibraries = ["astroid", "fastjsonschema"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""The generator of `constricter/fix/tables/`, from typeshed's standard-library stubs (see `generate`).
|
|
3
|
+
|
|
4
|
+
`constricter`'s own `__init__` loads those tables, so unless it's imported already, the modules of it
|
|
5
|
+
the generator uses are imported without it: a checkout has no tables until they're generated.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from types import ModuleType
|
|
11
|
+
from typing import Final
|
|
12
|
+
|
|
13
|
+
_NAME: Final = "constricter"
|
|
14
|
+
_PACKAGE: Final = ModuleType(_NAME)
|
|
15
|
+
_PACKAGE.__path__ = [str(Path(__file__).parents[1] / _NAME)]
|
|
16
|
+
_ = sys.modules.setdefault(_NAME, _PACKAGE)
|