/usr/share/pyshared/pytils/third/aspn426123.py is in python-pytils 0.2.3-2.
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 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | #!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
################################################################################
#
# Method call parameters/return value type checking decorators.
# (c) 2006-2007, Dmitry Dvoinikov <dmitry@targeted.org>
# Distributed under BSD license.
#
# Samples:
#
# from typecheck import *
#
# @takes(int, str) # takes int, str, upon a problem throws InputParameterError
# @returns(int) # returns int, upon a problem throws ReturnValueError
# def foo(i, s):
# return i + len(s)
#
# @takes((int, long), by_regex("^[0-9]+$")) # int or long, numerical string
# def foo(i, s, anything): # and the third parameter is not checked
# ...
#
# @takes(int, int, foo = int, bar = optional(int)) # keyword argument foo must be int
# def foo(a, b, **kwargs): # bar may be int or missing
# ...
#
# Note: @takes for positional arguments, @takes for keyword arguments and @returns
# all support the same checker syntax, for example for the following declaration
#
# @takes(C)
# def foo(x):
# ...
#
# then C may be one of the simple checkers:
#
# --------- C --------- ------------- semantics -------------
# typename ==> ok if x is is an instance of typename
# "typename" ==> ok if x is is an instance of typename
# with_attr("a", "b") ==> ok if x has specific attributes
# some_callable ==> ok if some_callable(x) is True
# one_of(1, "2") ==> ok if x is one of the literal values
# by_regex("^foo$") ==> ok if x is a matching basestring
# nothing ==> ok if x is None
# anything ==> always ok
#
# simple checkers can further be combined with OR semantics using tuples:
#
# --------- C --------- ------------- semantics -------------
# (checker1, checker2) ==> ok if x conforms with either checker
#
# be optional:
#
# --------- C --------- ------------- semantics -------------
# optional(checker) ==> ok if x is checker-conformant or None
#
# or nested recursively into one of the following checkers
#
# --------- C --------- ------------- semantics -------------
# list_of(checker) ==> ok if x is a list of checker-conformant values
# tuple_of(checker) ==> ok if x is a tuple of checker-conformant values
# dict_of(key_checker, value_checker) ==> ok if x is a dict mapping key_checker-
# conformant keys to value_checker-conformant values
#
# More samples:
#
# class foo(object):
# @takes("foo", optional(int)) # foo, maybe int, but foo is yet incomplete
# def __init__(self, i = None): # and is thus specified by name
# ...
# @takes("foo", int) # foo, and int if presents in args,
# def bar(self, *args): # if args is empty, the check passes ok
# ...
# @takes("foo")
# @returns(object) # returns foo which is fine, because
# def biz(self): # foo is an object
# return self
# @classmethod # classmethod's and staticmethod's
# @takes(type) # go same way
# def baz(cls):
# ...
#
# @takes(int)
# @returns(optional("int", foo)) # returns either int, foo or NoneType
# def bar(i): # "int" (rather than just int) is for fun
# if i > 0:
# return i
# elif i == 0:
# return foo() # otherwise returns NoneType
#
# @takes(callable) # built-in functions are treated as predicates
# @returns(lambda x: x == 123) # and so do user-defined functions or lambdas
# def execute(f, *args, **kwargs):
# return f(*args, **kwargs)
#
# assert execute(execute, execute, execute, lambda x: x, 123) == 123
#
# def readable(x): # user-defined type-checking predicate
# return hasattr(x, "read")
#
# anything is an alias for predicate lambda: True,
# nothing is an alias for NoneType, as in:
#
# @takes(callable, readable, optional(anything), optional(int))
# @returns(nothing)
# def foo(f, r, x = None, i = None):
# ...
#
# @takes(with_attr("read", "write")) # another way of protocol checking
# def foo(pipe):
# ...
#
# @takes(list_of(int)) # list of ints
# def foo(x):
# print x[0]
#
# @takes(tuple_of(callable)) # tuple of callables
# def foo(x):
# print x[0]()
#
# @takes(dict_of(str, list_of(int))) # dict mapping strs to lists of int
# def foo(x):
# print sum(x["foo"])
#
# @takes(by_regex("^[0-9]{1,8}$")) # integer-as-a-string regex
# def foo(x):
# i = int(x)
#
# @takes(one_of(1, 2)) # must be equal to either one
# def set_version(version):
# ...
#
# The (3 times longer) source code with self-tests is available from:
# http://www.targeted.org/python/recipes/typecheck.py
#
################################################################################
__all__ = [ "takes", "InputParameterError", "returns", "ReturnValueError",
"optional", "nothing", "anything", "list_of", "tuple_of", "dict_of",
"by_regex", "with_attr", "one_of" ]
no_check = False # set this to True to turn all checks off
################################################################################
from inspect import getargspec, isfunction, isbuiltin, isclass
from types import NoneType
from re import compile as regex
################################################################################
def base_names(C):
"Returns list of base class names for a given class"
return [ x.__name__ for x in C.__mro__ ]
################################################################################
def type_name(v):
"Returns the name of the passed value's type"
return type(v).__name__
################################################################################
class Checker(object):
def __init__(self, reference):
self.reference = reference
def check(self, value): # abstract
pass
_registered = [] # a list of registered descendant class factories
@staticmethod
def create(value): # static factory method
for f, t in Checker._registered:
if f(value):
return t(value)
else:
return None
################################################################################
class TypeChecker(Checker):
def check(self, value):
return isinstance(value, self.reference)
Checker._registered.append((isclass, TypeChecker))
nothing = NoneType
################################################################################
class StrChecker(Checker):
def check(self, value):
value_base_names = base_names(type(value))
return self.reference in value_base_names or "instance" in value_base_names
Checker._registered.append((lambda x: isinstance(x, str), StrChecker))
################################################################################
class TupleChecker(Checker):
def __init__(self, reference):
self.reference = map(Checker.create, reference)
def check(self, value):
return reduce(lambda r, c: r or c.check(value), self.reference, False)
Checker._registered.append((lambda x: isinstance(x, tuple) and not
filter(lambda y: Checker.create(y) is None,
x),
TupleChecker))
optional = lambda *args: args + (NoneType, )
################################################################################
class FunctionChecker(Checker):
def check(self, value):
return self.reference(value)
Checker._registered.append((lambda x: isfunction(x) or isbuiltin(x),
FunctionChecker))
anything = lambda *args: True
################################################################################
class ListOfChecker(Checker):
def __init__(self, reference):
self.reference = Checker.create(reference)
def check(self, value):
return isinstance(value, list) and \
not filter(lambda e: not self.reference.check(e), value)
list_of = lambda *args: lambda value: ListOfChecker(*args).check(value)
################################################################################
class TupleOfChecker(Checker):
def __init__(self, reference):
self.reference = Checker.create(reference)
def check(self, value):
return isinstance(value, tuple) and \
not filter(lambda e: not self.reference.check(e), value)
tuple_of = lambda *args: lambda value: TupleOfChecker(*args).check(value)
################################################################################
class DictOfChecker(Checker):
def __init__(self, key_reference, value_reference):
self.key_reference = Checker.create(key_reference)
self.value_reference = Checker.create(value_reference)
def check(self, value):
return isinstance(value, dict) and \
not filter(lambda e: not self.key_reference.check(e), value.iterkeys()) and \
not filter(lambda e: not self.value_reference.check(e), value.itervalues())
dict_of = lambda *args: lambda value: DictOfChecker(*args).check(value)
################################################################################
class RegexChecker(Checker):
def __init__(self, reference):
self.reference = regex(reference)
def check(self, value):
return isinstance(value, basestring) and self.reference.match(value)
by_regex = lambda *args: lambda value: RegexChecker(*args).check(value)
################################################################################
class AttrChecker(Checker):
def __init__(self, *attrs):
self.attrs = attrs
def check(self, value):
return reduce(lambda r, c: r and c, map(lambda a: hasattr(value, a), self.attrs), True)
with_attr = lambda *args: lambda value: AttrChecker(*args).check(value)
################################################################################
class OneOfChecker(Checker):
def __init__(self, *values):
self.values = values
def check(self, value):
return value in self.values
one_of = lambda *args: lambda value: OneOfChecker(*args).check(value)
################################################################################
def takes(*args, **kwargs):
"Method signature checking decorator"
# convert decorator arguments into a list of checkers
checkers = []
for i, arg in enumerate(args):
checker = Checker.create(arg)
if checker is None:
raise TypeError("@takes decorator got parameter %d of unsupported "
"type %s" % (i + 1, type_name(arg)))
checkers.append(checker)
kwcheckers = {}
for kwname, kwarg in kwargs.iteritems():
checker = Checker.create(kwarg)
if checker is None:
raise TypeError("@takes decorator got parameter %s of unsupported "
"type %s" % (kwname, type_name(kwarg)))
kwcheckers[kwname] = checker
if no_check: # no type checking is performed, return decorated method itself
def takes_proxy(method):
return method
else:
def takes_proxy(method):
method_args, method_defaults = getargspec(method)[0::3]
def takes_invocation_proxy(*args, **kwargs):
## do not append the default parameters // 'DONT' by Pythy
# if method_defaults is not None and len(method_defaults) > 0 \
# and len(method_args) - len(method_defaults) <= len(args) < len(method_args):
# args += method_defaults[len(args) - len(method_args):]
# check the types of the actual call parameters
for i, (arg, checker) in enumerate(zip(args, checkers)):
if not checker.check(arg):
raise InputParameterError("%s() got invalid parameter "
"%d of type %s" %
(method.__name__, i + 1,
type_name(arg)))
for kwname, checker in kwcheckers.iteritems():
if not checker.check(kwargs.get(kwname, None)):
raise InputParameterError("%s() got invalid parameter "
"%s of type %s" %
(method.__name__, kwname,
type_name(kwargs.get(kwname, None))))
return method(*args, **kwargs)
takes_invocation_proxy.__name__ = method.__name__
return takes_invocation_proxy
return takes_proxy
class InputParameterError(TypeError): pass
################################################################################
def returns(sometype):
"Return type checking decorator"
# convert decorator argument into a checker
checker = Checker.create(sometype)
if checker is None:
raise TypeError("@returns decorator got parameter of unsupported "
"type %s" % type_name(sometype))
if no_check: # no type checking is performed, return decorated method itself
def returns_proxy(method):
return method
else:
def returns_proxy(method):
def returns_invocation_proxy(*args, **kwargs):
result = method(*args, **kwargs)
if not checker.check(result):
raise ReturnValueError("%s() has returned an invalid "
"value of type %s" %
(method.__name__, type_name(result)))
return result
returns_invocation_proxy.__name__ = method.__name__
return returns_invocation_proxy
return returns_proxy
class ReturnValueError(TypeError): pass
################################################################################
# EOF
|