Hi One of the motives for PEP 671 is that the signature of a function fn, and hence the associated help(fn) is sometimes opaque regarding default values. I won't repeat the excellent examples already given. In the current implementation default values are handled outside the compiled code of the function, which is available at fn.__code__. Instead they are stored in 'metadata' associated with the function. Here's one way to see this. from inspect import signature as sig def fn(a, b=1, c=2): return a, b, c sig(fn) # Gives <Signature (a, b=1, c=2)> fn.__defaults__ = ('hi', 'there') sig(fn) # Gives <Signature (a, b='hi', c='there')> We can also change the __code__ object, but care is needed here. def gn(): return (1, 2, 3) fn.__code__ = gn.__code__ fn() # Gives (1, 2, 3). sig(fn) # Gives <Signature ()> fn.__defaults__ # Gives ('hi', 'there') The signature of fn, together with the arguments actually supplied, is used to initialise the code frame which is put on the top of the stack, and in which fn.__code__ executes. I suggest that an implementation which provides additional flexibility in the manner in which the code frame is initialised would be less invasive. Necessarily, PEP 671 allows programmer supplied code to be used in the 'initialisation phase'. The previous attempts place that code in fn.__code__. I suggest that the implementation adds a new attribute fn.__wibble__ to fn, which can either be None or a code object. And if fn.__wibble__ is not None, then it is used to initialise the code frame in which fn.__code__ executes. It would as before take as input the arguments actually supplied by the user. I stop here, saying nothing for now about two important questions. First, what is the programmer syntax for creating such a 'two-part' function fn. Second, what does the user see as a result of help(fn). Or in other words, how to extend the semantics of inspect.signature. with kind regards Jonathan