pycraftcore 2.0.0__tar.gz → 2.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.
- pycraftcore-2.1.0/PKG-INFO +418 -0
- pycraftcore-2.1.0/Readme.md +316 -0
- pycraftcore-2.1.0/pyproject.toml +113 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/schema.py +4 -1
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/operation_type.py +1 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/operation.py +12 -2
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/__init__.py +4 -0
- pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/svg/svg_reader.py +8 -0
- pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/svg/svg_writer.py +8 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/handler.py +3 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/adapter.py +2 -2
- pycraftcore-2.1.0/src/pycraftcore/retry/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/host_bridge_server.py +4 -1
- pycraftcore-2.1.0/src/pycraftcore/serializer/__init__.py +0 -0
- pycraftcore-2.1.0/src/pycraftcore.egg-info/PKG-INFO +418 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore.egg-info/SOURCES.txt +4 -0
- pycraftcore-2.1.0/src/pycraftcore.egg-info/requires.txt +96 -0
- pycraftcore-2.0.0/PKG-INFO +0 -257
- pycraftcore-2.0.0/Readme.md +0 -217
- pycraftcore-2.0.0/pyproject.toml +0 -84
- pycraftcore-2.0.0/src/pycraftcore.egg-info/PKG-INFO +0 -257
- pycraftcore-2.0.0/src/pycraftcore.egg-info/requires.txt +0 -26
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/LICENSE +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/setup.cfg +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/load_application_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/omega_configuration_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/connector_type.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/file_operation_action.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/run_type_application.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/run_type_environment.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/connector.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/port/configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/port/configuration_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/auth_type.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/basic_auth.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/no_auth.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/token_auth.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/adapter/aiobreaker_circuit_breaker_policy.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/configuration/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/configuration/circuit_breaker_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/enum/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/enum/circuit_breaker_status.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/exception/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/exception/circuit_breaker_open_exception.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/port/async_circuit_breaker.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/compute_engine.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/numpy_arithmetic_operation.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/scipy_calculus_operation.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/const_typing.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/arithmetic_operation.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/calculus_operation.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/engine.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/context/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/context/request_id_context.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/csv/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/csv/csv_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/csv/csv_writer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/json/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/json/json_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/json/json_writer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/markdown/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/markdown/markdown_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/markdown/markdown_writer.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/file_handler/adapter/extension/txt → pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/svg}/__init__.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/file_handler/adapter/extension/yml → pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/txt}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/txt/txt_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/txt/txt_writer.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/file_handler/enum → pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/yml}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/yml/yml_reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/yml/yml_writer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/strategy.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/http → pycraftcore-2.1.0/src/pycraftcore/file_handler/enum}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/enum/read_chunk_mode.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/file_handler_factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/file_handler_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/reader.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/writer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/schema/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/schema/file_manifest.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/http/context → pycraftcore-2.1.0/src/pycraftcore/http}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/adapter/aiohttp_client.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/adapter/httpx_client.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/client_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/http_client_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/limits_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/security_configuration.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/http/policy → pycraftcore-2.1.0/src/pycraftcore/http/context}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/context/request_context.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/enum/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/enum/http_method.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/middleware/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/middleware/request_id_middleware.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/middleware/request_middleware.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/profiler → pycraftcore-2.1.0/src/pycraftcore/http/policy}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/policy/http_error_policy.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/port/async_http_client.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/adapter/standard_logger.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/port/logger.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/query_language → pycraftcore-2.1.0/src/pycraftcore/profiler}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/profiler/profiled.py +0 -0
- /pycraftcore-2.0.0/src/pycraftcore/query_language/constants/sql/__init__.py → /pycraftcore-2.1.0/src/pycraftcore/py.typed +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/repository → pycraftcore-2.1.0/src/pycraftcore/query_language}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/sql/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/sql/adapter.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/sql/factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/constants/__init__.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/no_sql → pycraftcore-2.1.0/src/pycraftcore/query_language/constants/sql}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/constants/sql/allowed_sql_root_statements.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/constants/sql/forbidden_sql_statements.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/port/query_handler.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/no_sql/mongodb → pycraftcore-2.1.0/src/pycraftcore/repository}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/sql → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/no_sql}/__init__.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/sql/postgresql → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/no_sql/mongodb}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/adapter.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/mapper.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/schema.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/resilient_http → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/sql}/__init__.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/retry → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/sql/postgresql}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/adapter.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/mapper.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/schema.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/mapper.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/schema.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/port/repository.py +0 -0
- {pycraftcore-2.0.0/src/pycraftcore/serializer → pycraftcore-2.1.0/src/pycraftcore/resilient_http}/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_client.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_http_client_factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_transport.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_transport_factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/configuration/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/configuration/resilient_http_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/port/async_resilient_http_client.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/adapter/tenacity_retry_policy.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/configuration/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/configuration/retry_configuration.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/port/retry.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/adapter.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/factory.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/python_runner_template.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/port/code.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/code_result.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/code_stdout.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/host_bridge.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/safe_code_settings.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/binary_serializer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/dictionary_serializer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/json_serializer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/port/serializer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/null_telemetry.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_logger_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_meter_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_tracer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/__init__.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/logger_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/meter_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/telemetry.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/trace_provider.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/tracer.py +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore.egg-info/dependency_links.txt +0 -0
- {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycraftcore
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Provides reusable technical infrastructure and adapter implementations for Python applications
|
|
5
|
+
Author-email: Toufik Saouchi <toufik.saouchi@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/s-toufik/pycraftcore
|
|
8
|
+
Project-URL: Repository, https://github.com/s-toufik/pycraftcore
|
|
9
|
+
Project-URL: Issues, https://github.com/s-toufik/pycraftcore/issues
|
|
10
|
+
Keywords: infrastructure,hexagonal-architecture,configuration,logging,opentelemetry,resilient-http,repository,sandbox,asyncio
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Framework :: AsyncIO
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.14
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: authentication
|
|
24
|
+
Provides-Extra: context
|
|
25
|
+
Provides-Extra: logger
|
|
26
|
+
Provides-Extra: profiler
|
|
27
|
+
Requires-Dist: pyinstrument; extra == "profiler"
|
|
28
|
+
Provides-Extra: query-language
|
|
29
|
+
Requires-Dist: sqlglot; extra == "query-language"
|
|
30
|
+
Provides-Extra: computation-engine
|
|
31
|
+
Requires-Dist: numpy; extra == "computation-engine"
|
|
32
|
+
Requires-Dist: scipy; extra == "computation-engine"
|
|
33
|
+
Provides-Extra: serializer
|
|
34
|
+
Requires-Dist: orjson; extra == "serializer"
|
|
35
|
+
Requires-Dist: msgpack; extra == "serializer"
|
|
36
|
+
Requires-Dist: pydantic>=2; extra == "serializer"
|
|
37
|
+
Provides-Extra: file-handler
|
|
38
|
+
Requires-Dist: orjson; extra == "file-handler"
|
|
39
|
+
Requires-Dist: pyyaml; extra == "file-handler"
|
|
40
|
+
Provides-Extra: runtime
|
|
41
|
+
Requires-Dist: pydantic>=2; extra == "runtime"
|
|
42
|
+
Provides-Extra: circuit-breaker
|
|
43
|
+
Requires-Dist: aiobreaker; extra == "circuit-breaker"
|
|
44
|
+
Requires-Dist: pycraftcore[logger]; extra == "circuit-breaker"
|
|
45
|
+
Provides-Extra: retry
|
|
46
|
+
Requires-Dist: tenacity; extra == "retry"
|
|
47
|
+
Requires-Dist: pycraftcore[logger]; extra == "retry"
|
|
48
|
+
Provides-Extra: http
|
|
49
|
+
Requires-Dist: aiohttp; extra == "http"
|
|
50
|
+
Requires-Dist: httpx; extra == "http"
|
|
51
|
+
Requires-Dist: orjson; extra == "http"
|
|
52
|
+
Requires-Dist: starlette; extra == "http"
|
|
53
|
+
Requires-Dist: pycraftcore[context,logger]; extra == "http"
|
|
54
|
+
Provides-Extra: resilient-http
|
|
55
|
+
Requires-Dist: aiohttp; extra == "resilient-http"
|
|
56
|
+
Requires-Dist: httpx; extra == "resilient-http"
|
|
57
|
+
Requires-Dist: pycraftcore[circuit_breaker,http,logger,retry,telemetry]; extra == "resilient-http"
|
|
58
|
+
Provides-Extra: application-configuration
|
|
59
|
+
Requires-Dist: omegaconf; extra == "application-configuration"
|
|
60
|
+
Requires-Dist: pydantic>=2; extra == "application-configuration"
|
|
61
|
+
Requires-Dist: python-dotenv; extra == "application-configuration"
|
|
62
|
+
Requires-Dist: pycraftcore[authentication,http,logger]; extra == "application-configuration"
|
|
63
|
+
Provides-Extra: telemetry
|
|
64
|
+
Requires-Dist: opentelemetry-api; extra == "telemetry"
|
|
65
|
+
Requires-Dist: opentelemetry-sdk; extra == "telemetry"
|
|
66
|
+
Requires-Dist: opentelemetry-exporter-otlp; extra == "telemetry"
|
|
67
|
+
Requires-Dist: opentelemetry-instrumentation-logging; extra == "telemetry"
|
|
68
|
+
Requires-Dist: pycraftcore[application_configuration,context]; extra == "telemetry"
|
|
69
|
+
Provides-Extra: repository
|
|
70
|
+
Requires-Dist: aiosqlite; extra == "repository"
|
|
71
|
+
Requires-Dist: pymongo; extra == "repository"
|
|
72
|
+
Requires-Dist: psycopg[binary]; extra == "repository"
|
|
73
|
+
Requires-Dist: psycopg-pool; extra == "repository"
|
|
74
|
+
Requires-Dist: orjson; extra == "repository"
|
|
75
|
+
Requires-Dist: pydantic>=2; extra == "repository"
|
|
76
|
+
Requires-Dist: pycraftcore[application_configuration,authentication]; extra == "repository"
|
|
77
|
+
Provides-Extra: runtime-sandbox
|
|
78
|
+
Requires-Dist: pandas; extra == "runtime-sandbox"
|
|
79
|
+
Requires-Dist: numpy; extra == "runtime-sandbox"
|
|
80
|
+
Requires-Dist: matplotlib; extra == "runtime-sandbox"
|
|
81
|
+
Requires-Dist: python-dateutil; extra == "runtime-sandbox"
|
|
82
|
+
Requires-Dist: pycraftcore[runtime]; extra == "runtime-sandbox"
|
|
83
|
+
Provides-Extra: all
|
|
84
|
+
Requires-Dist: pycraftcore[application_configuration]; extra == "all"
|
|
85
|
+
Requires-Dist: pycraftcore[authentication]; extra == "all"
|
|
86
|
+
Requires-Dist: pycraftcore[circuit_breaker]; extra == "all"
|
|
87
|
+
Requires-Dist: pycraftcore[computation_engine]; extra == "all"
|
|
88
|
+
Requires-Dist: pycraftcore[context]; extra == "all"
|
|
89
|
+
Requires-Dist: pycraftcore[file_handler]; extra == "all"
|
|
90
|
+
Requires-Dist: pycraftcore[http]; extra == "all"
|
|
91
|
+
Requires-Dist: pycraftcore[logger]; extra == "all"
|
|
92
|
+
Requires-Dist: pycraftcore[profiler]; extra == "all"
|
|
93
|
+
Requires-Dist: pycraftcore[query_language]; extra == "all"
|
|
94
|
+
Requires-Dist: pycraftcore[repository]; extra == "all"
|
|
95
|
+
Requires-Dist: pycraftcore[resilient_http]; extra == "all"
|
|
96
|
+
Requires-Dist: pycraftcore[retry]; extra == "all"
|
|
97
|
+
Requires-Dist: pycraftcore[runtime]; extra == "all"
|
|
98
|
+
Requires-Dist: pycraftcore[runtime-sandbox]; extra == "all"
|
|
99
|
+
Requires-Dist: pycraftcore[serializer]; extra == "all"
|
|
100
|
+
Requires-Dist: pycraftcore[telemetry]; extra == "all"
|
|
101
|
+
Dynamic: license-file
|
|
102
|
+
|
|
103
|
+
# pycraftcore
|
|
104
|
+
|
|
105
|
+
The shared technical foundation of the homelab's Python services: configuration, logging, telemetry, resilient HTTP, database repositories, safe SQL and a sandbox for running untrusted Python. Small, swappable building blocks rather than a framework — every module is an interface (a port) plus the implementations behind it (adapters).
|
|
106
|
+
|
|
107
|
+
**[Using pycraftcore](#using-pycraftcore)** — install it and use each module in your service.
|
|
108
|
+
**[Working on pycraftcore](#working-on-pycraftcore)** — how it is organised and how to release it.
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
your service (agent-orchestrator, agent-toolbox, …)
|
|
112
|
+
│
|
|
113
|
+
▼
|
|
114
|
+
┌─ pycraftcore ─────────────────────────────────────────────────────────────┐
|
|
115
|
+
│ │
|
|
116
|
+
│ set up application_configuration · logger · telemetry · context │
|
|
117
|
+
│ talk out http · resilient_http (retry + circuit_breaker) │
|
|
118
|
+
│ store repository: SQLite · PostgreSQL · MongoDB │
|
|
119
|
+
│ run safely query_language (read-only SQL) · runtime (Python sandbox) │
|
|
120
|
+
│ utilities file_handler · serializer · computation_engine · profiler │
|
|
121
|
+
│ … new modules, same port + adapter shape │
|
|
122
|
+
│ │
|
|
123
|
+
└───────────────────────────────────────────────────────────────────────────┘
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Using pycraftcore
|
|
129
|
+
|
|
130
|
+
### Install
|
|
131
|
+
|
|
132
|
+
Requires Python 3.14. Each module's third-party dependencies sit behind an extra of the same name, so a service installs only what it uses:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
uv add "pycraftcore[http,repository]" # or: pip install "pycraftcore[http,repository]"
|
|
136
|
+
uv add "pycraftcore[all]" # every module
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A bare `pycraftcore` installs no third-party packages: only `authentication`, `context` and `logger` work without an extra. For any other module, importing its adapters without the extra fails with `ModuleNotFoundError`.
|
|
140
|
+
|
|
141
|
+
| Extra | Third-party packages | Also brings in |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `authentication`, `context`, `logger` | none | — |
|
|
144
|
+
| `profiler` | pyinstrument | — |
|
|
145
|
+
| `query_language` | sqlglot | — |
|
|
146
|
+
| `computation_engine` | numpy, scipy | — |
|
|
147
|
+
| `serializer` | orjson, msgpack, pydantic | — |
|
|
148
|
+
| `file_handler` | orjson, pyyaml | — |
|
|
149
|
+
| `runtime` | pydantic | — |
|
|
150
|
+
| `runtime-sandbox` | pandas, numpy, matplotlib, python-dateutil | `runtime` |
|
|
151
|
+
| `circuit_breaker` | aiobreaker | `logger` |
|
|
152
|
+
| `retry` | tenacity | `logger` |
|
|
153
|
+
| `http` | aiohttp, httpx, orjson, starlette | `context`, `logger` |
|
|
154
|
+
| `application_configuration` | omegaconf, pydantic, python-dotenv | `http`, `logger`, `authentication` |
|
|
155
|
+
| `telemetry` | opentelemetry (api, sdk, otlp exporter, logging instrumentation) | `application_configuration`, `context` |
|
|
156
|
+
| `resilient_http` | aiohttp, httpx | `circuit_breaker`, `http`, `retry`, `telemetry` |
|
|
157
|
+
| `repository` | aiosqlite, pymongo, psycopg, psycopg-pool, orjson, pydantic | `application_configuration`, `authentication` |
|
|
158
|
+
|
|
159
|
+
List every module your code imports, even when one extra already brings in another. That way your service stays correct if these links change. For the Python sandbox, use `runtime-sandbox` rather than `runtime`: the sandboxed code runs on your service's interpreter, so the libraries it may import (pandas, numpy, matplotlib, dateutil) must be installed there.
|
|
160
|
+
|
|
161
|
+
Development builds are published to TestPyPI; use them for testing only. Their dependencies come from PyPI, so add it as an extra index:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "pycraftcore[http]"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### What's inside
|
|
168
|
+
|
|
169
|
+
Each module stands on its own: use the ones you need. The modules today (new ones follow the same shape, see [Working on pycraftcore](#working-on-pycraftcore)):
|
|
170
|
+
|
|
171
|
+
| Package | What it gives you |
|
|
172
|
+
|---|---|
|
|
173
|
+
| `application_configuration` | YAML configuration (connectors and operations) driven by environment variables, loaded into typed objects |
|
|
174
|
+
| `authentication` | The auth models a connector can use: none, basic, token |
|
|
175
|
+
| `logger` | One log format for the whole process, with the request id on every line |
|
|
176
|
+
| `context` | `request_id_context`, the request id shared by logs, telemetry and outgoing calls |
|
|
177
|
+
| `telemetry` | OpenTelemetry traces, metrics and logs, exported to an OTLP collector (or nothing) |
|
|
178
|
+
| `http` | Async HTTP clients (httpx, aiohttp), request-id and request-context middlewares, retry policies |
|
|
179
|
+
| `resilient_http` | An HTTP transport with retries and a circuit breaker built in |
|
|
180
|
+
| `retry`, `circuit_breaker` | The two policies on their own, for any async call |
|
|
181
|
+
| `repository` | Async repositories built from a database connector — today SQLite (pooled), PostgreSQL and MongoDB |
|
|
182
|
+
| `query_language` | Checks that a SQL statement is read-only, and translates between dialects, before it reaches a database |
|
|
183
|
+
| `runtime` | Runs untrusted Python in a separate process with memory, time and module limits; optionally lets it call your functions |
|
|
184
|
+
| `file_handler` | Read and write files by extension (today csv, json, yml, md, txt, svg); a new format is a new reader/writer pair |
|
|
185
|
+
| `serializer` | JSON, dict and binary (msgpack) serialisation of dataclasses |
|
|
186
|
+
| `computation_engine` | Numerical helpers: financial arithmetic, integration, interpolation |
|
|
187
|
+
| `profiler` | `@profiled`, an async profiling decorator |
|
|
188
|
+
|
|
189
|
+
### Configuration
|
|
190
|
+
|
|
191
|
+
A service's configuration is a folder of YAML files, with values taken from environment variables (`${oc.env:NAME}`):
|
|
192
|
+
|
|
193
|
+
| Path | What it holds |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `root.yml` | The environment and the list of connectors and operations in use |
|
|
196
|
+
| `<env>/connector/*.yml` | Where things are: APIs, databases, files, MCP servers, telemetry |
|
|
197
|
+
| `<env>/operation/*.yml` | What you do with them; each operation points at a connector |
|
|
198
|
+
|
|
199
|
+
```yaml
|
|
200
|
+
# <env>/connector/api.yml
|
|
201
|
+
connector:
|
|
202
|
+
weather:
|
|
203
|
+
name: weather
|
|
204
|
+
type: api
|
|
205
|
+
base_url: https://<api-host>
|
|
206
|
+
timeout: 5
|
|
207
|
+
retry: 3
|
|
208
|
+
auth:
|
|
209
|
+
type: token
|
|
210
|
+
key_name: apikey
|
|
211
|
+
key_value: ${oc.env:WEATHER_API_KEY}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
# <env>/operation/weather.yml
|
|
216
|
+
operation:
|
|
217
|
+
forecast:
|
|
218
|
+
name: forecast
|
|
219
|
+
type: api
|
|
220
|
+
connector: ${connector.weather}
|
|
221
|
+
endpoint: /forecast
|
|
222
|
+
method: GET
|
|
223
|
+
parameters:
|
|
224
|
+
days: 3
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
# root.yml
|
|
229
|
+
application_configuration:
|
|
230
|
+
env: ${oc.env:APP_ENV,debug}
|
|
231
|
+
run: async
|
|
232
|
+
connector:
|
|
233
|
+
api:
|
|
234
|
+
weather: ${connector.weather}
|
|
235
|
+
operation:
|
|
236
|
+
forecast: ${operation.forecast}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from pathlib import Path
|
|
241
|
+
|
|
242
|
+
from pycraftcore.application_configuration.adapter import (
|
|
243
|
+
LoadApplicationConfiguration,
|
|
244
|
+
OmegaConfigurationReader,
|
|
245
|
+
)
|
|
246
|
+
from pycraftcore.application_configuration.enum import RunTypeEnvironment
|
|
247
|
+
from pycraftcore.logger.adapter import StandardLogger
|
|
248
|
+
|
|
249
|
+
reader = OmegaConfigurationReader(RunTypeEnvironment.debug, Path("config"))
|
|
250
|
+
config = LoadApplicationConfiguration(reader, StandardLogger()).load()
|
|
251
|
+
|
|
252
|
+
connector = config.connector.api("weather") # ApiConnector
|
|
253
|
+
operation = config.operation.api("forecast") # ApiOperation
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`config.connector` returns typed connectors per kind (`.api()`, `.database()`, `.file()`, `.mcp()`, `.telemetry()`) and `config.operation` typed operations (`.api()`, `.file()`); the `type` field of each entry decides which fields it takes.
|
|
257
|
+
|
|
258
|
+
### Logging and the request id
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from pycraftcore.logger import configure_logging
|
|
262
|
+
from pycraftcore.logger.adapter import StandardLogger
|
|
263
|
+
|
|
264
|
+
configure_logging("INFO")
|
|
265
|
+
logger = StandardLogger()
|
|
266
|
+
logger.info("booted")
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
```text
|
|
270
|
+
2026-09-27 10:06:17.679 | INFO | 1bddfefb-… | controller:execute:51 - stream request accepted
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- The third column is `request_id_context`. In a web service, `RequestIDMiddleware` (`pycraftcore.http.middleware`) sets it from the `X-Request-ID` header; every line logged during that request shows it, `-` otherwise. Don't write it in your messages.
|
|
274
|
+
- Uvicorn logs use the same format; chatty libraries (httpx, mcp, pymongo…) stay at `WARNING`.
|
|
275
|
+
- Calling `configure_logging` again replaces the handler rather than adding a second one.
|
|
276
|
+
|
|
277
|
+
### Telemetry
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from pycraftcore.application_configuration.enum import RunTypeEnvironment
|
|
281
|
+
from pycraftcore.telemetry.adapter import OpenTelemetryProvider
|
|
282
|
+
|
|
283
|
+
telemetry = OpenTelemetryProvider(
|
|
284
|
+
service_name="my-service",
|
|
285
|
+
environment=RunTypeEnvironment.debug,
|
|
286
|
+
otlp_endpoint="<collector-host>:<port>", # None: nothing is exported
|
|
287
|
+
)
|
|
288
|
+
tracer = telemetry.tracer("my-service")
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Spans and log records carry the request id as an attribute, so logs and traces line up in Grafana.
|
|
292
|
+
|
|
293
|
+
### Resilient HTTP
|
|
294
|
+
|
|
295
|
+
A transport that retries what is worth retrying and stops calling a backend that keeps failing:
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
from pycraftcore.circuit_breaker.configuration import CircuitBreakerSettings
|
|
299
|
+
from pycraftcore.http.configuration import HttpClientSettings, LimitsSettings
|
|
300
|
+
from pycraftcore.http.policy.http_error_policy import is_business_error, is_retryable
|
|
301
|
+
from pycraftcore.resilient_http.adapter import ResilientTransportFactory
|
|
302
|
+
from pycraftcore.resilient_http.configuration import ResilientHttpSettings
|
|
303
|
+
from pycraftcore.retry.configuration import RetrySettings
|
|
304
|
+
|
|
305
|
+
settings = ResilientHttpSettings(
|
|
306
|
+
http=HttpClientSettings(limits=LimitsSettings(timeout=10)),
|
|
307
|
+
retry=RetrySettings(
|
|
308
|
+
retry_count=3, retry_delay=1, max_retry_delay=20, should_retry=is_retryable
|
|
309
|
+
),
|
|
310
|
+
circuit_breaker=CircuitBreakerSettings(
|
|
311
|
+
failure_threshold=3, recovery_timeout=30, is_excluded=is_business_error, name="weather"
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
client = ResilientTransportFactory(
|
|
315
|
+
settings=settings, trace_manager=tracer, logger=logger
|
|
316
|
+
).create_async_client()
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
`client` is an `httpx.AsyncClient`: pass it to any library that accepts one.
|
|
320
|
+
|
|
321
|
+
### Databases
|
|
322
|
+
|
|
323
|
+
Repositories are built from a database connector and share one interface, `AsyncRepository.execute(statement)`:
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
from pycraftcore.repository.adapter import SqliteRepositoryFactory, SqliteSettingsMapper
|
|
327
|
+
|
|
328
|
+
factory = SqliteRepositoryFactory(SqliteSettingsMapper(config.connector.database("users"))())
|
|
329
|
+
repository = await factory.connect()
|
|
330
|
+
rows = await repository.execute("SELECT id, email FROM users LIMIT 10")
|
|
331
|
+
await factory.disconnect()
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`PostgresRepositoryFactory` / `PostgresSettingsMapper` and `MongoRepositoryFactory` / `MongoSettingsMapper` work the same way, and so will any database added later: a factory, a settings mapper and a repository behind the same `AsyncRepository` port.
|
|
335
|
+
|
|
336
|
+
### Read-only SQL
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
from pycraftcore.query_language.adapter import SqlHandlerFactory
|
|
340
|
+
|
|
341
|
+
statement = SqlHandlerFactory()("SELECT * FROM users", dialect="sqlite").transpile()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
`transpile()` raises when the statement writes, deletes or changes the schema, so only reads reach the database.
|
|
345
|
+
|
|
346
|
+
### Running untrusted Python
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
from pycraftcore.runtime.adapter import PythonSafeCodeFactory
|
|
350
|
+
from pycraftcore.runtime.schema import CodeResult, SafeCodeSettings
|
|
351
|
+
|
|
352
|
+
factory = PythonSafeCodeFactory(settings=SafeCodeSettings(code_timeout=30, max_memory_mb=256))
|
|
353
|
+
output = await factory(code="import math\nresult = math.sqrt(2)").execute()
|
|
354
|
+
print(CodeResult.from_stdout(output.stdout).value) # 1.4142135623730951
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
- The code runs in its own process; only the modules in `PYTHON_ALLOWLIST` can be imported (pandas, numpy, matplotlib, datetime…).
|
|
358
|
+
- `working_directory` in `SafeCodeSettings` confines file access to one folder.
|
|
359
|
+
- With a `HostBridgeServer`, the code can call functions of your service as if they were local — this is how agent-toolbox lets sandboxed code use its other tools.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Working on pycraftcore
|
|
364
|
+
|
|
365
|
+
### How a module is organised
|
|
366
|
+
|
|
367
|
+
Every package follows the same shape, so a new backend is a new adapter, never a change for the services that use the port:
|
|
368
|
+
|
|
369
|
+
```text
|
|
370
|
+
pycraftcore/<module>/
|
|
371
|
+
├── port/ Protocols the services depend on (AsyncRepository, Code, Logger…)
|
|
372
|
+
├── adapter/ Implementations (SqliteRepository, PythonSafeCode, StandardLogger…)
|
|
373
|
+
├── configuration/ Pydantic settings objects (RetrySettings, CircuitBreakerSettings…)
|
|
374
|
+
├── schema/ Data passed across the port (CodeResult, SafeCodeSettings…)
|
|
375
|
+
└── enum/ Fixed choices (ConnectorType, HttpMethod…)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Not every module needs every folder: add a folder when it has something to hold.
|
|
379
|
+
|
|
380
|
+
### Design rules
|
|
381
|
+
|
|
382
|
+
- Services import the **port** and receive an adapter; they never construct a third-party client themselves.
|
|
383
|
+
- Adapters are thin: they translate between the port and the library, and keep library types out of the port.
|
|
384
|
+
- Settings are validated pydantic models; nothing reads environment variables except the configuration loader.
|
|
385
|
+
- Every public module is typed: `ty` runs in strict mode.
|
|
386
|
+
|
|
387
|
+
### Adding a module or an adapter
|
|
388
|
+
|
|
389
|
+
1. Define the protocol in `<module>/port/`.
|
|
390
|
+
2. Implement it in `<module>/adapter/`, with its settings in `configuration/` and the data it exchanges in `schema/`.
|
|
391
|
+
3. Export the public names from the package's `__init__.py`.
|
|
392
|
+
4. Declare its third-party packages as an extra named after the module in `[project.optional-dependencies]` (pull in other modules with `pycraftcore[<module>]`). Then add it to `all`, to the [Install](#install) table, and to the `extras` matrix in `.github/workflows/cicd.yml`, which installs each extra alone and imports its package.
|
|
393
|
+
5. Add tests under `tests/<module>/`, and the module to [What's inside](#whats-inside) — with a short example under Using pycraftcore if services will use it directly.
|
|
394
|
+
|
|
395
|
+
### Tests and checks
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
make install_dev # uv sync --group dev --all-extras
|
|
399
|
+
make check # ruff + ty + pytest
|
|
400
|
+
make test
|
|
401
|
+
make lint / make fix / make format / make typecheck
|
|
402
|
+
uv run pytest --cov=pycraftcore --cov-report=term-missing # coverage per module
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Pre-commit runs the test suite before every commit and push:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
uv run pre-commit install --hook-type pre-commit --hook-type pre-push
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Releasing
|
|
412
|
+
|
|
413
|
+
1. Bump `version` in `pyproject.toml`.
|
|
414
|
+
2. `make build`.
|
|
415
|
+
3. `make publish_dev` to TestPyPI (needs `TEST_PYPI_TOKEN`), try it in a service, then `make publish` to PyPI (needs `RELEASE_PYPI_TOKEN`).
|
|
416
|
+
4. Raise the minimum version in the services that need the change (`pycraftcore[<extras>]>=…` in their `pyproject.toml`), then `uv lock --upgrade-package pycraftcore`.
|
|
417
|
+
|
|
418
|
+
To work on pycraftcore and a service at the same time, point the service at your checkout with an editable path dependency in its `pyproject.toml`: `pycraftcore = { path = "../pycraftcore", editable = true }` under `[tool.uv.sources]`.
|