X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/f7a0cd455c4da30cb5b65923c39651fc91d991b6..873fcf181c037cc1e42419bfeaf5bb70c9d9e239:/sdk/python/tests/test_api.py diff --git a/sdk/python/tests/test_api.py b/sdk/python/tests/test_api.py index d8136f4ac5..a4e943f6f2 100644 --- a/sdk/python/tests/test_api.py +++ b/sdk/python/tests/test_api.py @@ -2,15 +2,13 @@ # # SPDX-License-Identifier: Apache-2.0 -from __future__ import absolute_import -from builtins import str -from builtins import range import arvados import collections import contextlib import httplib2 import itertools import json +import logging import mimetypes import os import socket @@ -19,7 +17,7 @@ import sys import unittest import urllib.parse as urlparse -import mock +from unittest import mock from . import run_test_server from apiclient import errors as apiclient_errors @@ -29,8 +27,10 @@ from arvados.api import ( normalize_api_kwargs, api_kwargs_from_config, OrderedJsonModel, + _googleapiclient_log_lock, ) from .arvados_testutil import fake_httplib2_response, mock_api_responses, queue_with +import httplib2.error if not mimetypes.inited: mimetypes.init() @@ -38,7 +38,7 @@ if not mimetypes.inited: class ArvadosApiTest(run_test_server.TestCaseWithServers): MAIN_SERVER = {} ERROR_HEADERS = {'Content-Type': mimetypes.types_map['.json']} - RETRIED_4XX = frozenset([408, 409, 422, 423]) + RETRIED_4XX = frozenset([408, 409, 423]) def api_error_response(self, code, *errors): return (fake_httplib2_response(code, **self.ERROR_HEADERS), @@ -168,7 +168,10 @@ class ArvadosApiTest(run_test_server.TestCaseWithServers): def test_4xx_not_retried(self): client = arvados.api('v1', num_retries=3) - for code in [400, 401, 404]: + # Note that googleapiclient does retry 403 *if* the response JSON + # includes flags that say the request was denied by rate limiting. + # An empty JSON response like we use here should not be retried. + for code in [400, 401, 403, 404, 422]: with self.subTest(f'error {code}'), mock.patch('time.sleep'): with mock_api_responses( client, @@ -388,6 +391,46 @@ class ArvadosApiTest(run_test_server.TestCaseWithServers): args[arg_index] = arg_value api_client(*args, insecure=True) + def test_initial_retry_logs(self): + try: + _googleapiclient_log_lock.release() + except RuntimeError: + # Lock was never acquired - that's the state we want anyway + pass + real_logger = logging.getLogger('googleapiclient.http') + mock_logger = mock.Mock(wraps=real_logger) + mock_logger.handlers = logging.getLogger('googleapiclient').handlers + mock_logger.level = logging.NOTSET + with mock.patch('logging.getLogger', return_value=mock_logger), \ + mock.patch('time.sleep'), \ + self.assertLogs(real_logger, 'INFO') as actual_logs: + try: + api_client('v1', 'https://test.invalid/', 'NoToken', num_retries=1) + except httplib2.error.ServerNotFoundError: + pass + mock_logger.addFilter.assert_called() + mock_logger.addHandler.assert_called() + mock_logger.setLevel.assert_called() + mock_logger.removeHandler.assert_called() + mock_logger.removeFilter.assert_called() + self.assertRegex(actual_logs.output[0], r'^INFO:googleapiclient\.http:Sleeping \d') + + def test_configured_logger_untouched(self): + real_logger = logging.getLogger('googleapiclient.http') + mock_logger = mock.Mock(wraps=real_logger) + mock_logger.handlers = logging.getLogger().handlers + with mock.patch('logging.getLogger', return_value=mock_logger), \ + mock.patch('time.sleep'): + try: + api_client('v1', 'https://test.invalid/', 'NoToken', num_retries=1) + except httplib2.error.ServerNotFoundError: + pass + mock_logger.addFilter.assert_not_called() + mock_logger.addHandler.assert_not_called() + mock_logger.setLevel.assert_not_called() + mock_logger.removeHandler.assert_not_called() + mock_logger.removeFilter.assert_not_called() + class ConstructNumRetriesTestCase(unittest.TestCase): @staticmethod