diff options
author | Raymond Hettinger <python@rcn.com> | 2009-05-27 02:17:26 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2009-05-27 02:17:26 (GMT) |
commit | 4f1e16338d8b467880589277da447d7a6c725d99 (patch) | |
tree | 1516f911b2b9a605f4c5878f9ef569f478fbe7c1 /Lib | |
parent | 9c270c0362ac0803a9efacc3e01eafc16a590f77 (diff) | |
download | cpython-4f1e16338d8b467880589277da447d7a6c725d99.zip cpython-4f1e16338d8b467880589277da447d7a6c725d99.tar.gz cpython-4f1e16338d8b467880589277da447d7a6c725d99.tar.bz2 |
Fix field name conflicts for named tuples.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/collections.py | 13 | ||||
-rw-r--r-- | Lib/test/test_collections.py | 40 |
2 files changed, 47 insertions, 6 deletions
diff --git a/Lib/collections.py b/Lib/collections.py index 81d8928..b81effe 100644 --- a/Lib/collections.py +++ b/Lib/collections.py @@ -63,8 +63,8 @@ def namedtuple(typename, field_names, verbose=False): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n - def __new__(cls, %(argtxt)s): - return tuple.__new__(cls, (%(argtxt)s)) \n + def __new__(_cls, %(argtxt)s): + return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' @@ -77,22 +77,23 @@ def namedtuple(typename, field_names, verbose=False): def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n - def _replace(self, **kwds): + def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' - result = self._make(map(kwds.pop, %(field_names)r, self)) + result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): - template += ' %s = property(itemgetter(%d))\n' % (name, i) + template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] - namespace = dict(itemgetter=_itemgetter, __name__='namedtuple_%s' % typename) + namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, + _property=property, _tuple=tuple) try: exec template in namespace except SyntaxError, e: diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 3db5f25..4a068e0 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -2,6 +2,8 @@ import unittest, doctest from test import test_support from collections import namedtuple import pickle, cPickle, copy +import keyword +import re from collections import Hashable, Iterable, Iterator from collections import Sized, Container, Callable from collections import Set, MutableSet @@ -154,6 +156,44 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(p, q) self.assertEqual(p._fields, q._fields) + def test_name_conflicts(self): + # Some names like "self", "cls", "tuple", "itemgetter", and "property" + # failed when used as field names. Test to make sure these now work. + T = namedtuple('T', 'itemgetter property self cls tuple') + t = T(1, 2, 3, 4, 5) + self.assertEqual(t, (1,2,3,4,5)) + newt = t._replace(itemgetter=10, property=20, self=30, cls=40, tuple=50) + self.assertEqual(newt, (10,20,30,40,50)) + + # Broader test of all interesting names in a template + with test_support.captured_stdout() as template: + T = namedtuple('T', 'x', verbose=True) + words = set(re.findall('[A-Za-z]+', template.getvalue())) + words -= set(keyword.kwlist) + T = namedtuple('T', words) + # test __new__ + values = tuple(range(len(words))) + t = T(*values) + self.assertEqual(t, values) + t = T(**dict(zip(T._fields, values))) + self.assertEqual(t, values) + # test _make + t = T._make(values) + self.assertEqual(t, values) + # exercise __repr__ + repr(t) + # test _asdict + self.assertEqual(t._asdict(), dict(zip(T._fields, values))) + # test _replace + t = T._make(values) + newvalues = tuple(v*10 for v in values) + newt = t._replace(**dict(zip(T._fields, newvalues))) + self.assertEqual(newt, newvalues) + # test _fields + self.assertEqual(T._fields, tuple(words)) + # test __getnewargs__ + self.assertEqual(t.__getnewargs__(), values) + class ABCTestCase(unittest.TestCase): def validate_abstract_methods(self, abc, *names): |