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
|
|
|
|