Skip to content

Commit 359b7df

Browse files
authored
Python 3.10 and 3.12 compatibility changes and package version compatibility updates (#41)
These changes are fixes for datetime.utcnow() deprecation in python 3.10 and the change in randint to only accept integer arguments.
1 parent d8a5366 commit 359b7df

File tree

5 files changed

+101
-10
lines changed

5 files changed

+101
-10
lines changed

.github/workflows/python-package.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
19+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
2020

2121
steps:
2222
- uses: actions/checkout@v3

src/aws_secretsmanager_caching/cache/items.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import threading
1717
from abc import ABCMeta, abstractmethod
1818
from copy import deepcopy
19-
from datetime import datetime, timedelta
19+
from datetime import datetime, timedelta, timezone
2020
from random import randint
2121

2222
from .lru import LRUCache
@@ -61,7 +61,7 @@ def _is_refresh_needed(self):
6161
return False
6262
if self._next_retry_time is None:
6363
return False
64-
return self._next_retry_time <= datetime.utcnow()
64+
return self._next_retry_time <= datetime.now(timezone.utc)
6565

6666
@abstractmethod
6767
def _execute_refresh(self):
@@ -102,7 +102,7 @@ def __refresh(self):
102102
)
103103
self._exception_count += 1
104104
delay = min(delay, self._config.exception_retry_delay_max)
105-
self._next_retry_time = datetime.utcnow() + timedelta(milliseconds=delay)
105+
self._next_retry_time = datetime.now(timezone.utc) + timedelta(milliseconds=delay)
106106

107107
def get_secret_value(self, version_stage=None):
108108
"""Get the cached secret value for the given version stage.
@@ -156,7 +156,7 @@ def __init__(self, config, client, secret_id):
156156
"""
157157
super(SecretCacheItem, self).__init__(config, client, secret_id)
158158
self._versions = LRUCache(10)
159-
self._next_refresh_time = datetime.utcnow()
159+
self._next_refresh_time = datetime.now(timezone.utc)
160160

161161
def _is_refresh_needed(self):
162162
"""Determine if the cached item should be refreshed.
@@ -168,7 +168,7 @@ def _is_refresh_needed(self):
168168
return True
169169
if self._exception:
170170
return False
171-
return self._next_refresh_time <= datetime.utcnow()
171+
return self._next_refresh_time <= datetime.now(timezone.utc)
172172

173173
@staticmethod
174174
def _get_version_id(result, version_stage):
@@ -200,7 +200,7 @@ def _execute_refresh(self):
200200
"""
201201
result = self._client.describe_secret(SecretId=self._secret_id)
202202
ttl = self._config.secret_refresh_interval
203-
self._next_refresh_time = datetime.utcnow() + timedelta(seconds=randint(round(ttl / 2), ttl))
203+
self._next_refresh_time = datetime.now(timezone.utc) + timedelta(seconds=randint(round(ttl / 2), ttl))
204204
return result
205205

206206
def _get_version(self, version_stage):

src/aws_secretsmanager_caching/config.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"""Secret cache configuration object."""
1414

1515
from copy import deepcopy
16-
from datetime import timedelta
1716

1817

1918
class SecretCacheConfig:
@@ -54,7 +53,7 @@ class SecretCacheConfig:
5453
"exception_retry_growth_factor": 2,
5554
"exception_retry_delay_max": 3600,
5655
"default_version_stage": "AWSCURRENT",
57-
"secret_refresh_interval": timedelta(hours=1).total_seconds(),
56+
"secret_refresh_interval": 3600,
5857
"secret_cache_hook": None
5958
}
6059

test/unit/test_config.py

+4
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ def test_config_default_version_stage(self):
3333
stage = 'nothing'
3434
config = SecretCacheConfig(default_version_stage=stage)
3535
self.assertEqual(config.default_version_stage, stage)
36+
37+
def test_default_secret_refresh_interval_typing(self):
38+
config = SecretCacheConfig()
39+
self.assertIsInstance(config.secret_refresh_interval, int)

test/unit/test_items.py

+89-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
Unit test suite for items module
1515
"""
1616
import unittest
17+
from datetime import timezone, datetime, timedelta
18+
from unittest.mock import Mock
1719

18-
from aws_secretsmanager_caching.cache.items import SecretCacheObject
20+
from aws_secretsmanager_caching.cache.items import SecretCacheObject, SecretCacheItem
1921
from aws_secretsmanager_caching.config import SecretCacheConfig
2022

2123

@@ -47,3 +49,89 @@ def test_simple_2(self):
4749
self.assertIsNone(sco.get_secret_value())
4850
sco._exception = Exception("test")
4951
self.assertRaises(Exception, sco.get_secret_value)
52+
53+
def test_datetime_fix_is_refresh_needed(self):
54+
secret_cached_object = TestSecretCacheObject.TestObject(SecretCacheConfig(), None, None)
55+
56+
# Variable values set in order to be able to test modified line with assert statement (False is not None)
57+
secret_cached_object._next_retry_time = datetime.now(tz=timezone.utc)
58+
secret_cached_object._refresh_needed = False
59+
secret_cached_object._exception = not None
60+
61+
self.assertTrue(secret_cached_object._is_refresh_needed())
62+
63+
def test_datetime_fix_refresh(self):
64+
exp_factor = 11
65+
66+
secret_cached_object = SecretCacheObject(
67+
SecretCacheConfig(exception_retry_delay_base=1, exception_retry_growth_factor=2),
68+
None, None
69+
)
70+
secret_cached_object._set_result = Mock(side_effect=Exception("exception used for test"))
71+
secret_cached_object._refresh_needed = True
72+
secret_cached_object._exception_count = exp_factor # delay = min(1*(2^exp_factor) = 2048, 3600)
73+
74+
t_before = datetime.now(tz=timezone.utc)
75+
secret_cached_object._SecretCacheObject__refresh()
76+
t_after = datetime.now(tz=timezone.utc)
77+
78+
t_before_delay = t_before + timedelta(
79+
milliseconds=secret_cached_object._config.exception_retry_delay_base * (
80+
secret_cached_object._config.exception_retry_growth_factor ** exp_factor
81+
)
82+
)
83+
self.assertLessEqual(t_before_delay, secret_cached_object._next_retry_time)
84+
85+
t_after_delay = t_after + timedelta(
86+
milliseconds=secret_cached_object._config.exception_retry_delay_base * (
87+
secret_cached_object._config.exception_retry_growth_factor ** exp_factor
88+
)
89+
)
90+
self.assertGreaterEqual(t_after_delay, secret_cached_object._next_retry_time)
91+
92+
class TestSecretCacheItem(unittest.TestCase):
93+
def setUp(self):
94+
pass
95+
96+
def tearDown(self):
97+
pass
98+
99+
def test_datetime_fix_SCI_init(self):
100+
config = SecretCacheConfig()
101+
t_before = datetime.now(tz=timezone.utc)
102+
secret_cache_item = SecretCacheItem(config, None, None)
103+
t_after = datetime.now(tz=timezone.utc)
104+
105+
self.assertGreaterEqual(secret_cache_item._next_refresh_time, t_before)
106+
self.assertLessEqual(secret_cache_item._next_refresh_time, t_after)
107+
108+
def test_datetime_fix_refresh_needed(self):
109+
config = SecretCacheConfig()
110+
secret_cache_item = SecretCacheItem(config, None, None)
111+
112+
# Variable values set in order to be able to test modified line with assert statement (False is not None)
113+
secret_cache_item._refresh_needed = False
114+
secret_cache_item._exception = False
115+
secret_cache_item._next_retry_time = None
116+
117+
self.assertTrue(secret_cache_item._is_refresh_needed())
118+
119+
def test_datetime_fix_execute_refresh(self):
120+
client_mock = Mock()
121+
client_mock.describe_secret = Mock()
122+
client_mock.describe_secret.return_value = "test"
123+
124+
config = SecretCacheConfig()
125+
secret_cache_item = SecretCacheItem(config, client_mock, None)
126+
127+
t_before = datetime.now(tz=timezone.utc)
128+
secret_cache_item._execute_refresh()
129+
t_after = datetime.now(tz=timezone.utc)
130+
131+
# Make sure that the timezone is correctly set
132+
self.assertEqual(secret_cache_item._next_refresh_time.tzinfo, timezone.utc)
133+
134+
# Check that secret_refresh_interval addition works as intended
135+
self.assertGreaterEqual(secret_cache_item._next_refresh_time, t_before)
136+
t_max_after = t_after + timedelta(seconds=config.secret_refresh_interval)
137+
self.assertLessEqual(secret_cache_item._next_refresh_time, t_max_after)

0 commit comments

Comments
 (0)