From abfd8dff3bd6bd2836e824b185a367c4b6c8e1f4 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 16 Oct 2007 21:28:32 +0000 Subject: More docs, error messages, and tests --- Doc/library/collections.rst | 10 +++++++--- Lib/collections.py | 3 +++ Lib/test/test_collections.py | 17 +++++++++++------ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 4e93b15..8d24e23 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -365,9 +365,13 @@ they add the ability to access fields by name instead of position index. The *fieldnames* are a single string with each fieldname separated by whitespace and/or commas (for example 'x y' or 'x, y'). Alternatively, the *fieldnames* - can be specified as a list of strings (such as ['x', 'y']). Any valid - Python identifier may be used for a fieldname except for names starting and - ending with double underscores. + can be specified as a list of strings (such as ['x', 'y']). + + Any valid Python identifier may be used for a fieldname except for names + starting and ending with double underscores. Valid identifiers consist of + letters, digits, and underscores but do not start with a digit and cannot be + a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, *print*, + or *raise*. If *verbose* is true, will print the class definition. diff --git a/Lib/collections.py b/Lib/collections.py index fbc00d1..0feda0a 100644 --- a/Lib/collections.py +++ b/Lib/collections.py @@ -2,6 +2,7 @@ __all__ = ['deque', 'defaultdict', 'named_tuple'] from _collections import deque, defaultdict from operator import itemgetter as _itemgetter +from keyword import iskeyword as _iskeyword import sys as _sys def named_tuple(typename, field_names, verbose=False): @@ -35,6 +36,8 @@ def named_tuple(typename, field_names, verbose=False): for name in (typename,) + field_names: if not name.replace('_', '').isalnum(): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) + if _iskeyword(name): + 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() diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 1af20ca..b1f3add 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -11,11 +11,17 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(Point.__slots__, ()) self.assertEqual(Point.__module__, __name__) self.assertEqual(Point.__getitem__, tuple.__getitem__) - self.assertRaises(ValueError, named_tuple, 'abc%', 'def ghi') - self.assertRaises(ValueError, named_tuple, 'abc', 'def g%hi') - self.assertRaises(ValueError, named_tuple, 'abc', '__def__ ghi') - self.assertRaises(ValueError, named_tuple, 'abc', 'def def ghi') - self.assertRaises(ValueError, named_tuple, 'abc', '8def 9ghi') + + self.assertRaises(ValueError, named_tuple, 'abc%', 'efg ghi') # type has non-alpha char + self.assertRaises(ValueError, named_tuple, 'class', 'efg ghi') # type has keyword + self.assertRaises(ValueError, named_tuple, '9abc', 'efg ghi') # type starts with digit + + self.assertRaises(ValueError, named_tuple, 'abc', 'efg g%hi') # field with non-alpha char + self.assertRaises(ValueError, named_tuple, 'abc', 'abc class') # field has keyword + self.assertRaises(ValueError, named_tuple, 'abc', '8efg 9ghi') # field starts with digit + self.assertRaises(ValueError, named_tuple, 'abc', '__efg__ ghi') # field with double underscores + self.assertRaises(ValueError, named_tuple, 'abc', 'efg efg ghi') # duplicate field + named_tuple('Point0', 'x1 y2') # Verify that numbers are allowed in names def test_instance(self): @@ -66,7 +72,6 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(p.y, y) self.assertRaises(AttributeError, eval, 'p.z', locals()) - def test_odd_sizes(self): Zero = named_tuple('Zero', '') self.assertEqual(Zero(), ()) -- cgit v0.12