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
Add support for deprecated fields #8237
Conversation
CodSpeed Performance ReportMerging #8237 will not alter performanceComparing Summary
|
Please review (still in draft because I'm waiting for a stable release of typing extensions) |
Changes are looking good overall thus far. My initial thoughts:
Yes, we want to include
The fewer new parameters we add to # either
some_field = Field(deprecated=Deprecated(True, msg='blah blah')
# or
some_field = Field(deprecated=True) Reminds me a bit of the support we have for
I think yes, but let's see what @samuelcolvin thinks. Additionally, we'll need to update both the API docs and the concepts docs with information about the various ways in which to use Depending on when |
Isn't having
If having
I'll probably add them once we get the implementation sorted out. Would be nice to also support this on models: from pydantic import BaseModel
from typing_extensions import deprecated
@deprecated("msg") # Either with the decorator or model_config
class Model(BaseModel):
pass
model_config = {'deprecated': True}
Model.model_json_schema()
#> {'properties': {}, 'title': 'Model', 'type': 'object', 'deprecated': True} Probably better in another PR, but would be nice to have both features landing in the same version. |
That's fair, it's certainly redundant. I'm more in favor of a Let's wait and see what @dmontagu thinks about this API. Don't want to have you do more work than necessary. Sounds good re docs 👍. I agree, that'd be a nice feature on models. Indeed, a separate PR sounds like a good plan. |
@Viicos if we are going to do this, I'd be inclined to try to match the API for Is that reasonable to you? I know the decorator from that PEP has some keyword arguments that affect the emitted warnings, but I think trying to support those (not to mention emitting runtime warnings when accessing the field) is outside the scope of this feature request/implementation right now. (If we want to consider doing that, I think it would merit a bigger design discussion.) That said, I'll just note that I think we'll regret adding support for this if |
Sounds good.
Yes, I think this could be implemented later. I think for now I'll just implement emitting
will emit a
While I agree on this, no one has started a PEP related to this feature (or the hypothetical Having it implemented in Pydantic could also be an inspiration to implement it in dataclasses (e.g. in the same way it is currently done here: via |
I didn't realize the The main question for me is whether it is worth allowing instances of this class to work as a standalone annotation to mark a field as deprecated, or if we should just require an annotation of |
Nevermind, I see you added support for it as a plain annotation and it was basically trivial 👍 |
Yes thanks to Zac-HD who proposed a clever workaround ;) |
I think we should not filter deprecation warnings when calling validators. E.g., consider the case where a class LibraryModel(BaseModel):
a: Annotated[int, deprecated("use 'a_new'")] = 2
a_new: int = 5
class UserModel(LibraryModel):
@model_validator(mode="after")
def validate_model(self) -> Self:
self.a = self.a * 2 In this case I think it would be rather unfortunate if the user didn't see a deprecation warning (and failed to update their logic as appropriate!) because it was suppressed, thinking the user knew the field was deprecated. Even if I am just only working in a single package, it still feels problematic to me that I don't get notified about my use of a deprecated field just because Pydantic thought I knew what I was doing and was accessing it on purpose. (I may well not have been.) Even if there is some way to handle it that we think works slightly better in some cases, it sounds to me like it would require a decent amount of complexity, could still have surprising edge cases, and I just don't think that asking users to do the following is that high a price: import warnings
...
@model_validator(mode="after")
def validate_model(self) -> Self:
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
self.a = self.a * 2
self.a_new = self.a_new * 2.5 It just seems to me that if you have the ability to mark a field as deprecated, surely you can either modify the validator as above, or it's in someone else's code where they should be made aware the field is deprecated. (And again, if you can modify the validator, it seems just as likely to me that, in a big enough codebase, you might miss that the field is deprecated and that you should.) |
I think that, assuming this PR isn't already doing that, let's leave that (and any accompanying debate) for a separate PR and just get this merged. |
This is true, and considering we can't really know if the model is user-defined (and in that case we should raise the warning) or provided by a library, having the warning emitted seems to be the best option (and as you said a filter can still be added in the model validator!) |
@Viicos - last thing - could you please add a note to the docs suggesting how a user might filter / catch the warning, if desired? Other than that, looks great! |
Yes forgot about this. How should we ago about the fastapi issues? Should we disable the CI check for now while I work on it? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Truly amazing work!! 🚀
Re the FastAPI issues - why are the tests failing? Do you expect the fix to be relatively minor? If so, we could use xfails on those tests, but I want to better understand the reasoning behind the failed tests before we merge! |
You will find more context in the FastAPI issue, but basically FastAPI uses a
I'm not sure how it will be handled yet, but I don't expect it to be too much work. Will also depend on how it will get reviewed. |
Ah ok, thanks for linking the issue with more clarification. I don't think this is a case where we can just skip the FastAPI tests. I'm going to take a quick look + see if we can make a few minor changes on the FastAPI repo to guarantee that the existing behavior there is still preserved, while also supporting this new field on the |
Ah ok so at a first glance, this fixes the fastapi tests:
I could very well be overlooking something here, but if the change is this minimal, I'm feeling optimistic. |
Thanks for taking a look a it. My concern is that FastAPI currently defines deprecated: Optional[bool] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
**extra: Any,
):
self.deprecated = deprecated As FastAPI doesn't support |
Yeah, this seems like a better approach, given the type mismatch. |
Let's see what @dmontagu thinks, as well. |
Alright, so the conclusion was that even though this goes against our original thinking here, the easiest thing would be to support I'll push this change shortly, along with a few tests, and flipping the fastapi tests check back on! |
Change Summary
Starting implementing #2255
The new
deprecated
decorator could now be used as an annotation to mark fields as deprecated. The CPython implementation got tweaked to allow introspection by defining the decorator as a class instead as a function.This PR is of course subject to change depending on how you want this to be implemented in Pydantic.
The tests basically show how it is possible to mark a field as being deprecated. A couple questions:
deprecated
to be used as an argument toField
/computed_field
(as it is currently in this PR)? Or should we only allowAnnotated[..., deprecated(...)]
/@deprecated(...)
on a property?msg
to be used here as well? (instead ofbool
currently).computed_field
)Additional context: annotated-types/annotated-types#34 (comment)
Related issue number
Checklist
Selected Reviewer: @sydney-runkle