You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Resolve inconsistent typing in 37.0.0 delete operations #2752
What is the feature and why do you need it:
The Pydantic migration in 37.0.0 added strong typing, mostly, to the forward facing APIs. Which I greatly appreciate.
Where delete calls used to return V1Status they now generally return the appropriate model for their api type which matches the other APIs including golang.
Unfortunately, the typing is inconsistent on delete calls making it difficult to deal with.
Describe the solution you'd like to see:
Resolve the inconsistent typehinting on delete calls. For whatever reason create, patch, replace don't have this problem.
The dictionary results were intentional, but the object annotation leaves callers with too little information.
A successful Kubernetes DELETE can return either the resource or a Status. With Pydantic validation, treating a returned Job as V1Status fails because the Job's status is an object whereas V1Status.status is a string. #2664 preserves both response shapes as dictionaries and adds regression coverage for each.
gen#309 applied this correction only to individual-delete responses declared as Status in the specification. It left responses already declared as resources, such as Pod, unchanged. That explains the different return types in your examples.
dict[str, object] would describe the current dictionary results more precisely. Returning model instances would require deserialization that handles both the resource and V1Status; the ConfigMap example here already shows why V1ConfigMap alone would be incorrect, since the server returned kind: Status.
That makes sense, thank you both. dict[str, object] matches what the client actually returns today, and I can put together the annotation change in postprocess_python.py with tests plus the regenerated client if you would like. The prototype narrows only the individual deletes that currently annotate object; resource typed deletes and deletecollection stay as they are.
I chatted with codex and started work on modernizing the underlying OpenAPI schema to v3 which might allow better generated bindings. See kubernetes/kube-openapi#658.
If we are moving away from object, should we be annotating these as Union[V1Status, V1ConfigMap] or similar, given that a delete can return either type depending on the propagationPolicy?
Union[V1ConfigMap, V1Status] is what we’d want the generator to produce for delete_namespaced_config_map. We don’t write that annotation by hand. The Swagger 2 schema lists only Status, even though the server can also return a ConfigMap. Our preprocessing step uses object so the client can preserve either response as a dictionary.
That’s why we’re working on the OpenAPI v3 schema. We want Kubernetes to describe both possible responses so the Python generator can use that information to produce the union and decode each response into the right model.
Thanks for the help at any rate. Being able to use Pydantic with the k8s Python client was a breath of fresh air in the first place and don't want to lessen that thanks.
From what I gather trying some raw requests the Union[<object type>, V1Status] seem to be the most correct as k8s can return one or the other depending on a variety of conditions.
Looks like a pretty good reason why the go-client instead of returning an object in the first place is just an error interface. And controller-runtime is similar. Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
Parsing a delete result was a mess in general even before 37.
37 also made dealing with custom objects enormously easier by being able to define Pydantic models for them based on the existing models such as V1ObjectMeta and run them through CustomObjectsApi with model_dump for CREATE/PUT and model_validate on the GET/LIST/WATCH side. Made handling these rather pleasant instead of the previous nightmare. I suspect with a bit more work it might be possible to even write a typed api with generics to handle these.
I opened the generator change and the Python client change. With those changes, delete_namespaced_config_map() returns a V1ConfigMap when the server sends a ConfigMap and a V1Status when it sends a Status. That covers ordinary JSON responses; requesting a metadata-only representation is a separate case.
I’m proposing this for the next major release because 37.x callers were told to use dictionary access. The migration plan explains how we can use the v3 schema later without changing those return types again.
Activity
yliaog commentedon Oct 22, 2026
please checkout kubernetes-client/gen#309
also cc @tamird
tamird commentedon Oct 22, 2026
The dictionary results were intentional, but the
objectannotation leaves callers with too little information.A successful Kubernetes DELETE can return either the resource or a
Status. With Pydantic validation, treating a returned Job asV1Statusfails because the Job'sstatusis an object whereasV1Status.statusis a string. #2664 preserves both response shapes as dictionaries and adds regression coverage for each.gen#309 applied this correction only to individual-delete responses declared as
Statusin the specification. It left responses already declared as resources, such as Pod, unchanged. That explains the different return types in your examples.dict[str, object]would describe the current dictionary results more precisely. Returning model instances would require deserialization that handles both the resource andV1Status; the ConfigMap example here already shows whyV1ConfigMapalone would be incorrect, since the server returnedkind: Status.Written with Codex assistance.
anish-yen commentedon Oct 23, 2026
That makes sense, thank you both.
dict[str, object]matches what the client actually returns today, and I can put together the annotation change inpostprocess_python.pywith tests plus the regenerated client if you would like. The prototype narrows only the individual deletes that currently annotateobject; resource typed deletes and deletecollection stay as they are.tamird commentedon Oct 27, 2026
I chatted with codex and started work on modernizing the underlying OpenAPI schema to v3 which might allow better generated bindings. See kubernetes/kube-openapi#658.
nicecartoon commentedon Oct 28, 2026
If we are moving away from
object, should we be annotating these asUnion[V1Status, V1ConfigMap]or similar, given that a delete can return either type depending on thepropagationPolicy?tamird commentedon Oct 28, 2026
Union[V1ConfigMap, V1Status]is what we’d want the generator to produce fordelete_namespaced_config_map. We don’t write that annotation by hand. The Swagger 2 schema lists onlyStatus, even though the server can also return a ConfigMap. Our preprocessing step usesobjectso the client can preserve either response as a dictionary.That’s why we’re working on the OpenAPI v3 schema. We want Kubernetes to describe both possible responses so the Python generator can use that information to produce the union and decode each response into the right model.
Written with Codex assistance.
msfrucht commentedon Oct 28, 2026
Thanks for the help at any rate. Being able to use Pydantic with the k8s Python client was a breath of fresh air in the first place and don't want to lessen that thanks.
From what I gather trying some raw requests the
Union[<object type>, V1Status]seem to be the most correct as k8s can return one or the other depending on a variety of conditions.And still may not even return a full copy of the object and may just include metadata. https://kubernetes.io/docs/reference/using-api/api-concepts/#metadata-only-fetches
Looks like a pretty good reason why the go-client instead of returning an object in the first place is just an error interface. And controller-runtime is similar.
Delete(ctx context.Context, name string, opts metav1.DeleteOptions) errorParsing a delete result was a mess in general even before 37.
37 also made dealing with custom objects enormously easier by being able to define Pydantic models for them based on the existing models such as V1ObjectMeta and run them through CustomObjectsApi with model_dump for CREATE/PUT and model_validate on the GET/LIST/WATCH side. Made handling these rather pleasant instead of the previous nightmare. I suspect with a bit more work it might be possible to even write a typed api with generics to handle these.
tamird commentedon Oct 28, 2026
I opened the generator change and the Python client change. With those changes,
delete_namespaced_config_map()returns aV1ConfigMapwhen the server sends a ConfigMap and aV1Statuswhen it sends a Status. That covers ordinary JSON responses; requesting a metadata-only representation is a separate case.I’m proposing this for the next major release because 37.x callers were told to use dictionary access. The migration plan explains how we can use the v3 schema later without changing those return types again.
Prepared with Codex assistance.