Skip to content

Commit 7265c84

Browse files
committed
feat: restructure client supplier configuration
1 parent 2bac0de commit 7265c84

File tree

3 files changed

+46
-56
lines changed

3 files changed

+46
-56
lines changed

src/aws_encryption_sdk/keyrings/aws_kms/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
from aws_encryption_sdk.materials_managers import DecryptionMaterials, EncryptionMaterials
2020
from aws_encryption_sdk.structures import EncryptedDataKey, KeyringTrace, KeyringTraceFlag, MasterKeyInfo, RawDataKey
2121

22-
from .client_suppliers import ClientSupplier, DefaultClientSupplier
22+
from .client_suppliers import DefaultClientSupplier
23+
24+
from .client_suppliers import ClientSupplier # noqa - only used in docstring params; this confuses flake8
2325

2426
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
25-
from typing import Any, Dict, Iterable, Union # noqa pylint: disable=unused-import
27+
from typing import Dict, Iterable, Union # noqa pylint: disable=unused-import
2628
from .client_suppliers import ClientSupplierType # noqa pylint: disable=unused-import
2729
except ImportError: # pragma: no cover
2830
# We only actually need these imports when running the mypy checks

src/aws_encryption_sdk/keyrings/aws_kms/client_suppliers.py

+24-27
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,30 @@
99

1010
import attr
1111
import six
12-
from attr.validators import deep_iterable, instance_of
12+
from attr.validators import deep_iterable, instance_of, optional
1313
from botocore.client import BaseClient
14-
from botocore.session import Session as BotocoreSession
1514

1615
from aws_encryption_sdk.exceptions import UnknownRegionError
1716
from aws_encryption_sdk.internal.validators import value_is_not_a_string
1817

1918
from .client_cache import ClientCache
2019

2120
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
22-
from typing import Any, Dict, Union # noqa pylint: disable=unused-import
21+
from typing import Callable, Union # noqa pylint: disable=unused-import
22+
23+
ClientSupplierType = Callable[[Union[None, str]], BaseClient]
2324
except ImportError: # pragma: no cover
2425
# We only actually need these imports when running the mypy checks
2526
pass
2627

2728
_LOGGER = logging.getLogger(__name__)
28-
__all__ = ("ClientSupplier", "DefaultClientSupplier", "AllowRegionsClientSupplier", "DenyRegionsClientSupplier")
29+
__all__ = (
30+
"ClientSupplier",
31+
"ClientSupplierType",
32+
"DefaultClientSupplier",
33+
"AllowRegionsClientSupplier",
34+
"DenyRegionsClientSupplier",
35+
)
2936

3037

3138
class ClientSupplier(object):
@@ -55,19 +62,15 @@ class DefaultClientSupplier(ClientSupplier):
5562
:type botocore_session: botocore.session.Session
5663
"""
5764

58-
_botocore_session = attr.ib(default=attr.Factory(BotocoreSession), validator=instance_of(BotocoreSession))
59-
60-
def __attrs_post_init__(self):
61-
"""Set up internal client cache."""
62-
self._cache = ClientCache(botocore_session=self._botocore_session)
65+
_client_cache = attr.ib(default=attr.Factory(ClientCache), validator=instance_of(ClientCache))
6366

6467
def __call__(self, region_name):
6568
# type: (Union[None, str]) -> BaseClient
6669
"""Return a client for the requested region.
6770
6871
:rtype: BaseClient
6972
"""
70-
return self._cache.client(region_name=region_name, service="kms")
73+
return self._client_cache.client(region_name=region_name, service="kms")
7174

7275

7376
@attr.s
@@ -77,20 +80,17 @@ class AllowRegionsClientSupplier(ClientSupplier):
7780
.. versionadded:: 1.5.0
7881
7982
:param List[str] allowed_regions: Regions to allow
80-
:param botocore_session: botocore session to use when creating clients (optional)
81-
:type botocore_session: botocore.session.Session
83+
:param ClientSupplier client_supplier: Client supplier to wrap (optional)
8284
"""
8385

8486
allowed_regions = attr.ib(
8587
validator=(deep_iterable(member_validator=instance_of(six.string_types)), value_is_not_a_string)
8688
)
87-
_botocore_session = attr.ib(default=attr.Factory(BotocoreSession), validator=instance_of(BotocoreSession))
88-
89-
def __attrs_post_init__(self):
90-
"""Set up internal client supplier."""
91-
self._supplier = DefaultClientSupplier(botocore_session=self._botocore_session)
89+
_client_supplier = attr.ib(
90+
default=attr.Factory(DefaultClientSupplier), validator=optional(instance_of(ClientSupplier))
91+
)
9292

93-
def client(self, region_name):
93+
def __call__(self, region_name):
9494
# type: (Union[None, str]) -> BaseClient
9595
"""Return a client for the requested region.
9696
@@ -100,7 +100,7 @@ def client(self, region_name):
100100
if region_name not in self.allowed_regions:
101101
raise UnknownRegionError("Unable to provide client for region '{}'".format(region_name))
102102

103-
return self._supplier(region_name)
103+
return self._client_supplier(region_name)
104104

105105

106106
@attr.s
@@ -110,18 +110,15 @@ class DenyRegionsClientSupplier(ClientSupplier):
110110
.. versionadded:: 1.5.0
111111
112112
:param List[str] denied_regions: Regions to deny
113-
:param botocore_session: Botocore session to use when creating clients (optional)
114-
:type botocore_session: botocore.session.Session
113+
:param ClientSupplier client_supplier: Client supplier to wrap (optional)
115114
"""
116115

117116
denied_regions = attr.ib(
118117
validator=(deep_iterable(member_validator=instance_of(six.string_types)), value_is_not_a_string)
119118
)
120-
_botocore_session = attr.ib(default=attr.Factory(BotocoreSession), validator=instance_of(BotocoreSession))
121-
122-
def __attrs_post_init__(self):
123-
"""Set up internal client supplier."""
124-
self._supplier = DefaultClientSupplier(botocore_session=self._botocore_session)
119+
_client_supplier = attr.ib(
120+
default=attr.Factory(DefaultClientSupplier), validator=optional(instance_of(ClientSupplier))
121+
)
125122

126123
def __call__(self, region_name):
127124
# type: (Union[None, str]) -> BaseClient
@@ -133,4 +130,4 @@ def __call__(self, region_name):
133130
if region_name in self.denied_regions:
134131
raise UnknownRegionError("Unable to provide client for region '{}'".format(region_name))
135132

136-
return self._supplier(region_name)
133+
return self._client_supplier(region_name)

test/functional/keyrings/aws_kms/test_client_suppliers.py

+18-27
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# SPDX-License-Identifier: Apache-2.0
33
"""Functional tests for ``aws_encryption_sdk.keyrings.aws_kms.client_suppliers``."""
44
import pytest
5-
from botocore.session import Session
65

76
from aws_encryption_sdk.exceptions import UnknownRegionError
87
from aws_encryption_sdk.keyrings.aws_kms.client_suppliers import (
@@ -21,36 +20,20 @@ def test_default_supplier_not_implemented():
2120
with pytest.raises(NotImplementedError) as excinfo:
2221
test("region")
2322

24-
excinfo.match("'ClientSupplier' does not implement 'client'")
23+
excinfo.match("'ClientSupplier' is not callable")
2524

2625

2726
def test_default_supplier_uses_cache():
2827
supplier = DefaultClientSupplier()
2928

3029
region = "us-west-2"
31-
expected = supplier._cache.client(region_name=region, service="kms")
30+
expected = supplier._client_cache.client(region_name=region, service="kms")
3231

3332
test = supplier(region)
3433

3534
assert test is expected
3635

3736

38-
def test_default_supplier_passes_session():
39-
botocore_session = Session()
40-
41-
test = DefaultClientSupplier(botocore_session=botocore_session)
42-
43-
assert test._cache._botocore_session is botocore_session
44-
45-
46-
def test_allow_regions_supplier_passes_session():
47-
botocore_session = Session()
48-
49-
test = AllowRegionsClientSupplier(allowed_regions=["us-west-2"], botocore_session=botocore_session)
50-
51-
assert test._supplier._botocore_session is botocore_session
52-
53-
5437
@pytest.mark.parametrize(
5538
"kwargs",
5639
(
@@ -78,14 +61,6 @@ def test_allow_regions_supplier_denied_not_allowed_region():
7861
excinfo.match("Unable to provide client for region 'ap-northeast-2'")
7962

8063

81-
def test_deny_regions_supplier_passes_session():
82-
botocore_session = Session()
83-
84-
test = DenyRegionsClientSupplier(denied_regions=["us-west-2"], botocore_session=botocore_session)
85-
86-
assert test._supplier._botocore_session is botocore_session
87-
88-
8964
@pytest.mark.parametrize(
9065
"kwargs",
9166
(
@@ -111,3 +86,19 @@ def test_deny_regions_supplier_allows_not_denied_region():
11186
test = DenyRegionsClientSupplier(denied_regions=["us-west-2", "us-east-2"])
11287

11388
assert test("ap-northeast-2")
89+
90+
91+
def test_allow_deny_nested_supplier():
92+
test_allow = AllowRegionsClientSupplier(
93+
allowed_regions=["us-west-2", "us-east-2"], client_supplier=DefaultClientSupplier()
94+
)
95+
test_deny = DenyRegionsClientSupplier(denied_regions=["us-west-2"], client_supplier=test_allow)
96+
97+
# test_allow allows us-west-2
98+
test_allow("us-west-2")
99+
100+
# test_deny denies us-west-2 even though its internal supplier (test_allow) allows it
101+
with pytest.raises(UnknownRegionError) as excinfo:
102+
test_deny("us-west-2")
103+
104+
excinfo.match("Unable to provide client for region 'us-west-2'")

0 commit comments

Comments
 (0)