Feature: 放宽 pydantic compat model dump 类型 (#3898)

This commit is contained in:
呵呵です
2026-03-15 21:50:00 +08:00
committed by GitHub
parent 6090870244
commit 8f7aaf2b21
4 changed files with 58 additions and 16 deletions

View File

@@ -19,7 +19,9 @@ from typing import (
Generic, Generic,
Literal, Literal,
Protocol, Protocol,
TypeAlias,
TypeVar, TypeVar,
cast,
get_args, get_args,
get_origin, get_origin,
overload, overload,
@@ -44,6 +46,16 @@ if TYPE_CHECKING:
CVC = TypeVar("CVC", bound=_CustomValidationClass) CVC = TypeVar("CVC", bound=_CustomValidationClass)
ModelDumpIncEx: TypeAlias = (
set[int]
| set[str]
| dict[int, "ModelDumpIncEx"]
| dict[str, "ModelDumpIncEx"]
| None
)
"""Common include/exclude shape accepted by all supported pydantic versions."""
__all__ = ( __all__ = (
"DEFAULT_CONFIG", "DEFAULT_CONFIG",
"PYDANTIC_V2", "PYDANTIC_V2",
@@ -230,16 +242,17 @@ if PYDANTIC_V2: # pragma: pydantic-v2
def model_dump( def model_dump(
model: BaseModel, model: BaseModel,
include: set[str] | None = None, include: ModelDumpIncEx = None,
exclude: set[str] | None = None, exclude: ModelDumpIncEx = None,
by_alias: bool = False, by_alias: bool = False,
exclude_unset: bool = False, exclude_unset: bool = False,
exclude_defaults: bool = False, exclude_defaults: bool = False,
exclude_none: bool = False, exclude_none: bool = False,
) -> dict[str, Any]: ) -> dict[str, Any]:
return model.model_dump( return model.model_dump(
include=include, # Nested types cannot be inferred correctly
exclude=exclude, include=cast(Any, include),
exclude=cast(Any, exclude),
by_alias=by_alias, by_alias=by_alias,
exclude_unset=exclude_unset, exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults, exclude_defaults=exclude_defaults,
@@ -457,16 +470,16 @@ else: # pragma: pydantic-v1
def model_dump( def model_dump(
model: BaseModel, model: BaseModel,
include: set[str] | None = None, include: ModelDumpIncEx = None,
exclude: set[str] | None = None, exclude: ModelDumpIncEx = None,
by_alias: bool = False, by_alias: bool = False,
exclude_unset: bool = False, exclude_unset: bool = False,
exclude_defaults: bool = False, exclude_defaults: bool = False,
exclude_none: bool = False, exclude_none: bool = False,
) -> dict[str, Any]: ) -> dict[str, Any]:
return model.dict( return model.dict(
include=include, include=cast(Any, include),
exclude=exclude, exclude=cast(Any, exclude),
by_alias=by_alias, by_alias=by_alias,
exclude_unset=exclude_unset, exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults, exclude_defaults=exclude_defaults,

View File

@@ -57,7 +57,7 @@ test = [
"pytest-xdist >=3.0.2, <4.0.0", "pytest-xdist >=3.0.2, <4.0.0",
"coverage-conditional-plugin >=0.9.0, <0.10.0", "coverage-conditional-plugin >=0.9.0, <0.10.0",
] ]
docs = ["nb-autodoc >=1.0.3, <2.0.0"] docs = ["nb-autodoc >=1.0.4, <2.0.0"]
pydantic-v1 = ["pydantic >=1.10.0, <2.0.0"] pydantic-v1 = ["pydantic >=1.10.0, <2.0.0"]
pydantic-v2 = ["pydantic >=2.0.0, <3.0.0"] pydantic-v2 = ["pydantic >=2.0.0, <3.0.0"]

View File

@@ -73,12 +73,41 @@ def test_type_adapter():
def test_model_dump(): def test_model_dump():
class NestedModel(BaseModel):
hidden: int
shown: int
class TestModel(BaseModel): class TestModel(BaseModel):
test1: int test1: int
test2: int test2: int
nested: NestedModel
items: list[NestedModel]
assert model_dump(TestModel(test1=1, test2=2), include={"test1"}) == {"test1": 1} model = TestModel(
assert model_dump(TestModel(test1=1, test2=2), exclude={"test1"}) == {"test2": 2} test1=1,
test2=2,
nested=NestedModel(hidden=3, shown=4),
items=[NestedModel(hidden=5, shown=6)],
)
assert model_dump(model, include={"test1"}) == {"test1": 1}
assert model_dump(model, exclude={"test1"}) == {
"test2": 2,
"nested": {"hidden": 3, "shown": 4},
"items": [{"hidden": 5, "shown": 6}],
}
assert model_dump(model, exclude={"nested": {"hidden"}}) == {
"test1": 1,
"test2": 2,
"nested": {"shown": 4},
"items": [{"hidden": 5, "shown": 6}],
}
assert model_dump(model, exclude={"items": {"__all__": {"hidden"}}}) == {
"test1": 1,
"test2": 2,
"nested": {"hidden": 3, "shown": 4},
"items": [{"shown": 6}],
}
def test_model_validator(): def test_model_validator():

10
uv.lock generated
View File

@@ -1391,15 +1391,15 @@ wheels = [
[[package]] [[package]]
name = "nb-autodoc" name = "nb-autodoc"
version = "1.0.3" version = "1.0.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/0a/b7/46978e3423b8311e1599967c4b91a6c5233b9e2d853aa99a8aef4de4b8fa/nb_autodoc-1.0.3.tar.gz", hash = "sha256:8bb90a20820280adf12a0aad1b94603e9f0c65750d81af8378c6dd6cfb0be400", size = 62703, upload-time = "2025-12-01T08:52:16.891Z" } sdist = { url = "https://files.pythonhosted.org/packages/fc/36/07ffb6434e5a8554f89a5e1f405da67df8399b2f18a221199a3012e10010/nb_autodoc-1.0.4.tar.gz", hash = "sha256:57566cc074aa46de5d367bc6406364ade87956f7d0e6e30ce5fc2af64af912a0", size = 62982, upload-time = "2026-03-15T11:18:07.647Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/70/ce9c943da109599649efed7ca1d8e549515f60de9c8a046e93fe17b10ad4/nb_autodoc-1.0.3-py3-none-any.whl", hash = "sha256:6b5d1d244ee5c5ae896e0c6683193df64e47497986beceaeed2a76ae7235c06b", size = 53029, upload-time = "2025-12-01T08:52:15.301Z" }, { url = "https://files.pythonhosted.org/packages/3d/69/8c46be71fab80e7fee5b474eccf2efd234d5c72022a834807d54766199ba/nb_autodoc-1.0.4-py3-none-any.whl", hash = "sha256:68a75ee461dfc895efd5c31ced653dc7de1983605f65e701541d0eb172290032", size = 53035, upload-time = "2026-03-15T11:18:06.26Z" },
] ]
[[package]] [[package]]
@@ -1516,7 +1516,7 @@ provides-extras = ["websockets", "httpx", "aiohttp", "quart", "fastapi", "all"]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "coverage-conditional-plugin", specifier = ">=0.9.0,<0.10.0" }, { name = "coverage-conditional-plugin", specifier = ">=0.9.0,<0.10.0" },
{ name = "nb-autodoc", specifier = ">=1.0.3,<2.0.0" }, { name = "nb-autodoc", specifier = ">=1.0.4,<2.0.0" },
{ name = "nonebug", specifier = ">=0.4.1,<0.5.0" }, { name = "nonebug", specifier = ">=0.4.1,<0.5.0" },
{ name = "nonemoji", specifier = ">=0.1.2,<0.2.0" }, { name = "nonemoji", specifier = ">=0.1.2,<0.2.0" },
{ name = "pre-commit", specifier = ">=4.0.0,<5.0.0" }, { name = "pre-commit", specifier = ">=4.0.0,<5.0.0" },
@@ -1527,7 +1527,7 @@ dev = [
{ name = "werkzeug", specifier = ">=2.3.6,<4.0.0" }, { name = "werkzeug", specifier = ">=2.3.6,<4.0.0" },
{ name = "wsproto", specifier = ">=1.2.0,<2.0.0" }, { name = "wsproto", specifier = ">=1.2.0,<2.0.0" },
] ]
docs = [{ name = "nb-autodoc", specifier = ">=1.0.3,<2.0.0" }] docs = [{ name = "nb-autodoc", specifier = ">=1.0.4,<2.0.0" }]
pydantic-v1 = [{ name = "pydantic", specifier = ">=1.10.0,<2.0.0" }] pydantic-v1 = [{ name = "pydantic", specifier = ">=1.10.0,<2.0.0" }]
pydantic-v2 = [{ name = "pydantic", specifier = ">=2.0.0,<3.0.0" }] pydantic-v2 = [{ name = "pydantic", specifier = ">=2.0.0,<3.0.0" }]
test = [ test = [