Skip to content

Commit a47683e

Browse files
committed
feat(python): add preserveUnknownFields option
Pydantic drops fields a model does not declare, so undeclared fields in nested JSON request and response bodies were lost on validation and on serialization back to the API. Add python.preserveUnknownFields (default false). When enabled: - generated models and error bodies allow extra fields, including schemas with additionalProperties: false; explicitly typed additional properties still validate - model serializers read each declared field from the key Pydantic emitted and copy the remaining keys back out - marshal_json keeps nested nulls and only excludes a top-level None - UNSET ignores extra fields - generated test assertions compare only the fields their examples declare, since mock responses can carry more basemodel.py becomes a template, so the review SDK copy now passes through the Python formatter. The option is covered by an echo fragment exercised in the primary (disabled), secondary and quaternary variants.
1 parent e86efdc commit a47683e

31 files changed

Lines changed: 611 additions & 37 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
id: 1792768307-aaecd177
2+
features:
3+
- core
4+
targets:
5+
- pythonv2
6+
type: feat
7+
bump: patch
8+
description: add preserveUnknownFields to keep undeclared fields in JSON request and response models
9+
author: 2ynn
10+
date: "2026-10-23"

‎internal/features/tests.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,7 @@ const (
781781
TestJsonlStreamTimeoutStillBoundsSlowStream
782782
TestJsonlStreamWithTimeoutStreamsToCompletion
783783
TestSharedEnumConstructorDefault
784+
TestUnknownFieldsJsonRoundTrip
784785
)
785786

786787
func GetAllTests() []Test {

‎internal/features/tests_generated.go‎

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎templates/perms.go‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,7 @@ var fileMode = map[string]fs.FileMode{
15431543
"templates/pythonv2/auxiliary/tests/conftest.py.stmpl": 0o644,
15441544
"templates/pythonv2/auxiliary/tests/helpers.py.stmpl": 0o644,
15451545
"templates/pythonv2/auxiliary/tests/test_client.py.stmpl": 0o644,
1546+
"templates/pythonv2/auxiliary/tests/unknown_fields.py.stmpl": 0o644,
15461547
"templates/pythonv2/auxiliary/{{getSourcePath}}": 0o20000000755,
15471548
"templates/pythonv2/auxiliary/{{getSourcePath}}/__init__.py.stmpl": 0o644,
15481549
"templates/pythonv2/auxiliary/{{getSourcePath}}/_version.py.stmpl": 0o644,
@@ -1552,7 +1553,7 @@ var fileMode = map[string]fs.FileMode{
15521553
"templates/pythonv2/auxiliary/{{getSourcePath}}/types": 0o20000000755,
15531554
"templates/pythonv2/auxiliary/{{getSourcePath}}/types/__init__.py": 0o644,
15541555
"templates/pythonv2/auxiliary/{{getSourcePath}}/types/base64fileinput.py": 0o644,
1555-
"templates/pythonv2/auxiliary/{{getSourcePath}}/types/basemodel.py": 0o644,
1556+
"templates/pythonv2/auxiliary/{{getSourcePath}}/types/basemodel.py.stmpl": 0o644,
15561557
"templates/pythonv2/auxiliary/{{getSourcePath}}/utils": 0o20000000755,
15571558
"templates/pythonv2/auxiliary/{{getSourcePath}}/utils/__init__.py.stmpl": 0o644,
15581559
"templates/pythonv2/auxiliary/{{getSourcePath}}/utils/annotations.py": 0o644,
@@ -1668,6 +1669,7 @@ var fileMode = map[string]fs.FileMode{
16681669
"templates/pythonv2/tests/client-credentials/test_client_credentials_hook.py": 0o644,
16691670
"templates/pythonv2/tests/common": 0o20000000755,
16701671
"templates/pythonv2/tests/common/common_helpers.py": 0o644,
1672+
"templates/pythonv2/tests/common/unknown_fields_cases.py": 0o644,
16711673
"templates/pythonv2/tests/custom-http": 0o20000000755,
16721674
"templates/pythonv2/tests/custom-http/test_auth_additional.py": 0o644,
16731675
"templates/pythonv2/tests/oauth2-password": 0o20000000755,
@@ -1702,6 +1704,7 @@ var fileMode = map[string]fs.FileMode{
17021704
"templates/pythonv2/tests/primary/test_telemetry.py.stmpl": 0o644,
17031705
"templates/pythonv2/tests/primary/test_timeout_additional.py": 0o644,
17041706
"templates/pythonv2/tests/primary/test_unions_additional.py": 0o644,
1707+
"templates/pythonv2/tests/primary/test_unknown_fields_additional.py": 0o644,
17051708
"templates/pythonv2/tests/primary/test_validation_errors.py": 0o644,
17061709
"templates/pythonv2/tests/primary/test_x_ndjson_additional.py": 0o644,
17071710
"templates/pythonv2/tests/quaternary": 0o20000000755,
@@ -1711,6 +1714,7 @@ var fileMode = map[string]fs.FileMode{
17111714
"templates/pythonv2/tests/quaternary/test_globals_additional.py": 0o644,
17121715
"templates/pythonv2/tests/quaternary/test_jsonl_streaming_additional.py": 0o644,
17131716
"templates/pythonv2/tests/quaternary/test_servers.py": 0o644,
1717+
"templates/pythonv2/tests/quaternary/test_unknown_fields_additional.py": 0o644,
17141718
"templates/pythonv2/tests/quaternary/test_validation_errors.py": 0o644,
17151719
"templates/pythonv2/tests/secondary": 0o20000000755,
17161720
"templates/pythonv2/tests/secondary/test_auth_additional.py": 0o644,
@@ -1725,6 +1729,7 @@ var fileMode = map[string]fs.FileMode{
17251729
"templates/pythonv2/tests/secondary/test_pagination.py": 0o644,
17261730
"templates/pythonv2/tests/secondary/test_redirects_additional.py": 0o644,
17271731
"templates/pythonv2/tests/secondary/test_servers.py": 0o644,
1732+
"templates/pythonv2/tests/secondary/test_unknown_fields_additional.py": 0o644,
17281733
"templates/pythonv2/tests/security-options": 0o20000000755,
17291734
"templates/pythonv2/tests/security-options/test_auth_additional.py": 0o644,
17301735
"templates/pythonv2/tests/tertiary": 0o20000000755,

‎templates/templates/cli/features.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ function isTestSkipped(test: string): boolean {
359359
"parameters-path-parameter-formats",
360360
"shared-enum-constructor-default",
361361
"open-union-invalid-json-is-an-error",
362+
"unknown-fields-json-round-trip",
362363
].includes(test);
363364
}
364365

‎templates/templates/csharp/features.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ function isTestSkipped(test: string): boolean {
430430
"event-stream-absent-required-union",
431431
"event-stream-equivalent-number-lexemes",
432432
"event-stream-null-and-undeclared-fields",
433+
"unknown-fields-json-round-trip",
433434
].includes(test);
434435
}
435436

‎templates/templates/go/features.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ function isTestSkipped(test: string): boolean {
427427
"event-stream-absent-required-union",
428428
"event-stream-equivalent-number-lexemes",
429429
"event-stream-null-and-undeclared-fields",
430+
"unknown-fields-json-round-trip",
430431
].includes(test);
431432
}
432433

‎templates/templates/javav2/features.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ function isTestSkipped(test: string): boolean {
478478
"event-stream-absent-required-union",
479479
"event-stream-equivalent-number-lexemes",
480480
"event-stream-null-and-undeclared-fields",
481+
"unknown-fields-json-round-trip",
481482
].includes(test);
482483
}
483484

‎templates/templates/mcp-typescript/features.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ function isTestSkipped(test: string): boolean {
687687
"event-stream-absent-required-union",
688688
"event-stream-equivalent-number-lexemes",
689689
"event-stream-null-and-undeclared-fields",
690+
"unknown-fields-json-round-trip",
690691
].includes(test);
691692
}
692693

‎templates/templates/php/features.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ function isTestSkipped(test: string): boolean {
572572
"event-stream-absent-required-union",
573573
"event-stream-equivalent-number-lexemes",
574574
"event-stream-null-and-undeclared-fields",
575+
"unknown-fields-json-round-trip",
575576
].includes(test);
576577
}
577578

0 commit comments

Comments
 (0)