### Install makefun Source: https://smarie.github.io/python-makefun Install the makefun library using pip. ```bash > pip install makefun ``` -------------------------------- ### Test makefun.wraps Wrapper Behavior (Keyword) Source: https://smarie.github.io/python-makefun Demonstrates the correct behavior of the makefun.wraps decorator when arguments are passed as keywords. ```python assert enhanced_foo(b=0, a=1) == 1 # keyword 'b' ``` -------------------------------- ### Get Lambda Argument String Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Returns the string representation of arguments suitable for a lambda function definition. It extracts the argument string from the signature. ```python def get_lambda_argument_string(func_signature, evaldict): """ Returns the string to be used as arguments in a lambda function definition. If there is a non-native symbol in the defaults, it is created as a variable in the evaldict :param func_name: :param func_signature: :return: """ return get_signature_string('', func_signature, evaldict)[1:-2] ``` -------------------------------- ### Create Partial Function with Signature Preservation Source: https://smarie.github.io/python-makefun Demonstrates `makefun.partial` for creating a partial function that preserves the original signature and updates documentation. This is useful for fixing some arguments while exposing the remaining ones clearly. ```python def foo(x, y): """ a `foo` function :param x: :param y: :return: """ return x + y from makefun import partial bar = partial(foo, x=12) ``` -------------------------------- ### Get Attribute Aware of Partial Functions Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Use `getattr_partial_aware` for attribute retrieval that correctly handles `functools.partial` objects by recursing into the wrapped function if necessary. ```python def getattr_partial_aware(obj, att_name, *att_default): """ Same as getattr but recurses in obj.func if obj is a partial """ val = getattr(obj, att_name, *att_default) if isinstance(obj, functools.partial) and \ (val is None or att_name == '__dict__' and len(val) == 0): return getattr_partial_aware(obj.func, att_name, *att_default) else: return val ``` -------------------------------- ### Get Caller Frame Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Retrieves the caller's frame object, used for introspection. It prioritizes `sys._getframe` for performance but includes a fallback for compatibility with different Python implementations. ```python def _get_callerframe(offset=0): try: # inspect.stack is extremely slow, the fastest is sys._getframe or inspect.currentframe(). # See https://gist.github.com/JettJones/c236494013f22723c1822126df944b12 frame = sys._getframe(2 + offset) # frame = currentframe() # for _ in range(2 + offset): # frame = frame.f_back except AttributeError: # for IronPython and similar implementations frame = None return frame ``` -------------------------------- ### Create New Function with makefun Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html This snippet demonstrates creating a new function with a specified signature, implementation, and various metadata. It's used when you need to generate a function dynamically, potentially overriding its original properties or injecting arguments. ```python def replace_f(f): return create_function(func_signature=func_signature, func_impl=f, func_name=func_name, inject_as_first_arg=inject_as_first_arg, add_source=add_source, add_impl=add_impl, doc=doc, qualname=qualname, co_name=co_name, module_name=module_name, _with_sig_=True, # special trick to tell create_function that we're @with_signature **attrs ) return replace_f ``` -------------------------------- ### Equivalence of @wraps(f) and @with_signature Source: https://smarie.github.io/python-makefun/api_reference Demonstrates the equivalence between using `@wraps(f)` and explicitly using `@with_signature` with parameters derived from the original function `f`. This highlights how `@wraps` simplifies the process of creating signature-preserving wrappers. ```python `@with_signature(signature(f), func_name=f.__name__, doc=f.__doc__, module_name=f.__module__, qualname=f.__qualname__, __wrapped__=f, **f.__dict__, **attrs)` ``` -------------------------------- ### Get Caller Frame Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Retrieves the caller's frame object. It uses `sys._getframe` for performance, falling back to `None` for compatibility with environments like IronPython. An offset can be provided to go further up the call stack. ```python def _get_callerframe(offset=0): try: # inspect.stack is extremely slow, the fastest is sys._getframe or inspect.currentframe(). # See https://gist.github.com/JettJones/c236494013f22723c1822126df944b12 frame = sys._getframe(2 + offset) # frame = currentframe() # for _ in range(2 + offset): # frame = frame.f_back except AttributeError: # for IronPython and similar implementations frame = None return frame ``` -------------------------------- ### Generate Partial Function Documentation Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html This function creates a docstring for a partial function, indicating the preset arguments and appending the original function's docstring. It constructs an 'equivalent signature' string to show how the partial function would be called. ```python def gen_partial_doc(wrapped_name, wrapped_doc, orig_sig, new_sig, preset_pos_args): """ Generate a documentation indicating which positional arguments and keyword arguments are set in this partial implementation, and appending the wrapped function doc. :param wrapped_name: :param wrapped_doc: :param orig_sig: :param new_sig: :param preset_pos_args: :return: """ # generate the "equivalent signature": this is the original signature, # where all values injected by partial appear all_strs = [] kw_only = False for i, (p_name, _p) in enumerate(orig_sig.parameters.items()): if i < len(preset_pos_args): # use the preset positional. Use repr() instead of str() so that e.g. "yes" appears with quotes all_strs.append(repr(preset_pos_args[i])) else: # use the one in the new signature pnew = new_sig.parameters[p_name] if not kw_only: if (PY2 and pnew.default is KW_ONLY) or pnew.kind == Parameter.KEYWORD_ONLY: kw_only = True if PY2 and kw_only: all_strs.append(str(pnew).replace("=%s" % KW_ONLY, "")) else: all_strs.append(str(pnew)) argstring = ", ".join(all_strs) # Write the final docstring if wrapped_doc is None or len(wrapped_doc) == 0: partial_doc = "\n" % (wrapped_name, argstring) else: new_line = " " \ "" % (wrapped_name, argstring, wrapped_name) partial_doc = new_line + wrapped_doc return partial_doc ``` -------------------------------- ### Get Signature String with Protected Symbols Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Constructs the signature string for a function, protecting non-native symbols in defaults or type hints by creating variables in the evaldict if necessary. Handles version-specific type hint availability. ```python no_type_hints_allowed = sys.version_info < (3, 5) # protect the parameters if needed new_params = [] params_changed = False for p_name, p in func_signature.parameters.items(): # if default value can not be evaluated, protect it default_needs_protection = _signature_symbol_needs_protection(p.default, evaldict) new_default = _protect_signature_symbol(p.default, default_needs_protection, "DEFAULT_%s" % p_name, evaldict) if no_type_hints_allowed: new_annotation = Parameter.empty annotation_needs_protection = new_annotation is not p.annotation else: # if type hint can not be evaluated, protect it annotation_needs_protection = _signature_symbol_needs_protection(p.annotation, evaldict) new_annotation = _protect_signature_symbol(p.annotation, annotation_needs_protection, "HINT_%s" % p_name, evaldict) # only create if necessary (inspect __init__ methods are slow) if default_needs_protection or annotation_needs_protection: # replace the parameter with the possibly new default and hint p = Parameter(p.name, kind=p.kind, default=new_default, annotation=new_annotation) params_changed = True new_params.append(p) if no_type_hints_allowed: new_return_annotation = Parameter.empty else: # if return type hint can not be evaluated, protect it return_needs_protection = _signature_symbol_needs_protection(func_signature.return_annotation, evaldict) new_return_annotation = _protect_signature_symbol(func_signature.return_annotation, return_needs_protection, "HINT_return", evaldict) # create new signature if needed if params_changed or return_needs_protection: new_signature = Signature(parameters=new_params, return_annotation=new_return_annotation) else: new_signature = func_signature return str(new_signature) ``` -------------------------------- ### Generated Source Code with Keyword Arguments Source: https://smarie.github.io/python-makefun Demonstrates the generated source code for a function where arguments are passed as keyword arguments to the implementation. ```python print(gen_func.__source__) ``` -------------------------------- ### Package Version Retrieval Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275___init___py.html Handles package versioning, attempting to import from a generated _version.py file or falling back to using setuptools_scm to determine the version from source control. ```python try: from ._version import version as __version__ except ImportError: from setuptools_scm import get_version as _gv from os import path as _path __version__ = _gv(_path.join(_path.dirname(__file__), _path.pardir, _path.pardir)) ``` -------------------------------- ### Test makefun.wraps Wrapper Behavior (Default) Source: https://smarie.github.io/python-makefun Demonstrates the correct behavior of the makefun.wraps decorator when using default argument values. ```python assert enhanced_foo(1) == 2 # default 'b' ``` -------------------------------- ### Wrapper Function Call with Argument Values Source: https://smarie.github.io/python-makefun/reports/junit/report.html Illustrates a wrapper function `foo_wrapper` being called, demonstrating how arguments are passed and processed, including the value of `z` and the default values for `a` and `b`. ```python foo_wrapper called ! z=3 foo called: b=2, a=0 ``` -------------------------------- ### Simplify Wrapper with `prepend_args` Source: https://smarie.github.io/python-makefun Shows how to use the `prepend_args` parameter in the `@wraps` decorator to easily add arguments to the beginning of a wrapped function's signature. This simplifies the process of creating wrappers with modified argument lists. ```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)) ``` -------------------------------- ### Add and Remove Parameters using makefun Helpers Source: https://smarie.github.io/python-makefun Illustrates using `add_signature_parameters` to prepend and append parameters, and `remove_signature_parameters` to remove specific parameters from a function's signature. This provides a more declarative way to edit signatures. ```python from inspect import signature, Parameter 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) # let's modify it 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) ``` -------------------------------- ### Function Creation with Eval Dictionary Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Creates a new function using the '_make' utility, passing the function name, parameter names, body, and an evaluation dictionary. This dictionary maps '_func_impl_' to the actual function implementation. ```python evaldict['_func_impl_'] = func_impl ``` ```python f = _make("lambda_", params_names, body, evaldict) ``` ```python f = _make(co_name, params_names, body, evaldict) ``` -------------------------------- ### Generate New Signature with Preset Arguments Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html This code constructs a new function signature by applying preset positional and keyword arguments to an original signature. It handles parameter kind changes, default value overrides, and ensures all remaining parameters become keyword-only. Raises ValueError for invalid preset arguments. ```python def _makefun_new_sig(f, orig_sig, preset_pos_args, preset_kwargs): # ... (rest of the function code from lines 1307-1367) if len(orig_sig.parameters) < len(preset_pos_args): raise ValueError("Cannot preset %s positional args, function %s has only %s args." "" % (len(preset_pos_args), getattr(f, '__name__', f), len(orig_sig.parameters))) new_params = [] kwonly_flag = False for i, (p_name, p) in enumerate(orig_sig.parameters.items()): if i < len(preset_pos_args): # preset positional arg: disappears from signature continue try: # is this parameter overridden in `preset_kwargs` ? overridden_p_default = preset_kwargs.pop(p_name) except KeyError: # no: it will appear "as is" in the signature, in the same order # However we need to change the kind if the kind is not already "keyword only" # positional only: Parameter.POSITIONAL_ONLY, VAR_POSITIONAL # both: POSITIONAL_OR_KEYWORD # keyword only: KEYWORD_ONLY, VAR_KEYWORD if kwonly_flag and p.kind not in (Parameter.VAR_KEYWORD, Parameter.KEYWORD_ONLY): if PY2: # Special : we can not make if Keyword-only, but we can not leave it without default value new_kind = p.kind # set a default value of new_default = p.default if p.default is not Parameter.empty else KW_ONLY else: new_kind = Parameter.KEYWORD_ONLY new_default = p.default p = Parameter(name=p.name, kind=new_kind, default=new_default, annotation=p.annotation) else: # yes: override definition with the default. Note that the parameter will remain in the signature # but as "keyword only" (and so will be all following args) if p.kind is Parameter.POSITIONAL_ONLY: raise NotImplementedError("Predefining a positional-only argument using keyword is not supported as in " "python 3.8.8, 'signature()' does not support such functions and raises a" "ValueError. Please report this issue if support needs to be added in the " "future.") if not PY2 and p.kind not in (Parameter.VAR_KEYWORD, Parameter.KEYWORD_ONLY): # change kind to keyword-only new_kind = Parameter.KEYWORD_ONLY else: new_kind = p.kind p = Parameter(name=p.name, kind=new_kind, default=overridden_p_default, annotation=p.annotation) # from now on, all other parameters need to be keyword-only kwonly_flag = True # preserve order new_params.append(p) new_sig = Signature(parameters=tuple(new_params), return_annotation=orig_sig.return_annotation) if len(preset_kwargs) > 0: raise ValueError("Cannot preset keyword argument(s), not present in the signature of %s: %s" "" % (getattr(f, '__name__', f), preset_kwargs)) return new_sig ``` -------------------------------- ### Capture and Modify Function Signature with inspect.signature Source: https://smarie.github.io/python-makefun Illustrates how to capture an existing function's signature using inspect.signature, modify it by adding a parameter, and then use the modified signature with makefun.wraps. ```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) ``` -------------------------------- ### Import Core makefun Functions Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275___init___py.html Imports key functions and classes from the main module to make them directly accessible from the package level. ```python from .main import create_function, with_signature, remove_signature_parameters, add_signature_parameters, \ wraps, create_wrapper, partial, with_partial, compile_fun, UndefinedSymbolError, UnsupportedForCompilation, \ SourceUnavailable ``` -------------------------------- ### Create Partial Function using Decorator Source: https://smarie.github.io/python-makefun Illustrates the `@with_partial` decorator as a convenient way to create partial function views, especially for quick tests or when a decorator-based approach is preferred over direct function calls. ```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 ``` -------------------------------- ### Test makefun.wraps Wrapper Behavior (Positional) Source: https://smarie.github.io/python-makefun Demonstrates the correct behavior of the makefun.wraps decorator when arguments are passed positionally. ```python assert enhanced_foo(1, 2) == 3 # positional 'b' ``` -------------------------------- ### partial Source: https://smarie.github.io/python-makefun/api_reference An equivalent of `functools.partial` that relies on a dynamically-created function for better documentation appearance. ```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. ### Parameters * `f`: The function to partially apply. * `*preset_pos_args`: Positional arguments to preset. * `**preset_kwargs`: Keyword arguments to preset. ``` -------------------------------- ### Create Generator Function Dynamically Source: https://smarie.github.io/python-makefun Demonstrates how `create_function` automatically detects and creates a generator function when the implementation is a generator. It verifies the created function is a generator and tests its output. ```python from inspect import isgeneratorfunction from makefun import create_function # 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] ``` -------------------------------- ### Implement `partial` Functionality Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html This function provides an alternative to `functools.partial` by dynamically creating a function. It aims to present a cleaner signature and documentation to the user. It handles generator functions and asynchronous generator functions specifically. ```python def partial(f, # type: Callable *preset_pos_args, # type: Any **preset_kwargs # type: Any ): """ 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. :param preset_pos_args: :param preset_kwargs: :return: """ # TODO do we need to mimic `partial`'s behaviour concerning positional args? # (1) remove/change all preset arguments from the signature orig_sig = signature(f) if preset_pos_args or preset_kwargs: new_sig = gen_partial_sig(orig_sig, preset_pos_args, preset_kwargs, f) else: new_sig = None if _is_generator_func(f): if sys.version_info >= (3, 3): from makefun._main_py35_and_higher import make_partial_using_yield_from partial_f = make_partial_using_yield_from(new_sig, f, *preset_pos_args, **preset_kwargs) else: from makefun._main_legacy_py import make_partial_using_yield partial_f = make_partial_using_yield(new_sig, f, *preset_pos_args, **preset_kwargs) elif isasyncgenfunction(f) and sys.version_info >= (3, 6): from makefun._main_py36_and_higher import make_partial_using_async_for_in_yield partial_f = make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs) else: @wraps(f, new_sig=new_sig) def partial_f(*args, **kwargs): # since the signature does the checking for us, no need to check for redundancy. kwargs.update(preset_kwargs) return f(*itertools.chain(preset_pos_args, args), **kwargs) # update the doc. # Note that partial_f is generated above with a proper __name__ and __doc__ identical to the wrapped ones if new_sig is not None: partial_f.__doc__ = gen_partial_doc(partial_f.__name__, partial_f.__doc__, orig_sig, new_sig, preset_pos_args) # Set the func attribute as `functools.partial` does partial_f.func = f return partial_f ``` -------------------------------- ### Create Function with Custom Signature and Implementation Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Dynamically creates a new function with a specified signature and an implementation function. Arguments passed to the new function are propagated to the implementation, with options for argument injection and metadata control. ```python def create_function(func_signature, # type: Union[str, Signature] func_impl, # type: Callable[[Any], Any] func_name=None, # type: str inject_as_first_arg=False, # type: bool add_source=True, # type: bool add_impl=True, # type: bool doc=None, # type: str qualname=None, # type: str co_name=None, # type: str module_name=None, # type: str **attrs): """ 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 (so all the ``` -------------------------------- ### Partial Function Application Source: https://smarie.github.io/python-makefun/api_reference Utilize the partial function for equivalent functionality to functools.partial, but with enhanced documentation presentation. This is useful when you need a partial function that appears more user-friendly in terms of its signature and name. ```python def partial(f: Callable, *preset_pos_args, **preset_kwargs ): ``` -------------------------------- ### Generated Source Code with *args and **kwargs Source: https://smarie.github.io/python-makefun Shows the generated source code for a function that accepts arbitrary positional and keyword arguments, passing them to the implementation. ```python gen_func = create_function("foo(a=0, *args, **kwargs)", func_impl) print(gen_func.__source__) ``` -------------------------------- ### Create Function Signature using inspect.Signature Object Source: https://smarie.github.io/python-makefun Programmatically define a function signature using inspect.Signature and Parameter objects. This approach offers more control than string-based definitions. Requires explicit func_name argument. ```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 ``` -------------------------------- ### create_wrapper Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Creates a signature-preserving wrapper function. It is equivalent to `@makefun.wraps(wrapped, **kwargs)(wrapper)`. ```APIDOC ## create_wrapper ### Description Creates a signature-preserving wrapper function. `create_wrapper(wrapped, wrapper, **kwargs)` is equivalent to `wraps(wrapped, **kwargs)(wrapper)`. See `@makefun.wraps` ### 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. - **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) - Arguments to remove from the signature. - **func_name** (str, optional) - The name for the wrapper function. - **inject_as_first_arg** (bool, optional) - Whether to inject the wrapper as the first argument. - **add_source** (bool, optional) - Whether to add source code information. - **add_impl** (bool, optional) - Whether to add implementation details. - **doc** (str, optional) - The docstring for the wrapper function. - **qualname** (str, optional) - The qualified name for the wrapper function. - **co_name** (str, optional) - The code name for the wrapper function. - **module_name** (str, optional) - The module name for the wrapper function. - **kwargs** - Additional attributes to apply to the wrapper function. ``` -------------------------------- ### partial function Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Equivalent of `functools.partial` but relies on a dynamically-created function. This results in a function that appears nicer to users in terms of apparent documentation, name, etc. ```APIDOC ## partial(f, *preset_pos_args, **preset_kwargs) ### Description Creates a new function with some arguments of `f` pre-filled. This is an alternative to `functools.partial` that generates a function with a cleaner signature and documentation. See [documentation](./index.md#removing-parameters-easily) for details. ### Parameters - **f** (Callable): The function to partially apply. - **preset_pos_args**: Positional arguments to preset. - **preset_kwargs**: Keyword arguments to preset. ### Returns A new function with preset arguments applied. ``` -------------------------------- ### Create Parameter with Protected Symbols Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Creates a new Parameter object, potentially with updated default values or type hints that have been protected using _SymbolRef. This is done only if changes were necessary. ```python p = Parameter(p.name, kind=p.kind, default=new_default, annotation=new_annotation) ``` -------------------------------- ### create_wrapper Source: https://smarie.github.io/python-makefun/api_reference Creates a signature-preserving wrapper function. It's equivalent to using `functools.wraps` with additional keyword arguments. ```APIDOC ## `create_wrapper` ### Description Creates a signature-preserving wrapper function. `create_wrapper(wrapped, wrapper, **kwargs)` is equivalent to `wraps(wrapped, **kwargs)(wrapper)`. ### Parameters * `wrapped`: The function to wrap. * `wrapper`: The wrapper function. * `new_sig`: Optional new signature for the wrapper. * `prepend_args`: Arguments to prepend to the wrapper's signature. * `append_args`: Arguments to append to the wrapper's signature. * `remove_args`: Arguments to remove from the wrapper's signature. * `func_name`: The name for the wrapper function. * `inject_as_first_arg`: Whether to inject the wrapper as the first argument. * `add_source`: Whether to add source code information. * `add_impl`: Whether to add implementation details. * `doc`: Docstring for the wrapper. * `qualname`: Qualified name for the wrapper. * `co_name`: Code name for the wrapper. * `module_name`: Module name for the wrapper. * `**attrs`: Additional attributes to set on the wrapper. ``` -------------------------------- ### Function Wrapping with makefun Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html makefun provides a decorator that wraps functions, allowing for signature modification and attribute preservation, akin to functools.wraps. ```APIDOC ## @makefun.wraps ### Description Wraps a function, preserving its signature and attributes, with options for customization. ### Parameters - **wrapped_fun** (callable) - The function to be wrapped. Used as a reference for signature, __name__, __qualname__, __doc__, and __dict__. - **new_sig** (str or inspect.Signature, optional) - The new signature for the decorated function. Defaults to the signature of `wrapped_fun`. Can be a string like "foo(a, b: int, *args, **kwargs)" or a `Signature` object. - **prepend_args** (str or list[str], optional) - Arguments to prepend to the signature. These are not passed to `wrapped_fun`. - **append_args** (str or list[str], optional) - Arguments to append to the signature. These are not passed to `wrapped_fun`. - **remove_args** (str or list[str], optional) - Arguments to remove from the signature. These should be injected into `kwargs` before calling `wrapped_fun`. - **func_name** (str, optional) - Overrides the `__name__` and `__qualname__` of the created function. Defaults to `wrapped_fun`'s name or the name from `new_sig`. - **inject_as_first_arg** (bool, optional) - If True, injects the created function as the first positional argument. Defaults to False. - **add_source** (bool, optional) - If True, adds a `__source__` annotation. Defaults to True. - **add_impl** (bool, optional) - If True, adds a `__func_impl__` annotation. Defaults to True. - **doc** (str, optional) - The docstring for the generated function. Defaults to `wrapped_fun`'s docstring. - **qualname** (str, optional) - The qualified name to be used. Defaults to `wrapped_fun`'s `__qualname__` or the name from `new_sig`. - **co_name** (str, optional) - The name to be used in the compiled code (`__code__.co_name`). Defaults to the name from `func_signature`. - **module_name** (str, optional) - The module name to be set on the function (`__module__`). Defaults to `wrapped_fun`'s `__module__`. ``` -------------------------------- ### Create Function using Compile Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Creates a function object by compiling a generated body string. It maps the internal '_func_impl_' symbol to the actual function implementation and optionally adds source annotations or implementation handlers. ```python protect_eval_dict(evaldict, func_name, params_names) evaldict['_func_impl_'] = func_impl if create_lambda: f = _make("lambda_", params_names, body, evaldict) else: f = _make(co_name, params_names, body, evaldict) # add the source annotation if needed if add_source: attrs['__source__'] = body # add the handler if needed if add_impl: attrs['__func_impl__'] = func_impl # update the signature _update_fields(f, name=func_name, qualname=qualname, doc=doc, annotations=annotations, defaults=tuple(defaults), kwonlydefaults=kwonlydefaults, module=module_name, kw=attrs) return f ``` -------------------------------- ### Handle Source Code Availability Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Ensures that the source code for a function is available before compilation. Raises `SourceUnavailable` if `inspect.getsource` fails. ```python try: lines = getsource(target) except (OSError, IOError) as e: # noqa # distinct exceptions in old python versions if 'could not get source code' in str(e): raise SourceUnavailable(target, e) else: raise ``` -------------------------------- ### Create Signature from String Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Creates a `Signature` object from a given function signature string. It handles parsing the string and creating a dummy function to extract the signature. ```python def get_signature_from_string(func_sig_str, evaldict): """ Creates a `Signature` object from the given function signature string. :param func_sig_str: :return: (func_name, func_sig, func_sig_str). func_sig_str is guaranteed to contain the ':' symbol already """ # escape leading newline characters if func_sig_str.startswith('\n'): func_sig_str = func_sig_str[1:] # match the provided signature. note: fullmatch is not supported in python 2 def_match = FUNC_DEF.match(func_sig_str) if def_match is None: raise SyntaxError('The provided function template is not valid: "%s" does not match ' '"()[ -> ]".\n For information the regex used is: "%s"' '' % (func_sig_str, FUNC_DEF.pattern)) groups = def_match.groupdict() # extract function name and parameter names list func_name = groups['funcname'] if func_name is None or func_name == '': func_name_ = 'dummy' func_name = None else: func_name_ = func_name # params_str = groups['params'] # params_names = extract_params_names(params_str) # find the keyword parameters and the others # posonly_names, kwonly_names, unrestricted_names = separate_positional_and_kw(params_names) colon_end = groups['colon'] cmt_return_hint = groups['comment_return_hint'] if (colon_end is None or len(colon_end) == 0) \ and (cmt_return_hint is None or len(cmt_return_hint) == 0): func_sig_str = func_sig_str + ':' # Create a dummy function # complete the string if name is empty, so that we can actually use _make func_sig_str_ = (func_name_ + func_sig_str) if func_name is None else func_sig_str body = 'def %s\n pass\n' % func_sig_str_ dummy_f = _make(func_name_, [], body, evaldict) # return its signature return func_name, signature(dummy_f), func_sig_str ``` -------------------------------- ### Partial Function Application Utility Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html This function creates a new callable that behaves like the original function but with some arguments pre-set. It aims to provide a nicer user experience than functools.partial by preserving documentation and names. ```python def partial(f, # type: Callable *preset_pos_args, # type: Any **preset_kwargs # type: Any ): """ 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. :param preset_pos_args: :param preset_kwargs: :return: """ # TODO do we need to mimic `partial`'s behaviour concerning positional args? # (1) remove/change all preset arguments from the signature orig_sig = signature(f) if preset_pos_args or preset_kwargs: new_sig = gen_partial_sig(orig_sig, preset_pos_args, preset_kwargs, f) else: new_sig = None if _is_generator_func(f): if sys.version_info >= (3, 3): from makefun._main_py35_and_higher import make_partial_using_yield_from partial_f = make_partial_using_yield_from(new_sig, f, *preset_pos_args, **preset_kwargs) else: from makefun._main_legacy_py import make_partial_using_yield partial_f = make_partial_using_yield(new_sig, f, *preset_pos_args, **preset_kwargs) elif isasyncgenfunction(f) and sys.version_info >= (3, 6): from makefun._main_py36_and_higher import make_partial_using_async_for_in_yield partial_f = make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs) else: @wraps(f, new_sig=new_sig) def partial_f(*args, **kwargs): # since the signature does the checking for us, no need to check for redundancy. kwargs.update(preset_kwargs) return f(*itertools.chain(preset_pos_args, args), **kwargs) # update the doc. # Note that partial_f is generated above with a proper __name__ and __doc__ identical to the wrapped ones if new_sig is not None: partial_f.__doc__ = gen_partial_doc(partial_f.__name__, partial_f.__doc__, orig_sig, new_sig, preset_pos_args) # Set the func attribute as `functools.partial` does partial_f.func = f return partial_f ``` -------------------------------- ### create_function Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Creates a function with a specified signature that calls a given implementation function. Arguments are propagated as keyword arguments when possible. ```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 (so all the arguments passed to the generated function will be available in `func_impl` as keyword arguments). ### Parameters - **func_signature** (Union[str, Signature]) - The desired signature for the new function. - **func_impl** (Callable[[Any], Any]) - The implementation function to be called. - **func_name** (str, optional) - The name for the new function. - **inject_as_first_arg** (bool, optional) - If True, the generated function will be injected as the first argument to `func_impl`. - **add_source** (bool, optional) - If True, the source code of the generated function will be preserved. - **add_impl** (bool, optional) - If True, the implementation function will be included in the generated function's metadata. - **doc** (str, optional) - The docstring for the new function. - **qualname** (str, optional) - The qualified name for the new function. - **co_name** (str, optional) - The code name for the new function. - **module_name** (str, optional) - The module name for the new function. - **kwargs** - Additional attributes to set on the new function. ``` -------------------------------- ### Compile Function Manually Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Illustrates how to manually compile a function using compile_fun_manually. This function can be called directly or used to create a decorator. ```python from makefun import compile_fun_manually # Example of manual compilation (assuming 'my_func' is defined elsewhere) # compiled_func = compile_fun_manually(my_func, recurse=True, except_names=(), _evaldict=True) ``` -------------------------------- ### Compile Function with Parentheses Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Demonstrates using compile_fun with parentheses to configure its behavior, such as specifying recursion and names to exclude from compilation. ```python @compile_fun(recurse=True, except_names=()) def foo(a, b): return a + b ``` -------------------------------- ### Function Creation with New Signature Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Use `create_function` to generate a new function with a specified signature and implementation. This function can also accept various metadata parameters to customize the new function's attributes. ```python def replace_f(f): return create_function(func_signature=func_signature, func_impl=f, func_name=func_name, inject_as_first_arg=inject_as_first_arg, add_source=add_source, add_impl=add_impl, doc=doc, qualname=qualname, co_name=co_name, module_name=module_name, ``` -------------------------------- ### Create Signature-Preserving Wrapper Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html Use `create_wrapper` to create a wrapper function that preserves the signature of the original function. This is equivalent to using `@makefun.wraps` with additional arguments. ```python def create_wrapper(wrapped, wrapper, new_sig=None, # type: Union[str, Signature] prepend_args=None, # type: Union[str, Parameter, Iterable[Union[str, Parameter]]] append_args=None, # type: Union[str, Parameter, Iterable[Union[str, Parameter]]] remove_args=None, # type: Union[str, Iterable[str]] func_name=None, # type: str inject_as_first_arg=False, # type: bool add_source=True, # type: bool add_impl=True, # type: bool doc=None, # type: str qualname=None, # type: str co_name=None, # type: str module_name=None, # type: str **attrs ): """ Creates a signature-preserving wrapper function. `create_wrapper(wrapped, wrapper, **kwargs)` is equivalent to `wraps(wrapped, **kwargs)(wrapper)`. See `@makefun.wraps` """ return wraps(wrapped, new_sig=new_sig, prepend_args=prepend_args, append_args=append_args, remove_args=remove_args, func_name=func_name, inject_as_first_arg=inject_as_first_arg, add_source=add_source, add_impl=add_impl, doc=doc, qualname=qualname, module_name=module_name, co_name=co_name, **attrs)(wrapper) ``` -------------------------------- ### Python Version Definitions Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275__version_py.html Defines the package version as a string and a tuple. This file is auto-generated and should not be modified. ```python __version__ = version = '0.1.dev1+gbacb3e1' __version_tuple__ = version_tuple = (0, 1, 'dev1', 'gbacb3e1') ``` -------------------------------- ### Define `KW_ONLY` Singleton for Python 2 Source: https://smarie.github.io/python-makefun/reports/coverage/z_4ec72aff9ef81275_main_py.html In Python 2, keyword-only arguments are not supported. This singleton is used to mimic their behavior by assigning a default value to arguments that should be keyword-only. ```python class KwOnly: def __str__(self): return repr(self) def __repr__(self): return "KW_ONLY_ARG!" KW_ONLY = KwOnly() ``` -------------------------------- ### Define Function Signature with PEP484 Type Hints (String) Source: https://smarie.github.io/python-makefun Use PEP484 type hints directly within a string to define a function signature. Note that inspect.signature may not always detect these hints. ```python func_sig = "foo(b: int, a: float = 0) -> str" ``` -------------------------------- ### Add Parameters to Signature Source: https://smarie.github.io/python-makefun/reports/flake8/makefun.main.source.html Creates a new Signature instance by adding parameters to an existing one. Supports adding parameters at the beginning, end, or a custom index. Strings can be provided for parameters, and their kind will be auto-guessed. ```python def add_signature_parameters(s, # type: Signature first=(), # type: Union[str, Parameter, Iterable[Union[str, Parameter]]] last=(), # type: Union[str, Parameter, Iterable[Union[str, Parameter]]] custom=(), # type: Union[Parameter, Iterable[Parameter]] custom_idx=-1 # type: int ): """ Adds the provided parameters to the signature `s` (returns a new `Signature` instance). :param s: the original signature to edit :param first: a single element or a list of `Parameter` instances to be added at the beginning of the parameter's list. Strings can also be provided, in which case the parameter kind will be created based on best guess. :param last: a single element or a list of `Parameter` instances to be added at the end of the parameter's list. Strings can also be provided, in which case the parameter kind will be created based on best guess. :param custom: a single element or a list of `Parameter` instances to be added at a custom position in the list. That position is determined with `custom_idx` :param custom_idx: the custom position to insert the `custom` parameters to. :return: a new signature created from the original one by adding the specified parameters. """ params = OrderedDict(s.parameters.items()) lst = list(params.values()) # insert at custom position (but keep the order, that's why we use 'reversed') try: for param in reversed(custom): if param.name in params: raise ValueError("Parameter with name '%s' is present twice in the signature to create" % param.name) else: lst.insert(custom_idx, param) except TypeError: # a single argument if custom.name in params: raise ValueError("Parameter with name '%s' is present twice in the signature to create" % custom.name) else: lst.insert(custom_idx, custom) # prepend but keep the order first_param_kind = None try: for param in reversed(first): if isinstance(param, string_types): # Create a Parameter with auto-guessed 'kind' if first_param_kind is None: # by default use this first_param_kind = Parameter.POSITIONAL_OR_KEYWORD try: # check the first parameter kind first_param_kind = next(iter(params.values())).kind except StopIteration: # no parameter - ok pass # if the first parameter is a pos-only or a varpos we have to change to pos only. if first_param_kind in (Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL): first_param_kind = Parameter.POSITIONAL_ONLY param = Parameter(name=param, kind=first_param_kind) else: # remember the kind first_param_kind = param.kind if param.name in params: raise ValueError("Parameter with name '%s' is present twice in the signature to create" % param.name) else: lst.insert(0, param) except TypeError: # a single argument if first.name in params: raise ValueError("Parameter with name '%s' is present twice in the signature to create" % first.name) else: lst.insert(0, first) # append last_param_kind = None try: for param in last: if isinstance(param, string_types): # Create a Parameter with auto-guessed 'kind' if last_param_kind is None: # by default use this last_param_kind = Parameter.POSITIONAL_OR_KEYWORD try: # check the last parameter kind last_param_kind = next(reversed(params.values())).kind except StopIteration: # no parameter - ok pass # if the last parameter is a keyword-only or a varkw we have to change to kw only. if last_param_kind in (Parameter.KEYWORD_ONLY, Parameter.VAR_KEYWORD): last_param_kind = Parameter.KEYWORD_ONLY param = Parameter(name=param, kind=last_param_kind) else: # remember the kind last_param_kind = param.kind ```