arvados.safeapi
Thread-safe wrapper for an Arvados API client
This module provides ThreadSafeApiCache
, a thread-safe, API-compatible
Arvados API client.
1# Copyright (C) The Arvados Authors. All rights reserved. 2# 3# SPDX-License-Identifier: Apache-2.0 4"""Thread-safe wrapper for an Arvados API client 5 6This module provides `ThreadSafeApiCache`, a thread-safe, API-compatible 7Arvados API client. 8""" 9 10import sys 11import threading 12 13from typing import ( 14 Any, 15 Mapping, 16 Optional, 17) 18 19from . import config 20from . import keep 21from . import util 22 23api = sys.modules['arvados.api'] 24 25class ThreadSafeApiCache(object): 26 """Thread-safe wrapper for an Arvados API client 27 28 This class takes all the arguments necessary to build a lower-level 29 Arvados API client `googleapiclient.discovery.Resource`, then 30 transparently builds and wraps a unique object per thread. This works 31 around the fact that the client's underlying HTTP client object is not 32 thread-safe. 33 34 Arguments: 35 36 * apiconfig: Mapping[str, str] | None --- A mapping with entries for 37 `ARVADOS_API_HOST`, `ARVADOS_API_TOKEN`, and optionally 38 `ARVADOS_API_HOST_INSECURE`. If not provided, uses 39 `arvados.config.settings` to get these parameters from user 40 configuration. You can pass an empty mapping to build the client 41 solely from `api_params`. 42 43 * keep_params: Mapping[str, Any] --- Keyword arguments used to construct 44 an associated `arvados.keep.KeepClient`. 45 46 * api_params: Mapping[str, Any] --- Keyword arguments used to construct 47 each thread's API client. These have the same meaning as in the 48 `arvados.api.api` function. 49 50 * version: str | None --- A string naming the version of the Arvados API 51 to use. If not specified, the code will log a warning and fall back to 52 `'v1'`. 53 """ 54 def __init__( 55 self, 56 apiconfig: Optional[Mapping[str, str]]=None, 57 keep_params: Optional[Mapping[str, Any]]={}, 58 api_params: Optional[Mapping[str, Any]]={}, 59 version: Optional[str]=None, 60 ) -> None: 61 if apiconfig or apiconfig is None: 62 self._api_kwargs = api.api_kwargs_from_config(version, apiconfig, **api_params) 63 else: 64 self._api_kwargs = api.normalize_api_kwargs(version, **api_params) 65 self.api_token = self._api_kwargs['token'] 66 self.request_id = self._api_kwargs.get('request_id') 67 self.local = threading.local() 68 self.keep = keep.KeepClient(api_client=self, **keep_params) 69 70 def localapi(self) -> 'googleapiclient.discovery.Resource': 71 try: 72 client = self.local.api 73 except AttributeError: 74 client = api.api_client(**self._api_kwargs) 75 client._http._request_id = lambda: self.request_id or util.new_request_id() 76 self.local.api = client 77 return client 78 79 def __getattr__(self, name: str) -> Any: 80 # Proxy nonexistent attributes to the thread-local API client. 81 return getattr(self.localapi(), name)
26class ThreadSafeApiCache(object): 27 """Thread-safe wrapper for an Arvados API client 28 29 This class takes all the arguments necessary to build a lower-level 30 Arvados API client `googleapiclient.discovery.Resource`, then 31 transparently builds and wraps a unique object per thread. This works 32 around the fact that the client's underlying HTTP client object is not 33 thread-safe. 34 35 Arguments: 36 37 * apiconfig: Mapping[str, str] | None --- A mapping with entries for 38 `ARVADOS_API_HOST`, `ARVADOS_API_TOKEN`, and optionally 39 `ARVADOS_API_HOST_INSECURE`. If not provided, uses 40 `arvados.config.settings` to get these parameters from user 41 configuration. You can pass an empty mapping to build the client 42 solely from `api_params`. 43 44 * keep_params: Mapping[str, Any] --- Keyword arguments used to construct 45 an associated `arvados.keep.KeepClient`. 46 47 * api_params: Mapping[str, Any] --- Keyword arguments used to construct 48 each thread's API client. These have the same meaning as in the 49 `arvados.api.api` function. 50 51 * version: str | None --- A string naming the version of the Arvados API 52 to use. If not specified, the code will log a warning and fall back to 53 `'v1'`. 54 """ 55 def __init__( 56 self, 57 apiconfig: Optional[Mapping[str, str]]=None, 58 keep_params: Optional[Mapping[str, Any]]={}, 59 api_params: Optional[Mapping[str, Any]]={}, 60 version: Optional[str]=None, 61 ) -> None: 62 if apiconfig or apiconfig is None: 63 self._api_kwargs = api.api_kwargs_from_config(version, apiconfig, **api_params) 64 else: 65 self._api_kwargs = api.normalize_api_kwargs(version, **api_params) 66 self.api_token = self._api_kwargs['token'] 67 self.request_id = self._api_kwargs.get('request_id') 68 self.local = threading.local() 69 self.keep = keep.KeepClient(api_client=self, **keep_params) 70 71 def localapi(self) -> 'googleapiclient.discovery.Resource': 72 try: 73 client = self.local.api 74 except AttributeError: 75 client = api.api_client(**self._api_kwargs) 76 client._http._request_id = lambda: self.request_id or util.new_request_id() 77 self.local.api = client 78 return client 79 80 def __getattr__(self, name: str) -> Any: 81 # Proxy nonexistent attributes to the thread-local API client. 82 return getattr(self.localapi(), name)
Thread-safe wrapper for an Arvados API client
This class takes all the arguments necessary to build a lower-level
Arvados API client googleapiclient.discovery.Resource
, then
transparently builds and wraps a unique object per thread. This works
around the fact that the client’s underlying HTTP client object is not
thread-safe.
Arguments:
apiconfig: Mapping[str, str] | None — A mapping with entries for
ARVADOS_API_HOST
,ARVADOS_API_TOKEN
, and optionallyARVADOS_API_HOST_INSECURE
. If not provided, usesarvados.config.settings
to get these parameters from user configuration. You can pass an empty mapping to build the client solely fromapi_params
.keep_params: Mapping[str, Any] — Keyword arguments used to construct an associated
arvados.keep.KeepClient
.api_params: Mapping[str, Any] — Keyword arguments used to construct each thread’s API client. These have the same meaning as in the
arvados.api.api
function.version: str | None — A string naming the version of the Arvados API to use. If not specified, the code will log a warning and fall back to
'v1'
.
55 def __init__( 56 self, 57 apiconfig: Optional[Mapping[str, str]]=None, 58 keep_params: Optional[Mapping[str, Any]]={}, 59 api_params: Optional[Mapping[str, Any]]={}, 60 version: Optional[str]=None, 61 ) -> None: 62 if apiconfig or apiconfig is None: 63 self._api_kwargs = api.api_kwargs_from_config(version, apiconfig, **api_params) 64 else: 65 self._api_kwargs = api.normalize_api_kwargs(version, **api_params) 66 self.api_token = self._api_kwargs['token'] 67 self.request_id = self._api_kwargs.get('request_id') 68 self.local = threading.local() 69 self.keep = keep.KeepClient(api_client=self, **keep_params)