haiway 0.20.1__tar.gz → 0.21.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. {haiway-0.20.1 → haiway-0.21.1}/Makefile +1 -1
  2. {haiway-0.20.1 → haiway-0.21.1}/PKG-INFO +8 -4
  3. {haiway-0.20.1 → haiway-0.21.1}/README.md +4 -0
  4. {haiway-0.20.1 → haiway-0.21.1}/guidelines/functionalities.md +46 -80
  5. haiway-0.21.1/guidelines/llms.txt +259 -0
  6. {haiway-0.20.1 → haiway-0.21.1}/guidelines/packages.md +37 -40
  7. haiway-0.21.1/junit/test-results.xml +1 -0
  8. {haiway-0.20.1 → haiway-0.21.1}/pyproject.toml +4 -4
  9. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/__init__.py +0 -22
  10. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/__init__.py +1 -2
  11. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/asynchrony.py +22 -121
  12. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/throttling.py +33 -9
  13. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/state/requirement.py +1 -1
  14. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/types/__init__.py +0 -2
  15. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/mimic.py +2 -2
  16. haiway-0.21.1/tests/test_attribute_requirement.py +121 -0
  17. {haiway-0.20.1 → haiway-0.21.1}/tests/test_state.py +2 -2
  18. {haiway-0.20.1 → haiway-0.21.1}/uv.lock +201 -201
  19. haiway-0.20.1/junit/test-results.xml +0 -1
  20. haiway-0.20.1/src/haiway/types/frozen.py +0 -21
  21. {haiway-0.20.1 → haiway-0.21.1}/.github/workflows/ci.yml +0 -0
  22. {haiway-0.20.1 → haiway-0.21.1}/.github/workflows/publish.yml +0 -0
  23. {haiway-0.20.1 → haiway-0.21.1}/.gitignore +0 -0
  24. {haiway-0.20.1 → haiway-0.21.1}/LICENSE +0 -0
  25. {haiway-0.20.1 → haiway-0.21.1}/config/pre-push +0 -0
  26. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/.dockerignore +0 -0
  27. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/Dockerfile +0 -0
  28. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/Makefile +0 -0
  29. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/README.md +0 -0
  30. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/config/.env.example +0 -0
  31. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/config/unit.json +0 -0
  32. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/docker-compose.yml +0 -0
  33. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/pyproject.toml +0 -0
  34. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/features/__int__.py +0 -0
  35. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/features/todos/__init__.py +0 -0
  36. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/features/todos/config.py +0 -0
  37. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/features/todos/state.py +0 -0
  38. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/features/todos/types.py +0 -0
  39. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/features/todos/user_tasks.py +0 -0
  40. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/integrations/__init__.py +0 -0
  41. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/integrations/postgres/__init__.py +0 -0
  42. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/integrations/postgres/client.py +0 -0
  43. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/integrations/postgres/config.py +0 -0
  44. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/integrations/postgres/state.py +0 -0
  45. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/integrations/postgres/types.py +0 -0
  46. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/migrations/__init__.py +0 -0
  47. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/migrations/__main__.py +0 -0
  48. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/migrations/postgres/__init__.py +0 -0
  49. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/migrations/postgres/execution.py +0 -0
  50. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/migrations/postgres/migration_0.py +0 -0
  51. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/migrations/postgres/types.py +0 -0
  52. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/__init__.py +0 -0
  53. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/__main__.py +0 -0
  54. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/application.py +0 -0
  55. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/config.py +0 -0
  56. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/middlewares/__init__.py +0 -0
  57. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/middlewares/context.py +0 -0
  58. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/routes/__init__.py +0 -0
  59. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/routes/technical.py +0 -0
  60. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/server/routes/todos.py +0 -0
  61. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/solutions/__init__.py +0 -0
  62. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/solutions/user_tasks/__init__.py +0 -0
  63. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/solutions/user_tasks/config.py +0 -0
  64. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/solutions/user_tasks/postgres.py +0 -0
  65. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/solutions/user_tasks/state.py +0 -0
  66. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/src/solutions/user_tasks/types.py +0 -0
  67. {haiway-0.20.1 → haiway-0.21.1}/examples/fastAPI/uv.lock +0 -0
  68. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/__init__.py +0 -0
  69. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/access.py +0 -0
  70. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/disposables.py +0 -0
  71. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/identifier.py +0 -0
  72. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/observability.py +0 -0
  73. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/state.py +0 -0
  74. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/tasks.py +0 -0
  75. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/context/types.py +0 -0
  76. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/caching.py +0 -0
  77. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/concurrent.py +0 -0
  78. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/observability.py +0 -0
  79. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/retries.py +0 -0
  80. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/timeouted.py +0 -0
  81. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/helpers/tracing.py +0 -0
  82. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/opentelemetry/__init__.py +0 -0
  83. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/opentelemetry/observability.py +0 -0
  84. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/py.typed +0 -0
  85. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/state/__init__.py +0 -0
  86. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/state/attributes.py +0 -0
  87. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/state/path.py +0 -0
  88. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/state/structure.py +0 -0
  89. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/state/validation.py +0 -0
  90. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/types/default.py +0 -0
  91. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/types/missing.py +0 -0
  92. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/__init__.py +0 -0
  93. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/always.py +0 -0
  94. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/collections.py +0 -0
  95. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/env.py +0 -0
  96. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/formatting.py +0 -0
  97. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/freezing.py +0 -0
  98. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/logs.py +0 -0
  99. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/noop.py +0 -0
  100. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/queue.py +0 -0
  101. {haiway-0.20.1 → haiway-0.21.1}/src/haiway/utils/stream.py +0 -0
  102. {haiway-0.20.1 → haiway-0.21.1}/tests/__init__.py +0 -0
  103. {haiway-0.20.1 → haiway-0.21.1}/tests/test_async_queue.py +0 -0
  104. {haiway-0.20.1 → haiway-0.21.1}/tests/test_async_stream.py +0 -0
  105. {haiway-0.20.1 → haiway-0.21.1}/tests/test_attribute_path.py +0 -0
  106. {haiway-0.20.1 → haiway-0.21.1}/tests/test_auto_retry.py +0 -0
  107. {haiway-0.20.1 → haiway-0.21.1}/tests/test_cache.py +0 -0
  108. {haiway-0.20.1 → haiway-0.21.1}/tests/test_context.py +0 -0
  109. {haiway-0.20.1 → haiway-0.21.1}/tests/test_process_concurrently.py +0 -0
  110. {haiway-0.20.1 → haiway-0.21.1}/tests/test_streaming.py +0 -0
  111. {haiway-0.20.1 → haiway-0.21.1}/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.7.5
13
+ UV_VERSION := 0.7.6
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.20.1
3
+ Version: 0.21.1
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Project-URL: Homepage, https://miquido.com
6
6
  Project-URL: Repository, https://github.com/miquido/haiway.git
@@ -42,13 +42,17 @@ Requires-Dist: pytest-cov~=6.1; extra == 'dev'
42
42
  Requires-Dist: pytest~=8.3; extra == 'dev'
43
43
  Requires-Dist: ruff~=0.11; extra == 'dev'
44
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'
45
+ Requires-Dist: opentelemetry-api~=1.33; extra == 'opentelemetry'
46
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc~=1.33; extra == 'opentelemetry'
47
+ Requires-Dist: opentelemetry-sdk~=1.33; extra == 'opentelemetry'
48
48
  Description-Content-Type: text/markdown
49
49
 
50
50
  # 🚗 haiway 🚕 🚚 🚙
51
51
 
52
+ ![Python Version](https://img.shields.io/badge/Python-3.12+-blue)
53
+ ![License](https://img.shields.io/badge/License-MIT-green)
54
+ ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/miquido/haiway?utm_source=oss&utm_medium=github&utm_campaign=miquido%2Fhaiway&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)
55
+
52
56
  haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
53
57
 
54
58
  ## 🖥️ Install
@@ -1,5 +1,9 @@
1
1
  # 🚗 haiway 🚕 🚚 🚙
2
2
 
3
+ ![Python Version](https://img.shields.io/badge/Python-3.12+-blue)
4
+ ![License](https://img.shields.io/badge/License-MIT-green)
5
+ ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/miquido/haiway?utm_source=oss&utm_medium=github&utm_campaign=miquido%2Fhaiway&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)
6
+
3
7
  haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
4
8
 
5
9
  ## 🖥️ Install
@@ -1,25 +1,25 @@
1
1
  ## Functionalities
2
2
 
3
- haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
3
+ Haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, Haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context variables, Haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
4
4
 
5
- ### Functional basics
5
+ ### Functional Basics
6
6
 
7
- Functional programming centers around creating pure functions - functions that have no side effects and rely solely on their inputs to produce outputs. This approach promotes predictability, easier testing, and better modularity. While Python is inherently multi-paradigm and not strictly functional, haiway encourages adopting functional principles where feasible to enhance code clarity and reliability.
7
+ Functional programming centers around creating pure functions - functions that have no side effects and rely solely on their inputs to produce outputs. This approach promotes predictability, easier testing, and better modularity. While Python is inherently multi-paradigm and not strictly functional, Haiway encourages adopting functional principles where feasible to enhance code clarity and reliability.
8
8
 
9
9
  Key functional concepts:
10
10
  - Immutability: Data structures are immutable, preventing unintended side effects.
11
11
  - Pure Functions: Functions depend only on their inputs and produce outputs without altering external state.
12
12
  - Higher-Order Functions: Functions that can take other functions as arguments or return them as results.
13
13
 
14
- haiway balances functional purity with Pythons flexibility by allowing limited side effects when necessary, though minimizing them is recommended for maintainability.
14
+ Haiway balances functional purity with Python's flexibility by allowing limited side effects when necessary, though minimizing them is recommended for maintainability.
15
15
 
16
- Instead of preparing objects with internal state and methods, haiway encourages creating structures containing sets of functions and providing state either through function arguments or contextually using execution scope state. Using explicit function arguments is the preferred method; however, some functionalities may benefit from contextual, broader accessible state.
16
+ Instead of preparing objects with internal state and methods, Haiway encourages creating structures containing sets of functions and providing state either through function arguments or contextually using execution scope state. Using explicit function arguments is the preferred method; however, some functionalities may benefit from contextual, broader accessible state.
17
17
 
18
- ### Preparing functionalities
18
+ ### Preparing Functionalities
19
19
 
20
- In haiway, functionalities are modularized into two primary components: interfaces and implementations This separation ensures clear contracts for functionalities, promoting modularity and ease of testing.
20
+ In Haiway, functionalities are modularized into two primary components: interfaces and implementations. This separation ensures clear contracts for functionalities, promoting modularity and ease of testing.
21
21
 
22
- ### Defining types
22
+ ### Defining Types
23
23
 
24
24
  Interfaces define the public API of a functionality, specifying the data types and functions it exposes without detailing the underlying implementation. Preparing functionality starts from defining associated types - data structures and function types.
25
25
 
@@ -40,9 +40,9 @@ class FunctionSignature(Protocol):
40
40
 
41
41
  In the example above, typing.Protocol is used to fully define the function signature, along with a custom structure serving as its argument. Function type names should emphasize the nature of their operations by using continuous tense adjectives, such as 'ElementCreating' or 'ValueLoading.'
42
42
 
43
- ### Defining state
43
+ ### Defining State
44
44
 
45
- State represents the immutable data required by functionalities. It is propagated through contexts to maintain consistency and support dependency injection. haiway comes with a helpful base class `State` which utilizes dataclass-like transform combined with runtime type checking and immutability.
45
+ State represents the immutable data required by functionalities. It is propagated through contexts to maintain consistency and support dependency injection. Haiway comes with a helpful base class `State` which utilizes dataclass-like transform combined with runtime type checking and immutability.
46
46
 
47
47
  ```python
48
48
  # state.py
@@ -60,7 +60,7 @@ class Functionality(State):
60
60
 
61
61
  This example shows a state required by the functionality as well as a container for holding function implementations. Both are intended to be propagated contextually to be accessible throughout the application and possibly altered when needed.
62
62
 
63
- ### Defining implementation
63
+ ### Defining Implementation
64
64
 
65
65
  Implementations provide concrete behavior for the defined interfaces, ensuring that they conform to the specified contracts.
66
66
 
@@ -81,34 +81,15 @@ async def function_implementation(argument: FunctionArgument) -> None:
81
81
  # Factory function to instantiate the Functionality with its implementation
82
82
  def functionality_implementation() -> Functionality:
83
83
  return Functionality(function=function_implementation)
84
-
85
84
  ```
86
85
 
87
- In the example above, function_implementation is the concrete implementation of the previously declared function, and functionality_implementation is a factory method suitable for creating a full implementation of the Functionality.
86
+ In the example above, function_implementation is a concrete implementation of the previously declared function, and functionality_implementation is a factory method suitable for creating a full implementation of the Functionality.
88
87
 
89
- Alternatively, instead of providing a factory method, some implementations may allow to define default values within state. This approach is also valid to implement and allows to skip explicit definitions of state by leveraging automatically created defaults.
88
+ Alternatively, instead of providing a factory method, some implementations may allow defining default values within state. This approach is also valid to implement and allows skipping explicit definitions of state by leveraging automatically created defaults.
90
89
 
91
- ### Defining calls
90
+ ### Classmethod Calls
92
91
 
93
- Calls act as intermediaries that invoke the function implementations within the appropriate context. This abstraction simplifies access to functionalities by hiding non-essential details and access to various required components.
94
-
95
- ```python
96
- # calls.py
97
- from my_functionality.types import FunctionArgument
98
- from my_functionality.state import FunctionalityState, Functionality
99
- from haiway import ctx
100
-
101
- # Call function that invokes the functionality within the current context
102
- async def function_call(argument: FunctionArgument) -> None:
103
- # Invoke the function implementation from the contextual state
104
- await ctx.state(Functionality).function(argument=argument)
105
- ```
106
-
107
- The example above shows a simple proxy call that accesses the required contextual details of the implementation.
108
-
109
- ### Classmethod calls
110
-
111
- When possible, preferred way of defining calls is to put them within the functionality state type as class methods. This approach allows easier access to desired functions and improves egonomy over the free functions access.
92
+ Calls act as intermediaries that invoke the function implementations within the appropriate context. This abstraction simplifies access to functionalities by hiding non-essential details and access to various required components. When possible, the preferred way of defining calls is to put them within the functionality state type as class methods. This approach allows easier access to desired functions and improves ergonomics over the free functions access.
112
93
 
113
94
  ```python
114
95
  # state.py - revisited
@@ -116,22 +97,22 @@ When possible, preferred way of defining calls is to put them within the functio
116
97
  class Functionality(State):
117
98
  # define function call as a class method
118
99
  @classmethod
119
- def function_call(cls, argument: FunctionArgument) -> None:
100
+ async def function_call(cls, argument: FunctionArgument) -> None:
120
101
  # Invoke the function implementation from the contextual state
121
102
  await ctx.state(cls).function(argument=argument)
122
103
 
123
104
  function: FunctionSignature
124
105
  ```
125
106
 
126
- The approach presented above is an equivalent of previous `function_call` defined as a free function. Keeping it within the functionality interface class allows streamlined access and better control over the calls.
107
+ Keeping it within the functionality interface class allows streamlined access and better control over the calls.
127
108
 
128
- ### Using implementation
109
+ ### Using Implementation
129
110
 
130
- To utilize the defined functionalities within an application, contexts must be established to provide the necessary state and implementations. Below is an example of how to integrate haiway functionalities into an application.
111
+ To utilize the defined functionalities within an application, contexts must be established to provide the necessary state and implementations. Below is an example of how to integrate Haiway functionalities into an application.
131
112
 
132
113
  ```python
133
114
  # application.py
134
- from my_functionality import functionality_implementation, function, FunctionalityState
115
+ from my_functionality import functionality_implementation, Functionality, FunctionalityState
135
116
  from haiway import ctx
136
117
 
137
118
  # Example application function utilizing the functionality
@@ -143,42 +124,16 @@ async def application_function(argument: FunctionArgument) -> None:
143
124
  FunctionalityState(parameter="ExampleParameter")
144
125
  ):
145
126
  # Execute the functionality using the predefined helper
146
- await function_call(FunctionArgument(value="SampleValue"))
127
+ await Functionality.function_call(FunctionArgument(value="SampleValue"))
147
128
  ```
148
129
 
149
130
  Going through all of these layers may seem unnecessary at first, but in the long term, it creates a robust, modular system that is easy to manage and work with.
150
131
 
151
- ### Flexible arguments
152
-
153
- When a function defined within the functionality is intended to utilize contextual state it might be beneficial to allow it to take any additional keyword arguments that would be used to update contextual state within the implementation. This approach allows to update contextual state locally, only for a single function call without propagating the change deeply into call tree.
154
-
155
- ```python
156
- ...
157
-
158
- # function signature allowing extra arguments
159
- @runtime_checkable
160
- class FunctionSignature(Protocol):
161
- async def __call__(self, argument: FunctionArgument, **extra: Any) -> None: ...
162
-
163
- ...
164
-
165
- # function implementation utilizing extra arguments to update local state
166
- async def function_implementation(argument: FunctionArgument, **extra: Any) -> None:
167
- # Retrieve 'parameter' from the current context's state updated with extra arguments
168
- parameter = ctx.state(FunctionalityState).updated(**extra).parameter
169
- ...
170
-
171
- ```
172
-
173
- haiway `State` types allow to create object copies with updated attributes by using `updated` method. The updated object is a swallow copy of the original object allowing to change the state only in local context without affecting other users of that state. When there are no changes to be applied no copy is created. Additionally the `updated` method skips all unnecessary arguments to handle described case without additional code required. However, there is always risk of name collisions, this approach should be carefully considered to avoid any potential issues.
174
-
175
- Additionally, this approach allows to utilize additional, implementation specific arguments, hidden beneatch the usual function signatures.
176
-
177
132
  ## Example
178
133
 
179
- To better understand the whole idea we can take a look at more concrete example of a notes management functionality:
134
+ To better understand the whole idea, we can take a look at a more concrete example of a notes management functionality:
180
135
 
181
- First we define some basic types required by our functionality - management functions signatures and the note itself.
136
+ First, we define some basic types required by our functionality - management functions signatures and the note itself.
182
137
 
183
138
  ```python
184
139
  # notes/types.py
@@ -208,9 +163,9 @@ Then we can define the state holding our functions and defining some context.
208
163
 
209
164
  ```python
210
165
  # notes/state.py
211
- from notes.types import NoteCreating, NoteUpdating
212
-
213
- from haiway import State
166
+ from notes.types import NoteCreating, NoteUpdating, Note
167
+ from haiway import State, ctx
168
+ from typing import Any
214
169
 
215
170
  # State providing contextual state for the functionality
216
171
  class NotesDirectory(State):
@@ -219,48 +174,59 @@ class NotesDirectory(State):
219
174
  # State encapsulating the functionality with its interface
220
175
  class Notes(State):
221
176
  # Call of note creation function
177
+ @classmethod
222
178
  async def create_note(cls, content: str, **extra: Any) -> Note:
223
179
  # Invoke the function implementation from the contextual state
224
- await ctx.state(cls).create(content=content, **extra)
180
+ return await ctx.state(cls).creating(content=content, **extra)
225
181
 
226
182
  # Call of note update function
183
+ @classmethod
227
184
  async def update_note(cls, note: Note, **extra: Any) -> None:
228
185
  # Invoke the function implementation from the contextual state
229
- await ctx.state(cls).update(note=note, **extra)
186
+ await ctx.state(cls).updating(note=note, **extra)
230
187
 
231
188
  # instance variables holding function implementations
232
189
  creating: NoteCreating
233
190
  updating: NoteUpdating
234
191
  ```
235
192
 
236
- That allows us to provide a concrete implementation. Note that `extra` arguments would allow us to alter the `NotesDirectory` path for a single function call only. This might be very important feature in some cases i.e. when using recursive function calls.
193
+ That allows us to provide a concrete implementation. Note that `extra` arguments would allow us to alter the `NotesDirectory` path for a single function call only. This might be a very important feature in some cases, i.e., when using recursive function calls.
237
194
 
238
195
  ```python
239
196
  # notes/files.py
240
- from notes.types import FunctionArgument
197
+ from notes.types import Note
241
198
  from notes.state import Notes, NotesDirectory
242
199
  from haiway import ctx
200
+ from typing import Any
201
+ from uuid import uuid4
202
+ from datetime import datetime
243
203
 
244
204
  # Implementation of note creation function
245
205
  async def file_note_create(content: str, **extra: Any) -> Note:
246
206
  # Retrieve path from the current context's state, updated if needed
247
207
  path = ctx.state(NotesDirectory).updated(**extra).path
248
208
  # Store note in file within the path...
209
+ note = Note(
210
+ identifier=uuid4(),
211
+ last_update=datetime.now(),
212
+ content=content
213
+ )
214
+ # Implementation for storing note in file...
215
+ return note
249
216
 
250
217
  # Implementation of note update function
251
218
  async def file_note_update(note: Note, **extra: Any) -> None:
252
219
  # Retrieve path from the current context's state, updated if needed
253
220
  path = ctx.state(NotesDirectory).updated(**extra).path
254
221
  # Update the note...
255
-
222
+ # Implementation for updating note in file...
256
223
 
257
224
  # Factory function to instantiate the Notes utilizing files implementation
258
225
  def file_notes() -> Notes:
259
226
  return Notes(
260
- create=file_note_create,
261
- update=file_note_update,
227
+ creating=file_note_create,
228
+ updating=file_note_update,
262
229
  )
263
-
264
230
  ```
265
231
 
266
232
  You can now use the whole functionality by defining implementation for execution context and calling functionality methods.
@@ -273,5 +239,5 @@ from haiway import ctx
273
239
  # prepare the context with desired implementation
274
240
  async with ctx.scope("example", file_notes(), NotesDirectory(path="./examples/note.txt")):
275
241
  # and access its methods contextually
276
- await Notes.create_note("This was an example of haiway")
242
+ await Notes.create_note("This was an example of Haiway")
277
243
  ```
@@ -0,0 +1,259 @@
1
+ ## Package Structure
2
+
3
+ Organize your project into five distinct package types:
4
+
5
+ ```
6
+ src/
7
+ ├── commons/ # Shared utilities, types, extensions
8
+ ├── integrations/ # Third-party service connections
9
+ │ ├── integration_a/
10
+ │ └── integration_b/
11
+ ├── solutions/ # Low-level utilities
12
+ │ ├── solution_a/
13
+ │ └── solution_b/
14
+ ├── features/ # High-level functionalities
15
+ │ ├── feature_a/
16
+ │ └── feature_b/
17
+ └── entrypoints/ # Application entry points
18
+ ├── entrypoint_a/
19
+ └── entrypoint_b/
20
+ ```
21
+
22
+ Each functionality package should contain:
23
+ - `__init__.py`: Exports public API only
24
+ - `types.py`: Type definitions using Protocol and State
25
+ - `state.py`: State declarations with classmethod helpers
26
+ - `config.py`: Configuration constants
27
+
28
+ ## Implementing Types
29
+
30
+ Define interfaces using Protocol and data using State:
31
+
32
+ ```python
33
+ from typing import Protocol, Any, runtime_checkable
34
+ from haiway import State
35
+
36
+ # Data structure
37
+ class UserData(State):
38
+ id: str
39
+ name: str
40
+ email: str | None = None
41
+
42
+ # Function interface
43
+ @runtime_checkable
44
+ class UserFetching(Protocol):
45
+ async def __call__(self, id: str) -> UserData: ...
46
+ ```
47
+
48
+ ## State Management
49
+
50
+ Define states for configuration and functionality containers:
51
+
52
+ ```python
53
+ from haiway import State
54
+ from .types import UserFetching, UserData
55
+
56
+ # Configuration state
57
+ class UserServiceConfig(State):
58
+ api_url: str = "https://api.example.com"
59
+ timeout_seconds: int = 30
60
+
61
+ # Functionality container
62
+ class UserService(State):
63
+ # Function implementations
64
+ fetching: UserFetching
65
+
66
+ # Class method interface
67
+ @classmethod
68
+ async def fetch_user(cls, id: str) -> UserData:
69
+ return await ctx.state(cls).fetching(id)
70
+ ```
71
+
72
+ ## Implementing Functions
73
+
74
+ Create concrete implementations and factory methods:
75
+
76
+ ```python
77
+ from haiway import ctx
78
+ from .types import UserData
79
+ from .state import UserService, UserServiceConfig
80
+
81
+ # Concrete implementation
82
+ async def http_user_fetching(id: str) -> UserData:
83
+ config = ctx.state(UserServiceConfig)
84
+ # Implementation using config.api_url
85
+ # ...
86
+ return UserData(id=id, name="Example User")
87
+
88
+ # Factory function providing implementation
89
+ def HTTPUserService() -> UserService:
90
+ return UserService(fetching=http_user_fetching)
91
+ ```
92
+
93
+ ## Using Context Scopes
94
+
95
+ Establish context with state and implementations:
96
+
97
+ ```python
98
+ from haiway import ctx
99
+ from .state import UserService, UserServiceConfig
100
+ from .implementation import HTTPUserService
101
+
102
+ async def main():
103
+ # Set up execution context
104
+ async with ctx.scope(
105
+ "main",
106
+ HTTPUserService(),
107
+ UserServiceConfig(api_url="https://custom-api.example.com")
108
+ ):
109
+ # Use functionality through class methods
110
+ user = await UserService.fetch_user("user-123")
111
+ print(f"Found user: {user.name}")
112
+ ```
113
+
114
+ ## Managing State Updates
115
+
116
+ Create state variants without mutation:
117
+
118
+ ```python
119
+ # Current state
120
+ config = ctx.state(UserServiceConfig)
121
+
122
+ # Create new instance with updated values
123
+ dev_config = config.updated(api_url="https://dev-api.example.com")
124
+
125
+ # Use in context
126
+ async with ctx.scope("dev-context", dev_config):
127
+ # Operations will use dev_config
128
+ pass
129
+ ```
130
+
131
+ ## Complete Example: Notes Manager
132
+
133
+ ```python
134
+ from typing import Protocol, Any, runtime_checkable
135
+ from uuid import UUID, uuid4
136
+ from datetime import datetime
137
+ from haiway import State, ctx
138
+
139
+ # Types
140
+ class Note(State):
141
+ id: UUID
142
+ content: str
143
+ created_at: datetime
144
+ updated_at: datetime
145
+
146
+ @runtime_checkable
147
+ class NoteCreating(Protocol):
148
+ async def __call__(self, content: str) -> Note: ...
149
+
150
+ @runtime_checkable
151
+ class NoteFinding(Protocol):
152
+ async def __call__(self, id: UUID) -> Note | None: ...
153
+
154
+ # State
155
+ class NotesConfig(State):
156
+ storage_path: str = "./notes"
157
+
158
+ class NotesService(State):
159
+ creating: NoteCreating
160
+ finding: NoteFinding
161
+
162
+ @classmethod
163
+ async def create_note(cls, content: str) -> Note:
164
+ return await ctx.state(cls).creating(content)
165
+
166
+ @classmethod
167
+ async def find_note(cls, id: UUID) -> Note | None:
168
+ return await ctx.state(cls).finding(id)
169
+
170
+ # Implementation
171
+ async def file_note_creating(content: str) -> Note:
172
+ now = datetime.now()
173
+ note = Note(
174
+ id=uuid4(),
175
+ content=content,
176
+ created_at=now,
177
+ updated_at=now
178
+ )
179
+
180
+ config = ctx.state(NotesConfig)
181
+ # Save note to file at config.storage_path
182
+ # ...
183
+
184
+ return note
185
+
186
+ async def file_note_finding(id: UUID) -> Note | None:
187
+ config = ctx.state(NotesConfig)
188
+ # Find note in files at config.storage_path
189
+ # ...
190
+
191
+ # Return found note or None
192
+ # For demonstration:
193
+ return Note(
194
+ id=id,
195
+ content="Example note content",
196
+ created_at=datetime.now(),
197
+ updated_at=datetime.now()
198
+ )
199
+
200
+ # Factory
201
+ def FileNotesService() -> NotesService:
202
+ return NotesService(
203
+ creating=file_note_creating,
204
+ finding=file_note_finding
205
+ )
206
+
207
+ # Usage
208
+ async def run_notes_app():
209
+ async with ctx.scope(
210
+ "notes-app",
211
+ FileNotesService(),
212
+ NotesConfig(storage_path="./my_notes")
213
+ ):
214
+ # Create a note
215
+ new_note = await NotesService.create_note("This is a test note")
216
+ print(f"Created note with ID: {new_note.id}")
217
+
218
+ # Find the note
219
+ found_note = await NotesService.find_note(new_note.id)
220
+ if found_note:
221
+ print(f"Found note: {found_note.content}")
222
+ ```
223
+
224
+ ## Best Practices
225
+
226
+ 1. **Type Definitions**
227
+ - Use Protocol for function interfaces
228
+ - Use State for data structures
229
+ - Apply runtime_checkable for better type safety
230
+
231
+ 2. **State Management**
232
+ - Keep states immutable
233
+ - Use .updated() for state variants
234
+ - Define defaults for optional values
235
+
236
+ 3. **Function Implementations**
237
+ - Access context through ctx.state()
238
+ - Keep functions pure when possible
239
+ - Return new objects instead of modifying existing ones
240
+
241
+ 4. **Class Method Interface**
242
+ - Define classmethods for cleaner API
243
+ - Use cls parameter to access current state
244
+ - Follow consistent naming (verb_noun pattern)
245
+
246
+ 5. **Context Usage**
247
+ - Use descriptive context names
248
+ - Group related states in single context
249
+ - Keep context hierarchy shallow
250
+
251
+ 6. **Package Organization**
252
+ - Separate interfaces from implementations
253
+ - Export only public API from __init__.py
254
+ - Group related functionality in dedicated packages
255
+
256
+ 7. **Error Handling**
257
+ - Define custom error types in types.py
258
+ - Handle errors explicitly at appropriate levels
259
+ - Maintain immutability in error cases