Skip to content

Commit 1ca2eed

Browse files
pjbullmattijsdp
andauthored
Feat/pydantic serialization (#538) (#539)
* support pydantic serialization * update HISTORY.md * add PR num to HISTORY.md * black change * simplify doc example Co-authored-by: Mattijs De Paepe <[email protected]>
1 parent 28f1d94 commit 1ca2eed

File tree

5 files changed

+34
-0
lines changed

5 files changed

+34
-0
lines changed

HISTORY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# cloudpathlib Changelog
22

3+
## UNRELEASED
4+
- Added support for Pydantic serialization (Issue [#537](https://github.com/drivendataorg/cloudpathlib/issues/537), PR [#538](https://github.com/drivendataorg/cloudpathlib/pull/538))
5+
36
## v0.23.0 (2025-10-07)
47

58
- Added support for Python 3.14 (Issue [#529](https://github.com/drivendataorg/cloudpathlib/issues/529), PR [#530](https://github.com/drivendataorg/cloudpathlib/pull/530))

cloudpathlib/anypath.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ def __get_pydantic_core_schema__(cls, _source_type: Any, _handler):
4848
return core_schema.no_info_after_validator_function(
4949
cls.validate,
5050
core_schema.any_schema(),
51+
serialization=core_schema.plain_serializer_function_ser_schema(
52+
lambda x: str(x),
53+
return_schema=core_schema.str_schema(),
54+
),
5155
)
5256
except ImportError:
5357
return None

cloudpathlib/cloudpath.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,10 @@ def __get_pydantic_core_schema__(cls, _source_type: Any, _handler):
15931593
return core_schema.no_info_after_validator_function(
15941594
cls.validate,
15951595
core_schema.any_schema(),
1596+
serialization=core_schema.plain_serializer_function_ser_schema(
1597+
lambda x: str(x),
1598+
return_schema=core_schema.str_schema(),
1599+
),
15961600
)
15971601
except ImportError:
15981602
return None

docs/docs/integrations.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class MyModel(BaseModel):
1414
inst = MyModel(s3_file="s3://mybucket/myfile.txt")
1515
inst.s3_file
1616
#> S3Path('s3://mybucket/myfile.txt')
17+
inst.model_dump_json()
18+
#> '{"s3_file":"s3://mybucket/myfile.txt"}'
1719
```
1820

1921
This also works with the `AnyPath` polymorphic class. Inputs will get dispatched and instantiated as the appropriate class.
@@ -32,6 +34,23 @@ fancy1.path
3234
fancy2 = FancyModel(path="mydir/myfile.txt")
3335
fancy2.path
3436
#> PosixPath('mydir/myfile.txt')
37+
fancy2.model_dump_json()
38+
#> '{"path":"mydir/myfile.txt"}'
39+
```
40+
41+
As seen above, the default serialization uses the URI but Pydantic supports custom serializers.
42+
43+
```python
44+
from typing import Annotated
45+
from cloudpathlib import S3Path
46+
from pydantic import BaseModel, PlainSerializer
47+
48+
class MyModel(BaseModel):
49+
s3_file: Annotated[S3Path, PlainSerializer(lambda x: x.as_url())]
50+
51+
inst = MyModel(s3_file="s3://mybucket/myfile.txt")
52+
inst.model_dump_json()
53+
#> '{"s3_file":"https://mybucket.s3.amazonaws.com/myfile.txt"}'
3554
```
3655

3756
---

tests/test_integrations.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ class PydanticModel(BaseModel):
1414

1515
obj = PydanticModel(cloud_path=cp)
1616
assert obj.cloud_path == cp
17+
assert obj.model_dump_json() == f'{{"cloud_path":"{cp}"}}'
1718

1819
obj = PydanticModel(cloud_path=str(cp))
1920
assert obj.cloud_path == cp
21+
assert obj.model_dump_json() == f'{{"cloud_path":"{cp}"}}'
2022

2123
with pytest.raises(ValidationError):
2224
_ = PydanticModel(cloud_path=0)
@@ -30,9 +32,11 @@ class PydanticModel(BaseModel):
3032

3133
obj = PydanticModel(any_path=cp)
3234
assert obj.any_path == cp
35+
assert obj.model_dump_json() == f'{{"any_path":"{cp}"}}'
3336

3437
obj = PydanticModel(any_path=str(cp))
3538
assert obj.any_path == cp
39+
assert obj.model_dump_json() == f'{{"any_path":"{cp}"}}'
3640

3741
obj = PydanticModel(any_path=Path("a/b/c"))
3842
assert obj.any_path == Path("a/b/c")

0 commit comments

Comments
 (0)