Skip to content

Resolve inconsistent typing in 37.0.0 delete operations #2752

Open

Description

@msfrucht

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.

Examples from:
https://raw.githubusercontent.com/kubernetes-client/python/refs/heads/master/kubernetes/client/api/core_v1_api.py

def delete_namespaced_config_map(...) -> object:
def delete_namespaced_endpoints(...) -> object:
def delete_namespaced_persistent_volume_claim(...) -> V1PersistentVolumeClaim:
def delete_namespaced_pod(...) -> V1Pod:
def delete_namespaced_pod_template(...) -> V1PodTemplate:
def delete_namespaced_resource_quota(...) -> V1ResourceQuota:

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.

Sample code:

>>> import kubernetes
>>> kubernetes.config.load_config()
>>> corev1 = kubernetes.client.api.CoreV1Api()
>>> metadata = kubernetes.client.V1ObjectMeta(name="test", namespace="default")
>>> configmap = kubernetes.client.V1ConfigMap(metadata=metadata)
>>> new_map = corev1.create_namespaced_config_map(namespace="default", body=configmap)
>>> type(new_map)
<class 'kubernetes.client.models.v1_config_map.V1ConfigMap'>
>>> delete_map = corev1.delete_namespaced_config_map(name="test", namespace="default")
>>> type(delete_map)
<class 'dict'>
>>> print(delete_map)
{'kind': 'Status', 'apiVersion': 'v1', 'metadata': {}, 'status': 'Success', 'details': {'name': 'test', 'kind': 'configmaps', 'uid': 'b4e19f90-ed1e-4e44-bed1-0bfbc64e1982'}}
>>> deleted_pot = corev1.delete_namespaced_pod(name="example", namespace="default")
>>> deleted_pod = corev1.delete_namespaced_pod(name="example", namespace="default")
>>> type(deleted_pod)
<class 'kubernetes.client.models.v1_pod.V1Pod'>

Activity

  1. added
    kind/featureCategorizes issue or PR as related to a new feature.
    on Oct 21, 2026
  2. yliaog commented on Oct 22, 2026

    @yliaog
    Contributor

    please checkout kubernetes-client/gen#309

    also cc @tamird

  3. tamird commented on Oct 22, 2026

    @tamird
    Contributor

    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.

    Written with Codex assistance.

  4. anish-yen commented on Oct 23, 2026

    @anish-yen

    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.

  5. tamird commented on Oct 27, 2026

    @tamird
    Contributor

    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.

  6. nicecartoon commented on Oct 28, 2026

    @nicecartoon

    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?

  7. tamird commented on Oct 28, 2026

    @tamird
    Contributor

    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.

    Written with Codex assistance.

  8. msfrucht commented on Oct 28, 2026

    @msfrucht
    Author

    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) 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.

  9. tamird commented on Oct 28, 2026

    @tamird
    Contributor

    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.

    Prepared with Codex assistance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/featureCategorizes issue or PR as related to a new feature.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions