1 # Authors: Sylvain MARIE <sylvain.marie@se.com>
            
            2 #          + All contributors to <https://github.com/smarie/python-makefun>
            
            3 #
            
            4 # License: 3-clause BSD, <https://github.com/smarie/python-makefun/blob/master/LICENSE>
            
            5 from __future__ import print_function
            
            6  
            
            7 import functools
            
            8 import re
            
            9 import sys
            
            10 import itertools
            
            11 from collections import OrderedDict
            
            12 from copy import copy
            
            13 from inspect import getsource
            
            14 from keyword import iskeyword
            
            15 from textwrap import dedent
            
            16 from types import FunctionType
            
            17  
            
            18  
            
            19 if sys.version_info >= (3, 0):
            
            20     is_identifier = str.isidentifier
            
            21 else:
            
            22     def is_identifier(string):
            
            23         """
            
            24         Replacement for `str.isidentifier` when it is not available (e.g. on Python 2).
            
            25         :param string:
            
            26         :return:
            
            27         """
            
            28         if len(string) == 0 or string[0].isdigit():
            
            29             return False
            
            30         return all([(len(s) == 0) or s.isalnum() for s in string.split("_")])
            
            31  
            
            32 try:  # python 3.3+
            
            33     from inspect import signature, Signature, Parameter
            
            34 except ImportError:
            
            35     from funcsigs import signature, Signature, Parameter
            
            36  
            
            37 try:
            
            38     from inspect import iscoroutinefunction
            
            39 except ImportError:
            
            40     # let's assume there are no coroutine functions in old Python
            
            41     def iscoroutinefunction(f):
            
            42         return False
            
            43  
            
            44 try:
            
            45     from inspect import isgeneratorfunction
            
            46 except ImportError:
            
            47     # assume no generator function in old Python versions
            
            48     def isgeneratorfunction(f):
            
            49         return False
            
            50  
            
            51 try:
            
            52     from inspect import isasyncgenfunction
            
            53 except ImportError:
            
            54     # assume no generator function in old Python versions
            
            55     def isasyncgenfunction(f):
            
            56         return False
            
            57  
            
            58 try:  # python 3.5+
            
            59     from typing import Callable, Any, Union, Iterable, Dict, Tuple, Mapping
            
            60 except ImportError:
            
            61     pass
            
            62  
            
            63  
            
            64 PY2 = sys.version_info < (3,)
            
            65 if not PY2:
            
            66     string_types = str,
            
            67 else:
            
            68     string_types = basestring,  # noqa
            
            69  
            
            70  
            
            71 # macroscopic signature strings checker (we do not look inside params, `signature` will do it for us)
            
            72 FUNC_DEF = re.compile(
            
            73     '(?s)^\\s*(?P<funcname>[_\\w][_\\w\\d]*)?\\s*'
            
            74     '\\(\\s*(?P<params>.*?)\\s*\\)\\s*'
            
            75     '(((?P<typed_return_hint>->\\s*[^:]+)?(?P<colon>:)?\\s*)|:\\s*#\\s*(?P<comment_return_hint>.+))*$'
            
            76 )
            
            77  
            
            78  
            
            79 def create_wrapper(wrapped,
            
            80                    wrapper,
            
            81                    new_sig=None,               # type: Union[str, Signature]
            
            82                    prepend_args=None,          # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
            
            83                    append_args=None,           # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
            
            84                    remove_args=None,           # type: Union[str, Iterable[str]]
            
            85                    func_name=None,             # type: str
            
            86                    inject_as_first_arg=False,  # type: bool
            
            87                    add_source=True,            # type: bool
            
            88                    add_impl=True,              # type: bool
            
            89                    doc=None,                   # type: str
            
            90                    qualname=None,              # type: str
            
            91                    co_name=None,               # type: str
            
            92                    module_name=None,           # type: str
            
            93                    **attrs
            
            94                    ):
            
            95     """
            
            96     Creates a signature-preserving wrapper function.
            
            97     `create_wrapper(wrapped, wrapper, **kwargs)` is equivalent to `wraps(wrapped, **kwargs)(wrapper)`.
            
            98  
            
            99     See `@makefun.wraps`
            
            100     """
            
            101     return wraps(wrapped, new_sig=new_sig, prepend_args=prepend_args, append_args=append_args, remove_args=remove_args,
            
            102                  func_name=func_name, inject_as_first_arg=inject_as_first_arg, add_source=add_source,
            
            103                  add_impl=add_impl, doc=doc, qualname=qualname, module_name=module_name, co_name=co_name,
            
            104                  **attrs)(wrapper)
            
            105  
            
            106  
            
            107 def getattr_partial_aware(obj, att_name, *att_default):
            
            108     """ Same as getattr but recurses in obj.func if obj is a partial """
            
            109  
            
            110     val = getattr(obj, att_name, *att_default)
            
            111     if isinstance(obj, functools.partial) and \
            
            112             (val is None or att_name == '__dict__' and len(val) == 0):
            
            113         return getattr_partial_aware(obj.func, att_name, *att_default)
            
            114     else:
            
            115         return val
            
            116  
            
            117  
            
            118 def create_function(func_signature,             # type: Union[str, Signature]
            
            119                     func_impl,                  # type: Callable[[Any], Any]
            
            120                     func_name=None,             # type: str
            
            121                     inject_as_first_arg=False,  # type: bool
            
            122                     add_source=True,            # type: bool
            
            123                     add_impl=True,              # type: bool
            
            124                     doc=None,                   # type: str
            
            125                     qualname=None,              # type: str
            
            126                     co_name=None,               # type: str
            
            127                     module_name=None,           # type: str
            
            128                     **attrs):
            
            129     """
            
            130     Creates a function with signature `func_signature` that will call `func_impl` when called. All arguments received
            
            131     by the generated function will be propagated as keyword-arguments to `func_impl` when it is possible (so all the
            
            132     time, except for var-positional or positional-only arguments that get passed as *args. Note that positional-only
            
            133     does not yet exist in python but this case is already covered because it is supported by `Signature` objects).
            
            134  
            
            135     `func_signature` can be provided in different formats:
            
            136  
            
            137      - as a string containing the name and signature without 'def' keyword, such as `'foo(a, b: int, *args, **kwargs)'`.
            
            138        In which case the name in the string will be used for the `__name__` and `__qualname__` of the created function
            
            139        by default
            
            140      - as a `Signature` object, for example created using `signature(f)` or handcrafted. Since a `Signature` object
            
            141        does not contain any name, in this case the `__name__` and `__qualname__` of the created function will be copied
            
            142        from `func_impl` by default.
            
            143  
            
            144     All the other metadata of the created function are defined as follows:
            
            145  
            
            146      - default `__name__` attribute (see above) can be overridden by providing a non-None `func_name`
            
            147      - default `__qualname__` attribute (see above) can be overridden by providing a non-None `qualname`
            
            148      - `__annotations__` attribute is created to match the annotations in the signature.
            
            149      - `__doc__` attribute is copied from `func_impl.__doc__` except if overridden using `doc`
            
            150      - `__module__` attribute is copied from `func_impl.__module__` except if overridden using `module_name`
            
            151      - `__code__.co_name` (see above) defaults to the same value as the above `__name__` attribute, except when that
            
            152        value is not a valid Python identifier, in which case it will be `<lambda>`. It can be  overridden by providing
            
            153        a `co_name` that is either a valid Python identifier or `<lambda>`.
            
            154  
            
            155     Finally two new attributes are optionally created
            
            156  
            
            157      - `__source__` attribute: set if `add_source` is `True` (default), this attribute contains the source code of the
            
            158      generated function
            
            159      - `__func_impl__` attribute: set if `add_impl` is `True` (default), this attribute contains a pointer to
            
            160      `func_impl`
            
            161  
            
            162     A lambda function will be created in the following cases:
            
            163  
            
            164      - when `func_signature` is a `Signature` object and `func_impl` is itself a lambda function,
            
            165      - when the function name, either derived from a `func_signature` string, or given explicitly with `func_name`,
            
            166        is not a valid Python identifier, or
            
            167      - when the provided `co_name` is `<lambda>`.
            
            168  
            
            169     :param func_signature: either a string without 'def' such as "foo(a, b: int, *args, **kwargs)" or "(a, b: int)",
            
            170         or a `Signature` object, for example from the output of `inspect.signature` or from the `funcsigs.signature`
            
            171         backport. Note that these objects can be created manually too. If the signature is provided as a string and
            
            172         contains a non-empty name, this name will be used instead of the one of the decorated function.
            
            173     :param func_impl: the function that will be called when the generated function is executed. Its signature should
            
            174         be compliant with (=more generic than) `func_signature`
            
            175     :param inject_as_first_arg: if `True`, the created function will be injected as the first positional argument of
            
            176         `func_impl`. This can be handy in case the implementation is shared between several facades and needs
            
            177         to know from which context it was called. Default=`False`
            
            178     :param func_name: provide a non-`None` value to override the created function `__name__` and `__qualname__`. If this
            
            179         is `None` (default), the `__name__` will default to the one of `func_impl` if `func_signature` is a `Signature`,
            
            180         or to the name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty name.
            
            181     :param add_source: a boolean indicating if a '__source__' annotation should be added to the generated function
            
            182         (default: True)
            
            183     :param add_impl: a boolean indicating if a '__func_impl__' annotation should be added to the generated function
            
            184         (default: True)
            
            185     :param doc: a string representing the docstring that will be used to set the __doc__ attribute on the generated
            
            186         function. If None (default), the doc of func_impl will be used.
            
            187     :param qualname: a string representing the qualified name to be used. If None (default), the `__qualname__` will
            
            188         default to the one of `func_impl` if `func_signature` is a `Signature`, or to the name defined in
            
            189         `func_signature` if `func_signature` is a `str` and contains a non-empty name.
            
            190     :param co_name: a string representing the name to be used in the compiled code of the function. If None (default),
            
            191         the `__code__.co_name` will default to the one of `func_impl` if `func_signature` is a `Signature`, or to the
            
            192         name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty name.
            
            193     :param module_name: the name of the module to be set on the function (under __module__ ). If None (default),
            
            194         `func_impl.__module__` will be used.
            
            195     :param attrs: other keyword attributes that should be set on the function. Note that `func_impl.__dict__` is not
            
            196         automatically copied.
            
            197     :return:
            
            198     """
            
            199     # grab context from the caller frame
            
            200     try:
            
            201         attrs.pop('_with_sig_')
            
            202         # called from `@with_signature`
            
            203         frame = _get_callerframe(offset=1)
            
            204     except KeyError:
            
            205         frame = _get_callerframe()
            
            206     evaldict, _ = extract_module_and_evaldict(frame)
            
            207  
            
            208     # name defaults
            
            209     user_provided_name = True
            
            210     if func_name is None:
            
            211         # allow None, this will result in a lambda function being created
            
            212         func_name = getattr_partial_aware(func_impl, '__name__', None)
            
            213         user_provided_name = False
            
            214  
            
            215     # co_name default
            
            216     user_provided_co_name = co_name is not None
            
            217     if not user_provided_co_name:
            
            218         if func_name is None:
            
            219             co_name = '<lambda>'
            
            220         else:
            
            221             co_name = func_name
            
            222     else:
            
            223         if not (_is_valid_func_def_name(co_name)
            
            224                 or _is_lambda_func_name(co_name)):
            
            225             raise ValueError("Invalid co_name %r for created function. "
            
            226                              "It is not possible to declare a function "
            
            227                              "with the provided co_name." % co_name)
            
            228  
            
            229     # qname default
            
            230     user_provided_qname = True
            
            231     if qualname is None:
            
            232         qualname = getattr_partial_aware(func_impl, '__qualname__', None)
            
            233         user_provided_qname = False
            
            234  
            
            235     # doc default
            
            236     if doc is None:
            
            237         doc = getattr(func_impl, '__doc__', None)
            
            238         # note: as opposed to what we do in `@wraps`, we cannot easily generate a better doc for partials here.
            
            239         # Indeed the new signature may not easily match the one in the partial.
            
            240  
            
            241     # module name default
            
            242     if module_name is None:
            
            243         module_name = getattr_partial_aware(func_impl, '__module__', None)
            
            244  
            
            245     # input signature handling
            
            246     if isinstance(func_signature, str):
            
            247         # transform the string into a Signature and make sure the string contains ":"
            
            248         func_name_from_str, func_signature, func_signature_str = get_signature_from_string(func_signature, evaldict)
            
            249  
            
            250         # if not explicitly overridden using `func_name`, the name in the string takes over
            
            251         if func_name_from_str is not None:
            
            252             if not user_provided_name:
            
            253                 func_name = func_name_from_str
            
            254             if not user_provided_qname:
            
            255                 qualname = func_name
            
            256             if not user_provided_co_name:
            
            257                 co_name = func_name
            
            258  
            
            259         create_lambda = not _is_valid_func_def_name(co_name)
            
            260  
            
            261         # if lambda, strip the name, parentheses and colon from the signature
            
            262         if create_lambda:
            
            263             name_len = len(func_name_from_str) if func_name_from_str else 0
            
            264             func_signature_str = func_signature_str[name_len + 1: -2]
            
            265         # fix the signature if needed
            
            266         elif func_name_from_str is None:
            
            267             func_signature_str = co_name + func_signature_str
            
            268  
            
            269     elif isinstance(func_signature, Signature):
            
            270         # create the signature string
            
            271         create_lambda = not _is_valid_func_def_name(co_name)
            
            272  
            
            273         if create_lambda:
            
            274             # create signature string (or argument string in the case of a lambda function
            
            275             func_signature_str = get_lambda_argument_string(func_signature, evaldict)
            
            276         else:
            
            277             func_signature_str = get_signature_string(co_name, func_signature, evaldict)
            
            278     else:
            
            279         raise TypeError("Invalid type for `func_signature`: %s" % type(func_signature))
            
            280  
            
            281     # extract all information needed from the `Signature`
            
            282     params_to_kw_assignment_mode = get_signature_params(func_signature)
            
            283     params_names = list(params_to_kw_assignment_mode.keys())
            
            284  
            
            285     # Note: in decorator the annotations were extracted using getattr(func_impl, '__annotations__') instead.
            
            286     # This seems equivalent but more general (provided by the signature, not the function), but to check
            
            287     annotations, defaults, kwonlydefaults = get_signature_details(func_signature)
            
            288  
            
            289     # create the body of the function to compile
            
            290     # The generated function body should dispatch its received arguments to the inner function.
            
            291     # For this we will pass as much as possible the arguments as keywords.
            
            292     # However if there are varpositional arguments we cannot
            
            293     assignments = [("%s=%s" % (k, k)) if is_kw else k for k, is_kw in params_to_kw_assignment_mode.items()]
            
            294     params_str = ', '.join(assignments)
            
            295     if inject_as_first_arg:
            
            296         params_str = "%s, %s" % (func_name, params_str)
            
            297  
            
            298     if _is_generator_func(func_impl):
            
            299         if sys.version_info >= (3, 3):
            
            300             body = "def %s\n    yield from _func_impl_(%s)\n" % (func_signature_str, params_str)
            
            301         else:
            
            302             from makefun._main_legacy_py import get_legacy_py_generator_body_template
            
            303             body = get_legacy_py_generator_body_template() % (func_signature_str, params_str)
            
            304     elif isasyncgenfunction(func_impl):
            
            305         body = "async def %s\n    async for y in _func_impl_(%s):\n        yield y\n" % (func_signature_str, params_str)
            
            306     elif create_lambda:
            
            307         if func_signature_str:
            
            308             body = "lambda_ = lambda %s: _func_impl_(%s)\n" % (func_signature_str, params_str)
            
            309         else:
            
            310             body = "lambda_ = lambda: _func_impl_(%s)\n" % (params_str)
            
            311     else:
            
            312         body = "def %s\n    return _func_impl_(%s)\n" % (func_signature_str, params_str)
            
            313  
            
            314     if iscoroutinefunction(func_impl):
            
            315         body = ("async " + body).replace('return _func_impl_', 'return await _func_impl_')
            
            316  
            
            317     # create the function by compiling code, mapping the `_func_impl_` symbol to `func_impl`
            
            318     protect_eval_dict(evaldict, func_name, params_names)
            
            319     evaldict['_func_impl_'] = func_impl
            
            320     if create_lambda:
            
            321         f = _make("lambda_", params_names, body, evaldict)
            
            322     else:
            
            323         f = _make(co_name, params_names, body, evaldict)
            
            324  
            
            325     # add the source annotation if needed
            
            326     if add_source:
            
            327         attrs['__source__'] = body
            
            328  
            
            329     # add the handler if needed
            
            330     if add_impl:
            
            331         attrs['__func_impl__'] = func_impl
            
            332  
            
            333     # update the signature
            
            334     _update_fields(f, name=func_name, qualname=qualname, doc=doc, annotations=annotations,
            
            335                    defaults=tuple(defaults), kwonlydefaults=kwonlydefaults,
            
            336                    module=module_name, kw=attrs)
            
            337  
            
            338     return f
            
            339  
            
            340  
            
            341 def _is_generator_func(func_impl):
            
            342     """
            
            343     Return True if the func_impl is a generator
            
            344     :param func_impl:
            
            345     :return:
            
            346     """
            
            347     if (3, 5) <= sys.version_info < (3, 6):
            
            348         # with Python 3.5 isgeneratorfunction returns True for all coroutines
            
            349         # however we know that it is NOT possible to have a generator
            
            350         # coroutine in python 3.5: PEP525 was not there yet
            
            351         return isgeneratorfunction(func_impl) and not iscoroutinefunction(func_impl)
            
            352     else:
            
            353         return isgeneratorfunction(func_impl)
            
            354  
            
            355  
            
            356 def _is_lambda_func_name(func_name):
            
            357     """
            
            358     Return True if func_name is the name of a lambda
            
            359     :param func_name:
            
            360     :return:
            
            361     """
            
            362     return func_name == (lambda: None).__code__.co_name
            
            363  
            
            364  
            
            365 def _is_valid_func_def_name(func_name):
            
            366     """
            
            367     Return True if func_name is valid in a function definition.
            
            368     :param func_name:
            
            369     :return:
            
            370     """
            
            371     return is_identifier(func_name) and not iskeyword(func_name)
            
            372  
            
            373  
            
            374 class _SymbolRef:
            
            375     """
            
            376     A class used to protect signature default values and type hints when the local context would not be able
            
            377     to evaluate them properly when the new function is created. In this case we store them under a known name,
            
            378     we add that name to the locals(), and we use this symbol that has a repr() equal to the name.
            
            379     """
            
            380     __slots__ = 'varname'
            
            381  
            
            382     def __init__(self, varname):
            
            383         self.varname = varname
            
            384  
            
            385     def __repr__(self):
            
            386         return self.varname
            
            387  
            
            388  
            
            389 def get_signature_string(func_name, func_signature, evaldict):
            
            390     """
            
            391     Returns the string to be used as signature.
            
            392     If there is a non-native symbol in the defaults, it is created as a variable in the evaldict
            
            393     :param func_name:
            
            394     :param func_signature:
            
            395     :return:
            
            396     """
            
            397     no_type_hints_allowed = sys.version_info < (3, 5)
            
            398  
            
            399     # protect the parameters if needed
            
            400     new_params = []
            
            401     params_changed = False
            
            402     for p_name, p in func_signature.parameters.items():
            
            403         # if default value can not be evaluated, protect it
            
            404         default_needs_protection = _signature_symbol_needs_protection(p.default, evaldict)
            
            405         new_default = _protect_signature_symbol(p.default, default_needs_protection, "DEFAULT_%s" % p_name, evaldict)
            
            406  
            
            407         if no_type_hints_allowed:
            
            408             new_annotation = Parameter.empty
            
            409             annotation_needs_protection = new_annotation is not p.annotation
            
            410         else:
            
            411             # if type hint can not be evaluated, protect it
            
            412             annotation_needs_protection = _signature_symbol_needs_protection(p.annotation, evaldict)
            
            413             new_annotation = _protect_signature_symbol(p.annotation, annotation_needs_protection, "HINT_%s" % p_name,
            
            414                                                        evaldict)
            
            415  
            
            416         # only create if necessary (inspect __init__ methods are slow)
            
            417         if default_needs_protection or annotation_needs_protection:
            
            418             # replace the parameter with the possibly new default and hint
            
            419             p = Parameter(p.name, kind=p.kind, default=new_default, annotation=new_annotation)
            
            420             params_changed = True
            
            421  
            
            422         new_params.append(p)
            
            423  
            
            424     if no_type_hints_allowed:
            
            425         new_return_annotation = Parameter.empty
            
            426         return_needs_protection = new_return_annotation is not func_signature.return_annotation
            
            427     else:
            
            428         # if return type hint can not be evaluated, protect it
            
            429         return_needs_protection = _signature_symbol_needs_protection(func_signature.return_annotation, evaldict)
            
            430         new_return_annotation = _protect_signature_symbol(func_signature.return_annotation, return_needs_protection,
            
            431                                                           "RETURNHINT", evaldict)
            
            432  
            
            433     # only create new signature if necessary (inspect __init__ methods are slow)
            
            434     if params_changed or return_needs_protection:
            
            435         s = Signature(parameters=new_params, return_annotation=new_return_annotation)
            
            436     else:
            
            437         s = func_signature
            
            438  
            
            439     # return the final string representation
            
            440     return "%s%s:" % (func_name, s)
            
            441  
            
            442  
            
            443 def get_lambda_argument_string(func_signature, evaldict):
            
            444     """
            
            445     Returns the string to be used as arguments in a lambda function definition.
            
            446     If there is a non-native symbol in the defaults, it is created as a variable in the evaldict
            
            447     :param func_name:
            
            448     :param func_signature:
            
            449     :return:
            
            450     """
            
            451     return get_signature_string('', func_signature, evaldict)[1:-2]
            
            452  
            
            453  
            
            454 TYPES_WITH_SAFE_REPR = (int, str, bytes, bool)
            
            455 # IMPORTANT note: float is not in the above list because not all floats have a repr that is valid for the
            
            456 # compiler: float('nan'), float('-inf') and float('inf') or float('+inf') have an invalid repr.
            
            457  
            
            458  
            
            459 def _signature_symbol_needs_protection(symbol, evaldict):
            
            460     """
            
            461     Helper method for signature symbols (defaults, type hints) protection.
            
            462  
            
            463     Returns True if the given symbol needs to be protected - that is, if its repr() can not be correctly evaluated with
            
            464     current evaldict.
            
            465  
            
            466     :param symbol:
            
            467     :return:
            
            468     """
            
            469     if symbol is not None and symbol is not Parameter.empty and type(symbol) not in TYPES_WITH_SAFE_REPR:
            
            470         try:
            
            471             # check if the repr() of the default value is equal to itself.
            
            472             return eval(repr(symbol), evaldict) != symbol  # noqa  # we cannot use ast.literal_eval, too restrictive
            
            473         except Exception:
            
            474             # in case of error this needs protection
            
            475             return True
            
            476     else:
            
            477         return False
            
            478  
            
            479  
            
            480 def _protect_signature_symbol(val, needs_protection, varname, evaldict):
            
            481     """
            
            482     Helper method for signature symbols (defaults, type hints) protection.
            
            483  
            
            484     Returns either `val`, or a protection symbol. In that case the protection symbol
            
            485     is created with name `varname` and inserted into `evaldict`
            
            486  
            
            487     :param val:
            
            488     :param needs_protection:
            
            489     :param varname:
            
            490     :param evaldict:
            
            491     :return:
            
            492     """
            
            493     if needs_protection:
            
            494         # store the object in the evaldict and insert name
            
            495         evaldict[varname] = val
            
            496         return _SymbolRef(varname)
            
            497     else:
            
            498         return val
            
            499  
            
            500  
            
            501 def get_signature_from_string(func_sig_str, evaldict):
            
            502     """
            
            503     Creates a `Signature` object from the given function signature string.
            
            504  
            
            505     :param func_sig_str:
            
            506     :return: (func_name, func_sig, func_sig_str). func_sig_str is guaranteed to contain the ':' symbol already
            
            507     """
            
            508     # escape leading newline characters
            
            509     if func_sig_str.startswith('\n'):
            
            510         func_sig_str = func_sig_str[1:]
            
            511  
            
            512     # match the provided signature. note: fullmatch is not supported in python 2
            
            513     def_match = FUNC_DEF.match(func_sig_str)
            
            514     if def_match is None:
            
            515         raise SyntaxError('The provided function template is not valid: "%s" does not match '
            
            516                           '"<func_name>(<func_args>)[ -> <return-hint>]".\n For information the regex used is: "%s"'
            
            517                           '' % (func_sig_str, FUNC_DEF.pattern))
            
            518     groups = def_match.groupdict()
            
            519  
            
            520     # extract function name and parameter names list
            
            521     func_name = groups['funcname']
            
            522     if func_name is None or func_name == '':
            
            523         func_name_ = 'dummy'
            
            524         func_name = None
            
            525     else:
            
            526         func_name_ = func_name
            
            527     # params_str = groups['params']
            
            528     # params_names = extract_params_names(params_str)
            
            529  
            
            530     # find the keyword parameters and the others
            
            531     # posonly_names, kwonly_names, unrestricted_names = separate_positional_and_kw(params_names)
            
            532  
            
            533     colon_end = groups['colon']
            
            534     cmt_return_hint = groups['comment_return_hint']
            
            535     if (colon_end is None or len(colon_end) == 0) \
            
            536             and (cmt_return_hint is None or len(cmt_return_hint) == 0):
            
            537         func_sig_str = func_sig_str + ':'
            
            538  
            
            539     # Create a dummy function
            
            540     # complete the string if name is empty, so that we can actually use _make
            
            541     func_sig_str_ = (func_name_ + func_sig_str) if func_name is None else func_sig_str
            
            542     body = 'def %s\n    pass\n' % func_sig_str_
            
            543     dummy_f = _make(func_name_, [], body, evaldict)
            
            544  
            
            545     # return its signature
            
            546     return func_name, signature(dummy_f), func_sig_str
            
            547  
            
            548  
            
            549 # def extract_params_names(params_str):
            
            550 #     return [m.groupdict()['name'] for m in PARAM_DEF.finditer(params_str)]
            
            551  
            
            552  
            
            553 # def separate_positional_and_kw(params_names):
            
            554 #     """
            
            555 #     Extracts the names that are positional-only, keyword-only, or non-constrained
            
            556 #     :param params_names:
            
            557 #     :return:
            
            558 #     """
            
            559 #     # by default all parameters can be passed as positional or keyword
            
            560 #     posonly_names = []
            
            561 #     kwonly_names = []
            
            562 #     other_names = params_names
            
            563 #
            
            564 #     # but if we find explicit separation we have to change our mind
            
            565 #     for i in range(len(params_names)):
            
            566 #         name = params_names[i]
            
            567 #         if name == '*':
            
            568 #             del params_names[i]
            
            569 #             posonly_names = params_names[0:i]
            
            570 #             kwonly_names = params_names[i:]
            
            571 #             other_names = []
            
            572 #             break
            
            573 #         elif name[0] == '*' and name[1] != '*':  #
            
            574 #             # that's a *args. Next one will be keyword-only
            
            575 #             posonly_names = params_names[0:(i + 1)]
            
            576 #             kwonly_names = params_names[(i + 1):]
            
            577 #             other_names = []
            
            578 #             break
            
            579 #         else:
            
            580 #             # continue
            
            581 #             pass
            
            582 #
            
            583 #     return posonly_names, kwonly_names, other_names
            
            584  
            
            585  
            
            586 def get_signature_params(s):
            
            587     """
            
            588     Utility method to return the parameter names in the provided `Signature` object, by group of kind
            
            589  
            
            590     :param s:
            
            591     :return:
            
            592     """
            
            593     # this ordered dictionary will contain parameters and True/False whether we should use keyword assignment or not
            
            594     params_to_assignment_mode = OrderedDict()
            
            595     for p_name, p in s.parameters.items():
            
            596         if p.kind is Parameter.POSITIONAL_ONLY:
            
            597             params_to_assignment_mode[p_name] = False
            
            598         elif p.kind is Parameter.KEYWORD_ONLY:
            
            599             params_to_assignment_mode[p_name] = True
            
            600         elif p.kind is Parameter.POSITIONAL_OR_KEYWORD:
            
            601             params_to_assignment_mode[p_name] = True
            
            602         elif p.kind is Parameter.VAR_POSITIONAL:
            
            603             # We have to pass all the arguments that were here in previous positions, as positional too.
            
            604             for k in params_to_assignment_mode.keys():
            
            605                 params_to_assignment_mode[k] = False
            
            606             params_to_assignment_mode["*" + p_name] = False
            
            607         elif p.kind is Parameter.VAR_KEYWORD:
            
            608             params_to_assignment_mode["**" + p_name] = False
            
            609         else:
            
            610             raise ValueError("Unknown kind: %s" % p.kind)
            
            611  
            
            612     return params_to_assignment_mode
            
            613  
            
            614  
            
            615 def get_signature_details(s):
            
            616     """
            
            617     Utility method to extract the annotations, defaults and kwdefaults from a `Signature` object
            
            618  
            
            619     :param s:
            
            620     :return:
            
            621     """
            
            622     annotations = dict()
            
            623     defaults = []
            
            624     kwonlydefaults = dict()
            
            625     if s.return_annotation is not s.empty:
            
            626         annotations['return'] = s.return_annotation
            
            627     for p_name, p in s.parameters.items():
            
            628         if p.annotation is not s.empty:
            
            629             annotations[p_name] = p.annotation
            
            630         if p.default is not s.empty:
            
            631             # if p_name not in kwonly_names:
            
            632             if p.kind is not Parameter.KEYWORD_ONLY:
            
            633                 defaults.append(p.default)
            
            634             else:
            
            635                 kwonlydefaults[p_name] = p.default
            
            636     return annotations, defaults, kwonlydefaults
            
            637  
            
            638  
            
            639 def extract_module_and_evaldict(frame):
            
            640     """
            
            641     Utility function to extract the module name from the given frame,
            
            642     and to return a dictionary containing globals and locals merged together
            
            643  
            
            644     :param frame:
            
            645     :return:
            
            646     """
            
            647     try:
            
            648         # get the module name
            
            649         module_name = frame.f_globals.get('__name__', '?')
            
            650  
            
            651         # construct a dictionary with all variables
            
            652         # this is required e.g. if a symbol is used in a type hint
            
            653         evaldict = copy(frame.f_globals)
            
            654         evaldict.update(frame.f_locals)
            
            655  
            
            656     except AttributeError:
            
            657         # either the frame is None of the f_globals and f_locals are not available
            
            658         module_name = '?'
            
            659         evaldict = dict()
            
            660  
            
            661     return evaldict, module_name
            
            662  
            
            663  
            
            664 def protect_eval_dict(evaldict, func_name, params_names):
            
            665     """
            
            666     remove all symbols that could be harmful in evaldict
            
            667  
            
            668     :param evaldict:
            
            669     :param func_name:
            
            670     :param params_names:
            
            671     :return:
            
            672     """
            
            673     try:
            
            674         del evaldict[func_name]
            
            675     except KeyError:
            
            676         pass
            
            677     for n in params_names:
            
            678         try:
            
            679             del evaldict[n]
            
            680         except KeyError:
            
            681             pass
            
            682  
            
            683     return evaldict
            
            684  
            
            685  
            
            686 # Atomic get-and-increment provided by the GIL
            
            687 _compile_count = itertools.count()
            
            688  
            
            689  
            
            690 def _make(funcname, params_names, body, evaldict=None):
            
            691     """
            
            692     Make a new function from a given template and update the signature
            
            693  
            
            694     :param func_name:
            
            695     :param params_names:
            
            696     :param body:
            
            697     :param evaldict:
            
            698     :param add_source:
            
            699     :return:
            
            700     """
            
            701     evaldict = evaldict or {}
            
            702     for n in params_names:
            
            703         if n in ('_func_', '_func_impl_'):
            
            704             raise NameError('%s is overridden in\n%s' % (n, body))
            
            705  
            
            706     if not body.endswith('\n'):  # newline is needed for old Pythons
            
            707         raise ValueError("body should end with a newline")
            
            708  
            
            709     # Ensure each generated function has a unique filename for profilers
            
            710     # (such as cProfile) that depend on the tuple of (<filename>,
            
            711     # <definition line>, <function name>) being unique.
            
            712     filename = '<makefun-gen-%d>' % (next(_compile_count),)
            
            713     try:
            
            714         code = compile(body, filename, 'single')
            
            715         exec(code, evaldict)  # noqa
            
            716     except BaseException:
            
            
               717         print('Error in generated code:', file=sys.stderr)
             
            
               718         print(body, file=sys.stderr)
             
            719         raise
            
            720  
            
            721     # extract the function from compiled code
            
            722     func = evaldict[funcname]
            
            723  
            
            724     return func
            
            725  
            
            726  
            
            727 def _update_fields(
            
            728         func, name, qualname=None, doc=None, annotations=None, defaults=(), kwonlydefaults=None, module=None, kw=None
            
            729 ):
            
            730     """
            
            731     Update the signature of func with the provided information
            
            732  
            
            733     This method merely exists to remind which field have to be filled.
            
            734  
            
            735     :param func:
            
            736     :param name:
            
            737     :param qualname:
            
            738     :param kw:
            
            739     :return:
            
            740     """
            
            741     if kw is None:
            
            742         kw = dict()
            
            743  
            
            744     func.__name__ = name
            
            745  
            
            746     if qualname is not None:
            
            747         func.__qualname__ = qualname
            
            748  
            
            749     func.__doc__ = doc
            
            750     func.__dict__ = kw
            
            751  
            
            752     func.__defaults__ = defaults
            
            753     if len(kwonlydefaults) == 0:
            
            754         kwonlydefaults = None
            
            755     func.__kwdefaults__ = kwonlydefaults
            
            756  
            
            757     func.__annotations__ = annotations
            
            758     func.__module__ = module
            
            759  
            
            760  
            
            761 def _get_callerframe(offset=0):
            
            762     try:
            
            763         # inspect.stack is extremely slow, the fastest is sys._getframe or inspect.currentframe().
            
            764         # See https://gist.github.com/JettJones/c236494013f22723c1822126df944b12
            
            765         frame = sys._getframe(2 + offset)
            
            766         # frame = currentframe()
            
            767         # for _ in range(2 + offset):
            
            768         #     frame = frame.f_back
            
            769  
            
            770     except AttributeError:  # for IronPython and similar implementations
            
            771         frame = None
            
            772  
            
            773     return frame
            
            774  
            
            775  
            
            776 def wraps(wrapped_fun,
            
            777           new_sig=None,               # type: Union[str, Signature]
            
            778           prepend_args=None,          # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
            
            779           append_args=None,           # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
            
            780           remove_args=None,           # type: Union[str, Iterable[str]]
            
            781           func_name=None,             # type: str
            
            782           co_name=None,               # type: str
            
            783           inject_as_first_arg=False,  # type: bool
            
            784           add_source=True,            # type: bool
            
            785           add_impl=True,              # type: bool
            
            786           doc=None,                   # type: str
            
            787           qualname=None,              # type: str
            
            788           module_name=None,           # type: str
            
            789           **attrs
            
            790           ):
            
            791     """
            
            792     A decorator to create a signature-preserving wrapper function.
            
            793  
            
            794     It is similar to `functools.wraps`, but
            
            795  
            
            796      - relies on a proper dynamically-generated function. Therefore as opposed to `functools.wraps`,
            
            797  
            
            798         - the wrapper body will not be executed if the arguments provided are not compliant with the signature -
            
            799           instead a `TypeError` will be raised before entering the wrapper body.
            
            800         - the arguments will always be received as keywords by the wrapper, when possible. See
            
            801           [documentation](./index.md#signature-preserving-function-wrappers) for details.
            
            802  
            
            803      - you can modify the signature of the resulting function, by providing a new one with `new_sig` or by providing a
            
            804        list of arguments to remove in `remove_args`, to prepend in `prepend_args`, or to append in `append_args`.
            
            805        See [documentation](./index.md#editing-a-signature) for details.
            
            806  
            
            807     Comparison with `@with_signature`:`@wraps(f)` is equivalent to
            
            808  
            
            809         `@with_signature(signature(f),
            
            810                          func_name=f.__name__,
            
            811                          doc=f.__doc__,
            
            812                          module_name=f.__module__,
            
            813                          qualname=f.__qualname__,
            
            814                          __wrapped__=f,
            
            815                          **f.__dict__,
            
            816                          **attrs)`
            
            817  
            
            818     In other words, as opposed to `@with_signature`, the metadata (doc, module name, etc.) is provided by the wrapped
            
            819     `wrapped_fun`, so that the created function seems to be identical (except possibly for the signature).
            
            820     Note that all options in `with_signature` can still be overridden using parameters of `@wraps`.
            
            821  
            
            822     The additional `__wrapped__` attribute is set on the created function, to stay consistent
            
            823     with the `functools.wraps` behaviour. If the signature is modified through `new_sig`,
            
            824     `remove_args`, `append_args` or `prepend_args`, the additional
            
            825     `__signature__` attribute will be set so that `inspect.signature` and related functionality
            
            826     works as expected. See PEP 362 for more detail on `__wrapped__` and `__signature__`.
            
            827  
            
            828     See also [python documentation on @wraps](https://docs.python.org/3/library/functools.html#functools.wraps)
            
            829  
            
            830     :param wrapped_fun: the function that you intend to wrap with the decorated function. As in `functools.wraps`,
            
            831         `wrapped_fun` is used as the default reference for the exposed signature, `__name__`, `__qualname__`, `__doc__`
            
            832         and `__dict__`.
            
            833     :param new_sig: the new signature of the decorated function. By default it is `None` and means "same signature as
            
            834         in `wrapped_fun`" (similar behaviour as in `functools.wraps`). If you wish to modify the exposed signature
            
            835         you can either use `remove/prepend/append_args`, or pass a non-None `new_sig`. It can be either a string
            
            836         without 'def' such as "foo(a, b: int, *args, **kwargs)" of "(a, b: int)", or a `Signature` object, for example
            
            837         from the output of `inspect.signature` or from the `funcsigs.signature` backport. Note that these objects can
            
            838         be created manually too. If the signature is provided as a string and contains a non-empty name, this name
            
            839         will be used instead of the one of `wrapped_fun`.
            
            840     :param prepend_args: a string or list of strings to prepend to the signature of `wrapped_fun`. These extra arguments
            
            841         should not be passed to `wrapped_fun`, as it does not know them. This is typically used to easily create a
            
            842         wrapper with additional arguments, without having to manipulate the signature objects.
            
            843     :param append_args: a string or list of strings to append to the signature of `wrapped_fun`. These extra arguments
            
            844         should not be passed to `wrapped_fun`, as it does not know them. This is typically used to easily create a
            
            845         wrapper with additional arguments, without having to manipulate the signature objects.
            
            846     :param remove_args: a string or list of strings to remove from the signature of `wrapped_fun`. These arguments
            
            847         should be injected in the received `kwargs` before calling `wrapped_fun`, as it requires them. This is typically
            
            848         used to easily create a wrapper with less arguments, without having to manipulate the signature objects.
            
            849     :param func_name: provide a non-`None` value to override the created function `__name__` and `__qualname__`. If this
            
            850         is `None` (default), the `__name__` will default to the ones of `wrapped_fun` if `new_sig` is `None` or is a
            
            851         `Signature`, or to the name defined in `new_sig` if `new_sig` is a `str` and contains a non-empty name.
            
            852     :param inject_as_first_arg: if `True`, the created function will be injected as the first positional argument of
            
            853         the decorated function. This can be handy in case the implementation is shared between several facades and needs
            
            854         to know from which context it was called. Default=`False`
            
            855     :param add_source: a boolean indicating if a '__source__' annotation should be added to the generated function
            
            856         (default: True)
            
            857     :param add_impl: a boolean indicating if a '__func_impl__' annotation should be added to the generated function
            
            858         (default: True)
            
            859     :param doc: a string representing the docstring that will be used to set the __doc__ attribute on the generated
            
            860         function. If None (default), the doc of `wrapped_fun` will be used. If `wrapped_fun` is an instance of
            
            861         `functools.partial`, a special enhanced doc will be generated.
            
            862     :param qualname: a string representing the qualified name to be used. If None (default), the `__qualname__` will
            
            863         default to the one of `wrapped_fun`, or the one in `new_sig` if `new_sig` is provided as a string with a
            
            864         non-empty function name.
            
            865     :param co_name: a string representing the name to be used in the compiled code of the function. If None (default),
            
            866         the `__code__.co_name` will default to the one of `func_impl` if `func_signature` is a `Signature`, or to the
            
            867         name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty name.
            
            868     :param module_name: the name of the module to be set on the function (under __module__ ). If None (default), the
            
            869         `__module__` attribute of `wrapped_fun` will be used.
            
            870     :param attrs: other keyword attributes that should be set on the function. Note that the full `__dict__` of
            
            871         `wrapped_fun` is automatically copied.
            
            872     :return: a decorator
            
            873     """
            
            874     func_name, func_sig, doc, qualname, co_name, module_name, all_attrs = _get_args_for_wrapping(wrapped_fun, new_sig,
            
            875                                                                                                  remove_args,
            
            876                                                                                                  prepend_args,
            
            877                                                                                                  append_args,
            
            878                                                                                                  func_name, doc,
            
            879                                                                                                  qualname, co_name,
            
            880                                                                                                  module_name, attrs)
            
            881  
            
            882     return with_signature(func_sig,
            
            883                           func_name=func_name,
            
            884                           inject_as_first_arg=inject_as_first_arg,
            
            885                           add_source=add_source, add_impl=add_impl,
            
            886                           doc=doc,
            
            887                           qualname=qualname,
            
            888                           co_name=co_name,
            
            889                           module_name=module_name,
            
            890                           **all_attrs)
            
            891  
            
            892  
            
            893 def _get_args_for_wrapping(wrapped, new_sig, remove_args, prepend_args, append_args,
            
            894                            func_name, doc, qualname, co_name, module_name, attrs):
            
            895     """
            
            896     Internal method used by @wraps and create_wrapper
            
            897  
            
            898     :param wrapped:
            
            899     :param new_sig:
            
            900     :param remove_args:
            
            901     :param prepend_args:
            
            902     :param append_args:
            
            903     :param func_name:
            
            904     :param doc:
            
            905     :param qualname:
            
            906     :param co_name:
            
            907     :param module_name:
            
            908     :param attrs:
            
            909     :return:
            
            910     """
            
            911     # the desired signature
            
            912     has_new_sig = False
            
            913     if new_sig is not None:
            
            914         if remove_args is not None or prepend_args is not None or append_args is not None:
            
            915             raise ValueError("Only one of `[remove/prepend/append]_args` or `new_sig` should be provided")
            
            916         func_sig = new_sig
            
            917         has_new_sig = True
            
            918     else:
            
            919         func_sig = signature(wrapped)
            
            920         if remove_args:
            
            921             if isinstance(remove_args, string_types):
            
            922                 remove_args = (remove_args,)
            
            923             func_sig = remove_signature_parameters(func_sig, *remove_args)
            
            924             has_new_sig = True
            
            925  
            
            926         if prepend_args:
            
            927             if isinstance(prepend_args, string_types):
            
            928                 prepend_args = (prepend_args,)
            
            929         else:
            
            930             prepend_args = ()
            
            931  
            
            932         if append_args:
            
            933             if isinstance(append_args, string_types):
            
            934                 append_args = (append_args,)
            
            935         else:
            
            936             append_args = ()
            
            937  
            
            938         if prepend_args or append_args:
            
            939             has_new_sig = True
            
            940             func_sig = add_signature_parameters(func_sig, first=prepend_args, last=append_args)
            
            941  
            
            942     # the desired metadata
            
            943     if func_name is None:
            
            944         func_name = getattr_partial_aware(wrapped, '__name__', None)
            
            945     if doc is None:
            
            946         doc = getattr(wrapped, '__doc__', None)
            
            947         if isinstance(wrapped, functools.partial) and not has_new_sig \
            
            948                 and doc == functools.partial(lambda: True).__doc__:
            
            949             # the default generic partial doc. Generate a better doc, since we know that the sig is not messed with
            
            950             orig_sig = signature(wrapped.func)
            
            951             doc = gen_partial_doc(getattr_partial_aware(wrapped.func, '__name__', None),
            
            952                                   getattr_partial_aware(wrapped.func, '__doc__', None),
            
            953                                   orig_sig, func_sig, wrapped.args)
            
            954     if qualname is None:
            
            955         qualname = getattr_partial_aware(wrapped, '__qualname__', None)
            
            956     if module_name is None:
            
            957         module_name = getattr_partial_aware(wrapped, '__module__', None)
            
            958     if co_name is None:
            
            959         code = getattr_partial_aware(wrapped, '__code__', None)
            
            960         if code is not None:
            
            961             co_name = code.co_name
            
            962  
            
            963     # attributes: start from the wrapped dict, add '__wrapped__' if needed, and override with all attrs.
            
            964     all_attrs = copy(getattr_partial_aware(wrapped, '__dict__'))
            
            965     # PEP362: always set `__wrapped__`, and if signature was changed, set `__signature__` too
            
            966     all_attrs["__wrapped__"] = wrapped
            
            967     if has_new_sig:
            
            968         if isinstance(func_sig, Signature):
            
            969             all_attrs["__signature__"] = func_sig
            
            970         else:
            
            971             # __signature__ must be a Signature object, so if it is a string we need to evaluate it.
            
            972             frame = _get_callerframe(offset=1)
            
            973             evaldict, _ = extract_module_and_evaldict(frame)
            
            974             # Here we could wish to directly override `func_name` and `func_sig` so that this does not have to be done
            
            975             # again by `create_function` later... Would this be risky ?
            
            976             _func_name, func_sig_as_sig, _ = get_signature_from_string(func_sig, evaldict)
            
            977             all_attrs["__signature__"] = func_sig_as_sig
            
            978  
            
            979     all_attrs.update(attrs)
            
            980  
            
            981     return func_name, func_sig, doc, qualname, co_name, module_name, all_attrs
            
            982  
            
            983  
            
            984 def with_signature(func_signature,             # type: Union[str, Signature]
            
            985                    func_name=None,             # type: str
            
            986                    inject_as_first_arg=False,  # type: bool
            
            987                    add_source=True,             # type: bool
            
            988                    add_impl=True,            # type: bool
            
            989                    doc=None,                   # type: str
            
            990                    qualname=None,              # type: str
            
            991                    co_name=None,                # type: str
            
            992                    module_name=None,            # type: str
            
            993                    **attrs
            
            994                    ):
            
            995     """
            
            996     A decorator for functions, to change their signature. The new signature should be compliant with the old one.
            
            997  
            
            998     ```python
            
            999     @with_signature(<arguments>)
            
            1000     def impl(...):
            
            1001         ...
            
            1002     ```
            
            1003  
            
            1004     is totally equivalent to `impl = create_function(<arguments>, func_impl=impl)` except for one additional behaviour:
            
            1005  
            
            1006      - If `func_signature` is set to `None`, there is no `TypeError` as in create_function. Instead, this simply
            
            1007      applies the new metadata (name, doc, module_name, attrs) to the decorated function without creating a wrapper.
            
            1008      `add_source`, `add_impl` and `inject_as_first_arg` should not be set in this case.
            
            1009  
            
            1010     :param func_signature: the new signature of the decorated function. Either a string without 'def' such as
            
            1011         "foo(a, b: int, *args, **kwargs)" of "(a, b: int)", or a `Signature` object, for example from the output of
            
            1012         `inspect.signature` or from the `funcsigs.signature` backport. Note that these objects can be created manually
            
            1013         too. If the signature is provided as a string and contains a non-empty name, this name will be used instead
            
            1014         of the one of the decorated function. Finally `None` can be provided to indicate that user wants to only change
            
            1015         the medatadata (func_name, doc, module_name, attrs) of the decorated function, without generating a new
            
            1016         function.
            
            1017     :param inject_as_first_arg: if `True`, the created function will be injected as the first positional argument of
            
            1018         the decorated function. This can be handy in case the implementation is shared between several facades and needs
            
            1019         to know from which context it was called. Default=`False`
            
            1020     :param func_name: provide a non-`None` value to override the created function `__name__` and `__qualname__`. If this
            
            1021         is `None` (default), the `__name__` will default to the ones of the decorated function if `func_signature` is a
            
            1022         `Signature`, or to the name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty
            
            1023         name.
            
            1024     :param add_source: a boolean indicating if a '__source__' annotation should be added to the generated function
            
            1025         (default: True)
            
            1026     :param add_impl: a boolean indicating if a '__func_impl__' annotation should be added to the generated function
            
            1027         (default: True)
            
            1028     :param doc: a string representing the docstring that will be used to set the __doc__ attribute on the generated
            
            1029         function. If None (default), the doc of the decorated function will be used.
            
            1030     :param qualname: a string representing the qualified name to be used. If None (default), the `__qualname__` will
            
            1031         default to the one of `func_impl` if `func_signature` is a `Signature`, or to the name defined in
            
            1032         `func_signature` if `func_signature` is a `str` and contains a non-empty name.
            
            1033     :param co_name: a string representing the name to be used in the compiled code of the function. If None (default),
            
            1034         the `__code__.co_name` will default to the one of `func_impl` if `func_signature` is a `Signature`, or to the
            
            1035         name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty name.
            
            1036     :param module_name: the name of the module to be set on the function (under __module__ ). If None (default), the
            
            1037         `__module__` attribute of the decorated function will be used.
            
            1038     :param attrs: other keyword attributes that should be set on the function. Note that the full `__dict__` of the
            
            1039         decorated function is not automatically copied.
            
            1040     """
            
            1041     if func_signature is None and co_name is None:
            
            1042         # make sure that user does not provide non-default other args
            
            1043         if inject_as_first_arg or not add_source or not add_impl:
            
            1044             raise ValueError("If `func_signature=None` no new signature will be generated so only `func_name`, "
            
            1045                              "`module_name`, `doc` and `attrs` should be provided, to modify the metadata.")
            
            1046         else:
            
            1047             def replace_f(f):
            
            1048                 # manually apply all the non-None metadata, but do not call create_function - that's useless
            
            1049                 if func_name is not None:
            
            1050                     f.__name__ = func_name
            
            1051                 if doc is not None:
            
            1052                     f.__doc__ = doc
            
            1053                 if qualname is not None:
            
            1054                     f.__qualname__ = qualname
            
            1055                 if module_name is not None:
            
            1056                     f.__module__ = module_name
            
            1057                 for k, v in attrs.items():
            
            1058                     setattr(f, k, v)
            
            1059                 return f
            
            1060     else:
            
            1061         def replace_f(f):
            
            1062             return create_function(func_signature=func_signature,
            
            1063                                    func_impl=f,
            
            1064                                    func_name=func_name,
            
            1065                                    inject_as_first_arg=inject_as_first_arg,
            
            1066                                    add_source=add_source,
            
            1067                                    add_impl=add_impl,
            
            1068                                    doc=doc,
            
            1069                                    qualname=qualname,
            
            1070                                    co_name=co_name,
            
            1071                                    module_name=module_name,
            
            1072                                    _with_sig_=True,  # special trick to tell create_function that we're @with_signature
            
            1073                                    **attrs
            
            1074                                    )
            
            1075  
            
            1076     return replace_f
            
            1077  
            
            1078  
            
            1079 def remove_signature_parameters(s,
            
            1080                                 *param_names):
            
            1081     """
            
            1082     Removes the provided parameters from the signature `s` (returns a new `Signature` instance).
            
            1083  
            
            1084     :param s:
            
            1085     :param param_names: a list of parameter names to remove
            
            1086     :return:
            
            1087     """
            
            1088     params = OrderedDict(s.parameters.items())
            
            1089     for param_name in param_names:
            
            1090         del params[param_name]
            
            1091     return s.replace(parameters=params.values())
            
            1092  
            
            1093  
            
            1094 def add_signature_parameters(s,             # type: Signature
            
            1095                              first=(),      # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
            
            1096                              last=(),       # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
            
            1097                              custom=(),     # type: Union[Parameter, Iterable[Parameter]]
            
            1098                              custom_idx=-1  # type: int
            
            1099                              ):
            
            1100     """
            
            1101     Adds the provided parameters to the signature `s` (returns a new `Signature` instance).
            
            1102  
            
            1103     :param s: the original signature to edit
            
            1104     :param first: a single element or a list of `Parameter` instances to be added at the beginning of the parameter's
            
            1105         list. Strings can also be provided, in which case the parameter kind will be created based on best guess.
            
            1106     :param last: a single element or a list of `Parameter` instances to be added at the end of the parameter's list.
            
            1107         Strings can also be provided, in which case the parameter kind will be created based on best guess.
            
            1108     :param custom: a single element or a list of `Parameter` instances to be added at a custom position in the list.
            
            1109         That position is determined with `custom_idx`
            
            1110     :param custom_idx: the custom position to insert the `custom` parameters to.
            
            1111     :return: a new signature created from the original one by adding the specified parameters.
            
            1112     """
            
            1113     params = OrderedDict(s.parameters.items())
            
            1114     lst = list(params.values())
            
            1115  
            
            1116     # insert at custom position (but keep the order, that's why we use 'reversed')
            
            1117     try:
            
            1118         for param in reversed(custom):
            
            1119             if param.name in params:
            
            1120                 raise ValueError("Parameter with name '%s' is present twice in the signature to create" % param.name)
            
            1121             else:
            
            1122                 lst.insert(custom_idx, param)
            
            1123     except TypeError:
            
            1124         # a single argument
            
            1125         if custom.name in params:
            
            1126             raise ValueError("Parameter with name '%s' is present twice in the signature to create" % custom.name)
            
            1127         else:
            
            1128             lst.insert(custom_idx, custom)
            
            1129  
            
            1130     # prepend but keep the order
            
            1131     first_param_kind = None
            
            1132     try:
            
            1133         for param in reversed(first):
            
            1134             if isinstance(param, string_types):
            
            1135                 # Create a Parameter with auto-guessed 'kind'
            
            1136                 if first_param_kind is None:
            
            1137                     # by default use this
            
            1138                     first_param_kind = Parameter.POSITIONAL_OR_KEYWORD
            
            1139                     try:
            
            1140                         # check the first parameter kind
            
            1141                         first_param_kind = next(iter(params.values())).kind
            
            1142                     except StopIteration:
            
            1143                         # no parameter - ok
            
            1144                         pass
            
            1145                 # if the first parameter is a pos-only or a varpos we have to change to pos only.
            
            1146                 if first_param_kind in (Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL):
            
            1147                     first_param_kind = Parameter.POSITIONAL_ONLY
            
            1148                 param = Parameter(name=param, kind=first_param_kind)
            
            1149             else:
            
            1150                 # remember the kind
            
            1151                 first_param_kind = param.kind
            
            1152  
            
            1153             if param.name in params:
            
            1154                 raise ValueError("Parameter with name '%s' is present twice in the signature to create" % param.name)
            
            1155             else:
            
            1156                 lst.insert(0, param)
            
            1157     except TypeError:
            
            1158         # a single argument
            
            1159         if first.name in params:
            
            1160             raise ValueError("Parameter with name '%s' is present twice in the signature to create" % first.name)
            
            1161         else:
            
            1162             lst.insert(0, first)
            
            1163  
            
            1164     # append
            
            1165     last_param_kind = None
            
            1166     try:
            
            1167         for param in last:
            
            1168             if isinstance(param, string_types):
            
            1169                 # Create a Parameter with auto-guessed 'kind'
            
            1170                 if last_param_kind is None:
            
            1171                     # by default use this
            
            1172                     last_param_kind = Parameter.POSITIONAL_OR_KEYWORD
            
            1173                     try:
            
            1174                         # check the last parameter kind
            
            1175                         last_param_kind = next(reversed(params.values())).kind
            
            1176                     except StopIteration:
            
            1177                         # no parameter - ok
            
            1178                         pass
            
            1179                 # if the last parameter is a keyword-only or a varkw we have to change to kw only.
            
            1180                 if last_param_kind in (Parameter.KEYWORD_ONLY, Parameter.VAR_KEYWORD):
            
            1181                     last_param_kind = Parameter.KEYWORD_ONLY
            
            1182                 param = Parameter(name=param, kind=last_param_kind)
            
            1183             else:
            
            1184                 # remember the kind
            
            1185                 last_param_kind = param.kind
            
            1186  
            
            1187             if param.name in params:
            
            1188                 raise ValueError("Parameter with name '%s' is present twice in the signature to create" % param.name)
            
            1189             else:
            
            1190                 lst.append(param)
            
            1191     except TypeError:
            
            1192         # a single argument
            
            1193         if last.name in params:
            
            1194             raise ValueError("Parameter with name '%s' is present twice in the signature to create" % last.name)
            
            1195         else:
            
            1196             lst.append(last)
            
            1197  
            
            1198     return s.replace(parameters=lst)
            
            1199  
            
            1200  
            
            1201 def with_partial(*preset_pos_args, **preset_kwargs):
            
            1202     """
            
            1203     Decorator to 'partialize' a function using `partial`
            
            1204  
            
            1205     :param preset_pos_args:
            
            1206     :param preset_kwargs:
            
            1207     :return:
            
            1208     """
            
            1209     def apply_decorator(f):
            
            1210         return partial(f, *preset_pos_args, **preset_kwargs)
            
            1211     return apply_decorator
            
            1212  
            
            1213  
            
            1214 def partial(f,                 # type: Callable
            
            1215             *preset_pos_args,  # type: Any
            
            1216             **preset_kwargs    # type: Any
            
            1217             ):
            
            1218     """
            
            1219     Equivalent of `functools.partial` but relies on a dynamically-created function. As a result the function
            
            1220     looks nicer to users in terms of apparent documentation, name, etc.
            
            1221  
            
            1222     See [documentation](./index.md#removing-parameters-easily) for details.
            
            1223  
            
            1224     :param preset_pos_args:
            
            1225     :param preset_kwargs:
            
            1226     :return:
            
            1227     """
            
            1228     # TODO do we need to mimic `partial`'s behaviour concerning positional args?
            
            1229  
            
            1230     # (1) remove/change all preset arguments from the signature
            
            1231     orig_sig = signature(f)
            
            1232     if preset_pos_args or preset_kwargs:
            
            1233         new_sig = gen_partial_sig(orig_sig, preset_pos_args, preset_kwargs, f)
            
            1234     else:
            
            1235         new_sig = None
            
            1236  
            
            1237     if _is_generator_func(f):
            
            1238         if sys.version_info >= (3, 3):
            
            1239             from makefun._main_py35_and_higher import make_partial_using_yield_from
            
            1240             partial_f = make_partial_using_yield_from(new_sig, f, *preset_pos_args, **preset_kwargs)
            
            1241         else:
            
            1242             from makefun._main_legacy_py import make_partial_using_yield
            
            1243             partial_f = make_partial_using_yield(new_sig, f, *preset_pos_args, **preset_kwargs)
            
            1244     elif isasyncgenfunction(f) and sys.version_info >= (3, 6):
            
            1245         from makefun._main_py36_and_higher import make_partial_using_async_for_in_yield
            
            1246         partial_f = make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs)
            
            1247     else:
            
            1248         @wraps(f, new_sig=new_sig)
            
            1249         def partial_f(*args, **kwargs):
            
            1250             # since the signature does the checking for us, no need to check for redundancy.
            
            1251             kwargs.update(preset_kwargs)
            
            1252             return f(*itertools.chain(preset_pos_args, args), **kwargs)
            
            1253  
            
            1254     # update the doc.
            
            1255     # Note that partial_f is generated above with a proper __name__ and __doc__ identical to the wrapped ones
            
            1256     if new_sig is not None:
            
            1257         partial_f.__doc__ = gen_partial_doc(partial_f.__name__, partial_f.__doc__, orig_sig, new_sig, preset_pos_args)
            
            1258  
            
            1259     # Set the func attribute as `functools.partial` does
            
            1260     partial_f.func = f
            
            1261  
            
            1262     return partial_f
            
            1263  
            
            1264  
            
            1265 if PY2:
            
            1266     # In python 2 keyword-only arguments do not exist.
            
            1267     # so if they do not have a default value, we set them with a default value
            
            1268     # that is this singleton. This is the only way we can have the same behaviour
            
            1269     # in python 2 in terms of order of arguments, than what funcools.partial does.
            
            1270     class KwOnly:
            
            1271         def __str__(self):
            
            1272             return repr(self)
            
            1273  
            
            1274         def __repr__(self):
            
            1275             return "KW_ONLY_ARG!"
            
            1276  
            
            1277     KW_ONLY = KwOnly()
            
            1278 else:
            
            1279     KW_ONLY = None
            
            1280  
            
            1281  
            
            1282 def gen_partial_sig(orig_sig,         # type: Signature
            
            1283                     preset_pos_args,  # type: Tuple[Any]
            
            1284                     preset_kwargs,    # type: Mapping[str, Any]
            
            1285                     f,                # type: Callable
            
            1286                     ):
            
            1287     """
            
            1288     Returns the signature of partial(f, *preset_pos_args, **preset_kwargs)
            
            1289     Raises explicit errors in case of non-matching argument names.
            
            1290  
            
            1291     By default the behaviour is the same as `functools.partial`:
            
            1292  
            
            1293      - partialized positional arguments disappear from the signature
            
            1294      - partialized keyword arguments remain in the signature in the same order, but all keyword arguments after them
            
            1295        in the parameters order become keyword-only (if python 2, they do not become keyword-only as this is not allowed
            
            1296        in the compiler, but we pass them a bad default value "KEYWORD_ONLY")
            
            1297  
            
            1298     :param orig_sig:
            
            1299     :param preset_pos_args:
            
            1300     :param preset_kwargs:
            
            1301     :param f: used in error messages only
            
            1302     :return:
            
            1303     """
            
            1304     preset_kwargs = copy(preset_kwargs)
            
            1305  
            
            1306     # remove the first n positional, and assign/change default values for the keyword
            
            1307     if len(orig_sig.parameters) < len(preset_pos_args):
            
            1308         raise ValueError("Cannot preset %s positional args, function %s has only %s args."
            
            1309                          "" % (len(preset_pos_args), getattr(f, '__name__', f), len(orig_sig.parameters)))
            
            1310  
            
            1311     # then the keywords. If they have a new value override it
            
            1312     new_params = []
            
            1313     kwonly_flag = False
            
            1314     for i, (p_name, p) in enumerate(orig_sig.parameters.items()):
            
            1315         if i < len(preset_pos_args):
            
            1316             # preset positional arg: disappears from signature
            
            1317             continue
            
            1318         try:
            
            1319             # is this parameter overridden in `preset_kwargs` ?
            
            1320             overridden_p_default = preset_kwargs.pop(p_name)
            
            1321         except KeyError:
            
            1322             # no: it will appear "as is" in the signature, in the same order
            
            1323  
            
            1324             # However we need to change the kind if the kind is not already "keyword only"
            
            1325             # positional only:  Parameter.POSITIONAL_ONLY, VAR_POSITIONAL
            
            1326             # both: POSITIONAL_OR_KEYWORD
            
            1327             # keyword only: KEYWORD_ONLY, VAR_KEYWORD
            
            1328             if kwonly_flag and p.kind not in (Parameter.VAR_KEYWORD, Parameter.KEYWORD_ONLY):
            
            1329                 if PY2:
            
            1330                     # Special : we can not make if Keyword-only, but we can not leave it without default value
            
            1331                     new_kind = p.kind
            
            1332                     # set a default value of
            
            1333                     new_default = p.default if p.default is not Parameter.empty else KW_ONLY
            
            1334                 else:
            
            1335                     new_kind = Parameter.KEYWORD_ONLY
            
            1336                     new_default = p.default
            
            1337                 p = Parameter(name=p.name, kind=new_kind, default=new_default, annotation=p.annotation)
            
            1338  
            
            1339         else:
            
            1340             # yes: override definition with the default. Note that the parameter will remain in the signature
            
            1341             # but as "keyword only" (and so will be all following args)
            
            1342             if p.kind is Parameter.POSITIONAL_ONLY:
            
            1343                 raise NotImplementedError("Predefining a positional-only argument using keyword is not supported as in "
            
            1344                                           "python 3.8.8, 'signature()' does not support such functions and raises a"
            
            1345                                           "ValueError. Please report this issue if support needs to be added in the "
            
            1346                                           "future.")
            
            1347  
            
            1348             if not PY2 and p.kind not in (Parameter.VAR_KEYWORD, Parameter.KEYWORD_ONLY):
            
            1349                 # change kind to keyword-only
            
            1350                 new_kind = Parameter.KEYWORD_ONLY
            
            1351             else:
            
            1352                 new_kind = p.kind
            
            1353             p = Parameter(name=p.name, kind=new_kind, default=overridden_p_default, annotation=p.annotation)
            
            1354  
            
            1355             # from now on, all other parameters need to be keyword-only
            
            1356             kwonly_flag = True
            
            1357  
            
            1358         # preserve order
            
            1359         new_params.append(p)
            
            1360  
            
            1361     new_sig = Signature(parameters=tuple(new_params),
            
            1362                         return_annotation=orig_sig.return_annotation)
            
            1363  
            
            1364     if len(preset_kwargs) > 0:
            
            1365         raise ValueError("Cannot preset keyword argument(s), not present in the signature of %s: %s"
            
            1366                          "" % (getattr(f, '__name__', f), preset_kwargs))
            
            1367     return new_sig
            
            1368  
            
            1369  
            
            1370 def gen_partial_doc(wrapped_name, wrapped_doc, orig_sig, new_sig, preset_pos_args):
            
            1371     """
            
            1372     Generate a documentation indicating which positional arguments and keyword arguments are set in this
            
            1373     partial implementation, and appending the wrapped function doc.
            
            1374  
            
            1375     :param wrapped_name:
            
            1376     :param wrapped_doc:
            
            1377     :param orig_sig:
            
            1378     :param new_sig:
            
            1379     :param preset_pos_args:
            
            1380     :return:
            
            1381     """
            
            1382     # generate the "equivalent signature": this is the original signature,
            
            1383     # where all values injected by partial appear
            
            1384     all_strs = []
            
            1385     kw_only = False
            
            1386     for i, (p_name, _p) in enumerate(orig_sig.parameters.items()):
            
            1387         if i < len(preset_pos_args):
            
            1388             # use the preset positional. Use repr() instead of str() so that e.g. "yes" appears with quotes
            
            1389             all_strs.append(repr(preset_pos_args[i]))
            
            1390         else:
            
            1391             # use the one in the new signature
            
            1392             pnew = new_sig.parameters[p_name]
            
            1393             if not kw_only:
            
            1394                 if (PY2 and pnew.default is KW_ONLY) or pnew.kind == Parameter.KEYWORD_ONLY:
            
            1395                     kw_only = True
            
            1396  
            
            1397             if PY2 and kw_only:
            
            1398                 all_strs.append(str(pnew).replace("=%s" % KW_ONLY, ""))
            
            1399             else:
            
            1400                 all_strs.append(str(pnew))
            
            1401  
            
            1402     argstring = ", ".join(all_strs)
            
            1403  
            
            1404     # Write the final docstring
            
            1405     if wrapped_doc is None or len(wrapped_doc) == 0:
            
            1406         partial_doc = "<This function is equivalent to '%s(%s)'.>\n" % (wrapped_name, argstring)
            
            1407     else:
            
            1408         new_line = "<This function is equivalent to '%s(%s)', see original '%s' doc below.>\n" \
            
            1409                    "" % (wrapped_name, argstring, wrapped_name)
            
            1410         partial_doc = new_line + wrapped_doc
            
            1411  
            
            1412     return partial_doc
            
            1413  
            
            1414  
            
            1415 class UnsupportedForCompilation(TypeError):
            
            1416     """
            
            1417     Exception raised by @compile_fun when decorated target is not supported
            
            1418     """
            
            1419     pass
            
            1420  
            
            1421  
            
            1422 class UndefinedSymbolError(NameError):
            
            1423     """
            
            1424     Exception raised by @compile_fun when the function requires a name not yet defined
            
            1425     """
            
            1426     pass
            
            1427  
            
            1428  
            
            1429 class SourceUnavailable(OSError):
            
            1430     """
            
            1431     Exception raised by @compile_fun when the function source is not available (inspect.getsource raises an error)
            
            1432     """
            
            1433     pass
            
            1434  
            
            1435  
            
            1436 def compile_fun(recurse=True,     # type: Union[bool, Callable]
            
            1437                 except_names=(),  # type: Iterable[str]
            
            1438                 ):
            
            1439     """
            
            1440     A draft decorator to `compile` any existing function so that users cant
            
            1441     debug through it. It can be handy to mask some code from your users for
            
            1442     convenience (note that this does not provide any obfuscation, people can
            
            1443     still reverse engineer your code easily. Actually the source code even gets
            
            1444     copied in the function's `__source__` attribute for convenience):
            
            1445  
            
            1446     ```python
            
            1447     from makefun import compile_fun
            
            1448  
            
            1449     @compile_fun
            
            1450     def foo(a, b):
            
            1451         return a + b
            
            1452  
            
            1453     assert foo(5, -5.0) == 0
            
            1454     print(foo.__source__)
            
            1455     ```
            
            1456  
            
            1457     yields
            
            1458  
            
            1459     ```
            
            1460     @compile_fun
            
            1461     def foo(a, b):
            
            1462         return a + b
            
            1463     ```
            
            1464  
            
            1465     If the function closure includes functions, they are recursively replaced with compiled versions too (only for
            
            1466     this closure, this does not modify them otherwise).
            
            1467  
            
            1468     **IMPORTANT** this decorator is a "goodie" in early stage and has not been extensively tested. Feel free to
            
            1469     contribute !
            
            1470  
            
            1471     Note that according to [this post](https://stackoverflow.com/a/471227/7262247) compiling does not make the code
            
            1472     run any faster.
            
            1473  
            
            1474     Known issues: `NameError` will appear if your function code depends on symbols that have not yet been defined.
            
            1475     Make sure all symbols exist first ! See https://github.com/smarie/python-makefun/issues/47
            
            1476  
            
            1477     :param recurse: a boolean (default `True`) indicating if referenced symbols should be compiled too
            
            1478     :param except_names: an optional list of symbols to exclude from compilation when `recurse=True`
            
            1479     :return:
            
            1480     """
            
            1481     if callable(recurse):
            
            1482         # called with no-args, apply immediately
            
            1483         target = recurse
            
            1484         # noinspection PyTypeChecker
            
            1485         return compile_fun_manually(target, _evaldict=True)
            
            1486     else:
            
            1487         # called with parenthesis, return a decorator
            
            1488         def apply_compile_fun(target):
            
            1489             return compile_fun_manually(target, recurse=recurse, except_names=except_names, _evaldict=True)
            
            1490  
            
            1491         return apply_compile_fun
            
            1492  
            
            1493  
            
            1494 def compile_fun_manually(target,
            
            1495                          recurse=True,     # type: Union[bool, Callable]
            
            1496                          except_names=(),  # type: Iterable[str]
            
            1497                          _evaldict=None    # type: Union[bool, Dict]
            
            1498                          ):
            
            1499     """
            
            1500  
            
            1501     :param target:
            
            1502     :return:
            
            1503     """
            
            1504     if not isinstance(target, FunctionType):
            
            1505         raise UnsupportedForCompilation("Only functions can be compiled by this decorator")
            
            1506  
            
            1507     if _evaldict is None or _evaldict is True:
            
            1508         if _evaldict is True:
            
            1509             frame = _get_callerframe(offset=1)
            
            1510         else:
            
            1511             frame = _get_callerframe()
            
            1512         _evaldict, _ = extract_module_and_evaldict(frame)
            
            1513  
            
            1514     # first make sure that source code is available for compilation
            
            1515     try:
            
            1516         lines = getsource(target)
            
            1517     except (OSError, IOError) as e:  # noqa # distinct exceptions in old python versions
            
            1518         if 'could not get source code' in str(e):
            
            1519             raise SourceUnavailable(target, e)
            
            1520         else:
            
            1521             raise
            
            1522  
            
            1523     # compile all references first
            
            1524     try:
            
            1525         # python 3
            
            1526         func_closure = target.__closure__
            
            1527         func_code = target.__code__
            
            1528     except AttributeError:
            
            1529         # python 2
            
            1530         func_closure = target.func_closure
            
            1531         func_code = target.func_code
            
            1532  
            
            1533     # Does not work: if `self.i` is used in the code, `i` will appear here
            
            1534     # if func_code is not None:
            
            1535     #     for name in func_code.co_names:
            
            1536     #         try:
            
            1537     #             eval(name, _evaldict)
            
            1538     #         except NameError:
            
            1539     #             raise UndefinedSymbolError("Symbol `%s` does not seem to be defined yet. Make sure you apply "
            
            1540     #                                        "`compile_fun` *after* all required symbols have been defined." % name)
            
            1541  
            
            1542     if recurse and func_closure is not None:
            
            1543         # recurse-compile
            
            1544         for name, cell in zip(func_code.co_freevars, func_closure):
            
            1545             if name in except_names:
            
            1546                 continue
            
            1547             if name not in _evaldict:
            
            1548                 raise UndefinedSymbolError("Symbol %s does not seem to be defined yet. Make sure you apply "
            
            1549                                            "`compile_fun` *after* all required symbols have been defined." % name)
            
            1550             try:
            
            1551                 value = cell.cell_contents
            
            1552             except ValueError:
            
            1553                 # empty cell
            
            1554                 continue
            
            1555             else:
            
            1556                 # non-empty cell
            
            1557                 try:
            
            1558                     # note : not sure the compilation will be made in the appropriate order of dependencies...
            
            1559                     # if not, users will have to do it manually
            
            1560                     _evaldict[name] = compile_fun_manually(value,
            
            1561                                                            recurse=recurse, except_names=except_names,
            
            1562                                                            _evaldict=_evaldict)
            
            1563                 except (UnsupportedForCompilation, SourceUnavailable):
            
            1564                     pass
            
            1565  
            
            1566     # now compile from sources
            
            1567     lines = dedent(lines)
            
            1568     source_lines = lines
            
            1569     if lines.startswith('@compile_fun'):
            
            1570         lines = '\n'.join(lines.splitlines()[1:])
            
            1571     if '@compile_fun' in lines:
            
            1572         raise ValueError("@compile_fun seems to appear several times in the function source")
            
            1573     if lines[-1] != '\n':
            
            1574         lines += '\n'
            
            1575     # print("compiling: ")
            
            1576     # print(lines)
            
            1577     new_f = _make(target.__name__, (), lines, _evaldict)
            
            1578     new_f.__source__ = source_lines
            
            1579  
            
            1580     return new_f