CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-makefun

Small library to dynamically create python functions.

Pending
Overview
Eval results
Files

function-creation.mddocs/

Dynamic Function Creation

Core functionality for creating functions dynamically from signature specifications. This capability enables runtime generation of functions with precise signature control, proper introspection support, and comprehensive metadata management.

Capabilities

Function Creation from Signatures

Creates functions dynamically from string signatures or Signature objects, with full control over metadata and behavior.

def create_function(
    func_signature: Union[str, Signature],
    func_impl: Callable[[Any], Any],
    func_name: str = None,
    inject_as_first_arg: bool = False,
    add_source: bool = True,
    add_impl: bool = True,
    doc: str = None,
    qualname: str = None,
    co_name: str = None,
    module_name: str = None,
    **attrs
) -> FunctionType:
    """
    Creates a function with signature func_signature that calls func_impl.
    
    Parameters:
    - func_signature: Union[str, Signature]
        Function signature specification. Can be:
        - String without 'def': "foo(a, b: int, *args, **kwargs)" or "(a, b: int)"
        - Signature object from inspect.signature() or manually created
    - func_impl: Callable[[Any], Any]
        Implementation function that will be called by generated function
    - func_name: str, optional
        Override for __name__ and __qualname__ attributes
    - inject_as_first_arg: bool, default False
        If True, inject created function as first positional argument to func_impl
    - add_source: bool, default True
        Add __source__ attribute containing generated function source code
    - add_impl: bool, default True
        Add __func_impl__ attribute pointing to func_impl
    - doc: str, optional
        Docstring for generated function. Defaults to func_impl.__doc__
    - qualname: str, optional
        Qualified name for generated function
    - co_name: str, optional
        Name for compiled code object
    - module_name: str, optional
        Module name for generated function. Defaults to func_impl.__module__
    - **attrs: dict
        Additional attributes to set on generated function
        
    Returns:
    FunctionType: Generated function with specified signature
    
    Raises:
    TypeError: If func_signature has invalid type
    SyntaxError: If signature string is malformed
    ValueError: If co_name is invalid
    """

Usage Examples

Basic Function Creation from String

from makefun import create_function

def my_impl(name, age, city="Unknown"):
    return f"{name} ({age}) from {city}"

# Create function with specific signature
greet = create_function("greet(person_name: str, person_age: int, location: str = 'Unknown')", 
                       my_impl)

print(greet("Alice", 25))  # "Alice (25) from Unknown"
print(greet("Bob", 30, "New York"))  # "Bob (30) from New York"

Function Creation from Signature Object

from makefun import create_function
from inspect import signature, Signature, Parameter

def calculator(operation, x, y):
    operations = {"add": x + y, "sub": x - y, "mul": x * y}
    return operations.get(operation, "Unknown operation")

# Create signature object manually
params = [
    Parameter('op', Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
    Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, annotation=float),
    Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, annotation=float)
]
sig = Signature(parameters=params, return_annotation=float)

# Generate function with Signature object
calc = create_function(sig, calculator, func_name="calculate")
print(calc("add", 5.0, 3.0))  # 8.0

Advanced Metadata Control

from makefun import create_function

def internal_handler(*args, **kwargs):
    return f"Processed: {args}, {kwargs}"

# Create function with extensive metadata customization
processor = create_function(
    "process_data(data: list, options: dict = None, verbose: bool = False)",
    internal_handler,
    func_name="process_data",
    doc="Process data with optional configuration and verbosity control.",
    qualname="DataProcessor.process_data",
    module_name="data_processing",
    add_source=True,
    add_impl=True,
    version="1.0",
    author="Data Team"
)

print(processor.__name__)      # "process_data"
print(processor.__doc__)       # "Process data with optional configuration..."
print(processor.__module__)    # "data_processing"
print(processor.version)       # "1.0"
print(processor.author)        # "Data Team"

Generator and Coroutine Support

from makefun import create_function
import asyncio

def my_generator(*args, **kwargs):
    for i in range(3):
        yield f"Item {i}: {args}, {kwargs}"

async def my_coroutine(*args, **kwargs):
    await asyncio.sleep(0.1)
    return f"Async result: {args}, {kwargs}"

# Create generator function
gen_func = create_function("generate_items(count: int, prefix: str)", my_generator)
for item in gen_func(3, "test"):
    print(item)

# Create coroutine function  
async_func = create_function("fetch_data(url: str, timeout: int = 30)", my_coroutine)
result = asyncio.run(async_func("https://api.example.com", 60))
print(result)

Lambda Function Generation

from makefun import create_function

def simple_impl(x, y):
    return x + y

# Function name not valid identifier -> creates lambda
lambda_func = create_function("123invalid(x, y)", simple_impl)
print(lambda_func.__name__)  # "<lambda>"

# Explicit lambda creation
lambda_func2 = create_function("(x: int, y: int)", simple_impl, co_name="<lambda>")
print(lambda_func2.__name__)  # None (lambda functions have no __name__)

Function Argument Handling

The generated functions automatically handle argument passing to the implementation function:

  • Keyword Arguments: Most arguments are passed as keywords when possible for maximum flexibility
  • Positional-Only: Arguments marked as positional-only are passed positionally
  • Var-Positional: *args arguments are passed as positional arguments
  • Var-Keyword: **kwargs arguments are passed as keyword arguments

Signature String Format

Signature strings support the full Python function signature syntax:

# Basic parameters
"func(a, b, c)"

# Type annotations
"func(a: int, b: str, c: float)"

# Default values
"func(a: int, b: str = 'default', c: float = 1.0)"

# Var-positional and var-keyword
"func(a: int, *args, **kwargs)"

# Keyword-only parameters (Python 3+)
"func(a: int, *, b: str, c: float = 1.0)"

# Positional-only parameters (Python 3.8+)
"func(a: int, b: str, /, c: float)"

# Return type annotation
"func(a: int, b: str) -> str"

# Complex example
"process(data: list, /, mode: str, *extra, timeout: float = 30.0, **options) -> dict"

Error Handling

Common error scenarios and their handling:

from makefun import create_function

# Invalid signature format
try:
    create_function("invalid syntax here", lambda: None)
except SyntaxError as e:
    print(f"Signature error: {e}")

# Invalid function name  
try:
    create_function("func(x)", lambda x: x, co_name="123invalid")
except ValueError as e:
    print(f"Name error: {e}")

# Type mismatch
try:
    create_function(123, lambda: None)  # Should be str or Signature
except TypeError as e:
    print(f"Type error: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-makefun

docs

compilation.md

decorators.md

function-creation.md

index.md

partial.md

signature-utils.md

wrapping.md

tile.json