# Copyright (C) The Arvados Authors. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0
+"""Thread-safe wrapper for an Arvados API client
+
+This module provides `ThreadSafeApiCache`, a thread-safe, API-compatible
+Arvados API client.
+"""
from __future__ import absolute_import
from builtins import object
-import copy
import threading
-import arvados
-import arvados.keep as keep
-import arvados.config as config
+from . import api
+from . import config
+from . import keep
class ThreadSafeApiCache(object):
- """Threadsafe wrapper for API objects.
+ """Thread-safe wrapper for an Arvados API client
- This stores and returns a different api object per thread, because httplib2
- which underlies apiclient is not threadsafe.
+ 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:
- def __init__(self, apiconfig=None, keep_params={}, api_params={}):
- if apiconfig is None:
- apiconfig = config.settings()
- self.apiconfig = copy.copy(apiconfig)
- self.api_params = api_params
- self.local = threading.local()
+ apiconfig: Mapping[str, str] | None
+ : A mapping with entries for `ARVADOS_API_HOST`, `ARVADOS_API_TOKEN`,
+ and optionally `ARVADOS_API_HOST_INSECURE`. If not provided, uses
+ `arvados.config.settings` to get these parameters from user
+ configuration. You can pass an empty mapping to build the client
+ solely from `api_params`.
+
+ keep_params: Mapping[str, Any]
+ : Keyword arguments used to construct an associated
+ `arvados.keep.KeepClient`.
- # Initialize an API object for this thread before creating
- # KeepClient, this will report if ARVADOS_API_HOST or
- # ARVADOS_API_TOKEN are missing.
- self.localapi()
+ 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'.
+ """
+
+ def __init__(self, apiconfig=None, keep_params={}, api_params={}, version=None):
+ if apiconfig or apiconfig is None:
+ self._api_kwargs = api.api_kwargs_from_config(version, apiconfig, **api_params)
+ else:
+ self._api_kwargs = api.normalize_api_kwargs(version, **api_params)
+ self.api_token = self._api_kwargs['token']
+ self.local = threading.local()
self.keep = keep.KeepClient(api_client=self, **keep_params)
def localapi(self):
if 'api' not in self.local.__dict__:
- self.local.api = arvados.api_from_config('v1', apiconfig=self.apiconfig,
- **self.api_params)
+ self.local.api = api.api_client(**self._api_kwargs)
return self.local.api
def __getattr__(self, name):
# Proxy nonexistent attributes to the thread-local API client.
- if name == "api_token":
- return self.apiconfig['ARVADOS_API_TOKEN']
return getattr(self.localapi(), name)