Skip to content

Commit a8900c4

Browse files
committed
Use model's AWS credentials in threads
When a model specifies custom AWS credentials (instead of global ones), they should be used when creating new sessions in threads. Previously, threads would always use the global credentials.
1 parent e59d9bb commit a8900c4

File tree

5 files changed

+42
-9
lines changed

5 files changed

+42
-9
lines changed

docs/release_notes.rst

+13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@
33
Release Notes
44
=============
55

6+
v5.4.1
7+
----------
8+
* Use model's AWS credentials in threads (#1164)
9+
10+
A model can specify custom AWS credentials in the ``Meta`` class (in lieu of "global"
11+
AWS credentials from the environment). Previously those model-specific credentials
12+
were not used from within new threads.
13+
14+
Contributors to this release:
15+
16+
* @atsuoishimoto
17+
18+
619
v5.4.0
720
----------
821
* Expose transaction cancellation reasons in

pynamodb/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
"""
88
__author__ = 'Jharrod LaFon'
99
__license__ = 'MIT'
10-
__version__ = '5.4.0'
10+
__version__ = '5.4.1'

pynamodb/connection/base.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,10 @@ def __init__(self,
257257
max_retry_attempts: Optional[int] = None,
258258
base_backoff_ms: Optional[int] = None,
259259
max_pool_connections: Optional[int] = None,
260-
extra_headers: Optional[Mapping[str, str]] = None):
260+
extra_headers: Optional[Mapping[str, str]] = None,
261+
aws_access_key_id: Optional[str] = None,
262+
aws_secret_access_key: Optional[str] = None,
263+
aws_session_token: Optional[str] = None):
261264
self._tables: Dict[str, MetaTable] = {}
262265
self.host = host
263266
self._local = local()
@@ -298,6 +301,10 @@ def __init__(self,
298301
else:
299302
self._extra_headers = get_settings_value('extra_headers')
300303

304+
self._aws_access_key_id = aws_access_key_id
305+
self._aws_secret_access_key = aws_secret_access_key
306+
self._aws_session_token = aws_session_token
307+
301308
def __repr__(self) -> str:
302309
return "Connection<{}>".format(self.client.meta.endpoint_url)
303310

@@ -558,6 +565,10 @@ def session(self) -> botocore.session.Session:
558565
# botocore client creation is not thread safe as of v1.2.5+ (see issue #153)
559566
if getattr(self._local, 'session', None) is None:
560567
self._local.session = get_session()
568+
if self._aws_access_key_id and self._aws_secret_access_key:
569+
self._local.session.set_credentials(self._aws_access_key_id,
570+
self._aws_secret_access_key,
571+
self._aws_session_token)
561572
return self._local.session
562573

563574
@property

pynamodb/connection/table.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,14 @@ def __init__(
4141
max_retry_attempts=max_retry_attempts,
4242
base_backoff_ms=base_backoff_ms,
4343
max_pool_connections=max_pool_connections,
44-
extra_headers=extra_headers)
44+
extra_headers=extra_headers,
45+
aws_access_key_id=aws_access_key_id,
46+
aws_secret_access_key=aws_secret_access_key,
47+
aws_session_token=aws_session_token)
48+
4549
if meta_table is not None:
4650
self.connection.add_meta_table(meta_table)
4751

48-
if aws_access_key_id and aws_secret_access_key:
49-
self.connection.session.set_credentials(aws_access_key_id,
50-
aws_secret_access_key,
51-
aws_session_token)
52-
5352
def get_meta_table(self) -> MetaTable:
5453
"""
5554
Returns a MetaTable

tests/test_table_connection.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Test suite for the table class
33
"""
44
from unittest import TestCase
5+
from concurrent.futures import ThreadPoolExecutor
56

67
from pynamodb.connection import TableConnection
78
from pynamodb.constants import DEFAULT_REGION
@@ -39,7 +40,16 @@ def test_connection_session_set_credentials(self):
3940
aws_access_key_id='access_key_id',
4041
aws_secret_access_key='secret_access_key')
4142

42-
credentials = conn.connection.session.get_credentials()
43+
def get_credentials():
44+
return conn.connection.session.get_credentials()
45+
46+
credentials = get_credentials()
47+
self.assertEqual(credentials.access_key, 'access_key_id')
48+
self.assertEqual(credentials.secret_key, 'secret_access_key')
49+
50+
with ThreadPoolExecutor() as executor:
51+
fut = executor.submit(get_credentials)
52+
credentials = fut.result()
4353

4454
self.assertEqual(credentials.access_key, 'access_key_id')
4555
self.assertEqual(credentials.secret_key, 'secret_access_key')

0 commit comments

Comments
 (0)