Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 718e866

Browse files
authored
gh-98886: Fix issues with dataclass fields with special underscore names (#102032)
This commit prefixes `__dataclass` to several things in the locals dict: - Names like `_dflt_` (which cause trouble, see first test) - Names like `_type_` (not known to be able to cause trouble) - `_return_type` (not known to able to cause trouble) - `_HAS_DEFAULT_FACTORY` (which causes trouble, see second test) In addition, this removes `MISSING` from the locals dict. As far as I can tell, this wasn't needed even in the initial implementation of dataclasses.py (and tests on that version passed with it removed). This makes me wary :-) This is basically a continuation of #96151, where fixing this was welcomed in #98143 (comment)
1 parent 027223d commit 718e866

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

Lib/dataclasses.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,8 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
432432
locals = {}
433433
return_annotation = ''
434434
if return_type is not MISSING:
435-
locals['_return_type'] = return_type
436-
return_annotation = '->_return_type'
435+
locals['__dataclass_return_type__'] = return_type
436+
return_annotation = '->__dataclass_return_type__'
437437
args = ','.join(args)
438438
body = '\n'.join(f' {b}' for b in body)
439439

@@ -467,14 +467,14 @@ def _field_init(f, frozen, globals, self_name, slots):
467467
# Return the text of the line in the body of __init__ that will
468468
# initialize this field.
469469

470-
default_name = f'_dflt_{f.name}'
470+
default_name = f'__dataclass_dflt_{f.name}__'
471471
if f.default_factory is not MISSING:
472472
if f.init:
473473
# This field has a default factory. If a parameter is
474474
# given, use it. If not, call the factory.
475475
globals[default_name] = f.default_factory
476476
value = (f'{default_name}() '
477-
f'if {f.name} is _HAS_DEFAULT_FACTORY '
477+
f'if {f.name} is __dataclass_HAS_DEFAULT_FACTORY__ '
478478
f'else {f.name}')
479479
else:
480480
# This is a field that's not in the __init__ params, but
@@ -535,11 +535,11 @@ def _init_param(f):
535535
elif f.default is not MISSING:
536536
# There's a default, this will be the name that's used to look
537537
# it up.
538-
default = f'=_dflt_{f.name}'
538+
default = f'=__dataclass_dflt_{f.name}__'
539539
elif f.default_factory is not MISSING:
540540
# There's a factory function. Set a marker.
541-
default = '=_HAS_DEFAULT_FACTORY'
542-
return f'{f.name}:_type_{f.name}{default}'
541+
default = '=__dataclass_HAS_DEFAULT_FACTORY__'
542+
return f'{f.name}:__dataclass_type_{f.name}__{default}'
543543

544544

545545
def _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init,
@@ -562,10 +562,9 @@ def _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init,
562562
raise TypeError(f'non-default argument {f.name!r} '
563563
'follows default argument')
564564

565-
locals = {f'_type_{f.name}': f.type for f in fields}
565+
locals = {f'__dataclass_type_{f.name}__': f.type for f in fields}
566566
locals.update({
567-
'MISSING': MISSING,
568-
'_HAS_DEFAULT_FACTORY': _HAS_DEFAULT_FACTORY,
567+
'__dataclass_HAS_DEFAULT_FACTORY__': _HAS_DEFAULT_FACTORY,
569568
'__dataclass_builtins_object__': object,
570569
})
571570

Lib/test/test_dataclasses.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,23 @@ class C:
285285
c = C(5)
286286
self.assertEqual(c.BUILTINS, 5)
287287

288+
def test_field_with_special_single_underscore_names(self):
289+
# gh-98886
290+
291+
@dataclass
292+
class X:
293+
x: int = field(default_factory=lambda: 111)
294+
_dflt_x: int = field(default_factory=lambda: 222)
295+
296+
X()
297+
298+
@dataclass
299+
class Y:
300+
y: int = field(default_factory=lambda: 111)
301+
_HAS_DEFAULT_FACTORY: int = 222
302+
303+
assert Y(y=222).y == 222
304+
288305
def test_field_named_like_builtin(self):
289306
# Attribute names can shadow built-in names
290307
# since code generation is used.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix issues when defining dataclasses that have fields with specific underscore names that aren't clearly reserved by :mod:`dataclasses`.

0 commit comments

Comments
 (0)