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.
Files changed (98) hide show
  1. python_constricter-0.3.1/.gitignore +21 -0
  2. {python_constricter-0.2.9 → python_constricter-0.3.1}/PKG-INFO +8 -9
  3. {python_constricter-0.2.9 → python_constricter-0.3.1}/README.md +1 -1
  4. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/__init__.py +1 -1
  5. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/overloads.py +3 -2
  6. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/signatures.py +21 -4
  7. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/stdlib.py +4 -20
  8. python_constricter-0.3.1/constricter/fix/tables/source.json +22 -0
  9. python_constricter-0.3.1/hatch_build.py +78 -0
  10. {python_constricter-0.2.9 → python_constricter-0.3.1}/pyproject.toml +37 -12
  11. python_constricter-0.3.1/stdlib_tables/__init__.py +16 -0
  12. python_constricter-0.3.1/stdlib_tables/__main__.py +8 -0
  13. python_constricter-0.3.1/stdlib_tables/generate.py +777 -0
  14. python_constricter-0.3.1/stdlib_tables/overloads.py +889 -0
  15. python_constricter-0.3.1/stdlib_tables/partial.json +974 -0
  16. python_constricter-0.3.1/stdlib_tables/reading.py +764 -0
  17. python_constricter-0.3.1/stdlib_tables/stubs.py +525 -0
  18. python_constricter-0.3.1/stdlib_tables/templates.py +261 -0
  19. python_constricter-0.2.9/constricter/fix/tables/source.json +0 -4
  20. {python_constricter-0.2.9 → python_constricter-0.3.1}/LICENSE.md +0 -0
  21. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/__main__.py +0 -0
  22. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/__init__.py +0 -0
  23. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/baseline.py +0 -0
  24. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/collecting.py +0 -0
  25. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/command.py +0 -0
  26. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/config.py +0 -0
  27. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/explain.py +0 -0
  28. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/guard.py +0 -0
  29. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/hints.py +0 -0
  30. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/options.py +0 -0
  31. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/paths.py +0 -0
  32. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/protocol.py +0 -0
  33. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/report.py +0 -0
  34. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/runs.py +0 -0
  35. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/schedule.py +0 -0
  36. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/cli/workers.py +0 -0
  37. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/__init__.py +0 -0
  38. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/atoms.py +0 -0
  39. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/callers.py +0 -0
  40. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/classnames.py +0 -0
  41. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/declared.py +0 -0
  42. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/doubts.py +0 -0
  43. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/fills.py +0 -0
  44. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/fixes.py +0 -0
  45. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/guesses.py +0 -0
  46. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/hinted.py +0 -0
  47. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/imports.py +0 -0
  48. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/inference.py +0 -0
  49. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/installed.py +0 -0
  50. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/known.py +0 -0
  51. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/library.py +0 -0
  52. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/members.py +0 -0
  53. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/modules.py +0 -0
  54. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/narrowed.py +0 -0
  55. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/opened.py +0 -0
  56. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/order.py +0 -0
  57. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/project.py +0 -0
  58. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/returned.py +0 -0
  59. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/returns.py +0 -0
  60. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/stubbed.py +0 -0
  61. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/aliases.json +0 -0
  62. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/attributes.json +0 -0
  63. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/bases.json +0 -0
  64. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/classes.json +0 -0
  65. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/generic_attributes.json +0 -0
  66. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/method_overloads.json +0 -0
  67. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/method_signatures.json +0 -0
  68. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/methods.json +0 -0
  69. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/overloads.json +0 -0
  70. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/returns.json +0 -0
  71. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/scalar_members.json +0 -0
  72. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/scalars.json +0 -0
  73. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/subscriptable.json +0 -0
  74. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/type_parameters.json +0 -0
  75. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/tables/variables.json +0 -0
  76. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/fix/targets.py +0 -0
  77. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/jsonc.py +0 -0
  78. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/noqa.py +0 -0
  79. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/notebook.py +0 -0
  80. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/offences.py +0 -0
  81. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/plugins/__init__.py +0 -0
  82. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/plugins/flake8.py +0 -0
  83. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/plugins/pylint.py +0 -0
  84. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/py.typed +0 -0
  85. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/__init__.py +0 -0
  86. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/annotations.py +0 -0
  87. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/binding.py +0 -0
  88. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/calls.py +0 -0
  89. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/checker.py +0 -0
  90. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/flow.py +0 -0
  91. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/late.py +0 -0
  92. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/narrowing.py +0 -0
  93. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/parsed.py +0 -0
  94. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/rebinding.py +0 -0
  95. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/redundant.py +0 -0
  96. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/scope.py +0 -0
  97. {python_constricter-0.2.9 → python_constricter-0.3.1}/constricter/rules/syntax.py +0 -0
  98. {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.2.9
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
- License-File: LICENSE.md
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
- Import-Name: constricter
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.2.9
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
-
@@ -237,7 +237,7 @@ pre-commit, after ruff's hooks (or `constricter-fix`, which runs `--fix` first):
237
237
 
238
238
  ```yaml
239
239
  - repo: https://github.com/ivylikethevine/python-constricter
240
- rev: v0.2.9
240
+ rev: v0.3.1
241
241
  hooks:
242
242
  - id: constricter
243
243
  ```
@@ -25,7 +25,7 @@ from constricter.offences import (
25
25
  )
26
26
  from constricter.rules.checker import Coverage, annotation_coverage, check_source, check_tree
27
27
 
28
- __version__ = "0.2.9"
28
+ __version__ = "0.3.1"
29
29
  __all__ = [
30
30
  "COMMENT_TYPED_TARGET",
31
31
  "DEFAULT_CHECKS",
@@ -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 `tests/typeshed/overloads.py`): every parameter's kind, whether it has a
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[stdlib.Variant] = stdlib.OVERLOADS.get(name) or stdlib.method_signatures()[name]
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 the signature it's in (`ReadSignature`); see
5
- `constricter.fix.overloads`, which matches them, `constricter.fix.stdlib`, whose tables hold them,
6
- and `constricter.fix.stubbed`, which reads an installed package's.
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/`, one JSON file each (see
5
- `tests/typeshed/stdlib_tables.py`): `RETURNS` holds functions returning the same builtin type
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, NotRequired, TypeAlias, TypedDict, cast
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 Parameter
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 = ["flit_core>=4.1,<5"]
77
- build-backend = "flit_core.buildapi"
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.flit.module]
80
- name = "constricter"
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)
@@ -0,0 +1,8 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Generate the standard-library tables: `python -m stdlib_tables` (see `stdlib_tables.generate`)."""
3
+
4
+ import sys
5
+
6
+ from stdlib_tables.generate import main
7
+
8
+ sys.exit(main(sys.argv[1:]))