From 77e50e3fd8fa27cbd61c7faf7f6c403a0fe115bb Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 29 Jul 2026 16:49:09 -0400 Subject: [PATCH 1/2] Mark the Kubernetes distribution as typed Python type checkers look for py.typed at the root of an installed package. The generated synchronous and asyncio clients mark their nested packages, but the Kubernetes distribution omitted kubernetes/py.typed, so nested model fields were treated as unknown despite precise generated annotations. Package the root marker in source and wheel distributions, retain the existing client marker, and cover nested synchronous and asyncio model types. Refs #2608. --- MANIFEST.in | 1 + kubernetes/py.typed | 1 + kubernetes/test/test_typing.py | 78 ++++++++++++++++++++++++++++++++++ setup.py | 5 ++- 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 kubernetes/py.typed create mode 100644 kubernetes/test/test_typing.py diff --git a/MANIFEST.in b/MANIFEST.in index b254d3a125..003ddfe299 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,7 @@ include OWNERS include *.md include *.txt include *.ini +include kubernetes/py.typed include kubernetes/client/py.typed exclude .gitignore exclude .gitreview diff --git a/kubernetes/py.typed b/kubernetes/py.typed new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/kubernetes/py.typed @@ -0,0 +1 @@ + diff --git a/kubernetes/test/test_typing.py b/kubernetes/test/test_typing.py new file mode 100644 index 0000000000..15c6cc42e2 --- /dev/null +++ b/kubernetes/test/test_typing.py @@ -0,0 +1,78 @@ +# Copyright 2026 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from importlib.resources import files +import unittest + +from typing_extensions import assert_type + +from kubernetes import client +from kubernetes.aio import client as aio_client + + +class TestPackageTyping(unittest.TestCase): + def test_root_package_is_marked_as_typed(self): + self.assertTrue(files('kubernetes').joinpath('py.typed').is_file()) + + def test_synchronous_nested_model_types(self): + pod = client.V1Pod(status=client.V1PodStatus(container_statuses=[ + client.V1ContainerStatus( + image='image', + image_id='image-id', + name='container', + ready=True, + restart_count=0, + state=client.V1ContainerState(), + ), + ])) + + status = assert_type(pod.status, client.V1PodStatus | None) + assert status is not None + containers = assert_type( + status.container_statuses, + list[client.V1ContainerStatus] | None, + ) + assert containers is not None + state = assert_type( + containers[0].state, + client.V1ContainerState | None, + ) + self.assertIsInstance(state, client.V1ContainerState) + + def test_asynchronous_nested_model_types(self): + pod = aio_client.V1Pod( + status=aio_client.V1PodStatus(container_statuses=[ + aio_client.V1ContainerStatus( + image='image', + image_id='image-id', + name='container', + ready=True, + restart_count=0, + state=aio_client.V1ContainerState(), + ), + ]), + ) + + status = assert_type(pod.status, aio_client.V1PodStatus | None) + assert status is not None + containers = assert_type( + status.container_statuses, + list[aio_client.V1ContainerStatus] | None, + ) + assert containers is not None + state = assert_type( + containers[0].state, + aio_client.V1ContainerState | None, + ) + self.assertIsInstance(state, aio_client.V1ContainerState) diff --git a/setup.py b/setup.py index ed1321c242..892dfa8ff2 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,10 @@ 'kubernetes.leaderelection.resourcelock', 'kubernetes.informer', ], - package_data={'kubernetes.client': ['py.typed']}, + package_data={ + 'kubernetes': ['py.typed'], + 'kubernetes.client': ['py.typed'], + }, include_package_data=True, long_description="Python client for kubernetes http://kubernetes.io/", python_requires='>=3.10', From ca7fa344f8dd1fea047f147eacaa008410f333df Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 29 Jul 2026 20:01:36 -0400 Subject: [PATCH 2/2] Include typing metadata in release distributions The combined synchronous and asyncio release uses setup-release.py rather than setup.py. Include the top-level PEP 561 marker in its package data so published release wheels expose the same nested model types as source installations. --- setup-release.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup-release.py b/setup-release.py index 17fbcbd901..11dd55d7c4 100644 --- a/setup-release.py +++ b/setup-release.py @@ -80,7 +80,10 @@ 'kubernetes.aio.client.api', 'kubernetes.aio.client.models' ], - package_data={'kubernetes.client': ['py.typed']}, + package_data={ + 'kubernetes': ['py.typed'], + 'kubernetes.client': ['py.typed'], + }, include_package_data=True, long_description="Python client for kubernetes http://kubernetes.io/", python_requires='>=3.10',