Repository: pydantic/pydantic
Pull request: Do not use deprecated methods as default field values
<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->

## Change Summary

Fixes https://github.com/pydantic/pydantic/issues/11912.

<!-- Please give a short summary of the changes. -->

## Related issue number

<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->

## Checklist

* [ ] The pull request title is a good summary of the changes - it will be used in the changelog
* [ ] Unit tests for the changes exist
* [ ] Tests pass on CI
* [ ] Documentation reflects the changes where applicable
* [ ] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**


Problem statement:
Adding a field named `schema` results in the @schema property being sneakily added as the default value
### Initial Checks

- [x] I confirm that I'm using Pydantic V2

### Description

The code below outputs
```
{'schema': FieldInfo(annotation=int, required=False, default=<bound method BaseModel.schema of <class '__main__.Test'>>)}
```

I know we are not supposed to have a `schema` named field and that this becomes automatically resolved in V3. But I do need a `schema` field, and I discovered that it results in a sneaky default value being added.



### Example Code

```Python
from pydantic import BaseModel

class Test(BaseModel):
    schema: int

Test.model_fields
```

### Python, Pydantic & OS Version

```Text
pydantic version: 2.11.4
        pydantic-core version: 2.33.2
          pydantic-core build: profile=release pgo=false
               python version: 3.11.12 (main, Apr  9 2025, 03:49:53) [Clang 20.1.0 ]
                     platform: macOS-15.4.1-arm64-arm-64bit
             related packages: typing_extensions-4.13.2
                       commit: unknown
```

Hints from issue discussion:
The name `schema` is reserved and should not be used as a field name:

```bash
UserWarning: Field name "schema" in "Test" shadows an attribute in parent "BaseModel"
```

The default value is inherited from `BaseModel`.

For now you can do something like
```python
    schema: int = Field(default_factory=int)
    # or
    schema: int | None = None
```

Commit message: Do not use deprecated methods as default field values

Patch to review:
diff --git a/pydantic/_internal/_fields.py b/pydantic/_internal/_fields.py
index f81968c448..8b96e2c41a 100644
--- a/pydantic/_internal/_fields.py
+++ b/pydantic/_internal/_fields.py
@@ -199,6 +199,22 @@ def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, fie
         _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info)
 
 
+_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'}
+
+_deprecated_classmethod_names = {
+    'parse_obj',
+    'parse_raw',
+    'parse_file',
+    'from_orm',
+    'construct',
+    'schema',
+    'schema_json',
+    'validate',
+    'update_forward_refs',
+    '_get_value',
+}
+
+
 def collect_model_fields(  # noqa: C901
     cls: type[BaseModel],
     config_wrapper: ConfigWrapper,
@@ -231,6 +247,7 @@ def collect_model_fields(  # noqa: C901
             - If a field shadows an attribute in the parent model.
     """
     FieldInfo_ = import_cached_field_info()
+    BaseModel_ = import_cached_base_model()
 
     bases = cls.__bases__
     parent_fields_lookup: dict[str, FieldInfo] = {}
@@ -265,6 +282,20 @@ def collect_model_fields(  # noqa: C901
             continue
 
         assigned_value = getattr(cls, ann_name, PydanticUndefined)
+        if (
+            # One of the deprecated instance methods was used as a field name (e.g. `dict()`):
+            any(getattr(BaseModel_, depr_name, None) is assigned_value for depr_name in _deprecated_method_names)
+            # One of the deprecated class methods was used as a field name (e.g. `schema()`):
+            or (
+                hasattr(assigned_value, '__func__')
+                and any(
+                    getattr(getattr(BaseModel_, depr_name, None), '__func__', None) is assigned_value.__func__  # pyright: ignore[reportAttributeAccessIssue]
+                    for depr_name in _deprecated_classmethod_names
+                )
+            )
+        ):
+            # Then `assigned_value` would be the method, even though no default was specified:
+            assigned_value = PydanticUndefined
 
         if not is_valid_field_name(ann_name):
             continue
diff --git a/tests/test_main.py b/tests/test_main.py
index faeb30b579..accd2ccca5 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -3498,6 +3498,21 @@ class ChildWithRedefinedField(BaseModel, Parent):
             foo: bool = True
 
 
+def test_field_name_deprecated_method_name() -> None:
+    """https://github.com/pydantic/pydantic/issues/11912"""
+
+    with pytest.warns(UserWarning):
+
+        class Model(BaseModel):
+            # `collect_model_fields()` will special case these to not use
+            # the deprecated methods as default values:
+            dict: int
+            schema: str
+
+        assert Model.model_fields['dict'].is_required()
+        assert Model.model_fields['schema'].is_required()
+
+
 def test_eval_type_backport():
     class Model(BaseModel):
         foo: 'list[int | str]'


Code review comments:
diff_hunk: @@ -265,6 +282,20 @@ def collect_model_fields(  # noqa: C901
             continue
 
         assigned_value = getattr(cls, ann_name, PydanticUndefined)
+        if (
path: pydantic/_internal/_fields.py
text: @user1:
I think we can make this a bit more performant by skipping the checks if `assigned_values is PydanticUndefined`