Passed
Push — master ( 243c32...83a312 )
by Max
50s
created

structured_data._ctor   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 87
rs 10
c 0
b 0
f 0
wmc 22

3 Methods

Rating   Name   Duplication   Size   Complexity  
A Ctor.__class_getitem__() 0 4 2
A Ctor.__init_subclass__() 0 2 1
A Ctor.__new__() 0 6 2

6 Functions

Rating   Name   Duplication   Size   Complexity  
A _parse_constructor() 0 5 2
A _get_args_from_index() 0 4 3
A get_args() 0 7 3
A _interpret_args_from_non_string() 0 5 2
A _checked_eval() 0 5 2
A _extract_tuple_ast() 0 13 5
1
import ast
2
import weakref
3
4
import astor
5
6
_CTOR_CACHE = {}
7
8
9
ARGS = weakref.WeakKeyDictionary()
10
11
12
class Ctor:
13
    """Marker class for enum constructors.
14
15
    To use, index with a sequence of types, and annotate a variable in an
16
    enum-decorated class with it.
17
    """
18
19
    def __new__(cls, args):
20
        if args == ():
21
            return cls
22
        self = object.__new__(cls)
23
        ARGS[self] = args
24
        return _CTOR_CACHE.setdefault(args, self)
25
26
    def __init_subclass__(cls, **kwargs):
27
        raise TypeError
28
29
    def __class_getitem__(cls, args):
30
        if not isinstance(args, tuple):
31
            args = (args,)
32
        return cls(args)
33
34
35
ARGS[Ctor] = ()
36
37
38
def _interpret_args_from_non_string(constructor):
39
    try:
40
        return ARGS.get(constructor)
41
    except TypeError:
42
        return None
43
44
45
def _parse_constructor(constructor):
46
    try:
47
        return ast.parse(constructor, mode='eval')
48
    except Exception:
49
        raise ValueError('parsing annotation failed')
50
51
52
def _get_args_from_index(index):
53
    if isinstance(index, ast.Tuple):
54
        return tuple(astor.to_source(elt) for elt in index.elts)
55
    return (astor.to_source(index),)
56
57
58
def _checked_eval(source, global_ns):
59
    try:
60
        return eval(source, global_ns)
61
    except Exception:
62
        return None
63
64
65
def _extract_tuple_ast(constructor, global_ns):
66
    ctor_ast = _parse_constructor(constructor)
67
    if (
68
            isinstance(ctor_ast.body, ast.Subscript)
69
            and isinstance(ctor_ast.body.slice, ast.Index)):
70
        index = ctor_ast.body.slice.value
71
        ctor_ast.body = ctor_ast.body.value
72
        value = _checked_eval(compile(ctor_ast, '<annotation>', 'eval'), global_ns)
73
        if value is Ctor:
74
            return _get_args_from_index(index)
75
        if value is None:
76
            return None
77
    return _interpret_args_from_non_string(_checked_eval(constructor, global_ns))
78
79
80
def get_args(constructor, global_ns):
81
    if isinstance(constructor, str):
82
        try:
83
            return _extract_tuple_ast(constructor, global_ns)
84
        except ValueError:
85
            return None
86
    return _interpret_args_from_non_string(constructor)
87