From 5062263a05cce6c4f31a97722a7c200cda6c2ea0 Mon Sep 17 00:00:00 2001 From: Raphael Passini Date: Thu, 1 Feb 2024 19:31:21 -0300 Subject: [PATCH 1/3] [Feat] Allow custom timeout using env vars --- README.md | 6 ++++++ src/onepasswordconnectsdk/async_client.py | 15 +++++++++++++-- src/onepasswordconnectsdk/client.py | 6 +++--- src/tests/test_client_items.py | 17 +++++++++++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 03b5a0b..73dc830 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,12 @@ Check the [Python Connect SDK Example](example/README.md) to see an example of i export OP_CONNECT_HOST= && \ export OP_CONNECT_TOKEN= ``` + + 2.1 If you need a higher timeout on the client requests you can export `OP_CLIENT_REQ_TIMEOUT` environment variable: + ```sh + # set the timeout to 90 seconds + export OP_CLIENT_REQ_TIMEOUT=90 + ``` 3. Use the SDK: diff --git a/src/onepasswordconnectsdk/async_client.py b/src/onepasswordconnectsdk/async_client.py index f7e357b..cda36f5 100644 --- a/src/onepasswordconnectsdk/async_client.py +++ b/src/onepasswordconnectsdk/async_client.py @@ -1,9 +1,11 @@ """Python AsyncClient for connecting to 1Password Connect""" import httpx -from httpx import HTTPError +from httpx import HTTPError, USE_CLIENT_DEFAULT from typing import Dict, List, Union import os +from httpx._client import UseClientDefault + from onepasswordconnectsdk.serializer import Serializer from onepasswordconnectsdk.utils import build_headers, is_valid_uuid, PathBuilder from onepasswordconnectsdk.errors import ( @@ -13,6 +15,14 @@ from onepasswordconnectsdk.models import File, Item, ItemVault, SummaryItem, Vault +ENV_CLIENT_REQ_TIMEOUT = "OP_CONNECT_CLIENT_REQ_TIMEOUT" + + +def get_timeout() -> Union[int, UseClientDefault]: + timeout = int(os.getenv(ENV_CLIENT_REQ_TIMEOUT, 0)) + return timeout if timeout else USE_CLIENT_DEFAULT + + class AsyncClient: """Python Async Client Class""" @@ -24,7 +34,8 @@ def __init__(self, url: str, token: str) -> None: self.serializer = Serializer() def create_session(self, url: str, token: str) -> httpx.AsyncClient: - return httpx.AsyncClient(base_url=url, headers=self.build_headers(token)) + # import here to avoid circular import + return httpx.AsyncClient(base_url=url, headers=self.build_headers(token), timeout=get_timeout()) def build_headers(self, token: str) -> Dict[str, str]: return build_headers(token) diff --git a/src/onepasswordconnectsdk/client.py b/src/onepasswordconnectsdk/client.py index 2b40327..98218b1 100644 --- a/src/onepasswordconnectsdk/client.py +++ b/src/onepasswordconnectsdk/client.py @@ -1,11 +1,11 @@ """Python Client for connecting to 1Password Connect""" import httpx -from httpx import HTTPError +from httpx import HTTPError, USE_CLIENT_DEFAULT import json from typing import Dict, List, Union import os -from onepasswordconnectsdk.async_client import AsyncClient +from onepasswordconnectsdk.async_client import AsyncClient, get_timeout from onepasswordconnectsdk.serializer import Serializer from onepasswordconnectsdk.utils import build_headers, is_valid_uuid, PathBuilder from onepasswordconnectsdk.errors import ( @@ -32,7 +32,7 @@ def __init__(self, url: str, token: str) -> None: self.serializer = Serializer() def create_session(self, url: str, token: str) -> httpx.Client: - return httpx.Client(base_url=url, headers=self.build_headers(token)) + return httpx.Client(base_url=url, headers=self.build_headers(token), timeout=get_timeout()) def build_headers(self, token: str) -> Dict[str, str]: return build_headers(token) diff --git a/src/tests/test_client_items.py b/src/tests/test_client_items.py index 1f5f74d..ef034ca 100644 --- a/src/tests/test_client_items.py +++ b/src/tests/test_client_items.py @@ -1,6 +1,10 @@ +import os import pytest +from unittest import mock + from httpx import Response from onepasswordconnectsdk import client, models +from onepasswordconnectsdk.async_client import ENV_CLIENT_REQ_TIMEOUT VAULT_ID = "hfnjvi6aymbsnfc2xeeoheizda" VAULT_TITLE = "VaultA" @@ -440,3 +444,16 @@ def generate_full_item(): id="Section_47DC4DDBF26640AB8B8618DA36D5A499"))], sections=[models.Section(id="id", label="label")]) return item + + +def test_set_timeout_using_env_variable(): + with mock.patch.dict(os.environ, {ENV_CLIENT_REQ_TIMEOUT: '120'}): + client_instance = client.new_client(HOST, TOKEN) + assert client_instance.session.timeout.read == 120 + + +@pytest.mark.asyncio +def test_set_timeout_using_env_variable_async(): + with mock.patch.dict(os.environ, {ENV_CLIENT_REQ_TIMEOUT: '120'}): + client_instance = client.new_client(HOST, TOKEN, is_async=True) + assert client_instance.session.timeout.read == 120 \ No newline at end of file From 889a65848cca34cb18e55091e749f47a7945bffd Mon Sep 17 00:00:00 2001 From: Raphael Passini Date: Thu, 28 Mar 2024 14:45:54 -0300 Subject: [PATCH 2/3] [Feat] Rename OP_CLIENT_REQ_TIMEOUT and move get_timeout to utils package --- README.md | 2 +- src/onepasswordconnectsdk/async_client.py | 14 ++------------ src/onepasswordconnectsdk/client.py | 4 ++-- src/onepasswordconnectsdk/utils.py | 13 +++++++++++++ src/tests/test_client_items.py | 6 +++--- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 73dc830..1ef7540 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Check the [Python Connect SDK Example](example/README.md) to see an example of i 2.1 If you need a higher timeout on the client requests you can export `OP_CLIENT_REQ_TIMEOUT` environment variable: ```sh # set the timeout to 90 seconds - export OP_CLIENT_REQ_TIMEOUT=90 + export OP_CLIENT_REQUEST_TIMEOUT=90 ``` 3. Use the SDK: diff --git a/src/onepasswordconnectsdk/async_client.py b/src/onepasswordconnectsdk/async_client.py index cda36f5..23c1bfe 100644 --- a/src/onepasswordconnectsdk/async_client.py +++ b/src/onepasswordconnectsdk/async_client.py @@ -1,13 +1,11 @@ """Python AsyncClient for connecting to 1Password Connect""" import httpx -from httpx import HTTPError, USE_CLIENT_DEFAULT +from httpx import HTTPError from typing import Dict, List, Union import os -from httpx._client import UseClientDefault - from onepasswordconnectsdk.serializer import Serializer -from onepasswordconnectsdk.utils import build_headers, is_valid_uuid, PathBuilder +from onepasswordconnectsdk.utils import build_headers, is_valid_uuid, PathBuilder, get_timeout from onepasswordconnectsdk.errors import ( FailedToRetrieveItemException, FailedToRetrieveVaultException, @@ -15,14 +13,6 @@ from onepasswordconnectsdk.models import File, Item, ItemVault, SummaryItem, Vault -ENV_CLIENT_REQ_TIMEOUT = "OP_CONNECT_CLIENT_REQ_TIMEOUT" - - -def get_timeout() -> Union[int, UseClientDefault]: - timeout = int(os.getenv(ENV_CLIENT_REQ_TIMEOUT, 0)) - return timeout if timeout else USE_CLIENT_DEFAULT - - class AsyncClient: """Python Async Client Class""" diff --git a/src/onepasswordconnectsdk/client.py b/src/onepasswordconnectsdk/client.py index 98218b1..a6d5060 100644 --- a/src/onepasswordconnectsdk/client.py +++ b/src/onepasswordconnectsdk/client.py @@ -5,9 +5,9 @@ from typing import Dict, List, Union import os -from onepasswordconnectsdk.async_client import AsyncClient, get_timeout +from onepasswordconnectsdk.async_client import AsyncClient from onepasswordconnectsdk.serializer import Serializer -from onepasswordconnectsdk.utils import build_headers, is_valid_uuid, PathBuilder +from onepasswordconnectsdk.utils import build_headers, is_valid_uuid, PathBuilder, get_timeout from onepasswordconnectsdk.errors import ( FailedToRetrieveItemException, FailedToRetrieveVaultException, diff --git a/src/onepasswordconnectsdk/utils.py b/src/onepasswordconnectsdk/utils.py index da35d50..6699e42 100644 --- a/src/onepasswordconnectsdk/utils.py +++ b/src/onepasswordconnectsdk/utils.py @@ -1,4 +1,11 @@ +import os +from typing import Union + +from httpx import USE_CLIENT_DEFAULT +from httpx._client import UseClientDefault + UUIDLength = 26 +ENV_CLIENT_REQUEST_TIMEOUT = "OP_CONNECT_CLIENT_REQ_TIMEOUT" def is_valid_uuid(uuid): @@ -59,3 +66,9 @@ def _append_path(self, path_chunk: str = None, query: str = None) -> 'PathBuilde self.path += f"/{path_chunk}" if query is not None: self.path += f"?{query}" + + +def get_timeout() -> Union[int, UseClientDefault]: + """Get the timeout to be used in the HTTP Client""" + timeout = int(os.getenv(ENV_CLIENT_REQUEST_TIMEOUT, 0)) + return timeout if timeout else USE_CLIENT_DEFAULT diff --git a/src/tests/test_client_items.py b/src/tests/test_client_items.py index ef034ca..d9e23c9 100644 --- a/src/tests/test_client_items.py +++ b/src/tests/test_client_items.py @@ -4,7 +4,7 @@ from httpx import Response from onepasswordconnectsdk import client, models -from onepasswordconnectsdk.async_client import ENV_CLIENT_REQ_TIMEOUT +from onepasswordconnectsdk.utils import ENV_CLIENT_REQUEST_TIMEOUT VAULT_ID = "hfnjvi6aymbsnfc2xeeoheizda" VAULT_TITLE = "VaultA" @@ -447,13 +447,13 @@ def generate_full_item(): def test_set_timeout_using_env_variable(): - with mock.patch.dict(os.environ, {ENV_CLIENT_REQ_TIMEOUT: '120'}): + with mock.patch.dict(os.environ, {ENV_CLIENT_REQUEST_TIMEOUT: '120'}): client_instance = client.new_client(HOST, TOKEN) assert client_instance.session.timeout.read == 120 @pytest.mark.asyncio def test_set_timeout_using_env_variable_async(): - with mock.patch.dict(os.environ, {ENV_CLIENT_REQ_TIMEOUT: '120'}): + with mock.patch.dict(os.environ, {ENV_CLIENT_REQUEST_TIMEOUT: '120'}): client_instance = client.new_client(HOST, TOKEN, is_async=True) assert client_instance.session.timeout.read == 120 \ No newline at end of file From d01e5358950389bd645d2a5cc2d2f55d80e682dd Mon Sep 17 00:00:00 2001 From: Raphael Passini Date: Thu, 28 Mar 2024 14:47:30 -0300 Subject: [PATCH 3/3] [Fix] Minor mistake in README file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ef7540..df598e9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Check the [Python Connect SDK Example](example/README.md) to see an example of i export OP_CONNECT_TOKEN= ``` - 2.1 If you need a higher timeout on the client requests you can export `OP_CLIENT_REQ_TIMEOUT` environment variable: + 2.1 If you need a higher timeout on the client requests you can export `OP_CLIENT_REQUEST_TIMEOUT` environment variable: ```sh # set the timeout to 90 seconds export OP_CLIENT_REQUEST_TIMEOUT=90