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