[Python-checkins] cpython: Expose the namedtuple source with a _source attribute.

raymond.hettinger python-checkins at python.org
Wed Mar 23 21:05:24 CET 2011


http://hg.python.org/cpython/rev/bffdd7e9265c
changeset:   68879:bffdd7e9265c
user:        Raymond Hettinger <python at rcn.com>
date:        Wed Mar 23 12:52:23 2011 -0700
summary:
  Expose the namedtuple source with a _source attribute.

files:
  Doc/library/collections.rst  |  61 +++++------------------
  Lib/collections/__init__.py  |  11 ++-
  Lib/test/test_collections.py |   1 +
  3 files changed, 20 insertions(+), 53 deletions(-)


diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -694,7 +694,9 @@
    converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword
    ``def`` and the duplicate fieldname ``abc``.
 
-   If *verbose* is true, the class definition is printed just before being built.
+   If *verbose* is true, the class definition is printed after it is
+   built.  This option is outdated; instead, it is simpler to print the
+   :attr:`_source` attribute.
 
    Named tuple instances do not have per-instance dictionaries, so they are
    lightweight and require no more memory than regular tuples.
@@ -708,52 +710,6 @@
 
    >>> # Basic example
    >>> Point = namedtuple('Point', ['x', 'y'])
-   >>> p = Point(x=10, y=11)
-
-   >>> # Example using the verbose option to print the class definition
-   >>> Point = namedtuple('Point', ['x', 'y'], verbose=True)
-   class Point(tuple):
-       'Point(x, y)'
-   <BLANKLINE>
-       __slots__ = ()
-   <BLANKLINE>
-       _fields = ('x', 'y')
-   <BLANKLINE>
-       def __new__(_cls, x, y):
-           'Create a new instance of Point(x, y)'
-           return _tuple.__new__(_cls, (x, y))
-   <BLANKLINE>
-       @classmethod
-       def _make(cls, iterable, new=tuple.__new__, len=len):
-           'Make a new Point object from a sequence or iterable'
-           result = new(cls, iterable)
-           if len(result) != 2:
-               raise TypeError('Expected 2 arguments, got %d' % len(result))
-           return result
-   <BLANKLINE>
-       def __repr__(self):
-           'Return a nicely formatted representation string'
-           return self.__class__.__name__ + '(x=%r, y=%r)' % self
-   <BLANKLINE>
-       def _asdict(self):
-           'Return a new OrderedDict which maps field names to their values'
-           return OrderedDict(zip(self._fields, self))
-   <BLANKLINE>
-       def _replace(_self, **kwds):
-           'Return a new Point object replacing specified fields with new values'
-           result = _self._make(map(kwds.pop, ('x', 'y'), _self))
-           if kwds:
-               raise ValueError('Got unexpected field names: %r' % list(kwds))
-           return result
-   <BLANKLINE>
-       def __getnewargs__(self):
-           'Return self as a plain tuple.   Used by copy and pickle.'
-           return tuple(self)
-   <BLANKLINE>
-       x = _property(_itemgetter(0), doc='Alias for field number 0')
-   <BLANKLINE>
-       y = _property(_itemgetter(1), doc='Alias for field number 1')
-
    >>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
    >>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
    33
@@ -782,7 +738,7 @@
        print(emp.name, emp.title)
 
 In addition to the methods inherited from tuples, named tuples support
-three additional methods and one attribute.  To prevent conflicts with
+three additional methods and two attributes.  To prevent conflicts with
 field names, the method and attribute names start with an underscore.
 
 .. classmethod:: somenamedtuple._make(iterable)
@@ -820,6 +776,15 @@
       >>> for partnum, record in inventory.items():
       ...     inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
 
+.. attribute:: somenamedtuple._source
+
+   A string with the pure Python source code used to create the named
+   tuple class.  The source makes the named tuple self-documenting.
+   It can be printed, executed using :func:`exec`, or saved to a file
+   and imported.
+
+   .. versionadded:: 3.3
+
 .. attribute:: somenamedtuple._fields
 
    Tuple of strings listing the field names.  Useful for introspection
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -332,13 +332,13 @@
             raise ValueError('Type names and field names cannot be a keyword: %r' % name)
         if name[0].isdigit():
             raise ValueError('Type names and field names cannot start with a number: %r' % name)
-    seen_names = set()
+    seen = set()
     for name in field_names:
         if name.startswith('_') and not rename:
             raise ValueError('Field names cannot start with an underscore: %r' % name)
-        if name in seen_names:
+        if name in seen:
             raise ValueError('Encountered duplicate field name: %r' % name)
-        seen_names.add(name)
+        seen.add(name)
 
     # Fill-in the class template
     class_definition = _class_template.format(
@@ -350,8 +350,6 @@
         field_defs = '\n'.join(_field_template.format(index=index, name=name)
                                for index, name in enumerate(field_names))
     )
-    if verbose:
-        print(class_definition)
 
     # Execute the class definition string in a temporary namespace and
     # support tracing utilities by setting a value for frame.f_globals['__name__']
@@ -361,6 +359,9 @@
     except SyntaxError as e:
         raise SyntaxError(e.msg + ':\n\n' + class_definition)
     result = namespace[typename]
+    result._source = class_definition
+    if verbose:
+        print(result._source)
 
     # For pickling to work, the __module__ variable needs to be set to the frame
     # where the named tuple is created.  Bypass this step in enviroments where
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -127,6 +127,7 @@
         self.assertEqual(Point.__module__, __name__)
         self.assertEqual(Point.__getitem__, tuple.__getitem__)
         self.assertEqual(Point._fields, ('x', 'y'))
+        self.assertIn('class Point(tuple)', Point._source)
 
         self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi')       # type has non-alpha char
         self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi')      # type has keyword

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list