haiway 0.16.0__tar.gz → 0.18.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 (111) hide show
  1. {haiway-0.16.0 → haiway-0.18.0}/Makefile +1 -1
  2. {haiway-0.16.0 → haiway-0.18.0}/PKG-INFO +9 -5
  3. haiway-0.18.0/junit/test-results.xml +1 -0
  4. {haiway-0.16.0 → haiway-0.18.0}/pyproject.toml +11 -7
  5. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/__init__.py +18 -18
  6. haiway-0.18.0/src/haiway/context/__init__.py +34 -0
  7. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/context/access.py +92 -144
  8. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/context/disposables.py +2 -2
  9. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/context/identifier.py +4 -5
  10. haiway-0.18.0/src/haiway/context/observability.py +452 -0
  11. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/context/state.py +2 -2
  12. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/context/tasks.py +1 -3
  13. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/context/types.py +2 -2
  14. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/__init__.py +7 -6
  15. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/asynchrony.py +2 -2
  16. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/caching.py +2 -2
  17. haiway-0.18.0/src/haiway/helpers/observability.py +219 -0
  18. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/retries.py +1 -3
  19. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/throttling.py +1 -3
  20. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/timeouted.py +1 -3
  21. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/helpers/tracing.py +25 -17
  22. haiway-0.18.0/src/haiway/opentelemetry/__init__.py +3 -0
  23. haiway-0.18.0/src/haiway/opentelemetry/observability.py +420 -0
  24. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/state/__init__.py +2 -2
  25. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/state/attributes.py +2 -2
  26. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/state/path.py +1 -3
  27. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/state/requirement.py +1 -3
  28. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/state/structure.py +161 -30
  29. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/state/validation.py +2 -2
  30. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/types/__init__.py +2 -2
  31. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/types/default.py +2 -2
  32. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/types/frozen.py +1 -3
  33. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/types/missing.py +2 -2
  34. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/__init__.py +2 -2
  35. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/always.py +2 -2
  36. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/collections.py +2 -2
  37. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/env.py +2 -2
  38. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/freezing.py +1 -3
  39. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/logs.py +1 -3
  40. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/mimic.py +1 -3
  41. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/noop.py +2 -2
  42. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/queue.py +1 -3
  43. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/utils/stream.py +1 -3
  44. {haiway-0.16.0 → haiway-0.18.0}/tests/test_state.py +3 -3
  45. haiway-0.18.0/uv.lock +540 -0
  46. haiway-0.16.0/junit/test-results.xml +0 -1
  47. haiway-0.16.0/src/haiway/context/__init__.py +0 -30
  48. haiway-0.16.0/src/haiway/context/logging.py +0 -242
  49. haiway-0.16.0/src/haiway/context/metrics.py +0 -214
  50. haiway-0.16.0/src/haiway/helpers/metrics.py +0 -501
  51. haiway-0.16.0/uv.lock +0 -318
  52. {haiway-0.16.0 → haiway-0.18.0}/.github/workflows/ci.yml +0 -0
  53. {haiway-0.16.0 → haiway-0.18.0}/.github/workflows/publish.yml +0 -0
  54. {haiway-0.16.0 → haiway-0.18.0}/.gitignore +0 -0
  55. {haiway-0.16.0 → haiway-0.18.0}/LICENSE +0 -0
  56. {haiway-0.16.0 → haiway-0.18.0}/README.md +0 -0
  57. {haiway-0.16.0 → haiway-0.18.0}/config/pre-push +0 -0
  58. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/.dockerignore +0 -0
  59. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/Dockerfile +0 -0
  60. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/Makefile +0 -0
  61. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/README.md +0 -0
  62. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/config/.env.example +0 -0
  63. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/config/unit.json +0 -0
  64. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/docker-compose.yml +0 -0
  65. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/pyproject.toml +0 -0
  66. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/features/__int__.py +0 -0
  67. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/features/todos/__init__.py +0 -0
  68. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/features/todos/config.py +0 -0
  69. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/features/todos/state.py +0 -0
  70. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/features/todos/types.py +0 -0
  71. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/features/todos/user_tasks.py +0 -0
  72. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/integrations/__init__.py +0 -0
  73. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/integrations/postgres/__init__.py +0 -0
  74. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/integrations/postgres/client.py +0 -0
  75. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/integrations/postgres/config.py +0 -0
  76. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/integrations/postgres/state.py +0 -0
  77. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/integrations/postgres/types.py +0 -0
  78. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/migrations/__init__.py +0 -0
  79. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/migrations/__main__.py +0 -0
  80. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/migrations/postgres/__init__.py +0 -0
  81. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/migrations/postgres/execution.py +0 -0
  82. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/migrations/postgres/migration_0.py +0 -0
  83. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/migrations/postgres/types.py +0 -0
  84. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/__init__.py +0 -0
  85. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/__main__.py +0 -0
  86. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/application.py +0 -0
  87. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/config.py +0 -0
  88. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/middlewares/__init__.py +0 -0
  89. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/middlewares/context.py +0 -0
  90. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/routes/__init__.py +0 -0
  91. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/routes/technical.py +0 -0
  92. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/server/routes/todos.py +0 -0
  93. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/solutions/__init__.py +0 -0
  94. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/solutions/user_tasks/__init__.py +0 -0
  95. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/solutions/user_tasks/config.py +0 -0
  96. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/solutions/user_tasks/postgres.py +0 -0
  97. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/solutions/user_tasks/state.py +0 -0
  98. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/src/solutions/user_tasks/types.py +0 -0
  99. {haiway-0.16.0 → haiway-0.18.0}/examples/fastAPI/uv.lock +0 -0
  100. {haiway-0.16.0 → haiway-0.18.0}/guidelines/functionalities.md +0 -0
  101. {haiway-0.16.0 → haiway-0.18.0}/guidelines/packages.md +0 -0
  102. {haiway-0.16.0 → haiway-0.18.0}/src/haiway/py.typed +0 -0
  103. {haiway-0.16.0 → haiway-0.18.0}/tests/__init__.py +0 -0
  104. {haiway-0.16.0 → haiway-0.18.0}/tests/test_async_queue.py +0 -0
  105. {haiway-0.16.0 → haiway-0.18.0}/tests/test_async_stream.py +0 -0
  106. {haiway-0.16.0 → haiway-0.18.0}/tests/test_attribute_path.py +0 -0
  107. {haiway-0.16.0 → haiway-0.18.0}/tests/test_auto_retry.py +0 -0
  108. {haiway-0.16.0 → haiway-0.18.0}/tests/test_cache.py +0 -0
  109. {haiway-0.16.0 → haiway-0.18.0}/tests/test_context.py +0 -0
  110. {haiway-0.16.0 → haiway-0.18.0}/tests/test_streaming.py +0 -0
  111. {haiway-0.16.0 → haiway-0.18.0}/tests/test_timeout.py +0 -0
@@ -10,7 +10,7 @@ TESTS_PATH := tests
10
10
  -include .env
11
11
 
12
12
  ifndef UV_VERSION
13
- UV_VERSION := 0.6.5
13
+ UV_VERSION := 0.7.2
14
14
  endif
15
15
 
16
16
  .PHONY: uv_check venv sync update format lint test release
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiway
3
- Version: 0.16.0
3
+ Version: 0.18.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
@@ -35,12 +35,16 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
35
35
  Classifier: Typing :: Typed
36
36
  Requires-Python: >=3.12
37
37
  Provides-Extra: dev
38
- Requires-Dist: bandit~=1.7; extra == 'dev'
38
+ Requires-Dist: bandit~=1.8; extra == 'dev'
39
39
  Requires-Dist: pyright~=1.1; extra == 'dev'
40
- Requires-Dist: pytest-asyncio~=0.23; extra == 'dev'
41
- Requires-Dist: pytest-cov~=4.1; extra == 'dev'
42
- Requires-Dist: pytest~=7.4; extra == 'dev'
40
+ Requires-Dist: pytest-asyncio~=0.26; extra == 'dev'
41
+ Requires-Dist: pytest-cov~=6.1; extra == 'dev'
42
+ Requires-Dist: pytest~=8.3; extra == 'dev'
43
43
  Requires-Dist: ruff~=0.11; extra == 'dev'
44
+ Provides-Extra: opentelemetry
45
+ Requires-Dist: opentelemetry-api; extra == 'opentelemetry'
46
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc; extra == 'opentelemetry'
47
+ Requires-Dist: opentelemetry-sdk; extra == 'opentelemetry'
44
48
  Description-Content-Type: text/markdown
45
49
 
46
50
  # 🚗 haiway 🚕 🚚 🚙
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="88" time="1.122" timestamp="2025-05-12T14:08:49.319975+00:00" hostname="pkrvmberfyhpb9w"><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_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.007" /><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.001" /><testcase classname="tests.test_cache" name="test_returns_fresh_value_with_limit_exceed" time="0.001" /><testcase classname="tests.test_cache" name="test_returns_same_value_with_repeating_argument" time="0.001" /><testcase classname="tests.test_cache" name="test_fails_with_error" time="0.001" /><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_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.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_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.16.0"
8
+ version = "0.18.0"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -27,14 +27,18 @@ Homepage = "https://miquido.com"
27
27
  Repository = "https://github.com/miquido/haiway.git"
28
28
 
29
29
  [project.optional-dependencies]
30
+ opentelemetry = [
31
+ "opentelemetry-api",
32
+ "opentelemetry-sdk",
33
+ "opentelemetry-exporter-otlp-proto-grpc",
34
+ ]
30
35
  dev = [
31
- "haiway",
32
- "ruff~=0.11",
36
+ "bandit~=1.8",
33
37
  "pyright~=1.1",
34
- "bandit~=1.7",
35
- "pytest~=7.4",
36
- "pytest-cov~=4.1",
37
- "pytest-asyncio~=0.23",
38
+ "pytest~=8.3",
39
+ "pytest-asyncio~=0.26",
40
+ "pytest-cov~=6.1",
41
+ "ruff~=0.11",
38
42
  ]
39
43
 
40
44
  [tool.ruff]
@@ -1,14 +1,16 @@
1
1
  from haiway.context import (
2
2
  Disposable,
3
3
  Disposables,
4
- MetricsContext,
5
- MetricsHandler,
6
- MetricsReading,
7
- MetricsRecording,
8
- MetricsScopeEntering,
9
- MetricsScopeExiting,
10
4
  MissingContext,
11
5
  MissingState,
6
+ Observability,
7
+ ObservabilityContext,
8
+ ObservabilityEventRecording,
9
+ ObservabilityLevel,
10
+ ObservabilityLogRecording,
11
+ ObservabilityMetricRecording,
12
+ ObservabilityScopeEntering,
13
+ ObservabilityScopeExiting,
12
14
  ScopeContext,
13
15
  ScopeIdentifier,
14
16
  StateContext,
@@ -16,8 +18,6 @@ from haiway.context import (
16
18
  )
17
19
  from haiway.helpers import (
18
20
  ArgumentsTrace,
19
- MetricsHolder,
20
- MetricsLogger,
21
21
  ResultTrace,
22
22
  asynchronous,
23
23
  cache,
@@ -61,7 +61,7 @@ from haiway.utils import (
61
61
  without_missing,
62
62
  )
63
63
 
64
- __all__ = [
64
+ __all__ = (
65
65
  "MISSING",
66
66
  "ArgumentsTrace",
67
67
  "AsyncQueue",
@@ -72,17 +72,17 @@ __all__ = [
72
72
  "DefaultValue",
73
73
  "Disposable",
74
74
  "Disposables",
75
- "MetricsContext",
76
- "MetricsHandler",
77
- "MetricsHolder",
78
- "MetricsLogger",
79
- "MetricsReading",
80
- "MetricsRecording",
81
- "MetricsScopeEntering",
82
- "MetricsScopeExiting",
83
75
  "Missing",
84
76
  "MissingContext",
85
77
  "MissingState",
78
+ "Observability",
79
+ "ObservabilityContext",
80
+ "ObservabilityEventRecording",
81
+ "ObservabilityLevel",
82
+ "ObservabilityLogRecording",
83
+ "ObservabilityMetricRecording",
84
+ "ObservabilityScopeEntering",
85
+ "ObservabilityScopeExiting",
86
86
  "ResultTrace",
87
87
  "ScopeContext",
88
88
  "ScopeIdentifier",
@@ -118,4 +118,4 @@ __all__ = [
118
118
  "when_missing",
119
119
  "without_missing",
120
120
  "wrap_async",
121
- ]
121
+ )
@@ -0,0 +1,34 @@
1
+ from haiway.context.access import ScopeContext, ctx
2
+ from haiway.context.disposables import Disposable, Disposables
3
+ from haiway.context.identifier import ScopeIdentifier
4
+ from haiway.context.observability import (
5
+ Observability,
6
+ ObservabilityContext,
7
+ ObservabilityEventRecording,
8
+ ObservabilityLevel,
9
+ ObservabilityLogRecording,
10
+ ObservabilityMetricRecording,
11
+ ObservabilityScopeEntering,
12
+ ObservabilityScopeExiting,
13
+ )
14
+ from haiway.context.state import StateContext
15
+ from haiway.context.types import MissingContext, MissingState
16
+
17
+ __all__ = (
18
+ "Disposable",
19
+ "Disposables",
20
+ "MissingContext",
21
+ "MissingState",
22
+ "Observability",
23
+ "ObservabilityContext",
24
+ "ObservabilityEventRecording",
25
+ "ObservabilityLevel",
26
+ "ObservabilityLogRecording",
27
+ "ObservabilityMetricRecording",
28
+ "ObservabilityScopeEntering",
29
+ "ObservabilityScopeExiting",
30
+ "ScopeContext",
31
+ "ScopeIdentifier",
32
+ "StateContext",
33
+ "ctx",
34
+ )
@@ -18,17 +18,14 @@ from typing import Any, final, overload
18
18
 
19
19
  from haiway.context.disposables import Disposable, Disposables
20
20
  from haiway.context.identifier import ScopeIdentifier
21
- from haiway.context.logging import LoggerContext
22
- from haiway.context.metrics import MetricsContext, MetricsHandler
21
+ from haiway.context.observability import Observability, ObservabilityContext, ObservabilityLevel
23
22
  from haiway.context.state import ScopeState, StateContext
24
23
  from haiway.context.tasks import TaskGroupContext
25
24
  from haiway.state import State
26
25
  from haiway.utils import mimic_function
27
26
  from haiway.utils.stream import AsyncStream
28
27
 
29
- __all__ = [
30
- "ctx",
31
- ]
28
+ __all__ = ("ctx",)
32
29
 
33
30
 
34
31
  @final
@@ -36,8 +33,7 @@ class ScopeContext:
36
33
  __slots__ = (
37
34
  "_disposables",
38
35
  "_identifier",
39
- "_logger_context",
40
- "_metrics_context",
36
+ "_observability_context",
41
37
  "_state_context",
42
38
  "_task_group_context",
43
39
  )
@@ -45,11 +41,10 @@ class ScopeContext:
45
41
  def __init__(
46
42
  self,
47
43
  label: str,
48
- logger: Logger | None,
49
44
  task_group: TaskGroup | None,
50
45
  state: tuple[State, ...],
51
46
  disposables: Disposables | None,
52
- metrics: MetricsHandler | None,
47
+ observability: Observability | Logger | None,
53
48
  ) -> None:
54
49
  self._identifier: ScopeIdentifier
55
50
  object.__setattr__(
@@ -57,23 +52,6 @@ class ScopeContext:
57
52
  "_identifier",
58
53
  ScopeIdentifier.scope(label),
59
54
  )
60
- self._logger_context: LoggerContext
61
- object.__setattr__(
62
- self,
63
- "_logger_context",
64
- LoggerContext(
65
- self._identifier,
66
- logger=logger,
67
- ),
68
- )
69
- self._task_group_context: TaskGroupContext | None
70
- object.__setattr__(
71
- self,
72
- "_task_group_context",
73
- TaskGroupContext(task_group=task_group)
74
- if task_group is not None or self._identifier.is_root
75
- else None,
76
- )
77
55
  # prepare state context to capture current state
78
56
  self._state_context: StateContext
79
57
  object.__setattr__(
@@ -87,16 +65,24 @@ class ScopeContext:
87
65
  "_disposables",
88
66
  disposables,
89
67
  )
90
- self._metrics_context: MetricsContext
68
+ self._observability_context: ObservabilityContext
91
69
  object.__setattr__(
92
70
  self,
93
- "_metrics_context",
94
- # pre-building metrics context to ensure nested context registering
95
- MetricsContext.scope(
71
+ "_observability_context",
72
+ # pre-building observability context to ensure nested context registering
73
+ ObservabilityContext.scope(
96
74
  self._identifier,
97
- metrics=metrics,
75
+ observability=observability,
98
76
  ),
99
77
  )
78
+ self._task_group_context: TaskGroupContext | None
79
+ object.__setattr__(
80
+ self,
81
+ "_task_group_context",
82
+ TaskGroupContext(task_group=task_group)
83
+ if task_group is not None or self._identifier.is_root
84
+ else None,
85
+ )
100
86
 
101
87
  def __setattr__(
102
88
  self,
@@ -123,9 +109,8 @@ class ScopeContext:
123
109
  ), "Can't enter synchronous context with task group"
124
110
  assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
125
111
  self._identifier.__enter__()
126
- self._logger_context.__enter__()
112
+ self._observability_context.__enter__()
127
113
  self._state_context.__enter__()
128
- self._metrics_context.__enter__()
129
114
 
130
115
  return self._identifier.trace_id
131
116
 
@@ -135,24 +120,16 @@ class ScopeContext:
135
120
  exc_val: BaseException | None,
136
121
  exc_tb: TracebackType | None,
137
122
  ) -> None:
138
- self._metrics_context.__exit__(
139
- exc_type=exc_type,
140
- exc_val=exc_val,
141
- exc_tb=exc_tb,
142
- )
143
-
144
123
  self._state_context.__exit__(
145
124
  exc_type=exc_type,
146
125
  exc_val=exc_val,
147
126
  exc_tb=exc_tb,
148
127
  )
149
-
150
- self._logger_context.__exit__(
128
+ self._observability_context.__exit__(
151
129
  exc_type=exc_type,
152
130
  exc_val=exc_val,
153
131
  exc_tb=exc_tb,
154
132
  )
155
-
156
133
  self._identifier.__exit__(
157
134
  exc_type=exc_type,
158
135
  exc_val=exc_val,
@@ -161,7 +138,7 @@ class ScopeContext:
161
138
 
162
139
  async def __aenter__(self) -> str:
163
140
  self._identifier.__enter__()
164
- self._logger_context.__enter__()
141
+ self._observability_context.__enter__()
165
142
 
166
143
  if task_group := self._task_group_context:
167
144
  await task_group.__aenter__()
@@ -183,7 +160,6 @@ class ScopeContext:
183
160
  )
184
161
 
185
162
  self._state_context.__enter__()
186
- self._metrics_context.__enter__()
187
163
 
188
164
  return self._identifier.trace_id
189
165
 
@@ -207,19 +183,13 @@ class ScopeContext:
207
183
  exc_tb=exc_tb,
208
184
  )
209
185
 
210
- self._metrics_context.__exit__(
211
- exc_type=exc_type,
212
- exc_val=exc_val,
213
- exc_tb=exc_tb,
214
- )
215
-
216
186
  self._state_context.__exit__(
217
187
  exc_type=exc_type,
218
188
  exc_val=exc_val,
219
189
  exc_tb=exc_tb,
220
190
  )
221
191
 
222
- self._logger_context.__exit__(
192
+ self._observability_context.__exit__(
223
193
  exc_type=exc_type,
224
194
  exc_val=exc_val,
225
195
  exc_tb=exc_tb,
@@ -288,9 +258,8 @@ class ctx:
288
258
  /,
289
259
  *state: State,
290
260
  disposables: Disposables | Iterable[Disposable] | None = None,
291
- logger: Logger | None = None,
292
261
  task_group: TaskGroup | None = None,
293
- metrics: MetricsHandler | None = None,
262
+ observability: Observability | Logger | None = None,
294
263
  ) -> ScopeContext:
295
264
  """
296
265
  Prepare scope context with given parameters. When called within an existing context\
@@ -310,18 +279,14 @@ class ctx:
310
279
  be added to the scope state. Using asynchronous context is required if any disposables\
311
280
  were provided.
312
281
 
313
- logger: Logger | None
314
- logger used within the scope context, when not provided current logger will be used\
315
- if any, otherwise the logger with the scope name will be requested.
316
-
317
282
  task_group: TaskGroup | None
318
283
  task group used for spawning and joining tasks within the context. Root scope will
319
284
  always have task group created even when not set.
320
285
 
321
- metrics_store: MetricsStore | None = None
322
- metrics storage solution responsible for recording and storing metrics.\
323
- Metrics recroding will be ignored if storage is not provided.
324
- Assigning metrics_store within existing context will result in an error.
286
+ observability: Observability | Logger | None = None
287
+ observability solution responsible for recording and storing metrics, logs and events.\
288
+ Assigning observability within existing context will result in an error.
289
+ When not provided, logger with the scope name will be requested and used.
325
290
 
326
291
  Returns
327
292
  -------
@@ -343,11 +308,10 @@ class ctx:
343
308
 
344
309
  return ScopeContext(
345
310
  label=label,
346
- logger=logger,
347
311
  task_group=task_group,
348
312
  state=state,
349
313
  disposables=resolved_disposables,
350
- metrics=metrics,
314
+ observability=observability,
351
315
  )
352
316
 
353
317
  @staticmethod
@@ -492,83 +456,6 @@ class ctx:
492
456
  default=default,
493
457
  )
494
458
 
495
- @staticmethod
496
- def record(
497
- metric: State,
498
- /,
499
- ) -> None:
500
- """
501
- Record metric within current scope context.
502
-
503
- Parameters
504
- ----------
505
- metric: State
506
- value of metric to be recorded. When a metric implements __add__ it will be added to\
507
- current value if any, otherwise subsequent calls may replace existing value.
508
-
509
- Returns
510
- -------
511
- None
512
- """
513
-
514
- MetricsContext.record(metric)
515
-
516
- @overload
517
- @staticmethod
518
- async def read[Metric: State](
519
- metric: type[Metric],
520
- /,
521
- *,
522
- merged: bool = False,
523
- ) -> Metric | None: ...
524
-
525
- @overload
526
- @staticmethod
527
- async def read[Metric: State](
528
- metric: type[Metric],
529
- /,
530
- *,
531
- merged: bool = False,
532
- default: Metric,
533
- ) -> Metric: ...
534
-
535
- @staticmethod
536
- async def read[Metric: State](
537
- metric: type[Metric],
538
- /,
539
- *,
540
- merged: bool = False,
541
- default: Metric | None = None,
542
- ) -> Metric | None:
543
- """
544
- Read metric within current scope context.
545
-
546
- Parameters
547
- ----------
548
- metric: type[Metric]
549
- type of metric to be read from current context.
550
-
551
- merged: bool
552
- control wheather to merge metrics from nested scopes (True)\
553
- or access only the current scope value (False) without combining them
554
-
555
- default: Metric | None
556
- default value to return when metric was not recorded yet.
557
-
558
- Returns
559
- -------
560
- Metric | None
561
- """
562
-
563
- value: Metric | None = await MetricsContext.read(
564
- metric,
565
- merged=merged,
566
- )
567
- if value is None:
568
- return default
569
-
570
- return value
571
-
572
459
  @staticmethod
573
460
  def log_error(
574
461
  message: str,
@@ -596,7 +483,8 @@ class ctx:
596
483
  None
597
484
  """
598
485
 
599
- LoggerContext.log_error(
486
+ ObservabilityContext.record_log(
487
+ ObservabilityLevel.ERROR,
600
488
  message,
601
489
  *args,
602
490
  exception=exception,
@@ -629,7 +517,8 @@ class ctx:
629
517
  None
630
518
  """
631
519
 
632
- LoggerContext.log_warning(
520
+ ObservabilityContext.record_log(
521
+ ObservabilityLevel.WARNING,
633
522
  message,
634
523
  *args,
635
524
  exception=exception,
@@ -658,9 +547,11 @@ class ctx:
658
547
  None
659
548
  """
660
549
 
661
- LoggerContext.log_info(
550
+ ObservabilityContext.record_log(
551
+ ObservabilityLevel.INFO,
662
552
  message,
663
553
  *args,
554
+ exception=None,
664
555
  )
665
556
 
666
557
  @staticmethod
@@ -690,8 +581,65 @@ class ctx:
690
581
  None
691
582
  """
692
583
 
693
- LoggerContext.log_debug(
584
+ ObservabilityContext.record_log(
585
+ ObservabilityLevel.DEBUG,
694
586
  message,
695
587
  *args,
696
588
  exception=exception,
697
589
  )
590
+
591
+ @staticmethod
592
+ def event(
593
+ event: State,
594
+ /,
595
+ *,
596
+ level: ObservabilityLevel = ObservabilityLevel.INFO,
597
+ ) -> None:
598
+ """
599
+ Record event within current scope context.
600
+
601
+ Parameters
602
+ ----------
603
+ event: State
604
+ contents of event to be recorded.
605
+
606
+ Returns
607
+ -------
608
+ None
609
+ """
610
+
611
+ ObservabilityContext.record_event(
612
+ event,
613
+ level=level,
614
+ )
615
+
616
+ @staticmethod
617
+ def metric(
618
+ metric: str,
619
+ /,
620
+ *,
621
+ value: float | int,
622
+ unit: str | None = None,
623
+ ) -> None:
624
+ """
625
+ Record metric within current scope context.
626
+
627
+ Parameters
628
+ ----------
629
+ metric: State
630
+ name of metric to be recorded.
631
+ value: float | int
632
+ value of metric to be recorded.
633
+ unit: str | None = None
634
+ unit of metric to be recorded.
635
+
636
+ Returns
637
+ -------
638
+ None
639
+ """
640
+
641
+ ObservabilityContext.record_metric(
642
+ metric,
643
+ value=value,
644
+ unit=unit,
645
+ )
@@ -7,10 +7,10 @@ from typing import Any, final
7
7
 
8
8
  from haiway.state import State
9
9
 
10
- __all__ = [
10
+ __all__ = (
11
11
  "Disposable",
12
12
  "Disposables",
13
- ]
13
+ )
14
14
 
15
15
  type Disposable = AbstractAsyncContextManager[Iterable[State] | State | None]
16
16
 
@@ -3,9 +3,7 @@ from types import TracebackType
3
3
  from typing import Any, Self, final
4
4
  from uuid import uuid4
5
5
 
6
- __all__ = [
7
- "ScopeIdentifier",
8
- ]
6
+ __all__ = ("ScopeIdentifier",)
9
7
 
10
8
 
11
9
  @final
@@ -33,10 +31,11 @@ class ScopeIdentifier:
33
31
  except LookupError:
34
32
  # create root scope when missing
35
33
  trace_id: str = uuid4().hex
34
+ scope_id: str = uuid4().hex
36
35
  return cls(
37
36
  label=label,
38
- scope_id=uuid4().hex,
39
- parent_id=trace_id, # trace_id is parent_id for root
37
+ scope_id=scope_id,
38
+ parent_id=scope_id, # own id is parent_id for root
40
39
  trace_id=trace_id,
41
40
  )
42
41