@@ -2396,41 +2396,51 @@ def __round__(self, ndigits: int = 0) -> T_co:
23962396 pass
23972397
23982398
2399- def _make_nmtuple (name , types , module , defaults = ()):
2400- fields = [n for n , t in types ]
2401- types = {n : _type_check (t , f"field { n } annotation must be a type" )
2402- for n , t in types }
2403- nm_tpl = collections .namedtuple (name , fields ,
2404- defaults = defaults , module = module )
2405- nm_tpl .__annotations__ = nm_tpl .__new__ .__annotations__ = types
2399+ def _make_nmtuple (name , types ):
2400+ msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
2401+ types = [(n , _type_check (t , msg )) for n , t in types ]
2402+ nm_tpl = collections .namedtuple (name , [n for n , t in types ])
2403+ nm_tpl .__annotations__ = dict (types )
2404+ try :
2405+ nm_tpl .__module__ = sys ._getframe (2 ).f_globals .get ('__name__' , '__main__' )
2406+ except (AttributeError , ValueError ):
2407+ pass
24062408 return nm_tpl
24072409
24082410
24092411# attributes prohibited to set in NamedTuple class syntax
2410- _prohibited = frozenset ( {'__new__' , '__init__' , '__slots__' , '__getnewargs__' ,
2411- '_fields' , '_field_defaults' ,
2412- '_make' , '_replace' , '_asdict' , '_source' })
2412+ _prohibited = {'__new__' , '__init__' , '__slots__' , '__getnewargs__' ,
2413+ '_fields' , '_field_defaults' ,
2414+ '_make' , '_replace' , '_asdict' , '_source' }
24132415
2414- _special = frozenset ( {'__module__' , '__name__' , '__annotations__' })
2416+ _special = {'__module__' , '__name__' , '__annotations__' }
24152417
24162418
24172419class NamedTupleMeta (type ):
24182420
24192421 def __new__ (cls , typename , bases , ns ):
2420- assert bases [0 ] is _NamedTuple
2422+ if ns .get ('_root' , False ):
2423+ return super ().__new__ (cls , typename , bases , ns )
2424+ if len (bases ) > 1 :
2425+ raise TypeError ("Multiple inheritance with NamedTuple is not supported" )
2426+ assert bases [0 ] is NamedTuple
24212427 types = ns .get ('__annotations__' , {})
2422- default_names = []
2428+ nm_tpl = _make_nmtuple (typename , types .items ())
2429+ defaults = []
2430+ defaults_dict = {}
24232431 for field_name in types :
24242432 if field_name in ns :
2425- default_names .append (field_name )
2426- elif default_names :
2427- raise TypeError (f"Non-default namedtuple field { field_name } "
2428- f"cannot follow default field"
2429- f"{ 's' if len (default_names ) > 1 else '' } "
2430- f"{ ', ' .join (default_names )} " )
2431- nm_tpl = _make_nmtuple (typename , types .items (),
2432- defaults = [ns [n ] for n in default_names ],
2433- module = ns ['__module__' ])
2433+ default_value = ns [field_name ]
2434+ defaults .append (default_value )
2435+ defaults_dict [field_name ] = default_value
2436+ elif defaults :
2437+ raise TypeError ("Non-default namedtuple field {field_name} cannot "
2438+ "follow default field(s) {default_names}"
2439+ .format (field_name = field_name ,
2440+ default_names = ', ' .join (defaults_dict .keys ())))
2441+ nm_tpl .__new__ .__annotations__ = dict (types )
2442+ nm_tpl .__new__ .__defaults__ = tuple (defaults )
2443+ nm_tpl ._field_defaults = defaults_dict
24342444 # update from user namespace without overriding special namedtuple attributes
24352445 for key in ns :
24362446 if key in _prohibited :
@@ -2440,7 +2450,7 @@ def __new__(cls, typename, bases, ns):
24402450 return nm_tpl
24412451
24422452
2443- def NamedTuple (typename , fields = None , / , ** kwargs ):
2453+ class NamedTuple (metaclass = NamedTupleMeta ):
24442454 """Typed version of namedtuple.
24452455
24462456 Usage in Python versions >= 3.6::
@@ -2464,22 +2474,15 @@ class Employee(NamedTuple):
24642474
24652475 Employee = NamedTuple('Employee', [('name', str), ('id', int)])
24662476 """
2467- if fields is None :
2468- fields = kwargs .items ()
2469- elif kwargs :
2470- raise TypeError ("Either list of fields or keywords"
2471- " can be provided to NamedTuple, not both" )
2472- return _make_nmtuple (typename , fields , module = _caller ())
2473-
2474- _NamedTuple = type .__new__ (NamedTupleMeta , 'NamedTuple' , (), {})
2475-
2476- def _namedtuple_mro_entries (bases ):
2477- if len (bases ) > 1 :
2478- raise TypeError ("Multiple inheritance with NamedTuple is not supported" )
2479- assert bases [0 ] is NamedTuple
2480- return (_NamedTuple ,)
2481-
2482- NamedTuple .__mro_entries__ = _namedtuple_mro_entries
2477+ _root = True
2478+
2479+ def __new__ (cls , typename , fields = None , / , ** kwargs ):
2480+ if fields is None :
2481+ fields = kwargs .items ()
2482+ elif kwargs :
2483+ raise TypeError ("Either list of fields or keywords"
2484+ " can be provided to NamedTuple, not both" )
2485+ return _make_nmtuple (typename , fields )
24832486
24842487
24852488class _TypedDictMeta (type ):
0 commit comments