[POS-commit] r1308 - in kiwi/trunk: . kiwi tests

Johan Dahlin jdahlin at async.com.br
Fri Sep 30 11:50:38 BRT 2005


Author: jdahlin
Date: Fri Sep 30 11:50:37 2005
New Revision: 1308

Added:
   kiwi/trunk/kiwi/argcheck.py
   kiwi/trunk/tests/test_argcheck.py
Modified:
   kiwi/trunk/ChangeLog
Log:
    * tests/test_argcheck.py (ArgTest.testUserDefined): Add some basic
    tests.

    * kiwi/argcheck.py: Move in decorator from fiscalprinter.
    Add some additional tests and support for checking default values.



Modified: kiwi/trunk/ChangeLog
==============================================================================
--- kiwi/trunk/ChangeLog	(original)
+++ kiwi/trunk/ChangeLog	Fri Sep 30 11:50:37 2005
@@ -1,3 +1,11 @@
+2005-09-30  Johan Dahlin  <jdahlin at async.com.br>
+
+	* tests/test_argcheck.py (ArgTest.testUserDefined): Add some basic
+	tests.
+
+	* kiwi/argcheck.py: Move in decorator from fiscalprinter.
+	Add some additional tests and support for checking default values.
+
 2005-09-28  Johan Dahlin  <jdahlin at async.com.br>
 
 	* kiwi/ui/widgets/list.py: 

Added: kiwi/trunk/kiwi/argcheck.py
==============================================================================
--- (empty file)
+++ kiwi/trunk/kiwi/argcheck.py	Fri Sep 30 11:50:37 2005
@@ -0,0 +1,131 @@
+#
+# Kiwi: a Framework and Enhanced Widgets for Python
+#
+# Copyright (C) 2005 Async Open Source
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+# 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+# USA
+# 
+# Author(s): Johan Dahlin <jdahlin at async.com.br>
+#
+
+import inspect
+
+class CustomType(type):
+    @classmethod
+    def value_check(cls, name, value):
+        pass
+    
+class number(CustomType):
+    type = int, float
+    
+class percent(CustomType):
+    type = int, float
+    @classmethod
+    def value_check(cls, name, value):
+        if 0 > value < 100:
+            raise ValueError("%s must be between 0 and 100" % name)
+    
+class argcheck(object):
+    __enabled__ = True
+    
+    def __init__(self, *types):
+        for argtype in types:
+            if not isinstance(argtype, type):
+                raise TypeError("must be a type instance")
+        self.types = types
+
+    @classmethod
+    def enable(cls):
+        """
+        Enable argcheck globally
+        """
+        cls.__enabled__ = True
+        
+    @classmethod
+    def disable(cls):
+        """
+        Disable argcheck globally
+        """
+        cls.__enabled__ = False
+        
+    def __call__(self, func):
+        if not callable(func):
+            raise TypeError("%r must be callable" % func)
+        
+        spec = inspect.getargspec(func)
+        arg_names, is_varargs, is_kwargs, default_values = spec
+        if is_kwargs and not is_varargs and self.types:
+            raise TypeError("argcheck cannot be used with only keywords")
+        
+        types = self.types
+        defs = len(default_values or ())
+
+        kwarg_types = {}
+        for i, arg_name in enumerate(arg_names):
+            kwarg_types[arg_name] = types[i]
+            
+            pos = defs - i
+            if defs and pos < defs:
+                value = default_values[pos]
+                arg_type = types[pos]
+                try:
+                    self._type_check(value, arg_type, arg_name)
+                except TypeError:
+                    raise TypeError("default value for %s must be of type %s "
+                                    "and not %s" % (arg_name,
+                                                    arg_type.__name__,
+                                                    type(value).__name__))
+        if not is_varargs:
+            if len(types) != len(arg_names):
+                raise TypeError("%s has wrong number of arguments, "
+                                "%d specified in decorator, "
+                                "but function has %d" %
+                                (func.__name__,
+                                 len(types),
+                                 len(arg_names)))
+        
+        def wrapper(*args, **kwargs):
+            if self.__enabled__:
+                # Positional arguments
+                for arg, type, name in zip(args, types, arg_names):
+                    self._type_check(arg, type, name)
+
+                # Keyword arguments
+                for name, arg in kwargs.items():
+                    self._type_check(arg, kwarg_types[name], name)
+                
+            return func(*args, **kwargs)
+        wrapper.__name__ = func.__name__
+        return wrapper
+
+    def _type_check(self, value, argument_type, name):
+        if issubclass(argument_type, CustomType):
+            custom = True
+            check_type = argument_type.type
+        else:
+            custom = False
+            check_type = argument_type
+            
+        type_name = argument_type.__name__
+
+        if not isinstance(value, check_type):
+            raise TypeError(
+                "%s must be %s, not %s" % (name, type_name,
+                                           type(value).__name__))
+        if custom:
+            argument_type.value_check(name, value)
+
+

Added: kiwi/trunk/tests/test_argcheck.py
==============================================================================
--- (empty file)
+++ kiwi/trunk/tests/test_argcheck.py	Fri Sep 30 11:50:37 2005
@@ -0,0 +1,51 @@
+import unittest
+
+from kiwi.argcheck import argcheck, number, percent
+
+class ArgTest(unittest.TestCase):
+    def testOneArg(self):
+        f = argcheck(str)(lambda s: None)
+        f('str')
+        self.assertRaises(TypeError, f, None)
+        self.assertRaises(TypeError, f, 1)
+        
+    def testTwoArgs(self):
+        f = argcheck(str, int)(lambda s, i: None)
+        f('str', 1)
+        self.assertRaises(TypeError, f, 1, 1)         # first incorret
+        self.assertRaises(TypeError, f, 'str', 'str') # second incorrect
+        self.assertRaises(TypeError, f, 1, 'str')     # swapped
+        self.assertRaises(TypeError, f, 1)            # too few
+        self.assertRaises(TypeError, f, 'str', 1, 1)  # too many
+
+    def testVarArgs(self):
+        f = argcheck(int)(lambda *v: None)
+        f(1)
+        f(1, 'str')
+        f(1, 2, 3)
+        #self.assertRaises(TypeError, f, 'str')
+
+    def testDefault(self):
+        f1 = lambda a, b=1: None
+        d1 = argcheck(int, int)(f1)
+        self.assertRaises(TypeError, d1, 'f')
+
+        f2 = lambda a, b='str': None
+        self.assertRaises(TypeError, argcheck, f2)
+        
+    def testKwargs(self):
+        self.assertRaises(TypeError, argcheck, lambda **kw: None)
+
+    def testUserDefined(self):
+        class Payment(object):
+            pass
+    
+        @argcheck(Payment, str)
+        def pay(payment, description):
+            pass
+        pay(Payment(), 'foo')
+        self.assertRaises(TypeError, 'bar', 'bar')
+        self.assertRaises(TypeError, Payment(), Payment())
+        
+if __name__ == '__main__':
+    unittest.main()


More information about the POS-commit mailing list