Skip to content
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

Make shadowing attributes a warning instead of an error #7193

Merged
merged 2 commits into from Aug 22, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions pydantic/_internal/_fields.py
Expand Up @@ -151,9 +151,9 @@ def collect_model_fields( # noqa: C901
if base is generic_origin:
# Don't error when "shadowing" of attributes in parametrized generics
continue
raise NameError(
f'Field name "{ann_name}" shadows an attribute in parent "{base.__qualname__}"; '
f'you might want to use a different field name with "alias=\'{ann_name}\'".'
warnings.warn(
f'Field name "{ann_name}" shadows an attribute in parent "{base.__qualname__}"; ',
UserWarning,
)

try:
Expand Down
35 changes: 35 additions & 0 deletions tests/test_main.py
Expand Up @@ -2887,3 +2887,38 @@ class Model(BaseModel):
'ctx': {'max_length': 1},
}
]


def test_shadow_attribute() -> None:
"""https://github.com/pydantic/pydantic/issues/7108"""

class Model(BaseModel):
foo: str

@classmethod
def __pydantic_init_subclass__(cls, **kwargs: Any):
super().__pydantic_init_subclass__(**kwargs)
for key in cls.model_fields.keys():
setattr(cls, key, getattr(cls, key, '') + ' edited!')

class One(Model):
foo: str = 'abc'

with pytest.warns(UserWarning, match=r'"foo" shadows an attribute in parent ".*One"'):

class Two(One):
foo: str

with pytest.warns(UserWarning, match=r'"foo" shadows an attribute in parent ".*One"'):

class Three(One):
foo: str = 'xyz'

# unlike dataclasses BaseModel does not preserve the value of defaults
# so when we access the attribute in `Model.__pydantic_init_subclass__` there is no default
# and hence we append `edited!` to an empty string
# we've talked about changing this but this is the current behavior as of this test
assert getattr(Model, 'foo', None) is None
assert getattr(One, 'foo', None) == ' edited!'
assert getattr(Two, 'foo', None) == ' edited! edited!'
assert getattr(Three, 'foo', None) == ' edited! edited!'