Skip to content

Commit 24404a5

Browse files
Ben Baumgoldclaude
andcommitted
Add tests and docs for use_serializers in CliApp.serialize
Addresses PR feedback requesting test coverage and documentation for the use_serializers parameter added in the previous commits. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9fe1fa9 commit 24404a5

2 files changed

Lines changed: 99 additions & 0 deletions

File tree

docs/index.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,31 @@ print(CliApp.serialize(settings, dict_style='env'))
13151315
"""
13161316
```
13171317

1318+
To use [Pydantic field serializers](https://docs.pydantic.dev/latest/concepts/serialization/#field-serializers) during CLI serialization, pass `use_serializers=True`. This is opt-in (defaults to `False`) to preserve existing behavior.
1319+
1320+
```py
1321+
from pydantic import BaseModel, field_serializer
1322+
1323+
from pydantic_settings import CliApp
1324+
1325+
1326+
class Settings(BaseModel):
1327+
count: int
1328+
1329+
@field_serializer('count')
1330+
def double_count(self, v: int) -> int:
1331+
return v * 2
1332+
1333+
1334+
settings = Settings(count=3)
1335+
1336+
print(CliApp.serialize(settings))
1337+
#> ['--count', '3']
1338+
1339+
print(CliApp.serialize(settings, use_serializers=True))
1340+
#> ['--count', '6']
1341+
```
1342+
13181343
### Mutually Exclusive Groups
13191344

13201345
CLI mutually exclusive groups can be created by inheriting from the `CliMutuallyExclusiveGroup` class.

tests/test_source_cli.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
RootModel,
2424
Tag,
2525
ValidationError,
26+
field_serializer,
2627
field_validator,
2728
model_validator,
2829
)
@@ -3773,3 +3774,76 @@ class Root(BaseSettings, cli_prog_name='example.py'):
37733774
-x int (default: 1)
37743775
"""
37753776
)
3777+
3778+
3779+
def test_cli_serialize_use_serializers_field_serializer():
3780+
"""use_serializers=True applies field_serializer transforms."""
3781+
3782+
class Cfg(BaseModel):
3783+
value: int
3784+
3785+
@field_serializer('value')
3786+
def double_value(self, v: int) -> int:
3787+
return v * 2
3788+
3789+
cfg = Cfg(value=3)
3790+
3791+
# Default (False): raw Python value is used
3792+
assert CliApp.serialize(cfg) == ['--value', '3']
3793+
3794+
# With use_serializers=True: serializer doubles the value
3795+
assert CliApp.serialize(cfg, use_serializers=True) == ['--value', '6']
3796+
3797+
3798+
def test_cli_serialize_use_serializers_default_false():
3799+
"""use_serializers defaults to False, preserving existing behavior."""
3800+
3801+
class Cfg(BaseModel):
3802+
name: str
3803+
3804+
@field_serializer('name')
3805+
def upper_name(self, v: str) -> str:
3806+
return v.upper()
3807+
3808+
cfg = Cfg(name='hello')
3809+
assert CliApp.serialize(cfg) == ['--name', 'hello']
3810+
assert CliApp.serialize(cfg, use_serializers=False) == ['--name', 'hello']
3811+
3812+
3813+
def test_cli_serialize_use_serializers_pydantic_dataclass():
3814+
"""use_serializers=True works with pydantic dataclasses."""
3815+
from pydantic import dataclasses as pydantic_dataclasses
3816+
3817+
@pydantic_dataclasses.dataclass
3818+
class Cfg:
3819+
count: int
3820+
3821+
@field_serializer('count')
3822+
def negate(self, v: int) -> int:
3823+
return -v
3824+
3825+
cfg = Cfg(count=5)
3826+
assert CliApp.serialize(cfg, use_serializers=True) == ['--count', '-5']
3827+
3828+
3829+
def test_cli_serialize_use_serializers_nested():
3830+
"""use_serializers=True is propagated through nested subfields."""
3831+
3832+
class Inner(BaseModel):
3833+
x: int
3834+
3835+
@field_serializer('x')
3836+
def triple(self, v: int) -> int:
3837+
return v * 3
3838+
3839+
class Outer(BaseModel):
3840+
inner: Inner
3841+
y: int
3842+
3843+
cfg = Outer(inner=Inner(x=2), y=4)
3844+
serialized = CliApp.serialize(cfg, use_serializers=True)
3845+
# inner.x should be tripled; y has no serializer so unchanged
3846+
assert '--inner.x' in serialized
3847+
assert serialized[serialized.index('--inner.x') + 1] == '6'
3848+
y_flag = next(f for f in serialized if f.lstrip('-') == 'y')
3849+
assert serialized[serialized.index(y_flag) + 1] == '4'

0 commit comments

Comments
 (0)