contractgate 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- contractgate-0.1.0/.gitignore +25 -0
- contractgate-0.1.0/CHANGELOG.md +14 -0
- contractgate-0.1.0/LICENSE +21 -0
- contractgate-0.1.0/PKG-INFO +118 -0
- contractgate-0.1.0/README.md +88 -0
- contractgate-0.1.0/contractgate/__init__.py +0 -0
- contractgate-0.1.0/pyproject.toml +52 -0
- contractgate-0.1.0/src/contractgate/__init__.py +107 -0
- contractgate-0.1.0/src/contractgate/_transport.py +280 -0
- contractgate-0.1.0/src/contractgate/_version.py +1 -0
- contractgate-0.1.0/src/contractgate/async_client.py +205 -0
- contractgate-0.1.0/src/contractgate/client.py +237 -0
- contractgate-0.1.0/src/contractgate/contract.py +426 -0
- contractgate-0.1.0/src/contractgate/exceptions.py +126 -0
- contractgate-0.1.0/src/contractgate/models.py +260 -0
- contractgate-0.1.0/src/contractgate/validator.py +395 -0
- contractgate-0.1.0/tests/__init__.py +0 -0
- contractgate-0.1.0/tests/conftest.py +13 -0
- contractgate-0.1.0/tests/fixtures/contracts/compliance_mode.yaml +14 -0
- contractgate-0.1.0/tests/fixtures/contracts/user_events.yaml +31 -0
- contractgate-0.1.0/tests/fixtures/contracts/with_transform.yaml +15 -0
- contractgate-0.1.0/tests/test_client_async.py +93 -0
- contractgate-0.1.0/tests/test_client_sync.py +218 -0
- contractgate-0.1.0/tests/test_contract_parse.py +111 -0
- contractgate-0.1.0/tests/test_validator.py +226 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
target/
|
|
3
|
+
|
|
4
|
+
# Environment
|
|
5
|
+
.env
|
|
6
|
+
.env.local
|
|
7
|
+
|
|
8
|
+
# Node / Next.js
|
|
9
|
+
dashboard/node_modules/
|
|
10
|
+
dashboard/.next/
|
|
11
|
+
dashboard/out/
|
|
12
|
+
dashboard/.env.local
|
|
13
|
+
dashboard/.env
|
|
14
|
+
|
|
15
|
+
# OS
|
|
16
|
+
.DS_Store
|
|
17
|
+
*.swp
|
|
18
|
+
*.swo
|
|
19
|
+
|
|
20
|
+
# Editor
|
|
21
|
+
.vscode/
|
|
22
|
+
.idea/
|
|
23
|
+
dashboard/tsconfig.tsbuildinfo
|
|
24
|
+
# Added by code-review-graph
|
|
25
|
+
.code-review-graph/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `contractgate` Python SDK.
|
|
4
|
+
|
|
5
|
+
## 0.1.0 — 2026-04-26 (unreleased)
|
|
6
|
+
|
|
7
|
+
Initial scaffold per RFC-005.
|
|
8
|
+
|
|
9
|
+
- `Client` (sync) + `AsyncClient` (async) over httpx.
|
|
10
|
+
- `ingest`, `audit`, `get_contract`, `get_version`, `playground`.
|
|
11
|
+
- Local validator (`Contract.from_yaml`, `CompiledContract.validate`).
|
|
12
|
+
- Strict parity with Rust validator: same `ViolationKind`, same field
|
|
13
|
+
paths, same message text. Locked via shared fixture corpus.
|
|
14
|
+
- No PII transforms in the local validator (RFC-004 invariant).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContractGate
|
|
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,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: contractgate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for ContractGate — high-performance semantic contract enforcement gateway.
|
|
5
|
+
Project-URL: Homepage, https://contractgate.dev
|
|
6
|
+
Project-URL: Source, https://github.com/contractgate/contractgate
|
|
7
|
+
Author: ContractGate
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: contract,data-quality,ingestion,schema,validation
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: httpx<1.0,>=0.25
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# contractgate (Python SDK)
|
|
32
|
+
|
|
33
|
+
First-party Python SDK for [ContractGate][gw] — a high-performance
|
|
34
|
+
semantic contract enforcement gateway (Patent Pending).
|
|
35
|
+
|
|
36
|
+
[gw]: https://contractgate.dev
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install contractgate
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Python 3.9+. Runtime deps: `httpx`, `PyYAML`.
|
|
45
|
+
|
|
46
|
+
## Quickstart — HTTP client
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from contractgate import Client
|
|
50
|
+
|
|
51
|
+
cg = Client(base_url="https://gw.example.com", api_key="cg_live_...")
|
|
52
|
+
|
|
53
|
+
result = cg.ingest(
|
|
54
|
+
contract_id="11111111-1111-1111-1111-111111111111",
|
|
55
|
+
events=[
|
|
56
|
+
{"user_id": "alice_01", "event_type": "click", "timestamp": 1712000000},
|
|
57
|
+
],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
print(result.passed, "/", result.total, "events passed")
|
|
61
|
+
for r in result.results:
|
|
62
|
+
if not r.passed:
|
|
63
|
+
for v in r.violations:
|
|
64
|
+
print(v.field, v.kind, v.message)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Async equivalent:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import asyncio
|
|
71
|
+
from contractgate import AsyncClient
|
|
72
|
+
|
|
73
|
+
async def main():
|
|
74
|
+
async with AsyncClient(base_url="...", api_key="...") as cg:
|
|
75
|
+
result = await cg.ingest(contract_id="...", events=[...])
|
|
76
|
+
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quickstart — local validator
|
|
81
|
+
|
|
82
|
+
Pure-Python port of the Rust validator. Useful in unit tests and
|
|
83
|
+
pre-commit hooks:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from contractgate import Contract
|
|
87
|
+
|
|
88
|
+
contract = Contract.from_yaml(open("user_events.yaml").read())
|
|
89
|
+
compiled = contract.compile()
|
|
90
|
+
|
|
91
|
+
vr = compiled.validate({
|
|
92
|
+
"user_id": "alice_01",
|
|
93
|
+
"event_type": "click",
|
|
94
|
+
"timestamp": 1712000000,
|
|
95
|
+
})
|
|
96
|
+
assert vr.passed, vr.violations
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Caveats
|
|
100
|
+
|
|
101
|
+
- **Local validator does not run RFC-004 PII transforms** (`mask`,
|
|
102
|
+
`hash`, `drop`, `redact`). The per-contract salt is server-side
|
|
103
|
+
only. The gateway is the single source of truth for the
|
|
104
|
+
post-transform payload — read it from each per-event result's
|
|
105
|
+
`transformed_event` field.
|
|
106
|
+
- **Audit honesty**: every per-event result carries the
|
|
107
|
+
`contract_version` that *actually matched* the event (relevant
|
|
108
|
+
under `multi_stable_resolution: fallback`). Surface it as-is — do
|
|
109
|
+
not substitute the requested version.
|
|
110
|
+
- **Retries are off by default.** Layer `httpx.HTTPTransport(retries=)`
|
|
111
|
+
or `tenacity` if you need them. Avoid client-side retry on ingest
|
|
112
|
+
to prevent double-write; use the gateway's quarantine replay
|
|
113
|
+
endpoint instead.
|
|
114
|
+
- `httpx` is pinned `>=0.25,<1.0`; we'll widen once 1.x ships.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# contractgate (Python SDK)
|
|
2
|
+
|
|
3
|
+
First-party Python SDK for [ContractGate][gw] — a high-performance
|
|
4
|
+
semantic contract enforcement gateway (Patent Pending).
|
|
5
|
+
|
|
6
|
+
[gw]: https://contractgate.dev
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install contractgate
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Python 3.9+. Runtime deps: `httpx`, `PyYAML`.
|
|
15
|
+
|
|
16
|
+
## Quickstart — HTTP client
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from contractgate import Client
|
|
20
|
+
|
|
21
|
+
cg = Client(base_url="https://gw.example.com", api_key="cg_live_...")
|
|
22
|
+
|
|
23
|
+
result = cg.ingest(
|
|
24
|
+
contract_id="11111111-1111-1111-1111-111111111111",
|
|
25
|
+
events=[
|
|
26
|
+
{"user_id": "alice_01", "event_type": "click", "timestamp": 1712000000},
|
|
27
|
+
],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
print(result.passed, "/", result.total, "events passed")
|
|
31
|
+
for r in result.results:
|
|
32
|
+
if not r.passed:
|
|
33
|
+
for v in r.violations:
|
|
34
|
+
print(v.field, v.kind, v.message)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Async equivalent:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import asyncio
|
|
41
|
+
from contractgate import AsyncClient
|
|
42
|
+
|
|
43
|
+
async def main():
|
|
44
|
+
async with AsyncClient(base_url="...", api_key="...") as cg:
|
|
45
|
+
result = await cg.ingest(contract_id="...", events=[...])
|
|
46
|
+
|
|
47
|
+
asyncio.run(main())
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quickstart — local validator
|
|
51
|
+
|
|
52
|
+
Pure-Python port of the Rust validator. Useful in unit tests and
|
|
53
|
+
pre-commit hooks:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from contractgate import Contract
|
|
57
|
+
|
|
58
|
+
contract = Contract.from_yaml(open("user_events.yaml").read())
|
|
59
|
+
compiled = contract.compile()
|
|
60
|
+
|
|
61
|
+
vr = compiled.validate({
|
|
62
|
+
"user_id": "alice_01",
|
|
63
|
+
"event_type": "click",
|
|
64
|
+
"timestamp": 1712000000,
|
|
65
|
+
})
|
|
66
|
+
assert vr.passed, vr.violations
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Caveats
|
|
70
|
+
|
|
71
|
+
- **Local validator does not run RFC-004 PII transforms** (`mask`,
|
|
72
|
+
`hash`, `drop`, `redact`). The per-contract salt is server-side
|
|
73
|
+
only. The gateway is the single source of truth for the
|
|
74
|
+
post-transform payload — read it from each per-event result's
|
|
75
|
+
`transformed_event` field.
|
|
76
|
+
- **Audit honesty**: every per-event result carries the
|
|
77
|
+
`contract_version` that *actually matched* the event (relevant
|
|
78
|
+
under `multi_stable_resolution: fallback`). Surface it as-is — do
|
|
79
|
+
not substitute the requested version.
|
|
80
|
+
- **Retries are off by default.** Layer `httpx.HTTPTransport(retries=)`
|
|
81
|
+
or `tenacity` if you need them. Avoid client-side retry on ingest
|
|
82
|
+
to prevent double-write; use the gateway's quarantine replay
|
|
83
|
+
endpoint instead.
|
|
84
|
+
- `httpx` is pinned `>=0.25,<1.0`; we'll widen once 1.x ships.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.18"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "contractgate"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for ContractGate — high-performance semantic contract enforcement gateway."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "ContractGate" }]
|
|
13
|
+
keywords = ["contract", "validation", "data-quality", "ingestion", "schema"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Software Development :: Libraries",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"httpx>=0.25,<1.0",
|
|
27
|
+
"PyYAML>=6.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=8.0",
|
|
33
|
+
"pytest-asyncio>=0.23",
|
|
34
|
+
"pytest-httpx>=0.30",
|
|
35
|
+
"mypy>=1.8",
|
|
36
|
+
"ruff>=0.5",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://contractgate.dev"
|
|
41
|
+
Source = "https://github.com/contractgate/contractgate"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/contractgate"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
asyncio_mode = "auto"
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
line-length = 100
|
|
52
|
+
target-version = "py39"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""ContractGate Python SDK.
|
|
2
|
+
|
|
3
|
+
First-party client and pure-Python validator for the ContractGate
|
|
4
|
+
semantic contract enforcement gateway.
|
|
5
|
+
|
|
6
|
+
Public surface:
|
|
7
|
+
Client, AsyncClient -- HTTP clients (sync, async)
|
|
8
|
+
Contract, CompiledContract -- local contract parse + compile
|
|
9
|
+
FieldDefinition, FieldType -- ontology types
|
|
10
|
+
MetricDefinition, MetricType -- metric types
|
|
11
|
+
Transform, TransformKind, -- RFC-004 declarations (declared, not run)
|
|
12
|
+
MaskStyle
|
|
13
|
+
ValidationResult, Violation, -- validator outputs
|
|
14
|
+
ViolationKind
|
|
15
|
+
BatchIngestResponse, -- HTTP response shapes
|
|
16
|
+
IngestEventResult,
|
|
17
|
+
AuditEntry, ContractResponse,
|
|
18
|
+
VersionResponse, VersionSummary,
|
|
19
|
+
IngestionStats
|
|
20
|
+
ContractGateError, HTTPError, -- error hierarchy
|
|
21
|
+
BadRequestError, AuthError,
|
|
22
|
+
NotFoundError, ConflictError,
|
|
23
|
+
ValidationFailedError,
|
|
24
|
+
ServerError, ConnectionError,
|
|
25
|
+
ContractCompileError
|
|
26
|
+
|
|
27
|
+
See README.md for usage. See ../docs/rfcs/005-python-sdk.md for the
|
|
28
|
+
design rationale.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from contractgate._version import __version__
|
|
32
|
+
from contractgate.async_client import AsyncClient
|
|
33
|
+
from contractgate.client import Client
|
|
34
|
+
from contractgate.contract import (
|
|
35
|
+
CompiledContract,
|
|
36
|
+
Contract,
|
|
37
|
+
FieldDefinition,
|
|
38
|
+
FieldType,
|
|
39
|
+
MaskStyle,
|
|
40
|
+
MetricDefinition,
|
|
41
|
+
MetricType,
|
|
42
|
+
Transform,
|
|
43
|
+
TransformKind,
|
|
44
|
+
)
|
|
45
|
+
from contractgate.exceptions import (
|
|
46
|
+
AuthError,
|
|
47
|
+
BadRequestError,
|
|
48
|
+
ConflictError,
|
|
49
|
+
ConnectionError,
|
|
50
|
+
ContractCompileError,
|
|
51
|
+
ContractGateError,
|
|
52
|
+
HTTPError,
|
|
53
|
+
NotFoundError,
|
|
54
|
+
ServerError,
|
|
55
|
+
ValidationFailedError,
|
|
56
|
+
)
|
|
57
|
+
from contractgate.models import (
|
|
58
|
+
AuditEntry,
|
|
59
|
+
BatchIngestResponse,
|
|
60
|
+
ContractResponse,
|
|
61
|
+
IngestEventResult,
|
|
62
|
+
IngestionStats,
|
|
63
|
+
ValidationResult,
|
|
64
|
+
VersionResponse,
|
|
65
|
+
VersionSummary,
|
|
66
|
+
Violation,
|
|
67
|
+
ViolationKind,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
__all__ = [
|
|
71
|
+
"__version__",
|
|
72
|
+
# Clients
|
|
73
|
+
"Client",
|
|
74
|
+
"AsyncClient",
|
|
75
|
+
# Contract / validator
|
|
76
|
+
"Contract",
|
|
77
|
+
"CompiledContract",
|
|
78
|
+
"FieldDefinition",
|
|
79
|
+
"FieldType",
|
|
80
|
+
"MetricDefinition",
|
|
81
|
+
"MetricType",
|
|
82
|
+
"Transform",
|
|
83
|
+
"TransformKind",
|
|
84
|
+
"MaskStyle",
|
|
85
|
+
# Models
|
|
86
|
+
"ValidationResult",
|
|
87
|
+
"Violation",
|
|
88
|
+
"ViolationKind",
|
|
89
|
+
"BatchIngestResponse",
|
|
90
|
+
"IngestEventResult",
|
|
91
|
+
"AuditEntry",
|
|
92
|
+
"ContractResponse",
|
|
93
|
+
"VersionResponse",
|
|
94
|
+
"VersionSummary",
|
|
95
|
+
"IngestionStats",
|
|
96
|
+
# Errors
|
|
97
|
+
"ContractGateError",
|
|
98
|
+
"HTTPError",
|
|
99
|
+
"BadRequestError",
|
|
100
|
+
"AuthError",
|
|
101
|
+
"NotFoundError",
|
|
102
|
+
"ConflictError",
|
|
103
|
+
"ValidationFailedError",
|
|
104
|
+
"ServerError",
|
|
105
|
+
"ConnectionError",
|
|
106
|
+
"ContractCompileError",
|
|
107
|
+
]
|