From 833ba380d5e77939257f2caa57bc518dc6428e47 Mon Sep 17 00:00:00 2001 From: Eleanor Date: Fri, 12 Jun 2026 11:57:22 -0700 Subject: [PATCH] Forward client_decorator to the Benchling SDK --- liminal/connection/benchling_service.py | 12 +++++++-- liminal/tests/test_benchling_service.py | 34 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 liminal/tests/test_benchling_service.py diff --git a/liminal/connection/benchling_service.py b/liminal/connection/benchling_service.py index 0030189..30255cb 100644 --- a/liminal/connection/benchling_service.py +++ b/liminal/connection/benchling_service.py @@ -7,7 +7,7 @@ from playwright.async_api import async_playwright, Request import requests from benchling_sdk.auth.client_credentials_oauth2 import ClientCredentialsOAuth2 -from benchling_sdk.benchling import Benchling +from benchling_sdk.benchling import Benchling, BenchlingApiClientDecorator from benchling_sdk.helpers.retry_helpers import RetryStrategy from bs4 import BeautifulSoup from sqlalchemy import create_engine @@ -55,6 +55,10 @@ class BenchlingService(Benchling): Whether to connect to the Benchling Postgres database. Requires warehouse_connection_string from the connection object. use_internal_api: bool = False Whether to connect to the Benchling internal API. Requires internal_api_admin_email and internal_api_admin_password from the connection object. + client_decorator: BenchlingApiClientDecorator | None = None + An optional function that receives the default BenchlingApiClient and returns a + customized one. Forwarded to the Benchling SDK. A common use is raising the HTTP + timeout, which otherwise defaults to 10 seconds: ``lambda c: c.with_timeout(60)``. """ def __init__( @@ -63,6 +67,7 @@ def __init__( use_api: bool = True, use_db: bool = False, use_internal_api: bool = False, + client_decorator: BenchlingApiClientDecorator | None = None, ) -> None: self.connection = connection self._session: Session | None = None @@ -77,7 +82,10 @@ def __init__( ) url = f"https://{connection.tenant_name}.benchling.com" super().__init__( - url=url, auth_method=auth_method, retry_strategy=retry_strategy + url=url, + auth_method=auth_method, + retry_strategy=retry_strategy, + client_decorator=client_decorator, ) LOGGER.info(f"Tenant {connection.tenant_name}: Connected to Benchling API.") self.use_db = use_db diff --git a/liminal/tests/test_benchling_service.py b/liminal/tests/test_benchling_service.py new file mode 100644 index 0000000..ff80d36 --- /dev/null +++ b/liminal/tests/test_benchling_service.py @@ -0,0 +1,34 @@ +from typing import cast + +from benchling_api_client.v2.benchling_client import BenchlingApiClient + +from liminal.connection.benchling_connection import BenchlingConnection +from liminal.connection.benchling_service import BenchlingService + +# The Benchling SDK defaults the per-request HTTP timeout to 10 seconds. +SDK_DEFAULT_TIMEOUT_SECONDS = 10 + + +def _connection() -> BenchlingConnection: + return BenchlingConnection( + tenant_name="test-tenant", + api_client_id="test-id", + api_client_secret="test-secret", + ) + + +class TestBenchlingServiceClientDecorator: + def test_default_timeout_is_sdk_default(self) -> None: + service = BenchlingService(_connection(), use_db=False) + assert service._client.get_timeout() == SDK_DEFAULT_TIMEOUT_SECONDS + + def test_client_decorator_is_forwarded_to_sdk(self) -> None: + def raise_timeout(client: BenchlingApiClient) -> BenchlingApiClient: + # with_timeout is typed to return the base Client but preserves the subclass. + return cast(BenchlingApiClient, client.with_timeout(60)) + + service = BenchlingService( + _connection(), use_db=False, client_decorator=raise_timeout + ) + + assert service._client.get_timeout() == 60