# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Decorators for argument validation, courtesy of http://rmi.net/~lutz/rangetest.html""" def rangetest(**argchecks): """Validate ranges for both + defaults""" def onDecorator(func): """onCall remembers func and argchecks""" import sys code = func.__code__ if sys.version_info[0] == 3 else func.func_code allargs = code.co_varnames[:code.co_argcount] funcname = func.__name__ def onCall(*pargs, **kargs): # all pargs match first N args by position # the rest must be in kargs or omitted defaults positionals = list(allargs) positionals = positionals[:len(pargs)] for (argname, (low, high)) in argchecks.items(): # for all args to be checked if argname in kargs: # was passed by name if float(kargs[argname]) < low or \ float(kargs[argname]) > high: errmsg = '{0} argument "{1}" not in {2}..{3}' errmsg = errmsg.format(funcname, argname, low, high) raise TypeError(errmsg) elif argname in positionals: # was passed by position position = positionals.index(argname) if float(pargs[position]) < low or \ float(pargs[position]) > high: errmsg = '{0} argument "{1}" with value of {4} ' \ 'not in {2}..{3}' errmsg = errmsg.format(funcname, argname, low, high, pargs[position]) raise TypeError(errmsg) else: pass return func(*pargs, **kargs) # okay: run original call return onCall return onDecorator def typetest(**argchecks): def onDecorator(func): import sys code = func.__code__ if sys.version_info[0] == 3 else func.func_code allargs = code.co_varnames[:code.co_argcount] funcname = func.__name__ def onCall(*pargs, **kargs): positionals = list(allargs)[:len(pargs)] for (argname, typeof) in argchecks.items(): if argname in kargs: if not isinstance(kargs[argname], typeof): errmsg = '{0} argument "{1}" not of type {2}' errmsg = errmsg.format(funcname, argname, typeof) raise TypeError(errmsg) elif argname in positionals: position = positionals.index(argname) if not isinstance(pargs[position], typeof): errmsg = '{0} argument "{1}" with value of {2} ' \ 'not of type {3}' errmsg = errmsg.format(funcname, argname, pargs[position], typeof) raise TypeError(errmsg) else: pass return func(*pargs, **kargs) return onCall return onDecorator