haiway 0.11.1__tar.gz → 0.12.1__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.
- {haiway-0.11.1 → haiway-0.12.1}/PKG-INFO +1 -1
- {haiway-0.11.1 → haiway-0.12.1}/junit/test-results.xml +1 -1
- {haiway-0.11.1 → haiway-0.12.1}/pyproject.toml +1 -1
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/__init__.py +2 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/access.py +95 -17
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/disposables.py +27 -4
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/identifier.py +76 -9
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/logging.py +75 -9
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/metrics.py +56 -7
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/state.py +78 -10
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/tasks.py +50 -7
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/asynchrony.py +14 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/caching.py +30 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/metrics.py +20 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/throttling.py +16 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/timeouted.py +13 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/state/attributes.py +15 -1
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/state/path.py +172 -12
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/state/requirement.py +50 -7
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/state/structure.py +50 -4
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/state/validation.py +63 -8
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/types/default.py +2 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/__init__.py +9 -1
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/collections.py +14 -14
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/env.py +50 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/queue.py +69 -10
- {haiway-0.11.1 → haiway-0.12.1}/uv.lock +1 -1
- {haiway-0.11.1 → haiway-0.12.1}/.github/workflows/ci.yml +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/.github/workflows/publish.yml +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/.gitignore +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/LICENSE +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/Makefile +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/README.md +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/config/pre-push +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/.dockerignore +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/Dockerfile +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/Makefile +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/README.md +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/config/.env.example +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/config/unit.json +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/docker-compose.yml +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/pyproject.toml +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/__int__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/todos/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/todos/calls.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/todos/config.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/todos/state.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/todos/types.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/features/todos/user_tasks.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/integrations/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/integrations/postgres/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/integrations/postgres/client.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/integrations/postgres/config.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/integrations/postgres/state.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/integrations/postgres/types.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/__main__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/application.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/config.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/middlewares/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/middlewares/context.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/routes/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/routes/technical.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/server/routes/todos.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/user_tasks/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/user_tasks/calls.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/user_tasks/config.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/user_tasks/postgres.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/user_tasks/state.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/src/solutions/user_tasks/types.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/examples/fastAPI/uv.lock +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/guidelines/functionalities.md +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/guidelines/packages.md +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/context/types.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/retries.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/helpers/tracing.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/py.typed +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/state/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/types/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/types/frozen.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/types/missing.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/always.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/freezing.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/logs.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/mimic.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/src/haiway/utils/noop.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/__init__.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_async_queue.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_attribute_path.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_auto_retry.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_cache.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_context.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_state.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_streaming.py +0 -0
- {haiway-0.11.1 → haiway-0.12.1}/tests/test_timeout.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.12.1
|
4
4
|
Summary: Framework for dependency injection and state management within structured concurrency model.
|
5
5
|
Project-URL: Homepage, https://miquido.com
|
6
6
|
Project-URL: Repository, https://github.com/miquido/haiway.git
|
@@ -1 +1 @@
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="79" time="1.
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="79" time="1.137" timestamp="2025-03-19T13:54:11.788096" hostname="fv-az1914-448"><testcase classname="tests.test_async_queue" name="test_fails_when_stream_fails" time="0.001" /><testcase classname="tests.test_async_queue" name="test_cancels_when_iteration_cancels" time="0.001" /><testcase classname="tests.test_async_queue" name="test_ends_when_stream_ends" time="0.001" /><testcase classname="tests.test_async_queue" name="test_buffers_values_when_not_reading" time="0.001" /><testcase classname="tests.test_async_queue" name="test_delivers_buffer_when_streaming_fails" time="0.001" /><testcase classname="tests.test_async_queue" name="test_delivers_updates_when_sending" time="0.001" /><testcase classname="tests.test_async_queue" name="test_fails_when_sending_to_finished" time="0.001" /><testcase classname="tests.test_async_queue" name="test_ignores_when_finishing_when_finished" time="0.001" /><testcase classname="tests.test_attribute_path" name="test_id_path_points_to_self" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_attribute_path_points_to_attribute" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_nested_attribute_path_points_to_nested_attribute" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_recursive_attribute_path_points_to_attribute" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_list_item_path_points_to_item" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_tuple_item_path_points_to_item" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_mixed_tuple_item_path_points_to_item" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_dict_item_path_points_to_item" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_id_path_set_updates_self" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_attribute_path_set_updates_attribute" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_nested_attribute_path_set_updates_nested_attribute" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_recursive_attribute_set_updates_attribute" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_list_item_path_set_updates_item" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_tuple_item_path_set_updates_item" time="0.000" /><testcase classname="tests.test_attribute_path" name="test_mixed_tuple_item_set_updates_item" time="0.001" /><testcase classname="tests.test_attribute_path" name="test_dict_item_path_set_updates_item" time="0.000" /><testcase classname="tests.test_auto_retry" name="test_returns_value_without_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_retries_with_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_logs_issue_with_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_fails_with_exceeding_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_fails_with_cancellation" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_retries_with_selected_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_fails_with_not_selected_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_returns_value_without_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_retries_with_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_fails_with_exceeding_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_fails_with_cancellation" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_fails_when_cancelled" time="0.021" /><testcase classname="tests.test_auto_retry" name="test_async_uses_delay_with_errors" time="0.101" /><testcase classname="tests.test_auto_retry" name="test_async_uses_computed_delay_with_errors" time="0.106" /><testcase classname="tests.test_auto_retry" name="test_async_logs_issue_with_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_retries_with_selected_errors" time="0.001" /><testcase classname="tests.test_auto_retry" name="test_async_fails_with_not_selected_errors" time="0.001" /><testcase classname="tests.test_cache" name="test_returns_cache_value_with_same_argument" time="0.000" /><testcase classname="tests.test_cache" name="test_returns_fresh_value_with_different_argument" time="0.000" /><testcase classname="tests.test_cache" name="test_returns_fresh_value_with_limit_exceed" time="0.000" /><testcase classname="tests.test_cache" name="test_returns_same_value_with_repeating_argument" time="0.000" /><testcase classname="tests.test_cache" name="test_fails_with_error" time="0.000" /><testcase classname="tests.test_cache" name="test_returns_fresh_value_with_expiration_time_exceed" time="0.021" /><testcase classname="tests.test_cache" name="test_async_returns_cache_value_with_same_argument" time="0.001" /><testcase classname="tests.test_cache" name="test_async_returns_fresh_value_with_different_argument" time="0.001" /><testcase classname="tests.test_cache" name="test_async_returns_fresh_value_with_limit_exceed" time="0.001" /><testcase classname="tests.test_cache" name="test_async_returns_same_value_with_repeating_argument" time="0.001" /><testcase classname="tests.test_cache" name="test_async_returns_fresh_value_with_expiration_time_exceed" time="0.021" /><testcase classname="tests.test_cache" name="test_async_cancel_waiting_does_not_cancel_task" time="0.502" /><testcase classname="tests.test_cache" name="test_async_expiration_does_not_cancel_task" time="0.021" /><testcase classname="tests.test_cache" name="test_async_expiration_creates_new_task" time="0.041" /><testcase classname="tests.test_cache" name="test_async_fails_with_error" time="0.001" /><testcase classname="tests.test_context" name="test_state_is_available_according_to_context" time="0.001" /><testcase classname="tests.test_context" name="test_state_update_updates_local_context" time="0.001" /><testcase classname="tests.test_context" name="test_exceptions_are_propagated" time="0.001" /><testcase classname="tests.test_state" name="test_basic_initializes_with_arguments" time="0.002" /><testcase classname="tests.test_state" name="test_basic_initializes_with_defaults" time="0.001" /><testcase classname="tests.test_state" name="test_basic_equals_checks_properties" time="0.000" /><testcase classname="tests.test_state" name="test_basic_initializes_with_arguments_and_defaults" time="0.001" /><testcase classname="tests.test_state" name="test_parametrized_initializes_with_proper_parameters" time="0.001" /><testcase classname="tests.test_state" name="test_nested_initializes_with_proper_arguments" time="0.001" /><testcase classname="tests.test_state" name="test_dict_skips_missing_properties" time="0.001" /><testcase classname="tests.test_state" name="test_initialization_allows_missing_properties" time="0.001" /><testcase classname="tests.test_state" name="test_generic_subtypes_validation" time="0.002" /><testcase classname="tests.test_state" name="test_copying_leaves_same_object" time="0.001" /><testcase classname="tests.test_streaming" name="test_fails_when_generator_fails" time="0.001" /><testcase classname="tests.test_streaming" name="test_cancels_when_iteration_cancels" time="0.001" /><testcase classname="tests.test_streaming" name="test_ends_when_generator_ends" time="0.001" /><testcase classname="tests.test_streaming" name="test_delivers_updates_when_generating" time="0.001" /><testcase classname="tests.test_streaming" name="test_streaming_context_variables_access_is_preserved" time="0.001" /><testcase classname="tests.test_streaming" name="test_nested_streaming_streams_correctly" time="0.001" /><testcase classname="tests.test_timeout" name="test_returns_result_when_returning_value" time="0.001" /><testcase classname="tests.test_timeout" name="test_raises_with_error" time="0.001" /><testcase classname="tests.test_timeout" name="test_raises_with_cancel" time="0.011" /><testcase classname="tests.test_timeout" name="test_raises_with_timeout" time="0.011" /></testsuite></testsuites>
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
5
5
|
[project]
|
6
6
|
name = "haiway"
|
7
7
|
description = "Framework for dependency injection and state management within structured concurrency model."
|
8
|
-
version = "0.
|
8
|
+
version = "0.12.1"
|
9
9
|
readme = "README.md"
|
10
10
|
maintainers = [
|
11
11
|
{ name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
|
@@ -48,6 +48,7 @@ from haiway.utils import (
|
|
48
48
|
async_always,
|
49
49
|
async_noop,
|
50
50
|
freeze,
|
51
|
+
getenv_base64,
|
51
52
|
getenv_bool,
|
52
53
|
getenv_float,
|
53
54
|
getenv_int,
|
@@ -96,6 +97,7 @@ __all__ = [
|
|
96
97
|
"ctx",
|
97
98
|
"freeze",
|
98
99
|
"frozenlist",
|
100
|
+
"getenv_base64",
|
99
101
|
"getenv_bool",
|
100
102
|
"getenv_float",
|
101
103
|
"getenv_int",
|
@@ -23,7 +23,7 @@ from haiway.context.metrics import MetricsContext, MetricsHandler
|
|
23
23
|
from haiway.context.state import StateContext
|
24
24
|
from haiway.context.tasks import TaskGroupContext
|
25
25
|
from haiway.state import State
|
26
|
-
from haiway.utils import
|
26
|
+
from haiway.utils import mimic_function
|
27
27
|
|
28
28
|
__all__ = [
|
29
29
|
"ctx",
|
@@ -32,6 +32,16 @@ __all__ = [
|
|
32
32
|
|
33
33
|
@final
|
34
34
|
class ScopeContext:
|
35
|
+
__slots__ = (
|
36
|
+
"_disposables",
|
37
|
+
"_identifier",
|
38
|
+
"_logger_context",
|
39
|
+
"_metrics_context",
|
40
|
+
"_state",
|
41
|
+
"_state_context",
|
42
|
+
"_task_group_context",
|
43
|
+
)
|
44
|
+
|
35
45
|
def __init__(
|
36
46
|
self,
|
37
47
|
label: str,
|
@@ -40,29 +50,77 @@ class ScopeContext:
|
|
40
50
|
disposables: Disposables | None,
|
41
51
|
metrics: MetricsHandler | None,
|
42
52
|
) -> None:
|
43
|
-
self._identifier: ScopeIdentifier
|
44
|
-
|
45
|
-
self
|
46
|
-
|
53
|
+
self._identifier: ScopeIdentifier
|
54
|
+
object.__setattr__(
|
55
|
+
self,
|
56
|
+
"_identifier",
|
57
|
+
ScopeIdentifier.scope(label),
|
58
|
+
)
|
59
|
+
self._logger_context: LoggerContext
|
60
|
+
object.__setattr__(
|
61
|
+
self,
|
62
|
+
"_logger_context",
|
63
|
+
LoggerContext(
|
64
|
+
self._identifier,
|
65
|
+
logger=logger,
|
66
|
+
),
|
47
67
|
)
|
48
|
-
|
68
|
+
# postponing task group creation to include only when needed
|
69
|
+
self._task_group_context: TaskGroupContext
|
49
70
|
# postponing state creation to include disposables state when prepared
|
50
71
|
self._state_context: StateContext
|
51
|
-
self._state: tuple[State, ...]
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
72
|
+
self._state: tuple[State, ...]
|
73
|
+
object.__setattr__(
|
74
|
+
self,
|
75
|
+
"_state",
|
76
|
+
state,
|
77
|
+
)
|
78
|
+
self._disposables: Disposables | None
|
79
|
+
object.__setattr__(
|
80
|
+
self,
|
81
|
+
"_disposables",
|
82
|
+
disposables,
|
83
|
+
)
|
84
|
+
self._metrics_context: MetricsContext
|
85
|
+
object.__setattr__(
|
86
|
+
self,
|
87
|
+
"_metrics_context",
|
88
|
+
# pre-building metrics context to ensure nested context registering
|
89
|
+
MetricsContext.scope(
|
90
|
+
self._identifier,
|
91
|
+
metrics=metrics,
|
92
|
+
),
|
57
93
|
)
|
58
94
|
|
59
|
-
|
95
|
+
def __setattr__(
|
96
|
+
self,
|
97
|
+
name: str,
|
98
|
+
value: Any,
|
99
|
+
) -> Any:
|
100
|
+
raise AttributeError(
|
101
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
102
|
+
f" attribute - '{name}' cannot be modified"
|
103
|
+
)
|
104
|
+
|
105
|
+
def __delattr__(
|
106
|
+
self,
|
107
|
+
name: str,
|
108
|
+
) -> None:
|
109
|
+
raise AttributeError(
|
110
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
111
|
+
f" attribute - '{name}' cannot be deleted"
|
112
|
+
)
|
60
113
|
|
61
114
|
def __enter__(self) -> str:
|
62
115
|
assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
|
63
116
|
self._identifier.__enter__()
|
64
117
|
self._logger_context.__enter__()
|
65
|
-
|
118
|
+
# lazily initialize state
|
119
|
+
object.__setattr__(
|
120
|
+
self,
|
121
|
+
"_state_context",
|
122
|
+
StateContext.updated(self._state),
|
123
|
+
)
|
66
124
|
self._state_context.__enter__()
|
67
125
|
self._metrics_context.__enter__()
|
68
126
|
|
@@ -101,15 +159,33 @@ class ScopeContext:
|
|
101
159
|
async def __aenter__(self) -> str:
|
102
160
|
self._identifier.__enter__()
|
103
161
|
self._logger_context.__enter__()
|
162
|
+
# lazily initialize group when needed
|
163
|
+
object.__setattr__(
|
164
|
+
self,
|
165
|
+
"_task_group_context",
|
166
|
+
TaskGroupContext(),
|
167
|
+
)
|
104
168
|
await self._task_group_context.__aenter__()
|
105
169
|
|
170
|
+
# lazily initialize state to include disposables results
|
106
171
|
if self._disposables is not None:
|
107
|
-
|
108
|
-
|
172
|
+
object.__setattr__(
|
173
|
+
self,
|
174
|
+
"_state_context",
|
175
|
+
StateContext.updated(
|
176
|
+
(
|
177
|
+
*self._state,
|
178
|
+
*await self._disposables.__aenter__(),
|
179
|
+
)
|
180
|
+
),
|
109
181
|
)
|
110
182
|
|
111
183
|
else:
|
112
|
-
|
184
|
+
object.__setattr__(
|
185
|
+
self,
|
186
|
+
"_state_context",
|
187
|
+
StateContext.updated(self._state),
|
188
|
+
)
|
113
189
|
|
114
190
|
self._state_context.__enter__()
|
115
191
|
self._metrics_context.__enter__()
|
@@ -200,6 +276,8 @@ class ScopeContext:
|
|
200
276
|
|
201
277
|
@final
|
202
278
|
class ctx:
|
279
|
+
__slots__ = ()
|
280
|
+
|
203
281
|
@staticmethod
|
204
282
|
def trace_id() -> str:
|
205
283
|
"""
|
@@ -3,10 +3,9 @@ from collections.abc import Iterable
|
|
3
3
|
from contextlib import AbstractAsyncContextManager
|
4
4
|
from itertools import chain
|
5
5
|
from types import TracebackType
|
6
|
-
from typing import final
|
6
|
+
from typing import Any, final
|
7
7
|
|
8
8
|
from haiway.state import State
|
9
|
-
from haiway.utils import freeze
|
10
9
|
|
11
10
|
__all__ = [
|
12
11
|
"Disposable",
|
@@ -18,13 +17,37 @@ type Disposable = AbstractAsyncContextManager[Iterable[State] | State | None]
|
|
18
17
|
|
19
18
|
@final
|
20
19
|
class Disposables:
|
20
|
+
__slots__ = ("_disposables",)
|
21
|
+
|
21
22
|
def __init__(
|
22
23
|
self,
|
23
24
|
*disposables: Disposable,
|
24
25
|
) -> None:
|
25
|
-
self._disposables: tuple[Disposable, ...]
|
26
|
+
self._disposables: tuple[Disposable, ...]
|
27
|
+
object.__setattr__(
|
28
|
+
self,
|
29
|
+
"_disposables",
|
30
|
+
disposables,
|
31
|
+
)
|
32
|
+
|
33
|
+
def __setattr__(
|
34
|
+
self,
|
35
|
+
name: str,
|
36
|
+
value: Any,
|
37
|
+
) -> Any:
|
38
|
+
raise AttributeError(
|
39
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
40
|
+
f" attribute - '{name}' cannot be modified"
|
41
|
+
)
|
26
42
|
|
27
|
-
|
43
|
+
def __delattr__(
|
44
|
+
self,
|
45
|
+
name: str,
|
46
|
+
) -> None:
|
47
|
+
raise AttributeError(
|
48
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
49
|
+
f" attribute - '{name}' cannot be deleted"
|
50
|
+
)
|
28
51
|
|
29
52
|
def __bool__(self) -> bool:
|
30
53
|
return len(self._disposables) > 0
|
@@ -48,6 +48,15 @@ class ScopeIdentifier:
|
|
48
48
|
trace_id=current.trace_id,
|
49
49
|
)
|
50
50
|
|
51
|
+
__slots__ = (
|
52
|
+
"_token",
|
53
|
+
"label",
|
54
|
+
"parent_id",
|
55
|
+
"scope_id",
|
56
|
+
"trace_id",
|
57
|
+
"unique_name",
|
58
|
+
)
|
59
|
+
|
51
60
|
def __init__(
|
52
61
|
self,
|
53
62
|
trace_id: str,
|
@@ -55,11 +64,61 @@ class ScopeIdentifier:
|
|
55
64
|
scope_id: str,
|
56
65
|
label: str,
|
57
66
|
) -> None:
|
58
|
-
self.trace_id: str
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
self.trace_id: str
|
68
|
+
object.__setattr__(
|
69
|
+
self,
|
70
|
+
"trace_id",
|
71
|
+
trace_id,
|
72
|
+
)
|
73
|
+
self.parent_id: str
|
74
|
+
object.__setattr__(
|
75
|
+
self,
|
76
|
+
"parent_id",
|
77
|
+
parent_id,
|
78
|
+
)
|
79
|
+
self.scope_id: str
|
80
|
+
object.__setattr__(
|
81
|
+
self,
|
82
|
+
"scope_id",
|
83
|
+
scope_id,
|
84
|
+
)
|
85
|
+
self.label: str
|
86
|
+
object.__setattr__(
|
87
|
+
self,
|
88
|
+
"label",
|
89
|
+
label,
|
90
|
+
)
|
91
|
+
self.unique_name: str
|
92
|
+
object.__setattr__(
|
93
|
+
self,
|
94
|
+
"unique_name",
|
95
|
+
f"[{trace_id}] [{label}] [{scope_id}]",
|
96
|
+
)
|
97
|
+
self._token: Token[ScopeIdentifier] | None
|
98
|
+
object.__setattr__(
|
99
|
+
self,
|
100
|
+
"_token",
|
101
|
+
None,
|
102
|
+
)
|
103
|
+
|
104
|
+
def __setattr__(
|
105
|
+
self,
|
106
|
+
name: str,
|
107
|
+
value: Any,
|
108
|
+
) -> Any:
|
109
|
+
raise AttributeError(
|
110
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
111
|
+
f" attribute - '{name}' cannot be modified"
|
112
|
+
)
|
113
|
+
|
114
|
+
def __delattr__(
|
115
|
+
self,
|
116
|
+
name: str,
|
117
|
+
) -> None:
|
118
|
+
raise AttributeError(
|
119
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
120
|
+
f" attribute - '{name}' cannot be deleted"
|
121
|
+
)
|
63
122
|
|
64
123
|
@property
|
65
124
|
def is_root(self) -> bool:
|
@@ -78,8 +137,12 @@ class ScopeIdentifier:
|
|
78
137
|
return hash(self.scope_id)
|
79
138
|
|
80
139
|
def __enter__(self) -> None:
|
81
|
-
assert
|
82
|
-
|
140
|
+
assert self._token is None, "Context reentrance is not allowed" # nosec: B101
|
141
|
+
object.__setattr__(
|
142
|
+
self,
|
143
|
+
"_token",
|
144
|
+
ScopeIdentifier._context.set(self),
|
145
|
+
)
|
83
146
|
|
84
147
|
def __exit__(
|
85
148
|
self,
|
@@ -87,6 +150,10 @@ class ScopeIdentifier:
|
|
87
150
|
exc_val: BaseException | None,
|
88
151
|
exc_tb: TracebackType | None,
|
89
152
|
) -> None:
|
90
|
-
assert
|
153
|
+
assert self._token is not None, "Unbalanced context enter/exit" # nosec: B101
|
91
154
|
ScopeIdentifier._context.reset(self._token)
|
92
|
-
|
155
|
+
object.__setattr__(
|
156
|
+
self,
|
157
|
+
"_token",
|
158
|
+
None,
|
159
|
+
)
|
@@ -133,13 +133,61 @@ class LoggerContext:
|
|
133
133
|
exc_info=exception,
|
134
134
|
)
|
135
135
|
|
136
|
+
__slots__ = (
|
137
|
+
"_entered",
|
138
|
+
"_logger",
|
139
|
+
"_prefix",
|
140
|
+
"_token",
|
141
|
+
)
|
142
|
+
|
136
143
|
def __init__(
|
137
144
|
self,
|
138
145
|
scope: ScopeIdentifier,
|
139
146
|
logger: Logger | None,
|
140
147
|
) -> None:
|
141
|
-
self._prefix: str
|
142
|
-
|
148
|
+
self._prefix: str
|
149
|
+
object.__setattr__(
|
150
|
+
self,
|
151
|
+
"_prefix",
|
152
|
+
scope.unique_name,
|
153
|
+
)
|
154
|
+
self._logger: Logger
|
155
|
+
object.__setattr__(
|
156
|
+
self,
|
157
|
+
"_logger",
|
158
|
+
logger or getLogger(name=scope.label),
|
159
|
+
)
|
160
|
+
self._token: Token[LoggerContext] | None
|
161
|
+
object.__setattr__(
|
162
|
+
self,
|
163
|
+
"_token",
|
164
|
+
None,
|
165
|
+
)
|
166
|
+
self._entered: float | None
|
167
|
+
object.__setattr__(
|
168
|
+
self,
|
169
|
+
"_entered",
|
170
|
+
None,
|
171
|
+
)
|
172
|
+
|
173
|
+
def __setattr__(
|
174
|
+
self,
|
175
|
+
name: str,
|
176
|
+
value: Any,
|
177
|
+
) -> Any:
|
178
|
+
raise AttributeError(
|
179
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
180
|
+
f" attribute - '{name}' cannot be modified"
|
181
|
+
)
|
182
|
+
|
183
|
+
def __delattr__(
|
184
|
+
self,
|
185
|
+
name: str,
|
186
|
+
) -> None:
|
187
|
+
raise AttributeError(
|
188
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
189
|
+
f" attribute - '{name}' cannot be deleted"
|
190
|
+
)
|
143
191
|
|
144
192
|
def log(
|
145
193
|
self,
|
@@ -157,10 +205,18 @@ class LoggerContext:
|
|
157
205
|
)
|
158
206
|
|
159
207
|
def __enter__(self) -> None:
|
160
|
-
assert
|
161
|
-
assert
|
162
|
-
|
163
|
-
|
208
|
+
assert self._token is None, "Context reentrance is not allowed" # nosec: B101
|
209
|
+
assert self._entered is None, "Context reentrance is not allowed" # nosec: B101
|
210
|
+
object.__setattr__(
|
211
|
+
self,
|
212
|
+
"_token",
|
213
|
+
LoggerContext._context.set(self),
|
214
|
+
)
|
215
|
+
object.__setattr__(
|
216
|
+
self,
|
217
|
+
"_entered",
|
218
|
+
monotonic(),
|
219
|
+
)
|
164
220
|
self.log(DEBUG, "Entering context...")
|
165
221
|
|
166
222
|
def __exit__(
|
@@ -169,8 +225,18 @@ class LoggerContext:
|
|
169
225
|
exc_val: BaseException | None,
|
170
226
|
exc_tb: TracebackType | None,
|
171
227
|
) -> None:
|
172
|
-
assert
|
228
|
+
assert ( # nosec: B101
|
229
|
+
self._token is not None and self._entered is not None
|
230
|
+
), "Unbalanced context enter/exit"
|
173
231
|
LoggerContext._context.reset(self._token)
|
174
|
-
|
232
|
+
object.__setattr__(
|
233
|
+
self,
|
234
|
+
"_token",
|
235
|
+
None,
|
236
|
+
)
|
175
237
|
self.log(DEBUG, f"...exiting context after {monotonic() - self._entered:.2f}s")
|
176
|
-
|
238
|
+
object.__setattr__(
|
239
|
+
self,
|
240
|
+
"_entered",
|
241
|
+
None,
|
242
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from contextvars import ContextVar, Token
|
2
2
|
from types import TracebackType
|
3
|
-
from typing import Protocol, Self, final, runtime_checkable
|
3
|
+
from typing import Any, Protocol, Self, final, runtime_checkable
|
4
4
|
|
5
5
|
from haiway.context.identifier import ScopeIdentifier
|
6
6
|
from haiway.context.logging import LoggerContext
|
@@ -138,17 +138,62 @@ class MetricsContext:
|
|
138
138
|
exception=exc,
|
139
139
|
)
|
140
140
|
|
141
|
+
__slots__ = (
|
142
|
+
"_metrics",
|
143
|
+
"_scope",
|
144
|
+
"_token",
|
145
|
+
)
|
146
|
+
|
141
147
|
def __init__(
|
142
148
|
self,
|
143
149
|
scope: ScopeIdentifier,
|
144
150
|
metrics: MetricsHandler | None,
|
145
151
|
) -> None:
|
146
|
-
self._scope: ScopeIdentifier
|
147
|
-
|
152
|
+
self._scope: ScopeIdentifier
|
153
|
+
object.__setattr__(
|
154
|
+
self,
|
155
|
+
"_scope",
|
156
|
+
scope,
|
157
|
+
)
|
158
|
+
self._metrics: MetricsHandler | None
|
159
|
+
object.__setattr__(
|
160
|
+
self,
|
161
|
+
"_metrics",
|
162
|
+
metrics,
|
163
|
+
)
|
164
|
+
self._token: Token[MetricsContext] | None
|
165
|
+
object.__setattr__(
|
166
|
+
self,
|
167
|
+
"_token",
|
168
|
+
None,
|
169
|
+
)
|
170
|
+
|
171
|
+
def __setattr__(
|
172
|
+
self,
|
173
|
+
name: str,
|
174
|
+
value: Any,
|
175
|
+
) -> Any:
|
176
|
+
raise AttributeError(
|
177
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
178
|
+
f" attribute - '{name}' cannot be modified"
|
179
|
+
)
|
180
|
+
|
181
|
+
def __delattr__(
|
182
|
+
self,
|
183
|
+
name: str,
|
184
|
+
) -> None:
|
185
|
+
raise AttributeError(
|
186
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
187
|
+
f" attribute - '{name}' cannot be deleted"
|
188
|
+
)
|
148
189
|
|
149
190
|
def __enter__(self) -> None:
|
150
|
-
assert
|
151
|
-
|
191
|
+
assert self._token is None, "Context reentrance is not allowed" # nosec: B101
|
192
|
+
object.__setattr__(
|
193
|
+
self,
|
194
|
+
"_token",
|
195
|
+
MetricsContext._context.set(self),
|
196
|
+
)
|
152
197
|
if self._metrics is not None:
|
153
198
|
self._metrics.enter_scope(self._scope)
|
154
199
|
|
@@ -158,8 +203,12 @@ class MetricsContext:
|
|
158
203
|
exc_val: BaseException | None,
|
159
204
|
exc_tb: TracebackType | None,
|
160
205
|
) -> None:
|
161
|
-
assert
|
206
|
+
assert self._token is not None, "Unbalanced context enter/exit" # nosec: B101
|
162
207
|
MetricsContext._context.reset(self._token)
|
163
|
-
|
208
|
+
object.__setattr__(
|
209
|
+
self,
|
210
|
+
"_token",
|
211
|
+
None,
|
212
|
+
)
|
164
213
|
if self._metrics is not None:
|
165
214
|
self._metrics.exit_scope(self._scope)
|