Skip to content

Commit 6aed1be

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 6aed1be

3 files changed

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

0 commit comments

Comments
 (0)