Hide keyboard shortcuts

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 

10 

11 

12def change_style(name): 

13 """ 

14 Switches from *AaBb* into *aa_bb*. 

15 

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 + "_" 

22 

23 

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

49 

50 

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) 

57 

58 

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) 

65 

66 

67def debug_dump(clname, obj, folder=None, ops=None): 

68 """ 

69 Dumps an object for debug purpose. 

70 

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

102 

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 

113 

114 

115def debug_print(k, obj, printed): 

116 """ 

117 Displays informations on an object. 

118 

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

136 

137 

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. 

144 

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

181 

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 

207 

208 

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