Passed
Push — master ( fcc3bd...c60240 )
by Max
53s
created

SumMember.__init__()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
import inspect
2
import typing
3
import weakref
4
5
6
def _should_include(name, static):
7
    if name in SHADOWED_ATTRIBUTES and static is None:
8
        return False
9
    if isinstance(static, SumMember):
10
        return False
11
    return True
12
13
14
class ADTConstructor:
15
    """Base class for ADT Constructor classes."""
16
17
    __slots__ = ()
18
19
    def __dir__(self):
20
        return [
21
            attribute
22
            for attribute in super().__dir__()
23
            if _should_include(attribute, inspect.getattr_static(self, attribute))
24
        ]
25
26
    __eq__ = object.__eq__
27
    __ne__ = object.__ne__
28
    __hash__ = object.__hash__
29
30
31
SHADOWED_ATTRIBUTES = {
32
    "__add__",
33
    "__contains__",
34
    "__getitem__",
35
    "__iter__",
36
    "__len__",
37
    "__mul__",
38
    "__rmul__",
39
    "count",
40
    "index",
41
    "__lt__",
42
    "__le__",
43
    "__gt__",
44
    "__ge__",
45
}
46
47
48
for _attribute in SHADOWED_ATTRIBUTES:
49
    setattr(ADTConstructor, _attribute, None)
50
51
52
class SumMember:
53
    def __init__(self, subcls: type):
54
        self.subcls = subcls
55
56
    def __get__(self, obj, cls):
57
        if cls is ADT_BASES[self.subcls] and obj is None:
58
            return self.subcls
59
        raise AttributeError("Can only access adt members through base class.")
60
61
62
ADT_BASES: typing.MutableMapping[type, type] = weakref.WeakKeyDictionary()
63
64
65
def make_constructor(_cls, name: str, args: typing.Tuple, subclass_order):
66
    length = len(args)
67
68
    class Constructor(_cls, ADTConstructor, tuple):  # type: ignore
69
        __doc__ = f"""Auto-generated subclass {name} of ADT {_cls.__qualname__}.
70
71
        Takes {length} argument{'' if length == 1 else 's'}.
72
        """
73
74
        __slots__ = ()
75
76
        def __new__(cls, *args):
77
            if len(args) != length:
78
                raise ValueError
79
            return super().__new__(cls, args)
80
81
    ADT_BASES[Constructor] = _cls
82
83
    Constructor.__name__ = name
84
    Constructor.__qualname__ = "{qualname}.{name}".format(
85
        qualname=_cls.__qualname__, name=name
86
    )
87
88
    setattr(_cls, name, SumMember(Constructor))
89
    subclass_order.append(Constructor)
90
91
    annotations = {f"_{index}": arg for (index, arg) in enumerate(args)}
92
    parameters = [
93
        inspect.Parameter(name, inspect.Parameter.POSITIONAL_ONLY, annotation=arg)
94
        for (name, arg) in annotations.items()
95
    ]
96
    annotations["return"] = _cls.__qualname__
97
98
    Constructor.__new__.__signature__ = inspect.Signature(  # type: ignore
99
        [inspect.Parameter("cls", inspect.Parameter.POSITIONAL_ONLY)] + parameters,
100
        return_annotation=_cls.__qualname__,
101
    )
102
    Constructor.__new__.__annotations__ = annotations
103
104
105
__all__ = ["ADTConstructor", "make_constructor"]
106