i2mint / i2

Python Mint creation, manipulation, and use
Apache License 2.0
2 stars 1 forks source link

The case for having some wrapped versions of builtins #33

Open thorwhalen opened 2 years ago

thorwhalen commented 2 years ago

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)

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 the WRAPPER_ASSIGNMENTS that functools uses.

┆Issue is synchronized with this Asana task by Unito