Small library to dynamically create python functions.
—
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.
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
"""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"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.0from 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"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)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__)The generated functions automatically handle argument passing to the implementation function:
*args arguments are passed as positional arguments**kwargs arguments are passed as keyword argumentsSignature 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"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