Skip to content

Commit

Permalink
Do not warn about shadowed fields if they are not redefined in a chil…
Browse files Browse the repository at this point in the history
…d class (#9111)
  • Loading branch information
chan-vince committed Mar 26, 2024
1 parent f22c8b0 commit 09d2036
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
11 changes: 8 additions & 3 deletions pydantic/_internal/_fields.py
Expand Up @@ -177,21 +177,26 @@ def collect_model_fields( # noqa: C901
)

# when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
# "... shadows an attribute" errors
# "... shadows an attribute" warnings
generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
for base in bases:
dataclass_fields = {
field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
}
if hasattr(base, ann_name):
if base is generic_origin:
# Don't error when "shadowing" of attributes in parametrized generics
# Don't warn when "shadowing" of attributes in parametrized generics
continue

if ann_name in dataclass_fields:
# Don't error when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
# Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
# on the class instance.
continue

if ann_name not in annotations:
# Don't warn when a field exists in a parent class but has not been defined in the current class
continue

warnings.warn(
f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
f'"{base.__qualname__}"',
Expand Down
32 changes: 32 additions & 0 deletions tests/test_main.py
@@ -1,6 +1,7 @@
import json
import platform
import re
import warnings
from collections import defaultdict
from copy import deepcopy
from dataclasses import dataclass
Expand Down Expand Up @@ -3151,6 +3152,37 @@ class Three(One):
assert getattr(Three, 'foo', None) == ' edited! edited!'


def test_shadow_attribute_warn_for_redefined_fields() -> None:
"""https://github.com/pydantic/pydantic/issues/9107"""

# A simple class which defines a field
class Parent:
foo: bool = False

# When inheriting from the parent class, as long as the field is not defined at all, there should be no warning
# about shadowed fields.
with warnings.catch_warnings(record=True) as captured_warnings:
# Start capturing all warnings
warnings.simplefilter('always')

class ChildWithoutRedefinedField(BaseModel, Parent):
pass

# Check that no warnings were captured
assert len(captured_warnings) == 0

# But when inheriting from the parent class and a parent field is redefined, a warning should be raised about
# shadowed fields irrespective of whether it is defined with a type that is still compatible or narrower, or
# with a different default that is still compatible with the type definition.
with pytest.warns(
UserWarning,
match=r'"foo" in ".*ChildWithRedefinedField" shadows an attribute in parent ".*Parent"',
):

class ChildWithRedefinedField(BaseModel, Parent):
foo: bool = True


def test_eval_type_backport():
class Model(BaseModel):
foo: 'list[int | str]'
Expand Down

0 comments on commit 09d2036

Please sign in to comment.