33import collections
44import copy
55import doctest
6+ import inspect
67import operator
78import pickle
89from random import choice ,randrange
@@ -281,20 +282,50 @@ def test_defaults(self):
281282self .assertEqual (Point (1 ), (1 ,20 ))
282283self .assertEqual (Point (), (10 ,20 ))
283284
285+ def test_readonly (self ):
286+ Point = namedtuple ('Point' ,'x y' )
287+ p = Point (11 ,22 )
288+ with self .assertRaises (AttributeError ):
289+ p .x = 33
290+ with self .assertRaises (AttributeError ):
291+ del p .x
292+ with self .assertRaises (TypeError ):
293+ p [0 ]= 33
294+ with self .assertRaises (TypeError ):
295+ del p [0 ]
296+ self .assertEqual (p .x ,11 )
297+ self .assertEqual (p [0 ],11 )
284298
285299@unittest .skipIf (sys .flags .optimize >= 2 ,
286300"Docstrings are omitted with -O2 and above" )
287301def test_factory_doc_attr (self ):
288302Point = namedtuple ('Point' ,'x y' )
289303self .assertEqual (Point .__doc__ ,'Point(x, y)' )
304+ Point .__doc__ = '2D point'
305+ self .assertEqual (Point .__doc__ ,'2D point' )
290306
291307@unittest .skipIf (sys .flags .optimize >= 2 ,
292308"Docstrings are omitted with -O2 and above" )
293- def test_doc_writable (self ):
309+ def test_field_doc (self ):
294310Point = namedtuple ('Point' ,'x y' )
295311self .assertEqual (Point .x .__doc__ ,'Alias for field number 0' )
312+ self .assertEqual (Point .y .__doc__ ,'Alias for field number 1' )
296313Point .x .__doc__ = 'docstring for Point.x'
297314self .assertEqual (Point .x .__doc__ ,'docstring for Point.x' )
315+ # namedtuple can mutate doc of descriptors independently
316+ Vector = namedtuple ('Vector' ,'x y' )
317+ self .assertEqual (Vector .x .__doc__ ,'Alias for field number 0' )
318+ Vector .x .__doc__ = 'docstring for Vector.x'
319+ self .assertEqual (Vector .x .__doc__ ,'docstring for Vector.x' )
320+
321+ @support .cpython_only
322+ @unittest .skipIf (sys .flags .optimize >= 2 ,
323+ "Docstrings are omitted with -O2 and above" )
324+ def test_field_doc_reuse (self ):
325+ P = namedtuple ('P' , ['m' ,'n' ])
326+ Q = namedtuple ('Q' , ['o' ,'p' ])
327+ self .assertIs (P .m .__doc__ ,Q .o .__doc__ )
328+ self .assertIs (P .n .__doc__ ,Q .p .__doc__ )
298329
299330def test_name_fixer (self ):
300331for spec ,renamed in [
@@ -319,16 +350,18 @@ def test_instance(self):
319350self .assertEqual (p ,Point (y = 22 ,x = 11 ))
320351self .assertEqual (p ,Point (* (11 ,22 )))
321352self .assertEqual (p ,Point (** dict (x = 11 ,y = 22 )))
322- self .assertRaises (TypeError ,Point ,1 )# too few args
323- self .assertRaises (TypeError ,Point ,1 ,2 ,3 )# too many args
324- self .assertRaises (TypeError ,eval ,'Point(XXX=1, y=2)' ,locals ())# wrong keyword argument
325- self .assertRaises (TypeError ,eval ,'Point(x=1)' ,locals ())# missing keyword argument
353+ self .assertRaises (TypeError ,Point ,1 )# too few args
354+ self .assertRaises (TypeError ,Point ,1 ,2 ,3 )# too many args
355+ with self .assertRaises (TypeError ):# wrong keyword argument
356+ Point (XXX = 1 ,y = 2 )
357+ with self .assertRaises (TypeError ):# missing keyword argument
358+ Point (x = 1 )
326359self .assertEqual (repr (p ),'Point(x=11, y=22)' )
327360self .assertNotIn ('__weakref__' ,dir (p ))
328- self .assertEqual (p ,Point ._make ([11 ,22 ]))# test _make classmethod
329- self .assertEqual (p ._fields , ('x' ,'y' ))# test _fields attribute
330- self .assertEqual (p ._replace (x = 1 ), (1 ,22 ))# test _replace method
331- self .assertEqual (p ._asdict (),dict (x = 11 ,y = 22 ))# test _asdict method
361+ self .assertEqual (p ,Point ._make ([11 ,22 ]))# test _make classmethod
362+ self .assertEqual (p ._fields , ('x' ,'y' ))# test _fields attribute
363+ self .assertEqual (p ._replace (x = 1 ), (1 ,22 ))# test _replace method
364+ self .assertEqual (p ._asdict (),dict (x = 11 ,y = 22 ))# test _asdict method
332365
333366try :
334367p ._replace (x = 1 ,error = 2 )
@@ -360,11 +393,15 @@ def test_tupleness(self):
360393x ,y = p
361394self .assertEqual (p , (x ,y ))# unpacks like a tuple
362395self .assertEqual ((p [0 ],p [1 ]), (11 ,22 ))# indexable like a tuple
363- self .assertRaises (IndexError ,p .__getitem__ ,3 )
396+ with self .assertRaises (IndexError ):
397+ p [3 ]
398+ self .assertEqual (p [- 1 ],22 )
399+ self .assertEqual (hash (p ),hash ((11 ,22 )))
364400
365401self .assertEqual (p .x ,x )
366402self .assertEqual (p .y ,y )
367- self .assertRaises (AttributeError ,eval ,'p.z' ,locals ())
403+ with self .assertRaises (AttributeError ):
404+ p .z
368405
369406def test_odd_sizes (self ):
370407Zero = namedtuple ('Zero' ,'' )
@@ -514,13 +551,13 @@ class Point(namedtuple('_Point', ['x', 'y'])):
514551a .w = 5
515552self .assertEqual (a .__dict__ , {'w' :5 })
516553
517- def test_namedtuple_can_mutate_doc_of_descriptors_independently (self ):
518- A = namedtuple ('A ' ,'x y' )
519- B = namedtuple ( 'B' , 'x y' )
520- A . x . __doc__ = 'foo'
521- B . x . __doc__ = 'bar'
522- self .assertEqual ( A .x .__doc__ , 'foo' )
523- self .assertEqual ( B .x .__doc__ , 'bar' )
554+ def test_field_descriptor (self ):
555+ Point = namedtuple ('Point ' ,'x y' )
556+ p = Point ( 11 , 22 )
557+ self . assertTrue ( inspect . isdatadescriptor ( Point . x ))
558+ self . assertEqual ( Point . x . __get__ ( p ), 11 )
559+ self .assertRaises ( AttributeError , Point .x .__set__ , p , 33 )
560+ self .assertRaises ( AttributeError , Point .x .__delete__ , p )
524561
525562
526563################################################################################