[arvados] updated: 2.1.0-3165-g897610c89

git repository hosting git at public.arvados.org
Wed Dec 7 13:30:50 UTC 2022


Summary of changes:
 sdk/python/arvados/safeapi.py    |   8 ++
 sdk/python/tests/test_api.py     | 182 ++++++++++++++++++++++++++++++++++++++-
 sdk/python/tests/test_safeapi.py |  70 +++++++++++++++
 3 files changed, 259 insertions(+), 1 deletion(-)
 create mode 100644 sdk/python/tests/test_safeapi.py

       via  897610c897f37142d4fddcccd87d6877d62177ee (commit)
       via  bfac8edbaeeae0237289e8ae47a6e24d4a9155e1 (commit)
      from  cc4492ecff0b1b7f2cbd96174967227f1214b175 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 897610c897f37142d4fddcccd87d6877d62177ee
Author: Brett Smith <brett.smith at curii.com>
Date:   Wed Dec 7 05:05:12 2022 -0500

    19686: Support API version in ThreadSafeApiCache api_params
    
    This is a small backwards compatibility fix. Passing the API version in
    api_params was the only way to do it before the #19686 API
    additions. Continue supporting specifying the version this way when the
    caller does not provide an explicit version argument. Add a test to
    ensure it remains supported.
    
    Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>

diff --git a/sdk/python/arvados/safeapi.py b/sdk/python/arvados/safeapi.py
index e9dde1962..ed67b43f3 100644
--- a/sdk/python/arvados/safeapi.py
+++ b/sdk/python/arvados/safeapi.py
@@ -49,6 +49,14 @@ class ThreadSafeApiCache(object):
     """
 
     def __init__(self, apiconfig=None, keep_params={}, api_params={}, version=None):
+        if version is None:
+            try:
+                version = api_params['version']
+            except KeyError:
+                pass
+            else:
+                api_params = dict(api_params)
+                del api_params['version']
         if apiconfig or apiconfig is None:
             self._api_kwargs = api.api_kwargs_from_config(version, apiconfig, **api_params)
         else:
diff --git a/sdk/python/tests/test_safeapi.py b/sdk/python/tests/test_safeapi.py
index 220ba6ff5..c9ed72056 100644
--- a/sdk/python/tests/test_safeapi.py
+++ b/sdk/python/tests/test_safeapi.py
@@ -5,6 +5,8 @@
 import os
 import unittest
 
+import googleapiclient.errors
+
 from arvados import safeapi
 
 from . import run_test_server
@@ -59,3 +61,10 @@ class SafeApiTest(run_test_server.TestCaseWithServers):
     def test_constructor_bad_version(self):
         with self.assertRaises(googleapiclient.errors.UnknownApiNameOrVersion):
             safeapi.ThreadSafeApiCache(version='BadTestVersion')
+
+    def test_constructor_version_from_kwargs(self):
+        api_params = {'version': 'BadTestVersion'}
+        expected_params = api_params.copy()
+        with self.assertRaises(googleapiclient.errors.UnknownApiNameOrVersion):
+            safeapi.ThreadSafeApiCache(None, {}, api_params)
+        self.assertEqual(api_params, expected_params, "api_params was modified in-place")

commit bfac8edbaeeae0237289e8ae47a6e24d4a9155e1
Author: Brett Smith <brett.smith at curii.com>
Date:   Tue Dec 6 23:16:28 2022 -0500

    19686: Add tests for new Arvados client API
    
    This commit ensures all public-facing functions have basic tests for
    good inputs, as well as known-bad argument combinations (if any). This
    includes testing functions like api_from_config that were previously
    tested implicitly by other tests, but did not have their own dedicated
    tests.
    
    Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>

diff --git a/sdk/python/tests/test_api.py b/sdk/python/tests/test_api.py
index 03af8ce5e..20c4f346a 100644
--- a/sdk/python/tests/test_api.py
+++ b/sdk/python/tests/test_api.py
@@ -15,13 +15,22 @@ import os
 import socket
 import string
 import unittest
+import urllib.parse as urlparse
 
 import mock
 from . import run_test_server
 
 from apiclient import errors as apiclient_errors
 from apiclient import http as apiclient_http
-from arvados.api import OrderedJsonModel, RETRY_DELAY_INITIAL, RETRY_DELAY_BACKOFF, RETRY_COUNT
+from arvados.api import (
+    api_client,
+    normalize_api_kwargs,
+    api_kwargs_from_config,
+    OrderedJsonModel,
+    RETRY_DELAY_INITIAL,
+    RETRY_DELAY_BACKOFF,
+    RETRY_COUNT,
+)
 from .arvados_testutil import fake_httplib2_response, queue_with
 
 if not mimetypes.inited:
@@ -36,6 +45,23 @@ class ArvadosApiTest(run_test_server.TestCaseWithServers):
                 json.dumps({'errors': errors,
                             'error_token': '1234567890+12345678'}).encode())
 
+    def _config_from_environ(self):
+        return {
+            key: value
+            for key, value in os.environ.items()
+            if key.startswith('ARVADOS_API_')
+        }
+
+    def _discoveryServiceUrl(
+            self,
+            host=None,
+            path='/discovery/v1/apis/{api}/{apiVersion}/rest',
+            scheme='https',
+    ):
+        if host is None:
+            host = os.environ['ARVADOS_API_HOST']
+        return urlparse.urlunsplit((scheme, host, path, None, None))
+
     def test_new_api_objects_with_cache(self):
         clients = [arvados.api('v1', cache=True) for index in [0, 1]]
         self.assertIsNot(*clients)
@@ -160,6 +186,160 @@ class ArvadosApiTest(run_test_server.TestCaseWithServers):
                 self.assertTrue(hasattr(api_client, 'keep'),
                                 f"client missing keep attribute")
 
+    def test_api_host_constructor(self):
+        cache = True
+        insecure = True
+        client = arvados.api(
+            'v1',
+            cache,
+            os.environ['ARVADOS_API_HOST'],
+            os.environ['ARVADOS_API_TOKEN'],
+            insecure,
+        )
+        self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'],
+                         "client constructed with incorrect token")
+
+    def test_api_url_constructor(self):
+        client = arvados.api(
+            'v1',
+            discoveryServiceUrl=self._discoveryServiceUrl(),
+            token=os.environ['ARVADOS_API_TOKEN'],
+            insecure=True,
+        )
+        self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'],
+                         "client constructed with incorrect token")
+
+    def test_api_bad_args(self):
+        all_kwargs = {
+            'host': os.environ['ARVADOS_API_HOST'],
+            'token': os.environ['ARVADOS_API_TOKEN'],
+            'discoveryServiceUrl': self._discoveryServiceUrl(),
+        }
+        for use_keys in [
+                # Passing only a single key is missing required info
+                *([key] for key in all_kwargs.keys()),
+                # Passing all keys is a conflict
+                list(all_kwargs.keys()),
+        ]:
+            kwargs = {key: all_kwargs[key] for key in use_keys}
+            kwargs_list = ', '.join(use_keys)
+            with self.subTest(f"calling arvados.api with {kwargs_list} fails"), \
+                 self.assertRaises(ValueError):
+                arvados.api('v1', insecure=True, **kwargs)
+
+    def test_api_bad_url(self):
+        for bad_kwargs in [
+                {'discoveryServiceUrl': self._discoveryServiceUrl() + '/BadTestURL'},
+                {'version': 'BadTestVersion', 'host': os.environ['ARVADOS_API_HOST']},
+        ]:
+            bad_key = next(iter(bad_kwargs))
+            with self.subTest(f"api fails with bad {bad_key}"), \
+                 self.assertRaises(apiclient_errors.UnknownApiNameOrVersion):
+                arvados.api(**bad_kwargs, token='test_api_bad_url', insecure=True)
+
+    def test_normalize_api_good_args(self):
+        for version, discoveryServiceUrl, host in [
+                ('Test1', None, os.environ['ARVADOS_API_HOST']),
+                (None, self._discoveryServiceUrl(), None)
+        ]:
+            argname = 'discoveryServiceUrl' if host is None else 'host'
+            with self.subTest(f"normalize_api_kwargs with {argname}"):
+                actual = normalize_api_kwargs(
+                    version,
+                    discoveryServiceUrl,
+                    host,
+                    os.environ['ARVADOS_API_TOKEN'],
+                    insecure=True,
+                )
+                self.assertEqual(actual['discoveryServiceUrl'], self._discoveryServiceUrl())
+                self.assertEqual(actual['token'], os.environ['ARVADOS_API_TOKEN'])
+                self.assertEqual(actual['version'], version or 'v1')
+                self.assertTrue(actual['insecure'])
+                self.assertNotIn('host', actual)
+
+    def test_normalize_api_bad_args(self):
+        all_args = (
+            self._discoveryServiceUrl(),
+            os.environ['ARVADOS_API_HOST'],
+            os.environ['ARVADOS_API_TOKEN'],
+        )
+        for arg_index, arg_value in enumerate(all_args):
+            args = [None] * len(all_args)
+            args[arg_index] = arg_value
+            with self.subTest(f"normalize_api_kwargs with only arg #{arg_index + 1}"), \
+                 self.assertRaises(ValueError):
+                normalize_api_kwargs('v1', *args)
+        with self.subTest("normalize_api_kwargs with discoveryServiceUrl and host"), \
+             self.assertRaises(ValueError):
+            normalize_api_kwargs('v1', *all_args)
+
+    def test_api_from_config_default(self):
+        client = arvados.api_from_config('v1')
+        self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'],
+                         "client constructed with incorrect token")
+
+    def test_api_from_config_explicit(self):
+        config = self._config_from_environ()
+        client = arvados.api_from_config('v1', config)
+        self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'],
+                         "client constructed with incorrect token")
+
+    def test_api_from_bad_config(self):
+        base_config = self._config_from_environ()
+        for del_key in ['ARVADOS_API_HOST', 'ARVADOS_API_TOKEN']:
+            with self.subTest(f"api_from_config without {del_key} fails"), \
+                 self.assertRaises(ValueError):
+                config = dict(base_config)
+                del config[del_key]
+                arvados.api_from_config('v1', config)
+
+    def test_api_kwargs_from_good_config(self):
+        for config in [None, self._config_from_environ()]:
+            conf_type = 'default' if config is None else 'passed'
+            with self.subTest(f"api_kwargs_from_config with {conf_type} config"):
+                version = 'Test1' if config else None
+                actual = api_kwargs_from_config(version, config)
+                self.assertEqual(actual['discoveryServiceUrl'], self._discoveryServiceUrl())
+                self.assertEqual(actual['token'], os.environ['ARVADOS_API_TOKEN'])
+                self.assertEqual(actual['version'], version or 'v1')
+                self.assertTrue(actual['insecure'])
+                self.assertNotIn('host', actual)
+
+    def test_api_kwargs_from_bad_config(self):
+        base_config = self._config_from_environ()
+        for del_key in ['ARVADOS_API_HOST', 'ARVADOS_API_TOKEN']:
+            with self.subTest(f"api_kwargs_from_config without {del_key} fails"), \
+                 self.assertRaises(ValueError):
+                config = dict(base_config)
+                del config[del_key]
+                api_kwargs_from_config('v1', config)
+
+    def test_api_client_constructor(self):
+        client = api_client(
+            'v1',
+            self._discoveryServiceUrl(),
+            os.environ['ARVADOS_API_TOKEN'],
+            insecure=True,
+        )
+        self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'],
+                         "client constructed with incorrect token")
+        self.assertFalse(
+            hasattr(client, 'localapi'),
+            "client has localapi method when it should not be thread-safe",
+        )
+
+    def test_api_client_bad_url(self):
+        all_args = ('v1', self._discoveryServiceUrl(), 'test_api_client_bad_url')
+        for arg_index, arg_value in [
+                (0, 'BadTestVersion'),
+                (1, all_args[1] + '/BadTestURL'),
+        ]:
+            with self.subTest(f"api_client fails with {arg_index}={arg_value!r}"), \
+                 self.assertRaises(apiclient_errors.UnknownApiNameOrVersion):
+                args = list(all_args)
+                args[arg_index] = arg_value
+                api_client(*args, insecure=True)
+
 
 class RetryREST(unittest.TestCase):
     def setUp(self):
diff --git a/sdk/python/tests/test_safeapi.py b/sdk/python/tests/test_safeapi.py
new file mode 100644
index 000000000..220ba6ff5
--- /dev/null
+++ b/sdk/python/tests/test_safeapi.py
@@ -0,0 +1,61 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import os
+import unittest
+
+from arvados import safeapi
+
+from . import run_test_server
+
+class SafeApiTest(run_test_server.TestCaseWithServers):
+    MAIN_SERVER = {}
+
+    def test_constructor(self):
+        env_mapping = {
+            key: value
+            for key, value in os.environ.items()
+            if key.startswith('ARVADOS_API_')
+        }
+        extra_params = {
+            'timeout': 299,
+        }
+        base_params = {
+            key[12:].lower(): value
+            for key, value in env_mapping.items()
+        }
+        try:
+            base_params['insecure'] = base_params.pop('host_insecure')
+        except KeyError:
+            pass
+        keep_params = {}
+        for config, params, subtest in [
+                (None, {}, "default arguments"),
+                (None, extra_params, "extra params"),
+                (env_mapping, {}, "explicit config"),
+                (env_mapping, extra_params, "explicit config and params"),
+                ({}, base_params, "params only"),
+        ]:
+            with self.subTest(f"test constructor with {subtest}"):
+                expected_timeout = params.get('timeout', 300)
+                expected_params = dict(params)
+                expected_keep_params = dict(keep_params)
+                client = safeapi.ThreadSafeApiCache(config, keep_params, params, 'v1')
+                self.assertTrue(hasattr(client, 'localapi'), "client missing localapi method")
+                self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'])
+                self.assertEqual(client._http.timeout, expected_timeout)
+                self.assertEqual(params, expected_params,
+                                 "api_params was modified in-place")
+                self.assertEqual(keep_params, expected_keep_params,
+                                 "keep_params was modified in-place")
+
+    def test_constructor_no_args(self):
+        client = safeapi.ThreadSafeApiCache()
+        self.assertTrue(hasattr(client, 'localapi'), "client missing localapi method")
+        self.assertEqual(client.api_token, os.environ['ARVADOS_API_TOKEN'])
+        self.assertTrue(client.insecure)
+
+    def test_constructor_bad_version(self):
+        with self.assertRaises(googleapiclient.errors.UnknownApiNameOrVersion):
+            safeapi.ThreadSafeApiCache(version='BadTestVersion')

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list