Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief A couple of tools unrelated to what the package does.
4"""
5import pickle
6import keyword
7import re
8import types
9import numpy
12def change_style(name):
13 """
14 Switches from *AaBb* into *aa_bb*.
16 @param name name to convert
17 @return converted name
18 """
19 s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
20 s2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
21 return s2 if not keyword.iskeyword(s2) else s2 + "_"
24def numpy_min_max(x, fct, minmax=False):
25 """
26 Returns the minimum of an array.
27 Deals with text as well.
28 """
29 try:
30 if hasattr(x, 'todense'):
31 x = x.todense()
32 if (x.dtype.kind[0] not in 'Uc' or
33 x.dtype in {numpy.uint8}):
34 return fct(x)
35 try: # pragma: no cover
36 x = x.ravel()
37 except AttributeError: # pragma: no cover
38 pass
39 keep = list(filter(lambda s: isinstance(s, str), x))
40 if len(keep) == 0: # pragma: no cover
41 return numpy.nan
42 keep.sort(reverse=minmax)
43 val = keep[0]
44 if len(val) > 10: # pragma: no cover
45 val = val[:10] + '...'
46 return "%r" % val
47 except (ValueError, TypeError, AttributeError):
48 return '?'
51def numpy_min(x):
52 """
53 Returns the maximum of an array.
54 Deals with text as well.
55 """
56 return numpy_min_max(x, lambda x: x.min(), minmax=False)
59def numpy_max(x):
60 """
61 Returns the maximum of an array.
62 Deals with text as well.
63 """
64 return numpy_min_max(x, lambda x: x.max(), minmax=True)
67def debug_dump(clname, obj, folder=None, ops=None):
68 """
69 Dumps an object for debug purpose.
71 @param clname class name
72 @param obj object
73 @param folder folder
74 @param ops operator to dump
75 @return filename
76 """
77 def debug_print_(obj, prefix=''):
78 name = clname
79 if isinstance(obj, dict):
80 if 'in' in obj and 'out' in obj:
81 nan_in = any(map(lambda o: any(map(numpy.isnan, o.ravel())),
82 obj['in']))
83 nan_out = any(map(lambda o: any(map(numpy.isnan, o.ravel())),
84 obj['out']))
85 if not nan_in and nan_out:
86 print("NAN-notin-out ", name, prefix,
87 {k: getattr(ops, k, '?') for k in getattr(ops, 'atts', {})})
88 return True
89 return False # pragma: no cover
90 for k, v in obj.items(): # pragma: no cover
91 debug_print_([v], k)
92 return None # pragma: no cover
93 if isinstance(obj, list):
94 for i, o in enumerate(obj):
95 if o is None:
96 continue
97 if any(map(numpy.isnan, o.ravel())):
98 print("NAN", prefix, i, name, o.shape)
99 return None
100 raise NotImplementedError( # pragma: no cover
101 "Unable to debug object of type {}.".format(type(obj)))
103 dump = debug_print_(obj)
104 if dump:
105 name = 'cpu-{}-{}-{}.pkl'.format(
106 clname, id(obj), id(ops))
107 if folder is not None:
108 name = "/".join([folder, name])
109 with open(name, 'wb') as f:
110 pickle.dump(obj, f)
111 return name
112 return None
115def debug_print(k, obj, printed):
116 """
117 Displays informations on an object.
119 @param k name
120 @param obj object
121 @param printed memorizes already printed object
122 """
123 if k not in printed:
124 printed[k] = obj
125 if hasattr(obj, 'shape'):
126 print("-='{}' shape={} dtype={} min={} max={}{}".format(
127 k, obj.shape, obj.dtype, numpy_min(obj),
128 numpy_max(obj),
129 ' (sparse)' if 'coo_matrix' in str(type(obj)) else ''))
130 elif (isinstance(obj, list) and len(obj) > 0 and
131 not isinstance(obj[0], dict)): # pragma: no cover
132 print("-='{}' list len={} min={} max={}".format(
133 k, len(obj), min(obj), max(obj)))
134 else: # pragma: no cover
135 print("-='{}' type={}".format(k, type(obj)))
138def make_callable(fct, obj, code, gl, debug):
139 """
140 Creates a callable function able to
141 cope with default values as the combination
142 of functions *compile* and *exec* does not seem
143 able to take them into account.
145 @param fct function name
146 @param obj output of function *compile*
147 @param code code including the signature
148 @param gl context (local and global)
149 @param debug add debug function
150 @return callable functions
151 """
152 cst = "def " + fct + "("
153 sig = None
154 for line in code.split('\n'):
155 if line.startswith(cst):
156 sig = line
157 break
158 if sig is None: # pragma: no cover
159 raise ValueError(
160 "Unable to find function '{}' in\n{}".format(fct, code))
161 reg = re.compile(
162 "([a-z][A-Za-z_0-9]*)=((None)|(False)|(True)|([0-9.e+-]+))")
163 fall = reg.findall(sig)
164 defs = []
165 for name_value in fall:
166 name = name_value[0]
167 value = name_value[1]
168 if value == 'None':
169 defs.append((name, None))
170 continue
171 if value == 'True':
172 defs.append((name, True))
173 continue
174 if value == 'False':
175 defs.append((name, False))
176 continue
177 f = float(value)
178 if int(f) == f:
179 f = int(f)
180 defs.append((name, f))
182 # debug
183 if debug:
184 gl = gl.copy()
185 gl['debug_print'] = debug_print
186 gl['print'] = print
187 # specific
188 if "value=array([0.], dtype=float32)" in sig:
189 defs.append(('value', numpy.array([0.], dtype=numpy.float32)))
190 res = types.FunctionType(obj, gl, fct, tuple(_[1] for _ in defs))
191 if res.__defaults__ != tuple(_[1] for _ in defs): # pylint: disable=E1101
192 # See https://docs.python.org/3/library/inspect.html
193 # See https://stackoverflow.com/questions/11291242/python-dynamically-create-function-at-runtime
194 lines = [str(sig)] # pragma: no cover
195 for name in ['co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename',
196 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount',
197 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize',
198 'co_varnames']: # pragma: no cover
199 v = getattr(res.__code__, name, None) # pylint: disable=E1101
200 if v is not None:
201 lines.append('%s=%r' % (name, v))
202 raise RuntimeError( # pragma: no cover
203 "Defaults values of function '{}' (defaults={}) are missing.\nDefault: "
204 "{}\n{}\n----\n{}".format(
205 fct, res.__defaults__, defs, "\n".join(lines), code)) # pylint: disable=E1101
206 return res
209def print_code(code, begin=1):
210 """
211 Returns the code with line number.
212 """
213 rows = code.split("\n")
214 return "\n".join("%03d %s" % (i + begin, s)
215 for i, s in enumerate(rows))