Skip to content

Raise warnings when deprecated fields are filled at model instantiation #1551

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions python/pydantic_core/core_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2944,6 +2944,7 @@ class ModelField(TypedDict, total=False):
serialization_alias: str
serialization_exclude: bool # default: False
frozen: bool
deprecation_msg: str
metadata: Dict[str, Any]


Expand All @@ -2954,6 +2955,7 @@ def model_field(
serialization_alias: str | None = None,
serialization_exclude: bool | None = None,
frozen: bool | None = None,
deprecation_msg: str | None = None,
metadata: Dict[str, Any] | None = None,
) -> ModelField:
"""
Expand All @@ -2971,6 +2973,7 @@ def model_field(
serialization_alias: The alias to use as a key when serializing
serialization_exclude: Whether to exclude the field when serializing
frozen: Whether the field is frozen
deprecation_msg: A deprecation message indicating that the field is deprecated. `None` means that the field is not deprecated.
metadata: Any other information you want to include with the schema, not used by pydantic-core
"""
return _dict_not_none(
Expand All @@ -2980,6 +2983,7 @@ def model_field(
serialization_alias=serialization_alias,
serialization_exclude=serialization_exclude,
frozen=frozen,
deprecation_msg=deprecation_msg,
metadata=metadata,
)

Expand Down
6 changes: 6 additions & 0 deletions src/validators/model_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct Field {
name_py: Py<PyString>,
validator: CombinedValidator,
frozen: bool,
deprecation_msg: Option<String>,
}

impl_py_gc_traverse!(Field { validator });
Expand Down Expand Up @@ -92,6 +93,7 @@ impl BuildValidator for ModelFieldsValidator {
name_py: field_name_py.into(),
validator,
frozen: field_info.get_as::<bool>(intern!(py, "frozen"))?.unwrap_or(false),
deprecation_msg: field_info.get_as::<String>(intern!(py, "deprecation_msg"))?,
});
}

Expand Down Expand Up @@ -184,6 +186,10 @@ impl Validator for ModelFieldsValidator {
// extra logic either way
used_keys.insert(lookup_path.first_key());
}
if let Some(msg) = &field.deprecation_msg {
let deprecation_warning_type = py.import_bound("builtins")?.getattr("DeprecationWarning")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try this instead:

Suggested change
let deprecation_warning_type = py.import_bound("builtins")?.getattr("DeprecationWarning")?;
let deprecation_warning_type = pyo3::exceptions::PyDeprecationWarning::type_object_bound(py);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. This works. Thanks! There's another UserWarning imported in the same way somewhere in the code base. I can create another PR to change that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be a great follow-up, yes please 👍

PyErr::warn_bound(py, &deprecation_warning_type, msg, 2)?;
}
match field.validator.validate(py, value.borrow_input(), state) {
Ok(value) => {
model_dict.set_item(&field.name_py, value)?;
Expand Down
32 changes: 32 additions & 0 deletions tests/validators/test_model_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1781,3 +1781,35 @@ def test_extra_behavior_ignore(config: Union[core_schema.CoreConfig, None], sche
}
]
assert 'not_f' not in m


def test_deprecation_msg():
v = SchemaValidator(
{
'type': 'model-fields',
'fields': {
'a': {'type': 'model-field', 'schema': {'type': 'int'}},
'b': {
'type': 'model-field',
'schema': {'type': 'default', 'schema': {'type': 'int'}, 'default': 2},
'deprecation_msg': 'foo',
},
'c': {
'type': 'model-field',
'schema': {'type': 'default', 'schema': {'type': 'int'}, 'default': 2},
'deprecation_msg': 'bar',
},
},
}
)

# not touching the deprecated field: no warning
v.validate_python({'a': 1})

# validating the deprecated field: raise warning
# ensure that we get two warnings
with pytest.warns(DeprecationWarning) as w:
v.validate_python({'a': 1, 'b': 1, 'c': 1})
assert len(w) == 2
assert str(w[0].message) == 'foo'
assert str(w[1].message) == 'bar'