sahra-shared 0.2.2__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.
- sahra_shared-0.2.2/.gitignore +104 -0
- sahra_shared-0.2.2/LICENSE +21 -0
- sahra_shared-0.2.2/PKG-INFO +106 -0
- sahra_shared-0.2.2/README.md +82 -0
- sahra_shared-0.2.2/pyproject.toml +196 -0
- sahra_shared-0.2.2/sahra_shared/__init__.py +0 -0
- sahra_shared-0.2.2/sahra_shared/auth/__init__.py +53 -0
- sahra_shared-0.2.2/sahra_shared/auth/capabilities.py +299 -0
- sahra_shared-0.2.2/sahra_shared/auth/claims.py +90 -0
- sahra_shared-0.2.2/sahra_shared/auth/coverage.py +248 -0
- sahra_shared-0.2.2/sahra_shared/auth/dependencies.py +153 -0
- sahra_shared-0.2.2/sahra_shared/auth/guards.py +211 -0
- sahra_shared-0.2.2/sahra_shared/events/__init__.py +5 -0
- sahra_shared-0.2.2/sahra_shared/events/connection.py +66 -0
- sahra_shared-0.2.2/sahra_shared/events/consumer.py +140 -0
- sahra_shared-0.2.2/sahra_shared/events/envelope.py +18 -0
- sahra_shared-0.2.2/sahra_shared/events/publisher.py +76 -0
- sahra_shared-0.2.2/sahra_shared/events/subjects.py +43 -0
- sahra_shared-0.2.2/sahra_shared/http/__init__.py +5 -0
- sahra_shared-0.2.2/sahra_shared/http/exceptions.py +101 -0
- sahra_shared-0.2.2/sahra_shared/http/health.py +28 -0
- sahra_shared-0.2.2/sahra_shared/http/models.py +37 -0
- sahra_shared-0.2.2/sahra_shared/privacy/__init__.py +13 -0
- sahra_shared-0.2.2/sahra_shared/privacy/policies.py +53 -0
- sahra_shared-0.2.2/sahra_shared/privacy/types.py +12 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/__init__.py +64 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/context.py +46 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/http_client.py +50 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/http_middleware.py +108 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/lifecycle.py +193 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/logging.py +123 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/metrics.py +54 -0
- sahra_shared-0.2.2/sahra_shared/telemetry/otel.py +37 -0
- sahra_shared-0.2.2/sahra_shared/tenancy/__init__.py +89 -0
- sahra_shared-0.2.2/sahra_shared/tenancy/context.py +137 -0
- sahra_shared-0.2.2/sahra_shared/tenancy/rls.py +250 -0
- sahra_shared-0.2.2/sahra_shared/tenancy/routing.py +108 -0
- sahra_shared-0.2.2/sahra_shared/tenancy/session.py +244 -0
- sahra_shared-0.2.2/sahra_shared/tenancy/verify.py +351 -0
- sahra_shared-0.2.2/uv.lock +1468 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Python Bytecode
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Virtual Environment
|
|
7
|
+
venv/
|
|
8
|
+
.env/
|
|
9
|
+
.venv/
|
|
10
|
+
env/
|
|
11
|
+
ENV/
|
|
12
|
+
*.venv
|
|
13
|
+
|
|
14
|
+
# Development Tools
|
|
15
|
+
.idea/
|
|
16
|
+
.vscode/
|
|
17
|
+
*.swp
|
|
18
|
+
*.swo
|
|
19
|
+
*.swn
|
|
20
|
+
*.swm
|
|
21
|
+
|
|
22
|
+
# Testing and Coverage
|
|
23
|
+
htmlcov/
|
|
24
|
+
.tox/
|
|
25
|
+
.nox/
|
|
26
|
+
*.coverage
|
|
27
|
+
.coverage.*
|
|
28
|
+
.cache/
|
|
29
|
+
pytest_cache/
|
|
30
|
+
tests/__pycache__/
|
|
31
|
+
test-results/
|
|
32
|
+
coverage.xml
|
|
33
|
+
*.cover
|
|
34
|
+
|
|
35
|
+
# Logs and Temporary Files
|
|
36
|
+
*.log
|
|
37
|
+
*.tmp
|
|
38
|
+
*.temp
|
|
39
|
+
*.pid
|
|
40
|
+
*.bak
|
|
41
|
+
*.old
|
|
42
|
+
*.orig
|
|
43
|
+
*.rej
|
|
44
|
+
|
|
45
|
+
# Build Artifacts
|
|
46
|
+
build/
|
|
47
|
+
dist/
|
|
48
|
+
*.egg-info/
|
|
49
|
+
*.egg
|
|
50
|
+
.installed.cfg
|
|
51
|
+
eggs/
|
|
52
|
+
sdist/
|
|
53
|
+
wheels/
|
|
54
|
+
share/python-wheels/
|
|
55
|
+
|
|
56
|
+
# Type Checker Caches
|
|
57
|
+
.mypy_cache/
|
|
58
|
+
.dmypy.json
|
|
59
|
+
dmypy.json
|
|
60
|
+
.pytest_cache/
|
|
61
|
+
.ruff_cache/
|
|
62
|
+
.pyright/
|
|
63
|
+
|
|
64
|
+
# IDE Configurations
|
|
65
|
+
*.iml
|
|
66
|
+
*.sublime-project
|
|
67
|
+
*.sublime-workspace
|
|
68
|
+
|
|
69
|
+
# System Files
|
|
70
|
+
.DS_Store
|
|
71
|
+
Thumbs.db
|
|
72
|
+
desktop.ini
|
|
73
|
+
|
|
74
|
+
# Sensitive Data
|
|
75
|
+
.secret/
|
|
76
|
+
*.key
|
|
77
|
+
*.pem
|
|
78
|
+
.env
|
|
79
|
+
.env.*
|
|
80
|
+
!.env.example
|
|
81
|
+
|
|
82
|
+
tmp/
|
|
83
|
+
temp/
|
|
84
|
+
|
|
85
|
+
# uv (Python package manager)
|
|
86
|
+
.uv/
|
|
87
|
+
# uv.lock is COMMITTED for this repository. It is the shared kernel: its
|
|
88
|
+
# resolved dependency set is part of what a release tag means, and every one
|
|
89
|
+
# of the 18 consumers resolves `sahra-shared` from a git revision of this
|
|
90
|
+
# repo. An ignored lockfile made the release irreproducible, so it is tracked
|
|
91
|
+
# deliberately. Do not re-add it here.
|
|
92
|
+
|
|
93
|
+
# FastAPI / Alembic
|
|
94
|
+
alembic/versions/*.pyc
|
|
95
|
+
|
|
96
|
+
# Docker
|
|
97
|
+
*.env.docker
|
|
98
|
+
.dockerenv
|
|
99
|
+
|
|
100
|
+
# Local agent/tooling state. Agent worktrees must NEVER be committed.
|
|
101
|
+
# Note: the .claude/ rules already tracked in this repo stay tracked — gitignore
|
|
102
|
+
# does not untrack existing files. Use `git add -f` to add a new one on purpose.
|
|
103
|
+
.claude/
|
|
104
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hycind Santhibhavan
|
|
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,106 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sahra-shared
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Sahra Trans — shared kernel library
|
|
5
|
+
Project-URL: Homepage, https://github.com/Sahra-Trans/sahra-shared
|
|
6
|
+
Project-URL: Repository, https://github.com/Sahra-Trans/sahra-shared
|
|
7
|
+
Project-URL: Issues, https://github.com/Sahra-Trans/sahra-shared/issues
|
|
8
|
+
Author: Hycind Santhibhavan
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.13
|
|
12
|
+
Requires-Dist: cryptography>=42.0.0
|
|
13
|
+
Requires-Dist: fastapi>=0.115.0
|
|
14
|
+
Requires-Dist: httpx>=0.27.0
|
|
15
|
+
Requires-Dist: nats-py>=2.7.0
|
|
16
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.24.0
|
|
17
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.45b0
|
|
18
|
+
Requires-Dist: opentelemetry-sdk>=1.24.0
|
|
19
|
+
Requires-Dist: prometheus-client>=0.20.0
|
|
20
|
+
Requires-Dist: pydantic>=2.0.0
|
|
21
|
+
Requires-Dist: pyjwt[crypto]>=2.8.0
|
|
22
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# sahra-shared
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/sahra-shared/)
|
|
28
|
+
[](https://pypi.org/project/sahra-shared/)
|
|
29
|
+
[](#license)
|
|
30
|
+
|
|
31
|
+
**A reusable Python library — the shared kernel for Sahra Trans services.**
|
|
32
|
+
|
|
33
|
+
`sahra-shared` is a *library*, not a service. It has **no HTTP server, no database, no
|
|
34
|
+
port, no container and no entrypoint**. Nothing here starts; you `import` it. It is
|
|
35
|
+
published to PyPI and consumed by the Sahra Trans services as an ordinary dependency.
|
|
36
|
+
|
|
37
|
+
The import namespace is **`sahra_shared`**.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv add sahra-shared
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Use
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from sahra_shared.auth import Capability, CapabilityGuards
|
|
49
|
+
from sahra_shared.events import NATSPublisher, SahraEvent
|
|
50
|
+
from sahra_shared.http import ProblemDetail, make_health_router
|
|
51
|
+
from sahra_shared.privacy import CountryPrivacyPolicy, PIIEmail
|
|
52
|
+
from sahra_shared.telemetry import AppLogger, setup_otel
|
|
53
|
+
from sahra_shared.tenancy import TenantScope, tenant_transaction
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What it provides
|
|
57
|
+
|
|
58
|
+
| Sub-package | Surface |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `sahra_shared.auth` | `TokenClaims`, RS256 JWT verification against JWKS, the closed `Capability` vocabulary and deny-by-default `CapabilityGuards` |
|
|
61
|
+
| `sahra_shared.events` | `SahraEvent` (CloudEvents envelope), `NATSPublisher` / consumer helpers |
|
|
62
|
+
| `sahra_shared.http` | `ProblemDetail` (RFC 7807) and `make_health_router` — a router **factory** a service mounts; this library serves nothing itself |
|
|
63
|
+
| `sahra_shared.privacy` | PII types (`PIIEmail`, `PIIPhone`, …) and per-country privacy policies |
|
|
64
|
+
| `sahra_shared.telemetry` | OpenTelemetry setup, structured logging, `call_service` (propagates `traceparent`) |
|
|
65
|
+
| `sahra_shared.tenancy` | `TenantScope`, transaction-bound tenant context, row-level-security helpers |
|
|
66
|
+
|
|
67
|
+
Sub-packages are independent: they do not import one another (only `telemetry` is
|
|
68
|
+
depended upon in common). That boundary is enforced by
|
|
69
|
+
`tests/test_import_boundaries.py`.
|
|
70
|
+
|
|
71
|
+
## Requirements
|
|
72
|
+
|
|
73
|
+
Python **>= 3.13**.
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
This repository is **uv-only**. Do not use `pip`, `venv`, Poetry or Twine.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv sync --all-groups # install dev + test dependency groups
|
|
81
|
+
uv run pytest # tests
|
|
82
|
+
uv run ruff check . # lint
|
|
83
|
+
uv run ruff format --check .# format
|
|
84
|
+
uv run ty check # type-check
|
|
85
|
+
uv build # build wheel + sdist
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`uv.lock` is committed deliberately: the resolved dependency set is part of what a
|
|
89
|
+
release tag means.
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
Design notes, the tenancy/RLS reference and the logging integration guide live
|
|
94
|
+
in the project repository alongside the source, and are not shipped in the
|
|
95
|
+
distribution.
|
|
96
|
+
|
|
97
|
+
## Releasing
|
|
98
|
+
|
|
99
|
+
Releases are built and published to PyPI by GitHub Actions using **Trusted
|
|
100
|
+
Publishing (OIDC)**. There is no PyPI API token and no stored publishing
|
|
101
|
+
credential. A `vX.Y.Z` tag builds, gates and publishes; a `vX.Y.Z-rc1` tag runs
|
|
102
|
+
the identical gate and stops before publishing.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# sahra-shared
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/sahra-shared/)
|
|
4
|
+
[](https://pypi.org/project/sahra-shared/)
|
|
5
|
+
[](#license)
|
|
6
|
+
|
|
7
|
+
**A reusable Python library — the shared kernel for Sahra Trans services.**
|
|
8
|
+
|
|
9
|
+
`sahra-shared` is a *library*, not a service. It has **no HTTP server, no database, no
|
|
10
|
+
port, no container and no entrypoint**. Nothing here starts; you `import` it. It is
|
|
11
|
+
published to PyPI and consumed by the Sahra Trans services as an ordinary dependency.
|
|
12
|
+
|
|
13
|
+
The import namespace is **`sahra_shared`**.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv add sahra-shared
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Use
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from sahra_shared.auth import Capability, CapabilityGuards
|
|
25
|
+
from sahra_shared.events import NATSPublisher, SahraEvent
|
|
26
|
+
from sahra_shared.http import ProblemDetail, make_health_router
|
|
27
|
+
from sahra_shared.privacy import CountryPrivacyPolicy, PIIEmail
|
|
28
|
+
from sahra_shared.telemetry import AppLogger, setup_otel
|
|
29
|
+
from sahra_shared.tenancy import TenantScope, tenant_transaction
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## What it provides
|
|
33
|
+
|
|
34
|
+
| Sub-package | Surface |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `sahra_shared.auth` | `TokenClaims`, RS256 JWT verification against JWKS, the closed `Capability` vocabulary and deny-by-default `CapabilityGuards` |
|
|
37
|
+
| `sahra_shared.events` | `SahraEvent` (CloudEvents envelope), `NATSPublisher` / consumer helpers |
|
|
38
|
+
| `sahra_shared.http` | `ProblemDetail` (RFC 7807) and `make_health_router` — a router **factory** a service mounts; this library serves nothing itself |
|
|
39
|
+
| `sahra_shared.privacy` | PII types (`PIIEmail`, `PIIPhone`, …) and per-country privacy policies |
|
|
40
|
+
| `sahra_shared.telemetry` | OpenTelemetry setup, structured logging, `call_service` (propagates `traceparent`) |
|
|
41
|
+
| `sahra_shared.tenancy` | `TenantScope`, transaction-bound tenant context, row-level-security helpers |
|
|
42
|
+
|
|
43
|
+
Sub-packages are independent: they do not import one another (only `telemetry` is
|
|
44
|
+
depended upon in common). That boundary is enforced by
|
|
45
|
+
`tests/test_import_boundaries.py`.
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
Python **>= 3.13**.
|
|
50
|
+
|
|
51
|
+
## Development
|
|
52
|
+
|
|
53
|
+
This repository is **uv-only**. Do not use `pip`, `venv`, Poetry or Twine.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv sync --all-groups # install dev + test dependency groups
|
|
57
|
+
uv run pytest # tests
|
|
58
|
+
uv run ruff check . # lint
|
|
59
|
+
uv run ruff format --check .# format
|
|
60
|
+
uv run ty check # type-check
|
|
61
|
+
uv build # build wheel + sdist
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`uv.lock` is committed deliberately: the resolved dependency set is part of what a
|
|
65
|
+
release tag means.
|
|
66
|
+
|
|
67
|
+
## Documentation
|
|
68
|
+
|
|
69
|
+
Design notes, the tenancy/RLS reference and the logging integration guide live
|
|
70
|
+
in the project repository alongside the source, and are not shipped in the
|
|
71
|
+
distribution.
|
|
72
|
+
|
|
73
|
+
## Releasing
|
|
74
|
+
|
|
75
|
+
Releases are built and published to PyPI by GitHub Actions using **Trusted
|
|
76
|
+
Publishing (OIDC)**. There is no PyPI API token and no stored publishing
|
|
77
|
+
credential. A `vX.Y.Z` tag builds, gates and publishes; a `vX.Y.Z-rc1` tag runs
|
|
78
|
+
the identical gate and stops before publishing.
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# ======================================================================
|
|
2
|
+
# PROJECT METADATA
|
|
3
|
+
# ======================================================================
|
|
4
|
+
[project]
|
|
5
|
+
name = "sahra-shared"
|
|
6
|
+
version = "0.2.2"
|
|
7
|
+
description = "Sahra Trans — shared kernel library"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Hycind Santhibhavan" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastapi>=0.115.0",
|
|
15
|
+
"pydantic>=2.0.0",
|
|
16
|
+
"nats-py>=2.7.0",
|
|
17
|
+
"PyJWT[crypto]>=2.8.0",
|
|
18
|
+
"opentelemetry-sdk>=1.24.0",
|
|
19
|
+
"opentelemetry-exporter-otlp-proto-grpc>=1.24.0",
|
|
20
|
+
"opentelemetry-instrumentation-fastapi>=0.45b0",
|
|
21
|
+
"prometheus-client>=0.20.0",
|
|
22
|
+
"httpx>=0.27.0",
|
|
23
|
+
"sqlalchemy>=2.0.0",
|
|
24
|
+
# Imported directly by sahra_shared/auth/dependencies.py to reject a JWKS entry that
|
|
25
|
+
# carries private key material. Already present transitively via PyJWT[crypto];
|
|
26
|
+
# declared so the import is not a hidden transitive dependency.
|
|
27
|
+
"cryptography>=42.0.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/Sahra-Trans/sahra-shared"
|
|
33
|
+
Repository = "https://github.com/Sahra-Trans/sahra-shared"
|
|
34
|
+
Issues = "https://github.com/Sahra-Trans/sahra-shared/issues"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Dev and test tooling lives in [dependency-groups] only. A duplicate
|
|
38
|
+
# [project.optional-dependencies] dev = [...] block used to shadow it; nothing
|
|
39
|
+
# installed the extra (Makefile and both CI workflows use `uv sync --all-groups`)
|
|
40
|
+
# while deptry read its entries as runtime dependencies and failed the build.
|
|
41
|
+
[dependency-groups]
|
|
42
|
+
dev = ["deptry", "ty", "ruff", "vulture", "bandit>=1.7.9", "pip-audit>=2.7.3", "pyright>=1.1.400"]
|
|
43
|
+
test = ["pytest", "pytest-asyncio", "httpx"]
|
|
44
|
+
|
|
45
|
+
# ======================================================================
|
|
46
|
+
# BUILD SYSTEM
|
|
47
|
+
# ======================================================================
|
|
48
|
+
[build-system]
|
|
49
|
+
requires = ["hatchling"]
|
|
50
|
+
build-backend = "hatchling.build"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["sahra_shared"]
|
|
54
|
+
|
|
55
|
+
# The default sdist is "everything git tracks". That shipped the internal agent
|
|
56
|
+
# instructions (.clinerules/.cursorrules/.windsurfrules, CLAUDE.md, GEMINI.md,
|
|
57
|
+
# QWEN.md, AGENTS.md), the git hooks and their tamper manifest, the GitHub and
|
|
58
|
+
# GitLab CI configuration -- and, because hatchling does not consult .gitignore
|
|
59
|
+
# for this, an untracked `.env` as well.
|
|
60
|
+
#
|
|
61
|
+
# So this is an ALLOWLIST, not a denylist: a new internal file is excluded by
|
|
62
|
+
# default and has to be named here to ship. A denylist publishes whatever
|
|
63
|
+
# nobody remembered to add to it.
|
|
64
|
+
#
|
|
65
|
+
# `docs/` and `tests/` are deliberately NOT shipped. `docs/migrations/` carries
|
|
66
|
+
# the tenant row-level-security migration and `docs/TENANCY.md` the schema
|
|
67
|
+
# design behind it; the tests reference internal endpoint paths and the scheme
|
|
68
|
+
# that authenticates them. None of it is needed to build, install or verify the
|
|
69
|
+
# library, and this is a public index. They remain in the repository, which is
|
|
70
|
+
# where contributors read them.
|
|
71
|
+
[tool.hatch.build.targets.sdist]
|
|
72
|
+
# Patterns are gitignore-style, so they are root-anchored with a leading "/".
|
|
73
|
+
# A bare "README.md" matches at ANY depth and dragged
|
|
74
|
+
# tests/typecheck_violations/README.md into the sdist.
|
|
75
|
+
include = [
|
|
76
|
+
"/sahra_shared/",
|
|
77
|
+
"/README.md",
|
|
78
|
+
"/LICENSE",
|
|
79
|
+
"/pyproject.toml",
|
|
80
|
+
"/uv.lock",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
# ======================================================================
|
|
84
|
+
# TY (Fast Type Checker)
|
|
85
|
+
# ======================================================================
|
|
86
|
+
[tool.ty.src]
|
|
87
|
+
include = ["sahra_shared"]
|
|
88
|
+
exclude = ["tests", ".venv"]
|
|
89
|
+
respect-ignore-files = true
|
|
90
|
+
|
|
91
|
+
[tool.ty.environment]
|
|
92
|
+
python-version = "3.13"
|
|
93
|
+
root = ["."]
|
|
94
|
+
|
|
95
|
+
[tool.ty.analysis]
|
|
96
|
+
allowed-unresolved-imports = []
|
|
97
|
+
replace-imports-with-any = []
|
|
98
|
+
respect-type-ignore-comments = true
|
|
99
|
+
|
|
100
|
+
[tool.ty.rules]
|
|
101
|
+
all = "warn"
|
|
102
|
+
|
|
103
|
+
[tool.ty.terminal]
|
|
104
|
+
output-format = "full"
|
|
105
|
+
error-on-warning = false
|
|
106
|
+
|
|
107
|
+
[[tool.ty.overrides]]
|
|
108
|
+
include = ["tests/**", "**/test_*.py"]
|
|
109
|
+
|
|
110
|
+
[tool.ty.overrides.rules]
|
|
111
|
+
possibly-unresolved-reference = "ignore"
|
|
112
|
+
|
|
113
|
+
# ======================================================================
|
|
114
|
+
# PYRIGHT (Type Checker)
|
|
115
|
+
# ======================================================================
|
|
116
|
+
# The 18 services pin pythonVersion = "3.12" while declaring
|
|
117
|
+
# requires-python = ">=3.13". The shared kernel targets 3.13, matching what it
|
|
118
|
+
# actually declares and what its `requires-python` floor allows.
|
|
119
|
+
[tool.pyright]
|
|
120
|
+
include = ["sahra_shared"]
|
|
121
|
+
exclude = [".venv", "**/__pycache__", "tests/typecheck_violations"]
|
|
122
|
+
pythonVersion = "3.13"
|
|
123
|
+
typeCheckingMode = "strict"
|
|
124
|
+
reportMissingTypeStubs = false
|
|
125
|
+
reportUnknownMemberType = false
|
|
126
|
+
reportUnnecessaryIsInstance = false
|
|
127
|
+
# FastAPI route handlers and exception handlers are registered by their
|
|
128
|
+
# decorator and never referenced by name; strict mode reports every one of them
|
|
129
|
+
# as unused. All current hits are of exactly that shape.
|
|
130
|
+
reportUnusedFunction = false
|
|
131
|
+
# Generic third-party models (nats, SahraEvent[Any]) resolve to partially
|
|
132
|
+
# unknown types at their use sites; the annotations we control are explicit.
|
|
133
|
+
reportUnknownVariableType = false
|
|
134
|
+
|
|
135
|
+
# ======================================================================
|
|
136
|
+
# DEPTRY (Dependency Checker)
|
|
137
|
+
# ======================================================================
|
|
138
|
+
[tool.deptry]
|
|
139
|
+
ignore = []
|
|
140
|
+
known_first_party = ["sahra_shared"]
|
|
141
|
+
exclude = ["tests", "\\.venv", ".claude", "docs"]
|
|
142
|
+
|
|
143
|
+
# ======================================================================
|
|
144
|
+
# VULTURE (Find Dead Code)
|
|
145
|
+
# ======================================================================
|
|
146
|
+
[tool.vulture]
|
|
147
|
+
paths = ["sahra_shared"]
|
|
148
|
+
exclude = ["tests", "venv", ".venv"]
|
|
149
|
+
min_confidence = 95
|
|
150
|
+
sort_by_size = true
|
|
151
|
+
|
|
152
|
+
# ======================================================================
|
|
153
|
+
# PYTEST
|
|
154
|
+
# ======================================================================
|
|
155
|
+
[tool.pytest.ini_options]
|
|
156
|
+
testpaths = ["tests"]
|
|
157
|
+
python_files = ["test_*.py"]
|
|
158
|
+
asyncio_mode = "auto"
|
|
159
|
+
|
|
160
|
+
# ======================================================================
|
|
161
|
+
# RUFF (Fast Linter/Formatter)
|
|
162
|
+
# ======================================================================
|
|
163
|
+
[tool.ruff]
|
|
164
|
+
line-length = 180
|
|
165
|
+
indent-width = 4
|
|
166
|
+
target-version = "py313"
|
|
167
|
+
src = ["sahra_shared"]
|
|
168
|
+
extend-exclude = [".claude", "tests", "venv", ".venv", ".claude"]
|
|
169
|
+
|
|
170
|
+
[tool.ruff.lint]
|
|
171
|
+
select = [
|
|
172
|
+
"E", # pycodestyle errors
|
|
173
|
+
"W", # pycodestyle warnings
|
|
174
|
+
"F", # Pyflakes
|
|
175
|
+
"C90", # McCabe complexity
|
|
176
|
+
"I", # isort
|
|
177
|
+
"B", # flake8-bugbear
|
|
178
|
+
"C4", # flake8-comprehensions
|
|
179
|
+
"T20", # flake8-print
|
|
180
|
+
"S", # flake8-bandit (security warnings)
|
|
181
|
+
"UP", # pyupgrade
|
|
182
|
+
"N", # pep8-naming
|
|
183
|
+
]
|
|
184
|
+
ignore = [
|
|
185
|
+
"UP006", # Dict vs dict
|
|
186
|
+
"UP007", # List vs list
|
|
187
|
+
"UP018", # Typing.List vs list
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
[tool.ruff.lint.isort]
|
|
191
|
+
known-first-party = ["sahra_shared"]
|
|
192
|
+
|
|
193
|
+
[tool.ruff.format]
|
|
194
|
+
quote-style = "double"
|
|
195
|
+
indent-style = "space"
|
|
196
|
+
line-ending = "auto"
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from sahra_shared.auth.capabilities import (
|
|
2
|
+
FOREIGN_VOCABULARY,
|
|
3
|
+
ROLE_CAPABILITIES,
|
|
4
|
+
Capability,
|
|
5
|
+
RoleName,
|
|
6
|
+
capabilities_for_roles,
|
|
7
|
+
)
|
|
8
|
+
from sahra_shared.auth.claims import TokenClaims
|
|
9
|
+
from sahra_shared.auth.coverage import (
|
|
10
|
+
RouteDeclaration,
|
|
11
|
+
UndeclaredRouteError,
|
|
12
|
+
assert_capability_coverage,
|
|
13
|
+
describe_routes,
|
|
14
|
+
enforce_capability_coverage,
|
|
15
|
+
)
|
|
16
|
+
from sahra_shared.auth.dependencies import (
|
|
17
|
+
ForeignVocabularyError,
|
|
18
|
+
make_auth_dependency,
|
|
19
|
+
require_role,
|
|
20
|
+
)
|
|
21
|
+
from sahra_shared.auth.guards import (
|
|
22
|
+
CapabilityGuards,
|
|
23
|
+
CapabilityRequirement,
|
|
24
|
+
declared_capabilities,
|
|
25
|
+
internal_endpoint,
|
|
26
|
+
internal_reason,
|
|
27
|
+
public_endpoint,
|
|
28
|
+
public_reason,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"FOREIGN_VOCABULARY",
|
|
33
|
+
"ROLE_CAPABILITIES",
|
|
34
|
+
"Capability",
|
|
35
|
+
"CapabilityGuards",
|
|
36
|
+
"CapabilityRequirement",
|
|
37
|
+
"ForeignVocabularyError",
|
|
38
|
+
"RoleName",
|
|
39
|
+
"RouteDeclaration",
|
|
40
|
+
"TokenClaims",
|
|
41
|
+
"UndeclaredRouteError",
|
|
42
|
+
"assert_capability_coverage",
|
|
43
|
+
"capabilities_for_roles",
|
|
44
|
+
"declared_capabilities",
|
|
45
|
+
"describe_routes",
|
|
46
|
+
"enforce_capability_coverage",
|
|
47
|
+
"internal_endpoint",
|
|
48
|
+
"internal_reason",
|
|
49
|
+
"make_auth_dependency",
|
|
50
|
+
"public_endpoint",
|
|
51
|
+
"public_reason",
|
|
52
|
+
"require_role",
|
|
53
|
+
]
|