-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
tools.py
152 lines (113 loc) · 3.53 KB
/
tools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import sys
import traceback
# Python 3 compatibility
from ._compat import reduce, as_unicode
CHAR_ESCAPE = u'.'
CHAR_SEPARATOR = u','
def import_module(name, required=True):
"""
Import module by name
:param name:
Module name
:param required:
If set to `True` and module was not found - will throw exception.
If set to `False` and module was not found - will return None.
Default is `True`.
"""
try:
__import__(name, globals(), locals(), [])
except ImportError:
if not required and module_not_found():
return None
raise
return sys.modules[name]
def import_attribute(name):
"""
Import attribute using string reference.
:param name:
String reference.
Raises ImportError or AttributeError if module or attribute do not exist.
Example::
import_attribute('a.b.c.foo')
"""
path, attr = name.rsplit('.', 1)
module = __import__(path, globals(), locals(), [attr])
return getattr(module, attr)
def module_not_found(additional_depth=0):
"""
Checks if ImportError was raised because module does not exist or
something inside it raised ImportError
:param additional_depth:
supply int of depth of your call if you're not doing
import on the same level of code - f.e., if you call function, which is
doing import, you should pass 1 for single additional level of depth
"""
tb = sys.exc_info()[2]
if len(traceback.extract_tb(tb)) > (1 + additional_depth):
return False
return True
def rec_getattr(obj, attr, default=None):
"""
Recursive getattr.
:param attr:
Dot delimited attribute name
:param default:
Default value
Example::
rec_getattr(obj, 'a.b.c')
"""
try:
return reduce(getattr, attr.split('.'), obj)
except AttributeError:
return default
def get_dict_attr(obj, attr, default=None):
"""
Get attribute of the object without triggering its __getattr__.
:param obj:
Object
:param attr:
Attribute name
:param default:
Default value if attribute was not found
"""
for obj in [obj] + obj.__class__.mro():
if attr in obj.__dict__:
return obj.__dict__[attr]
return default
def escape(value):
return (as_unicode(value)
.replace(CHAR_ESCAPE, CHAR_ESCAPE + CHAR_ESCAPE)
.replace(CHAR_SEPARATOR, CHAR_ESCAPE + CHAR_SEPARATOR))
def iterencode(iter):
"""
Encode enumerable as compact string representation.
:param iter:
Enumerable
"""
return ','.join(as_unicode(v)
.replace(CHAR_ESCAPE, CHAR_ESCAPE + CHAR_ESCAPE)
.replace(CHAR_SEPARATOR, CHAR_ESCAPE + CHAR_SEPARATOR)
for v in iter)
def iterdecode(value):
"""
Decode enumerable from string presentation as a tuple
"""
if not value:
return tuple()
result = []
accumulator = u''
escaped = False
for c in value:
if not escaped:
if c == CHAR_ESCAPE:
escaped = True
continue
elif c == CHAR_SEPARATOR:
result.append(accumulator)
accumulator = u''
continue
else:
escaped = False
accumulator += c
result.append(accumulator)
return tuple(result)