1111from collections import OrderedDict
1212from copy import copy
1313from inspect import getsource
14+ from keyword import iskeyword
1415from textwrap import dedent
1516from types import FunctionType
1617
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 ([s .isalnum () for s in string .split ("_" )])
31+
1732try : # python 3.3+
1833 from inspect import signature , Signature , Parameter
1934except ImportError :
@@ -73,6 +88,7 @@ def create_wrapper(wrapped,
7388 add_impl = True , # type: bool
7489 doc = None , # type: str
7590 qualname = None , # type: str
91+ co_name = None , # type: str
7692 module_name = None , # type: str
7793 ** attrs
7894 ):
@@ -84,7 +100,8 @@ def create_wrapper(wrapped,
84100 """
85101 return wraps (wrapped , new_sig = new_sig , prepend_args = prepend_args , append_args = append_args , remove_args = remove_args ,
86102 func_name = func_name , inject_as_first_arg = inject_as_first_arg , add_source = add_source ,
87- add_impl = add_impl , doc = doc , qualname = qualname , module_name = module_name , ** attrs )(wrapper )
103+ add_impl = add_impl , doc = doc , qualname = qualname , module_name = module_name , co_name = co_name ,
104+ ** attrs )(wrapper )
88105
89106
90107def getattr_partial_aware (obj , att_name , * att_default ):
@@ -106,6 +123,7 @@ def create_function(func_signature, # type: Union[str, Signature]
106123 add_impl = True , # type: bool
107124 doc = None , # type: str
108125 qualname = None , # type: str
126+ co_name = None , # type: str
109127 module_name = None , # type: str
110128 ** attrs ):
111129 """
@@ -130,6 +148,9 @@ def create_function(func_signature, # type: Union[str, Signature]
130148 - `__annotations__` attribute is created to match the annotations in the signature.
131149 - `__doc__` attribute is copied from `func_impl.__doc__` except if overridden using `doc`
132150 - `__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>`.
133154
134155 Finally two new attributes are optionally created
135156
@@ -138,6 +159,13 @@ def create_function(func_signature, # type: Union[str, Signature]
138159 - `__func_impl__` attribute: set if `add_impl` is `True` (default), this attribute contains a pointer to
139160 `func_impl`
140161
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+
141169 :param func_signature: either a string without 'def' such as "foo(a, b: int, *args, **kwargs)" or "(a, b: int)",
142170 or a `Signature` object, for example from the output of `inspect.signature` or from the `funcsigs.signature`
143171 backport. Note that these objects can be created manually too. If the signature is provided as a string and
@@ -159,6 +187,9 @@ def create_function(func_signature, # type: Union[str, Signature]
159187 :param qualname: a string representing the qualified name to be used. If None (default), the `__qualname__` will
160188 default to the one of `func_impl` if `func_signature` is a `Signature`, or to the name defined in
161189 `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.
162193 :param module_name: the name of the module to be set on the function (under __module__ ). If None (default),
163194 `func_impl.__module__` will be used.
164195 :param attrs: other keyword attributes that should be set on the function. Note that `func_impl.__dict__` is not
@@ -177,10 +208,24 @@ def create_function(func_signature, # type: Union[str, Signature]
177208 # name defaults
178209 user_provided_name = True
179210 if func_name is None :
180- # allow None for now, we'll raise a ValueError later if needed
211+ # allow None, this will result in a lambda function being created
181212 func_name = getattr_partial_aware (func_impl , '__name__' , None )
182213 user_provided_name = False
183214
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+
184229 # qname default
185230 user_provided_qname = True
186231 if qualname is None :
@@ -208,25 +253,28 @@ def create_function(func_signature, # type: Union[str, Signature]
208253 func_name = func_name_from_str
209254 if not user_provided_qname :
210255 qualname = func_name
256+ if not user_provided_co_name :
257+ co_name = func_name
211258
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 ]
212265 # fix the signature if needed
213- if func_name_from_str is None :
214- if func_name is None :
215- raise ValueError ("Invalid signature for created function: `None` function name. This "
216- "probably happened because the decorated function %s has no __name__. You may "
217- "wish to pass an explicit `func_name` or to complete the signature string"
218- "with the name before the parenthesis." % func_impl )
219- func_signature_str = func_name + func_signature_str
266+ elif func_name_from_str is None :
267+ func_signature_str = co_name + func_signature_str
220268
221269 elif isinstance (func_signature , Signature ):
222270 # create the signature string
223- if func_name is None :
224- raise ValueError ("Invalid signature for created function: `None` function name. This "
225- "probably happened because the decorated function %s has no __name__. You may "
226- "wish to pass an explicit `func_name` or to provide the new signature as a "
227- "string containing the name" % func_impl )
228- func_signature_str = get_signature_string (func_name , func_signature , evaldict )
271+ create_lambda = not _is_valid_func_def_name (co_name )
229272
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 )
230278 else :
231279 raise TypeError ("Invalid type for `func_signature`: %s" % type (func_signature ))
232280
@@ -255,6 +303,11 @@ def create_function(func_signature, # type: Union[str, Signature]
255303 body = get_legacy_py_generator_body_template () % (func_signature_str , params_str )
256304 elif isasyncgenfunction (func_impl ):
257305 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 )
258311 else :
259312 body = "def %s\n return _func_impl_(%s)\n " % (func_signature_str , params_str )
260313
@@ -264,7 +317,10 @@ def create_function(func_signature, # type: Union[str, Signature]
264317 # create the function by compiling code, mapping the `_func_impl_` symbol to `func_impl`
265318 protect_eval_dict (evaldict , func_name , params_names )
266319 evaldict ['_func_impl_' ] = func_impl
267- f = _make (func_name , params_names , body , evaldict )
320+ if create_lambda :
321+ f = _make ("lambda_" , params_names , body , evaldict )
322+ else :
323+ f = _make (co_name , params_names , body , evaldict )
268324
269325 # add the source annotation if needed
270326 if add_source :
@@ -297,6 +353,24 @@ def _is_generator_func(func_impl):
297353 return isgeneratorfunction (func_impl )
298354
299355
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+
300374class _SymbolRef :
301375 """
302376 A class used to protect signature default values and type hints when the local context would not be able
@@ -366,6 +440,17 @@ def get_signature_string(func_name, func_signature, evaldict):
366440 return "%s%s:" % (func_name , s )
367441
368442
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+
369454TYPES_WITH_SAFE_REPR = (int , str , bytes , bool )
370455# IMPORTANT note: float is not in the above list because not all floats have a repr that is valid for the
371456# compiler: float('nan'), float('-inf') and float('inf') or float('+inf') have an invalid repr.
@@ -694,6 +779,7 @@ def wraps(wrapped_fun,
694779 append_args = None , # type: Union[str, Parameter, Iterable[Union[str, Parameter]]]
695780 remove_args = None , # type: Union[str, Iterable[str]]
696781 func_name = None , # type: str
782+ co_name = None , # type: str
697783 inject_as_first_arg = False , # type: bool
698784 add_source = True , # type: bool
699785 add_impl = True , # type: bool
@@ -774,30 +860,36 @@ def wraps(wrapped_fun,
774860 :param qualname: a string representing the qualified name to be used. If None (default), the `__qualname__` will
775861 default to the one of `wrapped_fun`, or the one in `new_sig` if `new_sig` is provided as a string with a
776862 non-empty function name.
863+ :param co_name: a string representing the name to be used in the compiled code of the function. If None (default),
864+ the `__code__.co_name` will default to the one of `func_impl` if `func_signature` is a `Signature`, or to the
865+ name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty name.
777866 :param module_name: the name of the module to be set on the function (under __module__ ). If None (default), the
778867 `__module__` attribute of `wrapped_fun` will be used.
779868 :param attrs: other keyword attributes that should be set on the function. Note that the full `__dict__` of
780869 `wrapped_fun` is automatically copied.
781870 :return: a decorator
782871 """
783- func_name , func_sig , doc , qualname , module_name , all_attrs = _get_args_for_wrapping (wrapped_fun , new_sig ,
784- remove_args ,
785- prepend_args , append_args ,
786- func_name , doc ,
787- qualname , module_name , attrs )
872+ func_name , func_sig , doc , qualname , co_name , module_name , all_attrs = _get_args_for_wrapping (wrapped_fun , new_sig ,
873+ remove_args ,
874+ prepend_args ,
875+ append_args ,
876+ func_name , doc ,
877+ qualname , co_name ,
878+ module_name , attrs )
788879
789880 return with_signature (func_sig ,
790881 func_name = func_name ,
791882 inject_as_first_arg = inject_as_first_arg ,
792883 add_source = add_source , add_impl = add_impl ,
793884 doc = doc ,
794885 qualname = qualname ,
886+ co_name = co_name ,
795887 module_name = module_name ,
796888 ** all_attrs )
797889
798890
799891def _get_args_for_wrapping (wrapped , new_sig , remove_args , prepend_args , append_args ,
800- func_name , doc , qualname , module_name , attrs ):
892+ func_name , doc , qualname , co_name , module_name , attrs ):
801893 """
802894 Internal method used by @wraps and create_wrapper
803895
@@ -809,6 +901,7 @@ def _get_args_for_wrapping(wrapped, new_sig, remove_args, prepend_args, append_a
809901 :param func_name:
810902 :param doc:
811903 :param qualname:
904+ :param co_name:
812905 :param module_name:
813906 :param attrs:
814907 :return:
@@ -860,6 +953,10 @@ def _get_args_for_wrapping(wrapped, new_sig, remove_args, prepend_args, append_a
860953 qualname = getattr_partial_aware (wrapped , '__qualname__' , None )
861954 if module_name is None :
862955 module_name = getattr_partial_aware (wrapped , '__module__' , None )
956+ if co_name is None :
957+ code = getattr_partial_aware (wrapped , '__code__' , None )
958+ if code is not None :
959+ co_name = code .co_name
863960
864961 # attributes: start from the wrapped dict, add '__wrapped__' if needed, and override with all attrs.
865962 all_attrs = copy (getattr_partial_aware (wrapped , '__dict__' ))
@@ -874,7 +971,7 @@ def _get_args_for_wrapping(wrapped, new_sig, remove_args, prepend_args, append_a
874971 all_attrs ['__wrapped__' ] = wrapped
875972 all_attrs .update (attrs )
876973
877- return func_name , func_sig , doc , qualname , module_name , all_attrs
974+ return func_name , func_sig , doc , qualname , co_name , module_name , all_attrs
878975
879976
880977def with_signature (func_signature , # type: Union[str, Signature]
@@ -884,6 +981,7 @@ def with_signature(func_signature, # type: Union[str, Signature]
884981 add_impl = True , # type: bool
885982 doc = None , # type: str
886983 qualname = None , # type: str
984+ co_name = None , # type: str
887985 module_name = None , # type: str
888986 ** attrs
889987 ):
@@ -925,12 +1023,15 @@ def impl(...):
9251023 :param qualname: a string representing the qualified name to be used. If None (default), the `__qualname__` will
9261024 default to the one of `func_impl` if `func_signature` is a `Signature`, or to the name defined in
9271025 `func_signature` if `func_signature` is a `str` and contains a non-empty name.
1026+ :param co_name: a string representing the name to be used in the compiled code of the function. If None (default),
1027+ the `__code__.co_name` will default to the one of `func_impl` if `func_signature` is a `Signature`, or to the
1028+ name defined in `func_signature` if `func_signature` is a `str` and contains a non-empty name.
9281029 :param module_name: the name of the module to be set on the function (under __module__ ). If None (default), the
9291030 `__module__` attribute of the decorated function will be used.
9301031 :param attrs: other keyword attributes that should be set on the function. Note that the full `__dict__` of the
9311032 decorated function is not automatically copied.
9321033 """
933- if func_signature is None :
1034+ if func_signature is None and co_name is None :
9341035 # make sure that user does not provide non-default other args
9351036 if inject_as_first_arg or not add_source or not add_impl :
9361037 raise ValueError ("If `func_signature=None` no new signature will be generated so only `func_name`, "
@@ -959,6 +1060,7 @@ def replace_f(f):
9591060 add_impl = add_impl ,
9601061 doc = doc ,
9611062 qualname = qualname ,
1063+ co_name = co_name ,
9621064 module_name = module_name ,
9631065 _with_sig_ = True , # special trick to tell create_function that we're @with_signature
9641066 ** attrs
0 commit comments