[Python-checkins] r59031 - peps/trunk/pep-0362.txt

brett.cannon python-checkins at python.org
Sat Nov 17 05:20:22 CET 2007


Author: brett.cannon
Date: Sat Nov 17 05:20:22 2007
New Revision: 59031

Modified:
   peps/trunk/pep-0362.txt
Log:
Answer two open issues and add a good-sized example.


Modified: peps/trunk/pep-0362.txt
==============================================================================
--- peps/trunk/pep-0362.txt	(original)
+++ peps/trunk/pep-0362.txt	Sat Nov 17 05:20:22 2007
@@ -79,10 +79,7 @@
     The keys are of the variable parameter with values of the
     annotation.  If an annotation does not exist for a variable
     parameter then the key does not exist in the dict.
-* has_annotation : bool
-    Signifies whether the function has an annotation for the return
-    type.
-* annotation : object
+* return_annotation : object
     If present, the attribute is set to the annotation for the return
     type of the function.
 * parameters : list(Parameter)
@@ -95,6 +92,14 @@
     being the value the parameter would have if this function was
     called with the given arguments.
 
+Signature objects also have the following methods:
+
+* __getitem__(self, key : str) -> Parameter
+    Returns the Parameter object for the named parameter.
+* __iter__(self)
+    Returns an iterator that returns Parameter objects in their
+    sequential order based on their 'position' attribute.
+
 The Signature object is stored in the ``__signature__`` attribute of
 a function.  When it is to be created is discussed in
 `Open Issues`_.
@@ -129,17 +134,11 @@
     to represent keyword-only parameters was rejected to prevent
     variable type usage and as a possible point of errors,
     respectively.
-* has_default : bool
-    True if the parameter has a default value, else False.
 * default_value : object
     The default value for the parameter, if present, else the
-    attribute does not exist.  This is done so that the attribute is
-    not accidentally used if no default value is set as any default
-    value could be a legitimate default value itself.
+    attribute does not exist.
 * keyword_only : bool
     True if the parameter is keyword-only, else False.
-* has_annotation : bool
-    True if the parameter has an annotation, else False.
 * annotation
     Set to the annotation for the parameter.  If ``has_annotation`` is
     False then the attribute does not exist to prevent accidental use.
@@ -157,6 +156,118 @@
 work with.
 
 
+Examples
+========
+
+Annotation Checker
+------------------
+::
+
+    def quack_check(fxn):
+        """Decorator to verify arguments and return value quack as they should.
+
+        Positional arguments.
+        >>> @quack_check
+        ... def one_arg(x:int): pass
+        ... 
+        >>> one_arg(42)
+        >>> one_arg('a')
+        Traceback (most recent call last):
+            ...
+        TypeError: 'a' does not quack like a <type 'int'>
+
+
+        *args
+        >>> @quack_check
+        ... def var_args(*args:int): pass
+        ... 
+        >>> var_args(*[1,2,3])
+        >>> var_args(*[1,'b',3])
+        Traceback (most recent call last):
+            ...
+        TypeError: *args contains a a value that does not quack like a <type 'int'>
+
+        **kwargs
+        >>> @quack_check
+        ... def var_kw_args(**kwargs:int): pass
+        ... 
+        >>> var_kw_args(**{'a': 1})
+        >>> var_kw_args(**{'a': 'A'})
+        Traceback (most recent call last):
+            ...
+        TypeError: **kwargs contains a value that does not quack like a <type 'int'>
+
+        Return annotations.
+        >>> @quack_check
+        ... def returned(x) -> int: return x
+        ... 
+        >>> returned(42)
+        42
+        >>> returned('a')
+        Traceback (most recent call last):
+            ...
+        TypeError: the return value 'a' does not quack like a <type 'int'>
+
+        """
+        # Get the signature; only needs to be calculated once.
+        sig = Signature(fxn)
+        def check(*args, **kwargs):
+            # Find out the variable -> value bindings.
+            bindings = sig.bind(*args, **kwargs)
+            # Check *args for the proper quack.
+            try:
+                duck = sig.var_annotations[sig.var_args]
+            except KeyError:
+                pass
+            else:
+                # Check every value in *args.
+                for value in bindings[sig.var_args]:
+                    if not isinstance(value, duck):
+                        raise TypeError("*%s contains a a value that does not "
+                                        "quack like a %r" %
+                                        (sig.var_args, duck))
+                # Remove it from the bindings so as to not check it again.
+                del bindings[sig.var_args]
+            # **kwargs.
+            try:
+                duck = sig.var_annotations[sig.var_kw_args]
+            except (KeyError, AttributeError):
+                pass
+            else:
+                # Check every value in **kwargs.
+                for value in bindings[sig.var_kw_args].values():
+                    if not isinstance(value, duck):
+                        raise TypeError("**%s contains a value that does not "
+                                        "quack like a %r" %
+                                        (sig.var_kw_args, duck))
+                # Remove from bindings so as to not check again.
+                del bindings[sig.var_kw_args]
+            # For each remaining variable ...
+            for var, value in bindings.items():
+                # See if an annotation was set.
+                try:
+                    duck = sig[var].annotation
+                except AttributeError:
+                    continue
+                # Check that the value quacks like it should.
+                if not isinstance(value, duck):
+                    raise TypeError('%r does not quack like a %s' % (value, duck))
+            else:
+                # All the ducks quack fine; let the call proceed.
+                returned = fxn(*args, **kwargs)
+                # Check the return value.
+                try:
+                    if not isinstance(returned, sig.return_annotation):
+                        raise TypeError('the return value %r does not quack like '
+                                        'a %r' % (returned,
+                                            sig.return_annotation))
+                except AttributeError:
+                    pass
+                return returned
+        # Full-featured version would set function metadata.
+        return check
+
+
 Open Issues
 ===========
 
@@ -180,33 +291,6 @@
 key (and the name would be used as the hash for a Parameter object).
 
 
-Provide a mapping of parameter name to Parameter object?
---------------------------------------------------------
-
-While providing access to the parameters in order is handy, it might
-also be beneficial to provide a way to retrieve Parameter objects from
-a Signature object based on the parameter's name.  Which style of
-access (sequential/iteration or mapping) will influence how the
-parameters are stored internally and whether __getitem__ accepts
-strings or integers.
-
-One possible compromise is to have ``__getitem__`` provide mapping
-support and have ``__iter__`` return Parameter objects based on their
-``position`` attribute.  This allows for getting the sequence of
-Parameter objects easily by using the ``__iter__`` method on Signature
-object along with the sequence constructor (e.g., ``list`` or
-``tuple``).
-
-
-Remove ``has_*`` attributes?
-----------------------------
-
-If an EAFP approach to the API is taken, both ``has_annotation`` and
-``has_default`` are unneeded as the respective ``annotation`` and
-``default_value`` attributes are simply not set.  It's simply a
-question of whether to have a EAFP or LBYL interface.
-
-
 Have ``var_args`` and ``_var_kw_args`` default to ``None``?
 ------------------------------------------------------------
 


More information about the Python-checkins mailing list