In #30 I made a point that we don't want to have Sig resolve signatures by wrapping builtins, because Sig's job is only to provide a valid signature (if any).
That said, it can be useful to have some wrapped builtins available to us. Especially if we can also have some control over particulars of these wrappers.
Following are a few examples of uses.
Providing a valid single signatures when a function is overloaded
Many builtin functions (that break inspect.signature's attempt at providing a signature) don't have a single valid signature, but several possible ones (due to function overloading).
Sometimes it's possible to invent a signature that is compatible with all of these, but sometimes it's just not.
Consider next for example.
The doc says next(iterator[, default]) which means that default is optional. But neither (iterator, default) nor (iterator, default=None) would work as the signature since the first shouldn't allow the function to be called without a default and the second would yield None if the iterator is empty (which is not what next(iterator) should be doing (it should raise a StopIteration error).
On the other hand, the following _next would work:
from i2 import mk_sentinel, Sig
NotGiven = mk_sentinel('Optional', boolean_value=False, repr_='Optional')
def _next(iterator, default=NotGiven):
if default is NotGiven:
return next(iterator)
else:
return next(iterator, default)
Making easier to curry more fully
How can I use functools.partial to make functions like is_int, is_str, is_int_or_float etc.?
Answer: I can't. At least not as "naturally" as I would if isinstance didn't have POSITION_ONLY arguments.
from functools import partial
def _isinstance(obj, class_or_tuple):
return isinstance(obj, class_or_tuple)
is_int = partial(_isinstance, class_or_tuple=int)
assert is_int(3)
assert not is_int('3')
Make some builtin choices make more sense (for a given context)
In order to be able to use wraps functionality more smoothly, I sometimes use one that adds __defaults__ and __kw_defaults)__ to the WRAPPER_ASSIGNMENTS that functools uses.
In #30 I made a point that we don't want to have
Sig
resolve signatures by wrapping builtins, becauseSig
's job is only to provide a valid signature (if any).That said, it can be useful to have some wrapped builtins available to us. Especially if we can also have some control over particulars of these wrappers.
Following are a few examples of uses.
Providing a valid single signatures when a function is overloaded
Many builtin functions (that break
inspect.signature
's attempt at providing a signature) don't have a single valid signature, but several possible ones (due to function overloading). Sometimes it's possible to invent a signature that is compatible with all of these, but sometimes it's just not.Consider
next
for example. The doc saysnext(iterator[, default])
which means that default is optional. But neither(iterator, default)
nor(iterator, default=None)
would work as the signature since the first shouldn't allow the function to be called without adefault
and the second would yieldNone
if the iterator is empty (which is not whatnext(iterator)
should be doing (it should raise aStopIteration
error).On the other hand, the following
_next
would work:Making easier to curry more fully
How can I use
functools.partial
to make functions likeis_int
,is_str
,is_int_or_float
etc.?Answer: I can't. At least not as "naturally" as I would if
isinstance
didn't havePOSITION_ONLY
arguments.Make some builtin choices make more sense (for a given context)
For example, the "Python
functools.wraps
doesn't deal with defaults correctly" python issue -- see also this pull request.In order to be able to use
wraps
functionality more smoothly, I sometimes use one that adds__defaults__
and__kw_defaults)__
to theWRAPPER_ASSIGNMENTS
thatfunctools
uses.┆Issue is synchronized with this Asana task by Unito