@@ -368,23 +368,24 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
368
368
# worries about external callers.
369
369
if locals is None :
370
370
locals = {}
371
- # __builtins__ may be the "builtins" module or
372
- # the value of its "__dict__",
373
- # so make sure "__builtins__" is the module.
374
- if globals is not None and '__builtins__' not in globals :
375
- globals ['__builtins__' ] = builtins
371
+ if 'BUILTINS' not in locals :
372
+ locals ['BUILTINS' ] = builtins
376
373
return_annotation = ''
377
374
if return_type is not MISSING :
378
375
locals ['_return_type' ] = return_type
379
376
return_annotation = '->_return_type'
380
377
args = ',' .join (args )
381
- body = '\n ' .join (f' { b } ' for b in body )
378
+ body = '\n ' .join (f' { b } ' for b in body )
382
379
383
380
# Compute the text of the entire function.
384
- txt = f'def { name } ({ args } ){ return_annotation } :\n { body } '
381
+ txt = f' def { name } ({ args } ){ return_annotation } :\n { body } '
385
382
386
- exec (txt , globals , locals )
387
- return locals [name ]
383
+ local_vars = ', ' .join (locals .keys ())
384
+ txt = f"def __create_fn__({ local_vars } ):\n { txt } \n return { name } "
385
+
386
+ ns = {}
387
+ exec (txt , globals , ns )
388
+ return ns ['__create_fn__' ](** locals )
388
389
389
390
390
391
def _field_assign (frozen , name , value , self_name ):
@@ -395,7 +396,7 @@ def _field_assign(frozen, name, value, self_name):
395
396
# self_name is what "self" is called in this function: don't
396
397
# hard-code "self", since that might be a field name.
397
398
if frozen :
398
- return f'__builtins__ .object.__setattr__({ self_name } ,{ name !r} ,{ value } )'
399
+ return f'BUILTINS .object.__setattr__({ self_name } ,{ name !r} ,{ value } )'
399
400
return f'{ self_name } .{ name } ={ value } '
400
401
401
402
@@ -472,7 +473,7 @@ def _init_param(f):
472
473
return f'{ f .name } :_type_{ f .name } { default } '
473
474
474
475
475
- def _init_fn (fields , frozen , has_post_init , self_name ):
476
+ def _init_fn (fields , frozen , has_post_init , self_name , globals ):
476
477
# fields contains both real fields and InitVar pseudo-fields.
477
478
478
479
# Make sure we don't have fields without defaults following fields
@@ -490,12 +491,15 @@ def _init_fn(fields, frozen, has_post_init, self_name):
490
491
raise TypeError (f'non-default argument { f .name !r} '
491
492
'follows default argument' )
492
493
493
- globals = {'MISSING' : MISSING ,
494
- '_HAS_DEFAULT_FACTORY' : _HAS_DEFAULT_FACTORY }
494
+ locals = {f'_type_{ f .name } ' : f .type for f in fields }
495
+ locals .update ({
496
+ 'MISSING' : MISSING ,
497
+ '_HAS_DEFAULT_FACTORY' : _HAS_DEFAULT_FACTORY ,
498
+ })
495
499
496
500
body_lines = []
497
501
for f in fields :
498
- line = _field_init (f , frozen , globals , self_name )
502
+ line = _field_init (f , frozen , locals , self_name )
499
503
# line is None means that this field doesn't require
500
504
# initialization (it's a pseudo-field). Just skip it.
501
505
if line :
@@ -511,7 +515,6 @@ def _init_fn(fields, frozen, has_post_init, self_name):
511
515
if not body_lines :
512
516
body_lines = ['pass' ]
513
517
514
- locals = {f'_type_{ f .name } ' : f .type for f in fields }
515
518
return _create_fn ('__init__' ,
516
519
[self_name ] + [_init_param (f ) for f in fields if f .init ],
517
520
body_lines ,
@@ -520,20 +523,19 @@ def _init_fn(fields, frozen, has_post_init, self_name):
520
523
return_type = None )
521
524
522
525
523
- def _repr_fn (fields ):
526
+ def _repr_fn (fields , globals ):
524
527
fn = _create_fn ('__repr__' ,
525
528
('self' ,),
526
529
['return self.__class__.__qualname__ + f"(' +
527
530
', ' .join ([f"{ f .name } ={{self.{ f .name } !r}}"
528
531
for f in fields ]) +
529
- ')"' ])
532
+ ')"' ],
533
+ globals = globals )
530
534
return _recursive_repr (fn )
531
535
532
536
533
- def _frozen_get_del_attr (cls , fields ):
534
- # XXX: globals is modified on the first call to _create_fn, then
535
- # the modified version is used in the second call. Is this okay?
536
- globals = {'cls' : cls ,
537
+ def _frozen_get_del_attr (cls , fields , globals ):
538
+ locals = {'cls' : cls ,
537
539
'FrozenInstanceError' : FrozenInstanceError }
538
540
if fields :
539
541
fields_str = '(' + ',' .join (repr (f .name ) for f in fields ) + ',)'
@@ -545,17 +547,19 @@ def _frozen_get_del_attr(cls, fields):
545
547
(f'if type(self) is cls or name in { fields_str } :' ,
546
548
' raise FrozenInstanceError(f"cannot assign to field {name!r}")' ,
547
549
f'super(cls, self).__setattr__(name, value)' ),
550
+ locals = locals ,
548
551
globals = globals ),
549
552
_create_fn ('__delattr__' ,
550
553
('self' , 'name' ),
551
554
(f'if type(self) is cls or name in { fields_str } :' ,
552
555
' raise FrozenInstanceError(f"cannot delete field {name!r}")' ,
553
556
f'super(cls, self).__delattr__(name)' ),
557
+ locals = locals ,
554
558
globals = globals ),
555
559
)
556
560
557
561
558
- def _cmp_fn (name , op , self_tuple , other_tuple ):
562
+ def _cmp_fn (name , op , self_tuple , other_tuple , globals ):
559
563
# Create a comparison function. If the fields in the object are
560
564
# named 'x' and 'y', then self_tuple is the string
561
565
# '(self.x,self.y)' and other_tuple is the string
@@ -565,14 +569,16 @@ def _cmp_fn(name, op, self_tuple, other_tuple):
565
569
('self' , 'other' ),
566
570
[ 'if other.__class__ is self.__class__:' ,
567
571
f' return { self_tuple } { op } { other_tuple } ' ,
568
- 'return NotImplemented' ])
572
+ 'return NotImplemented' ],
573
+ globals = globals )
569
574
570
575
571
- def _hash_fn (fields ):
576
+ def _hash_fn (fields , globals ):
572
577
self_tuple = _tuple_str ('self' , fields )
573
578
return _create_fn ('__hash__' ,
574
579
('self' ,),
575
- [f'return hash({ self_tuple } )' ])
580
+ [f'return hash({ self_tuple } )' ],
581
+ globals = globals )
576
582
577
583
578
584
def _is_classvar (a_type , typing ):
@@ -744,14 +750,14 @@ def _set_new_attribute(cls, name, value):
744
750
# take. The common case is to do nothing, so instead of providing a
745
751
# function that is a no-op, use None to signify that.
746
752
747
- def _hash_set_none (cls , fields ):
753
+ def _hash_set_none (cls , fields , globals ):
748
754
return None
749
755
750
- def _hash_add (cls , fields ):
756
+ def _hash_add (cls , fields , globals ):
751
757
flds = [f for f in fields if (f .compare if f .hash is None else f .hash )]
752
- return _hash_fn (flds )
758
+ return _hash_fn (flds , globals )
753
759
754
- def _hash_exception (cls , fields ):
760
+ def _hash_exception (cls , fields , globals ):
755
761
# Raise an exception.
756
762
raise TypeError (f'Cannot overwrite attribute __hash__ '
757
763
f'in class { cls .__name__ } ' )
@@ -793,6 +799,16 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
793
799
# is defined by the base class, which is found first.
794
800
fields = {}
795
801
802
+ if cls .__module__ in sys .modules :
803
+ globals = sys .modules [cls .__module__ ].__dict__
804
+ else :
805
+ # Theoretically this can happen if someone writes
806
+ # a custom string to cls.__module__. In which case
807
+ # such dataclass won't be fully introspectable
808
+ # (w.r.t. typing.get_type_hints) but will still function
809
+ # correctly.
810
+ globals = {}
811
+
796
812
setattr (cls , _PARAMS , _DataclassParams (init , repr , eq , order ,
797
813
unsafe_hash , frozen ))
798
814
@@ -902,6 +918,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
902
918
# if possible.
903
919
'__dataclass_self__' if 'self' in fields
904
920
else 'self' ,
921
+ globals ,
905
922
))
906
923
907
924
# Get the fields as a list, and include only real fields. This is
@@ -910,7 +927,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
910
927
911
928
if repr :
912
929
flds = [f for f in field_list if f .repr ]
913
- _set_new_attribute (cls , '__repr__' , _repr_fn (flds ))
930
+ _set_new_attribute (cls , '__repr__' , _repr_fn (flds , globals ))
914
931
915
932
if eq :
916
933
# Create _eq__ method. There's no need for a __ne__ method,
@@ -920,7 +937,8 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
920
937
other_tuple = _tuple_str ('other' , flds )
921
938
_set_new_attribute (cls , '__eq__' ,
922
939
_cmp_fn ('__eq__' , '==' ,
923
- self_tuple , other_tuple ))
940
+ self_tuple , other_tuple ,
941
+ globals = globals ))
924
942
925
943
if order :
926
944
# Create and set the ordering methods.
@@ -933,13 +951,14 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
933
951
('__ge__' , '>=' ),
934
952
]:
935
953
if _set_new_attribute (cls , name ,
936
- _cmp_fn (name , op , self_tuple , other_tuple )):
954
+ _cmp_fn (name , op , self_tuple , other_tuple ,
955
+ globals = globals )):
937
956
raise TypeError (f'Cannot overwrite attribute { name } '
938
957
f'in class { cls .__name__ } . Consider using '
939
958
'functools.total_ordering' )
940
959
941
960
if frozen :
942
- for fn in _frozen_get_del_attr (cls , field_list ):
961
+ for fn in _frozen_get_del_attr (cls , field_list , globals ):
943
962
if _set_new_attribute (cls , fn .__name__ , fn ):
944
963
raise TypeError (f'Cannot overwrite attribute { fn .__name__ } '
945
964
f'in class { cls .__name__ } ' )
@@ -952,7 +971,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
952
971
if hash_action :
953
972
# No need to call _set_new_attribute here, since by the time
954
973
# we're here the overwriting is unconditional.
955
- cls .__hash__ = hash_action (cls , field_list )
974
+ cls .__hash__ = hash_action (cls , field_list , globals )
956
975
957
976
if not getattr (cls , '__doc__' ):
958
977
# Create a class doc-string.
0 commit comments