Skip to content

Commit

Permalink
Start getting mypy --strict passing (#429)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtomlinson authored Jul 2, 2024
1 parent 0deeb8b commit 501af87
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ repos:
rev: 'v1.10.1'
hooks:
- id: mypy
exclude: "examples"
exclude: "examples|tests|venv|ci|docs|conftest.py"
additional_dependencies: [types-pyyaml>=6.0]
34 changes: 25 additions & 9 deletions kr8s/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
from functools import partial, update_wrapper
from typing import Optional, Union

import kr8s.objects # noqa

from ._api import ALL # noqa
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 # noqa
from ._async_utils import sync as _sync
from ._exceptions import (
APITimeoutError, # noqa
ConnectionClosedError, # noqa
ExecError, # noqa
NotFoundError, # noqa
ServerError, # noqa
APITimeoutError,
ConnectionClosedError,
ExecError,
NotFoundError,
ServerError,
)
from .asyncio import (
api as _api,
Expand Down Expand Up @@ -165,3 +163,21 @@ def whoami():
update_wrapper(watch, _watch)
api_resources = _run_sync(partial(_api_resources, _asyncio=False))
update_wrapper(api_resources, _api_resources)

__all__ = [
"__version__",
"__version_tuple__",
"ALL",
"api",
"api_resources",
"get",
"version",
"watch",
"whoami",
"Api",
"APITimeoutError",
"ConnectionClosedError",
"ExecError",
"NotFoundError",
"ServerError",
]
13 changes: 12 additions & 1 deletion kr8s/_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# SPDX-FileCopyrightText: Copyright (c) 2023-2024, Kr8s Developers (See LICENSE for list)
# SPDX-License-Identifier: BSD 3-Clause License

from typing import Optional

import httpx


class NotFoundError(Exception):
"""Unable to find the requested resource."""

Expand Down Expand Up @@ -27,7 +33,12 @@ class ServerError(Exception):
The httpx response object
"""

def __init__(self, message, status=None, response=None):
def __init__(
self,
message: str,
status: Optional[str] = None,
response: Optional[httpx.Response] = None,
) -> None:
self.status = status
self.response = response
super().__init__(message)
8 changes: 4 additions & 4 deletions kr8s/_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ async def get(
cls, name, namespace=namespace, **kwargs
)
except ServerError as e:
if e.response.status_code == 404:
if e.response and e.response.status_code == 404:
continue
raise e
elif label_selector or field_selector:
Expand Down Expand Up @@ -297,7 +297,7 @@ async def delete(self, propagation_policy: Optional[str] = None) -> None:
) as resp:
self.raw = resp.json()
except ServerError as e:
if e.response.status_code == 404:
if e.response and e.response.status_code == 404:
raise NotFoundError(f"Object {self.name} does not exist") from e
raise e

Expand All @@ -316,7 +316,7 @@ async def async_refresh(self) -> None:
) as resp:
self.raw = resp.json()
except ServerError as e:
if e.response.status_code == 404:
if e.response and e.response.status_code == 404:
raise NotFoundError(f"Object {self.name} does not exist") from e
raise e

Expand Down Expand Up @@ -344,7 +344,7 @@ async def async_patch(self, patch: Dict, *, subresource=None, type=None) -> None
) as resp:
self.raw = resp.json()
except ServerError as e:
if e.response.status_code == 404:
if e.response and e.response.status_code == 404:
raise NotFoundError(f"Object {self.name} does not exist") from e
raise e

Expand Down
3 changes: 2 additions & 1 deletion kr8s/_testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
# SPDX-License-Identifier: BSD 3-Clause License
import contextlib
import os
from typing import Generator


@contextlib.contextmanager
def set_env(**environ):
def set_env(**environ: str) -> Generator[None, None, None]:
"""
Temporarily set the process environment variables.
Expand Down
8 changes: 5 additions & 3 deletions kr8s/asyncio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-FileCopyrightText: Copyright (c) 2023-2024, Kr8s Developers (See LICENSE for list)
# SPDX-License-Identifier: BSD 3-Clause License
from kr8s._api import Api # noqa
from kr8s._api import Api

from ._api import api # noqa
from ._helpers import api_resources, get, version, watch, whoami # noqa
from ._api import api
from ._helpers import api_resources, get, version, watch, whoami

__all__ = ["api", "api_resources", "get", "version", "watch", "whoami", "Api"]
59 changes: 30 additions & 29 deletions kr8s/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
from kr8s.asyncio.objects import Pod, Table


async def test_factory_bypass():
async def test_factory_bypass() -> None:
with pytest.raises(ValueError, match="kr8s.api()"):
_ = kr8s.Api()
assert not kr8s.Api._instances
_ = kr8s.api()
assert kr8s.Api._instances


async def test_api_factory(serviceaccount):
async def test_api_factory(serviceaccount) -> None:
k1 = await kr8s.asyncio.api()
k2 = await kr8s.asyncio.api()
assert k1 is k2
Expand All @@ -35,7 +35,7 @@ async def test_api_factory(serviceaccount):
assert p.api is not k3


def test_api_factory_threaded():
def test_api_factory_threaded() -> None:
assert len(kr8s.Api._instances) == 0

q = queue.Queue()
Expand Down Expand Up @@ -66,7 +66,7 @@ async def create_api(q):
assert type(k1) is type(k2)


def test_api_factory_multi_event_loop():
def test_api_factory_multi_event_loop() -> None:
assert len(kr8s.Api._instances) == 0

async def create_api():
Expand All @@ -77,7 +77,7 @@ async def create_api():
assert k1 is not k2


async def test_api_factory_with_kubeconfig(k8s_cluster, serviceaccount):
async def test_api_factory_with_kubeconfig(k8s_cluster, serviceaccount) -> None:
k1 = await kr8s.asyncio.api(kubeconfig=k8s_cluster.kubeconfig_path)
k2 = await kr8s.asyncio.api(serviceaccount=serviceaccount)
k3 = await kr8s.asyncio.api()
Expand All @@ -96,30 +96,30 @@ async def test_api_factory_with_kubeconfig(k8s_cluster, serviceaccount):
assert p3.api is not k2


def test_version_sync():
def test_version_sync() -> None:
api = kr8s.api()
version = api.version()
assert "major" in version


async def test_version_sync_in_async():
async def test_version_sync_in_async() -> None:
api = kr8s.api()
version = api.version()
assert "major" in version


async def test_version():
async def test_version() -> None:
api = await kr8s.asyncio.api()
version = await api.version()
assert "major" in version


def test_helper_version():
def test_helper_version() -> None:
version = kr8s.version()
assert "major" in version


async def test_concurrent_api_creation():
async def test_concurrent_api_creation() -> None:
async def get_api():
api = await kr8s.asyncio.api()
await api.version()
Expand All @@ -129,7 +129,7 @@ async def get_api():
tg.start_soon(get_api)


async def test_both_api_creation_methods_together():
async def test_both_api_creation_methods_together() -> None:
async_api = await kr8s.asyncio.api()
api = kr8s.api()

Expand All @@ -144,30 +144,30 @@ async def test_both_api_creation_methods_together():
assert api.get("ns")[0]._asyncio is False


async def test_bad_api_version():
async def test_bad_api_version() -> None:
api = await kr8s.asyncio.api()
with pytest.raises(ValueError):
async with api.call_api("GET", version="foo"):
pass # pragma: no cover


@pytest.mark.parametrize("namespace", [kr8s.ALL, "kube-system"])
async def test_get_pods(namespace):
async def test_get_pods(namespace) -> None:
pods = await kr8s.asyncio.get("pods", namespace=namespace)
assert isinstance(pods, list)
assert len(pods) > 0
assert isinstance(pods[0], Pod)


async def test_get_pods_as_table():
async def test_get_pods_as_table() -> None:
api = await kr8s.asyncio.api()
pods = await api.get("pods", namespace="kube-system", as_object=Table)
assert isinstance(pods, Table)
assert len(pods.rows) > 0
assert not await pods.exists() # Cannot exist in the Kubernetes API


async def test_watch_pods(example_pod_spec, ns):
async def test_watch_pods(example_pod_spec, ns) -> None:
pod = await Pod(example_pod_spec)
await pod.create()
while not await pod.ready():
Expand All @@ -186,33 +186,33 @@ async def test_watch_pods(example_pod_spec, ns):
break


async def test_get_deployments():
async def test_get_deployments() -> None:
api = await kr8s.asyncio.api()
deployments = await api.get("deployments")
assert isinstance(deployments, list)


async def test_get_class():
async def test_get_class() -> None:
api = await kr8s.asyncio.api()
pods = await api.get(Pod, namespace=kr8s.ALL)
assert isinstance(pods, list)
assert len(pods) > 0
assert isinstance(pods[0], Pod)


async def test_api_versions():
async def test_api_versions() -> None:
api = await kr8s.asyncio.api()
versions = [version async for version in api.api_versions()]
assert "apps/v1" in versions


def test_api_versions_sync():
def test_api_versions_sync() -> None:
api = kr8s.api()
versions = [version for version in api.api_versions()]
assert "apps/v1" in versions


async def test_api_resources():
async def test_api_resources() -> None:
resources = await kr8s.asyncio.api_resources()

names = [r["name"] for r in resources]
Expand All @@ -235,31 +235,31 @@ async def test_api_resources():
assert "deploy" in deployment["shortNames"]


async def test_ns(ns):
async def test_ns(ns) -> None:
api = await kr8s.asyncio.api(namespace=ns)
assert ns == api.namespace

api.namespace = "foo"
assert api.namespace == "foo"


async def test_async_get_returns_async_objects():
async def test_async_get_returns_async_objects() -> None:
pods = await kr8s.asyncio.get("pods", namespace=kr8s.ALL)
assert pods[0]._asyncio is True


def test_sync_get_returns_sync_objects():
def test_sync_get_returns_sync_objects() -> None:
pods = kr8s.get("pods", namespace=kr8s.ALL)
assert pods[0]._asyncio is False


def test_sync_api_returns_sync_objects():
def test_sync_api_returns_sync_objects() -> None:
api = kr8s.api()
pods = api.get("pods", namespace=kr8s.ALL)
assert pods[0]._asyncio is False


async def test_api_names(example_pod_spec, ns):
async def test_api_names(example_pod_spec: dict, ns: str) -> None:
pod = await Pod(example_pod_spec)
await pod.create()
assert pod in await kr8s.asyncio.get("pods", namespace=ns)
Expand All @@ -275,17 +275,17 @@ async def test_api_names(example_pod_spec, ns):
await kr8s.asyncio.get("roles.rbac.authorization.k8s.io/v1", namespace=ns)


async def test_whoami():
async def test_whoami() -> None:
api = await kr8s.asyncio.api()
assert await kr8s.asyncio.whoami() == await api.whoami()


async def test_whoami_sync():
async def test_whoami_sync() -> None:
api = kr8s.api()
assert kr8s.whoami() == api.whoami()


async def test_api_resources_cache(caplog):
async def test_api_resources_cache(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level("INFO")
api = await kr8s.asyncio.api()
await api.api_resources()
Expand All @@ -294,12 +294,13 @@ async def test_api_resources_cache(caplog):
assert caplog.text.count('/apis/ "HTTP/1.1 200 OK"') == 1


async def test_api_timeout():
async def test_api_timeout() -> None:
from httpx import Timeout

api = await kr8s.asyncio.api()
api.timeout = 10
await api.version()
assert api._session
assert api._session.timeout.read == 10
api.timeout = 20
await api.version()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,4 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
target-version = "py310"

[tool.mypy]
exclude = ["examples"]
exclude = ["examples", "tests", "venv", "ci", "docs", "conftest.py"]

0 comments on commit 501af87

Please sign in to comment.