pycraftcore 0.2.0__tar.gz → 0.2.3__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.
- {pycraftcore-0.2.0/src/pycraftcore.egg-info → pycraftcore-0.2.3}/PKG-INFO +70 -34
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/Readme.md +69 -33
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/pyproject.toml +3 -3
- pycraftcore-0.2.3/src/pycraftcore/application_configuration/__init__.py +17 -0
- pycraftcore-0.2.3/src/pycraftcore/application_configuration/adapter/schema.py +64 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/enum/__init__.py +2 -0
- pycraftcore-0.2.3/src/pycraftcore/application_configuration/enum/operation_type.py +6 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/model/configuration.py +4 -5
- pycraftcore-0.2.3/src/pycraftcore/application_configuration/model/connector.py +104 -0
- pycraftcore-0.2.3/src/pycraftcore/application_configuration/model/operation.py +74 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/port/configuration.py +2 -3
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/port/configuration_reader.py +2 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/adapter/compute_engine.py +5 -3
- pycraftcore-0.2.3/src/pycraftcore/computation_engine/port/arithmetic_operation.py +15 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/port/calculus_operation.py +4 -3
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/port/engine.py +5 -4
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/adapter/aiohttp_client.py +2 -2
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/adapter/resilient_client.py +6 -2
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/port/async_circuit_breaker.py +2 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/port/async_http_client.py +4 -4
- pycraftcore-0.2.3/src/pycraftcore/http/port/async_resilient_http_client.py +8 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/port/retry.py +2 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/logger/port/logger.py +2 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/sql/sql_handler.py +3 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/repository.py +3 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/runtime/code.py +3 -1
- pycraftcore-0.2.3/src/pycraftcore/serializer/port/serializer.py +12 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/telemetry/port/telemetry.py +3 -1
- {pycraftcore-0.2.0 → pycraftcore-0.2.3/src/pycraftcore.egg-info}/PKG-INFO +70 -34
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore.egg-info/SOURCES.txt +1 -0
- pycraftcore-0.2.0/src/pycraftcore/application_configuration/__init__.py +0 -9
- pycraftcore-0.2.0/src/pycraftcore/application_configuration/adapter/schema.py +0 -29
- pycraftcore-0.2.0/src/pycraftcore/application_configuration/model/connector.py +0 -42
- pycraftcore-0.2.0/src/pycraftcore/application_configuration/model/operation.py +0 -30
- pycraftcore-0.2.0/src/pycraftcore/computation_engine/port/arithmetic_operation.py +0 -16
- pycraftcore-0.2.0/src/pycraftcore/http/port/async_resilient_http_client.py +0 -6
- pycraftcore-0.2.0/src/pycraftcore/serializer/port/serializer.py +0 -11
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/LICENSE +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/setup.cfg +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/adapter/load_application_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/adapter/omega_configuration_reader.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/enum/connector_type.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/enum/file_operation_action.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/enum/run_type_application.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/enum/run_type_environment.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/model/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/authentication/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/authentication/model/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/authentication/model/auth_type.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/authentication/model/basic_auth.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/authentication/model/no_auth.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/authentication/model/token_auth.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/adapter/numpy_arithmetic_operation.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/adapter/scipy_calculus_operation.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/const_typing.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/computation_engine/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/extension/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/extension/yml_reader.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/extension/yml_writer.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/factory.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/handler.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/provider.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/adapter/strategy.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/port/reader.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/file_handler/port/writer.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/adapter/circuit_breaker_policy.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/adapter/httpx_client.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/adapter/retry_policy.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/circuite_breaker_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/client_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/http_client_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/limits_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/retry_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/configuration/security_configuration.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/context/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/context/request_context.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/enum/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/enum/circuit_breaker_status.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/enum/http_method.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/exception/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/exception/circuit_breaker_open_exception.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/middleware/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/middleware/request_id_middleware.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/middleware/request_middleware.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/http/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/logger/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/logger/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/logger/adapter/loguru_logger.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/logger/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/profiler/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/profiler/profiled.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/constants/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/constants/allowed_root_statements.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/constants/forbidden_statements.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/sql/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/sql/adapter.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/query_language/sql/factory.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/sqlite/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/sqlite/adapter.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/sqlite/factory.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/sqlite/mapper.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/repository/sqlite/schema.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/runtime/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/runtime/python/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/runtime/python/adapter.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/runtime/python/factory.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/runtime/python/schema.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/serializer/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/serializer/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/serializer/adapter/binary_serializer.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/serializer/adapter/dictionary_serializer.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/serializer/adapter/json_serializer.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/serializer/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/telemetry/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/telemetry/adapter/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/telemetry/adapter/open_telemetry.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/telemetry/port/__init__.py +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore.egg-info/dependency_links.txt +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore.egg-info/requires.txt +0 -0
- {pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycraftcore
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Provides reusable technical infrastructure and adapter implementations for Python applications
|
|
5
5
|
Author-email: Toufik Saouchi <toufik.saouchi@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,43 +37,39 @@ Dynamic: license-file
|
|
|
37
37
|
|
|
38
38
|
# pycraftcore
|
|
39
39
|
|
|
40
|
-
`pycraftcore` is a reusable technical infrastructure library for Python applications: configuration
|
|
41
|
-
clients
|
|
42
|
-
|
|
43
|
-
than a framework.
|
|
40
|
+
`pycraftcore` is a reusable technical infrastructure library for Python applications: configuration, resilient HTTP
|
|
41
|
+
clients, a pooled async SQLite repository, a sandboxed Python code runner, safe SQL validation, serialization, and a
|
|
42
|
+
handful of other adapters — small, swappable building blocks rather than a framework.
|
|
44
43
|
|
|
45
44
|
**Design principles:** Modularity · Reusability · Maintainability
|
|
46
45
|
|
|
47
|
-
Every module follows the same shape: a `port/` package with `Protocol` interfaces and an `adapter/` package with one
|
|
48
|
-
or more concrete implementations, so consumers depend on the port, not the adapter.
|
|
49
|
-
|
|
50
46
|
---
|
|
51
47
|
|
|
52
48
|
## Table of Contents
|
|
53
49
|
|
|
54
|
-
- [
|
|
50
|
+
- [Functionality](#functionality)
|
|
55
51
|
- [Installation](#installation)
|
|
56
52
|
- [Configuration](#configuration)
|
|
57
53
|
- [Development](#development)
|
|
58
54
|
|
|
59
55
|
---
|
|
60
56
|
|
|
61
|
-
##
|
|
57
|
+
## Functionality
|
|
62
58
|
|
|
63
|
-
| Module | Package |
|
|
59
|
+
| Module | Package | What it gives you |
|
|
64
60
|
|---|---|---|
|
|
65
|
-
| Configuration Management | `application_configuration` | Environment-driven YAML
|
|
66
|
-
| Authentication | `application_configuration` |
|
|
67
|
-
| Logging | `logger` |
|
|
68
|
-
| Telemetry & Observability | `telemetry` |
|
|
69
|
-
| HTTP Client Utilities | `http` |
|
|
70
|
-
| Repository / SQLite | `repository` | Async SQLite repository
|
|
71
|
-
| Query Language / SQL Safety | `query_language` |
|
|
72
|
-
| Runtime / Sandboxed Execution | `runtime` | Runs
|
|
73
|
-
| Serialization | `serialization` | JSON, dict, and binary (msgpack) serialization for dataclasses
|
|
74
|
-
| Scientific computation engine | `computation` |
|
|
75
|
-
| File handler | `file_handler` |
|
|
76
|
-
| Profiler | `profiler` |
|
|
61
|
+
| Configuration Management | `application_configuration` | Environment-driven YAML configuration for connectors and operations, validated and typed |
|
|
62
|
+
| Authentication | `application_configuration` | Shared auth models (none / basic / token) for any connector |
|
|
63
|
+
| Logging | `logger` | Structured application logging |
|
|
64
|
+
| Telemetry & Observability | `telemetry` | Distributed tracing, exportable to console or an OTLP collector |
|
|
65
|
+
| HTTP Client Utilities | `http` | Resilient HTTP client with retries, circuit breaking, and tracing |
|
|
66
|
+
| Repository / SQLite | `repository` | Async SQLite repository with real connection pooling for concurrent access |
|
|
67
|
+
| Query Language / SQL Safety | `query_language` | Validates that a SQL statement is read-only before it reaches a database |
|
|
68
|
+
| Runtime / Sandboxed Execution | `runtime` | Runs untrusted Python code in an isolated process with resource limits |
|
|
69
|
+
| Serialization | `serialization` | JSON, dict, and binary (msgpack) serialization for dataclasses |
|
|
70
|
+
| Scientific computation engine | `computation` | Financial arithmetic and calculus (integration, interpolation) |
|
|
71
|
+
| File handler | `file_handler` | Read/write files by extension, pluggable for new formats |
|
|
72
|
+
| Profiler | `profiler` | Async function profiling decorator |
|
|
77
73
|
|
|
78
74
|
---
|
|
79
75
|
|
|
@@ -134,11 +130,46 @@ connector:
|
|
|
134
130
|
key_value: ${oc.env:connector_api_key}
|
|
135
131
|
```
|
|
136
132
|
|
|
137
|
-
|
|
138
|
-
many pooled connections the repository opens. An `operation/<tag>.yml` references a connector by tag and adds
|
|
139
|
-
`endpoint`, `method`, and `parameters` — see `pycraftcore.application_configuration.model` for the full schema.
|
|
133
|
+
**Operation example** (`operation/<tag>.yml`) — references a connector by tag:
|
|
140
134
|
|
|
141
|
-
|
|
135
|
+
```yaml
|
|
136
|
+
operation:
|
|
137
|
+
<operation_tag>:
|
|
138
|
+
name: <operation name>
|
|
139
|
+
type: api
|
|
140
|
+
connector: ${connector.<connector_tag>}
|
|
141
|
+
endpoint: /<path>
|
|
142
|
+
method: GET
|
|
143
|
+
parameters:
|
|
144
|
+
<param>: <value>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Root example** (`root.yml`) — wires everything together for a given environment:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
application_configuration:
|
|
151
|
+
env: ${oc.env:APP_ENV,debug}
|
|
152
|
+
run: async
|
|
153
|
+
|
|
154
|
+
connector:
|
|
155
|
+
api:
|
|
156
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
157
|
+
file:
|
|
158
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
159
|
+
database:
|
|
160
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
161
|
+
telemetry:
|
|
162
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
163
|
+
|
|
164
|
+
operation:
|
|
165
|
+
<operation_tag>: ${operation.<operation_tag>}
|
|
166
|
+
# ...
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Connectors and operations both declare a `type`, which determines which fields they take and which concrete object
|
|
170
|
+
they load as.
|
|
171
|
+
|
|
172
|
+
**Using it in code:**
|
|
142
173
|
|
|
143
174
|
```python
|
|
144
175
|
from pathlib import Path
|
|
@@ -149,17 +180,22 @@ from pycraftcore.application_configuration.adapter.load_application_configuratio
|
|
|
149
180
|
from pycraftcore.application_configuration.adapter.omega_configuration_reader import (
|
|
150
181
|
OmegaConfigurationReader,
|
|
151
182
|
)
|
|
152
|
-
from pycraftcore.application_configuration.enum.run_type_environment import
|
|
153
|
-
RunTypeEnvironment,
|
|
154
|
-
)
|
|
183
|
+
from pycraftcore.application_configuration.enum.run_type_environment import RunTypeEnvironment
|
|
155
184
|
from pycraftcore.logger.adapter.loguru_logger import LoguruLogger
|
|
156
185
|
|
|
157
186
|
reader = OmegaConfigurationReader(RunTypeEnvironment.deploy, Path("config"))
|
|
158
187
|
loader = LoadApplicationConfiguration(reader, LoguruLogger())
|
|
159
188
|
|
|
160
|
-
config = loader.load()
|
|
189
|
+
config = loader.load()
|
|
190
|
+
|
|
191
|
+
connector = config.connector.api("<connector_tag>") # ApiConnector
|
|
192
|
+
operation = config.operation.api("<operation_tag>") # ApiOperation
|
|
161
193
|
```
|
|
162
194
|
|
|
195
|
+
`config.connector`/`config.operation` return the concrete connector/operation type for a given tag (`.api()`,
|
|
196
|
+
`.database()`, `.file()`, `.telemetry()` where applicable) instead of a raw dict — see
|
|
197
|
+
`pycraftcore.application_configuration` for the full API.
|
|
198
|
+
|
|
163
199
|
---
|
|
164
200
|
|
|
165
201
|
## Development
|
|
@@ -186,9 +222,9 @@ uv run pre-commit run --all-files --hook-stage pre-commit
|
|
|
186
222
|
```bash
|
|
187
223
|
make install_dev # uv sync --group dev
|
|
188
224
|
make test # uv run pytest
|
|
189
|
-
make lint # uv run ruff check
|
|
190
|
-
make format # uv run ruff format
|
|
191
|
-
make typecheck
|
|
225
|
+
make lint # uv run ruff check
|
|
226
|
+
make format # uv run ruff format
|
|
227
|
+
make typecheck # uv run ty check (strict mode)
|
|
192
228
|
```
|
|
193
229
|
|
|
194
230
|
Run `pytest --cov=pycraftcore --cov-report=term-missing` for a coverage breakdown by module.
|
|
@@ -1,42 +1,38 @@
|
|
|
1
1
|
# pycraftcore
|
|
2
2
|
|
|
3
|
-
`pycraftcore` is a reusable technical infrastructure library for Python applications: configuration
|
|
4
|
-
clients
|
|
5
|
-
|
|
6
|
-
than a framework.
|
|
3
|
+
`pycraftcore` is a reusable technical infrastructure library for Python applications: configuration, resilient HTTP
|
|
4
|
+
clients, a pooled async SQLite repository, a sandboxed Python code runner, safe SQL validation, serialization, and a
|
|
5
|
+
handful of other adapters — small, swappable building blocks rather than a framework.
|
|
7
6
|
|
|
8
7
|
**Design principles:** Modularity · Reusability · Maintainability
|
|
9
8
|
|
|
10
|
-
Every module follows the same shape: a `port/` package with `Protocol` interfaces and an `adapter/` package with one
|
|
11
|
-
or more concrete implementations, so consumers depend on the port, not the adapter.
|
|
12
|
-
|
|
13
9
|
---
|
|
14
10
|
|
|
15
11
|
## Table of Contents
|
|
16
12
|
|
|
17
|
-
- [
|
|
13
|
+
- [Functionality](#functionality)
|
|
18
14
|
- [Installation](#installation)
|
|
19
15
|
- [Configuration](#configuration)
|
|
20
16
|
- [Development](#development)
|
|
21
17
|
|
|
22
18
|
---
|
|
23
19
|
|
|
24
|
-
##
|
|
20
|
+
## Functionality
|
|
25
21
|
|
|
26
|
-
| Module | Package |
|
|
22
|
+
| Module | Package | What it gives you |
|
|
27
23
|
|---|---|---|
|
|
28
|
-
| Configuration Management | `application_configuration` | Environment-driven YAML
|
|
29
|
-
| Authentication | `application_configuration` |
|
|
30
|
-
| Logging | `logger` |
|
|
31
|
-
| Telemetry & Observability | `telemetry` |
|
|
32
|
-
| HTTP Client Utilities | `http` |
|
|
33
|
-
| Repository / SQLite | `repository` | Async SQLite repository
|
|
34
|
-
| Query Language / SQL Safety | `query_language` |
|
|
35
|
-
| Runtime / Sandboxed Execution | `runtime` | Runs
|
|
36
|
-
| Serialization | `serialization` | JSON, dict, and binary (msgpack) serialization for dataclasses
|
|
37
|
-
| Scientific computation engine | `computation` |
|
|
38
|
-
| File handler | `file_handler` |
|
|
39
|
-
| Profiler | `profiler` |
|
|
24
|
+
| Configuration Management | `application_configuration` | Environment-driven YAML configuration for connectors and operations, validated and typed |
|
|
25
|
+
| Authentication | `application_configuration` | Shared auth models (none / basic / token) for any connector |
|
|
26
|
+
| Logging | `logger` | Structured application logging |
|
|
27
|
+
| Telemetry & Observability | `telemetry` | Distributed tracing, exportable to console or an OTLP collector |
|
|
28
|
+
| HTTP Client Utilities | `http` | Resilient HTTP client with retries, circuit breaking, and tracing |
|
|
29
|
+
| Repository / SQLite | `repository` | Async SQLite repository with real connection pooling for concurrent access |
|
|
30
|
+
| Query Language / SQL Safety | `query_language` | Validates that a SQL statement is read-only before it reaches a database |
|
|
31
|
+
| Runtime / Sandboxed Execution | `runtime` | Runs untrusted Python code in an isolated process with resource limits |
|
|
32
|
+
| Serialization | `serialization` | JSON, dict, and binary (msgpack) serialization for dataclasses |
|
|
33
|
+
| Scientific computation engine | `computation` | Financial arithmetic and calculus (integration, interpolation) |
|
|
34
|
+
| File handler | `file_handler` | Read/write files by extension, pluggable for new formats |
|
|
35
|
+
| Profiler | `profiler` | Async function profiling decorator |
|
|
40
36
|
|
|
41
37
|
---
|
|
42
38
|
|
|
@@ -97,11 +93,46 @@ connector:
|
|
|
97
93
|
key_value: ${oc.env:connector_api_key}
|
|
98
94
|
```
|
|
99
95
|
|
|
100
|
-
|
|
101
|
-
many pooled connections the repository opens. An `operation/<tag>.yml` references a connector by tag and adds
|
|
102
|
-
`endpoint`, `method`, and `parameters` — see `pycraftcore.application_configuration.model` for the full schema.
|
|
96
|
+
**Operation example** (`operation/<tag>.yml`) — references a connector by tag:
|
|
103
97
|
|
|
104
|
-
|
|
98
|
+
```yaml
|
|
99
|
+
operation:
|
|
100
|
+
<operation_tag>:
|
|
101
|
+
name: <operation name>
|
|
102
|
+
type: api
|
|
103
|
+
connector: ${connector.<connector_tag>}
|
|
104
|
+
endpoint: /<path>
|
|
105
|
+
method: GET
|
|
106
|
+
parameters:
|
|
107
|
+
<param>: <value>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Root example** (`root.yml`) — wires everything together for a given environment:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
application_configuration:
|
|
114
|
+
env: ${oc.env:APP_ENV,debug}
|
|
115
|
+
run: async
|
|
116
|
+
|
|
117
|
+
connector:
|
|
118
|
+
api:
|
|
119
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
120
|
+
file:
|
|
121
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
122
|
+
database:
|
|
123
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
124
|
+
telemetry:
|
|
125
|
+
<connector_tag>: ${connector.<connector_tag>}
|
|
126
|
+
|
|
127
|
+
operation:
|
|
128
|
+
<operation_tag>: ${operation.<operation_tag>}
|
|
129
|
+
# ...
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Connectors and operations both declare a `type`, which determines which fields they take and which concrete object
|
|
133
|
+
they load as.
|
|
134
|
+
|
|
135
|
+
**Using it in code:**
|
|
105
136
|
|
|
106
137
|
```python
|
|
107
138
|
from pathlib import Path
|
|
@@ -112,17 +143,22 @@ from pycraftcore.application_configuration.adapter.load_application_configuratio
|
|
|
112
143
|
from pycraftcore.application_configuration.adapter.omega_configuration_reader import (
|
|
113
144
|
OmegaConfigurationReader,
|
|
114
145
|
)
|
|
115
|
-
from pycraftcore.application_configuration.enum.run_type_environment import
|
|
116
|
-
RunTypeEnvironment,
|
|
117
|
-
)
|
|
146
|
+
from pycraftcore.application_configuration.enum.run_type_environment import RunTypeEnvironment
|
|
118
147
|
from pycraftcore.logger.adapter.loguru_logger import LoguruLogger
|
|
119
148
|
|
|
120
149
|
reader = OmegaConfigurationReader(RunTypeEnvironment.deploy, Path("config"))
|
|
121
150
|
loader = LoadApplicationConfiguration(reader, LoguruLogger())
|
|
122
151
|
|
|
123
|
-
config = loader.load()
|
|
152
|
+
config = loader.load()
|
|
153
|
+
|
|
154
|
+
connector = config.connector.api("<connector_tag>") # ApiConnector
|
|
155
|
+
operation = config.operation.api("<operation_tag>") # ApiOperation
|
|
124
156
|
```
|
|
125
157
|
|
|
158
|
+
`config.connector`/`config.operation` return the concrete connector/operation type for a given tag (`.api()`,
|
|
159
|
+
`.database()`, `.file()`, `.telemetry()` where applicable) instead of a raw dict — see
|
|
160
|
+
`pycraftcore.application_configuration` for the full API.
|
|
161
|
+
|
|
126
162
|
---
|
|
127
163
|
|
|
128
164
|
## Development
|
|
@@ -149,9 +185,9 @@ uv run pre-commit run --all-files --hook-stage pre-commit
|
|
|
149
185
|
```bash
|
|
150
186
|
make install_dev # uv sync --group dev
|
|
151
187
|
make test # uv run pytest
|
|
152
|
-
make lint # uv run ruff check
|
|
153
|
-
make format # uv run ruff format
|
|
154
|
-
make typecheck
|
|
188
|
+
make lint # uv run ruff check
|
|
189
|
+
make format # uv run ruff format
|
|
190
|
+
make typecheck # uv run ty check (strict mode)
|
|
155
191
|
```
|
|
156
192
|
|
|
157
193
|
Run `pytest --cov=pycraftcore --cov-report=term-missing` for a coverage breakdown by module.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pycraftcore"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "Toufik Saouchi", email = "toufik.saouchi@gmail.com" },
|
|
10
10
|
]
|
|
@@ -71,8 +71,8 @@ ignore = [
|
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
[tool.ty.src]
|
|
74
|
-
include = ["src"]
|
|
75
|
-
exclude = ["
|
|
74
|
+
include = ["src", "tests/ducktype"]
|
|
75
|
+
exclude = [".venv"]
|
|
76
76
|
|
|
77
77
|
[tool.setuptools.packages.find]
|
|
78
78
|
where = ["src"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from pycraftcore.application_configuration.model.configuration import (
|
|
2
|
+
ApplicationConfiguration,
|
|
3
|
+
)
|
|
4
|
+
from pycraftcore.application_configuration.model.connector import ConnectorRegistry
|
|
5
|
+
from pycraftcore.application_configuration.model.operation import OperationRegistry
|
|
6
|
+
from pycraftcore.application_configuration.port.configuration import Configuration
|
|
7
|
+
from pycraftcore.application_configuration.port.configuration_reader import (
|
|
8
|
+
ConfigurationReader,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"ApplicationConfiguration",
|
|
13
|
+
"Configuration",
|
|
14
|
+
"ConfigurationReader",
|
|
15
|
+
"ConnectorRegistry",
|
|
16
|
+
"OperationRegistry",
|
|
17
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import Annotated, Any
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Discriminator, Tag
|
|
4
|
+
|
|
5
|
+
from pycraftcore.application_configuration.enum.connector_type import ConnectorType
|
|
6
|
+
from pycraftcore.application_configuration.enum.run_type_application import (
|
|
7
|
+
RunTypeApplication,
|
|
8
|
+
)
|
|
9
|
+
from pycraftcore.application_configuration.enum.run_type_environment import (
|
|
10
|
+
RunTypeEnvironment,
|
|
11
|
+
)
|
|
12
|
+
from pycraftcore.application_configuration.model.configuration import (
|
|
13
|
+
ApplicationConfiguration,
|
|
14
|
+
)
|
|
15
|
+
from pycraftcore.application_configuration.model.connector import (
|
|
16
|
+
ApiConnector,
|
|
17
|
+
ConnectorRegistry,
|
|
18
|
+
DatabaseConnector,
|
|
19
|
+
FileConnector,
|
|
20
|
+
TelemetryConnector,
|
|
21
|
+
)
|
|
22
|
+
from pycraftcore.application_configuration.model.operation import (
|
|
23
|
+
ApiOperation,
|
|
24
|
+
FileOperation,
|
|
25
|
+
OperationRegistry,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _kind_tag(value: Any) -> str:
|
|
30
|
+
return value["type"] if isinstance(value, dict) else value.type.value
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_ConnectorSchemaTyping = Annotated[
|
|
34
|
+
Annotated[ApiConnector, Tag("api")]
|
|
35
|
+
| Annotated[DatabaseConnector, Tag("database")]
|
|
36
|
+
| Annotated[FileConnector, Tag("file")]
|
|
37
|
+
| Annotated[TelemetryConnector, Tag("telemetry")],
|
|
38
|
+
Discriminator(_kind_tag),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
_OperationSchemaTyping = Annotated[
|
|
42
|
+
Annotated[ApiOperation, Tag("api")] | Annotated[FileOperation, Tag("file")],
|
|
43
|
+
Discriminator(_kind_tag),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ApplicationConfigurationSchema(BaseModel):
|
|
48
|
+
env: RunTypeEnvironment
|
|
49
|
+
run: RunTypeApplication
|
|
50
|
+
connector: dict[ConnectorType, dict[str, _ConnectorSchemaTyping]]
|
|
51
|
+
operation: dict[str, _OperationSchemaTyping]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MapperDomainSchema:
|
|
55
|
+
@staticmethod
|
|
56
|
+
def map(
|
|
57
|
+
app_configuration_schema: ApplicationConfigurationSchema,
|
|
58
|
+
) -> ApplicationConfiguration:
|
|
59
|
+
return ApplicationConfiguration(
|
|
60
|
+
env=app_configuration_schema.env,
|
|
61
|
+
run=app_configuration_schema.run,
|
|
62
|
+
connector=ConnectorRegistry(app_configuration_schema.connector),
|
|
63
|
+
operation=OperationRegistry(app_configuration_schema.operation),
|
|
64
|
+
)
|
{pycraftcore-0.2.0 → pycraftcore-0.2.3}/src/pycraftcore/application_configuration/enum/__init__.py
RENAMED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from .connector_type import ConnectorType
|
|
2
2
|
from .file_operation_action import FileOperationAction
|
|
3
|
+
from .operation_type import OperationType
|
|
3
4
|
from .run_type_application import RunTypeApplication
|
|
4
5
|
from .run_type_environment import RunTypeEnvironment
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
8
|
"ConnectorType",
|
|
8
9
|
"FileOperationAction",
|
|
10
|
+
"OperationType",
|
|
9
11
|
"RunTypeApplication",
|
|
10
12
|
"RunTypeEnvironment",
|
|
11
13
|
]
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
|
|
3
|
-
from pycraftcore.application_configuration.enum.connector_type import ConnectorType
|
|
4
3
|
from pycraftcore.application_configuration.enum.run_type_application import (
|
|
5
4
|
RunTypeApplication,
|
|
6
5
|
)
|
|
7
6
|
from pycraftcore.application_configuration.enum.run_type_environment import (
|
|
8
7
|
RunTypeEnvironment,
|
|
9
8
|
)
|
|
10
|
-
from pycraftcore.application_configuration.model.connector import
|
|
11
|
-
from pycraftcore.application_configuration.model.operation import
|
|
9
|
+
from pycraftcore.application_configuration.model.connector import ConnectorRegistry
|
|
10
|
+
from pycraftcore.application_configuration.model.operation import OperationRegistry
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
@dataclass(frozen=True, slots=True)
|
|
15
14
|
class ApplicationConfiguration:
|
|
16
15
|
env: RunTypeEnvironment
|
|
17
16
|
run: RunTypeApplication
|
|
18
|
-
connector:
|
|
19
|
-
operation:
|
|
17
|
+
connector: ConnectorRegistry
|
|
18
|
+
operation: OperationRegistry
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Literal, overload
|
|
4
|
+
|
|
5
|
+
from pycraftcore.application_configuration.enum.connector_type import ConnectorType
|
|
6
|
+
from pycraftcore.authentication import AuthTyping
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(slots=True)
|
|
10
|
+
class BaseConnector:
|
|
11
|
+
name: str
|
|
12
|
+
type: ConnectorType
|
|
13
|
+
auth: AuthTyping
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(slots=True)
|
|
17
|
+
class ApiConnector(BaseConnector):
|
|
18
|
+
base_url: str
|
|
19
|
+
timeout: int
|
|
20
|
+
retry: int
|
|
21
|
+
certificate: str | None = field(default=None)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(slots=True)
|
|
25
|
+
class DatabaseConnector(BaseConnector):
|
|
26
|
+
engine: str
|
|
27
|
+
host: str
|
|
28
|
+
port: int
|
|
29
|
+
default_name: str
|
|
30
|
+
pool: dict[str, int]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(slots=True)
|
|
34
|
+
class FileConnector(BaseConnector):
|
|
35
|
+
base_path: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(slots=True)
|
|
39
|
+
class TelemetryConnector(BaseConnector):
|
|
40
|
+
host: str
|
|
41
|
+
port: int
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
ConnectorTyping = ApiConnector | FileConnector | DatabaseConnector | TelemetryConnector
|
|
45
|
+
|
|
46
|
+
_CONNECTOR_CLASS_BY_TYPE: dict[ConnectorType, type[ConnectorTyping]] = {
|
|
47
|
+
ConnectorType.api: ApiConnector,
|
|
48
|
+
ConnectorType.database: DatabaseConnector,
|
|
49
|
+
ConnectorType.file: FileConnector,
|
|
50
|
+
ConnectorType.telemetry: TelemetryConnector,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True, slots=True)
|
|
55
|
+
class ConnectorRegistry:
|
|
56
|
+
by_type: dict[ConnectorType, dict[str, ConnectorTyping]] = field(default_factory=dict)
|
|
57
|
+
|
|
58
|
+
def __post_init__(self) -> None:
|
|
59
|
+
for kind, bucket in self.by_type.items():
|
|
60
|
+
expected = _CONNECTOR_CLASS_BY_TYPE.get(kind)
|
|
61
|
+
if expected is None:
|
|
62
|
+
continue
|
|
63
|
+
for name, connector in bucket.items():
|
|
64
|
+
if not isinstance(connector, expected):
|
|
65
|
+
raise TypeError(
|
|
66
|
+
f"connector {name!r} declared under {kind.value!r} must be a "
|
|
67
|
+
f"{expected.__name__}, got {type(connector).__name__}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@overload
|
|
71
|
+
def __getitem__(self, kind: Literal[ConnectorType.api]) -> Mapping[str, ApiConnector]: ...
|
|
72
|
+
@overload
|
|
73
|
+
def __getitem__(
|
|
74
|
+
self, kind: Literal[ConnectorType.database]
|
|
75
|
+
) -> Mapping[str, DatabaseConnector]: ...
|
|
76
|
+
@overload
|
|
77
|
+
def __getitem__(self, kind: Literal[ConnectorType.file]) -> Mapping[str, FileConnector]: ...
|
|
78
|
+
@overload
|
|
79
|
+
def __getitem__(
|
|
80
|
+
self, kind: Literal[ConnectorType.telemetry]
|
|
81
|
+
) -> Mapping[str, TelemetryConnector]: ...
|
|
82
|
+
|
|
83
|
+
def __getitem__(self, kind: ConnectorType) -> Mapping[str, ConnectorTyping]:
|
|
84
|
+
return self.by_type.get(kind, {})
|
|
85
|
+
|
|
86
|
+
def api(self, name: str) -> ApiConnector:
|
|
87
|
+
return self._get(ConnectorType.api, name, ApiConnector)
|
|
88
|
+
|
|
89
|
+
def database(self, name: str) -> DatabaseConnector:
|
|
90
|
+
return self._get(ConnectorType.database, name, DatabaseConnector)
|
|
91
|
+
|
|
92
|
+
def file(self, name: str) -> FileConnector:
|
|
93
|
+
return self._get(ConnectorType.file, name, FileConnector)
|
|
94
|
+
|
|
95
|
+
def telemetry(self, name: str) -> TelemetryConnector:
|
|
96
|
+
return self._get(ConnectorType.telemetry, name, TelemetryConnector)
|
|
97
|
+
|
|
98
|
+
def _get[T: ConnectorTyping](self, kind: ConnectorType, name: str, expected: type[T]) -> T:
|
|
99
|
+
try:
|
|
100
|
+
connector = self.by_type[kind][name]
|
|
101
|
+
except KeyError:
|
|
102
|
+
raise KeyError(f"no {kind.value} connector named {name!r}") from None
|
|
103
|
+
assert isinstance(connector, expected)
|
|
104
|
+
return connector
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pycraftcore.application_configuration.enum.file_operation_action import (
|
|
5
|
+
FileOperationAction,
|
|
6
|
+
)
|
|
7
|
+
from pycraftcore.application_configuration.enum.operation_type import OperationType
|
|
8
|
+
from pycraftcore.application_configuration.model.connector import (
|
|
9
|
+
ApiConnector,
|
|
10
|
+
FileConnector,
|
|
11
|
+
)
|
|
12
|
+
from pycraftcore.http.enum.http_method import HttpMethod
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(slots=True)
|
|
16
|
+
class BaseOperation:
|
|
17
|
+
name: str
|
|
18
|
+
type: OperationType
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(slots=True)
|
|
22
|
+
class ApiOperation(BaseOperation):
|
|
23
|
+
connector: ApiConnector
|
|
24
|
+
endpoint: str
|
|
25
|
+
method: HttpMethod
|
|
26
|
+
parameters: dict[str, Any]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(slots=True)
|
|
30
|
+
class FileOperation(BaseOperation):
|
|
31
|
+
connector: FileConnector
|
|
32
|
+
action: FileOperationAction
|
|
33
|
+
parameters: dict[str, Any]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
OperationTyping = FileOperation | ApiOperation
|
|
37
|
+
|
|
38
|
+
_OPERATION_CLASS_BY_TYPE: dict[OperationType, type[OperationTyping]] = {
|
|
39
|
+
OperationType.api: ApiOperation,
|
|
40
|
+
OperationType.file: FileOperation,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True, slots=True)
|
|
45
|
+
class OperationRegistry:
|
|
46
|
+
by_name: dict[str, OperationTyping] = field(default_factory=dict)
|
|
47
|
+
|
|
48
|
+
def __post_init__(self) -> None:
|
|
49
|
+
for name, operation in self.by_name.items():
|
|
50
|
+
expected = _OPERATION_CLASS_BY_TYPE.get(operation.type)
|
|
51
|
+
if expected is None:
|
|
52
|
+
continue
|
|
53
|
+
if not isinstance(operation, expected):
|
|
54
|
+
raise TypeError(
|
|
55
|
+
f"operation {name!r} declared type {operation.type.value!r} but is a "
|
|
56
|
+
f"{type(operation).__name__}, expected {expected.__name__}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def api(self, name: str) -> ApiOperation:
|
|
60
|
+
return self._get(name, ApiOperation)
|
|
61
|
+
|
|
62
|
+
def file(self, name: str) -> FileOperation:
|
|
63
|
+
return self._get(name, FileOperation)
|
|
64
|
+
|
|
65
|
+
def _get[T: OperationTyping](self, name: str, expected: type[T]) -> T:
|
|
66
|
+
try:
|
|
67
|
+
operation = self.by_name[name]
|
|
68
|
+
except KeyError:
|
|
69
|
+
raise KeyError(f"no operation named {name!r}") from None
|
|
70
|
+
if not isinstance(operation, expected):
|
|
71
|
+
raise TypeError(
|
|
72
|
+
f"operation {name!r} is a {type(operation).__name__}, not {expected.__name__}"
|
|
73
|
+
)
|
|
74
|
+
return operation
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
from typing import Protocol
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
|
2
2
|
|
|
3
3
|
from pycraftcore.application_configuration.model.configuration import (
|
|
4
4
|
ApplicationConfiguration,
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
@runtime_checkable
|
|
8
9
|
class Configuration(Protocol):
|
|
9
10
|
def load(self) -> ApplicationConfiguration | None: ...
|
|
10
|
-
|
|
11
|
-
def reload(self) -> ApplicationConfiguration | None: ...
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from typing import Protocol
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
|
2
2
|
|
|
3
3
|
from pycraftcore.application_configuration.model.configuration import (
|
|
4
4
|
ApplicationConfiguration,
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
@runtime_checkable
|
|
8
9
|
class ConfigurationReader(Protocol):
|
|
9
10
|
def read(self) -> ApplicationConfiguration: ...
|