From ea44043b406d5d7f85466bb1b6605bc092dd1f29 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sun, 30 Jul 2023 21:45:07 +0200 Subject: [PATCH] Improve usability and documentation for changing timeout settings --- CHANGELOG.md | 2 ++ README.md | 9 +++++++++ grafana_client/api.py | 28 +++++++++++++++++++++++----- grafana_client/client.py | 4 +++- test/test_grafana_api.py | 27 +++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6ea7ef..60458f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * Add missing argument `dashboard_uid` to `get_annotation` method. Thanks, @nikita-b. * Add API method `get_alertrules_all`. Thanks, @harish422. +* Improve usability and documentation for changing timeout settings. + Thanks, @bukem and @dheeg. ## 3.5.0 (2022-12-07) diff --git a/README.md b/README.md index 86d486d..c90c286 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,15 @@ Please note that, on top of the specific examples above, the object obtained by `credential` can be an arbitrary `requests.auth.AuthBase` instance. +## Timeout settings + +The default timeout value is five seconds, used for both connect and read timeout. + +The constructors of `GrafanaApi` and `GrafanaClient`, as well as the factory methods +`from_url` and `from_env` accept the `timeout` argument, which can be obtained as a +scalar `float` value, or as a tuple of `(, )`. + + ## Proxy The underlying `requests` library honors the `HTTP_PROXY` and `HTTPS_PROXY` diff --git a/grafana_client/api.py b/grafana_client/api.py index 8bdcaea..e9963e5 100644 --- a/grafana_client/api.py +++ b/grafana_client/api.py @@ -9,7 +9,7 @@ import requests.auth from urllib3.exceptions import InsecureRequestWarning -from .client import GrafanaClient +from .client import DEFAULT_TIMEOUT, GrafanaClient from .elements import ( Admin, Alerting, @@ -44,7 +44,7 @@ def __init__( url_path_prefix="", protocol="http", verify=True, - timeout=5.0, + timeout=DEFAULT_TIMEOUT, user_agent: str = None, ): self.client = GrafanaClient( @@ -95,7 +95,12 @@ def version(self): return version @classmethod - def from_url(cls, url: str = None, credential: Union[str, Tuple[str, str], requests.auth.AuthBase] = None): + def from_url( + cls, + url: str = None, + credential: Union[str, Tuple[str, str], requests.auth.AuthBase] = None, + timeout: Union[float, Tuple[float, float]] = DEFAULT_TIMEOUT, + ): """ Factory method to create a `GrafanaApi` instance from a URL. @@ -129,14 +134,27 @@ def from_url(cls, url: str = None, credential: Union[str, Tuple[str, str], reque port=url.port, url_path_prefix=url.path.lstrip("/"), verify=verify, + timeout=timeout, ) grafana.url = original_url return grafana @classmethod - def from_env(cls): + def from_env(cls, timeout: Union[float, Tuple[float, float]] = None): """ Factory method to create a `GrafanaApi` instance from environment variables. """ - return cls.from_url(url=os.environ.get("GRAFANA_URL"), credential=os.environ.get("GRAFANA_TOKEN")) + if timeout is None: + if "GRAFANA_TIMEOUT" in os.environ: + try: + timeout = float(os.environ["GRAFANA_TIMEOUT"]) + except Exception as ex: + raise ValueError( + f"Unable to parse invalid `float` value from " f"`GRAFANA_TIMEOUT` environment variable: {ex}" + ) + if timeout is None: + timeout = DEFAULT_TIMEOUT + return cls.from_url( + url=os.environ.get("GRAFANA_URL"), credential=os.environ.get("GRAFANA_TOKEN"), timeout=timeout + ) diff --git a/grafana_client/client.py b/grafana_client/client.py index 35287ac..e4717fe 100644 --- a/grafana_client/client.py +++ b/grafana_client/client.py @@ -1,6 +1,8 @@ import requests import requests.auth +DEFAULT_TIMEOUT: float = 5.0 + class GrafanaException(Exception): def __init__(self, status_code, response, message): @@ -73,7 +75,7 @@ def __init__( url_path_prefix="", protocol="http", verify=True, - timeout=5.0, + timeout=DEFAULT_TIMEOUT, user_agent: str = None, ): self.auth = auth diff --git a/test/test_grafana_api.py b/test/test_grafana_api.py index 9857670..fbbf2e2 100644 --- a/test/test_grafana_api.py +++ b/test/test_grafana_api.py @@ -66,6 +66,14 @@ def test_from_url_full_on(self): self.assertEqual(grafana.client.verify, False) self.assertEqual(grafana.client.timeout, 5.0) + def test_from_url_with_timeout_value(self): + grafana = GrafanaApi.from_url(timeout=42.42) + self.assertEqual(grafana.client.timeout, 42.42) + + def test_from_url_with_timeout_tuple(self): + grafana = GrafanaApi.from_url(timeout=(3.05, 27)) + self.assertEqual(grafana.client.timeout, (3.05, 27)) + def test_from_env_default(self): grafana = GrafanaApi.from_env() self.assertIsInstance(grafana.client.auth, requests.auth.HTTPBasicAuth) @@ -114,3 +122,22 @@ def test_from_env_full_on(self): self.assertEqual(grafana.client.url_protocol, "https") self.assertEqual(grafana.client.verify, False) self.assertEqual(grafana.client.timeout, 5.0) + + @mock.patch.dict(os.environ, {"GRAFANA_TIMEOUT": "84.84"}) + def test_from_env_with_timeout_from_env_valid(self): + grafana = GrafanaApi.from_env() + self.assertEqual(grafana.client.timeout, 84.84) + + @mock.patch.dict(os.environ, {"GRAFANA_TIMEOUT": "foobar"}) + def test_from_env_with_timeout_from_env_invalid(self): + with self.assertRaises(ValueError) as ctx: + GrafanaApi.from_env() + self.assertEqual( + str(ctx.exception), + "Unable to parse invalid `float` value from `GRAFANA_TIMEOUT` " + "environment variable: could not convert string to float: 'foobar'", + ) + + def test_from_env_with_timeout_tuple(self): + grafana = GrafanaApi.from_env(timeout=(3.05, 27)) + self.assertEqual(grafana.client.timeout, (3.05, 27))