[Python-checkins] r53848 - sandbox/trunk/pep362/pep362.py sandbox/trunk/pep362/test_pep362.py
brett.cannon
python-checkins at python.org
Thu Feb 22 00:54:21 CET 2007
Author: brett.cannon
Date: Thu Feb 22 00:54:17 2007
New Revision: 53848
Added:
sandbox/trunk/pep362/pep362.py (contents, props changed)
sandbox/trunk/pep362/test_pep362.py (contents, props changed)
Log:
Initial commit of PEP 362 implementation. Torn directly out of local branch of
trunk where code was integrated into the 'inspect' module (and test_inspect),
so will need to fix imports and tests before any of this is usable.
Added: sandbox/trunk/pep362/pep362.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pep362/pep362.py Thu Feb 22 00:54:17 2007
@@ -0,0 +1,174 @@
+class BindError(TypeError):
+ """Represent a failure of inspect.Signature.bind() being able to to
+ determine if a binding of arguments to parameters is possible."""
+ pass
+
+class Signature(object):
+
+ """Object to represent the signature of a function/method."""
+
+ def __init__(self, func):
+ """Initialize from a function or method object."""
+ if hasattr(func, 'im_func'):
+ func = func.im_func
+ self.name = func.__name__
+
+ argspec = getargspec(func)
+
+ self.var_args = argspec[1] if (argspec[1] is not None) else ''
+ self.var_kw_args = argspec[2] if (argspec[2] is not None) else ''
+
+ arg_count = len(argspec[0])
+ defaults_start = (arg_count - len(argspec[3])
+ if argspec[3] else arg_count)
+ parameters = []
+ for index, arg_name in enumerate(argspec[0]):
+ if isinstance(arg_name, list):
+ arg_name = self.__list2tuple(arg_name)
+
+ if index >= defaults_start:
+ parameters.append(Parameter(arg_name, index, True,
+ argspec[3][index - defaults_start]))
+ else:
+ parameters.append(Parameter(arg_name, index, False))
+
+ self.parameters = tuple(parameters)
+
+ @classmethod
+ def __list2tuple(cls, list_):
+ if not isinstance(list_, list):
+ return list_
+ else:
+ return tuple(cls.__list2tuple(x) for x in list_)
+
+ def __str__(self):
+ """String representation of a signature as one might write it in source
+ code."""
+ result = "%s(" % self.name
+ result += ", ".join(str(param) for param in self.parameters)
+ if self.var_args:
+ if self.parameters:
+ result +=", "
+ result += "*%s" % self.var_args
+ if self.var_kw_args:
+ if self.parameters or self.var_args:
+ result += ", "
+ result += "**%s" % self.var_kw_args
+ result += ")"
+ return result
+
+ @classmethod
+ def __tuple_bind_ok(cls, tuple_, arg):
+ """Verify that 'arg' will unpack properly to be used with 'tuple_'."""
+ try:
+ if len(tuple_) != len(arg):
+ return False
+ except TypeError:
+ raise BindError("cannot determine the length of the argument")
+ if (hasattr(arg, '__iter__') and hasattr(arg, 'next') and
+ callable(arg.__iter__) and callable(arg.next)):
+ raise IndexError("do not want to mutate an iterator")
+ for tuple_item, arg_item in zip(tuple_, arg):
+ if isinstance(tuple_item, tuple):
+ if not cls.__tuple_bind_ok(tuple_item, arg_item):
+ return False
+ return True
+
+ @classmethod
+ def __positional_bind(cls, parameter, arg, bindings):
+ """Bind 'argument' to 'parameter' in 'bindings' if it is a legitimate
+ binding.
+
+ A binding can be illegitimate if the parameter is a tuple and the
+ argument will either unpack improperly or it cannot be determined if it
+ will without possibly mutating the object (e.g., if it is an
+ iterator).
+
+ """
+ if isinstance(parameter, tuple):
+ if not cls.__tuple_bind_ok(parameter, arg):
+ raise TypeError("cannot unpack argument for %s" % parameter)
+ bindings[parameter] = arg
+
+ def bind(self, *args, **kwargs):
+ """Return a dictionary mapping function arguments to their parameter
+ variables, if possible.
+
+ If the only way to determine the proper binding when tuple parameters
+ are present is to posssibly mutate an iterator then the method gives up
+ and raises a BindError. This is to prevent something like a generator
+ which is about to be used for an actual function call from being
+ exhausted by this method."""
+ bindings = {}
+ arg_names_seq = [param.name for param in self.parameters]
+ arg_names_set = set(arg_names_seq)
+ arg_names_cnt = len(arg_names_set)
+ required_args = set(param.name for param in self.parameters
+ if not param.has_default)
+ required_args_cnt = len(required_args)
+ # *args.
+ if self.var_args:
+ bindings[self.var_args] = args[arg_names_cnt:]
+ args = args[:arg_names_cnt]
+ if len(args) > arg_names_cnt:
+ raise TypeError("too many positional arguments provided")
+ for arg_name, value in zip(arg_names_seq, args):
+ self.__positional_bind(arg_name, value, bindings)
+ # Keyword arguments.
+ var_kw_args = {}
+ for key, value in kwargs.items():
+ if key not in arg_names_set:
+ if not self.var_kw_args:
+ raise TypeError("unexpected keyword argument: %r" % key)
+ else:
+ var_kw_args[key] = value
+ else:
+ if key in bindings:
+ raise TypeError("got multiple values for argument %r" %
+ key)
+ else:
+ bindings[key] = value
+ del kwargs[key]
+ if kwargs:
+ raise TypeError("too many keyword arguments provided")
+ # **kwargs.
+ if self.var_kw_args:
+ bindings[self.var_kw_args] = var_kw_args
+ # Default values.
+ for param in self.parameters:
+ if param.has_default:
+ if param.name not in bindings:
+ bindings[param.name] = param.default_value
+
+ # Make sure all required arguments are bound to.
+ for bound in bindings.iterkeys():
+ try:
+ required_args.remove(bound)
+ except KeyError:
+ pass
+ else:
+ if required_args:
+ raise TypeError("too few arguments provided")
+ return bindings
+
+
+def getsignature(func):
+ """Return a Signature object for the function or method.
+
+ If possible, return the existing value stored in __signature__. If that
+ attribute does not exist, then try to store the Signature object at that
+ attribute if possible (but is not required).
+
+ """
+ if hasattr(func, 'im_func'):
+ func = func.im_func
+ sig = Signature(func)
+ if not hasattr(func, '__signature__'):
+ try:
+ func.__signature__ = sig
+ except AttributeError:
+ pass
+ else:
+ sig = func.__signature__
+
+ return sig
Added: sandbox/trunk/pep362/test_pep362.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pep362/test_pep362.py Thu Feb 22 00:54:17 2007
@@ -0,0 +1,255 @@
+class ParameterObjectTests(unittest.TestCase):
+
+ """Test the Parameter object."""
+
+ def test_name(self):
+ # Test that 'name' attribute works.
+ # Must test both using a string and a tuple of strings.
+ name = "test"
+ param = inspect.Parameter(name, 0, False)
+ self.failUnlessEqual(param.name, name)
+ name = ('a', ('b',))
+ param = inspect.Parameter(name, 0, False)
+ self.failUnlessEqual(param.name, name)
+
+ def test_position(self):
+ # Test the 'position' attribute.
+ pos = 42
+ param = inspect.Parameter("_", pos, False)
+ self.failUnlessEqual(param.position, pos)
+
+ def test_has_default(self):
+ # Test the 'has_default' attribute.
+ # Testing that 'default_value' is not set is handled in the testing of
+ # that attribute.
+ param = inspect.Parameter('_', 0, True, None)
+ self.failUnlessEqual(param.has_default, True)
+ param = inspect.Parameter('_', 0, False)
+ self.failUnlessEqual(param.has_default, False)
+ self.failUnlessRaises(TypeError, inspect.Parameter,
+ ('_', 0, False, 'extra arg'))
+ self.failUnlessRaises(TypeError, inspect.Parameter,
+ ('_', 0, True))
+ self.failUnlessRaises(TypeError, inspect.Parameter,
+ ('_', 0, True, 'default', 'extra'))
+
+ def test_default_value(self):
+ # Test the 'default_value' attribute.
+ # Make sure that if has_default is set to False that default_value is
+ # not defined.
+ param = inspect.Parameter('_', 0, False)
+ self.failUnless(not hasattr(param, 'default_value'))
+ default = 42
+ param = inspect.Parameter('_', 0, True, default)
+ self.failUnlessEqual(param.default_value, default)
+
+ def test_str(self):
+ # Test __str__().
+ name = "X"
+ param = inspect.Parameter(name, 0, False)
+ self.failUnlessEqual(name, str(param))
+ default_value = 42
+ param = inspect.Parameter(name, 0, True, default_value)
+ self.failUnlessEqual("%s=%s" % (name, default_value), str(param))
+
+ def test_repr(self):
+ # Test __repr__().
+ name = "X"
+ pos = 0
+ param = inspect.Parameter(name, pos, False)
+ self.failUnlessEqual("Parameter(%r, %r, False)" % (name, pos),
+ repr(param))
+ default_value = 42
+ param = inspect.Parameter(name, pos, True, default_value)
+ self.failUnlessEqual("Parameter(%r, %r, True, %r)" %
+ (name, pos, default_value),
+ repr(param))
+
+
+class SignatureObjectTests(unittest.TestCase):
+
+ def test_no_args(self):
+ # Test a function with no arguments.
+ sig = inspect.Signature(mod.no_args)
+ self.failUnlessEqual('no_args', sig.name)
+ self.failUnless(not sig.var_args)
+ self.failUnless(not sig.var_kw_args)
+ self.failUnlessEqual(0, len(sig.parameters))
+
+ def test_var_args(self):
+ # Test the var_args attribute.
+ sig = inspect.Signature(mod.var_args)
+ self.failUnlessEqual('args', sig.var_args)
+ self.failUnlessEqual(0, len(sig.parameters))
+ sig = inspect.Signature(mod.no_args)
+ self.failUnlessEqual('', sig.var_args)
+
+ def test_var_kw_args(self):
+ # Test the var_kw_args attribute.
+ sig = inspect.Signature(mod.var_kw_args)
+ self.failUnlessEqual('var_kw_args', sig.name)
+ self.failUnlessEqual('kwargs', sig.var_kw_args)
+ self.failUnlessEqual(0, len(sig.parameters))
+ sig = inspect.Signature(mod.no_args)
+ self.failUnlessEqual('', sig.var_kw_args)
+
+ def test_parameter_positional(self):
+ sig = inspect.Signature(mod.no_default_args)
+ self.failUnlessEqual('no_default_args', sig.name)
+ param = sig.parameters[0]
+ self.failUnlessEqual('a', param.name)
+ self.failUnlessEqual(0, param.position)
+ self.failUnless(not param.has_default)
+ self.failUnless(not hasattr(param, 'default_value'))
+
+ def test_parameter_default(self):
+ sig = inspect.Signature(mod.default_args)
+ self.failUnlessEqual('default_args', sig.name)
+ param = sig.parameters[0]
+ self.failUnlessEqual('a', param.name)
+ self.failUnlessEqual(0, param.position)
+ self.failUnless(param.has_default)
+ self.failUnlessEqual(42, param.default_value)
+
+ def test_parameter_tuple(self):
+ sig = inspect.Signature(mod.tuple_args)
+ self.failUnlessEqual('tuple_args', sig.name)
+ param = sig.parameters[0]
+ self.failUnless(isinstance(param.name, tuple))
+ self.failUnlessEqual(('a', ('b',)), param.name)
+ self.failUnlessEqual(0, param.position)
+ self.failUnless(not param.has_default)
+ self.failUnless(not hasattr(param, 'default_value'))
+
+ def test_parameter_tuple_default(self):
+ sig = inspect.Signature(mod.default_tuple_args)
+ self.failUnlessEqual('default_tuple_args', sig.name)
+ param = sig.parameters[0]
+ self.failUnlessEqual(('a', ('b',)), param.name)
+ self.failUnlessEqual(0, param.position)
+ self.failUnless(param.has_default)
+ self.failUnlessEqual((1, (2,)), param.default_value)
+
+ def test_positioning(self):
+ sig = inspect.Signature(mod.all_args)
+ param = sig.parameters[2]
+ self.failUnlessEqual('d', param.name)
+
+ def test_getsignature(self):
+ def fresh_func():
+ pass
+ self.failUnless(not hasattr(fresh_func, '__signature__'))
+ sig = inspect.getsignature(fresh_func)
+ self.failUnlessEqual(sig, fresh_func.__signature__)
+ sig2 = inspect.getsignature(fresh_func)
+ self.failUnlessEqual(sig, sig2)
+ class FreshClass(object):
+ def fresh_method(self):
+ pass
+ sig = inspect.getsignature(FreshClass.fresh_method)
+ self.failUnlessEqual(sig, FreshClass.fresh_method.im_func.__signature__)
+
+ def test_str(self):
+ # Test __str__().
+ sig = inspect.Signature(mod.no_args)
+ self.failUnlessEqual("no_args()", str(sig))
+ sig = inspect.Signature(mod.var_args)
+ self.failUnlessEqual("var_args(*args)", str(sig))
+ sig = inspect.Signature(mod.var_kw_args)
+ self.failUnlessEqual("var_kw_args(**kwargs)", str(sig))
+ sig = inspect.Signature(mod.default_args)
+ self.failUnlessEqual("default_args(a=42)", str(sig))
+ sig = inspect.Signature(mod.no_default_args)
+ self.failUnlessEqual("no_default_args(a)", str(sig))
+ sig = inspect.Signature(mod.tuple_args)
+ self.failUnlessEqual("tuple_args((a, (b,)))", str(sig))
+ sig = inspect.Signature(mod.default_tuple_args)
+ self.failUnlessEqual("default_tuple_args((a, (b,))=(1, (2,)))",
+ str(sig))
+ sig = inspect.Signature(mod.all_args)
+ self.failUnlessEqual("all_args(a, (b, (c,)), d=0, "
+ "(e, (f,))=(1, (2,)), *g, **h)",
+ str(sig))
+
+class SignatureBindTests(unittest.TestCase):
+
+ """Test Signature.bind()."""
+
+ def test_no_parameters(self):
+ sig = inspect.Signature(mod.no_args)
+ binding = sig.bind()
+ self.failUnlessEqual({}, binding)
+ self.failUnlessRaises(TypeError, sig.bind, 42)
+ self.failUnlessRaises(TypeError, sig.bind, a=0)
+
+ def test_var_parameters(self):
+ sig = inspect.Signature(mod.var_args)
+ binding = sig.bind(0, 1, 2)
+ self.failUnlessEqual({'args':(0, 1, 2)}, binding)
+ binding = sig.bind()
+ self.failUnlessEqual({'args':tuple()}, binding)
+ self.failUnlessRaises(TypeError, sig.bind, a=0)
+
+ def test_var_kw_parameters(self):
+ sig = inspect.Signature(mod.var_kw_args)
+ binding = sig.bind(a=0)
+ self.failUnlessEqual({'kwargs':{'a':0}}, binding)
+ binding = sig.bind()
+ self.failUnlessEqual({'kwargs':{}}, binding)
+ self.failUnlessRaises(TypeError, sig.bind, 42)
+
+ def test_positional_parameters(self):
+ sig = inspect.Signature(mod.no_default_args)
+ binding = sig.bind(42)
+ self.failUnlessEqual({'a':42}, binding)
+ binding = sig.bind(a=42)
+ self.failUnlessEqual({'a':42}, binding)
+ self.failUnlessRaises(TypeError, sig.bind)
+ self.failUnlessRaises(TypeError, sig.bind, 0, 1)
+ self.failUnlessRaises(TypeError, sig.bind, b=0)
+
+ def test_keyword_parameters(self):
+ sig = inspect.Signature(mod.default_args)
+ binding = sig.bind(0)
+ self.failUnlessEqual({'a':0}, binding)
+ binding = sig.bind()
+ self.failUnlessEqual({'a':42}, binding)
+ binding = sig.bind(a=0)
+ self.failUnlessEqual({'a':0}, binding)
+ self.failUnlessRaises(TypeError, sig.bind, 0, 1)
+ self.failUnlessRaises(TypeError, sig.bind, a=0, b=1)
+ self.failUnlessRaises(TypeError, sig.bind, b=1)
+
+ def test_tuple_parameter(self):
+ sig = inspect.Signature(mod.tuple_args)
+ binding = sig.bind((1, (2,)))
+ self.failUnlessEqual({('a', ('b',)):(1, (2,))}, binding)
+ arg = (1, ((2,),))
+ binding = sig.bind(arg)
+ self.failUnlessEqual({('a', ('b',)):arg}, binding)
+ self.failUnlessRaises(TypeError, sig.bind, (1,2,3))
+ self.failUnlessRaises(TypeError, sig.bind, (1, 2))
+
+ def test_default_tuple_parameter(self):
+ sig = inspect.Signature(mod.default_tuple_args)
+ binding = sig.bind()
+ self.failUnlessEqual({('a', ('b',)):(1, (2,))}, binding)
+ arg = (0, (1,))
+ binding = sig.bind(arg)
+ self.failUnlessEqual({('a', ('b',)):arg}, binding)
+
+ def test_all_parameter_types(self):
+ sig = inspect.Signature(mod.all_args)
+ binding = sig.bind(0, (1, (2,)), 3, (4, (5,)), 6, i=7)
+ expected = {'a':0, ('b', ('c',)):(1, (2,)), 'd':3,
+ ('e', ('f',)):(4, (5,)), 'g':(6,), 'h':{'i':7}}
+ self.failUnlessEqual(expected, binding)
+
+ def test_BindError(self):
+ def gen():
+ yield 0
+ yield (1,)
+ sig = inspect.Signature(mod.tuple_args)
+ self.failUnlessRaises(inspect.BindError, sig.bind, gen())
+
+
More information about the Python-checkins
mailing list