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