@@ -315,10 +315,10 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
315315
316316 """
317317
318- # Parse and validate the field names. Validation serves two purposes,
319- # generating informative error messages and preventing template injection attacks .
318+ # Validate the field names. At the user's option, either generate an error
319+ # message or automatically replace the field name with a valid name .
320320 if isinstance (field_names , str ):
321- field_names = field_names .replace (',' , ' ' ).split () # names separated by whitespace and/or commas
321+ field_names = field_names .replace (',' , ' ' ).split ()
322322 field_names = list (map (str , field_names ))
323323 if rename :
324324 seen = set ()
@@ -333,15 +333,19 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
333333 seen .add (name )
334334 for name in [typename ] + field_names :
335335 if not all (c .isalnum () or c == '_' for c in name ):
336- raise ValueError ('Type names and field names can only contain alphanumeric characters and underscores: %r' % name )
336+ raise ValueError ('Type names and field names can only contain '
337+ 'alphanumeric characters and underscores: %r' % name )
337338 if _iskeyword (name ):
338- raise ValueError ('Type names and field names cannot be a keyword: %r' % name )
339+ raise ValueError ('Type names and field names cannot be a '
340+ 'keyword: %r' % name )
339341 if name [0 ].isdigit ():
340- raise ValueError ('Type names and field names cannot start with a number: %r' % name )
342+ raise ValueError ('Type names and field names cannot start with '
343+ 'a number: %r' % name )
341344 seen = set ()
342345 for name in field_names :
343346 if name .startswith ('_' ) and not rename :
344- raise ValueError ('Field names cannot start with an underscore: %r' % name )
347+ raise ValueError ('Field names cannot start with an underscore: '
348+ '%r' % name )
345349 if name in seen :
346350 raise ValueError ('Encountered duplicate field name: %r' % name )
347351 seen .add (name )
@@ -352,13 +356,14 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
352356 field_names = tuple (field_names ),
353357 num_fields = len (field_names ),
354358 arg_list = repr (tuple (field_names )).replace ("'" , "" )[1 :- 1 ],
355- repr_fmt = ', ' .join (_repr_template .format (name = name ) for name in field_names ),
359+ repr_fmt = ', ' .join (_repr_template .format (name = name )
360+ for name in field_names ),
356361 field_defs = '\n ' .join (_field_template .format (index = index , name = name )
357362 for index , name in enumerate (field_names ))
358363 )
359364
360- # Execute the template string in a temporary namespace and
361- # support tracing utilities by setting a value for frame.f_globals['__name__']
365+ # Execute the template string in a temporary namespace and support
366+ # tracing utilities by setting a value for frame.f_globals['__name__']
362367 namespace = dict (__name__ = 'namedtuple_%s' % typename )
363368 try :
364369 exec (class_definition , namespace )
@@ -1122,44 +1127,3 @@ def translate(self, *args):
11221127 return self .__class__ (self .data .translate (* args ))
11231128 def upper (self ): return self .__class__ (self .data .upper ())
11241129 def zfill (self , width ): return self .__class__ (self .data .zfill (width ))
1125-
1126-
1127-
1128- ################################################################################
1129- ### Simple tests
1130- ################################################################################
1131-
1132- if __name__ == '__main__' :
1133- # verify that instances can be pickled
1134- from pickle import loads , dumps
1135- Point = namedtuple ('Point' , 'x, y' , True )
1136- p = Point (x = 10 , y = 20 )
1137- assert p == loads (dumps (p ))
1138-
1139- # test and demonstrate ability to override methods
1140- class Point (namedtuple ('Point' , 'x y' )):
1141- __slots__ = ()
1142- @property
1143- def hypot (self ):
1144- return (self .x ** 2 + self .y ** 2 ) ** 0.5
1145- def __str__ (self ):
1146- return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self .x , self .y , self .hypot )
1147-
1148- for p in Point (3 , 4 ), Point (14 , 5 / 7. ):
1149- print (p )
1150-
1151- class Point (namedtuple ('Point' , 'x y' )):
1152- 'Point class with optimized _make() and _replace() without error-checking'
1153- __slots__ = ()
1154- _make = classmethod (tuple .__new__ )
1155- def _replace (self , _map = map , ** kwds ):
1156- return self ._make (_map (kwds .get , ('x' , 'y' ), self ))
1157-
1158- print (Point (11 , 22 )._replace (x = 100 ))
1159-
1160- Point3D = namedtuple ('Point3D' , Point ._fields + ('z' ,))
1161- print (Point3D .__doc__ )
1162-
1163- import doctest
1164- TestResults = namedtuple ('TestResults' , 'failed attempted' )
1165- print (TestResults (* doctest .testmod ()))
0 commit comments