From a358e691eb9d08c4ab848ab9d8fecf8ab0814a10 Mon Sep 17 00:00:00 2001 From: Daniel Perrefort Date: Tue, 2 Jul 2024 10:57:10 -0400 Subject: [PATCH] Formatting and QA inspections (#5) --- README.md | 1 - keystone_client/__init__.py | 2 +- keystone_client/client.py | 53 +++++++++++++++++++++++-------------- tests/test_dummy.py | 6 ++++- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c9307c6..61ee899 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,3 @@ A light-weight Python client for wrapping the Keystone API. - [Keystone-API](https://github.com/pitt-crc/keystone-api): Backend REST API for managing HPC allocations and resources. - [Keystone-Web](https://github.com/pitt-crc/keystone-web): Website frontend for HPC administration and self-service. - [Keystone-Docs](https://github.com/pitt-crc/keystone-docs): Documentation for the Keystone project and its components. - diff --git a/keystone_client/__init__.py b/keystone_client/__init__.py index 4c51a32..421945b 100644 --- a/keystone_client/__init__.py +++ b/keystone_client/__init__.py @@ -1 +1 @@ -from .client import * \ No newline at end of file +from .client import * diff --git a/keystone_client/client.py b/keystone_client/client.py index c910dac..8a1b354 100644 --- a/keystone_client/client.py +++ b/keystone_client/client.py @@ -1,3 +1,10 @@ +"""Keystone API Client + +This module provides a client class `KeystoneAPIClient` for interacting with the +Keystone API. It streamlines communication with the API, providing methods for +authentication, data retrieval, and data manipulation. +""" + from __future__ import annotations from collections import namedtuple @@ -12,17 +19,17 @@ __all__ = ["KeystoneClient"] # Custom types -ContentType = Literal['json', 'text', 'content'] +ContentType = Literal["json", "text", "content"] ResponseContent = Union[Dict[str, Any], str, bytes] QueryResult = Union[None, dict, List[dict]] -HTTPMethod = Literal['get', 'post', 'put', 'patch', 'delete'] +HTTPMethod = Literal["get", "post", "put", "patch", "delete"] # API schema mapping human-readable, python-friendly names to API endpoints -Schema = namedtuple('Schema', [ - 'allocations', - 'requests', - 'research_groups', - 'users', +Schema = namedtuple("Schema", [ + "allocations", + "requests", + "research_groups", + "users", ]) @@ -37,10 +44,10 @@ class KeystoneClient: authentication_blacklist = "authentication/blacklist/" authentication_refresh = "authentication/refresh/" schema = Schema( - allocations='allocations/allocations/', - requests='allocations/requests/', - research_groups='users/researchgroups/', - users='users/users/', + allocations="allocations/allocations/", + requests="allocations/requests/", + research_groups="users/researchgroups/", + users="users/users/", ) def __init__(self, url: str, auto_refresh: bool = True) -> None: @@ -71,10 +78,10 @@ def __new__(cls, *args, **kwargs) -> KeystoneClient: for key, endpoint in zip(cls.schema._fields, cls.schema): # Create a retrieve method - retrieve_name = f'retrieve_{key}' + retrieve_name = f"retrieve_{key}" if not hasattr(instance, retrieve_name): retrieve_method = partial(instance._retrieve_records, _endpoint=endpoint) - setattr(instance, f'retrieve_{key}', retrieve_method) + setattr(instance, f"retrieve_{key}", retrieve_method) return instance @@ -101,7 +108,7 @@ def _retrieve_records( """ if pk is not None: - _endpoint = f'{_endpoint}/{pk}/' + _endpoint = f"{_endpoint}/{pk}/" try: response = self.http_get(_endpoint, params=filters, timeout=timeout) @@ -129,7 +136,13 @@ def _get_headers(self) -> Dict[str, str]: "Content-Type": "application/json" } - def _send_request(self, method: HTTPMethod, url: str, timeout: int = default_timeout, **kwargs) -> requests.Response: + def _send_request( + self, + method: HTTPMethod, + url: str, + timeout: int = default_timeout, + **kwargs + ) -> requests.Response: """Send an HTTP request Args: @@ -142,7 +155,7 @@ def _send_request(self, method: HTTPMethod, url: str, timeout: int = default_tim """ if self.auto_refresh: - self.refresh(force=False, timeout=timeout) + self._refresh_tokens(force=False, timeout=timeout) response = requests.request(method, url, **kwargs) response.raise_for_status() @@ -298,12 +311,12 @@ def login(self, username: str, password: str, timeout: int = default_timeout) -> response.raise_for_status() # Parse data from the refresh token - refresh_payload = jwt.decode(self._refresh_token, options={"verify_signature": False}) + refresh_payload = jwt.decode(self._refresh_token) self._refresh_token = response.json().get("refresh") self._refresh_expiration = datetime.fromtimestamp(refresh_payload["exp"]) # Parse data from the access token - access_payload = jwt.decode(self._access_token, options={"verify_signature": False}) + access_payload = jwt.decode(self._access_token) self._access_token = response.json().get("access") self._access_expiration = datetime.fromtimestamp(access_payload["exp"]) @@ -332,7 +345,7 @@ def logout(self, timeout: int = default_timeout) -> None: self._access_token = None self._access_expiration = None - def refresh(self, force: bool = True, timeout: int = default_timeout) -> None: + def _refresh_tokens(self, force: bool = True, timeout: int = default_timeout) -> None: """Refresh the JWT access token Args: @@ -347,7 +360,7 @@ def refresh(self, force: bool = True, timeout: int = default_timeout) -> None: # Alert the user when a refresh is not possible if self._refresh_expiration > now: - raise RuntimeError('Refresh token has expired. Login again to continue.') + raise RuntimeError("Refresh token has expired. Login again to continue.") response = requests.post( f"{self.url}/{self.authentication_refresh}", diff --git a/tests/test_dummy.py b/tests/test_dummy.py index 627b65d..b3ecea7 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -1,6 +1,10 @@ +"""Placeholder test module""" + from unittest import TestCase class TestDummy(TestCase): + """Placeholder test class""" + def test_dummy(self): - pass + """Placeholder test"""