simplibs-actions 0.1.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 (97) hide show
  1. simplibs_actions-0.1.0/LICENSE +21 -0
  2. simplibs_actions-0.1.0/PKG-INFO +374 -0
  3. simplibs_actions-0.1.0/README.md +344 -0
  4. simplibs_actions-0.1.0/pyproject.toml +61 -0
  5. simplibs_actions-0.1.0/setup.cfg +4 -0
  6. simplibs_actions-0.1.0/src/simplibs/actions/__init__.py +103 -0
  7. simplibs_actions-0.1.0/src/simplibs/actions/base_class/Action.py +188 -0
  8. simplibs_actions-0.1.0/src/simplibs/actions/base_class/__init__.py +17 -0
  9. simplibs_actions-0.1.0/src/simplibs/actions/base_class/_validations/__init__.py +16 -0
  10. simplibs_actions-0.1.0/src/simplibs/actions/base_class/_validations/raise_invalid_action_target.py +54 -0
  11. simplibs_actions-0.1.0/src/simplibs/actions/containers/__init__.py +88 -0
  12. simplibs_actions-0.1.0/src/simplibs/actions/containers/_helpers/__init__.py +18 -0
  13. simplibs_actions-0.1.0/src/simplibs/actions/containers/_helpers/as_action.py +31 -0
  14. simplibs_actions-0.1.0/src/simplibs/actions/containers/_helpers/as_predicate.py +58 -0
  15. simplibs_actions-0.1.0/src/simplibs/actions/containers/_helpers/validations/__init__.py +16 -0
  16. simplibs_actions-0.1.0/src/simplibs/actions/containers/_helpers/validations/raise_invalid_predicate.py +42 -0
  17. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/__init__.py +21 -0
  18. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/_validations/__init__.py +18 -0
  19. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/_validations/raise_guard_failed.py +42 -0
  20. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/_validations/raise_not_iterable.py +39 -0
  21. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/aliases/__init__.py +24 -0
  22. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/aliases/apply_to_each.py +19 -0
  23. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/aliases/conditional_action.py +24 -0
  24. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/aliases/guarded_action.py +20 -0
  25. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/branch.py +77 -0
  26. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/for_each.py +58 -0
  27. simplibs_actions-0.1.0/src/simplibs/actions/containers/flow_control/guard.py +96 -0
  28. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/__init__.py +22 -0
  29. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/aliases/__init__.py +24 -0
  30. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/aliases/run_in_parallel.py +20 -0
  31. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/aliases/run_in_sequence.py +20 -0
  32. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/aliases/try_or_fallback.py +27 -0
  33. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/compose_class/ParallelCompose.py +53 -0
  34. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/compose_class/SequenceCompose.py +59 -0
  35. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/compose_class/__init__.py +23 -0
  36. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/fallback.py +88 -0
  37. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/parallel.py +66 -0
  38. simplibs_actions-0.1.0/src/simplibs/actions/containers/operators/sequence.py +74 -0
  39. simplibs_actions-0.1.0/src/simplibs/actions/containers/primitives/__init__.py +19 -0
  40. simplibs_actions-0.1.0/src/simplibs/actions/containers/primitives/aliases/__init__.py +22 -0
  41. simplibs_actions-0.1.0/src/simplibs/actions/containers/primitives/aliases/pass_through.py +19 -0
  42. simplibs_actions-0.1.0/src/simplibs/actions/containers/primitives/aliases/replace_with.py +19 -0
  43. simplibs_actions-0.1.0/src/simplibs/actions/containers/primitives/constant.py +46 -0
  44. simplibs_actions-0.1.0/src/simplibs/actions/containers/primitives/identity.py +53 -0
  45. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/__init__.py +23 -0
  46. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/_validations/__init__.py +16 -0
  47. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/_validations/raise_invalid_attempts.py +40 -0
  48. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/aliases/__init__.py +26 -0
  49. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/aliases/callable_action.py +21 -0
  50. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/aliases/log_step.py +21 -0
  51. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/aliases/retry_on_failure.py +23 -0
  52. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/aliases/run_side_effect.py +21 -0
  53. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/lambda_action.py +55 -0
  54. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/log_action.py +58 -0
  55. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/retry.py +82 -0
  56. simplibs_actions-0.1.0/src/simplibs/actions/containers/wrappers/tap.py +55 -0
  57. simplibs_actions-0.1.0/src/simplibs/actions/creator/__init__.py +26 -0
  58. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/__init__.py +16 -0
  59. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/constants/EMPTY.py +42 -0
  60. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/constants/__init__.py +16 -0
  61. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/__init__.py +20 -0
  62. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/_helpers/__init__.py +24 -0
  63. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/_helpers/_describe_annotation.py +53 -0
  64. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/_helpers/build_call_docstring.py +68 -0
  65. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/_helpers/build_init_docstring.py +56 -0
  66. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/create_act.py +145 -0
  67. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/create_call.py +102 -0
  68. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/creators/create_init_and_slots.py +103 -0
  69. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/__init__.py +23 -0
  70. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/resolve_class_name.py +54 -0
  71. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/resolve_param.py +77 -0
  72. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/unwrap_log_this_and_validate_call.py +72 -0
  73. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/validations/__init__.py +18 -0
  74. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/validations/raise_main_param_not_found.py +39 -0
  75. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/resolvers/validations/raise_no_parameters.py +37 -0
  76. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/validations/__init__.py +23 -0
  77. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/validations/_verify_type_is_validatable.py +48 -0
  78. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/validations/raise_action_instance_error.py +32 -0
  79. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/validations/validate_param_kinds.py +46 -0
  80. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/validations/validate_params_annotations.py +45 -0
  81. simplibs_actions-0.1.0/src/simplibs/actions/creator/_helpers/validations/validate_return_annotations.py +42 -0
  82. simplibs_actions-0.1.0/src/simplibs/actions/creator/create_action.py +229 -0
  83. simplibs_actions-0.1.0/src/simplibs/actions/decorator/__init__.py +26 -0
  84. simplibs_actions-0.1.0/src/simplibs/actions/decorator/to_action.py +109 -0
  85. simplibs_actions-0.1.0/src/simplibs/actions/testing/__init__.py +50 -0
  86. simplibs_actions-0.1.0/src/simplibs/actions/testing/assert_action.py +195 -0
  87. simplibs_actions-0.1.0/src/simplibs/actions/testing/assert_action_alias.py +102 -0
  88. simplibs_actions-0.1.0/src/simplibs/actions/testing/asserts/__init__.py +31 -0
  89. simplibs_actions-0.1.0/src/simplibs/actions/testing/asserts/assert_action_construction.py +105 -0
  90. simplibs_actions-0.1.0/src/simplibs/actions/testing/asserts/assert_action_io_types.py +80 -0
  91. simplibs_actions-0.1.0/src/simplibs/actions/testing/asserts/assert_action_output.py +74 -0
  92. simplibs_actions-0.1.0/src/simplibs/actions/testing/asserts/assert_action_raises.py +80 -0
  93. simplibs_actions-0.1.0/src/simplibs_actions.egg-info/PKG-INFO +374 -0
  94. simplibs_actions-0.1.0/src/simplibs_actions.egg-info/SOURCES.txt +95 -0
  95. simplibs_actions-0.1.0/src/simplibs_actions.egg-info/dependency_links.txt +1 -0
  96. simplibs_actions-0.1.0/src/simplibs_actions.egg-info/requires.txt +7 -0
  97. simplibs_actions-0.1.0/src/simplibs_actions.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dalibor Sova (Sudip2708)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,374 @@
1
+ Metadata-Version: 2.4
2
+ Name: simplibs-actions
3
+ Version: 0.1.0
4
+ Summary: A composable Action pipeline system — chainable via >>, &, and if_matches, built on simplibs-rules.
5
+ Author-email: "Dalibor Sova (Sudip2708)" <daliborsova@seznam.cz>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/simplibs/simplibs-actions
8
+ Project-URL: Repository, https://github.com/simplibs/simplibs-actions
9
+ Project-URL: Issues, https://github.com/simplibs/simplibs-actions/issues
10
+ Project-URL: Changelog, https://github.com/simplibs/simplibs-actions/blob/main/CHANGELOG.md
11
+ Keywords: actions,pipeline,composition,functional,validation,data-validation,simplibs,simplibs-actions,developer-tools
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: simplibs-rules>=0.2.2
24
+ Requires-Dist: simplibs-exception>=1.1.3
25
+ Requires-Dist: simplibs-validate>=0.2.1
26
+ Requires-Dist: simplibs-types>=0.2.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # 🔗 `simplibs-actions`
32
+
33
+ [![PyPI](https://img.shields.io/pypi/v/simplibs-actions)](https://pypi.org/project/simplibs-actions/)
34
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
35
+ [![Licence](https://img.shields.io/badge/licence-MIT-green)](https://github.com/simplibs/simplibs-actions/blob/main/LICENSE)
36
+
37
+ **Turn ordinary functions into composable pipeline steps — chain them with `>>`, `&`, and `|`.**
38
+
39
+ A lightweight Python library for building `Action` objects: single-purpose pipeline steps
40
+ that chain into sequences, run in parallel, and fall back on failure using plain Python
41
+ operators — no pipeline framework, no DSL, no boilerplate. Any function becomes an `Action`
42
+ with one decorator, complete with type-checked construction and calls, structured
43
+ diagnostics on failure, and its own dedicated testing toolkit.
44
+
45
+ ```python
46
+ from simplibs.actions import to_action
47
+
48
+ @to_action
49
+ def strip(data: str) -> str:
50
+ return data.strip()
51
+
52
+ @to_action
53
+ def shout(data: str) -> str:
54
+ return data.upper() + "!"
55
+
56
+ pipeline = strip() >> shout()
57
+ pipeline(" hello ") # -> "HELLO!"
58
+ ```
59
+
60
+ > `simplibs-actions` is the pipeline layer of the ecosystem, built directly on
61
+ > [`simplibs-rules`](https://pypi.org/project/simplibs-rules/) (conditions),
62
+ > [`simplibs-validate`](https://pypi.org/project/simplibs-validate/) (`validate_call`,
63
+ > `log_this`), and [`simplibs-exception`](https://pypi.org/project/simplibs-exception/)
64
+ > (structured diagnostics) — see [Related libraries](#-related-libraries) below.
65
+
66
+ ---
67
+
68
+ ## 🧭 The Core Philosophy
69
+
70
+ Most pipeline/workflow tooling forces a choice: write ad-hoc chains of `if`/`try` calls
71
+ scattered through your codebase, or adopt a heavy orchestration framework with its own
72
+ configuration language. `simplibs-actions` takes neither route. An `Action` is nothing
73
+ more than a plain function with a real signature — `create_action`/`@to_action` wraps it
74
+ once, and from then on it composes with the same three operators every function in this
75
+ library composes with:
76
+
77
+ ```python
78
+ step_a >> step_b >> step_c # sequence — thread output into the next input
79
+ step_a & step_b & step_c # parallel — same input, every branch, one flat tuple out
80
+ step_a | step_b # fallback — try step_a; on failure, run step_b instead
81
+ ```
82
+
83
+ Every generated `Action` keeps the function's own signature intact — its parameters are
84
+ still individually annotated, still individually type-checked at call time (via
85
+ `simplibs-validate`'s `validate_call`), and still individually documented, rather than
86
+ being flattened into an opaque `(*args, **kwargs)` wrapper. Because that generation is
87
+ itself a well-defined, mechanical process, the library ships its own testing tool
88
+ (`assert_action`) that audits the *entire* contract of any `Action` — this library's own
89
+ containers, or any `Action` a downstream library builds — with one function call.
90
+
91
+ ---
92
+
93
+ ## 📦 Installation
94
+
95
+ ```bash
96
+ pip install simplibs-actions
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 🚀 Quick Start in 60 Seconds
102
+
103
+ ### Level 1: Turning a function into an Action
104
+
105
+ ```python
106
+ from simplibs.actions import to_action
107
+
108
+ @to_action
109
+ def double(data: int) -> int:
110
+ return data * 2
111
+
112
+ action = double()
113
+ action(21) # -> 42
114
+ ```
115
+
116
+ ### Level 2: Composing Actions
117
+
118
+ ```python
119
+ from simplibs.actions import to_action
120
+
121
+ @to_action
122
+ def strip(data: str) -> str:
123
+ return data.strip()
124
+
125
+ @to_action
126
+ def upper(data: str) -> str:
127
+ return data.upper()
128
+
129
+ @to_action
130
+ def lower(data: str) -> str:
131
+ return data.lower()
132
+
133
+ # Sequence: thread output into the next input
134
+ pipeline = strip() >> upper()
135
+ pipeline(" hi ") # -> "HI"
136
+
137
+ # Parallel: same input, every branch, one flat tuple out
138
+ both = upper() & lower()
139
+ both("Hi") # -> ("HI", "hi")
140
+
141
+ # Fallback: try the left side; on failure, run the right side instead
142
+ safe = risky_parse() | default_value(value=None)
143
+ safe(raw_input)
144
+ ```
145
+
146
+ `Rule` instances from `simplibs-rules` compose the same way — `is_integer >> add_one`
147
+ verifies the rule first, then continues with the action.
148
+
149
+ ### Level 3: Testing an Action
150
+
151
+ ```python
152
+ from simplibs.actions.testing import assert_action
153
+
154
+ def test_double_contract(subtests):
155
+ assert_action(
156
+ subtests,
157
+ double(),
158
+ valid_values=[21],
159
+ expected_outputs=[42],
160
+ invalid_values=["not-a-number"],
161
+ expected_exception_type=ValidationError,
162
+ )
163
+ ```
164
+
165
+ One call audits the full deterministic contract: valid inputs produce the right output,
166
+ invalid inputs raise the right exception, and (by default) the generated class carries
167
+ correct `_input_type`/`_output_type` introspection.
168
+
169
+ ---
170
+
171
+ ## 🛠️ Architecture & Package Structure
172
+
173
+ ```
174
+ src/simplibs/actions/
175
+ ├── base_class/ ◄── Abstract base class Action — __call__, operators, resolve_action
176
+ ├── containers/ ◄── Every built-in Action
177
+ │ ├── flow_control/ ◄── guard, branch, for_each
178
+ │ ├── operators/ ◄── sequence, parallel, fallback, compose_class/ (self-flattening)
179
+ │ ├── primitives/ ◄── identity, constant
180
+ │ └── wrappers/ ◄── lambda_action, tap, retry, log_action
181
+ ├── creator/ ◄── create_action — dynamically builds Action classes from functions
182
+ ├── decorator/ ◄── to_action — the decorator interface for create_action
183
+ └── testing/ ◄── assert_action, assert_action_alias
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 🧩 The `Action` Class
189
+
190
+ Every action in this library — hand-written or generated — is an `Action`. It defines
191
+ the one mandatory contract (`__call__`) and provides full operator-based composition out
192
+ of the box.
193
+
194
+ ```python
195
+ class Action(ABC):
196
+
197
+ @abstractmethod
198
+ def __call__(self, data: Any, /) -> Any:
199
+ """Run this action over `data` and return the result."""
200
+ raise NotImplementedError
201
+
202
+ def then(self, next_step: Any) -> "Action":
203
+ """Chains this action with a subsequent action, rule, or callable."""
204
+ ...
205
+
206
+ def __and__(self, other: Any) -> "Action":
207
+ """Executes actions in parallel over identical input data."""
208
+ ...
209
+
210
+ def __or__(self, other: Any) -> "Action":
211
+ """Fallback handling: executes `other` if this action fails."""
212
+ ...
213
+
214
+ @staticmethod
215
+ def resolve_action(obj: Any) -> "Action":
216
+ """Convert an Action, a Rule, or a plain callable into an Action instance."""
217
+ ...
218
+ ```
219
+
220
+ ➡️ Full method-by-method reference: [README_ACTION](https://github.com/simplibs/simplibs-actions/blob/main/docs/README_ACTION.md)
221
+
222
+ ---
223
+
224
+ ## 📖 Built-in Containers
225
+
226
+ Every built-in `Action` is exposed as a `@to_action`-generated class, constructed with
227
+ its own configuration parameters and called with one main input value.
228
+
229
+ | Container | Package | Description |
230
+ |-----------------|----------------|--------------------------------------------------------------------------------------------------------------------|
231
+ | `guard` | `flow_control` | Hard gate — passes data through, or raises, based on a `Rule`/condition. |
232
+ | `branch` | `flow_control` | Soft conditional — runs one of two actions based on a condition, no error either way. |
233
+ | `for_each` | `flow_control` | Applies an action to every item of an iterable, collecting the results. |
234
+ | `sequence` | `operators` | Chains any number of steps left to right. Self-flattening variant: `SequenceCompose`. |
235
+ | `parallel` | `operators` | Runs N actions over the same input, returning a flat tuple of results. Self-flattening variant: `ParallelCompose`. |
236
+ | `fallback` | `operators` | Tries one action; on failure, runs another or returns the original data. |
237
+ | `identity` | `primitives` | No-op — returns data unchanged. |
238
+ | `constant` | `primitives` | Ignores data, always returns a fixed value. |
239
+ | `lambda_action` | `wrappers` | Adapts a raw callable into an Action. |
240
+ | `tap` | `wrappers` | Runs an action for its side effect; always returns the original data. |
241
+ | `retry` | `wrappers` | Retries an action up to N times on failure. |
242
+ | `log_action` | `wrappers` | Logs the data flowing through one pipeline point, unchanged. |
243
+
244
+ Every container above also ships under a more descriptive alias (`guarded_action`,
245
+ `run_in_sequence`, `pass_through`, ...) — see the full list in the containers reference.
246
+
247
+ ➡️ [README_CONTAINERS](https://github.com/simplibs/simplibs-actions/blob/main/docs/README_CONTAINERS.md)
248
+
249
+ ---
250
+
251
+ ## 🏗️ Creating Actions
252
+
253
+ Any function becomes an `Action` two ways: the decorator (everyday use), or the
254
+ function it wraps (when you need to build one dynamically):
255
+
256
+ ```python
257
+ from simplibs.actions import to_action, create_action
258
+
259
+ @to_action
260
+ def greet(data: dict, prefix: str = "Hi") -> str:
261
+ return f"{prefix}, {data['name']}!"
262
+
263
+ # Equivalent, without the decorator:
264
+ Greet = create_action(greet)
265
+ ```
266
+
267
+ `create_action` fully annotates, validates, and documents the generated class's
268
+ `__init__`/`__call__`/`act` — nothing about the source function's own signature is lost
269
+ or flattened into `(*args, **kwargs)`.
270
+
271
+ ➡️ [README_CREATOR](https://github.com/simplibs/simplibs-actions/blob/main/docs/README_CREATOR.md) — `create_action` and every internal resolver/generator it's built from
272
+ ➡️ [README_DECOTATOR](https://github.com/simplibs/simplibs-actions/blob/main/docs/README_DECOTATOR.md) — `to_action`, the decorator interface
273
+
274
+ ---
275
+
276
+ ## 🧪 Testing Utilities
277
+
278
+ `simplibs-actions` includes its own contract-testing tools, so this library's containers
279
+ — and any `Action` a downstream library builds on top of it — can be audited with one
280
+ function call instead of a hand-written test module per action:
281
+
282
+ ```python
283
+ from simplibs.actions.testing import assert_action
284
+
285
+ # Full deterministic contract: valid/invalid execution, type introspection, construction guard
286
+ assert_action(
287
+ subtests,
288
+ my_action,
289
+ valid_values=[...], expected_outputs=[...],
290
+ invalid_values=[...], expected_exception_type=SomeError,
291
+ )
292
+ ```
293
+
294
+ ➡️ [README_TESTING_ASSERT_ACTION](https://github.com/simplibs/simplibs-actions/blob/main/docs/README_TESTING_ASSERT_ACTION.md)
295
+ ➡️ [README_TESTING_ASSERT_ACTION_ALIAS](https://github.com/simplibs/simplibs-actions/blob/main/docs/README_TESTING_ASSERT_ACTION_ALIAS.md) — identity + smoke-test verification for alias objects
296
+
297
+ ---
298
+
299
+ ## ⚠️ Exceptions
300
+
301
+ Every diagnostic this library raises — a rejected constructor argument, a type
302
+ mismatch on call, an unsupported source-function signature — is a structured
303
+ [`simplibs.exception`](https://pypi.org/project/simplibs-exception/) card
304
+ (`ParamError`, `ValidationError`, ...), not a bare `TypeError`/`ValueError` traceback.
305
+ Validation failures on a generated `Action`'s own parameters (constructor or call) go
306
+ through [`simplibs-validate`](https://pypi.org/project/simplibs-validate/)'s
307
+ `validate_call`, using the same annotation-decomposition engine as
308
+ [`simplibs-rules`](https://pypi.org/project/simplibs-rules/).
309
+
310
+ ---
311
+
312
+ ## 🔗 Related libraries
313
+
314
+ * **[`simplibs-rules`](https://pypi.org/project/simplibs-rules/)** — the `Rule`
315
+ abstraction actions compose with directly (`rule >> action`, `rule & action`, ...)
316
+ and the engine behind `guard`'s condition checking.
317
+ * **[`simplibs-validate`](https://pypi.org/project/simplibs-validate/)** —
318
+ `validate_call`/`log_this`, applied to every generated `Action`'s `__init__`/
319
+ `__call__`/`act`.
320
+ * **[`simplibs-exception`](https://pypi.org/project/simplibs-exception/)** — the
321
+ structured diagnostic cards every failure in this library raises, and the testing
322
+ primitives (`assert_function_raises`, `assert_exception_function`) `assert_action`
323
+ is built on.
324
+ * **[`simplibs-types`](https://pypi.org/project/simplibs-types/)** — reusable
325
+ validated types (like `tuple_not_empty`) used as annotations on several built-in
326
+ containers (`sequence`'s `steps`, `parallel`'s `branches`).
327
+
328
+ `simplibs-actions` is, in turn, the foundation other `simplibs` libraries build their
329
+ own domain-specific actions on top of — each ships ready-made `Action`s for its own
330
+ area alongside its standalone functions.
331
+
332
+ ---
333
+
334
+ ## ☯️ About simplibs
335
+
336
+ All libraries in the **simplibs** (Simple Libraries) ecosystem share a common engineering philosophy:
337
+
338
+ * **Dyslexia-friendly:**
339
+ We actively minimize cognitive load. Code is atomized into small, self-contained units,
340
+ files are named directly after the logical task they perform,
341
+ and explanations describe *why* something is designed, not just *what* it is.
342
+ * **Programmer's Zen:**
343
+ Nothing should be missing, and nothing should be superfluous.
344
+ We value clean execution paths and robust, understandable code architectures over rushed, messy feature sets.
345
+ * **Defensive Style:**
346
+ We actively anticipate edge cases and failure modes so that only safe operational paths remain.
347
+ Our code is built to degrade gracefully rather than crash unexpectedly.
348
+ * **Minimalism:**
349
+ Find the most direct path to the goal in as few operational steps as possible
350
+ without taking shortcuts on safety, readability, or completeness.
351
+ * **Code as Craft:**
352
+ Code should be pleasant to look at, readable at a glance, and evoke structural harmony.
353
+ We treat software engineering as a precision trade.
354
+
355
+ ---
356
+
357
+ ### 🤝 Contributing & Community
358
+
359
+ This is an **open-source project** built with love and care.
360
+ We strongly believe in community collaboration and welcome any feedback, bug reports, or feature ideas!
361
+
362
+ * **Want to contribute?** Feel free to open an Issue or submit a Pull Request.
363
+ * **Want to get in touch?** If you'd like to discuss the project further, collaborate,
364
+ or just say hello, feel free to open a GitHub Issue or start a Discussion.
365
+
366
+ ---
367
+
368
+ ### 📝 License
369
+
370
+ This library is released under the **MIT License**. Build great things!
371
+
372
+ ---
373
+
374
+ [▲ Back to Top](#-simplibs-actions)