diff --git a/packages/common-library/src/common_library/json_serialization.py b/packages/common-library/src/common_library/json_serialization.py index ef11a2640cd..66ae07b739c 100644 --- a/packages/common-library/src/common_library/json_serialization.py +++ b/packages/common-library/src/common_library/json_serialization.py @@ -23,7 +23,7 @@ from uuid import UUID import orjson -from pydantic import NameEmail, SecretBytes, SecretStr +from pydantic import AnyHttpUrl, AnyUrl, HttpUrl, NameEmail, SecretBytes, SecretStr from pydantic_core import Url from pydantic_extra_types.color import Color @@ -62,6 +62,8 @@ def decimal_encoder(dec_value: Decimal) -> int | float: ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = { + AnyHttpUrl: str, + AnyUrl: str, bytes: lambda o: o.decode(), Color: str, datetime.date: isoformat, @@ -73,6 +75,7 @@ def decimal_encoder(dec_value: Decimal) -> int | float: frozenset: list, deque: list, GeneratorType: list, + HttpUrl: str, IPv4Address: str, IPv4Interface: str, IPv4Network: str, diff --git a/packages/common-library/tests/test_json_serialization.py b/packages/common-library/tests/test_json_serialization.py index 7684497e493..ab2092b61ae 100644 --- a/packages/common-library/tests/test_json_serialization.py +++ b/packages/common-library/tests/test_json_serialization.py @@ -15,7 +15,7 @@ json_loads, ) from faker import Faker -from pydantic import Field, TypeAdapter +from pydantic import AnyHttpUrl, AnyUrl, BaseModel, Field, HttpUrl, TypeAdapter from pydantic.json import pydantic_encoder @@ -95,3 +95,18 @@ def test_compatiblity_with_json_interface( # NOTE: cannot compare dumps directly because orjson compacts it more assert json_loads(orjson_dump) == json_loads(json_dump) + + +def test_serialized_model_with_urls(faker: Faker): + # See: https://github.com/ITISFoundation/osparc-simcore/pull/6852 + class M(BaseModel): + any_http_url: AnyHttpUrl + any_url: AnyUrl + http_url: HttpUrl + + obj = M( + any_http_url=faker.url(), + any_url=faker.url(), + http_url=faker.url(), + ) + json_dumps(obj) diff --git a/packages/models-library/src/models_library/utils/_original_fastapi_encoders.py b/packages/models-library/src/models_library/utils/_original_fastapi_encoders.py index 0b48328d91b..5eac7c1b2f1 100644 --- a/packages/models-library/src/models_library/utils/_original_fastapi_encoders.py +++ b/packages/models-library/src/models_library/utils/_original_fastapi_encoders.py @@ -8,7 +8,7 @@ from enum import Enum from pathlib import PurePath from types import GeneratorType -from typing import Any, Callable, Union +from typing import Any, Callable, Union, get_origin from common_library.json_serialization import ENCODERS_BY_TYPE from pydantic import BaseModel @@ -28,7 +28,8 @@ def generate_encoders_by_class_tuples( tuple ) for type_, encoder in type_encoder_map.items(): - encoders_by_class_tuples[encoder] += (type_,) + if get_origin(type_) is not Annotated: + encoders_by_class_tuples[encoder] += (type_,) return encoders_by_class_tuples diff --git a/services/dynamic-scheduler/tests/unit/test_cli.py b/services/dynamic-scheduler/tests/unit/test_cli.py index e94c51a9e15..6bdaa62d1e6 100644 --- a/services/dynamic-scheduler/tests/unit/test_cli.py +++ b/services/dynamic-scheduler/tests/unit/test_cli.py @@ -1,8 +1,10 @@ # pylint:disable=unused-argument import os +import traceback import pytest +from click.testing import Result from pytest_simcore.helpers.monkeypatch_envs import load_dotenv, setenvs_from_dict from pytest_simcore.helpers.typing_env import EnvVarsDict from simcore_service_dynamic_scheduler._meta import API_VERSION @@ -11,20 +13,26 @@ from typer.testing import CliRunner +def _format_cli_error(result: Result) -> str: + assert result.exception + tb_message = "\n".join(traceback.format_tb(result.exception.__traceback__)) + return f"Below exception was raised by the cli:\n{tb_message}" + + def test_cli_help_and_version(cli_runner: CliRunner): # simcore-service-dynamic-scheduler --help result = cli_runner.invoke(cli_main, "--help") - assert result.exit_code == os.EX_OK, result.output + assert result.exit_code == os.EX_OK, _format_cli_error(result) result = cli_runner.invoke(cli_main, "--version") - assert result.exit_code == os.EX_OK, result.output + assert result.exit_code == os.EX_OK, _format_cli_error(result) assert result.stdout.strip() == API_VERSION def test_echo_dotenv(cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch): # simcore-service-dynamic-scheduler echo-dotenv result = cli_runner.invoke(cli_main, "echo-dotenv") - assert result.exit_code == os.EX_OK, result.output + assert result.exit_code == os.EX_OK, _format_cli_error(result) environs = load_dotenv(result.stdout) @@ -33,10 +41,25 @@ def test_echo_dotenv(cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch): ApplicationSettings.create_from_envs() -def test_list_settings(cli_runner: CliRunner, app_environment: EnvVarsDict): - # simcore-service-dynamic-scheduler settings --show-secrets --as-json - result = cli_runner.invoke(cli_main, ["settings", "--show-secrets", "--as-json"]) - assert result.exit_code == os.EX_OK, result.output +def test_list_settings( + cli_runner: CliRunner, app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch +): + with monkeypatch.context() as patch: + setenvs_from_dict( + patch, + { + **app_environment, + "DYNAMIC_SCHEDULER_TRACING": "{}", + "TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT": "http://replace-with-opentelemetry-collector", + "TRACING_OPENTELEMETRY_COLLECTOR_PORT": "4318", + }, + ) + + # simcore-service-dynamic-scheduler settings --show-secrets --as-json + result = cli_runner.invoke( + cli_main, ["settings", "--show-secrets", "--as-json"] + ) + assert result.exit_code == os.EX_OK, _format_cli_error(result) print(result.output) settings = ApplicationSettings(result.output)