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):
281282 self .assertEqual (Point (1 ), (1 , 20 ))
282283 self .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" )
287301 def test_factory_doc_attr (self ):
288302 Point = namedtuple ('Point' , 'x y' )
289303 self .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 ):
294310 Point = namedtuple ('Point' , 'x y' )
295311 self .assertEqual (Point .x .__doc__ , 'Alias for field number 0' )
312+ self .assertEqual (Point .y .__doc__ , 'Alias for field number 1' )
296313 Point .x .__doc__ = 'docstring for Point.x'
297314 self .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
299330 def test_name_fixer (self ):
300331 for spec , renamed in [
@@ -319,16 +350,18 @@ def test_instance(self):
319350 self .assertEqual (p , Point (y = 22 , x = 11 ))
320351 self .assertEqual (p , Point (* (11 , 22 )))
321352 self .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 )
326359 self .assertEqual (repr (p ), 'Point(x=11, y=22)' )
327360 self .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
333366 try :
334367 p ._replace (x = 1 , error = 2 )
@@ -360,11 +393,15 @@ def test_tupleness(self):
360393 x , y = p
361394 self .assertEqual (p , (x , y )) # unpacks like a tuple
362395 self .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
365401 self .assertEqual (p .x , x )
366402 self .assertEqual (p .y , y )
367- self .assertRaises (AttributeError , eval , 'p.z' , locals ())
403+ with self .assertRaises (AttributeError ):
404+ p .z
368405
369406 def test_odd_sizes (self ):
370407 Zero = namedtuple ('Zero' , '' )
@@ -514,13 +551,13 @@ class Point(namedtuple('_Point', ['x', 'y'])):
514551 a .w = 5
515552 self .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################################################################################
0 commit comments