@@ -2396,41 +2396,51 @@ def __round__(self, ndigits: int = 0) -> T_co:
2396
2396
pass
2397
2397
2398
2398
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
2406
2408
return nm_tpl
2407
2409
2408
2410
2409
2411
# 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' }
2413
2415
2414
- _special = frozenset ( {'__module__' , '__name__' , '__annotations__' })
2416
+ _special = {'__module__' , '__name__' , '__annotations__' }
2415
2417
2416
2418
2417
2419
class NamedTupleMeta (type ):
2418
2420
2419
2421
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
2421
2427
types = ns .get ('__annotations__' , {})
2422
- default_names = []
2428
+ nm_tpl = _make_nmtuple (typename , types .items ())
2429
+ defaults = []
2430
+ defaults_dict = {}
2423
2431
for field_name in types :
2424
2432
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
2434
2444
# update from user namespace without overriding special namedtuple attributes
2435
2445
for key in ns :
2436
2446
if key in _prohibited :
@@ -2440,7 +2450,7 @@ def __new__(cls, typename, bases, ns):
2440
2450
return nm_tpl
2441
2451
2442
2452
2443
- def NamedTuple (typename , fields = None , / , ** kwargs ):
2453
+ class NamedTuple (metaclass = NamedTupleMeta ):
2444
2454
"""Typed version of namedtuple.
2445
2455
2446
2456
Usage in Python versions >= 3.6::
@@ -2464,22 +2474,15 @@ class Employee(NamedTuple):
2464
2474
2465
2475
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
2466
2476
"""
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 )
2483
2486
2484
2487
2485
2488
class _TypedDictMeta (type ):
0 commit comments