alitycs 1.0.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.
- alitycs-1.0.0/.gitignore +9 -0
- alitycs-1.0.0/LICENSE +21 -0
- alitycs-1.0.0/PKG-INFO +155 -0
- alitycs-1.0.0/README.md +125 -0
- alitycs-1.0.0/pyproject.toml +63 -0
- alitycs-1.0.0/scripts/coderabbit-schema.v2.json +2273 -0
- alitycs-1.0.0/scripts/coderabbit-validator-requirements.txt +451 -0
- alitycs-1.0.0/scripts/coverage_gate.py +68 -0
- alitycs-1.0.0/scripts/e2e_run.py +99 -0
- alitycs-1.0.0/scripts/test-coderabbit-policy.rb +168 -0
- alitycs-1.0.0/scripts/validate-coderabbit.sh +44 -0
- alitycs-1.0.0/scripts/verify-workflow-pins.rb +528 -0
- alitycs-1.0.0/src/alitycs/__init__.py +63 -0
- alitycs-1.0.0/src/alitycs/batch.py +500 -0
- alitycs-1.0.0/src/alitycs/client.py +507 -0
- alitycs-1.0.0/src/alitycs/config.py +85 -0
- alitycs-1.0.0/src/alitycs/context.py +85 -0
- alitycs-1.0.0/src/alitycs/persistence.py +317 -0
- alitycs-1.0.0/src/alitycs/session.py +81 -0
- alitycs-1.0.0/src/alitycs/transport.py +308 -0
- alitycs-1.0.0/src/alitycs/types.py +298 -0
- alitycs-1.0.0/src/alitycs/utils.py +161 -0
- alitycs-1.0.0/tests/conftest.py +144 -0
- alitycs-1.0.0/tests/integration/test_client_e2e.py +225 -0
- alitycs-1.0.0/tests/unit/test_batch.py +675 -0
- alitycs-1.0.0/tests/unit/test_client.py +585 -0
- alitycs-1.0.0/tests/unit/test_config.py +105 -0
- alitycs-1.0.0/tests/unit/test_context.py +127 -0
- alitycs-1.0.0/tests/unit/test_e2e_script.py +51 -0
- alitycs-1.0.0/tests/unit/test_session.py +98 -0
- alitycs-1.0.0/tests/unit/test_transport.py +494 -0
- alitycs-1.0.0/tests/unit/test_types.py +267 -0
- alitycs-1.0.0/tests/unit/test_utils.py +147 -0
alitycs-1.0.0/.gitignore
ADDED
alitycs-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alitycs Team
|
|
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.
|
alitycs-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: alitycs
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Alitycs analytics SDK for Python servers
|
|
5
|
+
Project-URL: Homepage, https://alitycs.com
|
|
6
|
+
Project-URL: Repository, https://github.com/alitycs/alitycs-sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/alitycs/alitycs-sdk-python/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: alitycs,analytics,events,telemetry
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
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: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: System :: Logging
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: build==1.3.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: editables==0.5; extra == 'dev'
|
|
25
|
+
Requires-Dist: hatchling==1.27.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-cov==6.3.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest==8.4.2; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff==0.12.3; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# alitycs-python
|
|
32
|
+
|
|
33
|
+
Official Alitycs analytics SDK for Python servers. Zero runtime dependencies —
|
|
34
|
+
HTTP uses `urllib.request` from the standard library.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install alitycs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from alitycs import Alitycs
|
|
46
|
+
|
|
47
|
+
client = Alitycs(api_key="pk_...")
|
|
48
|
+
|
|
49
|
+
client.track("checkout_completed", {"plan": "pro", "mrr": 49})
|
|
50
|
+
client.identify("user-123", {"email": "user@example.com"})
|
|
51
|
+
client.page("settings")
|
|
52
|
+
|
|
53
|
+
# Deliver everything queued before your process exits:
|
|
54
|
+
client.shutdown()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
For a client shared by concurrent server requests, scope identity to each event
|
|
58
|
+
instead of changing ambient state with `identify()`:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
client.track("checkout_started", user_id=request.user_id)
|
|
62
|
+
client.capture_error("checkout_failed", {"code": "E_CARD"}, user_id=request.user_id)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The same `user_id` keyword is accepted by `track_revenue()` and `page()` and
|
|
66
|
+
does not change the identity used by any other call.
|
|
67
|
+
|
|
68
|
+
With batching enabled (the default), events are queued and dispatched on a daemon flusher thread,
|
|
69
|
+
so `track` does not block on network I/O. With `batching=False`, each `track` call sends inline and
|
|
70
|
+
can block up to the configured request/retry limits. Batches flush when `flush_size` (default 20)
|
|
71
|
+
events are queued, every `flush_interval` seconds (default 2.0), or when you call `flush()` /
|
|
72
|
+
`shutdown()` explicitly. `shutdown()` waits up to 30 seconds by default; pass
|
|
73
|
+
`join_timeout=None` only when an unbounded drain is appropriate. On process exit a safety net
|
|
74
|
+
drains live instances; SIGTERM and SIGINT also trigger a best-effort drain before the default
|
|
75
|
+
termination disposition is restored (registered from the main thread only).
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
Alitycs(
|
|
81
|
+
api_key="pk_...", # required
|
|
82
|
+
endpoint="https://api.alitycs.com/events",
|
|
83
|
+
flush_size=20, # events per batch
|
|
84
|
+
flush_interval=2.0, # seconds; None disables the timer
|
|
85
|
+
debug=False,
|
|
86
|
+
max_queue_size=1000,
|
|
87
|
+
max_retries=3, # exponential backoff from retry_backoff_base
|
|
88
|
+
session_timeout=1800.0,
|
|
89
|
+
batching=True, # False sends each event inline
|
|
90
|
+
request_timeout=10.0,
|
|
91
|
+
retry_backoff_base=1.0,
|
|
92
|
+
persistence_path=None, # optional exact in-flight batch WAL file
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Delivery guarantees
|
|
97
|
+
|
|
98
|
+
- **Honest results**: `flush()` returns `True` only when every event was delivered.
|
|
99
|
+
Transient failures re-queue survivors at the head without persistence; with
|
|
100
|
+
`persistence_path`, the exact serialized in-flight batch remains on disk for restart.
|
|
101
|
+
Permanent refusals are dropped loudly.
|
|
102
|
+
- **No silent loss**: delivery failures and local rejections are logged at warn level
|
|
103
|
+
(never hidden behind `debug`) and counted — see `pending`, `rejected_locally`,
|
|
104
|
+
plus `delivered_total` / `requeued_total` / `lost_total` on the batch manager.
|
|
105
|
+
- **Split-on-rejection**: an HTTP 400 can mean one event poisoned a whole batch, so the
|
|
106
|
+
SDK splits that response in half to isolate valid events, with a hard cap of 64 sends.
|
|
107
|
+
Authentication, authorization, redirect, and other permanent responses are never split.
|
|
108
|
+
- Retries reuse the exact batch body so `batchId` stays stable for server-side dedup.
|
|
109
|
+
- SDK-generated exponential backoff is capped at 10 seconds. A server `Retry-After`
|
|
110
|
+
replaces that generated delay and is capped at one hour to keep delivery bounded.
|
|
111
|
+
- A new process using the same `persistence_path` replays retained bodies on
|
|
112
|
+
`flush()` (or an unbounded shutdown) and honors any remaining persisted `Retry-After` deadline.
|
|
113
|
+
If a finite shutdown deadline expires first, queued events are appended to the WAL in FIFO order.
|
|
114
|
+
The WAL starts immediately before the first network attempt and is capped at `max_queue_size`
|
|
115
|
+
retained events. Each path is exclusively owned by one live client; a same-process registry and
|
|
116
|
+
a POSIX advisory lock reject overlapping owners. After a fork, the child drops its
|
|
117
|
+
copy of the parent-owned queue and detaches from the inherited WAL; create a fresh client with a
|
|
118
|
+
child-specific path when child delivery also needs durability.
|
|
119
|
+
|
|
120
|
+
## Ingestion limits
|
|
121
|
+
|
|
122
|
+
Events violating these limits are rejected locally at build time — never queued,
|
|
123
|
+
never sent, never truncated (they would cause the server to reject the whole batch):
|
|
124
|
+
|
|
125
|
+
| Limit | Value |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| Properties per event | ≤ 50 |
|
|
128
|
+
| Property key length | ≤ 100 chars |
|
|
129
|
+
| Property value length | ≤ 1000 chars |
|
|
130
|
+
| Estimated event size | ≤ 64 KB |
|
|
131
|
+
| Required fields | non-blank name AND (`userId` or `anonymousId`) |
|
|
132
|
+
| Timestamp | epoch **milliseconds**, ≤ 7 days past, none future |
|
|
133
|
+
|
|
134
|
+
Revenue payloads (trusted ingestion, requires a key with `revenue:write`) validate
|
|
135
|
+
their per-kind fields strictly:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from alitycs import RevenuePayload
|
|
139
|
+
|
|
140
|
+
RevenuePayload.transaction(fact_id="inv-1", amount="19.99", currency="USD")
|
|
141
|
+
RevenuePayload.mrr_snapshot(
|
|
142
|
+
fact_id="snap-1", subscription_id="sub-1",
|
|
143
|
+
customer_id="cus-1", mrr_amount="250.00", currency="USD",
|
|
144
|
+
)
|
|
145
|
+
RevenuePayload.mrr_baseline_complete(fact_id="base-1", currency="USD", expected_active_subscriptions=120)
|
|
146
|
+
|
|
147
|
+
client.track_revenue(RevenuePayload.transaction(fact_id="inv-2", amount="9.99", currency="EUR"))
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pytest tests/unit # unit tests, no network
|
|
154
|
+
scripts/e2e_run.py # end-to-end against a local stack (Docker)
|
|
155
|
+
```
|
alitycs-1.0.0/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# alitycs-python
|
|
2
|
+
|
|
3
|
+
Official Alitycs analytics SDK for Python servers. Zero runtime dependencies —
|
|
4
|
+
HTTP uses `urllib.request` from the standard library.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install alitycs
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from alitycs import Alitycs
|
|
16
|
+
|
|
17
|
+
client = Alitycs(api_key="pk_...")
|
|
18
|
+
|
|
19
|
+
client.track("checkout_completed", {"plan": "pro", "mrr": 49})
|
|
20
|
+
client.identify("user-123", {"email": "user@example.com"})
|
|
21
|
+
client.page("settings")
|
|
22
|
+
|
|
23
|
+
# Deliver everything queued before your process exits:
|
|
24
|
+
client.shutdown()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For a client shared by concurrent server requests, scope identity to each event
|
|
28
|
+
instead of changing ambient state with `identify()`:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
client.track("checkout_started", user_id=request.user_id)
|
|
32
|
+
client.capture_error("checkout_failed", {"code": "E_CARD"}, user_id=request.user_id)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The same `user_id` keyword is accepted by `track_revenue()` and `page()` and
|
|
36
|
+
does not change the identity used by any other call.
|
|
37
|
+
|
|
38
|
+
With batching enabled (the default), events are queued and dispatched on a daemon flusher thread,
|
|
39
|
+
so `track` does not block on network I/O. With `batching=False`, each `track` call sends inline and
|
|
40
|
+
can block up to the configured request/retry limits. Batches flush when `flush_size` (default 20)
|
|
41
|
+
events are queued, every `flush_interval` seconds (default 2.0), or when you call `flush()` /
|
|
42
|
+
`shutdown()` explicitly. `shutdown()` waits up to 30 seconds by default; pass
|
|
43
|
+
`join_timeout=None` only when an unbounded drain is appropriate. On process exit a safety net
|
|
44
|
+
drains live instances; SIGTERM and SIGINT also trigger a best-effort drain before the default
|
|
45
|
+
termination disposition is restored (registered from the main thread only).
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
Alitycs(
|
|
51
|
+
api_key="pk_...", # required
|
|
52
|
+
endpoint="https://api.alitycs.com/events",
|
|
53
|
+
flush_size=20, # events per batch
|
|
54
|
+
flush_interval=2.0, # seconds; None disables the timer
|
|
55
|
+
debug=False,
|
|
56
|
+
max_queue_size=1000,
|
|
57
|
+
max_retries=3, # exponential backoff from retry_backoff_base
|
|
58
|
+
session_timeout=1800.0,
|
|
59
|
+
batching=True, # False sends each event inline
|
|
60
|
+
request_timeout=10.0,
|
|
61
|
+
retry_backoff_base=1.0,
|
|
62
|
+
persistence_path=None, # optional exact in-flight batch WAL file
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Delivery guarantees
|
|
67
|
+
|
|
68
|
+
- **Honest results**: `flush()` returns `True` only when every event was delivered.
|
|
69
|
+
Transient failures re-queue survivors at the head without persistence; with
|
|
70
|
+
`persistence_path`, the exact serialized in-flight batch remains on disk for restart.
|
|
71
|
+
Permanent refusals are dropped loudly.
|
|
72
|
+
- **No silent loss**: delivery failures and local rejections are logged at warn level
|
|
73
|
+
(never hidden behind `debug`) and counted — see `pending`, `rejected_locally`,
|
|
74
|
+
plus `delivered_total` / `requeued_total` / `lost_total` on the batch manager.
|
|
75
|
+
- **Split-on-rejection**: an HTTP 400 can mean one event poisoned a whole batch, so the
|
|
76
|
+
SDK splits that response in half to isolate valid events, with a hard cap of 64 sends.
|
|
77
|
+
Authentication, authorization, redirect, and other permanent responses are never split.
|
|
78
|
+
- Retries reuse the exact batch body so `batchId` stays stable for server-side dedup.
|
|
79
|
+
- SDK-generated exponential backoff is capped at 10 seconds. A server `Retry-After`
|
|
80
|
+
replaces that generated delay and is capped at one hour to keep delivery bounded.
|
|
81
|
+
- A new process using the same `persistence_path` replays retained bodies on
|
|
82
|
+
`flush()` (or an unbounded shutdown) and honors any remaining persisted `Retry-After` deadline.
|
|
83
|
+
If a finite shutdown deadline expires first, queued events are appended to the WAL in FIFO order.
|
|
84
|
+
The WAL starts immediately before the first network attempt and is capped at `max_queue_size`
|
|
85
|
+
retained events. Each path is exclusively owned by one live client; a same-process registry and
|
|
86
|
+
a POSIX advisory lock reject overlapping owners. After a fork, the child drops its
|
|
87
|
+
copy of the parent-owned queue and detaches from the inherited WAL; create a fresh client with a
|
|
88
|
+
child-specific path when child delivery also needs durability.
|
|
89
|
+
|
|
90
|
+
## Ingestion limits
|
|
91
|
+
|
|
92
|
+
Events violating these limits are rejected locally at build time — never queued,
|
|
93
|
+
never sent, never truncated (they would cause the server to reject the whole batch):
|
|
94
|
+
|
|
95
|
+
| Limit | Value |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| Properties per event | ≤ 50 |
|
|
98
|
+
| Property key length | ≤ 100 chars |
|
|
99
|
+
| Property value length | ≤ 1000 chars |
|
|
100
|
+
| Estimated event size | ≤ 64 KB |
|
|
101
|
+
| Required fields | non-blank name AND (`userId` or `anonymousId`) |
|
|
102
|
+
| Timestamp | epoch **milliseconds**, ≤ 7 days past, none future |
|
|
103
|
+
|
|
104
|
+
Revenue payloads (trusted ingestion, requires a key with `revenue:write`) validate
|
|
105
|
+
their per-kind fields strictly:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from alitycs import RevenuePayload
|
|
109
|
+
|
|
110
|
+
RevenuePayload.transaction(fact_id="inv-1", amount="19.99", currency="USD")
|
|
111
|
+
RevenuePayload.mrr_snapshot(
|
|
112
|
+
fact_id="snap-1", subscription_id="sub-1",
|
|
113
|
+
customer_id="cus-1", mrr_amount="250.00", currency="USD",
|
|
114
|
+
)
|
|
115
|
+
RevenuePayload.mrr_baseline_complete(fact_id="base-1", currency="USD", expected_active_subscriptions=120)
|
|
116
|
+
|
|
117
|
+
client.track_revenue(RevenuePayload.transaction(fact_id="inv-2", amount="9.99", currency="EUR"))
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pytest tests/unit # unit tests, no network
|
|
124
|
+
scripts/e2e_run.py # end-to-end against a local stack (Docker)
|
|
125
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling==1.27.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "alitycs"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Alitycs analytics SDK for Python servers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
keywords = ["analytics", "alitycs", "events", "telemetry"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 5 - Production/Stable",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.9",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: System :: Logging",
|
|
24
|
+
]
|
|
25
|
+
# Zero runtime dependencies by design: HTTP uses urllib.request from the stdlib,
|
|
26
|
+
# mirroring how alitycs-sdk-jvm uses java.net.http.
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://alitycs.com"
|
|
31
|
+
Repository = "https://github.com/alitycs/alitycs-sdk-python"
|
|
32
|
+
Issues = "https://github.com/alitycs/alitycs-sdk-python/issues"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"build==1.3.0",
|
|
37
|
+
"editables==0.5",
|
|
38
|
+
"hatchling==1.27.0",
|
|
39
|
+
"pytest==8.4.2",
|
|
40
|
+
"pytest-cov==6.3.0",
|
|
41
|
+
"ruff==0.12.3",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/alitycs"]
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
include = ["src", "tests", "scripts", "README.md", "pyproject.toml"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
pythonpath = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.coverage.run]
|
|
55
|
+
branch = true
|
|
56
|
+
source = ["alitycs"]
|
|
57
|
+
|
|
58
|
+
[tool.coverage.report]
|
|
59
|
+
# coverage.py has no function-coverage metric, so the contract's "functions >= 85%"
|
|
60
|
+
# gate is covered by branch coverage instead (see .agents/plans/sdk-python.md).
|
|
61
|
+
# scripts/coverage_gate.py additionally enforces the branch gate from coverage.json.
|
|
62
|
+
fail_under = 90
|
|
63
|
+
show_missing = true
|