### Install makefun Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Install the makefun library using pip. ```bash > pip install makefun ``` -------------------------------- ### Install nox environment requirements Source: https://github.com/smarie/python-makefun/blob/main/README.md Set up a Python 3.7 environment with conda and install the necessary requirements for nox. ```bash >>> conda create -n noxenv python="3.7" >>> activate noxenv (noxenv) >>> pip install -r noxfile-requirements.txt ``` -------------------------------- ### Build and Serve Documentation Locally with Nox Source: https://github.com/smarie/python-makefun/blob/main/README.md Use this command to build and serve a local version of the documentation site. It installs necessary dependencies and runs mkdocs serve. ```bash >>> nox -s docs nox > Running session docs-3.7 nox > Creating conda env in .nox\docs-3-7 with python=3.7 nox > [docs] Installing requirements with pip: ['mkdocs-material', 'mkdocs', 'pymdown-extensions', 'pygments'] nox > python -m pip install mkdocs-material mkdocs pymdown-extensions pygments nox > mkdocs serve -f ./docs/mkdocs.yml INFO - Building documentation... INFO - Cleaning site directory INFO - The following pages exist in the docs directory, but are not included in the "nav" configuration: - long_description.md INFO - Documentation built in 1.07 seconds INFO - Serving on http://127.0.0.1:8000 INFO - Start watching changes ... ``` -------------------------------- ### Generated Source Code Example Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Example of the source code generated by `create_function` for a simple function signature. ```python def foo(b, a=0): return _func_impl_(b=b, a=a) ``` -------------------------------- ### Generated Source Code with *args and **kwargs Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Example of generated source code when the signature includes `*args` and `**kwargs`. ```python gen_func = create_function("foo(a=0, *args, **kwargs)", func_impl) print(gen_func.__source__) ``` -------------------------------- ### Support for Generators and Coroutines Source: https://context7.com/smarie/python-makefun/llms.txt makefun automatically handles generators, coroutines, and async generators when creating dynamic functions. The examples show creating a generator function and an async coroutine function using `create_function` and verifying their types. ```python from makefun import create_function, with_signature from inspect import isgeneratorfunction, iscoroutinefunction import asyncio # Generator function def range_squared_impl(start, end): """Generate squared values in range.""" for i in range(start, end): yield i * i squared_range = create_function("squared_range(start: int, end: int)", range_squared_impl) print(isgeneratorfunction(squared_range)) # Output: True print(list(squared_range(1, 5))) # Output: [1, 4, 9, 16] # Async coroutine function async def fetch_data_impl(url, timeout=30): """Simulate fetching data from URL.""" await asyncio.sleep(0.1) # Simulate network delay return f"Data from {url}" fetch_data = create_function( "fetch_data(url: str, timeout: int = 30) -> str", fetch_data_impl ) print(iscoroutinefunction(fetch_data)) # Output: True async def main(): result = await fetch_data("https://api.example.com") print(result) # Output: Data from https://api.example.com asyncio.run(main()) ``` -------------------------------- ### Create function without 'def ' prefix Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md Version 0.2.0 removed the requirement for the `create_function` signature string to start with 'def '. This simplifies the signature definition process. ```python from makefun import create_function # Previously required: "def greet(name):" new_func = create_function("greet(name)", "print(f'Hello {name}')") new_func('World') # Output: Hello World ``` -------------------------------- ### Run specific nox test session Source: https://github.com/smarie/python-makefun/blob/main/README.md Execute tests for a specific Python environment by specifying the session name, for example, 'tests-3.6'. ```bash nox -s tests-3.6 ``` -------------------------------- ### List available nox tasks Source: https://github.com/smarie/python-makefun/blob/main/README.md List all available lifecycle tasks defined in the noxfile.py using the nox command. ```bash >>> nox --list ``` -------------------------------- ### Test Wrapper Behavior with Different Call Modes Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Demonstrates calling the enhanced_foo wrapper with positional, keyword, and default arguments to verify its correct behavior. ```python >>> assert enhanced_foo(1, 2) == 3 # positional 'b' hello! b=2 >>> assert enhanced_foo(b=0, a=1) == 1 # keyword 'b' hello! b=0 >>> assert enhanced_foo(1) == 2 # default 'b' hello! b=1 ``` -------------------------------- ### Python Makefun @wraps Decorator Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Detailed documentation for the @wraps decorator, including its parameters, behavior, and comparison with functools.wraps and @with_signature. ```APIDOC ## `@wraps` Decorator ### Description A decorator to create a signature-preserving wrapper function. It is similar to `functools.wraps` but offers enhanced features like runtime argument validation and advanced signature modification. ### Method Decorator ### Endpoint N/A (Python Decorator) ### Parameters - **f** (function) - The function to wrap. - **new_sig** (Union[str, Signature], optional) - The new signature for the decorated function. Defaults to None (uses the signature of `f`). Can be a string or a `Signature` object. - **prepend_args** (Union[str, Parameter, Iterable[Union[str, Parameter]]], optional) - Arguments to prepend to the signature. Defaults to None. - **append_args** (Union[str, Parameter, Iterable[Union[str, Parameter]]], optional) - Arguments to append to the signature. Defaults to None. - **remove_args** (Union[str, Iterable[str]], optional) - Arguments to remove from the signature. Defaults to None. - **func_name** (str, optional) - Override the `__name__` and `__qualname__` of the created function. Defaults to None. - **inject_as_first_arg** (bool, optional) - Whether to inject arguments as the first argument. Defaults to False. - **add_source** (bool, optional) - Whether to add source code information. Defaults to True. - **add_impl** (bool, optional) - Whether to add implementation details. Defaults to True. - **doc** (str, optional) - Override the `__doc__` attribute. Defaults to None. - **qualname** (str, optional) - Override the `__qualname__` attribute. Defaults to None. - **co_name** (str, optional) - Override the `__code__.co_name` attribute. Defaults to None. - **module_name** (str, optional) - Override the `__module__` attribute. Defaults to None. - **attrs** (dict, optional) - Additional attributes to add to the wrapper function. ### Request Example ```python from makefun import wraps def my_decorator(f): @wraps(f) def wrapper(*args, **kwargs): # Wrapper logic return f(*args, **kwargs) return wrapper ``` ### Response #### Success Response (200) N/A (Python Decorator - returns a decorated function) #### Response Example N/A ``` -------------------------------- ### Remove Parameters from Function Signature Source: https://context7.com/smarie/python-makefun/llms.txt Utilizes `remove_signature_parameters` to remove parameters from a `Signature` object by name. This is useful for creating wrappers that simplify function interfaces. The example shows removing single or multiple parameters and practical application with `@wraps`. ```python from makefun import remove_signature_parameters, wraps from inspect import signature def full_function(a, b, c, d=None, e=None): """Function with many parameters.""" return {'a': a, 'b': b, 'c': c, 'd': d, 'e': e} orig_sig = signature(full_function) print(f"Original: {orig_sig}") # Output: Original: (a, b, c, d=None, e=None) # Remove single parameter new_sig = remove_signature_parameters(orig_sig, 'c') print(f"Without c: {new_sig}") # Output: Without c: (a, b, d=None, e=None) # Remove multiple parameters new_sig = remove_signature_parameters(orig_sig, 'c', 'd', 'e') print(f"Simplified: {new_sig}") # Output: Simplified: (a, b) # Practical use: create a wrapper that injects removed parameters @wraps(full_function, remove_args=['c', 'd']) def simplified_function(*args, **kwargs): # Inject the removed parameters with default values kwargs['c'] = 'injected_c' kwargs['d'] = 'injected_d' return full_function(*args, **kwargs) result = simplified_function(1, 2, e='provided_e') print(result) # Output: {'a': 1, 'b': 2, 'c': 'injected_c', 'd': 'injected_d', 'e': 'provided_e'} ``` -------------------------------- ### Create Partial Functions with makefun.partial Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Utilize makefun.partial to create a new function with some arguments pre-filled, similar to functools.partial. This function preserves the original signature and updates the docstring to reflect the partial application. ```python from makefun import partial def foo(x, y): """ a `foo` function :param x: :param y: :return: """ return x + y bar = partial(foo, x=12) # we can test it: assert bar(1) == 13 help(bar) ``` -------------------------------- ### Creating Partial Functions with Dynamic Signatures using partial Source: https://context7.com/smarie/python-makefun/llms.txt Use `makefun.partial` to create partial functions with dynamically updated signatures, similar to `functools.partial` but with proper signature preservation and enhanced documentation. This is useful for pre-setting arguments. ```python from makefun import partial def power(base: int, exponent: int, mod: int = None) -> int: """ Calculate base raised to exponent, optionally with modulo. :param base: The base number :param exponent: The exponent :param mod: Optional modulo value :return: Result of base^exponent (mod if specified) """ result = base ** exponent return result % mod if mod else result # Create partial with preset positional argument square = partial(power, exponent=2) cube = partial(power, exponent=3) print(square(5)) # Output: 25 print(cube(3)) # Output: 27 print(square(4, mod=10)) # Output: 6 (16 % 10) # Check the updated signature and documentation from inspect import signature print(signature(square)) # Output: (base: int, mod: int = None) -> int # Documentation is automatically enhanced print(square.__doc__) # # Calculate base raised to exponent... # Create partial with multiple preset arguments mod_10_square = partial(power, exponent=2, mod=10) print(mod_10_square(7)) # Output: 9 (49 % 10) print(signature(mod_10_square)) # Output: (base: int) -> int ``` -------------------------------- ### Generator-based Coroutine with makefun Source: https://context7.com/smarie/python-makefun/llms.txt Demonstrates creating a generator-based coroutine using makefun's with_signature decorator. This pattern is useful for implementing stateful generators that can receive values via send(). ```python from makefun import with_signature @with_signature("echo_server(initial_message: str = 'Ready')") def echo_impl(initial_message): """Echo server that yields received messages.""" message = yield initial_message while message is not None: message = yield f"Echo: {message}" server = echo_server("Server started") print(next(server)) # Output: Server started print(server.send("Hello")) # Output: Echo: Hello print(server.send("World")) # Output: Echo: World ``` -------------------------------- ### Partial Function Application Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Creates a partial function application similar to `functools.partial`, but with improved documentation and naming for the resulting function. ```python def partial(f: Callable, *preset_pos_args, **preset_kwargs ): ``` -------------------------------- ### Create Function Ex Nihilo Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Dynamically create a function with a specified signature and implementation. The signature is provided as a string, and the implementation is a separate function. The generated function will have the specified signature and use the provided implementation. ```python from makefun import create_function # (1) define the signature. Warning: do not put 'def' keyword here! func_sig = "foo(b, a=0)" # (2) define the function implementation def func_impl(*args, **kwargs): """This docstring will be used in the generated function by default""" print("func_impl called !") return args, kwargs # (3) create the dynamic function gen_func = create_function(func_sig, func_impl) ``` -------------------------------- ### Inject Function Reference as First Argument Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Demonstrates injecting the called function as the first argument to the implementation using `inject_as_first_arg=True`. ```python def core_impl(f, *args, **kwargs): print("This is generic core called by %s" % f.__name__) # here you could use f.__name__ in a if statement to determine what to do if f.__name__ == "func1": print("called from func1 !") return args, kwargs # generate 2 functions func1 = create_function("func1(a, b)", core_impl, inject_as_first_arg=True) func2 = create_function("func2(a, d)", core_impl, inject_as_first_arg=True) func1(1, 2) func2(1, 2) ``` -------------------------------- ### Create Function with String Signature Source: https://context7.com/smarie/python-makefun/llms.txt Use `create_function` to generate a new function with a specified signature that delegates to an implementation function. The signature can be a string or an `inspect.Signature` object. Arguments are passed as keywords to the implementation when possible. Invalid calls raise `TypeError` before reaching the implementation. ```python from makefun import create_function # Define signature as a string with type hints func_signature = "greet(name: str, greeting: str = 'Hello') -> str" # Implementation receives arguments as keywords def greet_impl(*args, **kwargs): """Greet someone with a custom greeting.""" name = kwargs['name'] greeting = kwargs['greeting'] return f"{greeting}, {name}!" # Create the dynamic function greet = create_function(func_signature, greet_impl) # Usage - signature is enforced print(greet("Alice")) # Output: Hello, Alice! print(greet("Bob", greeting="Hi")) # Output: Hi, Bob! print(greet(name="Carol", greeting="Hey")) # Output: Hey, Carol! # Invalid calls raise TypeError before reaching implementation try: greet() # Missing required argument except TypeError as e: print(f"Error: {e}") # Error: greet() missing 1 required positional argument: 'name' # View generated source code print(greet.__source__) # def greet(name: str, greeting: str = 'Hello') -> str: # return _func_impl_(name=name, greeting=greeting) ``` -------------------------------- ### Create Signature-Preserving Wrapper with @wraps Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Use @makefun.wraps to create a wrapper that reliably accesses arguments by name and ensures the wrapper code only executes with valid arguments. This is an alternative to functools.wraps. ```python from makefun import wraps # a dummy function def foo(a, b=1): """ foo doc """ return a + b # our signature-preserving wrapper @wraps(foo) def enhanced_foo(*args, **kwargs): print('hello!') print('b=%s' % kwargs['b']) # we can reliably access 'b' return foo(*args, **kwargs) ``` -------------------------------- ### Helper Functions for Signature Manipulation Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Introduces makefun's helper functions, add_signature_parameters and remove_signature_parameters, for easier editing of function signatures. ```python from makefun import add_signature_parameters, remove_signature_parameters def foo(b, c, a=0): pass # original signature foo_sig = signature(foo) print("original signature: %s" % foo_sig) ``` -------------------------------- ### partial Decorator Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Provides an equivalent of `functools.partial` but generates a dynamically-created function that appears nicer in documentation and introspection. ```APIDOC ## @partial ### Description Equivalent of `functools.partial` but relies on a dynamically-created function. As a result, the function looks nicer to users in terms of apparent documentation, name, etc. See [documentation](./index.md#removing-parameters-easily) for details. ### Method `partial` ### Parameters #### Positional Arguments - **f** (Callable) - The function to partially apply. #### Variable Positional Arguments - ***preset_pos_args** (Any) - Preset positional arguments. #### Variable Keyword Arguments - ****preset_kwargs** (Any) - Preset keyword arguments. ``` -------------------------------- ### Generated Source Code with *args and **kwargs Output Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md The resulting source code for a function created with `*args` and `**kwargs` in its signature. ```python def foo(a=0, *args, **kwargs): return _func_impl_(a=a, *args, **kwargs) ``` -------------------------------- ### Add Parameters to Function Signature Source: https://context7.com/smarie/python-makefun/llms.txt Demonstrates adding parameters to an existing function signature using `add_signature_parameters`. Parameters can be added at the beginning or end, and multiple parameters can be added at once. Strings are automatically converted to `Parameter` objects. ```python from makefun import add_signature_parameters from inspect import Parameter # Assume orig_sig is an existing inspect.Signature object # Example: orig_sig = signature(lambda a, b, c=0: None) # Add parameter at the beginning new_sig = add_signature_parameters( orig_sig, first=Parameter('prefix', kind=Parameter.POSITIONAL_OR_KEYWORD) ) print(f"With prefix: {new_sig}") # Output: With prefix: (prefix, a, b, c=0) # Add parameter at the end new_sig = add_signature_parameters( orig_sig, last=Parameter('suffix', kind=Parameter.KEYWORD_ONLY, default=None) ) print(f"With suffix: {new_sig}") # Output: With suffix: (a, b, c=0, *, suffix=None) # Add multiple parameters new_sig = add_signature_parameters( orig_sig, first=['x', 'y'], # Strings are auto-converted to Parameters last=[Parameter('z', kind=Parameter.KEYWORD_ONLY, default=True)] ) print(f"With multiple: {new_sig}") # Output: With multiple: (x, y, a, b, c=0, *, z=True) # Use in function creation @with_signature(add_signature_parameters(orig_sig, first='context')) def enhanced_original(*args, **kwargs): ctx = kwargs['context'] print(f"Context: {ctx}, a={kwargs['a']}, b={kwargs['b']}, c={kwargs['c']}") enhanced_original('my_ctx', 1, 2, c=3) # Output: Context: my_ctx, a=1, b=2, c=3 ``` -------------------------------- ### Handle variable-length arguments (*args, **kwargs) Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md Version 0.2.0 ensures that variable-length arguments like `*args` and `**kwargs` are correctly handled by `create_function`. The generated functions will properly accept and process these arguments. ```python from makefun import create_function new_func = create_function("def process_all(a, *args, **kwargs):", "print(f'a={a}, args={args}, kwargs={kwargs}')") new_func(1, 2, 3, key1='val1', key2='val2') ``` -------------------------------- ### create_function API Source: https://context7.com/smarie/python-makefun/llms.txt Creates a new function with a specified signature that delegates to an implementation function. The signature can be provided as a string or an inspect.Signature object. Arguments are automatically passed as keyword arguments to the implementation when possible. ```APIDOC ## POST /create_function ### Description Creates a new function with a specified signature that delegates to an implementation function. The signature can be provided as a string (e.g., `'foo(a, b=1)'`) or as an `inspect.Signature` object. Arguments are automatically passed as keyword arguments to the implementation when possible. ### Method POST ### Endpoint /create_function ### Parameters #### Request Body - **func_signature** (string) - Required - The desired signature for the new function, e.g., `'greet(name: str, greeting: str = 'Hello') -> str'`. - **implementation** (function) - Required - The function that will be called to execute the logic. ### Request Example ```json { "func_signature": "greet(name: str, greeting: str = 'Hello') -> str", "implementation": "def greet_impl(*args, **kwargs):\n name = kwargs['name']\n greeting = kwargs['greeting']\n return f\"{greeting}, {name}!\"" } ``` ### Response #### Success Response (200) - **function** (function) - The newly created function with the specified signature. #### Response Example ```json { "function": "" } ``` ``` -------------------------------- ### Run all nox test sessions Source: https://github.com/smarie/python-makefun/blob/main/README.md Execute all defined nox test sessions, which includes running pytest on all supported Python environments and generating reports. ```bash nox ``` -------------------------------- ### Support return annotations in create_function Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md Version 0.2.0 added support for return annotations in `create_function`. The annotation is correctly parsed and applied to the generated function's signature. ```python from makefun import create_function new_func = create_function("def add(a: int, b: int) -> int:", "return a + b") # The return annotation 'int' is correctly set ``` -------------------------------- ### Handle type hints as comments Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md Version 0.2.0 supports type hints written as comments (e.g., `# type: int`). However, be aware that `inspect.signature` might lose these annotations in certain cases, affecting the generated function's metadata. ```python from makefun import create_function # Type hint as comment new_func = create_function("def process(data): # type: (str) -> int", "return int(data)") # Note: inspect.signature might not preserve these annotations ``` -------------------------------- ### Create Generator Functions with create_function Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md The create_function utility automatically detects if the implementation is a generator and creates a generator function. This allows dynamic creation of generator functions with custom signatures. ```python from makefun import create_function from inspect import isgeneratorfunction # define the implementation def my_generator_impl(b, a=0): for i in range(a, b): yield i * i # create the dynamic function gen_func = create_function("foo(a, b)", my_generator_impl) # verify that the new function is a generator and behaves as such assert isgeneratorfunction(gen_func) assert list(gen_func(1, 4)) == [1, 4, 9] ``` -------------------------------- ### Create Wrapper Function Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Creates a signature-preserving wrapper function. This is equivalent to using `functools.wraps` with additional signature manipulation capabilities. ```python def create_wrapper(wrapped, wrapper, new_sig: Union[str, Signature] = None, prepend_args: Union[str, Parameter, Iterable[Union[str, Parameter]]] = None, append_args: Union[str, Parameter, Iterable[Union[str, Parameter]]] = None, remove_args: Union[str, Iterable[str]] = None, 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 ): ``` -------------------------------- ### Using with_signature Decorator Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md This is the general syntax for applying the @with_signature decorator to a function implementation. The decorator takes arguments to define the new function's signature and metadata. ```python @with_signature() def impl(...): ... ``` -------------------------------- ### Wrapper with Prepending Arguments using wraps Source: https://context7.com/smarie/python-makefun/llms.txt Use `@wraps(compute, prepend_args='debug')` to add a new argument 'debug' at the beginning of the function signature. This is useful for conditionally enabling debug output. ```python from makefun import wraps def compute(x, y): """Compute x + y.""" return x + y @wraps(compute, prepend_args='debug') def compute_with_debug(debug, *args, **kwargs): if debug: print(f"Debug: x={kwargs['x']}, y={kwargs['y']}") return compute(*args, **kwargs) compute_with_debug(True, 5, 10) # Debug: x=5, y=10 -> returns 15 compute_with_debug(False, 5, 10) # returns 15 (no debug output) ``` -------------------------------- ### create_wrapper Function Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Creates a signature-preserving wrapper function. It is equivalent to using `functools.wraps` but with additional flexibility for signature manipulation. ```APIDOC ## create_wrapper ### Description Creates a signature-preserving wrapper function. `create_wrapper(wrapped, wrapper, **kwargs)` is equivalent to `wraps(wrapped, **kwargs)(wrapper)`. See [`@wraps`](#wraps). ### Method `create_wrapper` ### Parameters #### Keyword Parameters - **wrapped** (Callable) - The function to be wrapped. - **wrapper** (Callable) - The function that will act as the wrapper. - **new_sig** (Union[str, Signature], optional) - The new signature for the wrapper function. - **prepend_args** (Union[str, Parameter, Iterable[Union[str, Parameter]]], optional) - Arguments to prepend to the signature. - **append_args** (Union[str, Parameter, Iterable[Union[str, Parameter]]], optional) - Arguments to append to the signature. - **remove_args** (Union[str, Iterable[str]], optional) - Names of arguments to remove from the signature. - **func_name** (str, optional) - The name of the wrapper function. - **inject_as_first_arg** (bool, optional) - If `True`, the created function will be injected as the first positional argument of the decorated function. Default=`False`. - **add_source** (bool, optional) - A boolean indicating if a '__source__' annotation should be added to the generated function. Default=`True`. - **add_impl** (bool, optional) - A boolean indicating if a '__func_impl__' annotation should be added to the generated function. Default=`True`. - **doc** (str, optional) - A string representing the docstring for the generated function. If `None`, the doc of `wrapped_fun` will be used. - **qualname** (str, optional) - A string representing the qualified name to be used. If `None`, defaults to the `__qualname__` of `wrapped_fun`. - **co_name** (str, optional) - A string representing the name to be used in the compiled code of the function. If `None`, defaults based on `func_signature`. - **module_name** (str, optional) - The name of the module to be set on the function. If `None`, defaults to the `__module__` of `wrapped_fun`. - **attrs** (dict, optional) - Other keyword attributes to set on the function. ``` -------------------------------- ### Define create_function Signature Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md This defines the signature for the create_function utility. It allows for dynamic function creation with various customization options. ```python 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): ``` -------------------------------- ### Edit Function Signature by Adding a Parameter Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Demonstrates how to modify a function's signature using inspect.signature and makefun.wraps to add a new parameter 'z' as the first argument. ```python from makefun import wraps from inspect import signature, Parameter # (0) the reference function def foo(b, a=0): print("foo called: b=%s, a=%s" % (b, a)) return b, a # (1a) capture the signature of reference function `foo` foo_sig = signature(foo) print("Original Signature: %s" % foo_sig) # (1b) modify the signature to add a new parameter 'z' as first argument params = list(foo_sig.parameters.values()) params.insert(0, Parameter('z', kind=Parameter.POSITIONAL_OR_KEYWORD)) new_sig = foo_sig.replace(parameters=params) print("New Signature: %s" % new_sig) # (2) define the wrapper implementation @wraps(foo, new_sig=new_sig) def foo_wrapper(z, *args, **kwargs): print("foo_wrapper called ! z=%s" % z) # call the foo function output = foo(*args, **kwargs) # return augmented output return z, output # call it assert foo_wrapper(3, 2) == (3, (2, 0)) ``` -------------------------------- ### Create Feature Branch and Pull Changes Source: https://github.com/smarie/python-makefun/blob/main/README.md Commands for checking out a new branch and pulling changes from a specific feature branch on GitHub. Use `--ff-only` for a fast-forward merge if possible, otherwise perform a standard auto-merge. ```bash git checkout -b - main git pull https://github.com//python-makefun.git --no-commit --ff-only ``` ```bash git pull https://github.com//python-makefun.git --no-commit ``` -------------------------------- ### Define Function Signature using inspect.Signature Object Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Programmatically define a function signature using `inspect.Signature` and `inspect.Parameter` objects. When using a `Signature` object, an explicit `func_name` must be provided to `@with_signature`. ```python from makefun import with_signature from inspect import Signature, Parameter # (1) define the signature using objects. parameters = [Parameter('b', kind=Parameter.POSITIONAL_OR_KEYWORD), Parameter('a', kind=Parameter.POSITIONAL_OR_KEYWORD, default=0), ] func_sig = Signature(parameters) func_name = 'foo' # (2) define the function @with_signature(func_sig, func_name=func_name) def gen_func(*args, **kwargs): """This docstring will be used in the generated function by default""" print("func_impl called !") return args, kwargs ``` -------------------------------- ### compile_fun Utility Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Provides pseudo-compilation capabilities for functions. ```APIDOC ## compile_fun ### Description Provides pseudo-compilation capabilities for functions. ``` -------------------------------- ### Creating Signature-Preserving Wrappers with create_wrapper Source: https://context7.com/smarie/python-makefun/llms.txt Use `create_wrapper` to build a signature-preserving wrapper function without using decorator syntax. This allows for direct control over the wrapper implementation and signature. ```python from makefun import create_wrapper def base_function(x: float, y: float) -> float: """Multiply two numbers.""" return x * y def wrapper_impl(*args, **kwargs): print(f"Multiplying {kwargs['x']} * {kwargs['y']}") return base_function(*args, **kwargs) # Create wrapper function directly multiply = create_wrapper(base_function, wrapper_impl) result = multiply(3.0, 4.0) # Output: Multiplying 3.0 * 4.0 print(result) # Output: 12.0 print(multiply.__name__) # Output: base_function print(multiply.__doc__) # Output: Multiply two numbers. ``` ```python from makefun import create_wrapper def base_function(x: float, y: float) -> float: """Multiply two numbers.""" return x * y def validate_and_call(*args, **kwargs): value = kwargs['value'] if value < 0: raise ValueError("Value must be non-negative") return base_function(value, kwargs['y']) validated_multiply = create_wrapper( base_function, validate_and_call, new_sig="validated_multiply(value: float, y: float) -> float", doc="Multiply value by y, ensuring value is non-negative." ) print(validated_multiply(5.0, 2.0)) # Output: 10.0 ``` -------------------------------- ### wraps API Source: https://context7.com/smarie/python-makefun/llms.txt A decorator for creating signature-preserving function wrappers, similar to `functools.wraps` but with actual signature enforcement. Supports modifying signatures via `prepend_args`, `append_args`, and `remove_args` parameters. ```APIDOC ## POST /wraps ### Description A decorator that creates signature-preserving function wrappers. It enhances `functools.wraps` by enforcing the original function's signature and allowing modifications like prepending, appending, or removing arguments. ### Method POST ### Endpoint /wraps ### Parameters #### Request Body - **wrapped_function** (function) - Required - The original function to wrap. - **prepend_args** (list) - Optional - Arguments to prepend to the wrapper's signature. - **append_args** (list) - Optional - Arguments to append to the wrapper's signature. - **remove_args** (list) - Optional - Argument names to remove from the wrapper's signature. ### Request Example ```json { "wrapped_function": "def original_function(a: int, b: int, c: int = 10) -> int:\n return a + b + c", "prepend_args": ["log_level: str = 'INFO'"], "remove_args": ["c"] } ``` ### Response #### Success Response (200) - **wrapper_function** (function) - The new wrapper function that preserves the signature of the original function. #### Response Example ```json { "wrapper_function": "" } ``` ``` -------------------------------- ### Generate function from Signature object Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md Version 0.3.0 allows creating functions directly from `inspect.Signature` objects, in addition to string signatures. This provides more flexibility in defining function signatures programmatically, especially for wrappers. ```python from inspect import Signature, Parameter from makefun import create_function params = [ Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, annotation=int), Parameter('b', Parameter.VAR_POSITIONAL) ] signature = Signature(parameters=params) def handler(a, *b): return a, b new_func = create_function(signature, handler) # new_func is created with the specified signature ``` -------------------------------- ### Create Partial Functions with @with_partial Decorator Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md The @with_partial decorator provides a convenient way to create partial function views for quick testing or simple use cases. It applies the partial application directly as a decorator. ```python from makefun import with_partial @with_partial(x=12) def foo(x, y): """ a `foo` function :param x: :param y: :return: """ return x + y ``` -------------------------------- ### Use function as signature template with create_function Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md In version 0.4.0, `create_function` was enhanced to accept a function object as a signature template. This allows you to define the signature of a new function by copying it from an existing one. ```python from makefun import create_function def template_func(x: int, y: str) -> bool: pass def handler(x, y): return str(x) + y new_func = create_function(template_func, handler) # new_func will have the signature of template_func ``` -------------------------------- ### create_function Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md Dynamically creates a Python function with a specified signature and implementation. ```APIDOC ## create_function ### Description Creates a function with signature `func_signature` that will call `func_impl` when called. All arguments received by the generated function will be propagated as keyword-arguments to `func_impl` when it is possible. `func_signature` can be provided as a string (e.g., `'foo(a, b: int, *args, **kwargs)'`) or as a `Signature` object. Metadata like `__name__`, `__qualname__`, `__doc__`, `__module__`, and `__code__.co_name` can be customized. Optionally, `__source__` and `__func_impl__` attributes can be added. A lambda function is created under specific conditions, such as when the function name is not a valid Python identifier or when `co_name` is set to ''. ### Method ```python 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 ) ``` ### Parameters #### func_signature - **Type**: `str` or `Signature` - **Description**: The signature for the new function. Can be a string like "foo(a, b: int, *args, **kwargs)" or a `Signature` object. If a string with a name is provided, that name is used for `__name__` and `__qualname__` by default. #### func_impl - **Type**: `Callable[[Any], Any]` - **Description**: The function to be called when the generated function is executed. Its signature must be compliant with `func_signature`. #### inject_as_first_arg - **Type**: `bool` - **Default**: `False` - **Description**: If `True`, the created function is injected as the first positional argument of `func_impl`. #### func_name - **Type**: `str` or `None` - **Default**: `None` - **Description**: Overrides the created function's `__name__` and `__qualname__`. Defaults to the name from `func_signature` string or `func_impl` if `func_signature` is a `Signature` object. #### add_source - **Type**: `bool` - **Default**: `True` - **Description**: If `True`, adds a `__source__` attribute with the generated function's source code. #### add_impl - **Type**: `bool` - **Default**: `True` - **Description**: If `True`, adds a `__func_impl__` attribute pointing to the implementation function. #### doc - **Type**: `str` or `None` - **Default**: `None` - **Description**: Sets the `__doc__` attribute. Defaults to `func_impl.__doc__`. #### qualname - **Type**: `str` or `None` - **Default**: `None` - **Description**: Sets the `__qualname__` attribute. Defaults to the qualified name from `func_signature` string or `func_impl`. #### co_name - **Type**: `str` or `None` - **Default**: `None` - **Description**: Sets the `__code__.co_name` attribute. Defaults to the function name or '' if the name is invalid. #### module_name - **Type**: `str` or `None` - **Default**: `None` - **Description**: Sets the `__module__` attribute. Defaults to `func_impl.__module__`. #### **attrs - **Type**: `Any` - **Description**: Additional attributes to be set on the created function. ### Request Example ```python from makefun import create_function from inspect import Signature, Parameter def my_implementation(x, y): return x + y # Example 1: Using a string signature add_func_str = create_function('add(a: int, b: int) -> int', my_implementation) print(add_func_str(5, 3)) # Example 2: Using a Signature object params = [ Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, annotation=int), Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, annotation=int) ] signature_obj = Signature(parameters=params) add_func_sig = create_function(signature_obj, my_implementation, func_name='add_sig') print(add_func_sig(5, 3)) # Example 3: Injecting the function itself as the first argument def process_data(func, data): return func(data=data) process_data_func = create_function('process(data: dict) -> dict', process_data, inject_as_first_arg=True) print(process_data_func({'key': 'value'})) ``` ### Response - **Type**: `Callable` - **Description**: The newly created function object. ``` -------------------------------- ### with_partial Decorator Source: https://github.com/smarie/python-makefun/blob/main/docs/api_reference.md A decorator that applies partial application to a function using the `partial` utility. ```APIDOC ## @with_partial ### Description Decorator to 'partialize' a function using [`partial`](#partial). ### Method `with_partial` ### Parameters #### Variable Positional Arguments - ***preset_pos_args** (Any) - Preset positional arguments. #### Variable Keyword Arguments - ****preset_kwargs** (Any) - Preset keyword arguments. ``` -------------------------------- ### Add signature parameters with add_signature_parameters Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md Version 1.0.0 introduced `add_signature_parameters`, a helper function to easily add parameters to an existing function's signature. This is useful for creating wrappers or modifying function interfaces. ```python from inspect import Signature from makefun import add_signature_parameters def original_func(a): pass new_sig = Signature([original_func.__signature__.parameters['a'], Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, annotation=int)]) modified_func = add_signature_parameters(original_func, new_sig) # modified_func now has parameters 'a' and 'b' ``` -------------------------------- ### Create Native Asyncio Coroutines with create_function Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md create_function can also be used to create native asyncio coroutines if the implementation is an async function. This allows for dynamic creation of coroutines with specified signatures. ```python from makefun import create_function from asyncio import sleep, get_event_loop # define the impl that should be called async def my_native_coroutine_impl(sleep_time): await sleep(sleep_time) return sleep_time # create the dynamic function gen_func = create_function("foo(sleep_time=2)", my_native_coroutine_impl) # verify that the new function is a native coroutine and behaves correctly out = get_event_loop().run_until_complete(gen_func(5)) assert out == 5 ``` -------------------------------- ### Modify Function Signature with add_signature_parameters and remove_signature_parameters Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Demonstrates how to programmatically alter a function's signature by adding and removing parameters. This is useful for creating flexible function wrappers or adapting existing functions. ```python from inspect import Parameter from makefun import add_signature_parameters, remove_signature_parameters # Assuming foo_sig is a signature object # Example: foo_sig = Signature([Parameter('b', Parameter.POSITIONAL_OR_KEYWORD), Parameter('c', Parameter.POSITIONAL_OR_KEYWORD), Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, default=0)]) new_sig = add_signature_parameters(foo_sig, first=Parameter('z', kind=Parameter.POSITIONAL_OR_KEYWORD), last=Parameter('o', kind=Parameter.POSITIONAL_OR_KEYWORD, default=True) ) new_sig = remove_signature_parameters(new_sig, 'b', 'a') print("modified signature: %s" % new_sig) ``` -------------------------------- ### Easier Edits with @wraps Prepend Args Source: https://github.com/smarie/python-makefun/blob/main/docs/index.md Use the @wraps decorator with the 'prepend_args' parameter to easily add arguments to the beginning of a function's signature. This is useful for creating wrapper functions that add context or control flow. ```python from makefun import wraps def foo(b, a=0): print("foo called: b=%s, a=%s" % (b, a)) return b, a @wraps(foo, prepend_args='z') def foo_wrapper(z, *args, **kwargs): print("foo_wrapper called ! z=%s" % z) # call the foo function output = foo(*args, **kwargs) # return augmented output return z, output # call it assert foo_wrapper(3, 2) == (3, (2, 0)) ``` -------------------------------- ### Adding Parameters to a Signature with add_signature_parameters Source: https://context7.com/smarie/python-makefun/llms.txt Use `add_signature_parameters` as a utility to add parameters to an existing `Signature` object. Parameters can be inserted at the beginning, end, or custom positions within the signature. ```python from makefun import add_signature_parameters, with_signature from inspect import signature, Parameter, Signature def original(a, b, c=0): """Original function.""" pass # Get the original signature orig_sig = signature(original) print(f"Original: {orig_sig}") # Output: Original: (a, b, c=0) ``` -------------------------------- ### Use string signature with @with_signature Source: https://github.com/smarie/python-makefun/blob/main/docs/changelog.md In version 1.0.2, `@with_signature` now correctly handles string signatures. This resolves an issue where providing a string directly caused problems. ```python from makefun import with_signature @with_signature("def foo(a: int, b: str) -> bool:") def bar(x, y): pass ``` -------------------------------- ### Decorator for Creating Partial Functions with with_partial Source: https://context7.com/smarie/python-makefun/llms.txt Use the `@with_partial` decorator to create a partial function by applying preset arguments to the decorated function. This simplifies the creation of specialized versions of existing functions. ```python from makefun import with_partial @with_partial(protocol='https', port=443) def build_url(protocol: str, host: str, port: int, path: str = '/') -> str: """Build a URL from components.""" return f"{protocol}://{host}:{port}{path}" # Original signature: (protocol, host, port, path='/') # New signature: (host, path='/') - protocol and port are preset print(build_url('example.com')) # Output: https://example.com:443/ print(build_url('api.example.com', '/v1')) # Output: https://api.example.com:443/v1 from inspect import signature print(signature(build_url)) # Output: (host: str, path: str = '/') -> str ```