Kaydet (Commit) 4f1e1633 authored tarafından Raymond Hettinger's avatar Raymond Hettinger

Fix field name conflicts for named tuples.

üst 9c270c03
......@@ -524,8 +524,8 @@ Example:
<BLANKLINE>
_fields = ('x', 'y')
<BLANKLINE>
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
def __new__(_cls, x, y):
return _tuple.__new__(_cls, (x, y))
<BLANKLINE>
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
......@@ -542,9 +542,9 @@ Example:
'Return a new dict which maps field names to their values'
return {'x': t[0], 'y': t[1]}
<BLANKLINE>
def _replace(self, **kwds):
def _replace(_self, **kwds):
'Return a new Point object replacing specified fields with new values'
result = self._make(map(kwds.pop, ('x', 'y'), self))
result = _self._make(map(kwds.pop, ('x', 'y'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return result
......@@ -552,8 +552,8 @@ Example:
def __getnewargs__(self):
return tuple(self)
<BLANKLINE>
x = property(itemgetter(0))
y = property(itemgetter(1))
x = _property(_itemgetter(0))
y = _property(_itemgetter(1))
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
......
......@@ -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:
......
......@@ -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):
......
......@@ -53,6 +53,9 @@ Library
- Issue #6050: Don't fail extracting a directory from a zipfile if
the directory already exists.
- collections.namedtuple() was not working with the following field
names: cls, self, tuple, itemgetter, and property.
- Issue #1309352: fcntl now converts its third arguments to a C `long` rather
than an int, which makes some operations possible under 64-bit Linux (e.g.
DN_MULTISHOT with F_NOTIFY).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment