Rethink what i2.Sig instances should do, or not do.
#76
thorwhalen
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Compatible signatures
Note: There's a whole discussion on this subject here: Signature binary function (e.g. comparison) design
The
__signature__of just changes whatinspect.signaturewill show us (as well as what some IDEs (e.g. using Jedi) will show us. But there is another signature. The internal one. The one that apparently can't be changed dynamically at runtime (though__signature__can!).So in order to keep things sane when changing signatures, we had better not allow the user (unless they explicitly ask us to) change the signature of a function to one that is not compatible with the "actual/real/internal/unchangeable" signature that will be used at runtime.
What does compatible signature mean?
One that makes function call behavior consistent with what the user sees through
inspect.signature.For instance
yisPOSITIONAL_OR_KEYWORD, we should be able to call the function with either position or keyword argument specificationyis 2, we should have the same effect we have by calling the function withy=2as we have by not specifyingyat allKEYWORD_ONLYparameters and still have a consistent signature, since this doesn't effect the function's behavior at all(*args, **kwargs)signaturePOSITIONAL_ONLYandVAR_POSITIONALare consistent with(*args)KEYWORD_ONLYandVAR_KEYWORDare consistent with(**kwargs)So what we need is a
signatures_are_compatible(outer_sig, inner_sig) -> boolsuch that, for anyfuncwith (real/actual) signatureinner_sig,signatures_are_compatible(outer_sig, inner_sig) == Trueif, and only if, the functionwrapped_funcdefined as:is such that, any
(args, kwargs)inputs that are valid inputs offuncare also valid inputs ofwrapped_func.Though note that
func(*args, **kwargs)andwrapped_func(*args, **kwargs)could return different outputs (the defaults could be different.Appendix: More materials and rumination that lead to the above design
One often expects signatures to have an effect of the computation of a function, but it doesn't (see this and other references listed below).
Right now, when wrapping with a
Siginstance, we change the__signature__, but also tamper with the__defaults__and__kwdefaults__of the function to keep them aligned with the signature. Seems like the right thing to do, but it's not enough. It doesn't have the desired effect anyway.So either we should make
Sig:__signature__of the input function -- possibly with a warning or error if it's not a sane thing to do (though maybe some extras could be considered, such as assigning a__name__if the wrapped doesn't have one.)Here are a few examples of unexpected things current
Sigdoes (or doesn't do).Even more examples of
signatureandwrapsweirdness in the references below.More:
References
Relevant questions I've asked on stackoverflow:
Also relevant, a bpo and a pull request I made about
functools.wraps.Possible solutions
I say "solutions", but really the main issue here is to decide what
i2.Sigshould or should not do.Should it just write to
__signature__, or should it also write on__defaults__and__kwdefaults__, or should it go all the way to wrapping the function and rewire the inputs?The solution I'm leaning towards right now is leave
Sigbe back-compatible (so continue doing the extra__defaults__and__kwdefaults__stuff, but return an error (or warning?) when defaults AND kinds change, since the user will only know that there's a problem at run time.Elements of solutions
Following are some things to consider.
If kind and signature change we can implement the effect we want with
i2.wrapper.The following use of
partialto take care of defaults is notable:But it's not really a solution since this changes the signature will change and we can't just do this:
Beta Was this translation helpful? Give feedback.
All reactions