Welcome, guest | Sign In | My Account | Store | Cart
"""kwonly module

"""

def emulate_kwonly(kws, required, withdefaults, leftovers=False):
    """Emulate Python 3's kwonly arguments.

    Parameters:
      kws - the kwargs from which to extract the kwonly args.
      required - an iterable holding the required kwonly args.
      withdefaults - an iterable of pairs mapping names to defaults.
      leftovers - allow kws to be non-empty when all the kwonly args
            have already been popped off.

    Returns:
      The remainder of kws, followed by the values for the kwonly args
      in the same order as they stand in required and then in
      withdefaults.

    Examples:
      Below each "def" clause you'll find the clause that would be
      equivalent in Python 3 to the use of emulate_kwonly().

      >>> def f(a, **kwargs):
      ... #def f(a, *, b, c=5):
      ...     kwargs, b, c = emulate_kwonly(kwargs, ("b",), (("c", 5),))
      ...     # continue as normal
      ...
      >>> def g(a, *args, **kwargs):
      ... #def f(a, *args, b, **kwargs):
      ...     kwargs, b = emulate_kwonly(kwargs, ("b",), (), True)
      ...     # continue as normal

    """

    if hasattr(withdefaults, "items"):
        # allows for OrderedDict to be passed
        withdefaults = withdefaults.items()

    kwonly = []

    # extract the required keyword-only arguments
    missing = []
    for name in required:
        if name not in kws:
            missing.append(name)
        else:
            kwonly.append(kws.pop(name))

    # validate required keyword-only arguments
    if len(missing) > 2:
        end = "s: %s, and %s" % (", ".join(missing[:-1]), missing[-1])
    elif len(missing) == 2:
        end = "s: %s and %s" % tuple(missing)
    elif len(missing) == 1:
        end = ": %s" % tuple(missing)
    if missing:
        msg = "missing %s required keyword-only argument%s"
        raise TypeError(msg % (len(missing), end))

    # handle the withdefaults
    for name, value in withdefaults:
        if name not in kws:
            kwonly.append(value)
        else:
            kwonly.append(kws.pop(name))

    # handle any leftovers
    if not leftovers and kws:
        msg = "got an unexpected keyword argument '%s'"
        raise TypeError(msg % (kws.keys()[0]))

    return [kws] + kwonly


if __name__ == "__main__":

    def f(a, **kwargs):
    #def f(a, *, b, c=5):
        kwargs, b, c = emulate_kwonly(kwargs, ("b",), (("c", 5),))
        return a, b, c

    assert f(1, b=2) == (1, 2, 5)
    assert f(1, b=2, c=3) == (1, 2, 3)

    try: f(b=2)
    except TypeError: pass
    else: raise AssertionError

    try: f(1, 2)
    except TypeError: pass
    else: raise AssertionError

    try: f(1, c=2)
    except TypeError: pass
    else: raise AssertionError

    try: f(1, b=2, d=4)
    except TypeError: pass
    else: raise AssertionError


    def g(a, *args, **kwargs):
    #def f(a, *args, b, **kwargs):
        kwargs, b = emulate_kwonly(kwargs, ("b",), (), True)
        return a, b, kwargs

    assert g(1, b=2) == (1, 2, {})
    assert g(1, b=2, c=3) == (1, 2, dict(c=3))

History