SubClass Usage

SubClass decorator that requires args of a function to match or be a subclass of types specificed in constructor.

Includes features:

Sample Classes

from enum import IntEnum, auto

class Color(IntEnum):
    RED = auto()
    GREEN = auto()
    BLUE = auto()

    def __str__(self) -> str:
        return self._name_

class Base:
    def __str__(self) -> str:
        return self.__class__.__name__
class Obj:
    def __str__(self) -> str:
        return self.__class__.__name__
class Foo(Base): pass
class Bar(Foo): pass
class FooBar(Bar): pass
class ObjFoo(Obj): pass

Decorating with *args

Single arg match

This example requires that all args positive Foo or positive ObjFoo. Each subclass type in SubClass constructor represents one positional arg. In the following example two postional args are expected.

from kwhelp.decorator import SubClass

@SubClass(Foo, ObjFoo)
def do_something(*args):
    return [str(arg) for arg in args]

Expected args pass validatation.

>>> print(do_something(Foo(), ObjFoo()))
    ['Foo', 'ObjFoo']

Types dictate that if first arg is not a subclass of Foo or first arg is not a subclass of ObjFoo then an error will be raised.

>>> print(do_something(Foo(), Obj()))
TypeError: Arg in 2nd position is expected to be of a subclass of 'ObjFoo'.
SubClass decorator error.

Arguments passed into function must match the same number of SubClass Types. If not the same count then a ValueError is rasied.

>>> do_something(Foo(), ObjFoo(), Bar())
ValueError: Invalid number of arguments for do_something()
SubClass decorator error.

Multi Choice

from kwhelp.decorator import SubClass

@SubClass((FooBar, ObjFoo),(Color, Obj))
def do_something(*args):
    return str(first), str(last)

This call to do_something raises no errors.

>>> print(do_something(FooBar(), Color.RED))
['FooBar', 'RED']

This call to do_something raised TypeError due to first arg not being a subclass of FooBar or ObjFoo.

>>> print(do_something(Foo(), Color.RED))
TypeError: Arg in 1st position is expected to be of a subclass of 'ObjFoo' or 'FooBar'.
SubClass decorator error.

Decorating with Key, Value

Decorating when a function has key, value pairs for arguments is the same pattern as *args. SubClass type one matches position one of function. SubClass type two matches postiion two of function etc…

from kwhelp.decorator import SubClass

@SubClass(Foo, ObjFoo, Color)
def do_something(first, last, color=Color.GREEN):
    return str(first), str(last) , str(color)
>>> print(do_something(last=ObjFoo(), first=Foo()))
('Foo', 'ObjFoo', 'GREEN')
>>> print(do_something(last=ObjFoo(), first=1))
TypeError: Arg 'first' is expected be a subclass of 'Foo'.
SubClass decorator error.

Primitive Types

In python numbers and str instances are classes. SubClass can also be used to test for numbers and strings.

@SubClass(int, (int, float), str)
def do_something(first, last, end):
    return first, last , end
>>> print(do_something(1, 17, "!!!"))
(1, 17, '!!!')
>>> do_something(1, 44.556, "!!!")
(1, 44.556, '!!!')
>>> print(do_something(1, 44.556))
ValueError: Invalid number of arguments for do_something()
SubClass decorator error.
>>> print(do_something(1, 44.556, 10))
TypeError: Arg 'end' is expected be a subclass of 'str'.
SubClass decorator error.

Option opt_all_args

opt_all_args argument allows the last class type passed into SubClass to validate all remaining arguments of wrapped function.

For more examples see opt_all_args.

@SubClass(float, (float, int), opt_all_args=True)
def sum_num(*args):
    return sum(args)

The first arg of sum_num must be a float. Remaining args can be float or int.

>>> print(sum_num(1.3, 44.556, 10, 22, 45, 7.88))
130.736
>>> print(sum_num(1, 44.556, 10, 22, 45, 7.88))
TypeError: Arg in 1st position is expected to be of a subclass of 'float'.
SubClass decorator error.
>>> print(sum_num(1.3, 44.556, 10, 22, 45, 7.88, "77"))
TypeError: Arg in 7th position is expected to be of a subclass of 'float' or 'int'.
SubClass decorator error.

Opton opt_args_filter

The arguments are validated by SubClass can be filtered by setting opt_args_filter option.

For more examples see opt_args_filter.

In the following example all *args must of or derived from clase Base. opt_args_filter=DecArgEnum.ARGS filters SubClass to only process *args.

from kwhelp.decorator import SubClass, DecArgEnum

@SubClass(Base, opt_all_args=True, opt_args_filter=DecArgEnum.ARGS)
def foo(*args, msg: str):
    result = [str(t) for t in args]
    return msg + ', '.join(result)
>>> result = foo(Foo(), Bar(), FooBar(), ObjFoo(), msg='Summary: ')
>>> print(result)
Summary: Foo, Bar, FooBar, Foo
>>> result = foo(Foo(), Bar(), FooBar(), ObjFoo(), msg='Summary: ')
TypeError: Arg in 4th position is expected to be of a subclass of 'Base'.
SubClass decorator error.

Combined Decorators

SubClass can be combined with other decorators.

The following example limits how many args are allowed by applying ArgsMinMax decorator.

from kwhelp.decorator import SubClass, ArgsMinMax

@ArgsMinMax(max=6)
@SubClass(float, (float, int), opt_all_args=True)
def sum_num(*args):
    return sum(args)
>>> print(sum_num(1.3, 44.556, 10, 22, 45, 7.88))
130.736
>>> print(sum_num(1, 44.556, 10, 22, 45, 7.88, 100))
ValueError: Invalid number of args pass into 'sum_num'.
Expected max of '6'. Got '7' args.
ArgsMinMax decorator error.