haiway 0.22.0__tar.gz → 0.23.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.
Files changed (114) hide show
  1. {haiway-0.22.0 → haiway-0.23.0}/PKG-INFO +1 -1
  2. {haiway-0.22.0 → haiway-0.23.0}/junit/test-results.xml +1 -1
  3. {haiway-0.22.0 → haiway-0.23.0}/pyproject.toml +1 -1
  4. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/access.py +9 -1
  5. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/disposables.py +32 -5
  6. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/observability.py +2 -2
  7. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/state.py +18 -2
  8. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/__init__.py +1 -1
  9. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/observability.py +2 -3
  10. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/opentelemetry/observability.py +50 -14
  11. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/state/structure.py +16 -6
  12. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/__init__.py +0 -2
  13. {haiway-0.22.0 → haiway-0.23.0}/tests/test_disposables.py +2 -2
  14. {haiway-0.22.0 → haiway-0.23.0}/tests/test_state.py +178 -1
  15. {haiway-0.22.0 → haiway-0.23.0}/uv.lock +1 -1
  16. haiway-0.22.0/examples/fastAPI/.dockerignore +0 -5
  17. haiway-0.22.0/examples/fastAPI/Dockerfile +0 -63
  18. haiway-0.22.0/examples/fastAPI/Makefile +0 -90
  19. haiway-0.22.0/examples/fastAPI/README.md +0 -3
  20. haiway-0.22.0/examples/fastAPI/config/.env.example +0 -11
  21. haiway-0.22.0/examples/fastAPI/config/unit.json +0 -50
  22. haiway-0.22.0/examples/fastAPI/docker-compose.yml +0 -68
  23. haiway-0.22.0/examples/fastAPI/pyproject.toml +0 -62
  24. haiway-0.22.0/examples/fastAPI/src/features/__int__.py +0 -0
  25. haiway-0.22.0/examples/fastAPI/src/features/todos/__init__.py +0 -5
  26. haiway-0.22.0/examples/fastAPI/src/features/todos/config.py +0 -1
  27. haiway-0.22.0/examples/fastAPI/src/features/todos/state.py +0 -23
  28. haiway-0.22.0/examples/fastAPI/src/features/todos/types.py +0 -15
  29. haiway-0.22.0/examples/fastAPI/src/features/todos/user_tasks.py +0 -19
  30. haiway-0.22.0/examples/fastAPI/src/integrations/postgres/__init__.py +0 -12
  31. haiway-0.22.0/examples/fastAPI/src/integrations/postgres/client.py +0 -171
  32. haiway-0.22.0/examples/fastAPI/src/integrations/postgres/config.py +0 -19
  33. haiway-0.22.0/examples/fastAPI/src/integrations/postgres/state.py +0 -77
  34. haiway-0.22.0/examples/fastAPI/src/integrations/postgres/types.py +0 -69
  35. haiway-0.22.0/examples/fastAPI/src/migrations/__init__.py +0 -3
  36. haiway-0.22.0/examples/fastAPI/src/migrations/__main__.py +0 -26
  37. haiway-0.22.0/examples/fastAPI/src/migrations/postgres/__init__.py +0 -5
  38. haiway-0.22.0/examples/fastAPI/src/migrations/postgres/execution.py +0 -80
  39. haiway-0.22.0/examples/fastAPI/src/migrations/postgres/migration_0.py +0 -17
  40. haiway-0.22.0/examples/fastAPI/src/migrations/postgres/types.py +0 -9
  41. haiway-0.22.0/examples/fastAPI/src/server/__init__.py +0 -10
  42. haiway-0.22.0/examples/fastAPI/src/server/__main__.py +0 -10
  43. haiway-0.22.0/examples/fastAPI/src/server/application.py +0 -54
  44. haiway-0.22.0/examples/fastAPI/src/server/config.py +0 -11
  45. haiway-0.22.0/examples/fastAPI/src/server/middlewares/__init__.py +0 -5
  46. haiway-0.22.0/examples/fastAPI/src/server/middlewares/context.py +0 -133
  47. haiway-0.22.0/examples/fastAPI/src/server/routes/__init__.py +0 -7
  48. haiway-0.22.0/examples/fastAPI/src/server/routes/technical.py +0 -21
  49. haiway-0.22.0/examples/fastAPI/src/server/routes/todos.py +0 -32
  50. haiway-0.22.0/examples/fastAPI/src/solutions/__init__.py +0 -0
  51. haiway-0.22.0/examples/fastAPI/src/solutions/user_tasks/__init__.py +0 -7
  52. haiway-0.22.0/examples/fastAPI/src/solutions/user_tasks/config.py +0 -1
  53. haiway-0.22.0/examples/fastAPI/src/solutions/user_tasks/postgres.py +0 -218
  54. haiway-0.22.0/examples/fastAPI/src/solutions/user_tasks/state.py +0 -77
  55. haiway-0.22.0/examples/fastAPI/src/solutions/user_tasks/types.py +0 -71
  56. haiway-0.22.0/examples/fastAPI/uv.lock +0 -448
  57. haiway-0.22.0/src/haiway/utils/freezing.py +0 -46
  58. haiway-0.22.0/tests/__init__.py +0 -0
  59. {haiway-0.22.0 → haiway-0.23.0}/.github/workflows/ci.yml +0 -0
  60. {haiway-0.22.0 → haiway-0.23.0}/.github/workflows/publish.yml +0 -0
  61. {haiway-0.22.0 → haiway-0.23.0}/.gitignore +0 -0
  62. {haiway-0.22.0 → haiway-0.23.0}/CLAUDE.md +0 -0
  63. {haiway-0.22.0 → haiway-0.23.0}/LICENSE +0 -0
  64. {haiway-0.22.0 → haiway-0.23.0}/Makefile +0 -0
  65. {haiway-0.22.0 → haiway-0.23.0}/README.md +0 -0
  66. {haiway-0.22.0 → haiway-0.23.0}/config/pre-push +0 -0
  67. {haiway-0.22.0 → haiway-0.23.0}/guidelines/functionalities.md +0 -0
  68. {haiway-0.22.0 → haiway-0.23.0}/guidelines/llms.txt +0 -0
  69. {haiway-0.22.0 → haiway-0.23.0}/guidelines/packages.md +0 -0
  70. {haiway-0.22.0 → haiway-0.23.0}/guidelines/state.md +0 -0
  71. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/__init__.py +0 -0
  72. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/__init__.py +0 -0
  73. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/identifier.py +0 -0
  74. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/tasks.py +0 -0
  75. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/context/types.py +0 -0
  76. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/asynchrony.py +0 -0
  77. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/caching.py +0 -0
  78. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/concurrent.py +0 -0
  79. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/files.py +0 -0
  80. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/retries.py +0 -0
  81. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/throttling.py +0 -0
  82. /haiway-0.22.0/src/haiway/helpers/timeouted.py → /haiway-0.23.0/src/haiway/helpers/timeouting.py +0 -0
  83. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/helpers/tracing.py +0 -0
  84. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/opentelemetry/__init__.py +0 -0
  85. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/py.typed +0 -0
  86. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/state/__init__.py +0 -0
  87. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/state/attributes.py +0 -0
  88. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/state/path.py +0 -0
  89. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/state/requirement.py +0 -0
  90. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/state/validation.py +0 -0
  91. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/types/__init__.py +0 -0
  92. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/types/default.py +0 -0
  93. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/types/missing.py +0 -0
  94. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/always.py +0 -0
  95. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/collections.py +0 -0
  96. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/env.py +0 -0
  97. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/formatting.py +0 -0
  98. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/logs.py +0 -0
  99. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/mimic.py +0 -0
  100. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/noop.py +0 -0
  101. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/queue.py +0 -0
  102. {haiway-0.22.0 → haiway-0.23.0}/src/haiway/utils/stream.py +0 -0
  103. {haiway-0.22.0/examples/fastAPI/src/integrations → haiway-0.23.0/tests}/__init__.py +0 -0
  104. {haiway-0.22.0 → haiway-0.23.0}/tests/test_async_queue.py +0 -0
  105. {haiway-0.22.0 → haiway-0.23.0}/tests/test_async_stream.py +0 -0
  106. {haiway-0.22.0 → haiway-0.23.0}/tests/test_attribute_path.py +0 -0
  107. {haiway-0.22.0 → haiway-0.23.0}/tests/test_attribute_requirement.py +0 -0
  108. {haiway-0.22.0 → haiway-0.23.0}/tests/test_auto_retry.py +0 -0
  109. {haiway-0.22.0 → haiway-0.23.0}/tests/test_cache.py +0 -0
  110. {haiway-0.22.0 → haiway-0.23.0}/tests/test_context.py +0 -0
  111. {haiway-0.22.0 → haiway-0.23.0}/tests/test_process_concurrently.py +0 -0
  112. {haiway-0.22.0 → haiway-0.23.0}/tests/test_state_validation.py +0 -0
  113. {haiway-0.22.0 → haiway-0.23.0}/tests/test_streaming.py +0 -0
  114. {haiway-0.22.0 → haiway-0.23.0}/tests/test_timeout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiway
3
- Version: 0.22.0
3
+ Version: 0.23.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
@@ -1 +1 @@
1
- <?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="151" time="1.947" timestamp="2025-06-18T09:04:02.496497+00:00" hostname="pkrvmxyh4eaekms"><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.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.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.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.002" /><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.107" /><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_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_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.001" /><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_with_real_async_context_managers" time="0.001" /><testcase classname="tests.test_disposables" name="test_nested_disposables_usage" 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_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.001" /><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.000" /><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.001" /><testcase classname="tests.test_state" name="test_generic_subtypes_validation" time="0.001" /><testcase classname="tests.test_state" name="test_copying_leaves_same_object" time="0.000" /><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.001" /><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.000" /><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.000" /><testcase classname="tests.test_state_validation" name="test_validator_union_type" time="0.001" /><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.001" /><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_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.012" /><testcase classname="tests.test_timeout" name="test_raises_with_timeout" time="0.011" /></testsuite></testsuites>
1
+ <?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="159" time="1.956" timestamp="2025-06-25T10:09:57.916234+00:00" hostname="pkrvmxyh4eaekms"><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.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.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.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.102" /><testcase classname="tests.test_auto_retry" name="test_async_uses_computed_delay_with_errors" time="0.107" /><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.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_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_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.001" /><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_with_real_async_context_managers" time="0.001" /><testcase classname="tests.test_disposables" name="test_nested_disposables_usage" 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_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.000" /><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.001" /><testcase classname="tests.test_state" name="test_generic_subtypes_validation" time="0.001" /><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.001" /><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.001" /><testcase classname="tests.test_state" name="test_hash_with_nested_unhashable_collections" time="0.001" /><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.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.000" /><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.001" /><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.001" /><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_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.22.0"
8
+ version = "0.23.0"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -539,6 +539,8 @@ class ctx:
539
539
  def check_state[StateType: State](
540
540
  state: type[StateType],
541
541
  /,
542
+ *,
543
+ instantiate_defaults: bool = False,
542
544
  ) -> bool:
543
545
  """
544
546
  Check if state object is available in the current context.
@@ -551,12 +553,18 @@ class ctx:
551
553
  state: type[StateType]
552
554
  The type of state to check
553
555
 
556
+ instantiate_defaults: bool = False
557
+ Control if default value should be instantiated during check.
558
+
554
559
  Returns
555
560
  -------
556
561
  bool
557
562
  True if state is available, otherwise False.
558
563
  """
559
- return StateContext.check_state(state)
564
+ return StateContext.check_state(
565
+ state,
566
+ instantiate_defaults=instantiate_defaults,
567
+ )
560
568
 
561
569
  @staticmethod
562
570
  def state[StateType: State](
@@ -12,7 +12,7 @@ from itertools import chain
12
12
  from types import TracebackType
13
13
  from typing import Any, final
14
14
 
15
- from haiway.context.state import ScopeState, StateContext
15
+ from haiway.context.state import StateContext
16
16
  from haiway.state import State
17
17
  from haiway.utils.mimic import mimic_function
18
18
 
@@ -195,19 +195,25 @@ class Disposables:
195
195
  return multiple
196
196
 
197
197
  async def prepare(self) -> Iterable[State]:
198
+ """
199
+ Enter all contained disposables asynchronously.
200
+
201
+ Enters all disposables in parallel and collects any State objects they return.
202
+ """
198
203
  assert self._loop is None # nosec: B101
199
204
  object.__setattr__(
200
205
  self,
201
206
  "_loop",
202
207
  get_running_loop(),
203
208
  )
204
- return [
205
- *chain.from_iterable(
209
+
210
+ return tuple(
211
+ chain.from_iterable(
206
212
  await gather(
207
213
  *[self._setup(disposable) for disposable in self._disposables],
208
214
  )
209
215
  )
210
- ]
216
+ )
211
217
 
212
218
  async def __aenter__(self) -> None:
213
219
  """
@@ -218,7 +224,7 @@ class Disposables:
218
224
  """
219
225
 
220
226
  assert self._state_context is None, "Context reentrance is not allowed" # nosec: B101
221
- state_context = StateContext(state=ScopeState(await self.prepare()))
227
+ state_context = StateContext.updated(await self.prepare())
222
228
  state_context.__enter__()
223
229
  object.__setattr__(
224
230
  self,
@@ -252,6 +258,26 @@ class Disposables:
252
258
  exc_val: BaseException | None = None,
253
259
  exc_tb: TracebackType | None = None,
254
260
  ) -> None:
261
+ """
262
+ Exit all contained disposables asynchronously.
263
+
264
+ Properly disposes of all resources by calling their __aexit__ methods in parallel.
265
+ If multiple disposables raise exceptions, they are collected into a BaseExceptionGroup.
266
+
267
+ Parameters
268
+ ----------
269
+ exc_type: type[BaseException] | None
270
+ The type of exception that caused the context to be exited
271
+ exc_val: BaseException | None
272
+ The exception that caused the context to be exited
273
+ exc_tb: TracebackType | None
274
+ The traceback for the exception that caused the context to be exited
275
+
276
+ Raises
277
+ ------
278
+ BaseExceptionGroup
279
+ If multiple disposables raise exceptions during exit
280
+ """
255
281
  assert self._loop is not None # nosec: B101
256
282
  results: list[bool | BaseException | None]
257
283
 
@@ -306,6 +332,7 @@ class Disposables:
306
332
 
307
333
  Properly disposes of all resources by calling their __aexit__ methods in parallel.
308
334
  If multiple disposables raise exceptions, they are collected into a BaseExceptionGroup.
335
+ Additionally, produced state context will be also exited resetting state to previous.
309
336
 
310
337
  Parameters
311
338
  ----------
@@ -403,7 +403,7 @@ def _logger_observability(
403
403
  f"[{trace_id_hex}] {scope.unique_name} Recorded attributes: {format_str(attributes)}",
404
404
  )
405
405
 
406
- def scope_entering[Metric: State](
406
+ def scope_entering(
407
407
  scope: ScopeIdentifier,
408
408
  /,
409
409
  ) -> None:
@@ -412,7 +412,7 @@ def _logger_observability(
412
412
  f"[{trace_id_hex}] {scope.unique_name} Entering scope: {scope.label}",
413
413
  )
414
414
 
415
- def scope_exiting[Metric: State](
415
+ def scope_exiting(
416
416
  scope: ScopeIdentifier,
417
417
  /,
418
418
  *,
@@ -37,6 +37,7 @@ class ScopeState:
37
37
  "_state",
38
38
  {type(element): element for element in state},
39
39
  )
40
+ assert all(issubclass(key, State) for key in self._state.keys()) # nosec: B101
40
41
  self._lock: Lock
41
42
  object.__setattr__(
42
43
  self,
@@ -67,6 +68,8 @@ class ScopeState:
67
68
  self,
68
69
  state: type[StateType],
69
70
  /,
71
+ *,
72
+ instantiate_defaults: bool = False,
70
73
  ) -> bool:
71
74
  """
72
75
  Check state object availability by its type.
@@ -79,6 +82,9 @@ class ScopeState:
79
82
  state: type[StateType]
80
83
  The type of state to check
81
84
 
85
+ instantiate_defaults: bool = False
86
+ Control if default value should be instantiated during check.
87
+
82
88
  Returns
83
89
  -------
84
90
  bool
@@ -87,7 +93,7 @@ class ScopeState:
87
93
  if state in self._state:
88
94
  return True
89
95
 
90
- else:
96
+ elif instantiate_defaults:
91
97
  with self._lock:
92
98
  if state in self._state:
93
99
  return True
@@ -100,6 +106,9 @@ class ScopeState:
100
106
  except BaseException:
101
107
  return False # unavailable, we don't care the exception
102
108
 
109
+ else:
110
+ return False
111
+
103
112
  def state[StateType: State](
104
113
  self,
105
114
  state: type[StateType],
@@ -201,6 +210,7 @@ class StateContext:
201
210
  cls,
202
211
  state: type[StateType],
203
212
  /,
213
+ instantiate_defaults: bool = False,
204
214
  ) -> bool:
205
215
  """
206
216
  Check if state object is available in the current context.
@@ -212,13 +222,19 @@ class StateContext:
212
222
  state: type[StateType]
213
223
  The type of state to check
214
224
 
225
+ instantiate_defaults: bool = False
226
+ Control if default value should be instantiated during check.
227
+
215
228
  Returns
216
229
  -------
217
230
  bool
218
231
  True if state is available, otherwise False.
219
232
  """
220
233
  try:
221
- return cls._context.get().check_state(state)
234
+ return cls._context.get().check_state(
235
+ state,
236
+ instantiate_defaults=instantiate_defaults,
237
+ )
222
238
 
223
239
  except LookupError:
224
240
  return False # no context no state
@@ -5,7 +5,7 @@ from haiway.helpers.files import File, FileAccess
5
5
  from haiway.helpers.observability import LoggerObservability
6
6
  from haiway.helpers.retries import retry
7
7
  from haiway.helpers.throttling import throttle
8
- from haiway.helpers.timeouted import timeout
8
+ from haiway.helpers.timeouting import timeout
9
9
  from haiway.helpers.tracing import traced
10
10
 
11
11
  __all__ = (
@@ -6,7 +6,6 @@ from uuid import UUID, uuid4
6
6
 
7
7
  from haiway.context import Observability, ObservabilityLevel, ScopeIdentifier
8
8
  from haiway.context.observability import ObservabilityAttribute
9
- from haiway.state import State
10
9
  from haiway.utils.formatting import format_str
11
10
 
12
11
  __all__ = ("LoggerObservability",)
@@ -232,7 +231,7 @@ def LoggerObservability( # noqa: C901, PLR0915
232
231
  f"[{trace_id_hex}] {scope.unique_name} {attributes_str}",
233
232
  )
234
233
 
235
- def scope_entering[Metric: State](
234
+ def scope_entering(
236
235
  scope: ScopeIdentifier,
237
236
  /,
238
237
  ) -> None:
@@ -255,7 +254,7 @@ def LoggerObservability( # noqa: C901, PLR0915
255
254
  f"[{trace_id_hex}] {scope.unique_name} Entering scope: {scope.label}",
256
255
  )
257
256
 
258
- def scope_exiting[Metric: State](
257
+ def scope_exiting(
259
258
  scope: ScopeIdentifier,
260
259
  /,
261
260
  *,
@@ -1,5 +1,5 @@
1
1
  import os
2
- from collections.abc import Mapping
2
+ from collections.abc import Mapping, Sequence
3
3
  from typing import Any, ClassVar, Self, cast, final
4
4
  from uuid import UUID
5
5
 
@@ -26,12 +26,16 @@ from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExpo
26
26
  from opentelemetry.sdk.resources import Resource
27
27
  from opentelemetry.sdk.trace import TracerProvider
28
28
  from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter, SpanExporter
29
- from opentelemetry.trace import Span, StatusCode, Tracer
30
- from opentelemetry.trace.span import SpanContext
31
-
32
- from haiway.context import Observability, ObservabilityLevel, ScopeIdentifier
33
- from haiway.context.observability import ObservabilityAttribute
34
- from haiway.state import State
29
+ from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
30
+ from opentelemetry.trace import Link, Span, SpanContext, StatusCode, TraceFlags, Tracer
31
+
32
+ from haiway.context import (
33
+ Observability,
34
+ ObservabilityAttribute,
35
+ ObservabilityLevel,
36
+ ScopeIdentifier,
37
+ ctx,
38
+ )
35
39
  from haiway.types import MISSING
36
40
 
37
41
  __all__ = ("OpenTelemetry",)
@@ -437,6 +441,8 @@ class OpenTelemetry:
437
441
  def observability( # noqa: C901, PLR0915
438
442
  cls,
439
443
  level: ObservabilityLevel = ObservabilityLevel.INFO,
444
+ *,
445
+ external_trace_id: str | None = None,
440
446
  ) -> Observability:
441
447
  """
442
448
  Create an Observability implementation using OpenTelemetry.
@@ -449,6 +455,9 @@ class OpenTelemetry:
449
455
  ----------
450
456
  level : ObservabilityLevel, default=ObservabilityLevel.INFO
451
457
  The minimum observability level to record
458
+ external_trace_id : str | None, optional
459
+ External trace ID for distributed tracing context propagation.
460
+ If provided, the root span will be linked to this external trace.
452
461
 
453
462
  Returns
454
463
  -------
@@ -645,7 +654,7 @@ class OpenTelemetry:
645
654
 
646
655
  scopes[scope.scope_id].record_attributes(attributes)
647
656
 
648
- def scope_entering[Metric: State](
657
+ def scope_entering(
649
658
  scope: ScopeIdentifier,
650
659
  /,
651
660
  ) -> None:
@@ -678,17 +687,44 @@ class OpenTelemetry:
678
687
  scope_store: ScopeStore
679
688
  if root_scope is None:
680
689
  meter = metrics.get_meter(scope.label)
681
- context: Context = Context(
682
- **get_current(),
683
- # trace_id=scope.trace_id,
684
- # span_id=scope.scope_id,
685
- )
690
+ context: Context = get_current()
691
+
692
+ # Handle distributed tracing with external trace ID
693
+ links: Sequence[Link] | None = None
694
+ if external_trace_id is not None:
695
+ # Convert external trace ID to OpenTelemetry trace ID format
696
+ try:
697
+ # Generate a proper span ID for the external trace link
698
+ id_generator = RandomIdGenerator()
699
+
700
+ # Create a link to the external trace
701
+ links = (
702
+ Link(
703
+ SpanContext(
704
+ ( # Assume external_trace_id is a hex string, convert to int
705
+ int(external_trace_id, 16)
706
+ if isinstance(external_trace_id, str)
707
+ else int(external_trace_id)
708
+ ),
709
+ id_generator.generate_span_id(), # Generate proper span ID
710
+ True, # is_remote=True
711
+ TraceFlags.SAMPLED, # pyright: ignore[reportArgumentType]
712
+ )
713
+ ),
714
+ )
715
+
716
+ except (ValueError, TypeError):
717
+ ctx.log_warning(
718
+ "Failed to convert external trace ID to OpenTelemetry format"
719
+ )
720
+
686
721
  scope_store = ScopeStore(
687
722
  scope,
688
723
  context=context,
689
724
  span=tracer.start_span(
690
725
  name=scope.label,
691
726
  context=context,
727
+ links=links,
692
728
  ),
693
729
  meter=meter,
694
730
  logger=get_logger(scope.label),
@@ -711,7 +747,7 @@ class OpenTelemetry:
711
747
 
712
748
  scopes[scope.scope_id] = scope_store
713
749
 
714
- def scope_exiting[Metric: State](
750
+ def scope_exiting(
715
751
  scope: ScopeIdentifier,
716
752
  /,
717
753
  *,
@@ -729,12 +729,22 @@ class State(metaclass=StateMeta):
729
729
  )
730
730
 
731
731
  def __hash__(self) -> int:
732
- return hash(
733
- (
734
- self.__class__,
735
- *tuple(getattr(self, key, MISSING) for key in sorted(self.__ATTRIBUTES__.keys())),
736
- )
737
- )
732
+ hash_values: list[int] = []
733
+ for key in self.__ATTRIBUTES__.keys():
734
+ value: Any = getattr(self, key, MISSING)
735
+
736
+ # Skip MISSING values to ensure consistent hashing
737
+ if value is MISSING:
738
+ continue
739
+
740
+ # Convert to hashable representation
741
+ try:
742
+ hash_values.append(hash(value))
743
+
744
+ except TypeError:
745
+ continue # skip unhashable
746
+
747
+ return hash((self.__class__, tuple(hash_values)))
738
748
 
739
749
  def __setattr__(
740
750
  self,
@@ -9,7 +9,6 @@ from haiway.utils.env import (
9
9
  load_env,
10
10
  )
11
11
  from haiway.utils.formatting import format_str
12
- from haiway.utils.freezing import freeze
13
12
  from haiway.utils.logs import setup_logging
14
13
  from haiway.utils.mimic import mimic_function
15
14
  from haiway.utils.noop import async_noop, noop
@@ -27,7 +26,6 @@ __all__ = (
27
26
  "async_always",
28
27
  "async_noop",
29
28
  "format_str",
30
- "freeze",
31
29
  "getenv_base64",
32
30
  "getenv_bool",
33
31
  "getenv_float",
@@ -128,7 +128,7 @@ def test_non_empty_disposables_is_truthy():
128
128
  async def test_setup_with_no_disposables():
129
129
  disposables = Disposables()
130
130
  result = await disposables.prepare()
131
- assert result == []
131
+ assert result == ()
132
132
  assert disposables._loop is not None
133
133
 
134
134
 
@@ -137,7 +137,7 @@ async def test_setup_with_disposable_returning_none():
137
137
  mock = MockDisposable(enter_return=None)
138
138
  disposables = Disposables(mock)
139
139
  result = await disposables.prepare()
140
- assert result == []
140
+ assert result == ()
141
141
  assert mock.enter_called
142
142
  assert disposables._loop is not None
143
143