Skip to main content

iterativerecursion

iterativerecursion banner

CI PyPI version Python versions License: MIT DeepWiki

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.

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 returns FunctionReturn
  • 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 returns FunctionReturn
  • 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 | None): Name of the first function to call. Pass None to terminate immediately after recording environment_variables.
    • environment_variables (dict[str, Any]): Initial environment variables
    • arg_env_mapping (dict[str, str]): Parameter mapping for first function
    • max_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 missing
    • RuntimeError: If max_iterations limit is reached
    • ValueError: If function returns invalid structure
    • TypeError: 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 functions
  • environment_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.

Metadata

Release files for iterativerecursion 1.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for iterativerecursion 1.1.3
File Size Uploaded Provenance
iterativerecursion-1.1.3.tar.gz 11.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for iterativerecursion 1.1.3
File Interpreter ABI Platform
iterativerecursion-1.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 19.7 kB

Release files / iterativerecursion-1.1.3.tar.gz

Download URL iterativerecursion-1.1.3.tar.gz
Size 11.1 kB
Tags Source
SHA-256 checksum
How to use checksums
59977fb213ac1bc937a7cf5cb6831858132c4fa2aa3df79e518302960ca5129f
BLAKE2b-256 checksum
How to use checksums
6a0759af12afb52a9dfd6f22d0e6f57264a9039b5ae12a131eb886e90784f30b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.

Transparency log

Release files / iterativerecursion-1.1.3-py3-none-any.whl

Download URL iterativerecursion-1.1.3-py3-none-any.whl
Size 8.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7bf8b8fb2b73367c2ce37149905f853d12dc96351899b3fc7ceb625c00a0dca2
BLAKE2b-256 checksum
How to use checksums
75618589f24da7a129182c0a2bdc590ea96dbc29099e31184dbae30b52c678c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.

Transparency log

Security for iterativerecursion 1.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for iterativerecursion-1.1.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page