def as_traceback(self): if tproxy: return tproxy(TracebackType, self.__tproxy_handler) elif tb_set_next: f_code = self.tb_frame.f_code code = compile( '\n' * (self.tb_lineno - 1) + 'raise __traceback_maker', self.tb_frame.f_code.co_filename, 'exec') if PY3: code = CodeType(0, 0, f_code.co_nlocals, f_code.co_stacksize, f_code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename, f_code.co_name, code.co_firstlineno, b"", (), ()) else: code = CodeType(0, f_code.co_nlocals, f_code.co_stacksize, f_code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename.encode(), f_code.co_name.encode(), code.co_firstlineno, b"", (), ()) try: exec(code, self.tb_frame.f_globals, {}) except: tb = sys.exc_info()[2].tb_next tb_set_next(tb, self.tb_next and self.tb_next.as_traceback()) try: return tb finally: del tb else: raise RuntimeError("Cannot re-create traceback !")
def recreate_code(code, codes_offsets, codes_lines): """Recreate code with new lnotab Arguments: code -- original code object codes_offsets -- updated list of offsets codes_lines -- updated list of lines """ offsets = codes_offsets[id(code)] lines = codes_lines[id(code)] new_lnotab = reconstruct_lnotab(code.co_firstlineno, offsets, lines) new_consts = [] for const in code.co_consts: if isinstance(const, CodeType): new_consts.append(recreate_code(const, codes_offsets, codes_lines)) else: new_consts.append(const) if PY3: new_code = CodeType(code.co_argcount, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, tuple(new_consts), code.co_names, code.co_varnames, code.co_filename, code.co_name, code.co_firstlineno, new_lnotab, code.co_freevars, code.co_cellvars) else: new_code = CodeType(code.co_argcount, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, tuple(new_consts), code.co_names, code.co_varnames, code.co_filename, code.co_name, code.co_firstlineno, new_lnotab, code.co_freevars, code.co_cellvars) return new_code
def rename(f): if not isinstance(f, (LambdaType, FunctionType)): # TODO: Can't raise TypeError; @fploop et al. do-it-now-and-replace-def-with-result # TODO: decorators need to do this. return f f = copy(f) # __name__ for tools like pydoc; __qualname__ for repr(); __code__.co_name for stack traces # https://stackoverflow.com/questions/40661758/name-of-a-python-function-in-a-stack-trace # https://stackoverflow.com/questions/16064409/how-to-create-a-code-object-in-python f.__name__ = name j = f.__qualname__.rfind('.') f.__qualname__ = "{}.{}".format(f.__qualname__[:j], name) if j != -1 else name # __code__.co_name is read-only, but there's a types.CodeType constructor # that we can use to re-create the code object with the new name. # (This is no worse than what the stdlib's Lib/modulefinder.py already does.) co = f.__code__ # https://github.com/ipython/ipython/blob/master/IPython/core/interactiveshell.py # https://www.python.org/dev/peps/pep-0570/ if version_info > (3, 8, 0, 'alpha', 3): # Python 3.8+ f.__code__ = CodeType(co.co_argcount, co.co_posonlyargcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, name, co.co_firstlineno, co.co_lnotab, co.co_freevars, co.co_cellvars) else: f.__code__ = CodeType(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, name, co.co_firstlineno, co.co_lnotab, co.co_freevars, co.co_cellvars) return f
def as_traceback(self): """ Convert to a builtin Traceback object that is usable for raising or rendering a stacktrace. """ if tproxy: return tproxy(TracebackType, self.__tproxy__) if not tb_set_next: raise RuntimeError("Unsupported Python interpreter!") current = self top_tb = None tb = None while current: f_code = current.tb_frame.f_code code = compile( '\n' * (current.tb_lineno - 1) + 'raise __traceback_maker', current.tb_frame.f_code.co_filename, 'exec') if hasattr(code, "replace"): # Python 3.8 and newer code = code.replace(co_argcount=0, co_filename=f_code.co_filename, co_name=f_code.co_name, co_freevars=(), co_cellvars=()) elif PY3: code = CodeType(0, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename, f_code.co_name, code.co_firstlineno, code.co_lnotab, (), ()) else: code = CodeType(0, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename.encode(), f_code.co_name.encode(), code.co_firstlineno, code.co_lnotab, (), ()) # noinspection PyBroadException try: exec(code, dict(current.tb_frame.f_globals), {}) except Exception: next_tb = sys.exc_info()[2].tb_next if top_tb is None: top_tb = next_tb if tb is not None: tb_set_next(tb, next_tb) tb = next_tb del next_tb current = current.tb_next try: return top_tb finally: del top_tb del tb
def new_code( self, code, argcount=0, posonlyargcount=0, kwonlyargcount=0, nlocals=0, stacksize=0, flags=0, constants=(), names=(), varnames=(), filename="foo.py", name="foo", firstlineno=1, lnotab=b"", freevars=(), cellvars=(), ): if sys.version_info >= (3, 8): return CodeType( argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, constants, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars, ) assert not posonlyargcount return CodeType( argcount, kwonlyargcount, nlocals, stacksize, flags, code, constants, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars, )
def as_traceback(self): if tproxy: return tproxy(TracebackType, self.__tproxy_handler) if not tb_set_next: raise RuntimeError("Cannot re-create traceback !") current = self top_tb = None tb = None while current: f_code = current.tb_frame.f_code code = compile( '\n' * (current.tb_lineno - 1) + 'raise __traceback_maker', current.tb_frame.f_code.co_filename, 'exec') if hasattr(code, "replace"): # Python 3.8 and newer code = code.replace(co_argcount=0, co_filename=f_code.co_filename, co_name=f_code.co_name, co_freevars=(), co_cellvars=()) elif PY3: code = CodeType(0, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename, f_code.co_name, code.co_firstlineno, code.co_lnotab, (), ()) else: code = CodeType(0, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename.encode(), f_code.co_name.encode(), code.co_firstlineno, code.co_lnotab, (), ()) # noinspection PyBroadException try: exec(code, current.tb_frame.f_globals, {}) except: next_tb = sys.exc_info()[2].tb_next if top_tb is None: top_tb = next_tb if tb is not None: tb_set_next(tb, next_tb) tb = next_tb del next_tb current = current.tb_next try: return top_tb finally: del top_tb del tb
def code(self, parent=None): if self.blocks: raise AssertionError("%d unclosed block(s)" % len(self.blocks)) flags = self.co_flags & ~CO_NOFREE if parent is not None: locals_written = self.locals_written() self.makefree([ n for n in self.co_varnames[self.co_argcount + ( (self.co_flags & CO_VARARGS) == CO_VARARGS) + ( (self.co_flags & CO_VARKEYWORDS) == CO_VARKEYWORDS):] if n not in locals_written ]) if not self.co_freevars and not self.co_cellvars: flags |= CO_NOFREE elif parent is not None and self.co_freevars: parent.makecells(self.co_freevars) return CodeType(self.co_argcount, len(self.co_varnames), self.co_stacksize, flags, self.co_code.tostring(), tuple(self.co_consts), tuple(self.co_names), tuple(self.co_varnames), self.co_filename, self.co_name, self.co_firstlineno, self.co_lnotab.tostring(), self.co_freevars, self.co_cellvars)
def update(f, **kwargs): "A function that performs a functional update on a function." code = f.__code__ attrs = [ 'co_argcount', 'co_kwonlyargcount', 'co_nlocals', 'co_stacksize', 'co_flags', 'co_code', 'co_consts', 'co_names', 'co_varnames', 'co_filename', 'co_name', 'co_firstlineno', 'co_lnotab', 'co_freevars', 'co_cellvars', ] newcode = CodeType(*(kwargs.get(a, getattr(code, a)) for a in attrs)) return FunctionType( newcode, f.__globals__, f.__name__, f.__defaults__, f.__closure__, )
def deserialize_function(f: dict): code_fields = f[CODE_FIELD_NAME][VALUE_FIELD_NAME] code_args = [] for field in CODE_OBJECT_ARGS: arg = code_fields[field] if type(arg) == dict: code_args.append(deserialize_obj(arg)) # if arg[TYPE_FIELD_NAME] == "bytes": # code_args.append(bytes(arg[VALUE_FIELD_NAME])) # else: # code_args.append(tuple(arg[VALUE_FIELD_NAME])) else: code_args.append(arg) details = [CodeType(*code_args)] glob = {"__builtins__": __builtins__} for name, o in f[GLOBAL_FIELD_NAME].items(): glob[name] = deserialize_obj(o) details.append(glob) for attr in FUNCTION_ATTRS_NAMES: if attr == CODE_FIELD_NAME: continue details.append(deserialize_obj(f[attr])) result_func = FunctionType(*details) if result_func.__name__ in result_func.__getattribute__(GLOBAL_FIELD_NAME): result_func.__getattribute__(GLOBAL_FIELD_NAME)[ result_func.__name__] = result_func return result_func
def _import_codetup(codetup): if is_py38x: (argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) = codetup elif is_py3k: (argcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) = codetup else: (argcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) = codetup consts2 = [] for const in consts: if isinstance(const, tuple) and len(const) == 2 and const[0] == CODEOBJ_MAGIC: consts2.append(_import_codetup(const[1])) else: consts2.append(const) consts = tuple(consts2) if is_py38x: codetup = (argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) elif is_py3k: codetup = (argcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) else: codetup = (argcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) return CodeType(*codetup)
def implicit_self(function): code = function.func_code bytecode, varnames, names = inject_self(code) function.func_code = CodeType(code.co_argcount + 1, code.co_nlocals + 1, code.co_stacksize, code.co_flags, bytecode, code.co_consts, names, varnames, code.co_filename, code.co_name, code.co_firstlineno, code.co_lnotab, code.co_freevars, code.co_cellvars)
def copy_code_with_changes(codeobject, argcount=None, nlocals=None, stacksize=None, flags=None, code=None, consts=None, names=None, varnames=None, filename=None, name=None, firstlineno=None, lnotab=None): if argcount is None: argcount = codeobject.co_argcount if nlocals is None: nlocals = codeobject.co_nlocals if stacksize is None: stacksize = codeobject.co_stacksize if flags is None: flags = codeobject.co_flags if code is None: code = codeobject.co_code if consts is None: consts = codeobject.co_consts if names is None: names = codeobject.co_names if varnames is None: varnames = codeobject.co_varnames if filename is None: filename = codeobject.co_filename if name is None: name = codeobject.co_name if firstlineno is None: firstlineno = codeobject.co_firstlineno if lnotab is None: lnotab = codeobject.co_lnotab return CodeType(argcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab)
def create_new_co(code_object): new_code = swap_add_mul(code_object.co_code) new_const = [] for c in code_object.co_consts: if isinstance(c, CodeType): new_const.append(create_new_co(c)) else: new_const.append(c) new_code_object = CodeType( code_object.co_argcount, code_object.co_kwonlyargcount, code_object.co_nlocals, code_object.co_stacksize, code_object.co_flags, new_code, tuple(new_const), code_object.co_names, code_object.co_varnames, code_object.co_filename, code_object.co_name, code_object.co_firstlineno, code_object.co_lnotab, code_object.co_freevars, code_object.co_cellvars, ) return new_code_object
def new_code(c): '''A new code object with a __class__ cell added to freevars''' return CodeType(c.co_argcount, c.co_kwonlyargcount, c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names, c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno, c.co_lnotab, c.co_freevars + ('__class__', ), c.co_cellvars)
def _deserialize_func(funcs, globalDict): items = pickle.loads(funcs) res = None for objType, name, data in items: if objType == 'func': codeArgs, funcArgs, updatedGlobals = pickle.loads(data) code = CodeType(*codeArgs) globalDict.update(**updatedGlobals) value = FunctionType(code, globalDict, *funcArgs) elif objType == 'mod': value = __import__(data) elif objType == 'oldclass': class_name, module, bases, class_dict = data value = typesmod.ClassType(class_name, bases, {k:_deserialize_func(v, globalDict) for k, v in class_dict.items()}) value.__module__ = module elif objType == 'type': raise Exception('deserialize type') else: raise Exception('Unknown serialization type') globalDict[name] = value if res is None: res = value return res
def replace_paths_in_code(self, co: types.CodeType) -> types.CodeType: new_filename = original_filename = os.path.normpath(co.co_filename) for f, r in self.replace_paths: if original_filename.startswith(f): new_filename = r + original_filename[len(f):] break if self.debug and original_filename not in self.processed_paths: if new_filename != original_filename: self.msgout( 2, "co_filename %r changed to %r" % ( original_filename, new_filename, ), ) else: self.msgout( 2, "co_filename %r remains unchanged" % (original_filename, )) self.processed_paths.append(original_filename) consts = list(co.co_consts) for i in range(len(consts)): # pylint: disable=consider-using-enumerate if isinstance(consts[i], type(co)): consts[i] = self.replace_paths_in_code(consts[i]) return co.replace(co_consts=tuple(consts), co_filename=new_filename)
def code_with_custom_location(code, filename, location): return CodeType(code.co_argcount, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, filename, location, code.co_firstlineno, code.co_lnotab, code.co_freevars, code.co_cellvars)
def _make_cell_set_template_code(): """See _cell_set""" def _cell_set_factory(value): lambda: cell cell = value co = _cell_set_factory.__code__ _cell_set_template_code = CodeType( co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno, co.co_lnotab, co.co_cellvars, # co_freevars is initialized with co_cellvars (), # co_cellvars is made empty ) return _cell_set_template_code
def execute_bytecode(code): """ Executes the provided bytecode. Handy for getting the top item off the stack. """ from types import CodeType import builtins # This should be large enough for most things stacksize = 1024 # Load in enough for put_on_stack to work. # NOTE: This function is unable to call "import" or similar # dangerous things due to co_names acting as a whitelist. # (Python loads names from a constants array, so it can"t # load something that"s not there!) consts = (*range(256), ) names = ("chr", "ord", "globals", "locals", "getattr", "setattr") # Tag on a trailing RETURN call just incase. code += b"S\x00" # Construt the code object inject = CodeType(0, 0, 0, stacksize, 2, code, consts, names, (), "", "", 0, b"", (), ()) # Create a copy of globals() and load in builtins. builtins aren"t # normally included in global scope. globs = dict(globals()) globs.update({i: getattr(builtins, i) for i in dir(builtins)}) # Go go go! return eval(inject, globs)
def build(self): # let x = let a = 1 in a + a in x * x # (lambda x : x * x )( (lambda a: a + a) (1) ) #self.consts.add( 1 ) #self.varnames.add( 'x' ) #self.freevars.add( 'a' ) self.converts() self.flags = 1 # NOFREE + OPTIMZED self.code += [opmap["RETURN_VALUE"], 0] code = CodeType( self.argcount, # argcount self.kwonlyargcount, # kwonlyargcount self.nlocals, # nlocals self.stacksize, # stacksize self.flags, # flags bytes(self.code), # codestring self.consts, # consts self.names, # names self.varnames, # varnames self.filename, # filename self.name, # name self.firstlineno, # firstlineno self.lnotab, # lnotab self.freevars, # freevars self.cellvars, # cellvars ) return code
def __call__(self, func): self.func = func _globals_ = func.__globals__ _code_ = func.__code__ varnames = list(_code_.co_varnames) + [v for v in self.env.keys()] nlocals = len(varnames) binlst = list(_code_.co_code) lstbin = self.processGlobals(binlst, _code_.co_names, varnames) genCo = self.genCode(_code_.co_consts, _code_.co_varnames) lstbin = genCo + lstbin consts = list(_code_.co_consts) + [v for v in self.env.values()] code = CodeType( _code_.co_argcount, # argcount _code_.co_kwonlyargcount, # kwonlyargcount nlocals, # nlocals _code_.co_stacksize, # stacksize _code_.co_flags, # flags bytes(lstbin), # codestring tuple(consts), # consts _code_.co_names, # names tuple(varnames), # varnames _code_.co_filename, # filename _code_.co_name, # name _code_.co_firstlineno, # firstlineno _code_.co_lnotab, # lnotab _code_.co_freevars, # freevars _code_.co_cellvars, # cellvars ) #_globals_.update(self.env) function = FunctionType(code, _globals_) return function
def makeCode(self): self.converts() self.getIndexs() self.LabelConvert() self.nlocals = len(self.varnames) # varnames is local variable names and function arguments self.code += [opmap["RETURN_VALUE"],0] #print( self.code ) code = CodeType(self.argcount, # argcount self.kwonlyargcount, # kwonlyargcount self.nlocals, # nlocals self.stacksize, # stacksize self.flags, # flags bytes(self.code), # codestring self.consts, # consts self.names, # names self.varnames, # varnames self.filename, # filename self.name, # name self.firstlineno, # firstlineno self.lnotab, # lnotab self.freevars, # freevars self.cellvars, # cellvars ) return code
def code_object_replace(code: types.CodeType, **kwargs) -> types.CodeType: """ Return a copy of the code object with new values for the specified fields. """ try: kwargs["co_consts"] = tuple(kwargs["co_consts"]) except ValueError: pass # Python 3.8+ if hasattr(code, "replace"): return code.replace(**kwargs) params = [ kwargs.get("co_argcount", code.co_argcount), kwargs.get("co_kwonlyargcount", code.co_kwonlyargcount), kwargs.get("co_nlocals", code.co_nlocals), kwargs.get("co_stacksize", code.co_stacksize), kwargs.get("co_flags", code.co_flags), kwargs.get("co_code", code.co_code), kwargs.get("co_consts", code.co_consts), kwargs.get("co_names", code.co_names), kwargs.get("co_varnames", code.co_varnames), kwargs.get("co_filename", code.co_filename), kwargs.get("co_name", code.co_name), kwargs.get("co_firstlineno", code.co_firstlineno), kwargs.get("co_lnotab", code.co_lnotab), kwargs.get("co_freevars", code.co_freevars), kwargs.get("co_cellvars", code.co_cellvars), ] return types.CodeType(*params)
def stash(self, lineno=1, col_offset=0): stashed = super().stash(lineno, col_offset) for obj in self.objects: # Update the firstlineno in the functions so that it matches # the position in the written file (updates to the functions # above them might have pushed them down) co = obj.__code__ if co.co_firstlineno != lineno: try: obj.__code__ = CodeType( co.co_argcount, co.co_posonlyargcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name, lineno, co.co_lnotab, co.co_freevars, co.co_cellvars, ) except Exception: # pragma: no cover # It's not a major issue if it fails pass return stashed
def _replace_codestring(old_code, new_code): # new_code should have same clean signature as old_code ## | code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring, ## | constants, names, varnames, filename, name, firstlineno, ## | lnotab[, freevars[, cellvars]]) # codestring is co_code!! #from types import CodeType old_doc_new_const = old_code.co_consts[:1] + new_code.co_consts[1:] code = \ CodeType(new_code.co_argcount, new_code.co_kwonlyargcount, new_code.co_nlocals, new_code.co_stacksize, new_code.co_flags, new_code.co_code, # codestring, old_doc_new_const, # new_code.co_constants, new_code.co_names, new_code.co_varnames, old_code.co_filename, old_code.co_name, old_code.co_firstlineno, old_code.co_lnotab, new_code.co_freevars, new_code.co_cellvars) return code
def test_qualnames(self): self.assertEqual(cinder._get_qualname(f.__code__), "f") self.assertEqual(cinder._get_qualname(C.x.__code__), "C.x") self.assertEqual(cinder._get_qualname(C.sm.__code__), "C.sm") self.assertEqual(cinder._get_qualname(C.cm.__code__), "C.cm") self.assertEqual(cinder._get_qualname(C().f().__code__), "C.f.<locals>.G.y") c = f.__code__ co = CodeType( c.co_argcount, c.co_posonlyargcount, c.co_kwonlyargcount, c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names, c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno, c.co_lnotab, c.co_freevars, c.co_cellvars, ) self.assertIsNone(cinder._get_qualname(co)) co = c.replace(co_flags=c.co_flags) self.assertEqual(cinder._get_qualname(co), "f")
def mock_code(name): """Makes a fake code object by name for built-in functions.""" left_args = (0, 0, 0, 0, b'', (), (), (), '') if PY3: left_args = (0, ) + left_args args = left_args + (name, 0, b'') return CodeType(*args)
def build (self): code = CodeType(0, # argcount 0, # kwonlyargcount len(self.varnames), # nlocals 2, # stacksize 67, # flags bytes(self.opec), # codestring tuple(self.consts), # consts tuple(self.names), # names tuple(self.varnames), # varnames '<unknow>', # filename "<unknow>", # name 1, # firstlineno b'', # lnotab tuple (), # freevars tuple (), # cellvars ) return code
def visit(self, co, *, name=None): code = co.co_code consts = list(co.co_consts) names = co.co_names lnotab = co.co_lnotab for n in range(self._passes): code = _optimize(code, consts, names, lnotab) return super().visit( CodeType( co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, _calculate_stack_effect(code), co.co_flags, code, tuple(consts), names, tuple(self.visit_varnames(co.co_varnames)), co.co_filename, self.visit_name(name if name is not None else co.co_name), co.co_firstlineno, co.co_lnotab, tuple(self.visit_freevars(co.co_freevars)), tuple(self.visit_cellvars(co.co_cellvars)), ), name=name, )
def rename(f): if not isinstance(f, (LambdaType, FunctionType)): # TODO: Can't raise TypeError; @fploop et al. do-it-now-and-replace-def-with-result # TODO: decorators need to do this. return f f = copy(f) # __name__ for tools like pydoc; __qualname__ for repr(); __code__.co_name for stack traces # https://stackoverflow.com/questions/40661758/name-of-a-python-function-in-a-stack-trace # https://stackoverflow.com/questions/16064409/how-to-create-a-code-object-in-python f.__name__ = name idx = f.__qualname__.rfind('.') f.__qualname__ = f"{f.__qualname__[:idx]}.{name}" if idx != -1 else name # __code__.co_name is read-only, but there's a types.CodeType constructor # that we can use to re-create the code object with the new name. # (This is no worse than what the stdlib's Lib/modulefinder.py already does.) co = f.__code__ # https://github.com/ipython/ipython/blob/master/IPython/core/interactiveshell.py # https://www.python.org/dev/peps/pep-0570/ # https://docs.python.org/3/library/types.html#types.CodeType # https://docs.python.org/3/library/inspect.html#types-and-members if version_info >= (3, 8, 0): # Python 3.8+: positional-only parameters # In Python 3.8+, `CodeType` has the convenient `replace()` method to functionally update it. # In Python 3.10, we must actually use it to avoid losing the line number info, # or `inspect.stack()` will crash in the unit tests for `callsite_filename()`. f.__code__ = f.__code__.replace(co_name=name) else: f.__code__ = CodeType(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, name, co.co_firstlineno, co.co_lnotab, co.co_freevars, co.co_cellvars) return f