A Python module to simulate recursive function calls using iteration, providing explicit control over execution flow and avoiding stack overflow issues.
Project description
iterativerecursion
A Python module to simulate recursive function calls using iteration, providing explicit control over execution flow and avoiding stack overflow issues.
Overview
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.
Why Use This?
- Avoid Stack Overflow: Handle deep recursion without hitting Python's recursion limit
- Explicit Control: See and control the exact flow of function calls
- Debugging: Easier to trace execution without deep call stacks
- State Management: Shared environment for passing data between functions
- Safety: Built-in iteration limits to prevent infinite loops
When to Use This
This library is useful when you need:
- Deep recursion that exceeds Python's stack limit (~1000 calls)
- Explicit control over recursive execution flow
- To convert recursive algorithms to iterative ones systematically
- State machines or complex control flow patterns
Scope & limitations
This engine is essentially a trampoline / dispatcher: each step returns the name of the next function to run. That makes it a great fit for tail recursion (linear step-by-step recursion), workflows, and state machines.
It does not automatically emulate a full call stack. Patterns that require “returning to the caller”
(e.g. tree recursion like fib(n-1) + fib(n-2), post-order DFS reductions) require you to model an explicit
stack/frames (continuations) inside environment_variables.
For most cases, normal recursion is simpler and preferred. Use this when recursion depth or explicit control becomes a concern.
Documentation
📚 View interactive documentation on DeepWiki
Explore the full API reference, examples, and guides in an interactive format.
Installation
Using uv
uv add iterativerecursion
Using pip
pip install iterativerecursion
Quick Start
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
def greet(name: str) -> FunctionReturn:
print(f"Hello, {name}!")
return FunctionReturn(
returned_values={"next_name": "World"},
next_function_to_call="farewell",
arg_env_mapping={"name": "next_name"}
)
def farewell(name: str) -> FunctionReturn:
print(f"Goodbye, {name}!")
return FunctionReturn(
returned_values={}
# next_function_to_call defaults to None to terminate
)
# Create engine and register functions
engine = IterativeRecursionEngine()
engine.add_function(greet)
engine.add_function(farewell)
# Start execution
engine.start_function_caller(
next_function_to_call="greet",
environment_variables={"initial_name": "Alice"},
arg_env_mapping={"name": "initial_name"}
)
Output:
Hello, Alice!
Goodbye, World!
How It Works
Functions return a FunctionReturn dataclass instance with three attributes:
| Attribute | Type | Description |
|---|---|---|
returned_values |
dict[str, Any] |
Values to store in the shared environment |
next_function_to_call |
str | None |
Name of the next function to execute, or None to stop (default: None) |
arg_env_mapping |
dict[str, str] |
Mapping of parameter names to environment variable keys (default: auto-mapped from returned_values keys) |
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.
The engine maintains a shared environment where functions can store and retrieve values across calls.
Examples
Using the @register Decorator
The @register decorator provides a cleaner way to register functions:
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
engine = IterativeRecursionEngine()
@engine.register
def greet(name: str) -> FunctionReturn:
print(f"Hello, {name}!")
return FunctionReturn(
returned_values={"next_name": "World"},
next_function_to_call="farewell",
arg_env_mapping={"name": "next_name"}
)
@engine.register
def farewell(name: str) -> FunctionReturn:
print(f"Goodbye, {name}!")
return FunctionReturn(
returned_values={}
)
# Start execution and get final state
result = engine.start_function_caller(
next_function_to_call="greet",
environment_variables={"initial_name": "Alice"},
arg_env_mapping={"name": "initial_name"}
)
Factorial Calculation
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
def factorial_step(n: int, accumulator: int) -> FunctionReturn:
if n <= 1:
return FunctionReturn(
returned_values={"result": accumulator}
)
# Auto-mapping: {"n": n-1, "accumulator": ...} automatically maps to itself
return FunctionReturn(
returned_values={
"n": n - 1,
"accumulator": accumulator * n
},
next_function_to_call="factorial_step"
)
engine = IterativeRecursionEngine()
engine.add_function(factorial_step)
engine.start_function_caller(
next_function_to_call="factorial_step",
environment_variables={"n": 5, "accumulator": 1},
arg_env_mapping={"n": "n", "accumulator": "accumulator"}
)
print(f"5! = {engine.environment_variables['result']}") # Output: 5! = 120
Fibonacci Sequence (tail-recursive / iterative form)
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
def fibonacci(n: int, a: int, b: int) -> FunctionReturn:
if n == 0:
return FunctionReturn(
returned_values={"result": a}
)
# Auto-mapping handles {"n": "n", "a": "a", "b": "b"} automatically
return FunctionReturn(
returned_values={
"n": n - 1,
"a": b,
"b": a + b
},
next_function_to_call="fibonacci"
)
engine = IterativeRecursionEngine()
engine.add_function(fibonacci)
engine.start_function_caller(
next_function_to_call="fibonacci",
environment_variables={"n": 10, "a": 0, "b": 1},
arg_env_mapping={"n": "n", "a": "a", "b": "b"}
)
print(f"Fibonacci(10) = {engine.environment_variables['result']}") # Output: 55
Preventing Infinite Loops
Use the max_iterations parameter to prevent runaway execution:
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
def infinite_loop(counter: int) -> FunctionReturn:
print(f"Iteration: {counter}")
# Auto-mapping: no need for {"counter": "counter"}
return FunctionReturn(
returned_values={"counter": counter + 1},
next_function_to_call="infinite_loop"
)
engine = IterativeRecursionEngine()
engine.add_function(infinite_loop)
try:
engine.start_function_caller(
next_function_to_call="infinite_loop",
environment_variables={"counter": 0},
arg_env_mapping={"counter": "counter"},
max_iterations=10 # Safety limit
)
except RuntimeError as e:
print(f"Caught: {e}")
# Output: Caught: Maximum iteration limit (10) reached...
API Reference
IterativeRecursionEngine
The main execution engine for iterative recursion.
Methods
__init__()
Creates a new engine instance.
engine = IterativeRecursionEngine()
add_function(function)
Registers a function with the engine.
- Parameters:
function- A callable that returnsFunctionReturn - Returns: None
engine.add_function(my_function)
register(function)
Decorator to register a function with the engine. Alternative to add_function().
- Parameters:
function- A callable that returnsFunctionReturn - Returns: The same function (for chaining)
@engine.register
def my_function(x: int) -> FunctionReturn:
return FunctionReturn(
returned_values={"result": x * 2}
)
add_environment_variables(variables: dict[str, Any])
Adds or updates variables in the shared environment.
- Parameters:
variables- Dictionary of variable names and values - Returns: None
engine.add_environment_variables({"x": 10, "y": 20})
start_function_caller(next_function_to_call, environment_variables, arg_env_mapping, max_iterations=None)
Begins executing functions starting from the specified function.
- Parameters:
next_function_to_call(str): Name of the first function to callenvironment_variables(dict[str, Any]): Initial environment variablesarg_env_mapping(dict[str, str]): Parameter mapping for first functionmax_iterations(int | None): Maximum iterations allowed (default: None/unlimited)
- Returns:
dict[str, Any]- Final state of environment variables after execution - Raises:
KeyError: If function not found or environment variable missingRuntimeError: Ifmax_iterationslimit is reachedValueError: If function returns invalid structureTypeError: If function return has wrong types
result = engine.start_function_caller(
next_function_to_call="start_func",
environment_variables={"value": 42},
arg_env_mapping={"param": "value"},
max_iterations=1000
)
# Access final state directly from result
print(result["some_value"])
Attributes
functions_dict(dict): Registry of available functionsenvironment_variables(dict): Shared state accessible to all functions
Type Definitions
FunctionReturn
Dataclass defining the required return structure for functions.
@dataclass
class FunctionReturn:
returned_values: dict[str, Any]
next_function_to_call: str | None = None
arg_env_mapping: dict[str, str] = field(default_factory=dict)
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.
VarsDict
Type alias for variable dictionaries.
VarsDict = dict[str, Any]
Development
Running Tests
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/ -v
Test Coverage
The test suite includes:
- Basic function chaining
- Environment variable management
- Error handling and validation
- Runtime validation of return structures
- Iteration limits
- Decorator API (
@register) - Return value access
- Improved error messages
- Complex scenarios (factorial, state machines)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Author
Carlos A. Planchón - GitHub
Acknowledgments
This module was created as both a practical solution for deep recursion scenarios and an exploration of alternative execution patterns in Python.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file iterativerecursion-1.0.tar.gz.
File metadata
- Download URL: iterativerecursion-1.0.tar.gz
- Upload date:
- Size: 10.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
287078846648f6d3b1d221b34a5c12ffe9d48dcea3375e8bc34487d245dc9833
|
|
| MD5 |
b5a81e965d5baaef54120ecd611c1987
|
|
| BLAKE2b-256 |
e44728721ea07e91a1fe8c03bd294f98d8854f1359d395477f08585e86261ce6
|
File details
Details for the file iterativerecursion-1.0-py3-none-any.whl.
File metadata
- Download URL: iterativerecursion-1.0-py3-none-any.whl
- Upload date:
- Size: 8.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60435f13f6ccc16d3792d0d3beb674b5a9ba98162e8458f1e429af0b7e2f84b1
|
|
| MD5 |
d70ffa3c6175acc95620e8746f318efa
|
|
| BLAKE2b-256 |
0ced0c0828b2dc8998ab5810dbf00782d93e7d55e3d2f2c77ace7b6e2760bfca
|