@@ -378,23 +378,24 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
378
378
# worries about external callers.
379
379
if locals is None :
380
380
locals = {}
381
- # __builtins__ may be the "builtins" module or
382
- # the value of its "__dict__",
383
- # so make sure "__builtins__" is the module.
384
- if globals is not None and '__builtins__' not in globals :
385
- globals ['__builtins__' ] = builtins
381
+ if 'BUILTINS' not in locals :
382
+ locals ['BUILTINS' ] = builtins
386
383
return_annotation = ''
387
384
if return_type is not MISSING :
388
385
locals ['_return_type' ] = return_type
389
386
return_annotation = '->_return_type'
390
387
args = ',' .join (args )
391
- body = '\n ' .join (f' { b } ' for b in body )
388
+ body = '\n ' .join (f' { b } ' for b in body )
392
389
393
390
# Compute the text of the entire function.
394
- txt = f'def { name } ({ args } ){ return_annotation } :\n { body } '
391
+ txt = f' def { name } ({ args } ){ return_annotation } :\n { body } '
395
392
396
- exec (txt , globals , locals )
397
- return locals [name ]
393
+ local_vars = ', ' .join (locals .keys ())
394
+ txt = f"def __create_fn__({ local_vars } ):\n { txt } \n return { name } "
395
+
396
+ ns = {}
397
+ exec (txt , globals , ns )
398
+ return ns ['__create_fn__' ](** locals )
398
399
399
400
400
401
def _field_assign (frozen , name , value , self_name ):
@@ -405,7 +406,7 @@ def _field_assign(frozen, name, value, self_name):
405
406
# self_name is what "self" is called in this function: don't
406
407
# hard-code "self", since that might be a field name.
407
408
if frozen :
408
- return f'__builtins__ .object.__setattr__({ self_name } ,{ name !r} ,{ value } )'
409
+ return f'BUILTINS .object.__setattr__({ self_name } ,{ name !r} ,{ value } )'
409
410
return f'{ self_name } .{ name } ={ value } '
410
411
411
412
@@ -482,7 +483,7 @@ def _init_param(f):
482
483
return f'{ f .name } :_type_{ f .name } { default } '
483
484
484
485
485
- def _init_fn (fields , frozen , has_post_init , self_name ):
486
+ def _init_fn (fields , frozen , has_post_init , self_name , globals ):
486
487
# fields contains both real fields and InitVar pseudo-fields.
487
488
488
489
# Make sure we don't have fields without defaults following fields
@@ -500,12 +501,15 @@ def _init_fn(fields, frozen, has_post_init, self_name):
500
501
raise TypeError (f'non-default argument { f .name !r} '
501
502
'follows default argument' )
502
503
503
- globals = {'MISSING' : MISSING ,
504
- '_HAS_DEFAULT_FACTORY' : _HAS_DEFAULT_FACTORY }
504
+ locals = {f'_type_{ f .name } ' : f .type for f in fields }
505
+ locals .update ({
506
+ 'MISSING' : MISSING ,
507
+ '_HAS_DEFAULT_FACTORY' : _HAS_DEFAULT_FACTORY ,
508
+ })
505
509
506
510
body_lines = []
507
511
for f in fields :
508
- line = _field_init (f , frozen , globals , self_name )
512
+ line = _field_init (f , frozen , locals , self_name )
509
513
# line is None means that this field doesn't require
510
514
# initialization (it's a pseudo-field). Just skip it.
511
515
if line :
@@ -521,7 +525,6 @@ def _init_fn(fields, frozen, has_post_init, self_name):
521
525
if not body_lines :
522
526
body_lines = ['pass' ]
523
527
524
- locals = {f'_type_{ f .name } ' : f .type for f in fields }
525
528
return _create_fn ('__init__' ,
526
529
[self_name ] + [_init_param (f ) for f in fields if f .init ],
527
530
body_lines ,
@@ -530,20 +533,19 @@ def _init_fn(fields, frozen, has_post_init, self_name):
530
533
return_type = None )
531
534
532
535
533
- def _repr_fn (fields ):
536
+ def _repr_fn (fields , globals ):
534
537
fn = _create_fn ('__repr__' ,
535
538
('self' ,),
536
539
['return self.__class__.__qualname__ + f"(' +
537
540
', ' .join ([f"{ f .name } ={{self.{ f .name } !r}}"
538
541
for f in fields ]) +
539
- ')"' ])
542
+ ')"' ],
543
+ globals = globals )
540
544
return _recursive_repr (fn )
541
545
542
546
543
- def _frozen_get_del_attr (cls , fields ):
544
- # XXX: globals is modified on the first call to _create_fn, then
545
- # the modified version is used in the second call. Is this okay?
546
- globals = {'cls' : cls ,
547
+ def _frozen_get_del_attr (cls , fields , globals ):
548
+ locals = {'cls' : cls ,
547
549
'FrozenInstanceError' : FrozenInstanceError }
548
550
if fields :
549
551
fields_str = '(' + ',' .join (repr (f .name ) for f in fields ) + ',)'
@@ -555,17 +557,19 @@ def _frozen_get_del_attr(cls, fields):
555
557
(f'if type(self) is cls or name in { fields_str } :' ,
556
558
' raise FrozenInstanceError(f"cannot assign to field {name!r}")' ,
557
559
f'super(cls, self).__setattr__(name, value)' ),
560
+ locals = locals ,
558
561
globals = globals ),
559
562
_create_fn ('__delattr__' ,
560
563
('self' , 'name' ),
561
564
(f'if type(self) is cls or name in { fields_str } :' ,
562
565
' raise FrozenInstanceError(f"cannot delete field {name!r}")' ,
563
566
f'super(cls, self).__delattr__(name)' ),
567
+ locals = locals ,
564
568
globals = globals ),
565
569
)
566
570
567
571
568
- def _cmp_fn (name , op , self_tuple , other_tuple ):
572
+ def _cmp_fn (name , op , self_tuple , other_tuple , globals ):
569
573
# Create a comparison function. If the fields in the object are
570
574
# named 'x' and 'y', then self_tuple is the string
571
575
# '(self.x,self.y)' and other_tuple is the string
@@ -575,14 +579,16 @@ def _cmp_fn(name, op, self_tuple, other_tuple):
575
579
('self' , 'other' ),
576
580
[ 'if other.__class__ is self.__class__:' ,
577
581
f' return { self_tuple } { op } { other_tuple } ' ,
578
- 'return NotImplemented' ])
582
+ 'return NotImplemented' ],
583
+ globals = globals )
579
584
580
585
581
- def _hash_fn (fields ):
586
+ def _hash_fn (fields , globals ):
582
587
self_tuple = _tuple_str ('self' , fields )
583
588
return _create_fn ('__hash__' ,
584
589
('self' ,),
585
- [f'return hash({ self_tuple } )' ])
590
+ [f'return hash({ self_tuple } )' ],
591
+ globals = globals )
586
592
587
593
588
594
def _is_classvar (a_type , typing ):
@@ -755,14 +761,14 @@ def _set_new_attribute(cls, name, value):
755
761
# take. The common case is to do nothing, so instead of providing a
756
762
# function that is a no-op, use None to signify that.
757
763
758
- def _hash_set_none (cls , fields ):
764
+ def _hash_set_none (cls , fields , globals ):
759
765
return None
760
766
761
- def _hash_add (cls , fields ):
767
+ def _hash_add (cls , fields , globals ):
762
768
flds = [f for f in fields if (f .compare if f .hash is None else f .hash )]
763
- return _hash_fn (flds )
769
+ return _hash_fn (flds , globals )
764
770
765
- def _hash_exception (cls , fields ):
771
+ def _hash_exception (cls , fields , globals ):
766
772
# Raise an exception.
767
773
raise TypeError (f'Cannot overwrite attribute __hash__ '
768
774
f'in class { cls .__name__ } ' )
@@ -804,6 +810,16 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
804
810
# is defined by the base class, which is found first.
805
811
fields = {}
806
812
813
+ if cls .__module__ in sys .modules :
814
+ globals = sys .modules [cls .__module__ ].__dict__
815
+ else :
816
+ # Theoretically this can happen if someone writes
817
+ # a custom string to cls.__module__. In which case
818
+ # such dataclass won't be fully introspectable
819
+ # (w.r.t. typing.get_type_hints) but will still function
820
+ # correctly.
821
+ globals = {}
822
+
807
823
setattr (cls , _PARAMS , _DataclassParams (init , repr , eq , order ,
808
824
unsafe_hash , frozen ))
809
825
@@ -913,6 +929,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
913
929
# if possible.
914
930
'__dataclass_self__' if 'self' in fields
915
931
else 'self' ,
932
+ globals ,
916
933
))
917
934
918
935
# Get the fields as a list, and include only real fields. This is
@@ -921,7 +938,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
921
938
922
939
if repr :
923
940
flds = [f for f in field_list if f .repr ]
924
- _set_new_attribute (cls , '__repr__' , _repr_fn (flds ))
941
+ _set_new_attribute (cls , '__repr__' , _repr_fn (flds , globals ))
925
942
926
943
if eq :
927
944
# Create _eq__ method. There's no need for a __ne__ method,
@@ -931,7 +948,8 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
931
948
other_tuple = _tuple_str ('other' , flds )
932
949
_set_new_attribute (cls , '__eq__' ,
933
950
_cmp_fn ('__eq__' , '==' ,
934
- self_tuple , other_tuple ))
951
+ self_tuple , other_tuple ,
952
+ globals = globals ))
935
953
936
954
if order :
937
955
# Create and set the ordering methods.
@@ -944,13 +962,14 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
944
962
('__ge__' , '>=' ),
945
963
]:
946
964
if _set_new_attribute (cls , name ,
947
- _cmp_fn (name , op , self_tuple , other_tuple )):
965
+ _cmp_fn (name , op , self_tuple , other_tuple ,
966
+ globals = globals )):
948
967
raise TypeError (f'Cannot overwrite attribute { name } '
949
968
f'in class { cls .__name__ } . Consider using '
950
969
'functools.total_ordering' )
951
970
952
971
if frozen :
953
- for fn in _frozen_get_del_attr (cls , field_list ):
972
+ for fn in _frozen_get_del_attr (cls , field_list , globals ):
954
973
if _set_new_attribute (cls , fn .__name__ , fn ):
955
974
raise TypeError (f'Cannot overwrite attribute { fn .__name__ } '
956
975
f'in class { cls .__name__ } ' )
@@ -963,7 +982,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
963
982
if hash_action :
964
983
# No need to call _set_new_attribute here, since by the time
965
984
# we're here the overwriting is unconditional.
966
- cls .__hash__ = hash_action (cls , field_list )
985
+ cls .__hash__ = hash_action (cls , field_list , globals )
967
986
968
987
if not getattr (cls , '__doc__' ):
969
988
# Create a class doc-string.
0 commit comments