I have said that if I have time, I will sort out the source code of a modifier used to check the parameter type and put it on .. It took a while to complete the process. Because the code in the company was not convenient to come out, this module was completely knocked out. Despite some simple tests, I could not ensure that 100% was okay. The purpose is to share the idea of using the decorator :)
Python is a dynamic language without static checks. Sometimes you need to ensure that the parameters are valid when calling a function. Checking the validity of parameters is a significant aspect scenario, and each function can have this requirement. On the other hand, whether the validity of a parameter should be guaranteed by the caller is also a question that must be answered based on actual conditions. In short, both parties agree that it is okay not to check it or check it all. The following module is used in the function to verify the validity of parameters using the decorator.
You can directly execute this module for testing. If there is no output, it means it passes. You can also find several functions starting with _ test. All test cases are included in these functions. For usage, see the module documentation and test cases.
#-*-Coding: UTF-8-*-''' @ Summary: validators this module provides a modifier to verify that the parameter is valid by using: From validator import validparam, nullok, Multitype @ validparam (I = int) def Foo (I): return I + 1 Write validators: 1. verification type only: @ validparam (type ,...) for example, check whether the parameter at the first position is of the int type: @ validparam (INT) check whether the parameter named X is of the int type: @ validparam (x = int) to verify multiple parameters: @ validparam (INT, INT) specifies parameter name verification: @ validparam (INT, S = Str) the validators for * And ** parameters verify each element actually contained in these parameters: @ validparam (varargs = int) def Foo (* varargs ): Pass @ validparam (KWS = int) def foo7 (S, ** KWS): pass2. verification with conditions: @ validparam (type, condition ),...) condition is an expression string that uses X to reference the object to be verified. It determines whether the object has passed Verification Based on bool (expression value). If an exception is thrown during expression calculation, it is considered as a failure. For example, verify an integer between 10 and 20: @ validparam (I = (INT, '10 <x <20 ') to verify a string smaller than 20: @ validparam (S = (STR, 'len (x) <20'): @ validparam (STU = (student, 'x. age <20') in addition, if the type is string, condition can also use the start and end of a slash to indicate Regular Expression matching. Verify a string consisting of digits: @ validparam (S = (STR, '/^ \ D * $/') 3. the verification fails if the value is none by default. If none is a valid parameter, you can use nullok (). Nullok () accepts a verification condition as a parameter. For example, @ validparam (I = nullok (INT) @ validparam (I = nullok (INT, '10 <x <20') can also be abbreviated: @ validparam (I = nullok (INT, '10 <x <20') 4. if the parameter has multiple valid types, you can use Multitype (). Multitype () can accept multiple parameters. Each parameter is a verification condition. For example: @ validparam (S = Multitype (INT, STR) @ validparam (S = Multitype (INT, 'x> 20'), nullok (STR, '/^ \ D + $/') 5. if you have more complex verification requirements, you can write a function as the verification function to pass in. This function receives the object to be verified as a parameter and determines whether it passes Verification Based on the bool (return value). If an exception is thrown, it is regarded as a failure. For example, Def validfunction (x): Return isinstance (x, INT) and X> 0 @ validparam (I = validfunction) def Foo (I): Pass is equivalent: @ validparam (I = (INT, 'x> 0') def Foo (I): Pass @ Author: Huxi @ since: 2011-3-22 @ change: '''import inspectimport reclass validateexception (exception): passdef validparam (* varargs, ** keywords): ''' the modifier that verifies the parameter. '''Varargs = map (_ tostardardcondition, varargs) KEYWORDS = dict (K, _ tostardardcondition (keywords [k]) for K in keywords) def generator (func ): ARGs, varargname, kwname = inspect. getargspec (func) [: 3] dctvalidator = _ getcallargs (ARGs, varargname, kwname, varargs, keywords) def wrapper (* callvarargs, ** callkeywords ): dctcallargs = _ getcallargs (ARGs, varargname, kwname, callvarargs, callkeywords) K, item = None, none try: For k in dctvalidator: IF k = varargname: for item in dctcallargs [k]: assert dctvalidator [k] (item) Elif K = kwname: For item in dctcallargs [K]. values (): assert dctvalidator [k] (item) else: item = dctcallargs [k] assert dctvalidator [k] (item) Except T: Raise validateexception, \ ('% s () parameter validation fails, Param: % s, value: % s (% s) '% (func. func_name, K, item, item. _ class __. _ name _) return func (* callvarargs, ** callkeywords) wrapper = _ wrapps (wrapper, func) return wrapper return generatordef _ tostardardcondition (condition ): ''' converts the checking conditions in various formats to the checking function ''' if inspect. isclass (condition): Return Lambda X: isinstance (x, condition) If isinstance (condition, (tuple, list): CLs, condition = condition [: 2] If condition is none: Return _ tostardardcondition (CLS) If CLS in (STR, Unicode) and condition [0] = condition [-1] = '/': return Lambda X: (isinstance (x, CLS) and RE. match (condition [1:-1], x) is not none) return Lambda X: isinstance (x, CLS) and eval (condition) return conditiondef nullok (CLS, condition = none): ''' the check condition specified by this function can accept the value of none '''return Lambda X: X is none or _ tostardardcondition (CLS, condition )) (x) def Multitype (* conditions): ''' only one check condition specified by this function needs to pass '''lstvalidator = map (_ tostardardcondition, conditions) def validate (x): For V in lstvalidator: If V (x): Return true return validatedef _ getcallargs (ARGs, varargname, kwname, varargs, keywords ): '''dctargs ={} varargs = tuple (varargs) KEYWORDS = dict (keywords) argcount = Len (ARGs) varcount = Len (varargs) callvarargs = none if argcount <= varcount: for N, argname in enumerate (ARGs ): dctargs [argname] = varargs [N] callvarargs = varargs [-(varcount-argcount):] else: for N, VAR in enumerate (varargs ): dctargs [ARGs [N] = var for argname in ARGs [-(argcount-varcount):]: If argname in keywords: dctargs [argname] = keywords. pop (argname) callvarargs = () If varargname is not none: dctargs [varargname] = callvarargs if kwname is not none: dctargs [kwname] = keywords dctargs. update (keywords) return dctargsdef _ wrapps (wrapper, wrapped): '''copy metadata ''' for ATTR in ('_ module __', '_ name _', '_ Doc _'): setattr (wrapper, ATTR, getattr (wrapped, ATTR )) for ATTR in ('_ dict _',): getattr (wrapper, ATTR ). update (getattr (wrapped, ATTR ,{})) return wrapper #=============================================== ========================================================== ==# test #=============================================== ========================================================== ==== def _ unittest (func, * cases): For case in cases: _ functest (func, * case) def _ functest (func, isckpass, * ARGs, ** KWS): If isckpass: func (* ARGs, ** KWS) else: Try: func (* ARGs, ** KWS) assert false handle T validateexception: passdef _ test1_simple (): # Check whether the parameter at the first position is of the int type: @ validparam (INT) def foo1 (I): Pass _ unittest (foo1, (true, 1), (false, 'S '), (false, none) # Check whether the parameter named X is of the int type: @ validparam (x = int) def foo2 (S, X ): pass _ unittest (foo2, (true, 1, 2), (false, 's', 's') # verify multiple parameters: @ validparam (INT, INT) def foo3 (S, X): Pass _ unittest (foo3, (true, 1, 2), (false, 's', 2) # specify the parameter name for verification: @ validparam (INT, S = Str) def foo4 (I, S): Pass _ unittest (foo4, (true, 1, 'A'), (false,'s ', 1) # The validators for * And ** parameters will verify each element of these parameters: @ validparam (varargs = int) def foo5 (* varargs ): pass _ unittest (foo5, (true, 1, 2, 3, 4, 5), (false, 'A', 1) @ validparam (KWS = int) def foo6 (** KWS): Pass _ functest (foo6, true, A = 1, B = 2) _ functest (foo6, false, A = 'A ', B = 2) @ validparam (KWS = int) def foo7 (S, ** KWS): Pass _ functest (foo7, true, S = 'A', A = 1, B = 2) def _ test2_condition (): # verify an integer between 10 and 20: @ validparam (I = (INT, '10 <x <20 ')) def foo1 (X, I): Pass _ unittest (foo1, (true, 1, 11), (false, 1, 'A'), (false, 1, 1 )) # verify a string with a length less than 20: @ validparam (S = (STR, 'len (x) <20') def foo2 (A, S ): pass _ unittest (foo2, (true, 1, 'A'), (false, 1, 1), (false, 1, 'A' * 20 )) # verify a student younger than 20: class student (object): def _ init _ (self, age): Self. age = age @ validparam (STU = (student, 'x. age <20') def foo3 (Stu): Pass _ unittest (foo3, (true, student (18), (false, 1), (false, student (20) # verify a string consisting of digits: @ validparam (S = (STR, R'/^ \ D * $/') def foo4 (s): Pass _ unittest (foo4, (true, '1234'), (false, 1), (false, 'a1234') def _ test3_nullok (): @ validparam (I = nullok (INT) def foo1 (I): Pass _ unittest (foo1, (true, 1), (false, 'A'), (true, none) @ validparam (I = nullok (INT, '10 <x <20') def foo2 (I): Pass _ unittest (foo2, (true, 11 ), (false, 'A'), (true, none), (false, 1) def _ test4_multitype (): @ validparam (S = Multitype (INT, STR )) def foo1 (s): Pass _ unittest (foo1, (true, 1), (true, 'A'), (false, none), (false, 1.1 )) @ validparam (S = Multitype (INT, 'x> 20'), nullok (STR, '/^ \ D + $/') def foo2 (s ): pass _ unittest (foo2, (false, 1), (false, 'A'), (true, none), (false, 1.1), (true, 21 ), (true, '21') def _ main (): D = globals () from types import functiontype print for F in D: If F. startswith ('_ test'): F = d [f] If isinstance (F, functiontype): F () if _ name _ = '_ main _': _ main ()