19686: Introduce low-level api_client constructor
[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
19 class ThreadSafeApiCache(object):
20     """Thread-safe wrapper for an Arvados API client
21
22     This class takes all the arguments necessary to build a lower-level
23     Arvados API client `googleapiclient.discovery.Resource`, then
24     transparently builds and wraps a unique object per thread. This works
25     around the fact that the client's underlying HTTP client object is not
26     thread-safe.
27
28     Arguments:
29
30     apiconfig: Mapping[str, str] | None
31     : A mapping with entries for `ARVADOS_API_HOST`, `ARVADOS_API_TOKEN`,
32       and optionally `ARVADOS_API_HOST_INSECURE`. If not provided, uses
33       `arvados.config.settings` to get these parameters from user
34       configuration.  You can pass an empty mapping to build the client
35       solely from `api_params`.
36
37     keep_params: Mapping[str, Any]
38     : Keyword arguments used to construct an associated
39       `arvados.keep.KeepClient`.
40
41     api_params: Mapping[str, Any]
42     : Keyword arguments used to construct each thread's API client. These
43       have the same meaning as in the `arvados.api.api` function.
44
45     version: str | None
46     : A string naming the version of the Arvados API to use. If not specified,
47       the code will log a warning and fall back to 'v1'.
48     """
49
50     def __init__(self, apiconfig=None, keep_params={}, api_params={}, version=None):
51         if apiconfig or apiconfig is None:
52             self._api_kwargs = api.api_kwargs_from_config(version, apiconfig, **api_params)
53         else:
54             self._api_kwargs = api.normalize_api_kwargs(version, **api_params)
55         self.api_token = self._api_kwargs['token']
56         self.local = threading.local()
57         self.keep = keep.KeepClient(api_client=self, **keep_params)
58
59     def localapi(self):
60         if 'api' not in self.local.__dict__:
61             self.local.api = api.api_client(**self._api_kwargs)
62         return self.local.api
63
64     def __getattr__(self, name):
65         # Proxy nonexistent attributes to the thread-local API client.
66         return getattr(self.localapi(), name)