Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .github/workflows/black.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
python-version: "3.14"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ jobs:
fail-fast: false
matrix:
python-version:
- '3.9'
- '3.10'
- '3.13'
- '3.14'

steps:
- uses: actions/checkout@v5
Expand All @@ -36,17 +36,17 @@ jobs:
python -m pip install -r requirements.txt
python -m pip install -e .[dev]

- name: Lint with flake8
- name: Lint with Ruff
run: |
flake8 . --count --show-source --statistics
ruff check .

- name: Check typing with mypy
- name: Check formatting with Ruff
run: |
mypy --install-types --non-interactive .
ruff format --check .

- name: Check syntax with pyupgrade
- name: Check typing with mypy
run: |
find . -type f -regex '.*\.py$' -exec pyupgrade --py39-plus {} \;
mypy --install-types --non-interactive .

- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.12.0
Expand Down
49 changes: 28 additions & 21 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
[tool.black]
[tool.ruff]
line-length = 120
target-version = ['py39']
exclude = '''
target-version = "py311"
exclude = [
".eggs",
".git",
".hg",
".mypy_cache",
".tox",
".venv",
"_build",
"buck-out",
"build",
"dist",
"documentation",
"es_utils",
"alembic",
]

(
/(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
| es_utils
| alembic
| documentation
)
'''
[tool.ruff.lint]
select = [
"E",
"F",
"I",
"UP",
]
ignore = [
"E203",
"E501",
]

[tool.bandit]
exclude_dirs = ["./.git", "./kubernetes", "./documentation", "./src/wiremind_kubernetes/tests", "./.venv", "./build"]
Expand Down
50 changes: 16 additions & 34 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,54 +1,36 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --no-emit-index-url setup.py
#
cachetools==5.2.0
# via google-auth
certifi==2024.7.4
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml --no-emit-index-url --python-platform=linux --output-file=requirements.txt
certifi==2026.2.25
# via
# kubernetes
# requests
charset-normalizer==2.1.1
charset-normalizer==3.4.5
# via requests
google-auth==2.11.1
durationpy==0.10
# via kubernetes
idna==3.7
idna==3.11
# via requests
kubernetes==24.2.0
# via wiremind-kubernetes (setup.py)
oauthlib==3.2.2
kubernetes==35.0.0
# via wiremind-kubernetes (pyproject.toml)
oauthlib==3.3.1
# via requests-oauthlib
pyasn1==0.4.8
# via
# pyasn1-modules
# rsa
pyasn1-modules==0.2.8
# via google-auth
python-dateutil==2.8.2
python-dateutil==2.9.0.post0
# via kubernetes
pyyaml==6.0
pyyaml==6.0.3
# via kubernetes
requests==2.32.4
requests==2.32.5
# via
# kubernetes
# requests-oauthlib
requests-oauthlib==1.3.1
requests-oauthlib==2.0.0
# via kubernetes
rsa==4.9
# via google-auth
six==1.16.0
six==1.17.0
# via
# google-auth
# kubernetes
# python-dateutil
urllib3==1.26.19
urllib3==2.6.3
# via
# kubernetes
# requests
websocket-client==1.4.1
websocket-client==1.9.0
# via kubernetes

# The following packages are considered to be unsafe in a requirements file:
# setuptools
27 changes: 2 additions & 25 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
[options]
python_requires = >= 3.9

[flake8]
max-line-length = 120
# Enable flake8-mutable
enable-extensions = M511
# W503 line break before binary operator. Black causes this error
# E203 whitespace before ':'
# E231 missing whitespace after ','. Black causes this error but seems to be right (see pep8)
# E501 line too long
# Q000 Double quotes found but single quotes preferred
ignore = W503, E203, E231, E501, Q000
jobs = 4
python_requires = >= 3.13

[mypy]
python_version = 3.9
python_version = 3.13
ignore_missing_imports = True
check_untyped_defs = True
disallow_untyped_defs = True
Expand All @@ -26,17 +14,6 @@ no_implicit_optional = True
show_error_codes = True
files = src

[isort]
src_paths = src,tests
profile = black
line_length = 120
sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
no_lines_before = LOCALFOLDER
multi_line_output = 3
include_trailing_comma = True
use_parentheses = True
force_grid_wrap = 0

[tool:pytest]
log_level=INFO
# Deterministic ordering for tests; useful for pytest-xdist.
Expand Down
14 changes: 6 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
wiremind-kubernetes
"""

from setuptools import find_packages, setup

with open("VERSION") as version_file:
Expand All @@ -16,13 +17,7 @@
]
extra_require_dev = (
[
"flake8",
"black",
"isort",
"bandit",
"flake8-mutable",
"pip-tools>=3.7.0",
"pyupgrade",
"ruff",
"safety",
]
+ extra_require_mypy
Expand Down Expand Up @@ -55,11 +50,14 @@
"mypy": extra_require_mypy,
"test": extra_require_test,
},
python_requires=">=3.9.0",
python_requires=">=3.13.0",
keywords=["kubernetes"],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
],
)
6 changes: 5 additions & 1 deletion src/wiremind_kubernetes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# noqa: F401
from .kubernetes_helper import KubernetesDeploymentManager, KubernetesHelper, NamespacedKubernetesHelper # noqa: F401
from .kubernetes_helper import (
KubernetesDeploymentManager, # noqa: F401
KubernetesHelper, # noqa: F401
NamespacedKubernetesHelper, # noqa: F401
)
from .utils import run_command # noqa: F401
5 changes: 1 addition & 4 deletions src/wiremind_kubernetes/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
from typing import Optional


class WiremindKubernetesException(Exception):
"""
Base wiremind-kubernetes Exception.
"""

message: str

def __init__(self, message: Optional[str] = None):
def __init__(self, message: str | None = None):
super().__init__()
if message:
self.message = message
Expand Down
7 changes: 4 additions & 3 deletions src/wiremind_kubernetes/kube_config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import logging
import os
from typing import Optional

import kubernetes

logger = logging.getLogger(__name__)


def _load_kubeconfig(config_file: Optional[str] = None, context: Optional[str] = None) -> None:
def _load_kubeconfig(config_file: str | None = None, context: str | None = None) -> None:
kubernetes.config.load_kube_config(config_file=config_file, context=context)
logger.debug("Kubernetes configuration successfully set.")

Expand All @@ -18,7 +17,9 @@ def _load_incluster_config() -> None:


def load_kubernetes_config(
use_kubeconfig: Optional[bool] = None, config_file: Optional[str] = None, context: Optional[str] = None
use_kubeconfig: bool | None = None,
config_file: str | None = None,
context: str | None = None,
) -> None:
"""
Load kubernetes configuration in memory, either from incluster method or from kubeconfig.
Expand Down
36 changes: 29 additions & 7 deletions src/wiremind_kubernetes/kubernetes_client_additional_arguments.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict
from typing import Any

import kubernetes.client

Expand All @@ -11,14 +11,14 @@ class ClientWithArguments:
"""

client: Any
read_additional_arguments: Dict[str, Any]
additional_arguments: Dict[str, Any]
read_additional_arguments: dict[str, Any]
additional_arguments: dict[str, Any]
read_argument_exclusions: dict[str, set[str]]

def __init__(self, client: Any, dry_run: bool = False, pretty: bool = True):
self.client = client() # like kubernetes.client.CoreV1Api
self.read_additional_arguments = {}
# Only add it when its true because we set pretty client wide,
# read_cluster_custom_object which accepts it will not have it set, but it's ok for now.
self.read_argument_exclusions = {}
if pretty:
self.read_additional_arguments["pretty"] = pretty
# Every request, either read or write, will have those arguments added
Expand All @@ -27,6 +27,13 @@ def __init__(self, client: Any, dry_run: bool = False, pretty: bool = True):
# Dry run, in kube API, is not true or false, but either dry_run: All or not defined.
self.additional_arguments["dry_run"] = "All"

def get_read_additional_arguments(self, attr: str) -> dict[str, Any]:
read_additional_arguments = self.read_additional_arguments.copy()
# Some generated client read methods do not accept every shared read kwarg.
for argument_name in self.read_argument_exclusions.get(attr, set()):
read_additional_arguments.pop(argument_name, None)
return read_additional_arguments

def __getattr__(self, attr: str) -> Any:
original_attr = getattr(self.client, attr)

Expand All @@ -43,7 +50,7 @@ def fn(*args: Any, **kwargs: Any) -> Any:
if is_write_function:
kwargs.update(self.additional_arguments)
else: # A read function
kwargs.update(self.read_additional_arguments)
kwargs.update(self.get_read_additional_arguments(attr))
return original_attr(*args, **kwargs)

return fn
Expand All @@ -68,6 +75,7 @@ class AutoscalingV1ApiWithArguments(ClientWithArguments):
def __init__(self, *args: Any, dry_run: bool = False, pretty: bool = False, **kwargs: Any) -> None:
super().__init__(client=kubernetes.client.AutoscalingV1Api, dry_run=dry_run, pretty=pretty)


class AutoscalingV2ApiWithArguments(ClientWithArguments):
def __init__(self, *args: Any, dry_run: bool = False, pretty: bool = False, **kwargs: Any) -> None:
super().__init__(client=kubernetes.client.AutoscalingV2Api, dry_run=dry_run, pretty=pretty)
Expand All @@ -76,11 +84,25 @@ def __init__(self, *args: Any, dry_run: bool = False, pretty: bool = False, **kw
class CustomObjectsApiWithArguments(ClientWithArguments):
def __init__(self, *args: Any, dry_run: bool = False, pretty: bool = False, **kwargs: Any) -> None:
super().__init__(client=kubernetes.client.CustomObjectsApi, dry_run=dry_run, pretty=pretty)
# Kubernetes custom-object GET-like methods reject `pretty`, while list methods still accept it.
self.read_argument_exclusions = {
"get_api_resources": {"pretty"},
"get_cluster_custom_object": {"pretty"},
"get_cluster_custom_object_scale": {"pretty"},
"get_cluster_custom_object_status": {"pretty"},
"get_namespaced_custom_object": {"pretty"},
"get_namespaced_custom_object_scale": {"pretty"},
"get_namespaced_custom_object_status": {"pretty"},
}


class RbacAuthorizationV1ApiWithArguments(ClientWithArguments):
def __init__(self, *args: Any, dry_run: bool = False, pretty: bool = False, **kwargs: Any) -> None:
super().__init__(client=kubernetes.client.RbacAuthorizationV1Api, dry_run=dry_run, pretty=pretty)
super().__init__(
client=kubernetes.client.RbacAuthorizationV1Api,
dry_run=dry_run,
pretty=pretty,
)


class NetworkingV1ApiWithArguments(ClientWithArguments):
Expand Down
Loading
Loading