codepact 0.3.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.
@@ -0,0 +1,5 @@
1
+ /target/
2
+ /dist/
3
+ /.venv/
4
+ __pycache__/
5
+ *.py[cod]
codepact-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CodePact 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.
@@ -0,0 +1,276 @@
1
+ Metadata-Version: 2.4
2
+ Name: codepact
3
+ Version: 0.3.0
4
+ Summary: Lightweight Python contract annotations for CodePact
5
+ Author: CodePact contributors
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 2 - Pre-Alpha
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: codepact-cli==0.3.0
18
+ Description-Content-Type: text/markdown
19
+
20
+ # CodePact
21
+
22
+ CodePact catches conflicting resource declarations and supported lifecycle bugs such as use-after-close and missed cleanup in Python. Its analyzer reads source without importing or running application code.
23
+
24
+ [![PyPI](https://img.shields.io/pypi/v/codepact.svg)](https://pypi.org/project/codepact/)
25
+ [![CLI on PyPI](https://img.shields.io/pypi/v/codepact-cli.svg?label=CLI)](https://pypi.org/project/codepact-cli/)
26
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white)](#compatibility)
27
+ [![Quality](https://github.com/eddiethedean/codepact/actions/workflows/quality.yml/badge.svg?branch=main)](https://github.com/eddiethedean/codepact/actions/workflows/quality.yml)
28
+ [![Documentation](https://readthedocs.org/projects/typepact/badge/?version=latest)](https://typepact.readthedocs.io/en/latest/)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/eddiethedean/codepact/blob/main/LICENSE)
30
+
31
+ Declare how resources close and how APIs intend to access them alongside ordinary Python types. CodePact 0.3.0 is a candidate that checks cleanup obligations across supported exits, in addition to use-after-close. It is not published yet. The previously published 0.1.0 and 0.2.0 releases used the TypePact name and package names. APIs, diagnostics, and the JSON schema remain provisional during 0.x.
32
+
33
+ | Task | Command | Availability |
34
+ | --- | --- | --- |
35
+ | Inspect lifecycle and access declarations | `codepact inspect-contracts FILE...` | CodePact 0.3.0 candidate |
36
+ | Check use-after-close and cleanup obligations | `codepact check --root DIR` | CodePact 0.3.0 candidate |
37
+
38
+ Build the candidate from source using [installation](#install), then follow [contract inspection](#inspect-your-first-contract) or [lifecycle checking](#try-lifecycle-checking-from-source). See the [availability guide](docs/availability.md) for release status and the platform matrix.
39
+
40
+ ## Install
41
+
42
+ CodePact 0.3.0 has passed its pre-tag qualification; the renamed Python packages and Rust crate have not been published yet. To try this candidate from a checkout:
43
+
44
+ ```sh
45
+ git clone https://github.com/eddiethedean/codepact.git
46
+ cd codepact
47
+ uv sync --locked
48
+ uv run --locked codepact --version
49
+ ```
50
+
51
+ After the maintainer qualifies and publishes 0.3.0, install the helper and its matching CLI dependency with `uv add codepact==0.3.0` or `python -m pip install 'codepact==0.3.0'`.
52
+
53
+ This installs two matching distributions:
54
+
55
+ | Distribution | Contents |
56
+ | --- | --- |
57
+ | `codepact` | Pure Python declaration helpers, typing metadata, and `py.typed` |
58
+ | `codepact-cli` | The native Rust `codepact` executable |
59
+
60
+ `codepact` pins `codepact-cli` to the same version. Importing the Python helpers does not start the analyzer or download an executable. Prebuilt CLI wheels require no Rust compiler; building the CLI from source requires Rust 1.96 or newer and a linker.
61
+
62
+ For the command alone after publication:
63
+
64
+ ```sh
65
+ uv tool install codepact-cli==0.3.0
66
+ codepact --version
67
+ ```
68
+
69
+ ## Inspect your first contract
70
+
71
+ Save this as `connection.py`:
72
+
73
+ ```python
74
+ from codepact import resource
75
+ from codepact.rust import Borrowed, Owned
76
+
77
+
78
+ @resource(closed_by="close")
79
+ class Connection:
80
+ def close(self) -> None:
81
+ pass
82
+
83
+
84
+ def read(conn: Borrowed[Connection]) -> str:
85
+ return "ready"
86
+
87
+
88
+ def consume(conn: Owned[Connection]) -> None:
89
+ conn.close()
90
+ ```
91
+
92
+ Inspect its declarations:
93
+
94
+ ```sh
95
+ uv run codepact inspect-contracts connection.py
96
+ ```
97
+
98
+ CodePact emits JSON containing these contract views:
99
+
100
+ | Declaration | Base type | Lifecycle | Access intent |
101
+ | --- | --- | --- | --- |
102
+ | `Connection` | `connection.Connection` | Closed by `close` | Unspecified |
103
+ | `read.conn` | `connection.Connection` | Closed by `close` | Shared |
104
+ | `consume.conn` | `connection.Connection` | Closed by `close` | Owned |
105
+
106
+ The `resource` decorator preserves the class identity and records lifecycle metadata. The inspector reads the file as text without importing it, evaluating annotations, or invoking decorators. In the historical TypePact 0.1.0 release, method bodies were not checked: declaring `closed_by="close"` did not establish that `close` releases anything.
107
+
108
+ The inspector also rejects conflicting declarations. The
109
+ [TP4002 example](docs/rules.md#example-conflicting-lifecycle-metadata) shows a
110
+ resource alias that declares a different close method, the reported locations,
111
+ and a valid repair. Use the `check` command below for lifecycle findings.
112
+
113
+ See the complete [paired example](https://github.com/eddiethedean/codepact/blob/main/examples/contracts.py) and its [JSON snapshot](https://github.com/eddiethedean/codepact/blob/main/tests/snapshots/example-contracts.json).
114
+
115
+ ## Try lifecycle checking from source
116
+
117
+ The TypePact 0.2.0 checker remains available from its historical tag. For the CodePact 0.3.0 candidate, use Rust 1.96+ and build the current checkout:
118
+
119
+ ```sh
120
+ git clone https://github.com/eddiethedean/codepact.git
121
+ cd codepact
122
+ uv sync --locked
123
+ uv run --locked codepact check --root examples
124
+ ```
125
+
126
+ Record `git rev-parse HEAD` with candidate results.
127
+
128
+ The [lifecycle example](https://github.com/eddiethedean/codepact/blob/main/examples/lifecycle.py) constructs a connection, passes it to a Borrowed helper, closes it, then calls the helper again. The final call produces a definite `TP1001`, includes the close location, and exits with status `1`. This example intentionally contains an error.
129
+
130
+ The checker follows supported normal synchronous flow, local aliases, direct calls, and `if`/`else` joins. It also reports closing a `Borrowed` parameter as `TP2001`. Possible findings and incomplete coverage are visible warnings that can accompany exit `0`; read the report even when the command succeeds.
131
+
132
+ Follow the [lifecycle tutorial](https://typepact.readthedocs.io/en/latest/lifecycle.html) for an isolated example, expected output, and a valid counterpart. The checker does not establish leak freedom, cleanup on every exit, general ownership safety, or that the declared close method releases a runtime resource. Release status and supported behavior are summarized in the [availability guide](docs/availability.md).
133
+
134
+ ## Annotation forms
135
+
136
+ The access aliases use `typing.Annotated`, so ordinary Python type checkers retain the underlying type:
137
+
138
+ | Alias from `codepact.rust` | Metadata from `codepact.contracts` | Intent |
139
+ | --- | --- | --- |
140
+ | `Borrowed[T]` | `Annotated[T, SharedBorrow()]` | Shared access |
141
+ | `Owned[T]` | `Annotated[T, OwnedAccess()]` | Owned access |
142
+ | `BorrowedMut[T]` | `Annotated[T, ExclusiveBorrow()]` | Exclusive access |
143
+
144
+ For example, `Borrowed[Connection]` and `Annotated[Connection, SharedBorrow()]` produce the same shared-access facts. The aliases are also exported from `codepact.contracts`.
145
+
146
+ You can attach lifecycle and access metadata to an alias of an existing class:
147
+
148
+ ```python
149
+ from typing import Annotated
150
+
151
+ from codepact.contracts import Resource, SharedBorrow
152
+
153
+
154
+ ConnectionView = Annotated[
155
+ Connection,
156
+ Resource(closed_by="close"),
157
+ SharedBorrow(),
158
+ ]
159
+ ```
160
+
161
+ Here, `Connection` is the class from the quick start. Metadata on an alias describes that view; it does not change every use of the underlying class. CodePact combines compatible lifecycle and access declarations and reports conflicting values with their source locations. These annotations express intent; they do not enforce ownership or borrowing at runtime.
162
+
163
+ ## Inspecting a project
164
+
165
+ Choose a source target explicitly when needed. The default is Python 3.11:
166
+
167
+ ```sh
168
+ uv run codepact inspect-contracts --python-version 3.13 connection.py
169
+ ```
170
+
171
+ For imports between files, pass every module that should participate in resolution. For a project with a `src` directory:
172
+
173
+ ```sh
174
+ uv run codepact inspect-contracts --root src src/models.py src/client.py
175
+ ```
176
+
177
+ For `inspect-contracts`, `--root` determines logical paths and package-qualified module names without discovering files. Without it, each input file's stem supplies its module name. Absolute `from module import Class` imports and direct imports of supported assignment aliases resolve across supplied files. The `check` command instead scans `.py` files below its root; explicit filenames add inputs and do not restrict that scan.
178
+
179
+ ### Inspection output and exit codes
180
+
181
+ JSON is the default output; `--json` is accepted explicitly. Each result includes:
182
+
183
+ - `contracts`: normalized base symbols, lifecycle and access facts, source spans, and declaration provenance.
184
+ - `diagnostics`: errors and unsupported-boundary findings, including related locations where applicable.
185
+ - `coverage`: `complete-for-supported-subset` or `incomplete`, with reasons and work counts.
186
+ - `schema_version: 0` and `assurance: "contract-inspection-only"`.
187
+
188
+ Contract provenance is `declared-unverified`. Line and column numbers are one-based; columns count UTF-8 bytes.
189
+
190
+ | Exit code | Meaning |
191
+ | --- | --- |
192
+ | `0` | Inspection completed with no declaration errors or incomplete coverage |
193
+ | `1` | Declaration errors, syntax errors, or unsupported/unresolved constructs were reported |
194
+ | `2` | An invocation, file access, input limit, or fatal execution error prevented inspection |
195
+
196
+ A successful inspection establishes the recorded declarations within the supported subset. It does not prove resource safety.
197
+
198
+ ## Scope of the original 0.1 release
199
+
200
+ The inspector supports module-level classes, literal CodePact resource decorators, simple assignment aliases, parameter and return annotations, and direct absolute imports among explicitly supplied modules. It resolves supported API imports by identity, respects local rebinding, and preserves the class and metadata captured by earlier aliases.
201
+
202
+ Relevant unsupported constructs produce incomplete coverage. Current boundaries include:
203
+
204
+ - String annotations and `from __future__ import annotations`.
205
+ - Relative imports, star imports, import re-export chains, and supplied-module cycles.
206
+ - Class inheritance and metaclass behavior, other class decorators, and decorated functions.
207
+ - Computed contract expressions, dynamic decorator arguments, and eager named expressions that could rebind module names.
208
+ - Module-level control flow and unsupported nested or generic declarations.
209
+
210
+ The inspector has explicit source and work limits, including a 1,000,000-byte total source budget per request. See the [inspector guide](https://github.com/eddiethedean/codepact/blob/main/docs/inspector.md) for the detailed limits and Rust session API.
211
+
212
+ The historical TypePact 0.2.0 release added bounded lifecycle flow checks. The CodePact 0.3.0 candidate adds cleanup obligations across supported exits. General ownership enforcement and precise alias analysis through fields and containers remain future work. The [release roadmap](https://github.com/eddiethedean/codepact/blob/main/ROADMAP.md) describes that progression.
213
+
214
+ ## Compatibility
215
+
216
+ | Surface | Support |
217
+ | --- | --- |
218
+ | Python helper runtime | Python 3.11+; CI covers CPython 3.11–3.14 |
219
+ | Analyzed Python source targets | 3.11, 3.12, and 3.13 |
220
+ | Python typing | Inline types and `py.typed`; compatibility fixtures for mypy and Pyright |
221
+ | Rust source builds | Rust 1.96+ |
222
+
223
+ The release configuration defines eight native CLI wheel targets. The current
224
+ candidate's build and install evidence is recorded in the
225
+ [availability guide](docs/availability.md):
226
+
227
+ | Platform | x86-64 | ARM64 |
228
+ | --- | --- | --- |
229
+ | Linux glibc | glibc 2.17+ | glibc 2.28+ |
230
+ | Linux musl | musl 1.2+ | musl 1.2+ |
231
+ | macOS | 10.13+ | 11.0+ |
232
+ | Windows | Supported | Supported |
233
+
234
+ The helper wheel is universal pure Python. CLI wheels contain a standalone executable and do not depend on a particular CPython ABI. Identifier validation follows the Unicode rules of the selected source target. See [compatibility](https://github.com/eddiethedean/codepact/blob/main/docs/planning/compatibility.md) and [distribution details](https://github.com/eddiethedean/codepact/blob/main/docs/planning/releases.md) for the complete policy.
235
+
236
+ ## Architecture
237
+
238
+ The Python helpers supply declarations; the Rust engine performs inspection. Pinned Ruff parser and AST components provide structured syntax and source ranges. Salsa tracks parsing and module semantics so reusable sessions can retain unaffected work across edits.
239
+
240
+ The Rust `codepact` crate exposes `inspect` for a single request and `AnalysisSession` for repeated updates, removal, reset, cancellation, and revision-tagged results. The candidate adds `check` and versioned `check_report` methods. CLI and library calls share the analyzer; the host application supplies the Rust API's source snapshot. See the [Rust API guide](https://typepact.readthedocs.io/en/latest/rust-api.html) and [analyzer design](https://github.com/eddiethedean/codepact/blob/main/docs/planning/analyzer-migration.md).
241
+
242
+ ## Development
243
+
244
+ Use a source checkout with `uv`, Rust 1.96+, rustfmt, Clippy, and Make. Install the pinned dependency auditors, then run the full local gate:
245
+
246
+ ```sh
247
+ uv sync --locked
248
+ cargo install cargo-audit --version 0.22.2 --locked
249
+ cargo install cargo-deny --version 0.20.2 --locked
250
+ uv run --locked make check
251
+ ```
252
+
253
+ `make check` covers Rust formatting, compilation, Clippy, tests, MSRV validation, dependency audits, strict Python typing and linting, identifier-table verification, package builds and clean installation checks, and the inspector's execution guard. GitHub Actions runs the Python checks on 3.11–3.14 alongside the Rust and dependency checks.
254
+
255
+ Run the repository example from the checkout:
256
+
257
+ ```sh
258
+ uv run --locked codepact inspect-contracts --root . examples/contracts.py
259
+ ```
260
+
261
+ See [CONTRIBUTING.md](https://github.com/eddiethedean/codepact/blob/main/CONTRIBUTING.md) for focused check targets and contribution guidance.
262
+
263
+ ## Documentation
264
+
265
+ - [Full documentation](https://typepact.readthedocs.io/en/latest/): installation, quick start, adoption guidance, APIs, CLI, output, examples, security, and support.
266
+ - [Documentation source](https://github.com/eddiethedean/codepact/tree/main/docs): guides in the repository if the hosted site is unavailable.
267
+ - [Release and capability availability](docs/availability.md): published versions, capabilities, and platform support.
268
+ - [Inspector guide](https://github.com/eddiethedean/codepact/blob/main/docs/inspector.md): supported syntax, output, limits, and sessions.
269
+ - [Release roadmap](https://github.com/eddiethedean/codepact/blob/main/ROADMAP.md): the planned 0.1–0.9 sequence.
270
+ - [Product vision](https://github.com/eddiethedean/codepact/blob/main/docs/planning/vision.md): goals and intended use cases.
271
+ - [Limitations](https://github.com/eddiethedean/codepact/blob/main/docs/planning/limitations.md): analysis and assurance boundaries.
272
+ - [Planning index](https://github.com/eddiethedean/codepact/blob/main/docs/planning/README.md): architecture, decisions, and future feature specifications.
273
+
274
+ ## License
275
+
276
+ CodePact is distributed under the [MIT license](https://github.com/eddiethedean/codepact/blob/main/LICENSE).
@@ -0,0 +1,257 @@
1
+ # CodePact
2
+
3
+ CodePact catches conflicting resource declarations and supported lifecycle bugs such as use-after-close and missed cleanup in Python. Its analyzer reads source without importing or running application code.
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/codepact.svg)](https://pypi.org/project/codepact/)
6
+ [![CLI on PyPI](https://img.shields.io/pypi/v/codepact-cli.svg?label=CLI)](https://pypi.org/project/codepact-cli/)
7
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white)](#compatibility)
8
+ [![Quality](https://github.com/eddiethedean/codepact/actions/workflows/quality.yml/badge.svg?branch=main)](https://github.com/eddiethedean/codepact/actions/workflows/quality.yml)
9
+ [![Documentation](https://readthedocs.org/projects/typepact/badge/?version=latest)](https://typepact.readthedocs.io/en/latest/)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/eddiethedean/codepact/blob/main/LICENSE)
11
+
12
+ Declare how resources close and how APIs intend to access them alongside ordinary Python types. CodePact 0.3.0 is a candidate that checks cleanup obligations across supported exits, in addition to use-after-close. It is not published yet. The previously published 0.1.0 and 0.2.0 releases used the TypePact name and package names. APIs, diagnostics, and the JSON schema remain provisional during 0.x.
13
+
14
+ | Task | Command | Availability |
15
+ | --- | --- | --- |
16
+ | Inspect lifecycle and access declarations | `codepact inspect-contracts FILE...` | CodePact 0.3.0 candidate |
17
+ | Check use-after-close and cleanup obligations | `codepact check --root DIR` | CodePact 0.3.0 candidate |
18
+
19
+ Build the candidate from source using [installation](#install), then follow [contract inspection](#inspect-your-first-contract) or [lifecycle checking](#try-lifecycle-checking-from-source). See the [availability guide](docs/availability.md) for release status and the platform matrix.
20
+
21
+ ## Install
22
+
23
+ CodePact 0.3.0 has passed its pre-tag qualification; the renamed Python packages and Rust crate have not been published yet. To try this candidate from a checkout:
24
+
25
+ ```sh
26
+ git clone https://github.com/eddiethedean/codepact.git
27
+ cd codepact
28
+ uv sync --locked
29
+ uv run --locked codepact --version
30
+ ```
31
+
32
+ After the maintainer qualifies and publishes 0.3.0, install the helper and its matching CLI dependency with `uv add codepact==0.3.0` or `python -m pip install 'codepact==0.3.0'`.
33
+
34
+ This installs two matching distributions:
35
+
36
+ | Distribution | Contents |
37
+ | --- | --- |
38
+ | `codepact` | Pure Python declaration helpers, typing metadata, and `py.typed` |
39
+ | `codepact-cli` | The native Rust `codepact` executable |
40
+
41
+ `codepact` pins `codepact-cli` to the same version. Importing the Python helpers does not start the analyzer or download an executable. Prebuilt CLI wheels require no Rust compiler; building the CLI from source requires Rust 1.96 or newer and a linker.
42
+
43
+ For the command alone after publication:
44
+
45
+ ```sh
46
+ uv tool install codepact-cli==0.3.0
47
+ codepact --version
48
+ ```
49
+
50
+ ## Inspect your first contract
51
+
52
+ Save this as `connection.py`:
53
+
54
+ ```python
55
+ from codepact import resource
56
+ from codepact.rust import Borrowed, Owned
57
+
58
+
59
+ @resource(closed_by="close")
60
+ class Connection:
61
+ def close(self) -> None:
62
+ pass
63
+
64
+
65
+ def read(conn: Borrowed[Connection]) -> str:
66
+ return "ready"
67
+
68
+
69
+ def consume(conn: Owned[Connection]) -> None:
70
+ conn.close()
71
+ ```
72
+
73
+ Inspect its declarations:
74
+
75
+ ```sh
76
+ uv run codepact inspect-contracts connection.py
77
+ ```
78
+
79
+ CodePact emits JSON containing these contract views:
80
+
81
+ | Declaration | Base type | Lifecycle | Access intent |
82
+ | --- | --- | --- | --- |
83
+ | `Connection` | `connection.Connection` | Closed by `close` | Unspecified |
84
+ | `read.conn` | `connection.Connection` | Closed by `close` | Shared |
85
+ | `consume.conn` | `connection.Connection` | Closed by `close` | Owned |
86
+
87
+ The `resource` decorator preserves the class identity and records lifecycle metadata. The inspector reads the file as text without importing it, evaluating annotations, or invoking decorators. In the historical TypePact 0.1.0 release, method bodies were not checked: declaring `closed_by="close"` did not establish that `close` releases anything.
88
+
89
+ The inspector also rejects conflicting declarations. The
90
+ [TP4002 example](docs/rules.md#example-conflicting-lifecycle-metadata) shows a
91
+ resource alias that declares a different close method, the reported locations,
92
+ and a valid repair. Use the `check` command below for lifecycle findings.
93
+
94
+ See the complete [paired example](https://github.com/eddiethedean/codepact/blob/main/examples/contracts.py) and its [JSON snapshot](https://github.com/eddiethedean/codepact/blob/main/tests/snapshots/example-contracts.json).
95
+
96
+ ## Try lifecycle checking from source
97
+
98
+ The TypePact 0.2.0 checker remains available from its historical tag. For the CodePact 0.3.0 candidate, use Rust 1.96+ and build the current checkout:
99
+
100
+ ```sh
101
+ git clone https://github.com/eddiethedean/codepact.git
102
+ cd codepact
103
+ uv sync --locked
104
+ uv run --locked codepact check --root examples
105
+ ```
106
+
107
+ Record `git rev-parse HEAD` with candidate results.
108
+
109
+ The [lifecycle example](https://github.com/eddiethedean/codepact/blob/main/examples/lifecycle.py) constructs a connection, passes it to a Borrowed helper, closes it, then calls the helper again. The final call produces a definite `TP1001`, includes the close location, and exits with status `1`. This example intentionally contains an error.
110
+
111
+ The checker follows supported normal synchronous flow, local aliases, direct calls, and `if`/`else` joins. It also reports closing a `Borrowed` parameter as `TP2001`. Possible findings and incomplete coverage are visible warnings that can accompany exit `0`; read the report even when the command succeeds.
112
+
113
+ Follow the [lifecycle tutorial](https://typepact.readthedocs.io/en/latest/lifecycle.html) for an isolated example, expected output, and a valid counterpart. The checker does not establish leak freedom, cleanup on every exit, general ownership safety, or that the declared close method releases a runtime resource. Release status and supported behavior are summarized in the [availability guide](docs/availability.md).
114
+
115
+ ## Annotation forms
116
+
117
+ The access aliases use `typing.Annotated`, so ordinary Python type checkers retain the underlying type:
118
+
119
+ | Alias from `codepact.rust` | Metadata from `codepact.contracts` | Intent |
120
+ | --- | --- | --- |
121
+ | `Borrowed[T]` | `Annotated[T, SharedBorrow()]` | Shared access |
122
+ | `Owned[T]` | `Annotated[T, OwnedAccess()]` | Owned access |
123
+ | `BorrowedMut[T]` | `Annotated[T, ExclusiveBorrow()]` | Exclusive access |
124
+
125
+ For example, `Borrowed[Connection]` and `Annotated[Connection, SharedBorrow()]` produce the same shared-access facts. The aliases are also exported from `codepact.contracts`.
126
+
127
+ You can attach lifecycle and access metadata to an alias of an existing class:
128
+
129
+ ```python
130
+ from typing import Annotated
131
+
132
+ from codepact.contracts import Resource, SharedBorrow
133
+
134
+
135
+ ConnectionView = Annotated[
136
+ Connection,
137
+ Resource(closed_by="close"),
138
+ SharedBorrow(),
139
+ ]
140
+ ```
141
+
142
+ Here, `Connection` is the class from the quick start. Metadata on an alias describes that view; it does not change every use of the underlying class. CodePact combines compatible lifecycle and access declarations and reports conflicting values with their source locations. These annotations express intent; they do not enforce ownership or borrowing at runtime.
143
+
144
+ ## Inspecting a project
145
+
146
+ Choose a source target explicitly when needed. The default is Python 3.11:
147
+
148
+ ```sh
149
+ uv run codepact inspect-contracts --python-version 3.13 connection.py
150
+ ```
151
+
152
+ For imports between files, pass every module that should participate in resolution. For a project with a `src` directory:
153
+
154
+ ```sh
155
+ uv run codepact inspect-contracts --root src src/models.py src/client.py
156
+ ```
157
+
158
+ For `inspect-contracts`, `--root` determines logical paths and package-qualified module names without discovering files. Without it, each input file's stem supplies its module name. Absolute `from module import Class` imports and direct imports of supported assignment aliases resolve across supplied files. The `check` command instead scans `.py` files below its root; explicit filenames add inputs and do not restrict that scan.
159
+
160
+ ### Inspection output and exit codes
161
+
162
+ JSON is the default output; `--json` is accepted explicitly. Each result includes:
163
+
164
+ - `contracts`: normalized base symbols, lifecycle and access facts, source spans, and declaration provenance.
165
+ - `diagnostics`: errors and unsupported-boundary findings, including related locations where applicable.
166
+ - `coverage`: `complete-for-supported-subset` or `incomplete`, with reasons and work counts.
167
+ - `schema_version: 0` and `assurance: "contract-inspection-only"`.
168
+
169
+ Contract provenance is `declared-unverified`. Line and column numbers are one-based; columns count UTF-8 bytes.
170
+
171
+ | Exit code | Meaning |
172
+ | --- | --- |
173
+ | `0` | Inspection completed with no declaration errors or incomplete coverage |
174
+ | `1` | Declaration errors, syntax errors, or unsupported/unresolved constructs were reported |
175
+ | `2` | An invocation, file access, input limit, or fatal execution error prevented inspection |
176
+
177
+ A successful inspection establishes the recorded declarations within the supported subset. It does not prove resource safety.
178
+
179
+ ## Scope of the original 0.1 release
180
+
181
+ The inspector supports module-level classes, literal CodePact resource decorators, simple assignment aliases, parameter and return annotations, and direct absolute imports among explicitly supplied modules. It resolves supported API imports by identity, respects local rebinding, and preserves the class and metadata captured by earlier aliases.
182
+
183
+ Relevant unsupported constructs produce incomplete coverage. Current boundaries include:
184
+
185
+ - String annotations and `from __future__ import annotations`.
186
+ - Relative imports, star imports, import re-export chains, and supplied-module cycles.
187
+ - Class inheritance and metaclass behavior, other class decorators, and decorated functions.
188
+ - Computed contract expressions, dynamic decorator arguments, and eager named expressions that could rebind module names.
189
+ - Module-level control flow and unsupported nested or generic declarations.
190
+
191
+ The inspector has explicit source and work limits, including a 1,000,000-byte total source budget per request. See the [inspector guide](https://github.com/eddiethedean/codepact/blob/main/docs/inspector.md) for the detailed limits and Rust session API.
192
+
193
+ The historical TypePact 0.2.0 release added bounded lifecycle flow checks. The CodePact 0.3.0 candidate adds cleanup obligations across supported exits. General ownership enforcement and precise alias analysis through fields and containers remain future work. The [release roadmap](https://github.com/eddiethedean/codepact/blob/main/ROADMAP.md) describes that progression.
194
+
195
+ ## Compatibility
196
+
197
+ | Surface | Support |
198
+ | --- | --- |
199
+ | Python helper runtime | Python 3.11+; CI covers CPython 3.11–3.14 |
200
+ | Analyzed Python source targets | 3.11, 3.12, and 3.13 |
201
+ | Python typing | Inline types and `py.typed`; compatibility fixtures for mypy and Pyright |
202
+ | Rust source builds | Rust 1.96+ |
203
+
204
+ The release configuration defines eight native CLI wheel targets. The current
205
+ candidate's build and install evidence is recorded in the
206
+ [availability guide](docs/availability.md):
207
+
208
+ | Platform | x86-64 | ARM64 |
209
+ | --- | --- | --- |
210
+ | Linux glibc | glibc 2.17+ | glibc 2.28+ |
211
+ | Linux musl | musl 1.2+ | musl 1.2+ |
212
+ | macOS | 10.13+ | 11.0+ |
213
+ | Windows | Supported | Supported |
214
+
215
+ The helper wheel is universal pure Python. CLI wheels contain a standalone executable and do not depend on a particular CPython ABI. Identifier validation follows the Unicode rules of the selected source target. See [compatibility](https://github.com/eddiethedean/codepact/blob/main/docs/planning/compatibility.md) and [distribution details](https://github.com/eddiethedean/codepact/blob/main/docs/planning/releases.md) for the complete policy.
216
+
217
+ ## Architecture
218
+
219
+ The Python helpers supply declarations; the Rust engine performs inspection. Pinned Ruff parser and AST components provide structured syntax and source ranges. Salsa tracks parsing and module semantics so reusable sessions can retain unaffected work across edits.
220
+
221
+ The Rust `codepact` crate exposes `inspect` for a single request and `AnalysisSession` for repeated updates, removal, reset, cancellation, and revision-tagged results. The candidate adds `check` and versioned `check_report` methods. CLI and library calls share the analyzer; the host application supplies the Rust API's source snapshot. See the [Rust API guide](https://typepact.readthedocs.io/en/latest/rust-api.html) and [analyzer design](https://github.com/eddiethedean/codepact/blob/main/docs/planning/analyzer-migration.md).
222
+
223
+ ## Development
224
+
225
+ Use a source checkout with `uv`, Rust 1.96+, rustfmt, Clippy, and Make. Install the pinned dependency auditors, then run the full local gate:
226
+
227
+ ```sh
228
+ uv sync --locked
229
+ cargo install cargo-audit --version 0.22.2 --locked
230
+ cargo install cargo-deny --version 0.20.2 --locked
231
+ uv run --locked make check
232
+ ```
233
+
234
+ `make check` covers Rust formatting, compilation, Clippy, tests, MSRV validation, dependency audits, strict Python typing and linting, identifier-table verification, package builds and clean installation checks, and the inspector's execution guard. GitHub Actions runs the Python checks on 3.11–3.14 alongside the Rust and dependency checks.
235
+
236
+ Run the repository example from the checkout:
237
+
238
+ ```sh
239
+ uv run --locked codepact inspect-contracts --root . examples/contracts.py
240
+ ```
241
+
242
+ See [CONTRIBUTING.md](https://github.com/eddiethedean/codepact/blob/main/CONTRIBUTING.md) for focused check targets and contribution guidance.
243
+
244
+ ## Documentation
245
+
246
+ - [Full documentation](https://typepact.readthedocs.io/en/latest/): installation, quick start, adoption guidance, APIs, CLI, output, examples, security, and support.
247
+ - [Documentation source](https://github.com/eddiethedean/codepact/tree/main/docs): guides in the repository if the hosted site is unavailable.
248
+ - [Release and capability availability](docs/availability.md): published versions, capabilities, and platform support.
249
+ - [Inspector guide](https://github.com/eddiethedean/codepact/blob/main/docs/inspector.md): supported syntax, output, limits, and sessions.
250
+ - [Release roadmap](https://github.com/eddiethedean/codepact/blob/main/ROADMAP.md): the planned 0.1–0.9 sequence.
251
+ - [Product vision](https://github.com/eddiethedean/codepact/blob/main/docs/planning/vision.md): goals and intended use cases.
252
+ - [Limitations](https://github.com/eddiethedean/codepact/blob/main/docs/planning/limitations.md): analysis and assurance boundaries.
253
+ - [Planning index](https://github.com/eddiethedean/codepact/blob/main/docs/planning/README.md): architecture, decisions, and future feature specifications.
254
+
255
+ ## License
256
+
257
+ CodePact is distributed under the [MIT license](https://github.com/eddiethedean/codepact/blob/main/LICENSE).
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = ["hatchling==1.27.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "codepact"
7
+ version = "0.3.0"
8
+ description = "Lightweight Python contract annotations for CodePact"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = ["codepact-cli==0.3.0"]
12
+ license = "MIT"
13
+ authors = [{ name = "CodePact contributors" }]
14
+ classifiers = [
15
+ "Development Status :: 2 - Pre-Alpha",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Typing :: Typed",
23
+ ]
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "basedpyright==1.40.1",
28
+ "build==1.2.2.post1",
29
+ "maturin==1.9.6",
30
+ "hatchling==1.27.0",
31
+ "mypy==1.19.1",
32
+ "packaging==26.3",
33
+ "pyright==1.1.411",
34
+ "ruff==0.17.3",
35
+ "twine==6.1.0",
36
+ ]
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["python/codepact"]
40
+
41
+ [tool.hatch.build.targets.sdist]
42
+ include = ["/python/codepact", "/README.md", "/LICENSE", "/pyproject.toml"]
43
+
44
+ [tool.uv.workspace]
45
+ members = ["cli"]
46
+
47
+ [tool.uv.sources]
48
+ codepact-cli = { workspace = true }
49
+
50
+ [tool.basedpyright]
51
+ include = ["python/codepact", "scripts"]
52
+ pythonVersion = "3.11"
53
+ typeCheckingMode = "strict"
54
+ deprecateTypingAliases = true
55
+ enableTypeIgnoreComments = false
56
+ failOnWarnings = true
57
+ reportDeprecated = "error"
58
+ reportAny = "error"
59
+ reportExplicitAny = "error"
60
+ reportUnknownArgumentType = "error"
61
+ reportUnknownMemberType = "error"
62
+ reportUnknownParameterType = "error"
63
+ reportUnknownVariableType = "error"
64
+ reportIgnoreCommentWithoutRule = true
65
+ reportUnnecessaryTypeIgnoreComment = "error"
66
+
67
+ [tool.ruff]
68
+ target-version = "py311"
69
+ src = ["python"]
70
+
71
+ [tool.ruff.lint]
72
+ select = ["E", "F", "ANN", "PYI"]
73
+
74
+ [tool.ruff.format]
75
+ quote-style = "double"
@@ -0,0 +1,47 @@
1
+ """Lightweight runtime declarations for CodePact.
2
+
3
+ Importing this module never loads the Rust inspector or executes analysis.
4
+ """
5
+
6
+ from collections.abc import Callable
7
+ from typing import TypeVar, overload
8
+
9
+ from .contracts import Resource
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ def _is_identifier(value: str) -> bool:
15
+ try:
16
+ return value.isidentifier()
17
+ except AttributeError:
18
+ return False
19
+
20
+
21
+ @overload
22
+ def resource(*, closed_by: str) -> Callable[[type[T]], type[T]]: ...
23
+
24
+
25
+ @overload
26
+ def resource(cls: type[T], *, closed_by: str) -> type[T]: ...
27
+
28
+
29
+ def resource(
30
+ cls: type[T] | None = None, *, closed_by: str
31
+ ) -> type[T] | Callable[[type[T]], type[T]]:
32
+ """Attach a declared lifecycle contract while preserving class identity."""
33
+ if not _is_identifier(closed_by):
34
+ raise ValueError("closed_by must be a method name")
35
+
36
+ def decorate(target: type[T]) -> type[T]:
37
+ existing = target.__dict__.get("__codepact_resource__")
38
+ contract = Resource(closed_by=closed_by)
39
+ if existing is not None and existing != contract:
40
+ raise ValueError("conflicting CodePact resource declarations")
41
+ setattr(target, "__codepact_resource__", contract)
42
+ return target
43
+
44
+ return decorate(cls) if cls is not None else decorate
45
+
46
+
47
+ __all__ = ["resource", "Resource"]
@@ -0,0 +1,43 @@
1
+ """Typing metadata understood by the CodePact source inspector."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Annotated, TypeAlias, TypeVar
5
+
6
+ T = TypeVar("T")
7
+
8
+
9
+ @dataclass(frozen=True, slots=True)
10
+ class Resource:
11
+ """Declared lifecycle metadata; it does not enforce runtime behavior."""
12
+
13
+ closed_by: str
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class SharedBorrow:
18
+ """Intent to access a value through a shared borrow."""
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class OwnedAccess:
23
+ """Intent to access or transfer an owned handle."""
24
+
25
+
26
+ @dataclass(frozen=True, slots=True)
27
+ class ExclusiveBorrow:
28
+ """Intent to access a value through an exclusive borrow."""
29
+
30
+
31
+ Owned: TypeAlias = Annotated[T, OwnedAccess()]
32
+ Borrowed: TypeAlias = Annotated[T, SharedBorrow()]
33
+ BorrowedMut: TypeAlias = Annotated[T, ExclusiveBorrow()]
34
+
35
+ __all__ = [
36
+ "Resource",
37
+ "SharedBorrow",
38
+ "OwnedAccess",
39
+ "ExclusiveBorrow",
40
+ "Owned",
41
+ "Borrowed",
42
+ "BorrowedMut",
43
+ ]
File without changes
@@ -0,0 +1,5 @@
1
+ """Explicit Rust-inspired annotation spellings for ordinary Python values."""
2
+
3
+ from .contracts import Borrowed, BorrowedMut, Owned
4
+
5
+ __all__ = ["Owned", "Borrowed", "BorrowedMut"]