A note on **kwargs parsing

A note on **kwargs parsing#

[1]:
%load_ext autoreload
%autoreload 2
[2]:
from functools import wraps, update_wrapper
from majordome.parsing import FuncArguments
# import majordome.walang

Illustrating FuncArguments#

[3]:
def f(*args, **kwargs):
    """ Arbitrary interface function for illustration. """
    f.parser.update(*args, **kwargs)

    try:
        print(f"a = {f.parser.get("a")}, b = {f.parser.get("b")}")
        f.parser.close()
    except Exception as err:
        print(err)
        pass
  • Arguments are both positional-mandatory

[4]:
f.parser = FuncArguments()
f.parser.add("a", index=0)
f.parser.add("b", index=1)

f(3)
f(3, 4)
Cannot retrieve mandatory positional argument at position 1
a = 3, b = 4
  • Arguments are both keyword-only

[5]:
f.parser = FuncArguments()
f.parser.add("a", default="aaaa")
f.parser.add("b", default="bbbb")

f(3)
f(3, 4)
f(3, b=4)
f(a=3, b=4)
a = aaaa, b = bbbb
Too many positional arguments, got 1 but only 0 were used.
a = aaaa, b = bbbb
Too many positional arguments, got 2 but only 0 were used.
a = aaaa, b = 4
Too many positional arguments, got 1 but only 0 were used.
a = 3, b = 4
  • One positional and another (maybe) positional

[6]:
f.parser = FuncArguments()
f.parser.add("a", index=0)
f.parser.add("b", index=1, default=6)

f(3)
f(3, 4)
f(3, 4, b=4)
a = 3, b = 6
a = 3, b = 4
Cannot have both positional and keyword version of b (1) simultaneously
  • Badly configured

[7]:
f.parser = FuncArguments()

try:
    f.parser.add("a")
    f.parser.add("b")
except Exception as err:
    print(err)
Argument must be either positional or keyword, cannot be neither: a

Creating class constructors#

[8]:
def _init_some_class(cls):
    """ Decorator to enhance SomeClass with argument parsing. """
    orig_init = cls.__init__

    # Maybe stash for later use?
    # cls.__orig_init__ = orig_init

    parser = FuncArguments(greedy_args=False, pop_kw=True)
    parser.add("a", 0)
    parser.add("x", default=None)

    @wraps(orig_init)
    def new_init(self, *args, **kwargs):
        parser.update(*args, **kwargs)
        a = parser.get("a")
        x = parser.get("x")

        orig_init(self, *parser.args, **parser.kwargs)
        parser.close()

        # -- logic goes here
        print(f"some a = {a}")
        print(f"some x = {x}")

        return None

    cls.__init__ = update_wrapper(new_init, orig_init)
    return cls
[9]:
class BaseClass:
    def __init__(self, *args, **kwargs) -> None:
        print(f"args   = {args}")
        print(f"kwargs = {kwargs}")
[10]:
@_init_some_class
class SomeClass(BaseClass):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
[11]:
some = SomeClass(1, 2, 3, x=1, y=2)
args   = (1, 2, 3)
kwargs = {'y': 2}
some a = 1
some x = 1