haiway 0.21.1__tar.gz → 0.21.2__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 (112) hide show
  1. haiway-0.21.2/CLAUDE.md +163 -0
  2. {haiway-0.21.1 → haiway-0.21.2}/PKG-INFO +1 -1
  3. {haiway-0.21.1 → haiway-0.21.2}/guidelines/llms.txt +30 -1
  4. haiway-0.21.2/guidelines/state.md +282 -0
  5. {haiway-0.21.1 → haiway-0.21.2}/junit/test-results.xml +1 -1
  6. {haiway-0.21.1 → haiway-0.21.2}/pyproject.toml +1 -1
  7. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/access.py +6 -6
  8. haiway-0.21.2/tests/test_state_validation.py +542 -0
  9. {haiway-0.21.1 → haiway-0.21.2}/uv.lock +1 -1
  10. {haiway-0.21.1 → haiway-0.21.2}/.github/workflows/ci.yml +0 -0
  11. {haiway-0.21.1 → haiway-0.21.2}/.github/workflows/publish.yml +0 -0
  12. {haiway-0.21.1 → haiway-0.21.2}/.gitignore +0 -0
  13. {haiway-0.21.1 → haiway-0.21.2}/LICENSE +0 -0
  14. {haiway-0.21.1 → haiway-0.21.2}/Makefile +0 -0
  15. {haiway-0.21.1 → haiway-0.21.2}/README.md +0 -0
  16. {haiway-0.21.1 → haiway-0.21.2}/config/pre-push +0 -0
  17. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/.dockerignore +0 -0
  18. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/Dockerfile +0 -0
  19. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/Makefile +0 -0
  20. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/README.md +0 -0
  21. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/config/.env.example +0 -0
  22. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/config/unit.json +0 -0
  23. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/docker-compose.yml +0 -0
  24. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/pyproject.toml +0 -0
  25. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/features/__int__.py +0 -0
  26. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/features/todos/__init__.py +0 -0
  27. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/features/todos/config.py +0 -0
  28. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/features/todos/state.py +0 -0
  29. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/features/todos/types.py +0 -0
  30. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/features/todos/user_tasks.py +0 -0
  31. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/integrations/__init__.py +0 -0
  32. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/integrations/postgres/__init__.py +0 -0
  33. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/integrations/postgres/client.py +0 -0
  34. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/integrations/postgres/config.py +0 -0
  35. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/integrations/postgres/state.py +0 -0
  36. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/integrations/postgres/types.py +0 -0
  37. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/migrations/__init__.py +0 -0
  38. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/migrations/__main__.py +0 -0
  39. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/migrations/postgres/__init__.py +0 -0
  40. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/migrations/postgres/execution.py +0 -0
  41. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/migrations/postgres/migration_0.py +0 -0
  42. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/migrations/postgres/types.py +0 -0
  43. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/__init__.py +0 -0
  44. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/__main__.py +0 -0
  45. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/application.py +0 -0
  46. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/config.py +0 -0
  47. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/middlewares/__init__.py +0 -0
  48. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/middlewares/context.py +0 -0
  49. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/routes/__init__.py +0 -0
  50. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/routes/technical.py +0 -0
  51. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/server/routes/todos.py +0 -0
  52. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/solutions/__init__.py +0 -0
  53. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/solutions/user_tasks/__init__.py +0 -0
  54. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/solutions/user_tasks/config.py +0 -0
  55. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/solutions/user_tasks/postgres.py +0 -0
  56. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/solutions/user_tasks/state.py +0 -0
  57. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/src/solutions/user_tasks/types.py +0 -0
  58. {haiway-0.21.1 → haiway-0.21.2}/examples/fastAPI/uv.lock +0 -0
  59. {haiway-0.21.1 → haiway-0.21.2}/guidelines/functionalities.md +0 -0
  60. {haiway-0.21.1 → haiway-0.21.2}/guidelines/packages.md +0 -0
  61. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/__init__.py +0 -0
  62. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/__init__.py +0 -0
  63. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/disposables.py +0 -0
  64. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/identifier.py +0 -0
  65. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/observability.py +0 -0
  66. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/state.py +0 -0
  67. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/tasks.py +0 -0
  68. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/context/types.py +0 -0
  69. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/__init__.py +0 -0
  70. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/asynchrony.py +0 -0
  71. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/caching.py +0 -0
  72. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/concurrent.py +0 -0
  73. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/observability.py +0 -0
  74. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/retries.py +0 -0
  75. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/throttling.py +0 -0
  76. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/timeouted.py +0 -0
  77. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/helpers/tracing.py +0 -0
  78. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/opentelemetry/__init__.py +0 -0
  79. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/opentelemetry/observability.py +0 -0
  80. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/py.typed +0 -0
  81. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/state/__init__.py +0 -0
  82. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/state/attributes.py +0 -0
  83. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/state/path.py +0 -0
  84. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/state/requirement.py +0 -0
  85. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/state/structure.py +0 -0
  86. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/state/validation.py +0 -0
  87. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/types/__init__.py +0 -0
  88. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/types/default.py +0 -0
  89. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/types/missing.py +0 -0
  90. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/__init__.py +0 -0
  91. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/always.py +0 -0
  92. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/collections.py +0 -0
  93. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/env.py +0 -0
  94. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/formatting.py +0 -0
  95. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/freezing.py +0 -0
  96. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/logs.py +0 -0
  97. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/mimic.py +0 -0
  98. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/noop.py +0 -0
  99. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/queue.py +0 -0
  100. {haiway-0.21.1 → haiway-0.21.2}/src/haiway/utils/stream.py +0 -0
  101. {haiway-0.21.1 → haiway-0.21.2}/tests/__init__.py +0 -0
  102. {haiway-0.21.1 → haiway-0.21.2}/tests/test_async_queue.py +0 -0
  103. {haiway-0.21.1 → haiway-0.21.2}/tests/test_async_stream.py +0 -0
  104. {haiway-0.21.1 → haiway-0.21.2}/tests/test_attribute_path.py +0 -0
  105. {haiway-0.21.1 → haiway-0.21.2}/tests/test_attribute_requirement.py +0 -0
  106. {haiway-0.21.1 → haiway-0.21.2}/tests/test_auto_retry.py +0 -0
  107. {haiway-0.21.1 → haiway-0.21.2}/tests/test_cache.py +0 -0
  108. {haiway-0.21.1 → haiway-0.21.2}/tests/test_context.py +0 -0
  109. {haiway-0.21.1 → haiway-0.21.2}/tests/test_process_concurrently.py +0 -0
  110. {haiway-0.21.1 → haiway-0.21.2}/tests/test_state.py +0 -0
  111. {haiway-0.21.1 → haiway-0.21.2}/tests/test_streaming.py +0 -0
  112. {haiway-0.21.1 → haiway-0.21.2}/tests/test_timeout.py +0 -0
@@ -0,0 +1,163 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Development Commands
6
+
7
+ ```bash
8
+ # Setup development environment
9
+ make venv
10
+ . ./.venv/bin/activate
11
+
12
+ # Sync dependencies
13
+ make sync
14
+
15
+ # Update dependencies
16
+ make update
17
+
18
+ # Run formatter - make sure to run with activated venv
19
+ make format
20
+
21
+ # Run linters and type checker - make sure to run with activated venv
22
+ make lint
23
+
24
+ # Run test suite - make sure to run with activated venv
25
+ make test
26
+
27
+ # Run a single test - make sure to run with activated venv
28
+ python -B -m pytest -v tests/test_file.py::test_function
29
+ ```
30
+
31
+ ## Architecture Overview
32
+
33
+ Haiway is a Python framework (3.12+) designed for functional programming with structured concurrency. It focuses on:
34
+
35
+ 1. **Immutable State Management**: Using State classes for type-safe, immutable data structures
36
+ 2. **Context-based Dependency Injection**: Propagating state through execution contexts
37
+ 3. **Functional Approach**: Emphasizing pure functions over objects with methods
38
+
39
+ ### Core Components
40
+
41
+ - **Context System** (`haiway.context`): Provides scoped execution environments with access to state
42
+ - **State Management** (`haiway.state`): Immutable data structures with validation
43
+ - **Helpers** (`haiway.helpers`): Utilities for async operations, caching, retries, etc.
44
+ - **Types** (`haiway.types`): Base type definitions
45
+
46
+ ## Development Patterns
47
+
48
+ ### Defining Types
49
+
50
+ ```python
51
+ from typing import Protocol, runtime_checkable
52
+ from haiway import State
53
+
54
+ # Data structure
55
+ class UserData(State):
56
+ id: str
57
+ name: str
58
+ email: str | None = None
59
+
60
+ # Function interface
61
+ @runtime_checkable
62
+ class UserFetching(Protocol):
63
+ async def __call__(self, id: str) -> UserData: ...
64
+ ```
65
+
66
+ ### Managing State
67
+
68
+ ```python
69
+ from haiway import State, ctx
70
+ from .types import UserFetching, UserData
71
+
72
+ # Configuration state
73
+ class UserServiceConfig(State):
74
+ api_url: str = "https://api.example.com"
75
+ timeout_seconds: int = 30
76
+
77
+ # Functionality container
78
+ class UserService(State):
79
+ # Function implementations
80
+ fetching: UserFetching
81
+
82
+ # Class method interface
83
+ @classmethod
84
+ async def fetch_user(cls, id: str) -> UserData:
85
+ return await ctx.state(cls).fetching(id)
86
+ ```
87
+
88
+ ### Implementation
89
+
90
+ ```python
91
+ from haiway import ctx
92
+ from .types import UserData
93
+ from .state import UserService, UserServiceConfig
94
+
95
+ # Concrete implementation
96
+ async def http_user_fetching(id: str) -> UserData:
97
+ config = ctx.state(UserServiceConfig)
98
+ # Implementation using config.api_url
99
+ return UserData(id=id, name="Example User")
100
+
101
+ # Factory function
102
+ def http_user_service() -> UserService:
103
+ return UserService(fetching=http_user_fetching)
104
+ ```
105
+
106
+ ### Context Usage
107
+
108
+ ```python
109
+ from haiway import ctx
110
+ from .implementation import http_user_service
111
+ from .state import UserServiceConfig
112
+
113
+ async def main():
114
+ # Set up execution context
115
+ async with ctx.scope(
116
+ "main",
117
+ http_user_service(),
118
+ UserServiceConfig(api_url="https://custom-api.example.com")
119
+ ):
120
+ # Use functionality through class methods
121
+ user = await UserService.fetch_user("user-123")
122
+ ```
123
+
124
+ ## Common Patterns
125
+
126
+ 1. **Immutable Updates**:
127
+ ```python
128
+ # Create new instance with updated values
129
+ updated_config = config.updated(api_url="https://new-api.example.com")
130
+ ```
131
+
132
+ 2. **Context Access**:
133
+ ```python
134
+ # Get state from current context
135
+ config = ctx.state(ConfigType)
136
+ ```
137
+
138
+ 3. **Disposable Resources**:
139
+ ```python
140
+ # Resources that need cleanup
141
+ async def create_resource() -> tuple[Resource, Disposable]:
142
+ resource = Resource()
143
+ async def cleanup(): await resource.close()
144
+ return resource, ctx.disposable(cleanup)
145
+ ```
146
+
147
+ ## Testing Guidelines
148
+
149
+ - Tests use pytest with pytest-asyncio
150
+ - Mock context values with `ctx.updated`
151
+ - For async tests, use the `@pytest.mark.asyncio` decorator
152
+ - Immutable state makes testing simpler - no need to reset between tests
153
+
154
+ ```python
155
+ import pytest
156
+ from haiway import ctx
157
+
158
+ @pytest.mark.asyncio
159
+ async def test_functionality():
160
+ async with ctx.scope("test", TestState(value="test")):
161
+ result = await some_function()
162
+ assert result == expected_result
163
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiway
3
- Version: 0.21.1
3
+ Version: 0.21.2
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
@@ -31,13 +31,17 @@ Define interfaces using Protocol and data using State:
31
31
 
32
32
  ```python
33
33
  from typing import Protocol, Any, runtime_checkable
34
+ from collections.abc import Sequence, Mapping, Set
34
35
  from haiway import State
35
36
 
36
- # Data structure
37
+ # Data structure - use abstract collection types
37
38
  class UserData(State):
38
39
  id: str
39
40
  name: str
40
41
  email: str | None = None
42
+ tags: Sequence[str] = () # Not list[str] - becomes tuple
43
+ metadata: Mapping[str, str] = {} # Not dict[str, str] - stays dict
44
+ roles: Set[str] = frozenset() # Not set[str] - becomes frozenset
41
45
 
42
46
  # Function interface
43
47
  @runtime_checkable
@@ -132,6 +136,7 @@ async with ctx.scope("dev-context", dev_config):
132
136
 
133
137
  ```python
134
138
  from typing import Protocol, Any, runtime_checkable
139
+ from collections.abc import Sequence
135
140
  from uuid import UUID, uuid4
136
141
  from datetime import datetime
137
142
  from haiway import State, ctx
@@ -142,6 +147,7 @@ class Note(State):
142
147
  content: str
143
148
  created_at: datetime
144
149
  updated_at: datetime
150
+ tags: Sequence[str] = () # Use Sequence, not list
145
151
 
146
152
  @runtime_checkable
147
153
  class NoteCreating(Protocol):
@@ -232,6 +238,8 @@ async def run_notes_app():
232
238
  - Keep states immutable
233
239
  - Use .updated() for state variants
234
240
  - Define defaults for optional values
241
+ - Use abstract collection types: Sequence[T] not list[T], Mapping[K,V] not dict[K,V], Set[T] not set[T]
242
+ - Lists become tuples, sets become frozensets for immutability
235
243
 
236
244
  3. **Function Implementations**
237
245
  - Access context through ctx.state()
@@ -257,3 +265,24 @@ async def run_notes_app():
257
265
  - Define custom error types in types.py
258
266
  - Handle errors explicitly at appropriate levels
259
267
  - Maintain immutability in error cases
268
+
269
+ ## State Type Validation
270
+
271
+ State classes validate all supported Python types:
272
+
273
+ - **Basic Types**: int, str, bool, float, bytes, None
274
+ - **Collection Types**:
275
+ - Sequence[T] (converts lists to tuples)
276
+ - Mapping[K,V] (keeps as dict)
277
+ - Set[T] (converts to frozenset)
278
+ - tuple[T, ...] (fixed/variable length)
279
+ - **Special Types**: UUID, datetime, date, time, timedelta, timezone, Path, re.Pattern
280
+ - **Union Types**: str | None, int | float
281
+ - **Literal Types**: Literal["a", "b", "c"]
282
+ - **Enum Types**: Standard Enum and StrEnum classes
283
+ - **Callable Types**: Function types and Protocol interfaces
284
+ - **TypedDict**: Structure validation with Required/NotRequired
285
+ - **Nested State**: Recursive validation including generics
286
+ - **Any Type**: Accepts any value without validation
287
+
288
+ **Critical Rule**: Always use abstract collection types (Sequence, Mapping, Set) instead of concrete types (list, dict, set) to ensure immutability and proper validation.
@@ -0,0 +1,282 @@
1
+ ## State Management
2
+
3
+ Haiway's state management system is built around the `State` class, which provides immutable, type-safe data structures with validation. Unlike traditional mutable objects, State instances cannot be modified after creation, ensuring predictable behavior, especially in concurrent environments. This guide explains how to effectively use the State class to manage your application's data.
4
+
5
+ ### Defining State Classes
6
+
7
+ State classes are defined by subclassing the `State` base class and declaring typed attributes:
8
+
9
+ ```python
10
+ from haiway import State
11
+ from uuid import UUID
12
+ from datetime import datetime
13
+
14
+ class User(State):
15
+ id: UUID
16
+ name: str
17
+ email: str | None = None
18
+ created_at: datetime
19
+ ```
20
+
21
+ Key features of State class definitions:
22
+
23
+ - **Type Annotations**: All attributes must have type annotations
24
+ - **Optional Attributes**: Provide default values for optional attributes
25
+ - **Immutability**: All instances are immutable once created
26
+ - **Validation**: Values are validated against their type annotations at creation time
27
+
28
+ ### Creating State Instances
29
+
30
+ State instances are created using standard constructor syntax:
31
+
32
+ ```python
33
+ from uuid import uuid4
34
+ from datetime import datetime
35
+
36
+ user = User(
37
+ id=uuid4(),
38
+ name="Alice Smith",
39
+ created_at=datetime.now()
40
+ )
41
+ ```
42
+
43
+ All required attributes must be provided, and all values are validated against their type annotations. If a value fails validation, an exception will be raised.
44
+
45
+ ### Immutability and Updates
46
+
47
+ State instances are immutable, so you cannot modify them directly:
48
+
49
+ ```python
50
+ user.name = "Bob" # Raises AttributeError
51
+ ```
52
+
53
+ Instead, create new instances with updated values using the `updated` method:
54
+
55
+ ```python
56
+ updated_user = user.updated(name="Bob Smith")
57
+ ```
58
+
59
+ This creates a new instance with the updated value, leaving the original instance unchanged. The `updated` method accepts keyword arguments for any attributes you want to change.
60
+
61
+ ### Path-Based Updates
62
+
63
+ For nested updates, you can use the path-based `updating` method:
64
+
65
+ ```python
66
+ class Address(State):
67
+ street: str
68
+ city: str
69
+ postal_code: str
70
+
71
+ class Contact(State):
72
+ name: str
73
+ address: Address
74
+
75
+ # Create an instance
76
+ contact = Contact(
77
+ name="Alice",
78
+ address=Address(
79
+ street="123 Main St",
80
+ city="Springfield",
81
+ postal_code="12345"
82
+ )
83
+ )
84
+
85
+ # Update a nested value using path syntax
86
+ updated_contact = contact.updating(Contact._.address.city, "New City")
87
+ ```
88
+
89
+ The `Class._.attribute` syntax creates an `AttributePath` that can be used to update nested attributes.
90
+
91
+ ### Generic State Classes
92
+
93
+ State supports generic type parameters, allowing you to create reusable containers:
94
+
95
+ ```python
96
+ from typing import Generic, TypeVar
97
+
98
+ T = TypeVar('T')
99
+
100
+ class Container(State, Generic[T]):
101
+ value: T
102
+
103
+ # Create specialized instances
104
+ int_container = Container[int](value=42)
105
+ str_container = Container[str](value="hello")
106
+ ```
107
+
108
+ The type parameter is enforced during validation:
109
+
110
+ ```python
111
+ int_container.updated(value="string") # Raises TypeError
112
+ ```
113
+
114
+ ### Conversion to Dictionary
115
+
116
+ You can convert a State instance to a dictionary using the `to_mapping` method:
117
+
118
+ ```python
119
+ user_dict = user.to_mapping()
120
+ # {"id": UUID('...'), "name": "Alice Smith", "email": None, "created_at": datetime(...)}
121
+
122
+ # For nested conversion
123
+ user_dict = user.to_mapping(recursive=True)
124
+ ```
125
+
126
+ This is useful for serialization or when you need to work with plain dictionaries.
127
+
128
+ ### Type Validation
129
+
130
+ State classes perform thorough type validation for all supported Python types:
131
+
132
+ - **Basic Types**: int, str, bool, float, bytes
133
+ - **Container Types**:
134
+ - **Sequence[T]**: Use `Sequence[T]` instead of `list[T]` - converted to immutable tuples
135
+ - **Mapping[K, V]**: Use `Mapping[K, V]` instead of `dict[K, V]` - remains as dict
136
+ - **Set[T]**: Use `Set[T]` instead of `set[T]` - converted to immutable frozensets
137
+ - **tuple[T, ...]**: Fixed or variable-length tuples
138
+ - **Special Types**: UUID, datetime, date, time, timedelta, timezone, Path, re.Pattern
139
+ - **Union Types**: str | None, int | float
140
+ - **Literal Types**: Literal["a", "b", "c"]
141
+ - **Enum Types**: Standard Enum and StrEnum classes
142
+ - **Callable Types**: Function types and Protocol interfaces
143
+ - **TypedDict**: Validates structure with Required/NotRequired fields
144
+ - **Nested State Classes**: Validates recursively including generic State types
145
+ - **Any Type**: Accepts any value without validation
146
+
147
+ #### Important Typing Requirements
148
+
149
+ **Always use abstract collection types instead of concrete types:**
150
+
151
+ ```python
152
+ # ✅ Correct - Use abstract types
153
+ from collections.abc import Sequence, Mapping, Set
154
+
155
+ class Config(State):
156
+ items: Sequence[str] # Not list[str]
157
+ data: Mapping[str, int] # Not dict[str, int]
158
+ tags: Set[str] # Not set[str]
159
+
160
+ # ✅ Lists are converted to tuples (immutable)
161
+ config = Config(
162
+ items=["a", "b", "c"], # Becomes ("a", "b", "c")
163
+ data={"key": 1}, # Remains {"key": 1}
164
+ tags={"tag1", "tag2"} # Becomes frozenset({"tag1", "tag2"})
165
+ )
166
+
167
+ # ❌ Incorrect - Don't use concrete types
168
+ class BadConfig(State):
169
+ items: list[str] # Will cause validation errors
170
+ data: dict[str, int] # Will cause validation errors
171
+ tags: set[str] # Will cause validation errors
172
+ ```
173
+
174
+ This requirement ensures immutability and type safety within the State system.
175
+
176
+ ### Best Practices
177
+
178
+ 1. **Use Immutability**: Embrace the immutable nature of State - never try to modify instances.
179
+ 2. **Make Small States**: Keep State classes focused on a single concern.
180
+ 3. **Provide Defaults**: Use default values for optional attributes to make creation easier.
181
+ 4. **Use Type Annotations**: Always provide accurate type annotations for all attributes.
182
+ 5. **Consistent Updates**: Always use `updated` or `updating` methods for changes.
183
+ 6. **Composition**: Compose complex states from simpler ones.
184
+
185
+ ### Example: Complex State Management
186
+
187
+ Here's a more complete example showing complex state management:
188
+
189
+ ```python
190
+ from haiway import State
191
+ from uuid import UUID, uuid4
192
+ from datetime import datetime
193
+ from collections.abc import Sequence
194
+
195
+ class Address(State):
196
+ street: str
197
+ city: str
198
+ country: str = "USA"
199
+
200
+ class Contact(State):
201
+ email: str
202
+ phone: str | None = None
203
+
204
+ class User(State):
205
+ id: UUID
206
+ name: str
207
+ address: Address
208
+ contact: Contact
209
+ roles: Sequence[str] = ()
210
+ active: bool = True
211
+ created_at: datetime
212
+ updated_at: datetime
213
+
214
+ # Create an instance
215
+ user = User(
216
+ id=uuid4(),
217
+ name="Alice Smith",
218
+ address=Address(
219
+ street="123 Main St",
220
+ city="Springfield",
221
+ ),
222
+ contact=Contact(
223
+ email="alice@example.com",
224
+ ),
225
+ created_at=datetime.now(),
226
+ updated_at=datetime.now(),
227
+ )
228
+
229
+ # Update a simple attribute
230
+ user1 = user.updated(name="Alice Johnson")
231
+
232
+ # Update a nested attribute
233
+ user2 = user.updating(User._.address.city, "New City")
234
+
235
+ # Update multiple attributes
236
+ user3 = user.updated(
237
+ active=False,
238
+ updated_at=datetime.now(),
239
+ )
240
+
241
+ # Update a nested attribute directly
242
+ new_address = user.address.updated(street="456 Oak Ave")
243
+ user4 = user.updated(address=new_address)
244
+
245
+ # Chain updates
246
+ user5 = user.updated(name="Bob").updating(User._.contact.phone, "555-1234")
247
+ ```
248
+
249
+ ### Performance Considerations
250
+
251
+ While State instances are immutable, creating new instances for updates has minimal overhead as only the changed paths are reconstructed. The validation system is optimized to be fast for typical use cases.
252
+
253
+ For high-performance scenarios:
254
+ - Keep State classes relatively small and focused
255
+ - Consider using path-based updates for nested changes
256
+ - If needed, batch multiple updates into a single `updated` call
257
+
258
+ ### Integration with Haiway Context
259
+
260
+ State classes are designed to work seamlessly with Haiway's context system:
261
+
262
+ ```python
263
+ from haiway import ctx, State
264
+
265
+ class AppConfig(State):
266
+ debug: bool = False
267
+ log_level: str = "INFO"
268
+
269
+ async def main():
270
+ # Provide state to context
271
+ async with ctx.scope("main", AppConfig(debug=True)):
272
+ # Access state from context
273
+ config = ctx.state(AppConfig)
274
+
275
+ # Create updated state in nested context
276
+ async with ctx.scope("debug", config.updated(log_level="DEBUG")):
277
+ # Use updated state
278
+ debug_config = ctx.state(AppConfig)
279
+ assert debug_config.log_level == "DEBUG"
280
+ ```
281
+
282
+ This pattern enables effective dependency injection and state propagation throughout your application.
@@ -1 +1 @@
1
- <?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="105" time="1.822" timestamp="2025-05-22T14:52:29.443450+00:00" hostname="pkrvmf6wy0o8zjz"><testcase classname="tests.test_async_queue" name="test_fails_when_stream_fails" time="0.002" /><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.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_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.001" /><testcase classname="tests.test_state" name="test_nested_initializes_with_proper_arguments" time="0.001" /><testcase classname="tests.test_state" name="test_dict_skips_missing_properties" time="0.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>
1
+ <?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="126" time="1.863" timestamp="2025-05-23T15:40:02.453645+00:00" hostname="pkrvmf6wy0o8zjz"><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.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_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.201" /><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.001" /><testcase classname="tests.test_state" name="test_nested_initializes_with_proper_arguments" time="0.001" /><testcase classname="tests.test_state" name="test_dict_skips_missing_properties" time="0.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.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.000" /><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.21.1"
8
+ version = "0.21.2"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -302,7 +302,7 @@ class ctx:
302
302
  def scope(
303
303
  label: str,
304
304
  /,
305
- *state: State,
305
+ *state: State | None,
306
306
  disposables: Disposables | Iterable[Disposable] | None = None,
307
307
  task_group: TaskGroup | None = None,
308
308
  observability: Observability | Logger | None = None,
@@ -316,7 +316,7 @@ class ctx:
316
316
  label: str
317
317
  name of the scope context
318
318
 
319
- *state: State | Disposable
319
+ *state: State | None
320
320
  state propagated within the scope context, will be merged with current state by\
321
321
  replacing current with provided on conflict.
322
322
 
@@ -355,14 +355,14 @@ class ctx:
355
355
  return ScopeContext(
356
356
  label=label,
357
357
  task_group=task_group,
358
- state=state,
358
+ state=tuple(element for element in state if element is not None),
359
359
  disposables=resolved_disposables,
360
360
  observability=observability,
361
361
  )
362
362
 
363
363
  @staticmethod
364
364
  def updated(
365
- *state: State,
365
+ *state: State | None,
366
366
  ) -> StateContext:
367
367
  """
368
368
  Update scope context with given state. When called within an existing context\
@@ -370,7 +370,7 @@ class ctx:
370
370
 
371
371
  Parameters
372
372
  ----------
373
- *state: State
373
+ *state: State | None
374
374
  state propagated within the updated scope context, will be merged with current if any\
375
375
  by replacing current with provided on conflict
376
376
 
@@ -380,7 +380,7 @@ class ctx:
380
380
  state part of context object intended to enter context manager with it
381
381
  """
382
382
 
383
- return StateContext.updated(state)
383
+ return StateContext.updated(element for element in state if element is not None)
384
384
 
385
385
  @staticmethod
386
386
  def spawn[Result, **Arguments](