X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/0d868ba0ba58f1bdf4e75d8651d894c39d1f69ff..806072055c23ebbd9fcd4f595c6129f06e803c79:/sdk/python/arvados/api.py diff --git a/sdk/python/arvados/api.py b/sdk/python/arvados/api.py index 94b8013997..6b6ee36b3e 100644 --- a/sdk/python/arvados/api.py +++ b/sdk/python/arvados/api.py @@ -9,12 +9,7 @@ niceties such as caching, X-Request-Id header for tracking, and more. The main client constructors are `api` and `api_from_config`. """ -from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() -from builtins import range import collections -import http.client import httplib2 import json import logging @@ -28,6 +23,14 @@ import threading import time import types +from typing import ( + Any, + Dict, + List, + Mapping, + Optional, +) + import apiclient import apiclient.http from apiclient import discovery as apiclient_discovery @@ -43,13 +46,12 @@ _logger = logging.getLogger('arvados.api') _googleapiclient_log_lock = threading.Lock() MAX_IDLE_CONNECTION_DURATION = 30 - -# These constants supported our own retry logic that we've since removed in -# favor of using googleapiclient's num_retries. They're kept here purely for -# API compatibility, but set to 0 to indicate no retries happen. -RETRY_DELAY_INITIAL = 0 -RETRY_DELAY_BACKOFF = 0 -RETRY_COUNT = 0 +""" +Number of seconds that API client HTTP connections should be allowed to idle +in keepalive state before they are forced closed. Client code can adjust this +constant, and it will be used for all Arvados API clients constructed after +that point. +""" # An unused HTTP 5xx status code to request a retry internally. # See _intercept_http_request. This should not be user-visible. @@ -58,26 +60,6 @@ _RETRY_4XX_STATUS = 545 if sys.version_info >= (3,): httplib2.SSLHandshakeError = None -class OrderedJsonModel(apiclient.model.JsonModel): - """Model class for JSON that preserves the contents' order. - - API clients that care about preserving the order of fields in API - server responses can use this model to do so, like this: - - from arvados.api import OrderedJsonModel - client = arvados.api('v1', ..., model=OrderedJsonModel()) - """ - - def deserialize(self, content): - # This is a very slightly modified version of the parent class' - # implementation. Copyright (c) 2010 Google. - content = content.decode('utf-8') - body = json.loads(content, object_pairs_hook=collections.OrderedDict) - if self._data_wrapper and isinstance(body, dict) and 'data' in body: - body = body['data'] - return body - - _orig_retry_request = apiclient.http._retry_request def _retry_request(http, num_retries, *args, **kwargs): try: @@ -173,31 +155,40 @@ def _new_http_error(cls, *args, **kwargs): errors.ApiError, *args, **kwargs) apiclient_errors.HttpError.__new__ = staticmethod(_new_http_error) -def http_cache(data_type): - try: - homedir = pathlib.Path.home() - except RuntimeError: - return None - path = pathlib.Path(homedir, '.cache', 'arvados', data_type) +def http_cache(data_type: str) -> cache.SafeHTTPCache: + """Set up an HTTP file cache + + This function constructs and returns an `arvados.cache.SafeHTTPCache` + backed by the filesystem under a cache directory from the environment, or + `None` if the directory cannot be set up. The return value can be passed to + `httplib2.Http` as the `cache` argument. + + Arguments: + + * data_type: str --- The name of the subdirectory + where data is cached. + """ try: - path.mkdir(parents=True, exist_ok=True) - except OSError: + path = util._BaseDirectories('CACHE').storage_path() / data_type + path.mkdir(exist_ok=True) + except (OSError, RuntimeError): return None - return cache.SafeHTTPCache(str(path), max_age=60*60*24*2) + else: + return cache.SafeHTTPCache(str(path), max_age=60*60*24*2) def api_client( - version, - discoveryServiceUrl, - token, + version: str, + discoveryServiceUrl: str, + token: str, *, - cache=True, - http=None, - insecure=False, - num_retries=10, - request_id=None, - timeout=5*60, - **kwargs, -): + cache: bool=True, + http: Optional[httplib2.Http]=None, + insecure: bool=False, + num_retries: int=10, + request_id: Optional[str]=None, + timeout: int=5*60, + **kwargs: Any, +) -> apiclient_discovery.Resource: """Build an Arvados API client This function returns a `googleapiclient.discovery.Resource` object @@ -217,8 +208,7 @@ def api_client( Keyword-only arguments: * cache: bool --- If true, loads the API discovery document from, or - saves it to, a cache on disk (located at - `~/.cache/arvados/discovery`). + saves it to, a cache on disk. * http: httplib2.Http | None --- The HTTP client object the API client object will use to make requests. If not provided, this function will @@ -241,7 +231,6 @@ def api_client( Additional keyword arguments will be passed directly to `googleapiclient.discovery.build`. - """ if http is None: http = httplib2.Http( @@ -303,12 +292,12 @@ def api_client( return svc def normalize_api_kwargs( - version=None, - discoveryServiceUrl=None, - host=None, - token=None, - **kwargs, -): + version: Optional[str]=None, + discoveryServiceUrl: Optional[str]=None, + host: Optional[str]=None, + token: Optional[str]=None, + **kwargs: Any, +) -> Dict[str, Any]: """Validate kwargs from `api` and build kwargs for `api_client` This method takes high-level keyword arguments passed to the `api` @@ -361,7 +350,11 @@ def normalize_api_kwargs( **kwargs, } -def api_kwargs_from_config(version=None, apiconfig=None, **kwargs): +def api_kwargs_from_config( + version: Optional[str]=None, + apiconfig: Optional[Mapping[str, str]]=None, + **kwargs: Any +) -> Dict[str, Any]: """Build `api_client` keyword arguments from configuration This function accepts a mapping with Arvados configuration settings like @@ -404,9 +397,18 @@ def api_kwargs_from_config(version=None, apiconfig=None, **kwargs): **kwargs, ) -def api(version=None, cache=True, host=None, token=None, insecure=False, - request_id=None, timeout=5*60, *, - discoveryServiceUrl=None, **kwargs): +def api( + version: Optional[str]=None, + cache: bool=True, + host: Optional[str]=None, + token: Optional[str]=None, + insecure: bool=False, + request_id: Optional[str]=None, + timeout: int=5*60, + *, + discoveryServiceUrl: Optional[str]=None, + **kwargs: Any, +) -> 'arvados.safeapi.ThreadSafeApiCache': """Dynamically build an Arvados API client This function provides a high-level "do what I mean" interface to build an @@ -458,7 +460,11 @@ def api(version=None, cache=True, host=None, token=None, insecure=False, from .safeapi import ThreadSafeApiCache return ThreadSafeApiCache({}, {}, kwargs, version) -def api_from_config(version=None, apiconfig=None, **kwargs): +def api_from_config( + version: Optional[str]=None, + apiconfig: Optional[Mapping[str, str]]=None, + **kwargs: Any +) -> 'arvados.safeapi.ThreadSafeApiCache': """Build an Arvados API client from a configuration mapping This function builds an Arvados API client from a mapping with user @@ -486,3 +492,49 @@ def api_from_config(version=None, apiconfig=None, **kwargs): docstring for more information about their meaning. """ return api(**api_kwargs_from_config(version, apiconfig, **kwargs)) + +class OrderedJsonModel(apiclient.model.JsonModel): + """Model class for JSON that preserves the contents' order + + .. WARNING:: Deprecated + This model is redundant now that Python dictionaries preserve insertion + ordering. Code that passes this model to API constructors can remove it. + + In Python versions before 3.6, API clients that cared about preserving the + order of fields in API server responses could use this model to do so. + Typical usage looked like: + + from arvados.api import OrderedJsonModel + client = arvados.api('v1', ..., model=OrderedJsonModel()) + """ + @util._deprecated(preferred="the default model and rely on Python's built-in dictionary ordering") + def __init__(self, data_wrapper=False): + return super().__init__(data_wrapper) + + +RETRY_DELAY_INITIAL = 0 +""" +.. WARNING:: Deprecated + This constant was used by retry code in previous versions of the Arvados SDK. + Changing the value has no effect anymore. + Prefer passing `num_retries` to an API client constructor instead. + Refer to the constructor docstrings for details. +""" + +RETRY_DELAY_BACKOFF = 0 +""" +.. WARNING:: Deprecated + This constant was used by retry code in previous versions of the Arvados SDK. + Changing the value has no effect anymore. + Prefer passing `num_retries` to an API client constructor instead. + Refer to the constructor docstrings for details. +""" + +RETRY_COUNT = 0 +""" +.. WARNING:: Deprecated + This constant was used by retry code in previous versions of the Arvados SDK. + Changing the value has no effect anymore. + Prefer passing `num_retries` to an API client constructor instead. + Refer to the constructor docstrings for details. +"""