reconstructing validate decorator devel
authorSandro Knauß <knauss@netzguerilla.net>
Fri, 27 Jan 2012 15:01:59 +0100
branchdevel
changeset 114 1ed072cc6793
parent 113 abdece5f6be6
child 115 323d06431100
reconstructing validate decorator
iro/controller/viewinterface.py
iro/model/user.py
iro/model/utils.py
iro/validate.py
tests/xmlrpc.py
--- a/iro/controller/viewinterface.py	Thu Jan 26 01:23:04 2012 +0100
+++ b/iro/controller/viewinterface.py	Fri Jan 27 15:01:59 2012 +0100
@@ -1,10 +1,12 @@
 # -*- coding: utf-8 -*-
 from ..model.user import with_user
-
+from ..validate import validate, boolean, validateHash
 class Interface(object):
     '''class for a xmlrpc user
     '''
     
+    @validate(kwd="detailed", func=boolean, need=False)
+    @validate(kwd="id", func=validateHash, need=False)
     @with_user
     def status(self, user, id=None, detailed=False):
         '''Gibt den aktuellen Status eines Auftrages oder Mehreren zurück.
@@ -21,7 +23,10 @@
 
 
         '''
-        return str(user)
+        if id or detailed:
+            return str(user),id,detailed
+        else:
+            return str(user)
         #return user.status(id,detailed)
         return ""
 
--- a/iro/model/user.py	Thu Jan 26 01:23:04 2012 +0100
+++ b/iro/model/user.py	Fri Jan 27 15:01:59 2012 +0100
@@ -1,9 +1,11 @@
 from inspect import getcallargs
+from functools import wraps
 
 from .schema import User
 from .utils import DBDefer
 from ..validate import vuserhash, validate
 from ..error import UserNotFound, InterfaceException
+
 dbdefer=DBDefer(None)
 
 def setEngine(engine,autocommit=False): 
@@ -11,7 +13,7 @@
     dbdefer.autocommit = autocommit
 
 @dbdefer
-@validate(apikey=vuserhash)
+@validate(kwd="apikey", func=vuserhash)
 def getuser(apikey, session):
     user = session.query(User).filter_by(apikey=apikey).first()
     if user is None:
@@ -20,8 +22,9 @@
         return user
 
 def with_user(f):
-    def new_f(*args,**kargs):
-        kp=getcallargs(f,*args,**kargs)
+    @wraps(f)
+    def wrapper(*args,**kargs):
+        kp=getcallargs(wrapper.original,*args,**kargs)
         try:
             apikey = kp["user"]
         except KeyError:
@@ -33,8 +36,10 @@
 
         d=getuser(apikey=apikey)
         return d.addCallback(_gotResult)
-
-    new_f.__name__ = f.__name__
-    return new_f
+    try:
+        wrapper.original=f.original
+    except AttributeError:
+        wrapper.original=f
+    return wrapper
 
 
--- a/iro/model/utils.py	Thu Jan 26 01:23:04 2012 +0100
+++ b/iro/model/utils.py	Fri Jan 27 15:01:59 2012 +0100
@@ -3,6 +3,8 @@
 from twisted.internet import threads
 from twisted.python.threadpool import ThreadPool
 
+from functools import wraps
+
 POOL_SIZE=5     #how many threads should the db connector pool should have
 
 class Data:
@@ -19,6 +21,7 @@
 
 def run_in_db_thread(f):
     """Decorator to run DB queries in Twisted's thread pool"""
+    @wraps(f)
     def wrapper(*args, **kwargs):
         return threads.deferToThreadPool(d.reactor, d.pool, f, *args, **kwargs)
     return wrapper
@@ -50,6 +53,7 @@
 
     def __call__(self, func):
         @run_in_db_thread
+        @wraps(func)
         def wrapper(*args, **kwargs):
             with WithSession(self.engine, self.autocommit) as session:
                 return func(*args, session=session, **kwargs)
--- a/iro/validate.py	Thu Jan 26 01:23:04 2012 +0100
+++ b/iro/validate.py	Fri Jan 27 15:01:59 2012 +0100
@@ -1,27 +1,57 @@
 import re
+from functools import wraps
+
 from inspect import getcallargs
 from .error import ValidateException
 
-def vuserhash(hash,field):
-    '''vailidate function for userhash'''
-    if not re.match(r'^[a-f0-9]{15,}$', hash.lower()):
+def boolean(value, field):
+    t=[True,"true",1]
+    f=[False,"false",0]
+    if value in t:
+        return True
+    elif value in f:
+        return False
+    else:
+        raise ValidateException(field=field, msg='%s is not boolean' % field)
+
+
+def validateHash(value,field,minlength=None,maxlength=None):
+    if not re.match(r'^[a-f0-9]*$', value.lower()):
         raise ValidateException(field=field)
-    return True
+    if minlength and len(value)<minlength:
+        raise ValidateException(field=field)
+    if maxlength and len(value)>maxlength:
+        raise ValidateException(field=field)
+    return value
 
-def validate(**kargs):
+def vuserhash(value,field):
+    '''vailidate function for userhash'''
+    return validateHash(value,field,minlength=15,maxlength=15)
+
+def validate(kwd,func, need=True,*args,**kargs):
     '''validate decorator
     use it like this:
-        @validate(userhash=vuserhash)
+        @validate(kwd=userhash, func=vuserhash)
         f(userhash)
-    that will validate usrhah with the function vuserhash.
+    that will validate usrhash with the function vuserhash.
     Every validate function should raise an Exception, if the the value is not valid'''
     def v(f):
+        @wraps(f)
         def new_f(*a,**k):
-            kp=getcallargs(f,*a,**k)
-            for i in kargs:
-                kargs[i](kp[i],i)
-            return f(*a,**k)
-        new_f.__name__ = f.__name__
+            kp=getcallargs(new_f.original,*a,**k)
+            try:
+                if need or kp[kwd] is not None:
+                    kp[kwd] = func(kp[kwd],kwd,*args,**kargs)
+                else:
+                    kp[kwd] = None
+            except KeyError:
+                if need:
+                    raise ValidateException(field=kwd,msg="%s is nessasary"%kwd)
+            return f(**kp)
+        try:
+            new_f.original=f.original
+        except AttributeError:
+            new_f.original=f
         return new_f
     return v
  
--- a/tests/xmlrpc.py	Thu Jan 26 01:23:04 2012 +0100
+++ b/tests/xmlrpc.py	Fri Jan 27 15:01:59 2012 +0100
@@ -52,7 +52,7 @@
 
     def __rpc2(self):
         return ServerProxy('http://localhost:7080/RPC2')
-
+    
     def testDebugHello(self):
         '''simple test for the connection to xmlrpc server'''
         ret=self.__debug().hello()
@@ -66,6 +66,13 @@
     def testStatus(self):
         ret = self.__rpc2().status('abcdef123456789')
         self.failUnlessEqual(ret, "<User('test','abcdef123456789')>")
+        self.failUnlessEqual(self.__rpc2().status('abcdef123456789','abcde'), ["<User('test','abcdef123456789')>",'abcde',False])
+        self.failUnlessEqual(self.__rpc2().status('abcdef123456789','abcde', True), ["<User('test','abcdef123456789')>",'abcde', True])
+        self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 'true'), ["<User('test','abcdef123456789')>", '', True])
+        self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 'false'), "<User('test','abcdef123456789')>")
+        self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 0), "<User('test','abcdef123456789')>")
+        self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 1), ["<User('test','abcdef123456789')>", '', True])
+
 
     def testNoSuchUser(self):
         '''a unknown user should raise a UserNotNound Exception
@@ -85,7 +92,7 @@
         self.failUnlessEqual(exc.faultCode, 8001)
         self.failUnlessEqual(exc.faultString, "procedure nosuchmethod not found")
     
-    def testValidation(self):
+    def testValidationFault(self):
         '''a validate Exception should be translated to a xmlrpclib.Fault.'''
         with self.assertRaises(Fault) as fault:
             self.__rpc2().status('xxx')
@@ -93,8 +100,6 @@
         self.failUnlessEqual(exc.faultCode, 700)
         self.failUnlessEqual(exc.faultString, "Validation of 'apikey' failed.")
 
-
-
 def startReactor(engine):
     """starts the Rector with a special debug Clild, so that the reactor can be stopped remotly. """
     from twisted.internet import reactor