8000 [3.7] bpo-32896: Fix error when subclassing a dataclass with a field that uses a default_factory (GH-6170) by miss-islington · Pull Request #6171 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

[3.7] bpo-32896: Fix error when subclassing a dataclass with a field that uses a default_factory (GH-6170) #6171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,17 +574,18 @@ def _get_field(cls, a_name, a_type):

def _find_fields(cls):
# Return a list of Field objects, in order, for this class (and no
# base classes). Fields are found from __annotations__ (which is
# guaranteed to be ordered). Default values are from class
# attributes, if a field has a default. If the default value is
# a Field(), then it contains additional info beyond (and
# possibly including) the actual default value. Pseudo-fields
# ClassVars and InitVars are included, despite the fact that
# they're not real fields. That's dealt with later.

annotations = getattr(cls, '__annotations__', {})
return [_get_field(cls, a_name, a_type)
for a_name, a_type in annotations.items()]
# base classes). Fields are found from the class dict's
# __annotations__ (which is guaranteed to be ordered). Default
# values are from class attributes, if a field has a default. If
# the default value is a Field(), then it contains additional
# info beyond (and possibly including) the actual default value.
# Pseudo-fields ClassVars and InitVars are included, despite the
# fact that they're not real fields. That's dealt with later.

# If __annotations__ isn't present, then this class adds no new
# annotations.
annotations = cls.__dict__.get('__annotations__', {})
return [_get_field(cls, name, type) for name, type in annotations.items()]


def _set_new_attribute(cls, name, value):
Expand Down
49 changes: 49 additions & 0 deletions Lib/test/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,55 @@ class C:
C().x
self.assertEqual(factory.call_count, 2)

def test_default_factory_derived(self):
# See bpo-32896.
@dataclass
class Foo:
x: dict = field(default_factory=dict)

@dataclass
class Bar(Foo):
y: int = 1

self.assertEqual(Foo().x, {})
self.assertEqual(Bar().x, {})
self.assertEqual(Bar().y, 1)

@dataclass
class Baz(Foo):
pass
self.assertEqual(Baz().x, {})

def test_intermediate_non_dataclass(self):
# Test that an intermediate class that defines
# annotations does not define fields.

@dataclass
class A:
x: int

class B(A):
y: int

@dataclass
class C(B):
z: int

c = C(1, 3)
self.assertEqual((c.x, c.z), (1, 3))

# .y was not initialized.
with self.assertRaisesRegex(AttributeError,
'object has no attribute'):
c.y

# And if we again derive a non-dataclass, no fields are added.
class D(C):
t: int
d = D(4, 5)
self.assertEqual((d.x, d.z), (4, 5))


def x_test_classvar_default_factory(self):
# XXX: it's an error for a ClassVar to have a factory function
@dataclass
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix an error where subclassing a dataclass with a field that uses a
default_factory would generate an incorrect class.
0