From abcf3471e78efcff3a52f787795efe2410daff73 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:52:00 +0100 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrades=20deprecated=20py?= =?UTF-8?q?dantic=20calls=20repo-wide=20(#6864)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schemas/scripts/create_node-meta-schema.py | 3 ++- .../schemas/scripts/create_project-schema.py | 3 ++- packages/models-library/Makefile | 7 ------- .../services/iter_sensitivity.py | 7 +++++-- .../src/models_library/utils/common_validators.py | 5 +++-- .../src/models_library/utils/services_io.py | 8 ++++++-- .../tests/test__models_fit_schemas.py | 3 +-- .../models-library/tests/test__pydantic_models.py | 7 +++++-- .../tests/test_service_settings_labels.py | 4 ++-- packages/models-library/tests/test_services_io.py | 3 ++- .../models-library/tests/test_sidecar_volumes.py | 2 +- .../src/pytest_simcore/helpers/faker_catalog.py | 1 - .../tests/unit/test_node_ports_v2_port_mapping.py | 11 ++++++----- .../unit/test_node_ports_v2_port_validation.py | 14 +++++++++----- 14 files changed, 44 insertions(+), 34 deletions(-) diff --git a/api/specs/director/schemas/scripts/create_node-meta-schema.py b/api/specs/director/schemas/scripts/create_node-meta-schema.py index 29b4a02a9b2..89623889b9f 100644 --- a/api/specs/director/schemas/scripts/create_node-meta-schema.py +++ b/api/specs/director/schemas/scripts/create_node-meta-schema.py @@ -8,6 +8,7 @@ from pathlib import Path import jsonref +from common_library.json_serialization import json_dumps from models_library.services import ServiceMetaDataPublished CURRENT_DIR = Path(sys.argv[0] if __name__ == "__main__" else __file__).resolve().parent @@ -15,7 +16,7 @@ if __name__ == "__main__": with Path.open(CURRENT_DIR.parent / "node-meta-v0.0.1-pydantic.json", "w") as f: - schema = ServiceMetaDataPublished.schema_json() + schema = json_dumps(ServiceMetaDataPublished.model_json_schema()) schema_without_ref = jsonref.loads(schema) json.dump(schema_without_ref, f, indent=2) diff --git a/api/specs/director/schemas/scripts/create_project-schema.py b/api/specs/director/schemas/scripts/create_project-schema.py index 9ede930ec35..6bcbc3a9d1a 100644 --- a/api/specs/director/schemas/scripts/create_project-schema.py +++ b/api/specs/director/schemas/scripts/create_project-schema.py @@ -8,6 +8,7 @@ from pathlib import Path import jsonref +from common_library.json_serialization import json_dumps from models_library.projects import Project CURRENT_DIR = Path(sys.argv[0] if __name__ == "__main__" else __file__).resolve().parent @@ -17,7 +18,7 @@ with Path.open( CURRENT_DIR.parent / "common/schemas/project-v0.0.1-pydantic.json", "w" ) as f: - schema = Project.schema_json() + schema = json_dumps(Project.model_json_schema()) schema_without_ref = jsonref.loads(schema) json.dump(schema_without_ref, f, indent=2) diff --git a/packages/models-library/Makefile b/packages/models-library/Makefile index 008e7a19e05..01e5b586df2 100644 --- a/packages/models-library/Makefile +++ b/packages/models-library/Makefile @@ -49,13 +49,6 @@ tests-ci: ## runs unit tests [ci-mode] -m "not heavy_load" \ $(CURDIR)/tests -.PHONY: project-jsonschema.ignore.json -project-jsonschema.ignore.json: ## creates project-v0.0.1.json for DEV purposes - python3 -c "from models_library.projects import Project; print(Project.schema_json(indent=2))" > $@ - -.PHONY: service-jsonschema.ignore.json -node-meta-jsonschema.ignore.json: ## creates node-meta-v0.0.1.json for DEV purposes - python3 -c "from models_library.services import ServiceDockerData as cls; print(cls.schema_json(indent=2))" > $@ DOCKER_API_VERSION ?= 1.41 .PHONY: docker_rest_api.py diff --git a/packages/models-library/src/models_library/function_services_catalog/services/iter_sensitivity.py b/packages/models-library/src/models_library/function_services_catalog/services/iter_sensitivity.py index a2be976c651..cfe170bc7f4 100644 --- a/packages/models-library/src/models_library/function_services_catalog/services/iter_sensitivity.py +++ b/packages/models-library/src/models_library/function_services_catalog/services/iter_sensitivity.py @@ -2,7 +2,7 @@ from copy import deepcopy from typing import Any -from pydantic import schema_of +from pydantic import TypeAdapter from ...projects_nodes import OutputID, OutputsDict from ...services import ServiceMetaDataPublished, ServiceType @@ -10,7 +10,10 @@ from .._key_labels import FUNCTION_SERVICE_KEY_PREFIX from .._utils import EN, OM, FunctionServices, create_fake_thumbnail_url -LIST_NUMBERS_SCHEMA: dict[str, Any] = schema_of(list[float], title="list[number]") +LIST_NUMBERS_SCHEMA: dict[str, Any] = { + **TypeAdapter(list[float]).json_schema(), + "title": "list[number]", +} META = ServiceMetaDataPublished.model_validate( diff --git a/packages/models-library/src/models_library/utils/common_validators.py b/packages/models-library/src/models_library/utils/common_validators.py index 5b2cdbf560a..23cb62739db 100644 --- a/packages/models-library/src/models_library/utils/common_validators.py +++ b/packages/models-library/src/models_library/utils/common_validators.py @@ -22,6 +22,7 @@ class MyModel(BaseModel): from common_library.json_serialization import json_loads from orjson import JSONDecodeError +from pydantic import BaseModel def empty_str_to_none_pre_validator(value: Any): @@ -102,8 +103,8 @@ def create__check_only_one_is_set__root_validator(alternative_field_names: list[ SEE test_uid_or_email_are_set.py for more details """ - def _validator(cls, values): - assert set(alternative_field_names).issubset(cls.__fields__) # nosec + def _validator(cls: type[BaseModel], values): + assert set(alternative_field_names).issubset(cls.model_fields) # nosec got = { field_name: getattr(values, field_name) diff --git a/packages/models-library/src/models_library/utils/services_io.py b/packages/models-library/src/models_library/utils/services_io.py index 2f862a79262..f7dbce8dcea 100644 --- a/packages/models-library/src/models_library/utils/services_io.py +++ b/packages/models-library/src/models_library/utils/services_io.py @@ -2,7 +2,7 @@ from copy import deepcopy from typing import Any, Literal -from pydantic import schema_of +from pydantic import TypeAdapter from ..services import ServiceInput, ServiceOutput from ..services_regex import PROPERTY_TYPE_TO_PYTHON_TYPE_MAP @@ -10,8 +10,12 @@ PortKindStr = Literal["input", "output"] JsonSchemaDict = dict[str, Any] + _PROPERTY_TYPE_TO_SCHEMAS = { - property_type: schema_of(python_type, title=property_type.capitalize()) + property_type: { + **TypeAdapter(python_type).json_schema(), + "title": property_type.capitalize(), + } for property_type, python_type in PROPERTY_TYPE_TO_PYTHON_TYPE_MAP.items() } diff --git a/packages/models-library/tests/test__models_fit_schemas.py b/packages/models-library/tests/test__models_fit_schemas.py index 31952e7108f..7b940696e8c 100644 --- a/packages/models-library/tests/test__models_fit_schemas.py +++ b/packages/models-library/tests/test__models_fit_schemas.py @@ -2,7 +2,6 @@ # pylint:disable=unused-argument # pylint:disable=redefined-outer-name # pylint:disable=protected-access -import json from collections.abc import Callable import pytest @@ -28,7 +27,7 @@ def test_generated_schema_same_as_original( # TODO: create instead a fixture that returns a Callable and do these checks # on separate test_* files that follow the same package submodule's hierarchy # - generated_schema = json.loads(pydantic_model.schema_json(indent=2)) + generated_schema = pydantic_model.model_json_schema() original_schema = json_schema_dict(original_json_schema) # NOTE: A change is considered an addition when the destination schema has become more permissive relative to the source schema. For example {"type": "string"} -> {"type": ["string", "number"]}. diff --git a/packages/models-library/tests/test__pydantic_models.py b/packages/models-library/tests/test__pydantic_models.py index d4724972d00..1d9dc14e3b4 100644 --- a/packages/models-library/tests/test__pydantic_models.py +++ b/packages/models-library/tests/test__pydantic_models.py @@ -9,9 +9,10 @@ from typing import Any, Union, get_args, get_origin import pytest +from common_library.json_serialization import json_dumps from models_library.projects_nodes import InputTypes, OutputTypes from models_library.projects_nodes_io import SimCoreFileLink -from pydantic import BaseModel, Field, ValidationError, schema_json_of +from pydantic import BaseModel, Field, TypeAdapter, ValidationError from pydantic.types import Json from pydantic.version import version_short @@ -37,7 +38,9 @@ class ArgumentAnnotation(BaseModel): data_schema: Json # notice that this is a raw string! - jsonschema_of_x = schema_json_of(list[int], title="schema[x]") + jsonschema_of_x = json_dumps( + {**TypeAdapter(list[int]).json_schema(), "title": "schema[x]"} + ) assert isinstance(jsonschema_of_x, str) x_annotation = ArgumentAnnotation(name="x", data_schema=jsonschema_of_x) diff --git a/packages/models-library/tests/test_service_settings_labels.py b/packages/models-library/tests/test_service_settings_labels.py index 3d7a71bf7ff..0c582905d22 100644 --- a/packages/models-library/tests/test_service_settings_labels.py +++ b/packages/models-library/tests/test_service_settings_labels.py @@ -8,6 +8,7 @@ from pprint import pformat from typing import Any, Final, NamedTuple +import pydantic_core import pytest from models_library.basic_types import PortInt from models_library.osparc_variable_identifier import ( @@ -32,7 +33,6 @@ from models_library.services_resources import DEFAULT_SINGLE_SERVICE_NAME from models_library.utils.string_substitution import TextTemplate from pydantic import BaseModel, TypeAdapter, ValidationError -from pydantic.json import pydantic_encoder class _Parametrization(NamedTuple): @@ -560,7 +560,7 @@ def test_can_parse_labels_with_osparc_identifiers( def servicelib__json_serialization__json_dumps(obj: Any, **kwargs): # Analogous to 'models_library.utils.json_serialization.json_dumps' - return json.dumps(obj, default=pydantic_encoder, **kwargs) + return json.dumps(obj, default=pydantic_core.to_jsonable_python, **kwargs) def test_resolving_some_service_labels_at_load_time( diff --git a/packages/models-library/tests/test_services_io.py b/packages/models-library/tests/test_services_io.py index e056647665f..6f794eff85a 100644 --- a/packages/models-library/tests/test_services_io.py +++ b/packages/models-library/tests/test_services_io.py @@ -5,6 +5,7 @@ from pathlib import Path import yaml +from common_library.json_serialization import json_dumps from models_library.services import ServiceInput, ServiceMetaDataPublished from pint import Unit, UnitRegistry @@ -13,7 +14,7 @@ def test_service_port_units(tests_data_dir: Path): ureg = UnitRegistry() data = yaml.safe_load((tests_data_dir / "metadata-sleeper-2.0.2.yaml").read_text()) - print(ServiceMetaDataPublished.schema_json(indent=2)) + print(json_dumps(ServiceMetaDataPublished.model_json_schema(), indent=2)) service_meta = ServiceMetaDataPublished.model_validate(data) assert service_meta.inputs diff --git a/packages/models-library/tests/test_sidecar_volumes.py b/packages/models-library/tests/test_sidecar_volumes.py index 402899726bc..b6de8518d11 100644 --- a/packages/models-library/tests/test_sidecar_volumes.py +++ b/packages/models-library/tests/test_sidecar_volumes.py @@ -13,5 +13,5 @@ def test_volume_state_equality_does_not_use_last_changed(status: VolumeStatus): # NOTE: `last_changed` is initialized with the utc datetime # at the moment of the creation of the object. assert VolumeState(status=status) == VolumeState(status=status) - schema_property_count = len(VolumeState.schema()["properties"]) + schema_property_count = len(VolumeState.model_json_schema()["properties"]) assert len(VolumeState(status=status).model_dump()) == schema_property_count diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/faker_catalog.py b/packages/pytest-simcore/src/pytest_simcore/helpers/faker_catalog.py index f89b869d3d1..c0cc6488b76 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/faker_catalog.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/faker_catalog.py @@ -32,7 +32,6 @@ def create_service_out2(**overrides): # https://github.com/ITISFoundation/osparc-simcore/blob/master/services/catalog/src/simcore_service_catalog/models/schemas/services.py # # docker exec -it $(docker ps --filter="ancestor=local/catalog:development" -q) - # python -c "from simcore_service_catalog.models.schemas.services import ServiceOut;print(ServiceOut.schema_json(indent=2))" > services/catalog/ignore-schema.json # put file in https://json-schema-faker.js.org/ and get fake output # diff --git a/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_mapping.py b/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_mapping.py index 3746520f42c..4926a9f4123 100644 --- a/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_mapping.py +++ b/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_mapping.py @@ -8,7 +8,7 @@ import pytest from models_library.services import ServiceInput -from pydantic import Field, ValidationError, schema_of +from pydantic import Field, TypeAdapter, ValidationError from simcore_sdk.node_ports_v2 import exceptions from simcore_sdk.node_ports_v2.port import Port from simcore_sdk.node_ports_v2.ports_mapping import InputsList, OutputsList @@ -74,10 +74,11 @@ def test_io_ports_are_not_aliases(): @pytest.fixture def fake_port_meta() -> dict[str, Any]: """Service port metadata: defines a list of non-negative numbers""" - schema = schema_of( - list[Annotated[float, Field(ge=0)]], - title="list[non-negative number]", - ) + schema = { + **TypeAdapter(list[Annotated[float, Field(ge=0)]]).json_schema(), + "title": "list[non-negative number]", + } + schema.update( description="Port with an array of numbers", x_unit="millimeter", diff --git a/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_validation.py b/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_validation.py index ee0d19cec90..e6bcf71f323 100644 --- a/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_validation.py +++ b/packages/simcore-sdk/tests/unit/test_node_ports_v2_port_validation.py @@ -13,8 +13,7 @@ from unittest.mock import AsyncMock import pytest -from pydantic import BaseModel, Field, schema_of -from pydantic import ValidationError +from pydantic import BaseModel, Field, TypeAdapter, ValidationError from simcore_sdk.node_ports_v2.port import Port from simcore_sdk.node_ports_v2.port_validation import ( PortUnitError, @@ -132,9 +131,14 @@ class A(BaseModel): i: Annotated[int, Field(gt=3)] b: bool = False s: str - l: list[int] + t: list[int] - content_schema = _resolve_refs(schema_of(list[A], title="array[A]")) + content_schema = _resolve_refs( + { + **TypeAdapter(list[A]).json_schema(), + "title": "array[A]", + } + ) port_meta = { "label": "array_", @@ -142,7 +146,7 @@ class A(BaseModel): "type": "ref_contentSchema", "contentSchema": content_schema, } - sample = [{"i": 5, "s": "x", "l": [1, 2]}, {"i": 6, "s": "y", "l": [2]}] + sample = [{"i": 5, "s": "x", "t": [1, 2]}, {"i": 6, "s": "y", "t": [2]}] expected_value = [A(**i).model_dump() for i in sample] print(json.dumps(port_meta, indent=1))