haiway 0.24.3__tar.gz → 0.25.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.
- {haiway-0.24.3 → haiway-0.25.0}/Makefile +1 -1
- {haiway-0.24.3 → haiway-0.25.0}/PKG-INFO +1 -1
- haiway-0.25.0/junit/test-results.xml +1 -0
- {haiway-0.24.3 → haiway-0.25.0}/pyproject.toml +1 -1
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/__init__.py +4 -1
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/__init__.py +4 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/access.py +214 -63
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/disposables.py +5 -119
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/identifier.py +19 -44
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/observability.py +22 -142
- haiway-0.25.0/src/haiway/context/presets.py +337 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/state.py +38 -84
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/tasks.py +7 -10
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/observability.py +4 -6
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/opentelemetry/observability.py +5 -5
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/state/__init__.py +2 -0
- haiway-0.25.0/src/haiway/state/immutable.py +127 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/state/structure.py +63 -117
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/state/validation.py +95 -60
- haiway-0.25.0/tests/test_context_presets.py +464 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_disposables.py +0 -35
- haiway-0.25.0/tests/test_state_typing.py +66 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_state_validation.py +5 -47
- {haiway-0.24.3 → haiway-0.25.0}/uv.lock +10 -10
- haiway-0.24.3/junit/test-results.xml +0 -1
- {haiway-0.24.3 → haiway-0.25.0}/.claude/settings.json +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/.github/workflows/ci.yml +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/.github/workflows/publish.yml +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/.gitignore +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/CLAUDE.md +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/LICENSE +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/README.md +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/config/pre-push +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/guidelines/functionalities.md +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/guidelines/llms.txt +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/guidelines/packages.md +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/guidelines/state.md +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/context/types.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/__init__.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/asynchrony.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/caching.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/concurrent.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/files.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/retries.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/throttling.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/timeouting.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/helpers/tracing.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/opentelemetry/__init__.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/py.typed +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/state/attributes.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/state/path.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/state/requirement.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/types/__init__.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/types/default.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/types/missing.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/__init__.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/always.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/collections.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/env.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/formatting.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/logs.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/mimic.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/noop.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/queue.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/src/haiway/utils/stream.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/__init__.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_async_queue.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_async_stream.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_attribute_path.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_attribute_requirement.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_auto_retry.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_cache.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_context.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_execute_concurrently.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_process_concurrently.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_state.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_stream_concurrently.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/tests/test_streaming.py +0 -0
- {haiway-0.24.3 → haiway-0.25.0}/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.25.0
|
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
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="199" time="3.844" timestamp="2025-07-14T10:25:32.182364+00:00" hostname="pkrvmq0rgcvqdmg"><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_async_stream" name="test_fails_when_stream_fails" time="0.001" /><testcase classname="tests.test_async_stream" name="test_cancels_when_iteration_cancels" time="0.001" /><testcase classname="tests.test_async_stream" name="test_ends_when_stream_ends" time="0.001" /><testcase classname="tests.test_async_stream" name="test_finishes_without_buffer" time="0.001" /><testcase classname="tests.test_async_stream" name="test_fails_without_buffer" time="0.001" /><testcase classname="tests.test_async_stream" name="test_delivers_updates_when_sending" time="0.001" /><testcase classname="tests.test_async_stream" name="test_ignores_when_sending_to_finished" time="0.001" /><testcase classname="tests.test_async_stream" name="test_ignores_when_sending_to_failed" time="0.001" /><testcase classname="tests.test_async_stream" name="test_ignores_when_finishing_when_finished" time="0.001" /><testcase classname="tests.test_async_stream" name="test_delivers_all_when_sending_async" 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.001" /><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.000" /><testcase classname="tests.test_attribute_path" name="test_dict_item_path_set_updates_item" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_equal_requirement" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_not_equal_requirement" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_contains_requirement" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_contains_any_requirement" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_contained_in_requirement" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_logical_and_or_requirements" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_filter" time="0.000" /><testcase classname="tests.test_attribute_requirement" name="test_immutability" time="0.001" /><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.102" /><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.001" /><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.020" /><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.501" /><testcase classname="tests.test_cache" name="test_async_expiration_does_not_cancel_task" time="0.021" /><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_context_presets" name="test_preset_creation" time="0.000" /><testcase classname="tests.test_context_presets" name="test_preset_with_state" time="0.000" /><testcase classname="tests.test_context_presets" name="test_preset_with_disposable" time="0.000" /><testcase classname="tests.test_context_presets" name="test_preset_extended" time="0.000" /><testcase classname="tests.test_context_presets" name="test_preset_prepare_with_static_state" time="0.001" /><testcase classname="tests.test_context_presets" name="test_preset_prepare_with_state_factory" time="0.001" /><testcase classname="tests.test_context_presets" name="test_preset_prepare_with_multiple_states_factory" time="0.001" /><testcase classname="tests.test_context_presets" name="test_preset_prepare_with_disposables" time="0.001" /><testcase classname="tests.test_context_presets" name="test_preset_prepare_with_multiple_disposables" time="0.001" /><testcase classname="tests.test_context_presets" name="test_preset_prepare_mixed_state_and_disposables" time="0.001" /><testcase classname="tests.test_context_presets" name="test_registry_creation" time="0.000" /><testcase classname="tests.test_context_presets" name="test_registry_immutable" time="0.000" /><testcase classname="tests.test_context_presets" name="test_scope_with_preset" time="0.001" /><testcase classname="tests.test_context_presets" name="test_scope_preset_with_override" time="0.001" /><testcase classname="tests.test_context_presets" name="test_scope_preset_not_found" time="0.001" /><testcase classname="tests.test_context_presets" name="test_sync_scope_with_preset_fails" time="0.000" /><testcase classname="tests.test_context_presets" name="test_preset_with_async_state_factory" time="0.021" /><testcase classname="tests.test_context_presets" name="test_preset_with_disposable_lifecycle" time="0.001" /><testcase classname="tests.test_context_presets" name="test_nested_preset_registries" time="0.001" /><testcase classname="tests.test_context_presets" name="test_preset_with_mixed_state_sources" time="0.011" /><testcase classname="tests.test_disposables" name="test_empty_initialization" time="0.000" /><testcase classname="tests.test_disposables" name="test_single_disposable_initialization" time="0.000" /><testcase classname="tests.test_disposables" name="test_multiple_disposables_initialization" time="0.000" /><testcase classname="tests.test_disposables" name="test_cannot_set_attributes" time="0.000" /><testcase classname="tests.test_disposables" name="test_cannot_delete_attributes" time="0.000" /><testcase classname="tests.test_disposables" name="test_empty_disposables_is_falsy" time="0.000" /><testcase classname="tests.test_disposables" name="test_non_empty_disposables_is_truthy" time="0.000" /><testcase classname="tests.test_disposables" name="test_setup_with_no_disposables" time="0.001" /><testcase classname="tests.test_disposables" name="test_setup_with_disposable_returning_none" time="0.001" /><testcase classname="tests.test_disposables" name="test_setup_with_disposable_returning_single_state" time="0.001" /><testcase classname="tests.test_disposables" name="test_setup_with_disposable_returning_multiple_states" time="0.001" /><testcase classname="tests.test_disposables" name="test_setup_with_multiple_disposables_mixed_returns" time="0.001" /><testcase classname="tests.test_disposables" name="test_setup_sets_loop_correctly" time="0.001" /><testcase classname="tests.test_disposables" name="test_dispose_with_no_disposables" time="0.001" /><testcase classname="tests.test_disposables" name="test_dispose_with_successful_cleanup" time="0.001" /><testcase classname="tests.test_disposables" name="test_dispose_with_exception_context" time="0.001" /><testcase classname="tests.test_disposables" name="test_dispose_with_multiple_exceptions_creates_group" time="0.001" /><testcase classname="tests.test_disposables" name="test_dispose_with_single_exception_is_risen" time="0.001" /><testcase classname="tests.test_disposables" name="test_dispose_resets_loop_even_on_exception" time="0.001" /><testcase classname="tests.test_disposables" name="test_same_loop_cleanup" time="0.001" /><testcase classname="tests.test_disposables" name="test_exception_during_setup_phase" time="0.001" /><testcase classname="tests.test_disposables" name="test_assertion_on_doubleprepare" time="0.001" /><testcase classname="tests.test_disposables" name="test_assertion_on_dispose_withoutprepare" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_processes_all_elements" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_preserves_order" time="0.302" /><testcase classname="tests.test_execute_concurrently" name="test_handles_empty_collection" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_propagates_handler_exceptions" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_returns_exceptions_when_configured" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_cancels_running_tasks_on_cancellation" time="0.101" /><testcase classname="tests.test_execute_concurrently" name="test_respects_concurrency_limit" time="0.202" /><testcase classname="tests.test_execute_concurrently" name="test_works_with_different_types" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_handles_mixed_success_and_failure" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_works_with_sets_and_tuples" time="0.001" /><testcase classname="tests.test_execute_concurrently" name="test_exception_details_preserved" time="0.001" /><testcase classname="tests.test_process_concurrently" name="test_processes_all_elements" time="0.001" /><testcase classname="tests.test_process_concurrently" name="test_processes_elements_concurrently" time="0.302" /><testcase classname="tests.test_process_concurrently" name="test_handles_empty_source" time="0.001" /><testcase classname="tests.test_process_concurrently" name="test_propagates_handler_exceptions" time="0.001" /><testcase classname="tests.test_process_concurrently" name="test_ignores_handler_exceptions_when_configured" time="0.001" /><testcase classname="tests.test_process_concurrently" name="test_handles_source_exception" time="0.001" /><testcase classname="tests.test_process_concurrently" name="test_cancels_running_tasks_on_cancellation" time="0.101" /><testcase classname="tests.test_process_concurrently" name="test_respects_concurrency_limit" time="0.202" /><testcase classname="tests.test_process_concurrently" name="test_processes_elements_from_queue" time="0.052" /><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.000" /><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.000" /><testcase classname="tests.test_state" name="test_initialization_allows_missing_properties" time="0.000" /><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.000" /><testcase classname="tests.test_state" name="test_hash_consistency_with_missing_values" time="0.000" /><testcase classname="tests.test_state" name="test_hash_with_unhashable_attributes" time="0.001" /><testcase classname="tests.test_state" name="test_hash_with_dict_key_order_independence" time="0.000" /><testcase classname="tests.test_state" name="test_hash_with_custom_objects" time="0.000" /><testcase classname="tests.test_state" name="test_hash_performance_with_many_attributes" time="0.000" /><testcase classname="tests.test_state" name="test_hash_with_nested_unhashable_collections" time="0.000" /><testcase classname="tests.test_state" name="test_hash_stability_across_instances" time="0.000" /><testcase classname="tests.test_state" name="test_hash_excludes_missing_values" time="0.000" /><testcase classname="tests.test_state_typing" name="test_state_typing_subclass_and_instance_checks" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validator_basic_types" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validator_none_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_missing_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_literal_type" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validator_enum_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_sequence_type" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validator_set_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_mapping_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_tuple_type" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validator_union_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_callable_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_typed_dict" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_state_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_complex_types" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validator_recursive_state" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_generic_state" time="0.001" /><testcase classname="tests.test_state_validation" name="test_validation_error_messages" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validation_any_type" time="0.000" /><testcase classname="tests.test_state_validation" name="test_validator_with_defaults" time="0.000" /><testcase classname="tests.test_state_validation" name="test_attribute_validator_direct_usage" time="0.000" /><testcase classname="tests.test_state_validation" name="test_unsupported_type_annotation" time="0.000" /><testcase classname="tests.test_stream_concurrently" name="test_merges_two_streams" time="0.001" /><testcase classname="tests.test_stream_concurrently" name="test_interleaves_based_on_timing" time="0.152" /><testcase classname="tests.test_stream_concurrently" name="test_handles_empty_iterators" time="0.001" /><testcase classname="tests.test_stream_concurrently" name="test_handles_different_lengths" time="0.001" /><testcase classname="tests.test_stream_concurrently" name="test_propagates_exceptions_from_source_a" time="0.001" /><testcase classname="tests.test_stream_concurrently" name="test_propagates_exceptions_from_source_b" time="0.001" /><testcase classname="tests.test_stream_concurrently" name="test_cancellation_cancels_both_sources" time="0.201" /><testcase classname="tests.test_stream_concurrently" name="test_works_with_different_types" time="0.001" /><testcase classname="tests.test_stream_concurrently" name="test_immediate_yield" time="0.601" /><testcase classname="tests.test_stream_concurrently" name="test_concurrent_execution" time="0.201" /><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.25.0"
|
9
9
|
readme = "README.md"
|
10
10
|
maintainers = [
|
11
11
|
{ name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from haiway.context import (
|
2
|
+
ContextPresets,
|
2
3
|
Disposable,
|
3
4
|
Disposables,
|
4
5
|
MissingContext,
|
@@ -26,7 +27,7 @@ from haiway.helpers import (
|
|
26
27
|
timeout,
|
27
28
|
traced,
|
28
29
|
)
|
29
|
-
from haiway.state import AttributePath, AttributeRequirement, State
|
30
|
+
from haiway.state import AttributePath, AttributeRequirement, Immutable, State
|
30
31
|
from haiway.types import (
|
31
32
|
MISSING,
|
32
33
|
Default,
|
@@ -64,12 +65,14 @@ __all__ = (
|
|
64
65
|
"AsyncStream",
|
65
66
|
"AttributePath",
|
66
67
|
"AttributeRequirement",
|
68
|
+
"ContextPresets",
|
67
69
|
"Default",
|
68
70
|
"DefaultValue",
|
69
71
|
"Disposable",
|
70
72
|
"Disposables",
|
71
73
|
"File",
|
72
74
|
"FileAccess",
|
75
|
+
"Immutable",
|
73
76
|
"LoggerObservability",
|
74
77
|
"Missing",
|
75
78
|
"MissingContext",
|
@@ -14,12 +14,16 @@ from haiway.context.observability import (
|
|
14
14
|
ObservabilityScopeExiting,
|
15
15
|
ObservabilityTraceIdentifying,
|
16
16
|
)
|
17
|
+
from haiway.context.presets import ContextPresets
|
17
18
|
from haiway.context.state import StateContext
|
18
19
|
from haiway.context.types import MissingContext, MissingState
|
20
|
+
from haiway.state import Immutable
|
19
21
|
|
20
22
|
__all__ = (
|
23
|
+
"ContextPresets",
|
21
24
|
"Disposable",
|
22
25
|
"Disposables",
|
26
|
+
"Immutable",
|
23
27
|
"MissingContext",
|
24
28
|
"MissingState",
|
25
29
|
"Observability",
|
@@ -8,6 +8,7 @@ from collections.abc import (
|
|
8
8
|
AsyncGenerator,
|
9
9
|
AsyncIterator,
|
10
10
|
Callable,
|
11
|
+
Collection,
|
11
12
|
Coroutine,
|
12
13
|
Iterable,
|
13
14
|
Mapping,
|
@@ -24,16 +25,20 @@ from haiway.context.observability import (
|
|
24
25
|
ObservabilityContext,
|
25
26
|
ObservabilityLevel,
|
26
27
|
)
|
28
|
+
from haiway.context.presets import (
|
29
|
+
ContextPresets,
|
30
|
+
ContextPresetsRegistry,
|
31
|
+
ContextPresetsRegistryContext,
|
32
|
+
)
|
27
33
|
from haiway.context.state import ScopeState, StateContext
|
28
34
|
from haiway.context.tasks import TaskGroupContext
|
29
|
-
from haiway.state import State
|
35
|
+
from haiway.state import Immutable, State
|
30
36
|
from haiway.utils.stream import AsyncStream
|
31
37
|
|
32
38
|
__all__ = ("ctx",)
|
33
39
|
|
34
40
|
|
35
|
-
|
36
|
-
class ScopeContext:
|
41
|
+
class ScopeContext(Immutable):
|
37
42
|
"""
|
38
43
|
Context manager for executing code within a defined scope.
|
39
44
|
|
@@ -45,42 +50,62 @@ class ScopeContext:
|
|
45
50
|
to create scope contexts.
|
46
51
|
"""
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
_identifier: ScopeIdentifier
|
54
|
+
_state: Collection[State]
|
55
|
+
_captured_state: Collection[State]
|
56
|
+
_resolved_state_context: StateContext | None
|
57
|
+
_disposables: Disposables | None
|
58
|
+
_presets: ContextPresets | None
|
59
|
+
_presets_disposables: Disposables | None
|
60
|
+
_observability_context: ObservabilityContext
|
61
|
+
_task_group_context: TaskGroupContext | None
|
55
62
|
|
56
63
|
def __init__(
|
57
64
|
self,
|
58
|
-
|
65
|
+
name: str,
|
59
66
|
task_group: TaskGroup | None,
|
60
67
|
state: tuple[State, ...],
|
61
68
|
disposables: Disposables | None,
|
62
69
|
observability: Observability | Logger | None,
|
63
70
|
) -> None:
|
64
|
-
self._identifier: ScopeIdentifier
|
65
71
|
object.__setattr__(
|
66
72
|
self,
|
67
73
|
"_identifier",
|
68
|
-
ScopeIdentifier.scope(
|
74
|
+
ScopeIdentifier.scope(name),
|
69
75
|
)
|
70
|
-
#
|
71
|
-
self._state_context: StateContext
|
76
|
+
# store explicit state separately for priority control
|
72
77
|
object.__setattr__(
|
73
78
|
self,
|
74
|
-
"
|
75
|
-
|
79
|
+
"_state",
|
80
|
+
state,
|
81
|
+
)
|
82
|
+
# capture current contextual state (without new additions)
|
83
|
+
object.__setattr__(
|
84
|
+
self,
|
85
|
+
"_captured_state",
|
86
|
+
StateContext.current_state(),
|
87
|
+
)
|
88
|
+
# placeholder for temporary, resolved state context
|
89
|
+
object.__setattr__(
|
90
|
+
self,
|
91
|
+
"_resolved_state_context",
|
92
|
+
None,
|
76
93
|
)
|
77
|
-
self._disposables: Disposables | None
|
78
94
|
object.__setattr__(
|
79
95
|
self,
|
80
96
|
"_disposables",
|
81
97
|
disposables,
|
82
98
|
)
|
83
|
-
|
99
|
+
object.__setattr__(
|
100
|
+
self,
|
101
|
+
"_presets",
|
102
|
+
ContextPresetsRegistryContext.select(name),
|
103
|
+
)
|
104
|
+
object.__setattr__(
|
105
|
+
self,
|
106
|
+
"_presets_disposables",
|
107
|
+
None,
|
108
|
+
)
|
84
109
|
object.__setattr__(
|
85
110
|
self,
|
86
111
|
"_observability_context",
|
@@ -90,7 +115,6 @@ class ScopeContext:
|
|
90
115
|
observability=observability,
|
91
116
|
),
|
92
117
|
)
|
93
|
-
self._task_group_context: TaskGroupContext | None
|
94
118
|
object.__setattr__(
|
95
119
|
self,
|
96
120
|
"_task_group_context",
|
@@ -99,35 +123,27 @@ class ScopeContext:
|
|
99
123
|
else None,
|
100
124
|
)
|
101
125
|
|
102
|
-
def __setattr__(
|
103
|
-
self,
|
104
|
-
name: str,
|
105
|
-
value: Any,
|
106
|
-
) -> Any:
|
107
|
-
raise AttributeError(
|
108
|
-
f"Can't modify immutable {self.__class__.__qualname__},"
|
109
|
-
f" attribute - '{name}' cannot be modified"
|
110
|
-
)
|
111
|
-
|
112
|
-
def __delattr__(
|
113
|
-
self,
|
114
|
-
name: str,
|
115
|
-
) -> None:
|
116
|
-
raise AttributeError(
|
117
|
-
f"Can't modify immutable {self.__class__.__qualname__},"
|
118
|
-
f" attribute - '{name}' cannot be deleted"
|
119
|
-
)
|
120
|
-
|
121
126
|
def __enter__(self) -> str:
|
122
127
|
assert ( # nosec: B101
|
123
128
|
self._task_group_context is None or self._identifier.is_root
|
124
129
|
), "Can't enter synchronous context with task group"
|
125
130
|
assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
|
131
|
+
assert self._resolved_state_context is None # nosec: B101
|
132
|
+
assert self._presets is None, "Can't enter synchronous context with presets" # nosec: B101
|
126
133
|
self._identifier.__enter__()
|
127
134
|
self._observability_context.__enter__()
|
128
|
-
|
135
|
+
# For sync context, only use explicit state (no presets or disposables allowed)
|
136
|
+
resolved_state_context: StateContext = StateContext(
|
137
|
+
_state=ScopeState((*self._captured_state, *self._state))
|
138
|
+
)
|
139
|
+
object.__setattr__(
|
140
|
+
self,
|
141
|
+
"_resolved_state_context",
|
142
|
+
resolved_state_context,
|
143
|
+
)
|
144
|
+
resolved_state_context.__enter__()
|
129
145
|
|
130
|
-
return self._observability_context.observability.trace_identifying(self._identifier)
|
146
|
+
return str(self._observability_context.observability.trace_identifying(self._identifier))
|
131
147
|
|
132
148
|
def __exit__(
|
133
149
|
self,
|
@@ -135,11 +151,17 @@ class ScopeContext:
|
|
135
151
|
exc_val: BaseException | None,
|
136
152
|
exc_tb: TracebackType | None,
|
137
153
|
) -> None:
|
138
|
-
self.
|
154
|
+
assert self._resolved_state_context is not None # nosec: B101
|
155
|
+
self._resolved_state_context.__exit__(
|
139
156
|
exc_type=exc_type,
|
140
157
|
exc_val=exc_val,
|
141
158
|
exc_tb=exc_tb,
|
142
159
|
)
|
160
|
+
object.__setattr__(
|
161
|
+
self,
|
162
|
+
"_resolved_state_context",
|
163
|
+
None,
|
164
|
+
)
|
143
165
|
self._observability_context.__exit__(
|
144
166
|
exc_type=exc_type,
|
145
167
|
exc_val=exc_val,
|
@@ -152,31 +174,49 @@ class ScopeContext:
|
|
152
174
|
)
|
153
175
|
|
154
176
|
async def __aenter__(self) -> str:
|
177
|
+
assert self._presets_disposables is None # nosec: B101
|
178
|
+
assert self._resolved_state_context is None # nosec: B101
|
155
179
|
self._identifier.__enter__()
|
156
180
|
self._observability_context.__enter__()
|
157
181
|
|
158
182
|
if task_group := self._task_group_context:
|
159
183
|
await task_group.__aenter__()
|
160
184
|
|
161
|
-
#
|
162
|
-
|
163
|
-
|
185
|
+
# Collect all state sources in priority order (lowest to highest priority)
|
186
|
+
collected_state: list[State] = []
|
187
|
+
|
188
|
+
# 1. Add contextual state first (lowest priority)
|
189
|
+
collected_state.extend(self._captured_state)
|
190
|
+
|
191
|
+
# 2. Add preset state (low priority, overrides contextual)
|
192
|
+
if self._presets is not None:
|
193
|
+
presets_disposables: Disposables = await self._presets.prepare()
|
164
194
|
object.__setattr__(
|
165
195
|
self,
|
166
|
-
"
|
167
|
-
|
168
|
-
state=ScopeState(
|
169
|
-
(
|
170
|
-
*self._state_context._state._state.values(),
|
171
|
-
*await disposables.prepare(),
|
172
|
-
)
|
173
|
-
),
|
174
|
-
),
|
196
|
+
"_presets_disposables",
|
197
|
+
presets_disposables,
|
175
198
|
)
|
199
|
+
collected_state.extend(await presets_disposables.prepare())
|
200
|
+
|
201
|
+
# 3. Add explicit disposables state (medium priority)
|
202
|
+
if self._disposables is not None:
|
203
|
+
collected_state.extend(await self._disposables.prepare())
|
204
|
+
|
205
|
+
# 4. Add explicit state last (highest priority)
|
206
|
+
collected_state.extend(self._state)
|
207
|
+
# Create resolved state context with all collected state
|
208
|
+
resolved_state_context: StateContext = StateContext(
|
209
|
+
_state=ScopeState(tuple(collected_state))
|
210
|
+
)
|
176
211
|
|
177
|
-
|
212
|
+
resolved_state_context.__enter__()
|
213
|
+
object.__setattr__(
|
214
|
+
self,
|
215
|
+
"_resolved_state_context",
|
216
|
+
resolved_state_context,
|
217
|
+
)
|
178
218
|
|
179
|
-
return self._observability_context.observability.trace_identifying(self._identifier)
|
219
|
+
return str(self._observability_context.observability.trace_identifying(self._identifier))
|
180
220
|
|
181
221
|
async def __aexit__(
|
182
222
|
self,
|
@@ -184,12 +224,25 @@ class ScopeContext:
|
|
184
224
|
exc_val: BaseException | None,
|
185
225
|
exc_tb: TracebackType | None,
|
186
226
|
) -> None:
|
187
|
-
|
188
|
-
|
227
|
+
assert self._resolved_state_context is not None # nosec: B101
|
228
|
+
if self._disposables is not None:
|
229
|
+
await self._disposables.dispose(
|
230
|
+
exc_type=exc_type,
|
231
|
+
exc_val=exc_val,
|
232
|
+
exc_tb=exc_tb,
|
233
|
+
)
|
234
|
+
|
235
|
+
if self._presets_disposables is not None:
|
236
|
+
await self._presets_disposables.dispose(
|
189
237
|
exc_type=exc_type,
|
190
238
|
exc_val=exc_val,
|
191
239
|
exc_tb=exc_tb,
|
192
240
|
)
|
241
|
+
object.__setattr__(
|
242
|
+
self,
|
243
|
+
"_presets_disposables",
|
244
|
+
None,
|
245
|
+
)
|
193
246
|
|
194
247
|
if task_group := self._task_group_context:
|
195
248
|
await task_group.__aexit__(
|
@@ -198,11 +251,16 @@ class ScopeContext:
|
|
198
251
|
exc_tb=exc_tb,
|
199
252
|
)
|
200
253
|
|
201
|
-
self.
|
254
|
+
self._resolved_state_context.__exit__(
|
202
255
|
exc_type=exc_type,
|
203
256
|
exc_val=exc_val,
|
204
257
|
exc_tb=exc_tb,
|
205
258
|
)
|
259
|
+
object.__setattr__(
|
260
|
+
self,
|
261
|
+
"_resolved_state_context",
|
262
|
+
None,
|
263
|
+
)
|
206
264
|
|
207
265
|
self._observability_context.__exit__(
|
208
266
|
exc_type=exc_type,
|
@@ -258,9 +316,90 @@ class ctx:
|
|
258
316
|
"""
|
259
317
|
return ObservabilityContext.trace_id(scope_identifier)
|
260
318
|
|
319
|
+
@staticmethod
|
320
|
+
def presets(
|
321
|
+
*presets: ContextPresets,
|
322
|
+
) -> ContextPresetsRegistryContext:
|
323
|
+
"""
|
324
|
+
Create a context manager for a preset registry.
|
325
|
+
|
326
|
+
This method creates a registry of context presets that can be used within
|
327
|
+
nested scopes. Presets allow you to define reusable combinations of state
|
328
|
+
and disposables that can be referenced by name when creating scopes.
|
329
|
+
|
330
|
+
When entering this context manager, the provided presets become available
|
331
|
+
for use with ctx.scope(). The presets are looked up by their name when
|
332
|
+
creating scopes.
|
333
|
+
|
334
|
+
Parameters
|
335
|
+
----------
|
336
|
+
*presets: ContextPresets
|
337
|
+
Variable number of preset configurations to register. Each preset
|
338
|
+
must have a unique name within the registry.
|
339
|
+
|
340
|
+
Returns
|
341
|
+
-------
|
342
|
+
ContextPresetsRegistryContext
|
343
|
+
A context manager that makes the presets available in nested scopes
|
344
|
+
|
345
|
+
Examples
|
346
|
+
--------
|
347
|
+
Basic preset usage:
|
348
|
+
|
349
|
+
>>> from haiway import ctx, State
|
350
|
+
>>> from haiway.context import ContextPresets
|
351
|
+
>>>
|
352
|
+
>>> class ApiConfig(State):
|
353
|
+
... base_url: str
|
354
|
+
... timeout: int = 30
|
355
|
+
>>>
|
356
|
+
>>> # Define presets
|
357
|
+
>>> dev_preset = ContextPresets(
|
358
|
+
... name="development",
|
359
|
+
... _state=[ApiConfig(base_url="https://dev-api.example.com")]
|
360
|
+
... )
|
361
|
+
>>>
|
362
|
+
>>> prod_preset = ContextPresets(
|
363
|
+
... name="production",
|
364
|
+
... _state=[ApiConfig(base_url="https://api.example.com", timeout=60)]
|
365
|
+
... )
|
366
|
+
>>>
|
367
|
+
>>> # Use presets
|
368
|
+
>>> with ctx.presets(dev_preset, prod_preset):
|
369
|
+
... async with ctx.scope("development"):
|
370
|
+
... config = ctx.state(ApiConfig)
|
371
|
+
... assert config.base_url == "https://dev-api.example.com"
|
372
|
+
|
373
|
+
Nested preset registries:
|
374
|
+
|
375
|
+
>>> base_presets = [dev_preset, prod_preset]
|
376
|
+
>>> override_preset = ContextPresets(
|
377
|
+
... name="development",
|
378
|
+
... _state=[ApiConfig(base_url="https://staging.example.com")]
|
379
|
+
... )
|
380
|
+
>>>
|
381
|
+
>>> with ctx.presets(*base_presets):
|
382
|
+
... # Outer registry has dev and prod presets
|
383
|
+
... with ctx.presets(override_preset):
|
384
|
+
... # Inner registry overrides dev preset
|
385
|
+
... async with ctx.scope("development"):
|
386
|
+
... config = ctx.state(ApiConfig)
|
387
|
+
... assert config.base_url == "https://staging.example.com"
|
388
|
+
|
389
|
+
See Also
|
390
|
+
--------
|
391
|
+
ContextPresets : For creating individual preset configurations
|
392
|
+
ctx.scope : For creating scopes that can use presets
|
393
|
+
"""
|
394
|
+
return ContextPresetsRegistryContext(
|
395
|
+
registry=ContextPresetsRegistry(
|
396
|
+
presets=presets,
|
397
|
+
),
|
398
|
+
)
|
399
|
+
|
261
400
|
@staticmethod
|
262
401
|
def scope(
|
263
|
-
|
402
|
+
name: str,
|
264
403
|
/,
|
265
404
|
*state: State | None,
|
266
405
|
disposables: Disposables | Iterable[Disposable] | None = None,
|
@@ -271,10 +410,22 @@ class ctx:
|
|
271
410
|
Prepare scope context with given parameters. When called within an existing context\
|
272
411
|
it becomes nested with current context as its parent.
|
273
412
|
|
413
|
+
State Priority System
|
414
|
+
---------------------
|
415
|
+
State resolution follows a 4-layer priority system (highest to lowest):
|
416
|
+
|
417
|
+
1. **Explicit state** (passed to ctx.scope()) - HIGHEST priority
|
418
|
+
2. **Explicit disposables** (passed to ctx.scope()) - medium priority
|
419
|
+
3. **Preset state** (from presets) - low priority
|
420
|
+
4. **Contextual state** (from parent contexts) - LOWEST priority
|
421
|
+
|
422
|
+
When state types conflict, higher priority sources override lower priority ones.
|
423
|
+
State objects are resolved by type, with the highest priority instance winning.
|
424
|
+
|
274
425
|
Parameters
|
275
426
|
----------
|
276
|
-
|
277
|
-
name of the scope context
|
427
|
+
name: str
|
428
|
+
name of the scope context, can be associated with state presets
|
278
429
|
|
279
430
|
*state: State | None
|
280
431
|
state propagated within the scope context, will be merged with current state by\
|
@@ -313,7 +464,7 @@ class ctx:
|
|
313
464
|
resolved_disposables = Disposables(*iterable)
|
314
465
|
|
315
466
|
return ScopeContext(
|
316
|
-
|
467
|
+
name=name,
|
317
468
|
task_group=task_group,
|
318
469
|
state=tuple(element for element in state if element is not None),
|
319
470
|
disposables=resolved_disposables,
|