[Python-checkins] r53940 - sandbox/trunk/pep362/pep362.py sandbox/trunk/pep362/test_pep362.py

brett.cannon python-checkins at python.org
Mon Feb 26 16:29:05 CET 2007


Author: brett.cannon
Date: Mon Feb 26 16:29:03 2007
New Revision: 53940

Modified:
   sandbox/trunk/pep362/pep362.py
   sandbox/trunk/pep362/test_pep362.py
Log:
Expand worst-case tests to thoroughly check every parameter.  Also fix a
Signature.bind bug that was subsequently found.

This means the code finally works (both in 2.6 and 3.0).


Modified: sandbox/trunk/pep362/pep362.py
==============================================================================
--- sandbox/trunk/pep362/pep362.py	(original)
+++ sandbox/trunk/pep362/pep362.py	Mon Feb 26 16:29:03 2007
@@ -52,7 +52,23 @@
 
 class Signature(object):
 
-    """Object to represent the signature of a function/method."""
+    """Object to represent the signature of a function/method.
+
+    Attributes:
+    * parameters
+        Sequence of Parameter objects.
+    * name
+        Name of the function/method.
+    * var_args
+        Name of the variable positional parameter, else ''.
+    * var_kw_wargs
+        Name of the variable keywrod parameter, else ''.
+    * var_annotations
+        Dict keyed on the variable parameter names with the values of the
+        annotation for the parameter.  If an annotation does not exist for a
+        parameter, the key does not exist.
+
+    """
 
     def __init__(self, func):
         """Initialize from a function or method object."""
@@ -62,7 +78,7 @@
 
         self.name = func.__name__
 
-        argspec = getargspec(func)
+        argspec = getargspec(func)  # Needed only for tuple parameters.
         parameters = []
 
         # Parameter information.
@@ -71,24 +87,25 @@
             keyword_only_count = func_code.co_kwonlyargcount
         else:
             keyword_only_count = 0
-        #positional = func_code.co_varnames[:pos_count]
         positional = argspec[0]
-        keyword_only = func_code.co_varnames[pos_count:keyword_only_count]
+        keyword_only = func_code.co_varnames[pos_count:
+                                                pos_count+keyword_only_count]
         if func.func_defaults:
             pos_default_count = len(func.func_defaults)
         else:
             pos_default_count = 0
 
-        # XXX Use inspect where tuple parameters are possible.
         # Non-keyword-only parameters w/o defaults.
         non_default_count = pos_count - pos_default_count
         for index, name in enumerate(positional[:non_default_count]):
+            name = self._convert_name(name)
             has_annotation, annotation = self._find_annotation(func, name)
             param = Parameter(name, index, has_default=False,
                     has_annotation=has_annotation, annotation=annotation)
             parameters.append(param)
         # ... w/ defaults.
         for offset, name in enumerate(positional[non_default_count:]):
+            name = self._convert_name(name)
             has_annotation, annotation = self._find_annotation(func, name)
             default_value = func.func_defaults[offset]
             param = Parameter(name, offset+non_default_count,
@@ -152,11 +169,11 @@
                 annotation = func.func_annotations[name]
         return has_annotation, annotation
 
-    def _list2tuple(self, list_):
-        if not isinstance(list_, list):
-            return list_
+    def _convert_name(self, name):
+        if not isinstance(name, list):
+            return name
         else:
-            return tuple(cls._list2tuple(x) for x in list_)
+            return tuple(self._convert_name(x) for x in name)
 
     def bind(self, *args, **kwargs):
         """Return a dictionary mapping function arguments to their parameter
@@ -184,17 +201,18 @@
         if not self.parameters and args and self.var_args:
             bindings[self.var_args] = args
             args = tuple()
-        for index, position_arg in enumerate(args):
+        for index, position_arg in enumerate(args[:]):
             try:
                 param = positional.pop(0)
             except IndexError:
                 # *args.
                 if self.var_args:
-                    bindings[self.var_args] = tuple(args[index-1:])
+                    bindings[self.var_args] = tuple(args)
                     break
                 else:
                     raise BindError("too many positional arguments")
             self._tuple_bind(bindings, param.name, position_arg)
+            args = args[1:]
         # Keyword arguments & default values.
         else:
             for positional_param in positional:

Modified: sandbox/trunk/pep362/test_pep362.py
==============================================================================
--- sandbox/trunk/pep362/test_pep362.py	(original)
+++ sandbox/trunk/pep362/test_pep362.py	Mon Feb 26 16:29:03 2007
@@ -44,6 +44,7 @@
         self.failUnlessEqual(param.default_value, default_value)
         param = pep362.Parameter('_', 0, False)
         self.failUnlessEqual(param.has_default, False)
+        self.failUnless(not hasattr(param, 'default_value'))
 
     def test_keyword_only(self):
         # Setting the value for keyword_only should create an attribute.
@@ -110,7 +111,7 @@
         self.failUnless(param.has_default)
         self.failUnlessEqual(42, param.default_value)
 
-    def XXX_test_parameter_tuple(self):
+    def test_parameter_tuple(self):
         # A function with a tuple as a parameter should work.
         sig = pep362.Signature(pep362_fodder.tuple_args)
         self.failUnlessEqual('tuple_args', sig.name)
@@ -121,7 +122,7 @@
         self.failUnless(not param.has_default)
         self.failUnless(not hasattr(param, 'default_value'))
 
-    def XXX_test_parameter_tuple_default(self):
+    def test_parameter_tuple_default(self):
         # A default argument for a tuple parameter needs to work.
         sig = pep362.Signature(pep362_fodder.default_tuple_args)
         self.failUnlessEqual('default_tuple_args', sig.name)
@@ -265,7 +266,7 @@
         self.failUnlessRaises(pep362.BindError, sig.bind, a=0, b=1)
         self.failUnlessRaises(pep362.BindError, sig.bind, b=1)
 
-    def XXX_test_tuple_parameter(self):
+    def test_tuple_parameter(self):
         sig = pep362.Signature(pep362_fodder.tuple_args)
         arg = (1, ((2,),))
         binding = sig.bind(arg)
@@ -273,7 +274,7 @@
         self.failUnlessRaises(pep362.BindError, sig.bind, (1,2,3))
         self.failUnlessRaises(pep362.BindError, sig.bind, (1, 2))
 
-    def XXX_test_default_tuple_parameter(self):
+    def test_default_tuple_parameter(self):
         sig = pep362.Signature(pep362_fodder.default_tuple_args)
         binding = sig.bind()
         self.failUnlessEqual({'a':1, 'b':2}, binding)
@@ -281,8 +282,31 @@
         binding = sig.bind(arg)
         self.failUnlessEqual({'a':0, 'b':1}, binding)
 
-    def XXX_test_all_parameter_types(self):
+    def test_all_args(self):
         sig = pep362.Signature(pep362_fodder.all_args)
+        # a, (b, (c,)), d=0, (e, (f,))=(4, (5,)), *g, **h
+        # name, position, has_default, default value
+        expect = (('a', 0, False, None),
+                    (('b', ('c',)), 1, False, None),
+                    ('d', 2, True, 0),
+                    (('e', ('f',)), 3, True, (4, (5,))))
+        self.failUnlessEqual(len(sig.parameters), len(expect))
+        for param, check in zip(sig.parameters, expect):
+            name, pos, has_default, default_value = check
+            self.failUnlessEqual(param.name, name)
+            self.failUnlessEqual(param.position, pos)
+            if has_default:
+                self.failUnless(param.has_default)
+                self.failUnlessEqual(param.default_value, default_value)
+            else:
+                self.failUnless(not param.has_default)
+                self.failUnless(not hasattr(param, 'default_value'))
+            self.failUnless(not param.keyword_only)
+            self.failUnless(not param.has_annotation)
+            self.failUnless(not hasattr(param, 'annotation'))
+        self.failUnlessEqual(sig.var_args, 'g')
+        self.failUnlessEqual(sig.var_kw_args, 'h')
+        self.failUnlessEqual(len(sig.var_annotations), 0)
         binding = sig.bind(0, (1, (2,)), d=3, i=7)
         expected = {'a':0, 'b':1, 'c':2, 'd':3, 'e':4, 'f':5, 'g':tuple(),
                     'h':{'i':7}}
@@ -306,8 +330,46 @@
         self.failUnlessRaises(pep362.BindError, sig.bind, 1)
 
     @py3k_test
-    def XXX_test_all_args(self):
+    def test_all_py3k_args(self):
+        # a, (b, (c,)), d=0, (e, (f,))=(0, (0,)), *args, g, h=8, **kwargs
         sig = pep362.Signature(pep362_py3k_fodder.all_args)
+        # name, position, kw only, has_default, default, has anno, anno
+        expected = (('a', 0, False, False, None, True, int),
+                    (('b', ('c',)), 1, False, False, None, False, None),
+                    ('d', 2, False, True, 0, False, None),
+                    (('e', ('f',)), 3, False, True, (0, (0,)), False, None),
+                    ('g', 4, True, False, None, True, int),
+                    ('h', 5, True, True, 8, True, int))
+        self.failUnlessEqual(len(sig.parameters), len(expected),
+                "len(%r) != len(%r)" % ([param.name
+                                            for param in sig.parameters],
+                                        [expect[0] for expect in expected]))
+        for param, check in zip(sig.parameters, expected):
+            name, pos, kw_only, has_default, default, has_anno, anno = check
+            self.failUnlessEqual(param.name, name)
+            self.failUnlessEqual(param.position, pos)
+            if kw_only:
+                self.failUnless(param.keyword_only)
+            else:
+                self.failUnless(not param.keyword_only)
+            if has_default:
+                self.failUnless(param.has_default)
+                self.failUnlessEqual(param.default_value, default)
+            else:
+                self.failUnless(not param.has_default)
+                self.failUnless(not hasattr(param, 'default_value'))
+            if has_anno:
+                self.failUnless(param.has_annotation)
+                self.failUnlessEqual(param.annotation, anno)
+            else:
+                self.failUnless(not param.has_annotation)
+                self.failUnless(not hasattr(param, 'annotation'))
+        self.failUnlessEqual(sig.var_args, 'args')
+        self.failUnless(sig.var_args in sig.var_annotations)
+        self.failUnlessEqual(sig.var_annotations[sig.var_args], int)
+        self.failUnlessEqual(sig.var_kw_args, 'kwargs')
+        self.failUnless(sig.var_kw_args in sig.var_annotations)
+        self.failUnlessEqual(sig.var_annotations[sig.var_kw_args], int)
         binding = sig.bind(0, (1, (2,)), 3, (4, (5,)), 6, g=7, i=9)
         expected = {'a':0, 'b':1, 'c':2, 'd':3, 'e':4, 'f':5, 'g':7, 'h':8,
                 'args':(6,), 'kwargs':{'i':9}}


More information about the Python-checkins mailing list