iterativerecursion 0.2__tar.gz → 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Carlos A. Planchón
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,399 @@
1
+ Metadata-Version: 2.4
2
+ Name: iterativerecursion
3
+ Version: 1.0
4
+ Summary: A Python module to simulate recursive function calls using iteration, providing explicit control over execution flow and avoiding stack overflow issues.
5
+ Author-email: "Carlos A. Planchón" <carlosandresplanchonprestes@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/carlosplanchon/iterativerecursion
8
+ Keywords: iterative,recursion
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Build Tools
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ # iterativerecursion
25
+
26
+ ![iterativerecursion banner](https://raw.githubusercontent.com/carlosplanchon/iterativerecursion/refs/heads/master/assets/banner.jpeg)
27
+
28
+ *A Python module to simulate recursive function calls using iteration, providing explicit control over execution flow and avoiding stack overflow issues.*
29
+
30
+ ## Overview
31
+
32
+ `iterativerecursion` provides a mechanism to chain function calls iteratively while maintaining a recursive-like pattern. Instead of relying on the call stack, functions explicitly declare what to call next, making the execution flow transparent and controllable.
33
+
34
+ ### Why Use This?
35
+
36
+ - **Avoid Stack Overflow**: Handle deep recursion without hitting Python's recursion limit
37
+ - **Explicit Control**: See and control the exact flow of function calls
38
+ - **Debugging**: Easier to trace execution without deep call stacks
39
+ - **State Management**: Shared environment for passing data between functions
40
+ - **Safety**: Built-in iteration limits to prevent infinite loops
41
+
42
+ ### When to Use This
43
+
44
+ This library is useful when you need:
45
+ - Deep recursion that exceeds Python's stack limit (~1000 calls)
46
+ - Explicit control over recursive execution flow
47
+ - To convert recursive algorithms to iterative ones systematically
48
+ - State machines or complex control flow patterns
49
+
50
+ ## Scope & limitations
51
+
52
+ This engine is essentially a **trampoline / dispatcher**: each step returns the name of the next function to run.
53
+ That makes it a great fit for **tail recursion (linear step-by-step recursion)**, workflows, and state machines.
54
+
55
+ It does **not** automatically emulate a full call stack. Patterns that require “returning to the caller”
56
+ (e.g. tree recursion like `fib(n-1) + fib(n-2)`, post-order DFS reductions) require you to model an explicit
57
+ stack/frames (continuations) inside `environment_variables`.
58
+
59
+ For most cases, **normal recursion is simpler and preferred**. Use this when recursion depth or explicit control becomes a concern.
60
+
61
+ ## Documentation
62
+
63
+ 📚 **[View interactive documentation on DeepWiki](https://deepwiki.com/carlosplanchon/iterativerecursion)**
64
+
65
+ Explore the full API reference, examples, and guides in an interactive format.
66
+
67
+ ## Installation
68
+
69
+ ### Using uv
70
+ ```bash
71
+ uv add iterativerecursion
72
+ ```
73
+
74
+ ### Using pip
75
+ ```bash
76
+ pip install iterativerecursion
77
+ ```
78
+
79
+ ## Quick Start
80
+
81
+ ```python
82
+ from iterativerecursion import IterativeRecursionEngine, FunctionReturn
83
+
84
+ def greet(name: str) -> FunctionReturn:
85
+ print(f"Hello, {name}!")
86
+ return FunctionReturn(
87
+ returned_values={"next_name": "World"},
88
+ next_function_to_call="farewell",
89
+ arg_env_mapping={"name": "next_name"}
90
+ )
91
+
92
+ def farewell(name: str) -> FunctionReturn:
93
+ print(f"Goodbye, {name}!")
94
+ return FunctionReturn(
95
+ returned_values={}
96
+ # next_function_to_call defaults to None to terminate
97
+ )
98
+
99
+ # Create engine and register functions
100
+ engine = IterativeRecursionEngine()
101
+ engine.add_function(greet)
102
+ engine.add_function(farewell)
103
+
104
+ # Start execution
105
+ engine.start_function_caller(
106
+ next_function_to_call="greet",
107
+ environment_variables={"initial_name": "Alice"},
108
+ arg_env_mapping={"name": "initial_name"}
109
+ )
110
+ ```
111
+
112
+ **Output:**
113
+ ```
114
+ Hello, Alice!
115
+ Goodbye, World!
116
+ ```
117
+
118
+ ## How It Works
119
+
120
+ Functions return a `FunctionReturn` dataclass instance with three attributes:
121
+
122
+ | Attribute | Type | Description |
123
+ |-----------|------|-------------|
124
+ | `returned_values` | `dict[str, Any]` | Values to store in the shared environment |
125
+ | `next_function_to_call` | `str \| None` | Name of the next function to execute, or `None` to stop (default: `None`) |
126
+ | `arg_env_mapping` | `dict[str, str]` | Mapping of parameter names to environment variable keys (default: auto-mapped from `returned_values` keys) |
127
+
128
+ **Key Feature**: If `arg_env_mapping` is not specified, it automatically maps each key in `returned_values` to itself. For example, `{"counter": 5}` automatically creates `{"counter": "counter"}` mapping.
129
+
130
+ The engine maintains a shared environment where functions can store and retrieve values across calls.
131
+
132
+ ## Examples
133
+
134
+ ### Using the @register Decorator
135
+
136
+ The `@register` decorator provides a cleaner way to register functions:
137
+
138
+ ```python
139
+ from iterativerecursion import IterativeRecursionEngine, FunctionReturn
140
+
141
+ engine = IterativeRecursionEngine()
142
+
143
+ @engine.register
144
+ def greet(name: str) -> FunctionReturn:
145
+ print(f"Hello, {name}!")
146
+ return FunctionReturn(
147
+ returned_values={"next_name": "World"},
148
+ next_function_to_call="farewell",
149
+ arg_env_mapping={"name": "next_name"}
150
+ )
151
+
152
+ @engine.register
153
+ def farewell(name: str) -> FunctionReturn:
154
+ print(f"Goodbye, {name}!")
155
+ return FunctionReturn(
156
+ returned_values={}
157
+ )
158
+
159
+ # Start execution and get final state
160
+ result = engine.start_function_caller(
161
+ next_function_to_call="greet",
162
+ environment_variables={"initial_name": "Alice"},
163
+ arg_env_mapping={"name": "initial_name"}
164
+ )
165
+ ```
166
+
167
+ ### Factorial Calculation
168
+
169
+ ```python
170
+ from iterativerecursion import IterativeRecursionEngine, FunctionReturn
171
+
172
+ def factorial_step(n: int, accumulator: int) -> FunctionReturn:
173
+ if n <= 1:
174
+ return FunctionReturn(
175
+ returned_values={"result": accumulator}
176
+ )
177
+ # Auto-mapping: {"n": n-1, "accumulator": ...} automatically maps to itself
178
+ return FunctionReturn(
179
+ returned_values={
180
+ "n": n - 1,
181
+ "accumulator": accumulator * n
182
+ },
183
+ next_function_to_call="factorial_step"
184
+ )
185
+
186
+ engine = IterativeRecursionEngine()
187
+ engine.add_function(factorial_step)
188
+ engine.start_function_caller(
189
+ next_function_to_call="factorial_step",
190
+ environment_variables={"n": 5, "accumulator": 1},
191
+ arg_env_mapping={"n": "n", "accumulator": "accumulator"}
192
+ )
193
+
194
+ print(f"5! = {engine.environment_variables['result']}") # Output: 5! = 120
195
+ ```
196
+
197
+ ### Fibonacci Sequence (tail-recursive / iterative form)
198
+
199
+ ```python
200
+ from iterativerecursion import IterativeRecursionEngine, FunctionReturn
201
+
202
+ def fibonacci(n: int, a: int, b: int) -> FunctionReturn:
203
+ if n == 0:
204
+ return FunctionReturn(
205
+ returned_values={"result": a}
206
+ )
207
+ # Auto-mapping handles {"n": "n", "a": "a", "b": "b"} automatically
208
+ return FunctionReturn(
209
+ returned_values={
210
+ "n": n - 1,
211
+ "a": b,
212
+ "b": a + b
213
+ },
214
+ next_function_to_call="fibonacci"
215
+ )
216
+
217
+ engine = IterativeRecursionEngine()
218
+ engine.add_function(fibonacci)
219
+ engine.start_function_caller(
220
+ next_function_to_call="fibonacci",
221
+ environment_variables={"n": 10, "a": 0, "b": 1},
222
+ arg_env_mapping={"n": "n", "a": "a", "b": "b"}
223
+ )
224
+
225
+ print(f"Fibonacci(10) = {engine.environment_variables['result']}") # Output: 55
226
+ ```
227
+
228
+ ### Preventing Infinite Loops
229
+
230
+ Use the `max_iterations` parameter to prevent runaway execution:
231
+
232
+ ```python
233
+ from iterativerecursion import IterativeRecursionEngine, FunctionReturn
234
+
235
+ def infinite_loop(counter: int) -> FunctionReturn:
236
+ print(f"Iteration: {counter}")
237
+ # Auto-mapping: no need for {"counter": "counter"}
238
+ return FunctionReturn(
239
+ returned_values={"counter": counter + 1},
240
+ next_function_to_call="infinite_loop"
241
+ )
242
+
243
+ engine = IterativeRecursionEngine()
244
+ engine.add_function(infinite_loop)
245
+
246
+ try:
247
+ engine.start_function_caller(
248
+ next_function_to_call="infinite_loop",
249
+ environment_variables={"counter": 0},
250
+ arg_env_mapping={"counter": "counter"},
251
+ max_iterations=10 # Safety limit
252
+ )
253
+ except RuntimeError as e:
254
+ print(f"Caught: {e}")
255
+ # Output: Caught: Maximum iteration limit (10) reached...
256
+ ```
257
+
258
+ ## API Reference
259
+
260
+ ### `IterativeRecursionEngine`
261
+
262
+ The main execution engine for iterative recursion.
263
+
264
+ #### Methods
265
+
266
+ ##### `__init__()`
267
+ Creates a new engine instance.
268
+
269
+ ```python
270
+ engine = IterativeRecursionEngine()
271
+ ```
272
+
273
+ ##### `add_function(function)`
274
+ Registers a function with the engine.
275
+
276
+ - **Parameters**: `function` - A callable that returns `FunctionReturn`
277
+ - **Returns**: None
278
+
279
+ ```python
280
+ engine.add_function(my_function)
281
+ ```
282
+
283
+ ##### `register(function)`
284
+ Decorator to register a function with the engine. Alternative to `add_function()`.
285
+
286
+ - **Parameters**: `function` - A callable that returns `FunctionReturn`
287
+ - **Returns**: The same function (for chaining)
288
+
289
+ ```python
290
+ @engine.register
291
+ def my_function(x: int) -> FunctionReturn:
292
+ return FunctionReturn(
293
+ returned_values={"result": x * 2}
294
+ )
295
+ ```
296
+
297
+ ##### `add_environment_variables(variables: dict[str, Any])`
298
+ Adds or updates variables in the shared environment.
299
+
300
+ - **Parameters**: `variables` - Dictionary of variable names and values
301
+ - **Returns**: None
302
+
303
+ ```python
304
+ engine.add_environment_variables({"x": 10, "y": 20})
305
+ ```
306
+
307
+ ##### `start_function_caller(next_function_to_call, environment_variables, arg_env_mapping, max_iterations=None)`
308
+ Begins executing functions starting from the specified function.
309
+
310
+ - **Parameters**:
311
+ - `next_function_to_call` (str): Name of the first function to call
312
+ - `environment_variables` (dict[str, Any]): Initial environment variables
313
+ - `arg_env_mapping` (dict[str, str]): Parameter mapping for first function
314
+ - `max_iterations` (int | None): Maximum iterations allowed (default: None/unlimited)
315
+ - **Returns**: `dict[str, Any]` - Final state of environment variables after execution
316
+ - **Raises**:
317
+ - `KeyError`: If function not found or environment variable missing
318
+ - `RuntimeError`: If `max_iterations` limit is reached
319
+ - `ValueError`: If function returns invalid structure
320
+ - `TypeError`: If function return has wrong types
321
+
322
+ ```python
323
+ result = engine.start_function_caller(
324
+ next_function_to_call="start_func",
325
+ environment_variables={"value": 42},
326
+ arg_env_mapping={"param": "value"},
327
+ max_iterations=1000
328
+ )
329
+ # Access final state directly from result
330
+ print(result["some_value"])
331
+ ```
332
+
333
+ #### Attributes
334
+
335
+ - `functions_dict` (dict): Registry of available functions
336
+ - `environment_variables` (dict): Shared state accessible to all functions
337
+
338
+ ### Type Definitions
339
+
340
+ #### `FunctionReturn`
341
+ Dataclass defining the required return structure for functions.
342
+
343
+ ```python
344
+ @dataclass
345
+ class FunctionReturn:
346
+ returned_values: dict[str, Any]
347
+ next_function_to_call: str | None = None
348
+ arg_env_mapping: dict[str, str] = field(default_factory=dict)
349
+ ```
350
+
351
+ **Auto-mapping feature**: If `arg_env_mapping` is not provided, it automatically maps each key in `returned_values` to itself. This means you rarely need to specify `arg_env_mapping` explicitly.
352
+
353
+ #### `VarsDict`
354
+ Type alias for variable dictionaries.
355
+
356
+ ```python
357
+ VarsDict = dict[str, Any]
358
+ ```
359
+
360
+ ## Development
361
+
362
+ ### Running Tests
363
+
364
+ ```bash
365
+ # Install with dev dependencies
366
+ pip install -e ".[dev]"
367
+
368
+ # Run tests
369
+ pytest tests/ -v
370
+ ```
371
+
372
+ ### Test Coverage
373
+
374
+ The test suite includes:
375
+ - Basic function chaining
376
+ - Environment variable management
377
+ - Error handling and validation
378
+ - Runtime validation of return structures
379
+ - Iteration limits
380
+ - Decorator API (`@register`)
381
+ - Return value access
382
+ - Improved error messages
383
+ - Complex scenarios (factorial, state machines)
384
+
385
+ ## Contributing
386
+
387
+ Contributions are welcome! Please feel free to submit a Pull Request.
388
+
389
+ ## License
390
+
391
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
392
+
393
+ ## Author
394
+
395
+ Carlos A. Planchón - [GitHub](https://github.com/carlosplanchon/iterativerecursion)
396
+
397
+ ## Acknowledgments
398
+
399
+ This module was created as both a practical solution for deep recursion scenarios and an exploration of alternative execution patterns in Python.