/usr/share/pyshared/scitools/errorcheck.py is in python-scitools 0.9.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | """
Functions for checking compatibility of data structures,
right type of data etc.
Example: given a program "check.py"::
from numpy import linspace
q1 = linspace(0, 1, 5)
try:
right_length(q1, 2)
except Exception, e:
print e.__class__.__name__, e
try:
right_size1(q1, (2,2))
except Exception, e:
print e.__class__.__name__, e
try:
q2 = linspace(0, 1, 6)
except Exception, e:
print e.__class__.__name__, e
try:
right_size2(q1, q2)
except Exception, e:
print e.__class__.__name__, e
try:
right_type(q2, list)
except Exception, e:
print e.__class__.__name__, e
try:
wrong_type(q1)
except Exception, e:
print e.__class__.__name__, e
Here is the output (each call triggers the exception)::
ValueError file "check.py", line 5, main program
q1 has length 5, which is not compatible with assumed length 2
ValueError file "check.py", line 9, main program
q1 has size (5,), which is not compatible with assumed size (2, 2)
ValueError file "check.py", line 17, main program
q1 has size (5,), which is not compatible with size (6,) of q2
TypeError file "check.py", line 21, main program
q2 is of type ndarray, while we expected <type 'list'>
TypeError q1 is of wrong type (ndarray).
"""
import types, re, inspect
__all__ = ['right_length', 'right_size1', 'right_size2',
'get_type', 'right_type', 'wrong_type']
def get_argname_in_call(call_name, arg_no):
"""
Utility function for extracting the name of an argument in a function
call.
Caveat: the function call is obtained as a string, and the arguments
obtained by splitting that string with respect to comma.
This can easily lead to errors, e.g., 'myfunc(a, (list,tuple), b)'
will lead to the arguments 'a', '(list', 'tuple)', and 'b'.
'b' is not obtained as argument 3.
========= ======================================================
call_name name of the function in the call
arg_no argument number to extract (1, 2, ...).
return name of the argument and a string ("where") describing
the original call's filename, line number and function.
========= ======================================================
"""
stack = inspect.stack()
#import pprint; pprint.pprint(stack)
#print 'frameinfo:\n', inspect.getframeinfo(stack[3][0])
# call from some function:
filename, line_number, func_name, func_call = stack[2][1:5]
if func_name == '?':
func_name = 'main program'
if func_call is None:
return 'variable', \
'(info on variable names are missing since the call was made from an interpreter)'
func_call = func_call[0]
where = 'file "%s", line %d, %s' % (filename, line_number, func_name)
pattern = r'%s\s*\((.*)\)' % call_name
m = re.search(pattern, func_call)
if m:
args = m.group(1)
# assume comma is separator (will not work if one of the args
# is a tuple, dict or list explicitly listed with its value
# in the call...)
argname = args.split(',')[arg_no-1].strip()
return argname, where
else:
raise ValueError('pattern="%s" does not match "%s"' % \
(pattern, func_call))
def right_length(a, length):
"""
Check that len(a) == length.
========= ======================================================
a any variable for which len(a) is meaningful
length the expected length of a (integer).
========= ======================================================
"""
if len(a) != length:
a_name, where = get_argname_in_call('right_length', 1)
raise ValueError(
'%s\n%s has length %s, which is not compatible with '\
'assumed length %s' % (where, a_name, len(a), length))
else:
return True
def right_size1(a, shape):
"""
Check that a has correct shape.
========= ======================================================
a NumPy array
shape the expected shape of a (int or tuple)
========= ======================================================
"""
if not hasattr(a, 'shape'):
raise TypeError('%s is %s and not a NumPy array' % \
(a_name, type(a)))
if isinstance(shape, int):
shape = (shape,) # wrap in tuple
if a.shape != shape:
a_name, where = get_argname_in_call('right_size1', 1)
raise ValueError(
'%s\n%s has size %s, which is not compatible with assumed size %s' \
% (where, a_name, a.shape, shape))
else:
return True
def right_size2(a1, a2):
"""
Check that a1 and a2 have equal shapes.
a1 and a2 are NumPy arrays.
"""
if hasattr(a1, 'shape') and hasattr(a2, 'shape'):
pass # ok, a1 and a2 are NumPy arrays
else:
raise TypeError('%s is %s and %s is %s - both must be NumPy arrays' \
% (a1_name, type(a1), a2_name, type(a2)))
if a1.shape != a2.shape:
a1_name, where = get_argname_in_call('right_size2', 1)
a2_name, where = get_argname_in_call('right_size2', 2)
raise ValueError(
'%s\n%s has size %s, which is not compatible with size %s of %s' \
% (where, a1_name, a1.shape, a2.shape, a2_name))
else:
return True
def get_type(a):
"""
Extract a's type. First, try to extract a's class name. If this
is unsuccessful, return type(a).
The session below illustrates differences between type(a) nad
get_type(a) for standard classes, instances of user-defined classes,
and class objects (new style and classic classes).
>>> # standard built-in types:
>>> c = [1,2,3]
>>> d = 'my string'
>>> type(c)
<type 'list'>
>>> get_type(c)
'list'
>>> type(d)
<type 'str'>
>>> get_type(d)
'str'
>>>
>>> # user-defined classes and instances:
>>> class A: # classic class
... pass
...
>>> class B(object): # new style class
... pass
...
>>> a = A()
>>> type(a)
<type 'instance'>
>>> get_type(a)
'A'
>>>
>>> b = B()
>>> type(b)
<class '__main__.B'>
>>> get_type(b)
'B'
>>>
>>> # class objects A and B:
>>> type(A)
<type 'classobj'>
>>> get_type(A)
'classobj (i.e. a class object)'
>>> type(B)
<type 'type'>
>>> get_type(B)
'type (i.e. a class object)'
>>>
"""
try:
# try to get a's class name (if possible)
tp = a.__class__.__name__
if tp == 'type': # new style class object?
# add some explanation (can get output "a is of type type")
tp += ' (i.e. a class object)'
except:
# rely on the type (would be less informative if a is instance)
tp = str(type(a))
# if a is a classic class object, tp is "<type 'classobj'>"
# which we translate into something more readable:
if tp == "<type 'classobj'>":
tp = "classobj (i.e. a class object)"
return tp
global message
message = ''
def right_type(a, expected_types, raise_exception=True):
"""
Check that variable a is of the type(s) specified by expected_types,
which is a list/tuple of built-in types (class names), user-defined
class names, or one of the functions callable or numpy.iterable.
return: True if the type is right. Otherwise, raise a TypeError exception
if raise_exception is True. If this value is False,
return False and place a message string explaining what is wrong
in the module variable errorcheck.message.
"""
if not isinstance(expected_types, (list,tuple)):
expected_types = [expected_types] # wrap in list if just single type
t = get_type(a)
if 'a class object' in t:
a_is_class_def = True
else:
a_is_class_def = False
import numpy
match = False
for tp in expected_types:
if tp is callable or tp is numpy.iterable:
if tp(a):
match = True
break
if a_is_class_def:
# a is a class object
#print 'a is class definition'
if type(a) == types.ClassType:
try:
if issubclass(a, tp):
match = True
break
except:
pass
else:
try:
#print 'testing isinstance(a, %s)' % str(tp)
if isinstance(a, tp):
match = True
break
except TypeError:
if isinstance(a, type(tp)):
match = True
break
if match:
return True
else:
a_name, where = get_argname_in_call('right_type', 1)
msg = '%s\n%s is of type %s, while we expected %s' % \
(where, a_name, t, ' or '.join([str(e) for e in expected_types]))
if raise_exception:
raise TypeError(msg)
else:
global message
message = msg
return False
def wrong_type(a, comment=''):
"""
Raise a TypeError exception expressing the type of variable a
and that this type is wrong.
a is any variable of any type, and comment is a text that can be
appended to the exception string.
"""
tp = get_type(a)
a_name, where = get_argname_in_call('wrong_type', 1)
raise TypeError('%s is of wrong type (%s). %s' % (a_name, tp, comment))
_SAFECODE = True
try:
from globaldata import SAFECODE as _SAFECODE
except ImportError:
pass
if not _SAFECODE:
# define (efficient) empty check functions that overrides the
# comprehensive and expensive functions above:
for func in __all__:
efficient_version = 'def %s(*args): return True' % func
exec efficient_version
def _test():
def myfunc(a, b, c):
return a + b + c
A = 1.1
B = [1,2,3]
class myclass:
pass
C = myclass()
print right_type(myfunc, [callable, float, int])
print right_type(A, [float, int])
print right_type(C, [myclass])
print right_type(myclass, [myclass])
print right_type(B, tuple, raise_exception=False)
print message
if __name__ == '__main__':
_test()
|