From 2e95490f741fd1d240d0ef1f4f49344106e3d462 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Feb 2024 02:55:40 -0600 Subject: [PATCH] Update URL handling to use yarl (#62) * Update URL handling to use yarl * remove insecure warnings --- README.md | 4 +--- tesla_powerwall/api.py | 35 ++++++----------------------------- tesla_powerwall/powerwall.py | 4 +--- tests/unit/test_api.py | 10 +++++++--- 4 files changed, 15 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 7d51f38..f72a0a5 100644 --- a/README.md +++ b/README.md @@ -80,14 +80,12 @@ powerwall = Powerwall( # Provide a requests.Session or None. If None is provided, a Session will be created. http_session=None, # Whether to verify the SSL certificate or not - verify_ssl=False, - disable_insecure_warning=True + verify_ssl=False ) #=> ``` > Note: By default the API client does not verify the SSL certificate of the Powerwall. If you want to verify the SSL certificate you can set `verify_ssl` to `True`. -> The API client suppresses warnings about an inseucre request (because we aren't verifing the certificate). If you want to enable those warnings you can set `disable_insecure_warning` to `False`. ### Authentication diff --git a/tesla_powerwall/api.py b/tesla_powerwall/api.py index 27f40c9..02e1cc3 100644 --- a/tesla_powerwall/api.py +++ b/tesla_powerwall/api.py @@ -2,12 +2,10 @@ from json.decoder import JSONDecodeError from types import TracebackType from typing import Any, List, Optional, Type -from urllib.parse import urljoin import aiohttp import orjson -from urllib3 import disable_warnings -from urllib3.exceptions import InsecureRequestWarning +from yarl import URL from .error import AccessDeniedError, ApiError, PowerwallUnreachableError @@ -19,12 +17,10 @@ def __init__( timeout: int = 10, http_session: Optional[aiohttp.ClientSession] = None, verify_ssl: bool = False, - disable_insecure_warning: bool = True, ) -> None: - if disable_insecure_warning: - disable_warnings(InsecureRequestWarning) - - self._endpoint = self._parse_endpoint(endpoint) + if not endpoint.startswith("http"): + endpoint = f"https://{endpoint}" + self._endpoint = URL(endpoint).with_path("api").with_scheme("https") self._timeout = aiohttp.ClientTimeout(total=timeout) self._owns_http_session = False if http_session else True self._ssl = None if verify_ssl else False @@ -40,25 +36,6 @@ def __init__( jar = aiohttp.CookieJar(unsafe=True) self._http_session = aiohttp.ClientSession(cookie_jar=jar) - @staticmethod - def _parse_endpoint(endpoint: str) -> str: - if endpoint.startswith("https"): - endpoint = endpoint - elif endpoint.startswith("http"): - endpoint = endpoint.replace("http", "https") - else: - # Use str.format instead of f'strings to be backwards compatible - endpoint = "https://{}".format(endpoint) - - if not endpoint.endswith("api") and not endpoint.endswith("/"): - endpoint += "/api/" - elif endpoint.endswith("api"): - endpoint += "/" - elif endpoint.endswith("/") and not endpoint.endswith("api/"): - endpoint += "api/" - - return endpoint - @staticmethod async def _handle_error(response: aiohttp.ClientResponse) -> None: if response.status == 404: @@ -121,8 +98,8 @@ async def _process_response(self, response: aiohttp.ClientResponse) -> dict: return response_json - def url(self, path: str): - return urljoin(self._endpoint, path) + def url(self, path: str) -> URL: + return self._endpoint.joinpath(path) async def get(self, path: str, headers: dict = {}) -> Any: try: diff --git a/tesla_powerwall/powerwall.py b/tesla_powerwall/powerwall.py index d77e9cc..131b587 100644 --- a/tesla_powerwall/powerwall.py +++ b/tesla_powerwall/powerwall.py @@ -26,14 +26,12 @@ def __init__( timeout: int = 10, http_session: Union[aiohttp.ClientSession, None] = None, verify_ssl: bool = False, - disable_insecure_warning: bool = True, - ): + ) -> None: self._api = API( endpoint=endpoint, timeout=timeout, http_session=http_session, verify_ssl=verify_ssl, - disable_insecure_warning=disable_insecure_warning, ) async def login_as( diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index c796ea1..d9f9c35 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -3,6 +3,7 @@ import aiohttp import aresponses +from yarl import URL from tesla_powerwall import API, AccessDeniedError, ApiError from tesla_powerwall.const import User @@ -22,7 +23,7 @@ async def asyncTearDown(self): await self.session.close() await self.aresponses.__aexit__(None, None, None) - def test_parse_endpoint(self): + async def test_parse_endpoint(self): test_endpoints = [ "1.1.1.1", "http://1.1.1.1", @@ -30,8 +31,11 @@ def test_parse_endpoint(self): "https://1.1.1.1/api", "https://1.1.1.1/", ] + for test_endpoint in test_endpoints: - self.assertEqual(self.api._parse_endpoint(test_endpoint), ENDPOINT) + api = API(test_endpoint) + await api.close() + self.assertEqual(api.url(""), URL(ENDPOINT.rstrip("/"))) async def test_process_response(self): status = 0 @@ -122,7 +126,7 @@ async def test_is_authenticated(self): self.assertEqual(self.api.is_authenticated(), True) def test_url(self): - self.assertEqual(self.api.url("test"), ENDPOINT + "test") + self.assertEqual(self.api.url("test"), URL(ENDPOINT + "test")) async def test_login(self): jar = aiohttp.CookieJar(unsafe=True)