-
Couldn't load subscription status.
- Fork 32
⬆️ Upgrade models-library (pydantic v2) #6333
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
⬆️ Upgrade models-library (pydantic v2) #6333
Conversation
packages/models-library/src/models_library/api_schemas_directorv2/dynamic_services_service.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/rabbitmq_messages.py
Outdated
Show resolved
Hide resolved
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.
Thanks a lot, looks good! 💯 I have added few questions.
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.
I guess this is still in progress.
There are missing model examples and a =None that might have been added by the migration tool?
Happy to review again once these are in. thanks a lot for the good work!!!
packages/models-library/src/models_library/api_schemas_clusters_keeper/clusters.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/clusters.py
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/clusters.py
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/clusters.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/clusters.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/dynamic_services.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/dynamic_services.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/health.py
Outdated
Show resolved
Hide resolved
|
Thanks a lot for your feedback, the PR is still in progress :) All your precious observations are already under control. |
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.
this is my initial round. I'd like to talk about this PR in person if possible. there are some things that I would like to understand .
Also it's extreenmly hard to review
packages/models-library/src/models_library/api_schemas_catalog/services.py
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_catalog/services.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_clusters_keeper/clusters.py
Outdated
Show resolved
Hide resolved
packages/models-library/src/models_library/api_schemas_directorv2/clusters.py
Outdated
Show resolved
Hide resolved
| class Config(ProjectAtDB.Config): | ||
| extra = Extra.forbid | ||
|
|
||
| # TODO: missing in ProjectAtDB |
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.
can you please check if this comment has any relevance and just drop it if not?
| with pytest.raises(KeyError, match="missing"): | ||
| str(MyErrorBefore(value=42)) |
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.
what about this example? why was it removed?
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.
MyErrorBefore inherited from PydanticErrorMixin that in Pydantic v2 changed completely.
|
|
||
| def test_schema(): | ||
| assert MyModel.schema() == { | ||
| assert MyModel.model_json_schema() == { |
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.
looks like your changes broke the test. Are you sure?
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.
Actually this tests is showing that now pydantic NICELY distinguishes nullable fields. NOTE
class MyModel(BaseModel):
a: int
b: int | None = Field(...)
c: int = 42
d: int | None = None
e: int = FieldNotRequired(description="optional non-nullable")And we should get an schema that defines:
ais a requiredintbis a required eitherintorNullablecis an optionalintthat defaults to42dis an optional eitherintorNullablethat defaults toNoneeis an optionalint(if not set, it just gives youNone)
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.
@GitHK: change is correct.
The method "schema" in class "BaseModel" is deprecated
Theschemamethod is deprecated; usemodel_json_schemainstead.
…/services.py Co-authored-by: Andrei Neagu <[email protected]>
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.
Bravo! I left some comments and questions. Also
On my side this is good enough for a first merge. We can further refine.
packages/models-library/src/models_library/api_schemas_catalog/services_specifications.py
Outdated
Show resolved
Hide resolved
| if v is None: | ||
| cluster_type = values["type"] | ||
| default_thumbnails = { | ||
| ClusterTypeInModel.AWS.value: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/250px-Amazon_Web_Services_Logo.svg.png", |
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.
THOUGHT: wouldn't it make more sense to have links here to assets that we control? they could indeed be a copy of these (if allowed). At least we make sure nobody changes the source
e.g. we have https://github.com/ITISFoundation/osparc-assets repo where we could add default assets link these
| @validate_arguments | ||
| @validate_call | ||
| def update( | ||
| self, |
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.
I have the impression that here self is also validated. Do we want this? I think there is a way to skip some of the parameters if so.
packages/models-library/src/models_library/api_schemas_long_running_tasks/base.py
Outdated
Show resolved
Hide resolved
| class FileMetaDataArray(BaseModel): | ||
| __root__: list[FileMetaDataGet] = [] | ||
| class FileMetaDataArray(RootModel[list[FileMetaDataGet]]): | ||
| root: list[FileMetaDataGet] = [] |
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.
THOUGHT: IMO we should get rid of all these root models. They are very confusing. In this case, I would rather annotated list[FileMetaDataGet] than FileMetaDataArray
| AnyUrl = Annotated[str, pydantic.AnyUrl] | ||
|
|
||
| AnyHttpUrl = Annotated[str, pydantic.AnyHttpUrl] | ||
|
|
||
| HttpUrl = Annotated[str, pydantic.HttpUrl] | ||
|
|
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.
Overriding names this way is misleading ...
Either you use different names or force users to use explicit Annotated (my preferred option)
Imaging this scenario:
from models_library.basic_types import AnyUrl
from pydantic import *
class MyModel(BaseModel):
url : Annotated[str, AnyUrl]|
|
||
| class DictModel(GenericModel, Generic[DictKey, DictValue]): | ||
| __root__: dict[DictKey, DictValue] | ||
| class DictModel(RootModel[dict[DictKey, DictValue]], Generic[DictKey, DictValue]): |
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.
if ListModel and DictModel are not used anywhere in the codebase, I suggest to drop them and rather use explicit list[MyModel] and dict[str, MyModel] CC @sanderegg , @GitHK @matusdrobuliak66
|
|
||
| def test_schema(): | ||
| assert MyModel.schema() == { | ||
| assert MyModel.model_json_schema() == { |
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.
Actually this tests is showing that now pydantic NICELY distinguishes nullable fields. NOTE
class MyModel(BaseModel):
a: int
b: int | None = Field(...)
c: int = 42
d: int | None = None
e: int = FieldNotRequired(description="optional non-nullable")And we should get an schema that defines:
ais a requiredintbis a required eitherintorNullablecis an optionalintthat defaults to42dis an optional eitherintorNullablethat defaults toNoneeis an optionalint(if not set, it just gives youNone)
| "title": "D", | ||
| }, | ||
| "e": { | ||
| "default": None, |
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.
I just wonder whether we could change FieldNotRequired to returns some NotSet -like value
|
From Pydantic v1 official documentation:
from pydantic import BaseModel, Field, ValidationError
class Model(BaseModel):
a: int | None
b: int | None = ...
c: int | None = Field(...)
print(Model(b=1, c=2))
#> a=None b=1 c=2
try:
Model(a=1, b=2)
except ValidationError as e:
print(e)
"""
1 validation error for Model
c
field required (type=value_error.missing)
"""
Details: https://docs.pydantic.dev/1.10/usage/models/#required-optional-fields |
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.
🥇
| ui: EmptyModel | StudyUI | None | ||
| state: ProjectState | None = None | ||
| ui: EmptyModel | StudyUI | None = None | ||
| quality: dict[str, Any] = {} |
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.
@pcrespov If I revert the = None for the fields in this model, the create-project-schemas test fails because the mocked response payload doesn't contain them. Are these fields intended as optional?
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.
@giancarloromeo IMO this case a default =None makes sense
These mocks were generated using real payload from the front-end so it is important that they pass since there is no guarantee that the front-end always follows the openapi specs in the web-api.
Also, as you can see in services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml that these fields are not required:

BTW, once all the tests in the models_library pass, you might want to try to re-generate services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml and see if the changes make sense. In principle only the new nullable fields should be update and the rest should be the same. Pay special attention to required fields for instance ...
For that you can use
cd web/server
make openapi-specs
|
d2d81c0
into
ITISFoundation:pydantic_v2_migration



What do these changes do?
Related issue/s
How to test
Dev-ops checklist