Skip to content

Commit b3641d9

Browse files
authored
♻️ Maintenance: refactors application settings repo-wide (#7112)
1 parent 483d4e4 commit b3641d9

File tree

36 files changed

+1733
-1191
lines changed

36 files changed

+1733
-1191
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33

44
from pydantic_core import PydanticUndefined
55

6-
# SEE https://github.com/fastapi/fastapi/blob/master/fastapi/_compat.py#L75-L78
76
Undefined = PydanticUndefined
87
DEFAULT_FACTORY: Any = Undefined
9-
# Use `UNSET` as default when default_factory
8+
# Use `DEFAULT_FACTORY` as field default when using Field(default_factory=...)
109
# SEE https://github.com/ITISFoundation/osparc-simcore/pull/6882
10+
# SEE https://github.com/ITISFoundation/osparc-simcore/pull/7112#discussion_r1933432238
11+
# SEE https://github.com/fastapi/fastapi/blob/master/fastapi/_compat.py#L75-L78
1112

1213

1314
class LogLevel(StrEnum):

packages/settings-library/setup.cfg

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ universal = 1
1414
# Define setup.py command aliases here
1515
test = pytest
1616

17-
# NOTE: uncomment when pytest-asyncio is added in requirements
18-
# [tool:pytest]
19-
# asyncio_mode = auto
17+
[tool:pytest]
18+
# SEE https://docs.pytest.org/en/stable/how-to/capture-warnings.html
19+
filterwarnings =
20+
error

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Annotated
2+
13
from pydantic import Field, PositiveInt
24

35
from .base import BaseCustomSettings
@@ -18,11 +20,13 @@ class BaseApplicationSettings(BaseCustomSettings):
1820
# @Dockerfile
1921
SC_BOOT_MODE: BootModeEnum | None = None
2022
SC_BOOT_TARGET: BuildTargetEnum | None = None
21-
SC_HEALTHCHECK_TIMEOUT: PositiveInt | None = Field(
22-
default=None,
23-
description="If a single run of the check takes longer than timeout seconds "
24-
"then the check is considered to have failed."
25-
"It takes retries consecutive failures of the health check for the container to be considered unhealthy.",
26-
)
23+
SC_HEALTHCHECK_TIMEOUT: Annotated[
24+
PositiveInt | None,
25+
Field(
26+
description="If a single run of the check takes longer than timeout seconds "
27+
"then the check is considered to have failed."
28+
"It takes retries consecutive failures of the health check for the container to be considered unhealthy.",
29+
),
30+
] = None
2731
SC_USER_ID: int | None = None
2832
SC_USER_NAME: str | None = None

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from functools import cached_property
2+
from typing import Annotated
23

34
from pydantic import AnyHttpUrl, Field, TypeAdapter
45
from settings_library.base import BaseCustomSettings
@@ -10,9 +11,9 @@ class DirectorV0Settings(BaseCustomSettings):
1011

1112
DIRECTOR_HOST: str = "director"
1213
DIRECTOR_PORT: PortInt = TypeAdapter(PortInt).validate_python(8000)
13-
DIRECTOR_VTAG: VersionTag = Field(
14-
default="v0", description="Director-v0 service API's version tag"
15-
)
14+
DIRECTOR_VTAG: Annotated[
15+
VersionTag, Field(description="Director-v0 service API's version tag")
16+
] = "v0"
1617

1718
@cached_property
1819
def endpoint(self) -> str:

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

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from functools import cached_property
2-
from typing import Any, Self
2+
from typing import Annotated, Any, Self
33

44
from pydantic import (
55
AnyHttpUrl,
@@ -15,37 +15,45 @@
1515

1616

1717
class RegistrySettings(BaseCustomSettings):
18-
REGISTRY_AUTH: bool = Field(..., description="do registry authentication")
19-
REGISTRY_PATH: str | None = Field(
20-
default=None,
21-
# This is useful in case of a local registry, where the registry url (path) is relative to the host docker engine"
22-
description="development mode only, in case a local registry is used - "
23-
"this is the hostname to the docker registry as seen from the host running the containers (e.g. 127.0.0.1:5000)",
24-
)
25-
# NOTE: name is missleading, http or https protocol are not included
26-
REGISTRY_URL: str = Field(
27-
...,
28-
description="hostname of docker registry (without protocol but with port if available)",
29-
min_length=1,
30-
)
18+
REGISTRY_AUTH: Annotated[bool, Field(description="do registry authentication")]
19+
REGISTRY_PATH: Annotated[
20+
str | None,
21+
Field(
22+
# This is useful in case of a local registry, where the registry url (path) is relative to the host docker engine"
23+
description="development mode only, in case a local registry is used - "
24+
"this is the hostname to the docker registry as seen from the host running the containers (e.g. 127.0.0.1:5000)",
25+
),
26+
] = None
3127

32-
REGISTRY_USER: str = Field(
33-
..., description="username to access the docker registry"
34-
)
35-
REGISTRY_PW: SecretStr = Field(
36-
..., description="password to access the docker registry"
37-
)
38-
REGISTRY_SSL: bool = Field(
39-
..., description="True if docker registry is using HTTPS protocol"
40-
)
28+
REGISTRY_URL: Annotated[
29+
str,
30+
Field(
31+
# NOTE: name is missleading, http or https protocol are not included
32+
description="hostname of docker registry (without protocol but with port if available)",
33+
min_length=1,
34+
),
35+
]
36+
37+
REGISTRY_USER: Annotated[
38+
str,
39+
Field(description="username to access the docker registry"),
40+
]
41+
REGISTRY_PW: Annotated[
42+
SecretStr,
43+
Field(description="password to access the docker registry"),
44+
]
45+
REGISTRY_SSL: Annotated[
46+
bool,
47+
Field(description="True if docker registry is using HTTPS protocol"),
48+
]
4149

4250
@field_validator("REGISTRY_PATH", mode="before")
4351
@classmethod
4452
def _escape_none_string(cls, v) -> Any | None:
4553
return None if v == "None" else v
4654

4755
@model_validator(mode="after")
48-
def check_registry_authentication(self: Self) -> Self:
56+
def _check_registry_authentication(self: Self) -> Self:
4957
if self.REGISTRY_AUTH and any(
5058
not v for v in (self.REGISTRY_USER, self.REGISTRY_PW)
5159
):

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
from typing import Annotated
22

3-
from pydantic import AnyHttpUrl, BeforeValidator, Field, TypeAdapter
3+
from pydantic import BeforeValidator, Field
44
from pydantic_settings import SettingsConfigDict
55

66
from .base import BaseCustomSettings
7-
8-
ANY_HTTP_URL_ADAPTER: TypeAdapter = TypeAdapter(AnyHttpUrl)
7+
from .utils_validators import validate_nullable_url
98

109

1110
class EC2Settings(BaseCustomSettings):
1211
EC2_ACCESS_KEY_ID: str
1312
EC2_ENDPOINT: Annotated[
14-
str, BeforeValidator(lambda x: str(ANY_HTTP_URL_ADAPTER.validate_python(x)))
15-
] | None = Field(default=None, description="do not define if using standard AWS")
13+
str | None,
14+
BeforeValidator(validate_nullable_url),
15+
Field(description="do not define if using standard AWS"),
16+
] = None
1617
EC2_REGION_NAME: str = "us-east-1"
1718
EC2_SECRET_ACCESS_KEY: str
1819

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
from pathlib import Path
2+
from typing import Annotated
23

34
from pydantic import Field
45

56
from .base import BaseCustomSettings
67

78

89
class AwsEfsSettings(BaseCustomSettings):
9-
EFS_DNS_NAME: str = Field(
10-
description="AWS Elastic File System DNS name",
11-
examples=["fs-xxx.efs.us-east-1.amazonaws.com"],
12-
)
10+
EFS_DNS_NAME: Annotated[
11+
str,
12+
Field(
13+
description="AWS Elastic File System DNS name",
14+
examples=["fs-xxx.efs.us-east-1.amazonaws.com"],
15+
),
16+
]
1317
EFS_PROJECT_SPECIFIC_DATA_DIRECTORY: str
14-
EFS_MOUNTED_PATH: Path = Field(
15-
description="This is the path where EFS is mounted to the EC2 machine",
16-
)
18+
EFS_MOUNTED_PATH: Annotated[
19+
Path,
20+
Field(
21+
description="This is the path where EFS is mounted to the EC2 machine",
22+
),
23+
]
1724

1825

1926
NFS_PROTOCOL = "4.1"

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from enum import Enum
2-
from typing import Self
2+
from typing import Annotated, Self
33

44
from pydantic import model_validator
55
from pydantic.fields import Field
@@ -25,12 +25,15 @@ class SMTPSettings(BaseCustomSettings):
2525

2626
SMTP_HOST: str
2727
SMTP_PORT: PortInt
28-
SMTP_PROTOCOL: EmailProtocol = Field(
29-
EmailProtocol.UNENCRYPTED,
30-
description="Select between TLS, STARTTLS Secure Mode or unencrypted communication",
31-
)
32-
SMTP_USERNAME: str | None = Field(None, min_length=1)
33-
SMTP_PASSWORD: SecretStr | None = Field(None, min_length=1)
28+
SMTP_PROTOCOL: Annotated[
29+
EmailProtocol,
30+
Field(
31+
description="Select between TLS, STARTTLS Secure Mode or unencrypted communication",
32+
),
33+
] = EmailProtocol.UNENCRYPTED
34+
35+
SMTP_USERNAME: Annotated[str | None, Field(min_length=1)] = None
36+
SMTP_PASSWORD: Annotated[SecretStr | None, Field(min_length=1)] = None
3437

3538
@model_validator(mode="after")
3639
def _both_credentials_must_be_set(self) -> Self:
Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Annotated
2+
13
from pydantic import Field
24

35
from .base import BaseCustomSettings
@@ -6,26 +8,32 @@
68
class ClientRequestSettings(BaseCustomSettings):
79
# NOTE: These entries are used in some old services as well. These need to be updated if these
810
# variable names or defaults are changed.
9-
HTTP_CLIENT_REQUEST_TOTAL_TIMEOUT: int | None = Field(
10-
default=20,
11-
description="timeout in seconds used for outgoing http requests",
12-
)
11+
HTTP_CLIENT_REQUEST_TOTAL_TIMEOUT: Annotated[
12+
int | None,
13+
Field(
14+
description="timeout in seconds used for outgoing http requests",
15+
),
16+
] = 20
1317

14-
HTTP_CLIENT_REQUEST_AIOHTTP_CONNECT_TIMEOUT: int | None = Field(
15-
default=None,
16-
description=(
17-
"Maximal number of seconds for acquiring a connection"
18-
" from pool. The time consists connection establishment"
19-
" for a new connection or waiting for a free connection"
20-
" from a pool if pool connection limits are exceeded. "
21-
"For pure socket connection establishment time use sock_connect."
18+
HTTP_CLIENT_REQUEST_AIOHTTP_CONNECT_TIMEOUT: Annotated[
19+
int | None,
20+
Field(
21+
description=(
22+
"Maximal number of seconds for acquiring a connection"
23+
" from pool. The time consists connection establishment"
24+
" for a new connection or waiting for a free connection"
25+
" from a pool if pool connection limits are exceeded. "
26+
"For pure socket connection establishment time use sock_connect."
27+
),
2228
),
23-
)
29+
] = None
2430

25-
HTTP_CLIENT_REQUEST_AIOHTTP_SOCK_CONNECT_TIMEOUT: int | None = Field(
26-
default=5,
27-
description=(
28-
"aiohttp specific field used in ClientTimeout, timeout for connecting to a "
29-
"peer for a new connection not given a pool"
31+
HTTP_CLIENT_REQUEST_AIOHTTP_SOCK_CONNECT_TIMEOUT: Annotated[
32+
int | None,
33+
Field(
34+
description=(
35+
"aiohttp specific field used in ClientTimeout, timeout for connecting to a "
36+
"peer for a new connection not given a pool"
37+
),
3038
),
31-
)
39+
] = 5

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

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from functools import cached_property
2+
from typing import Annotated
23
from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
34

45
from pydantic import (
@@ -25,26 +26,28 @@ class PostgresSettings(BaseCustomSettings):
2526
POSTGRES_PASSWORD: SecretStr
2627

2728
# database
28-
POSTGRES_DB: str = Field(..., description="Database name")
29+
POSTGRES_DB: Annotated[str, Field(description="Database name")]
2930

3031
# pool connection limits
31-
POSTGRES_MINSIZE: int = Field(
32-
default=1, description="Minimum number of connections in the pool", ge=1
33-
)
34-
POSTGRES_MAXSIZE: int = Field(
35-
default=50, description="Maximum number of connections in the pool", ge=1
36-
)
32+
POSTGRES_MINSIZE: Annotated[
33+
int, Field(description="Minimum number of connections in the pool", ge=1)
34+
] = 1
35+
POSTGRES_MAXSIZE: Annotated[
36+
int, Field(description="Maximum number of connections in the pool", ge=1)
37+
] = 50
3738

38-
POSTGRES_CLIENT_NAME: str | None = Field(
39-
default=None,
40-
description="Name of the application connecting the postgres database, will default to use the host hostname (hostname on linux)",
41-
validation_alias=AliasChoices(
42-
"POSTGRES_CLIENT_NAME",
43-
# This is useful when running inside a docker container, then the hostname is set each client gets a different name
44-
"HOST",
45-
"HOSTNAME",
39+
POSTGRES_CLIENT_NAME: Annotated[
40+
str | None,
41+
Field(
42+
description="Name of the application connecting the postgres database, will default to use the host hostname (hostname on linux)",
43+
validation_alias=AliasChoices(
44+
"POSTGRES_CLIENT_NAME",
45+
# This is useful when running inside a docker container, then the hostname is set each client gets a different name
46+
"HOST",
47+
"HOSTNAME",
48+
),
4649
),
47-
)
50+
] = None
4851

4952
@field_validator("POSTGRES_MAXSIZE")
5053
@classmethod

0 commit comments

Comments
 (0)