Skip to content

Commit 56f8706

Browse files
authored
🐛 Fix/setting exclude unset option (ITISFoundation#2846)
1 parent bb60d87 commit 56f8706

File tree

4 files changed

+173
-74
lines changed

4 files changed

+173
-74
lines changed

packages/models-library/src/models_library/settings/base.py

Lines changed: 0 additions & 53 deletions
This file was deleted.

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from pydantic import BaseConfig, BaseSettings, Extra, ValidationError, validator
55
from pydantic.error_wrappers import ErrorList, ErrorWrapper
66
from pydantic.fields import ModelField, Undefined
7-
from pydantic.types import SecretStr
87

98

109
class DefaultFromEnvFactoryError(ValidationError):
@@ -65,7 +64,6 @@ class Config(BaseConfig):
6564
allow_mutation = False
6665
frozen = True
6766
validate_all = True
68-
json_encoders = {SecretStr: lambda v: v.get_secret_value()}
6967
keep_untouched = (cached_property,)
7068

7169
@classmethod

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

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import logging
22
import os
3+
from functools import partial
34
from pprint import pformat
4-
from typing import Callable, Optional, Type
5+
from typing import Any, Callable, Dict, Optional, Type
56

67
import typer
7-
from pydantic import ValidationError
8+
from pydantic import BaseModel, SecretStr, ValidationError
89
from pydantic.env_settings import BaseSettings
10+
from pydantic.json import custom_pydantic_encoder
911

1012
from ._constants import HEADER_STR
1113
from .base import BaseCustomSettings
@@ -19,11 +21,20 @@ def print_as_envfile(
1921
show_secrets: bool,
2022
**pydantic_export_options,
2123
):
24+
exclude_unset = pydantic_export_options.get("exclude_unset", False)
25+
2226
for field in settings_obj.__fields__.values():
27+
auto_default_from_env = field.field_info.extra.get(
28+
"auto_default_from_env", False
29+
)
2330

2431
value = getattr(settings_obj, field.name)
25-
if show_secrets and hasattr(value, "get_secret_value"):
26-
value = value.get_secret_value()
32+
33+
if exclude_unset and field.name not in settings_obj.__fields_set__:
34+
if not auto_default_from_env:
35+
continue
36+
if value is None:
37+
continue
2738

2839
if isinstance(value, BaseSettings):
2940
if compact:
@@ -32,9 +43,15 @@ def print_as_envfile(
3243
if verbose:
3344
typer.echo(f"\n# --- {field.name} --- ")
3445
print_as_envfile(
35-
value, compact=False, verbose=verbose, show_secrets=show_secrets
46+
value,
47+
compact=False,
48+
verbose=verbose,
49+
show_secrets=show_secrets,
50+
**pydantic_export_options,
3651
)
3752
continue
53+
elif show_secrets and hasattr(value, "get_secret_value"):
54+
value = value.get_secret_value()
3855

3956
if verbose:
4057
field_info = field.field_info
@@ -50,6 +67,18 @@ def print_as_json(settings_obj, *, compact=False, **pydantic_export_options):
5067
)
5168

5269

70+
def create_json_encoder_wo_secrets(model_cls: Type[BaseModel]):
71+
current_encoders = getattr(model_cls.Config, "json_encoders", {})
72+
encoder = partial(
73+
custom_pydantic_encoder,
74+
{
75+
SecretStr: lambda v: v.get_secret_value(),
76+
**current_encoders,
77+
},
78+
)
79+
return encoder
80+
81+
5382
def create_settings_command(
5483
settings_cls: Type[BaseCustomSettings], logger: Optional[logging.Logger] = None
5584
) -> Callable:
@@ -74,13 +103,13 @@ def settings(
74103
),
75104
):
76105
"""Resolves settings and prints envfile"""
77-
pydantic_export_options = {"exclude_unset": exclude_unset}
78106

79107
if as_json_schema:
80108
typer.echo(settings_cls.schema_json(indent=0 if compact else 2))
81109
return
82110

83111
try:
112+
84113
settings_obj = settings_cls.create_from_envs()
85114

86115
except ValidationError as err:
@@ -110,6 +139,13 @@ def settings(
110139
)
111140
raise
112141

142+
pydantic_export_options: Dict[str, Any] = {"exclude_unset": exclude_unset}
143+
if show_secrets:
144+
# NOTE: this option is for json-only
145+
pydantic_export_options["encoder"] = create_json_encoder_wo_secrets(
146+
settings_cls
147+
)
148+
113149
if as_json:
114150
print_as_json(settings_obj, compact=compact, **pydantic_export_options)
115151
else:

0 commit comments

Comments
 (0)