Skip to content

Commit 1e57220

Browse files
add utils
1 parent 4a30d09 commit 1e57220

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from types import UnionType
2+
from typing import Any, Literal, get_args, get_origin
3+
4+
from pydantic.fields import FieldInfo
5+
6+
7+
def get_type(info: FieldInfo) -> Any:
8+
field_type = info.annotation
9+
if args := get_args(info.annotation):
10+
field_type = next(a for a in args if a != type(None))
11+
return field_type
12+
13+
14+
def is_literal(info: FieldInfo) -> bool:
15+
return get_origin(info.annotation) is Literal
16+
17+
18+
def is_nullable(info: FieldInfo) -> bool:
19+
origin = get_origin(info.annotation) # X | None or Optional[X] will return Union
20+
if origin is UnionType:
21+
return any(x in get_args(info.annotation) for x in (type(None), Any))
22+
return False
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import Any
2+
3+
from common_library.utils.pydantic_fields_extension import get_type
4+
from pydantic import BaseModel, SecretStr
5+
6+
7+
def model_dump_with_secrets(
8+
settings_obj: BaseModel, show_secrets: bool, **pydantic_export_options
9+
) -> dict[str, Any]:
10+
data = settings_obj.model_dump(**pydantic_export_options)
11+
12+
for field_name in settings_obj.model_fields:
13+
if field_name not in data:
14+
continue
15+
16+
field_data = data[field_name]
17+
18+
if isinstance(field_data, SecretStr):
19+
if show_secrets:
20+
data[field_name] = field_data.get_secret_value()
21+
else:
22+
data[field_name] = str(field_data)
23+
elif isinstance(field_data, dict):
24+
field_type = get_type(settings_obj.model_fields[field_name])
25+
if issubclass(field_type, BaseModel):
26+
data[field_name] = model_dump_with_secrets(
27+
field_type.model_validate(field_data),
28+
show_secrets,
29+
**pydantic_export_options,
30+
)
31+
32+
return data
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from typing import Literal
2+
3+
import pytest
4+
from common_library.utils.pydantic_fields_extension import (
5+
get_type,
6+
is_literal,
7+
is_nullable,
8+
)
9+
from pydantic import BaseModel, Field
10+
11+
12+
class MyModel(BaseModel):
13+
a: int
14+
b: float | None = Field(...)
15+
c: str = "bla"
16+
d: bool | None = None
17+
e: Literal["bla"]
18+
19+
20+
@pytest.mark.parametrize(
21+
"fn,expected,name",
22+
[
23+
(
24+
get_type,
25+
int,
26+
"a",
27+
),
28+
(
29+
get_type,
30+
float,
31+
"b",
32+
),
33+
(
34+
get_type,
35+
str,
36+
"c",
37+
),
38+
(get_type, bool, "d"),
39+
(
40+
is_literal,
41+
False,
42+
"a",
43+
),
44+
(
45+
is_literal,
46+
False,
47+
"b",
48+
),
49+
(
50+
is_literal,
51+
False,
52+
"c",
53+
),
54+
(is_literal, False, "d"),
55+
(is_literal, True, "e"),
56+
(
57+
is_nullable,
58+
False,
59+
"a",
60+
),
61+
(
62+
is_nullable,
63+
True,
64+
"b",
65+
),
66+
(
67+
is_nullable,
68+
False,
69+
"c",
70+
),
71+
(is_nullable, True, "d"),
72+
(is_nullable, False, "e"),
73+
],
74+
)
75+
def test_field_fn(fn, expected, name):
76+
assert expected == fn(MyModel.model_fields[name])
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Final
2+
3+
import pytest
4+
from models_library.utils.serialization import model_dump_with_secrets
5+
from pydantic import BaseModel, SecretStr
6+
7+
8+
class Credentials(BaseModel):
9+
USERNAME: str | None = None
10+
PASSWORD: SecretStr | None = None
11+
12+
13+
ME: Final[Credentials] = Credentials(USERNAME="DeepThought", PASSWORD=SecretStr("42"))
14+
15+
16+
@pytest.mark.parametrize(
17+
"expected,show_secrets",
18+
[
19+
(
20+
{"USERNAME": "DeepThought", "PASSWORD": "42"},
21+
True,
22+
),
23+
(
24+
{"USERNAME": "DeepThought", "PASSWORD": "**********"},
25+
False, # hide secrets
26+
),
27+
],
28+
)
29+
def test_model_dump_with_secrets(expected: dict, show_secrets: bool):
30+
assert expected == model_dump_with_secrets(ME, show_secrets=show_secrets)

0 commit comments

Comments
 (0)