api: time.clock compatible code (#70650)
time.clock is removed in Python 3.8. Add time.clock compatible code. Fixes: #70649 Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
This commit is contained in:
parent
4a735adc21
commit
055871cbb8
4 changed files with 113 additions and 25 deletions
2
changelogs/fragments/70649_time_clock.yml
Normal file
2
changelogs/fragments/70649_time_clock.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- api - time.clock is removed in Python 3.8, add backward compatible code (https://github.com/ansible/ansible/issues/70649).
|
|
@ -4,27 +4,9 @@
|
|||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# (c) 2015 Brian Ccoa, <bcoca@ansible.com>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
# Copyright: (c) 2015, Brian Coca, <bcoca@ansible.com>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
"""
|
||||
This module adds shared support for generic api modules
|
||||
|
||||
|
@ -44,6 +26,7 @@ The 'api' module provides the following common argument specs:
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
|
@ -91,12 +74,16 @@ def rate_limit(rate=None, rate_limit=None):
|
|||
last = [0.0]
|
||||
|
||||
def ratelimited(*args, **kwargs):
|
||||
if sys.version_info >= (3, 8):
|
||||
real_time = time.process_time
|
||||
else:
|
||||
real_time = time.clock
|
||||
if minrate is not None:
|
||||
elapsed = time.clock() - last[0]
|
||||
elapsed = real_time() - last[0]
|
||||
left = minrate - elapsed
|
||||
if left > 0:
|
||||
time.sleep(left)
|
||||
last[0] = time.clock()
|
||||
last[0] = real_time()
|
||||
ret = f(*args, **kwargs)
|
||||
return ret
|
||||
|
||||
|
@ -107,14 +94,13 @@ def rate_limit(rate=None, rate_limit=None):
|
|||
def retry(retries=None, retry_pause=1):
|
||||
"""Retry decorator"""
|
||||
def wrapper(f):
|
||||
retry_count = 0
|
||||
|
||||
def retried(*args, **kwargs):
|
||||
retry_count = 0
|
||||
if retries is not None:
|
||||
ret = None
|
||||
while True:
|
||||
# pylint doesn't understand this is a closure
|
||||
retry_count += 1 # pylint: disable=undefined-variable
|
||||
retry_count += 1
|
||||
if retry_count >= retries:
|
||||
raise Exception("Retry limit exceeded: %d" % retries)
|
||||
try:
|
||||
|
|
|
@ -14,6 +14,7 @@ import pytest
|
|||
|
||||
from units.compat.mock import MagicMock
|
||||
from ansible.module_utils import basic
|
||||
from ansible.module_utils.api import basic_auth_argument_spec, rate_limit_argument_spec, retry_argument_spec
|
||||
from ansible.module_utils.common.warnings import get_deprecation_messages, get_warning_messages
|
||||
from ansible.module_utils.six import integer_types, string_types
|
||||
from ansible.module_utils.six.moves import builtins
|
||||
|
@ -88,6 +89,25 @@ INVALID_SPECS = (
|
|||
({'arg': {'required': True}}, {}, 'missing required arguments: arg'),
|
||||
)
|
||||
|
||||
BASIC_AUTH_VALID_ARGS = [
|
||||
{'api_username': 'user1', 'api_password': 'password1', 'api_url': 'http://example.com', 'validate_certs': False},
|
||||
{'api_username': 'user1', 'api_password': 'password1', 'api_url': 'http://example.com', 'validate_certs': True},
|
||||
]
|
||||
|
||||
RATE_LIMIT_VALID_ARGS = [
|
||||
{'rate': 1, 'rate_limit': 1},
|
||||
{'rate': '1', 'rate_limit': 1},
|
||||
{'rate': 1, 'rate_limit': '1'},
|
||||
{'rate': '1', 'rate_limit': '1'},
|
||||
]
|
||||
|
||||
RETRY_VALID_ARGS = [
|
||||
{'retries': 1, 'retry_pause': 1.5},
|
||||
{'retries': '1', 'retry_pause': '1.5'},
|
||||
{'retries': 1, 'retry_pause': '1.5'},
|
||||
{'retries': '1', 'retry_pause': 1.5},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def complex_argspec():
|
||||
|
@ -213,6 +233,38 @@ def test_validator_function(mocker, stdin):
|
|||
assert am.params['arg'] == 27
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stdin', BASIC_AUTH_VALID_ARGS, indirect=['stdin'])
|
||||
def test_validate_basic_auth_arg(mocker, stdin):
|
||||
kwargs = dict(
|
||||
argument_spec=basic_auth_argument_spec()
|
||||
)
|
||||
am = basic.AnsibleModule(**kwargs)
|
||||
assert isinstance(am.params['api_username'], string_types)
|
||||
assert isinstance(am.params['api_password'], string_types)
|
||||
assert isinstance(am.params['api_url'], string_types)
|
||||
assert isinstance(am.params['validate_certs'], bool)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stdin', RATE_LIMIT_VALID_ARGS, indirect=['stdin'])
|
||||
def test_validate_rate_limit_argument_spec(mocker, stdin):
|
||||
kwargs = dict(
|
||||
argument_spec=rate_limit_argument_spec()
|
||||
)
|
||||
am = basic.AnsibleModule(**kwargs)
|
||||
assert isinstance(am.params['rate'], integer_types)
|
||||
assert isinstance(am.params['rate_limit'], integer_types)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stdin', RETRY_VALID_ARGS, indirect=['stdin'])
|
||||
def test_validate_retry_argument_spec(mocker, stdin):
|
||||
kwargs = dict(
|
||||
argument_spec=retry_argument_spec()
|
||||
)
|
||||
am = basic.AnsibleModule(**kwargs)
|
||||
assert isinstance(am.params['retries'], integer_types)
|
||||
assert isinstance(am.params['retry_pause'], float)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stdin', [{'arg': '123'}, {'arg': 123}], indirect=['stdin'])
|
||||
def test_validator_string_type(mocker, stdin):
|
||||
# Custom callable that is 'str'
|
||||
|
|
48
test/units/module_utils/test_api.py
Normal file
48
test/units/module_utils/test_api.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2020, Abhijeet Kasurde <akasurde@redhat.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.api import rate_limit, retry
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class TestRateLimit:
|
||||
|
||||
def test_ratelimit(self):
|
||||
@rate_limit(rate=1, rate_limit=1)
|
||||
def login_database():
|
||||
return "success"
|
||||
r = login_database()
|
||||
|
||||
assert r == 'success'
|
||||
|
||||
|
||||
class TestRetry:
|
||||
|
||||
def test_no_retry_required(self):
|
||||
self.counter = 0
|
||||
|
||||
@retry(retries=4, retry_pause=2)
|
||||
def login_database():
|
||||
self.counter += 1
|
||||
return 'success'
|
||||
|
||||
r = login_database()
|
||||
|
||||
assert r == 'success'
|
||||
assert self.counter == 1
|
||||
|
||||
def test_catch_exception(self):
|
||||
|
||||
@retry(retries=1)
|
||||
def login_database():
|
||||
return 'success'
|
||||
|
||||
with pytest.raises(Exception):
|
||||
login_database()
|
Loading…
Reference in a new issue