Skip to content

Since 37.0.0, kubernetes.utils breaks the typing spec's rules leading to pyright errors #2724

Description

@r4victor

What happened (please include outputs or screenshots):

Starting with 37.0.0, the kubernetes package includes a py.typed marker (added in #2657), which declares it a typed library. A typed library must follow the library interface rules of the typing spec: an imported name is private unless it is re-exported with import X as X or from Y import X as X, or is listed in __all__. kubernetes/utils/__init__.py breaks these rules: it imports its public functions without re-exporting them, so under the spec, create_from_yaml, parse_quantity, and the other functions are private names of kubernetes.utils. Type checkers that follow the spec, such as pyright, report every documented utils.<function> call as an error:

repro.py:3:7 - error: "create_from_yaml" is not exported from module "kubernetes.utils" (reportPrivateImportUsage)
repro.py:4:7 - error: "parse_quantity" is not exported from module "kubernetes.utils" (reportPrivateImportUsage)

We hit this in the dstack CI right after 37.0.0 was released: https://github.com/dstackai/dstack/actions/runs/37585855653/job/112676202973

These are the imports in kubernetes/utils/__init__.py:

from .create_from_yaml import (FailToCreateError, create_from_dict,
create_from_yaml, create_from_directory)
from .quantity import parse_quantity
from .duration import parse_duration
from .metrics import (get_nodes_metrics, get_pods_metrics,
get_pods_metrics_in_all_namespaces)
from .retry import (Backoff, DEFAULT_BACKOFF, DEFAULT_RETRY,
DEFAULT_RETRY_AFTER_BACKOFF, is_conflict,
is_retry_after_response, is_too_many_requests, on_error,
on_retry_after_error, retry_after_backoff,
retry_after_max_retries, retry_on_conflict,
retry_after_seconds)
from .keepalive import tcp_keepalive_socket_options

The official examples use the same pattern, for example:

from kubernetes import client, config, utils
def main():
config.load_kube_config()
k8s_client = client.ApiClient()
yaml_file = 'examples/yaml_dir/configmap-demo-pod.yml'
utils.create_from_yaml(k8s_client,yaml_file,verbose=True)

examples/apply_from_directory.py, examples/apply_from_dict.py, and examples/metrics_example.py are affected in the same way, and so is kubernetes.aio.utils, which examples_asyncio/pod_exec.py uses.

What you expected to happen:

kubernetes.utils and kubernetes.aio.utils re-export their public functions as the library interface rules require, so that from kubernetes import utils followed by utils.create_from_yaml(...) type-checks without errors, as it did with 36.0.3.

How to reproduce it (as minimally and precisely as possible):

# repro.py
from kubernetes import client, utils

utils.create_from_yaml(client.ApiClient(), "manifest.yaml")
utils.parse_quantity("1Gi")
pip install kubernetes==37.0.0 pyright
pyright repro.py

With kubernetes==36.0.3, pyright reports no errors for the same file.

Anything else we need to know?:

One possible fix is to add an __all__ list with the imported names to kubernetes/utils/__init__.py and kubernetes/aio/utils/__init__.py. Another is to use redundant aliases, such as from .quantity import parse_quantity as parse_quantity. As a workaround, users can import from the module that defines the function, for example from kubernetes.utils.quantity import parse_quantity.

mypy applies these rules only with --no-implicit-reexport or --strict, which may be why the problem went unnoticed.

Environment:

  • Kubernetes version (kubectl version): N/A (static type checking)
  • OS (e.g., MacOS 10.13.6): Ubuntu (GitHub Actions ubuntu-latest), macOS
  • Python version (python --version): 3.10, 3.12
  • Python client version (pip list | grep kubernetes): 37.0.0
  • pyright: 1.1.403

Activity

  1. yliaog commented on Oct 7, 2026

    @yliaog
    Contributor

    @r4victor could you file a PR for the fix?

  2. r4victor commented on Oct 8, 2026

    @r4victor
    ContributorAuthor

    @yliaog , yes, will do shortly.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions