Skip to content

Commit

Permalink
Replace @sync decorator with APIObjectSyncMixin for all sync objects (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtomlinson authored Jan 15, 2025
1 parent 06794cc commit 0d74bab
Show file tree
Hide file tree
Showing 10 changed files with 523 additions and 192 deletions.
3 changes: 2 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import gc
import os
import time
from collections.abc import Generator

import pytest
from pytest_kind.cluster import KindCluster
Expand All @@ -17,7 +18,7 @@ def ensure_gc():


@pytest.fixture(scope="session", autouse=True)
def k8s_cluster(request) -> KindCluster:
def k8s_cluster(request) -> Generator[KindCluster, None, None]:
image = None
if version := os.environ.get("KUBERNETES_VERSION"):
image = f"kindest/node:v{version}"
Expand Down
89 changes: 74 additions & 15 deletions kr8s/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@
At the top level, `kr8s` provides a synchronous API that wraps the asynchronous API provided by `kr8s.asyncio`.
Both APIs are functionally identical with the same objects, method signatures and return values.
"""
# Disable missing docstrings, these are inherited from the async version of the objects
# ruff: noqa: D102
from __future__ import annotations

from functools import partial, update_wrapper
from typing import Dict, Optional, Type, Union
from typing import Generator

from . import asyncio, objects, portforward
from ._api import ALL
from ._api import Api as _AsyncApi
from ._async_utils import run_sync as _run_sync
from ._async_utils import sync as _sync
from ._exceptions import (
APITimeoutError,
ConnectionClosedError,
ExecError,
NotFoundError,
ServerError,
)
from ._objects import APIObject
from .asyncio import (
api as _api,
)
Expand Down Expand Up @@ -48,18 +52,73 @@
__version_tuple__ = (0, 0, 0)


@_sync
class Api(_AsyncApi):
__doc__ = _AsyncApi.__doc__
_asyncio = False

def version(self) -> dict: # type: ignore
return _run_sync(self.async_version)() # type: ignore

def reauthenticate(self): # type: ignore
return _run_sync(self.async_reauthenticate)() # type: ignore

def whoami(self): # type: ignore
return _run_sync(self.async_whoami)() # type: ignore

def lookup_kind(self, kind) -> tuple[str, str, bool]: # type: ignore
return _run_sync(self.async_lookup_kind)(kind) # type: ignore

def get( # type: ignore
self,
kind: str | type,
*names: str,
namespace: str | None = None,
label_selector: str | dict | None = None,
field_selector: str | dict | None = None,
as_object: type[APIObject] | None = None,
allow_unknown_type: bool = True,
**kwargs,
) -> Generator[APIObject]:
yield from _run_sync(self.async_get)(
kind,
*names,
namespace=namespace,
label_selector=label_selector,
field_selector=field_selector,
as_object=as_object,
allow_unknown_type=allow_unknown_type,
**kwargs,
)

def watch( # type: ignore
self,
kind: str,
namespace: str | None = None,
label_selector: str | dict | None = None,
field_selector: str | dict | None = None,
since: str | None = None,
) -> Generator[tuple[str, APIObject]]:
yield from _run_sync(self.async_watch)(
kind,
namespace=namespace,
label_selector=label_selector,
field_selector=field_selector,
since=since,
)

def api_resources(self) -> list[dict]: # type: ignore
return _run_sync(self.async_api_resources)() # type: ignore

def api_versions(self) -> Generator[str]: # type: ignore
yield from _run_sync(self.async_api_versions)()


def get(
kind: str,
*names: str,
namespace: Optional[str] = None,
label_selector: Optional[Union[str, Dict]] = None,
field_selector: Optional[Union[str, Dict]] = None,
as_object: Optional[Type] = None,
namespace: str | None = None,
label_selector: str | dict | None = None,
field_selector: str | dict | None = None,
as_object: type | None = None,
allow_unknown_type: bool = True,
api=None,
**kwargs,
Expand Down Expand Up @@ -109,12 +168,12 @@ def get(


def api(
url: Optional[str] = None,
kubeconfig: Optional[str] = None,
serviceaccount: Optional[str] = None,
namespace: Optional[str] = None,
context: Optional[str] = None,
) -> Union[Api, _AsyncApi]:
url: str | None = None,
kubeconfig: str | None = None,
serviceaccount: str | None = None,
namespace: str | None = None,
context: str | None = None,
) -> Api:
"""Create a :class:`kr8s.Api` object for interacting with the Kubernetes API.
If a kr8s object already exists with the same arguments in this thread, it will be returned.
Expand Down Expand Up @@ -142,7 +201,7 @@ def api(
context=context,
_asyncio=False,
)
assert isinstance(ret, (Api, _AsyncApi))
assert isinstance(ret, Api)
return ret


Expand Down
37 changes: 20 additions & 17 deletions kr8s/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ async def async_version(self) -> dict:

async def reauthenticate(self) -> None:
"""Reauthenticate the API."""
return await self.async_reauthenticate()

async def async_reauthenticate(self) -> None:
await self.auth.reauthenticate()

async def whoami(self):
Expand Down Expand Up @@ -293,6 +296,22 @@ async def async_whoami(self):
[name] = cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)
return name.value

async def lookup_kind(self, kind) -> tuple[str, str, bool]:
"""Lookup a Kubernetes resource kind.
Check whether a resource kind exists on the remote server.
Args:
kind: The kind of resource to lookup.
Returns:
The kind of resource, the plural form and whether the resource is namespaced
Raises:
ValueError: If the kind is not found.
"""
return await self.async_lookup_kind(kind)

async def async_lookup_kind(self, kind) -> tuple[str, str, bool]:
"""Lookup a Kubernetes resource kind."""
from ._objects import parse_kind
Expand Down Expand Up @@ -321,22 +340,6 @@ async def async_lookup_kind(self, kind) -> tuple[str, str, bool]:
)
raise ValueError(f"Kind {kind} not found.")

async def lookup_kind(self, kind) -> tuple[str, str, bool]:
"""Lookup a Kubernetes resource kind.
Check whether a resource kind exists on the remote server.
Args:
kind: The kind of resource to lookup.
Returns:
The kind of resource, the plural form and whether the resource is namespaced
Raises:
ValueError: If the kind is not found.
"""
return await self.async_lookup_kind(kind)

@contextlib.asynccontextmanager
async def async_get_kind(
self,
Expand Down Expand Up @@ -542,7 +545,7 @@ async def watch(
label_selector: str | dict | None = None,
field_selector: str | dict | None = None,
since: str | None = None,
):
) -> AsyncGenerator[tuple[str, APIObject]]:
"""Watch a Kubernetes resource."""
async for t, object in self.async_watch(
kind,
Expand Down
Loading

0 comments on commit 0d74bab

Please sign in to comment.