Skip to content

Commit 3e9b7c2

Browse files
committed
fix: update model field access to use class reference in serialization and settings
1 parent 4773e3c commit 3e9b7c2

File tree

5 files changed

+16
-57
lines changed

5 files changed

+16
-57
lines changed

packages/common-library/src/common_library/serialization.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ def model_dump_with_secrets(
1111
) -> dict[str, Any]:
1212
data = settings_obj.model_dump(**pydantic_export_options)
1313

14-
for field_name in settings_obj.model_fields:
14+
settings_cls = settings_obj.__class__
15+
16+
for field_name in settings_cls.model_fields:
1517
if field_name not in data:
1618
continue
1719

@@ -29,7 +31,9 @@ def model_dump_with_secrets(
2931
data[field_name] = str(field_data)
3032

3133
elif isinstance(field_data, dict):
32-
possible_pydantic_model = settings_obj.model_fields[field_name].annotation
34+
possible_pydantic_model = settings_obj.__class__.model_fields[
35+
field_name
36+
].annotation
3337
# NOTE: data could be a dict which does not represent a pydantic model or a union of models
3438
with contextlib.suppress(AttributeError, ValidationError):
3539
data[field_name] = model_dump_with_secrets(

packages/settings-library/src/settings_library/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ def _parse_none(cls, v, info: ValidationInfo):
120120
model_config = SettingsConfigDict(
121121
case_sensitive=True, # All must be capitalized
122122
env_parse_none_str="null",
123-
extra="ignore",
123+
extra="ignore", # NOTE: if "strict" then fields with multiple aliases defined in the envs will fail to validate!
124124
frozen=True,
125125
ignored_types=(cached_property,),
126-
populate_by_name=True, # deprecated in pydantic v2.11+
126+
populate_by_name=True, # NOTE: populate_by_name deprecated in pydantic v2.11+
127127
validate_by_alias=True,
128128
validate_by_name=True,
129129
validate_default=True,

packages/settings-library/tests/test__pydantic_settings.py

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,19 @@
1212
1313
"""
1414

15-
from collections.abc import Callable
16-
from typing import Annotated, Any
15+
from typing import Annotated
1716

1817
import pytest
1918
from common_library.basic_types import LogLevel
2019
from common_library.pydantic_fields_extension import is_nullable
2120
from pydantic import (
2221
AliasChoices,
23-
AmqpDsn,
24-
BaseModel,
2522
Field,
26-
ImportString,
27-
PostgresDsn,
28-
RedisDsn,
2923
ValidationInfo,
3024
field_validator,
3125
)
3226
from pydantic_core import PydanticUndefined
33-
from pydantic_settings import BaseSettings, SettingsConfigDict
27+
from pydantic_settings import BaseSettings
3428
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
3529
from settings_library.application import BaseApplicationSettings
3630

@@ -209,46 +203,3 @@ def test_pydantic_serialization_user_warning(monkeypatch: pytest.MonkeyPatch):
209203
settings = _TestSettings.create_from_envs()
210204
assert settings.APP_LOGLEVEL == LogLevel.DEBUG
211205
assert settings.model_dump_json(indent=2)
212-
213-
214-
def test_it():
215-
class SubModel(BaseModel):
216-
foo: str = "bar"
217-
apple: int = 1
218-
219-
class Settings(BaseSettings):
220-
auth_key: str = Field(validation_alias="my_auth_key")
221-
222-
api_key: str = Field(alias="my_api_key")
223-
224-
redis_dsn: RedisDsn = Field(
225-
"redis://user:pass@localhost:6379/1",
226-
validation_alias=AliasChoices("service_redis_dsn", "redis_url"),
227-
)
228-
pg_dsn: PostgresDsn = "postgres://user:pass@localhost:5432/foobar"
229-
amqp_dsn: AmqpDsn = "amqp://user:pass@localhost:5672/"
230-
231-
special_function: ImportString[Callable[[Any], Any]] = "math.cos"
232-
233-
# to override domains:
234-
# export my_prefix_domains='["foo.com", "bar.com"]'
235-
domains: set[str] = set()
236-
237-
# to override more_settings:
238-
# export my_prefix_more_settings='{"foo": "x", "apple": 1}'
239-
more_settings: SubModel = SubModel()
240-
241-
model_config = SettingsConfigDict(env_prefix="my_prefix_")
242-
243-
import math
244-
245-
assert Settings().model_dump() == {
246-
"auth_key": "xxx",
247-
"api_key": "xxx",
248-
"redis_dsn": RedisDsn("redis://user:pass@localhost:6379/1"),
249-
"pg_dsn": PostgresDsn("postgres://user:pass@localhost:5432/foobar"),
250-
"amqp_dsn": AmqpDsn("amqp://user:pass@localhost:5672/"),
251-
"special_function": math.cos,
252-
"domains": set(),
253-
"more_settings": {"foo": "bar", "apple": 1},
254-
}

packages/settings-library/tests/test_base_w_postgres.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ class _FakePostgresSettings(BaseCustomSettings):
6262
str | None,
6363
Field(
6464
validation_alias=AliasChoices(
65-
"HOST", "HOSTNAME", "POSTGRES_CLIENT_NAME"
65+
"POSTGRES_CLIENT_NAME",
66+
"HOST",
67+
"HOSTNAME",
6668
),
6769
),
6870
] = None

services/api-server/src/simcore_service_api_server/core/settings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class WebServerSettings(WebServerBaseSettings, MixinSessionSettings):
3434
description="Secret key to encrypt cookies. "
3535
'TIP: python3 -c "from cryptography.fernet import *; print(Fernet.generate_key())"',
3636
min_length=44,
37-
validation_alias=AliasChoices("SESSION_SECRET_KEY"),
37+
validation_alias=AliasChoices(
38+
"WEBSERVER_SESSION_SECRET_KEY", "SESSION_SECRET_KEY"
39+
),
3840
),
3941
]
4042
WEBSERVER_SESSION_NAME: str = DEFAULT_SESSION_COOKIE_NAME

0 commit comments

Comments
 (0)