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: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -266,33 +266,46 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

# Public API functions can use boolean parameters
"src/ipsdk/connection.py" = [
"E402", # Module level import not at top of file (after module docstring)
"FBT001", # Boolean-typed positional argument (part of public API)
"FBT002", # Boolean default positional argument (part of public API)
"TRY301", # Abstract raise to inner function (acceptable for error handling)
]

"src/ipsdk/platform.py" = [
"E402", # Module level import not at top of file (after module docstring)
"FBT001", # Boolean-typed positional argument (part of public API)
"FBT002", # Boolean default positional argument (part of public API)
"TRY301", # Abstract raise to inner function (acceptable for error handling)
]

"src/ipsdk/gateway.py" = [
"E402", # Module level import not at top of file (after module docstring)
"FBT001", # Boolean-typed positional argument (part of public API)
"FBT002", # Boolean default positional argument (part of public API)
]

"src/ipsdk/exceptions.py" = [
"E402", # Module level import not at top of file (after module docstring)
"S110", # try-except-pass (intentional for error handling)
]

"src/ipsdk/logging.py" = [
"E402", # Module level import not at top of file (after module docstring)
"FBT001", # Boolean-typed positional argument (part of public API)
"FBT002", # Boolean default positional argument (part of public API)
"G004", # Logging statement uses f-string (acceptable for informational logging)
"E501", # Line too long (docstrings can be longer)
]

"src/ipsdk/heuristics.py" = [
"E402", # Module level import not at top of file (after module docstring)
]

"src/ipsdk/http.py" = [
"E402", # Module level import not at top of file (after module docstring)
]

[tool.ruff.lint.isort]
known-first-party = ["ipsdk"]
force-single-line = true
Expand Down
71 changes: 37 additions & 34 deletions src/ipsdk/connection.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2025 Itential, Inc
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import annotations

"""HTTP connection implementations for the Itential Python SDK.

This module provides both synchronous and asynchronous HTTP client implementations
Expand Down Expand Up @@ -130,9 +132,6 @@ async def fetch_devices():
import urllib.parse

from typing import Any
from typing import Dict
from typing import Optional
from typing import Union

import httpx

Expand All @@ -144,7 +143,7 @@ async def fetch_devices():


class ConnectionBase:
client: Union[httpx.Client, httpx.AsyncClient]
client: httpx.Client | httpx.AsyncClient

def __init__(
self,
Expand Down Expand Up @@ -196,7 +195,7 @@ def __init__(
self.token = None

self.authenticated = False
self._auth_lock: Optional[Any] = None
self._auth_lock: Any | None = None

self.client = self.__init_client__(
base_url=self._make_base_url(host, port, base_path, use_tls),
Expand Down Expand Up @@ -247,8 +246,8 @@ def _build_request(
self,
method: HTTPMethod,
path: str,
json: Union[str, bytes, dict, list] | None = None,
params: dict[str, Any] | None = None,
json: str | bytes | dict | list | None = None,
params: dict[str, Any | None] | None = None,
) -> httpx.Request:
"""Build an HTTP request object.

Expand Down Expand Up @@ -307,8 +306,8 @@ def _validate_request_args(
self,
method: HTTPMethod,
path: str,
params: Optional[Dict[str, Any]] = None,
json: Optional[Union[str, bytes, dict, list]] = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | dict | (list | None) = None,
) -> None:
"""
Validate request arguments to ensure they have correct types.
Expand All @@ -321,8 +320,8 @@ def _validate_request_args(
Args:
method (HTTPMethod): The HTTP method enum value to validate
path (str): The request path to validate
params (Optional[Dict[str, Any]]): Query parameters dict to validate
json (Optional[Union[str, bytes, dict, list]]): JSON body to validate
params (dict[str, Any | None]): Query parameters dict to validate
json (Union[str, bytes, dict, list | None]): JSON body to validate

Returns:
None
Expand Down Expand Up @@ -353,7 +352,7 @@ def _validate_request_args(
@abc.abstractmethod
def __init_client__(
self, base_url: str | None = None, verify: bool = True, timeout: int = 30
) -> Union[httpx.Client, httpx.AsyncClient]:
) -> httpx.Client | httpx.AsyncClient:
"""Initialize the HTTP client.

Abstract method to be implemented by subclasses to create either a
Expand All @@ -365,7 +364,7 @@ def __init_client__(
timeout: Connection timeout in seconds. Defaults to 30.

Returns:
Union[httpx.Client, httpx.AsyncClient]: The initialized HTTP client.
httpx.Client | httpx.AsyncClient: The initialized HTTP client.

Raises:
None
Expand Down Expand Up @@ -416,8 +415,8 @@ def _send_request(
self,
method: HTTPMethod,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, dict, list] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | dict | list | None = None,
) -> Response:
"""Send an HTTP request to the API endpoint.

Expand Down Expand Up @@ -473,7 +472,7 @@ def _send_request(

return Response(res)

def get(self, path: str, params: dict[str, Any] | None = None) -> Response:
def get(self, path: str, params: dict[str, Any | None] | None = None) -> Response:
"""Send an HTTP GET request to the server.

Args:
Expand All @@ -490,7 +489,9 @@ def get(self, path: str, params: dict[str, Any] | None = None) -> Response:
logging.trace(self.get, modname=__name__, clsname=self.__class__)
return self._send_request(HTTPMethod.GET, path=path, params=params)

def delete(self, path: str, params: dict[str, Any] | None = None) -> Response:
def delete(
self, path: str, params: dict[str, Any | None] | None = None
) -> Response:
"""Send an HTTP DELETE request to the server.

Args:
Expand All @@ -510,8 +511,8 @@ def delete(self, path: str, params: dict[str, Any] | None = None) -> Response:
def post(
self,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, list, dict] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | list | dict | None = None,
) -> Response:
"""Send an HTTP POST request to the server.

Expand All @@ -534,8 +535,8 @@ def post(
def put(
self,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, list, dict] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | list | dict | None = None,
) -> Response:
"""Send an HTTP PUT request to the server.

Expand All @@ -558,8 +559,8 @@ def put(
def patch(
self,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, list, dict] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | list | dict | None = None,
) -> Response:
"""Send an HTTP PATCH request to the server.

Expand Down Expand Up @@ -588,7 +589,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
self._auth_lock = asyncio.Lock()

def __init_client__(
self, base_url: Optional[str] = None, verify: bool = True, timeout: int = 30
self, base_url: str | None = None, verify: bool = True, timeout: int = 30
) -> httpx.AsyncClient:
"""
Initialize the httpx.AsyncClient instance
Expand Down Expand Up @@ -625,8 +626,8 @@ async def _send_request(
self,
method: HTTPMethod,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, dict, list] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | dict | list | None = None,
) -> Response:
"""Send an asynchronous HTTP request to the API endpoint.

Expand Down Expand Up @@ -682,7 +683,9 @@ async def _send_request(

return Response(res)

async def get(self, path: str, params: dict[str, Any] | None = None) -> Response:
async def get(
self, path: str, params: dict[str, Any | None] | None = None
) -> Response:
"""
Send a HTTP GET request to the server and return the response.

Expand All @@ -705,7 +708,7 @@ async def get(self, path: str, params: dict[str, Any] | None = None) -> Response
return await self._send_request(HTTPMethod.GET, path=path, params=params)

async def delete(
self, path: str, params: dict[str, Any] | None = None
self, path: str, params: dict[str, Any | None] | None = None
) -> Response:
"""
Send a HTTP DELETE request to the server and return the response.
Expand All @@ -731,8 +734,8 @@ async def delete(
async def post(
self,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, dict, list] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | dict | list | None = None,
) -> Response:
"""
Send a HTTP POST request to the server and return the response.
Expand Down Expand Up @@ -766,8 +769,8 @@ async def post(
async def put(
self,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, dict, list] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | dict | list | None = None,
) -> Response:
"""
Send a HTTP PUT request to the server and return the response.
Expand Down Expand Up @@ -801,8 +804,8 @@ async def put(
async def patch(
self,
path: str,
params: dict[str, Any] | None = None,
json: Union[str, bytes, dict, list] | None = None,
params: dict[str, Any | None] | None = None,
json: str | bytes | dict | list | None = None,
) -> Response:
"""
Send a HTTP PATCH request to the server and return the response.
Expand Down
11 changes: 7 additions & 4 deletions src/ipsdk/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2025 Itential, Inc
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import annotations

"""
Exception hierarchy for the Itential Python SDK.

Expand Down Expand Up @@ -73,13 +75,14 @@
print(f"Response body: {e.response.text}")
"""

from typing import TYPE_CHECKING
from typing import Any
from typing import Optional

import httpx

from . import logging

if TYPE_CHECKING:
import httpx


class IpsdkError(Exception):
"""
Expand All @@ -93,7 +96,7 @@ class IpsdkError(Exception):
details (dict): Additional error details and context
"""

def __init__(self, message: str, exc: Optional[httpx.HTTPError] = None) -> None:
def __init__(self, message: str, exc: httpx.HTTPError | None = None) -> None:
"""
Initialize the base SDK exception.

Expand Down
11 changes: 6 additions & 5 deletions src/ipsdk/gateway.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2025 Itential, Inc
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import annotations

"""Itential Automation Gateway client implementation for the SDK.

This module provides client implementations for connecting to and interacting
Expand Down Expand Up @@ -149,7 +151,6 @@ async def get_devices():
"""

from typing import Any
from typing import Optional

import httpx

Expand Down Expand Up @@ -206,8 +207,8 @@ class AuthMixin:
"""

# Attributes that should be provided by ConnectionBase
user: Optional[str]
password: Optional[str]
user: str | None
password: str | None
client: httpx.Client

def authenticate(self) -> None:
Expand Down Expand Up @@ -242,8 +243,8 @@ class AsyncAuthMixin:
"""

# Attributes that should be provided by ConnectionBase
user: Optional[str]
password: Optional[str]
user: str | None
password: str | None
client: httpx.AsyncClient

async def authenticate(self) -> None:
Expand Down
Loading