Skip to content

Commit ad4b590

Browse files
committed
refactor: move async API and its related import logic to a dedicated subpackage.
1 parent 933f6bb commit ad4b590

10 files changed

Lines changed: 84 additions & 246 deletions

File tree

amazon_creatorsapi/__init__.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,9 @@
33
A Python wrapper for the Amazon Creators API.
44
"""
55

6-
from importlib.util import find_spec
7-
86
__author__ = "Sergio Abad"
97
__all__ = ["AmazonCreatorsApi", "Country", "models"]
108

119
from . import models
1210
from .api import AmazonCreatorsApi
1311
from .core import Country
14-
15-
# Async support (requires 'async' extra: pip install python-amazon-paapi[async])
16-
if find_spec("httpx") is not None:
17-
from .async_api import AsyncAmazonCreatorsApi
18-
19-
__all__ += ["AsyncAmazonCreatorsApi"]

amazon_creatorsapi/aio/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Async support for Amazon Creators API."""
2+
3+
try:
4+
import httpx # noqa: F401
5+
except ImportError as exc:
6+
msg = (
7+
"httpx is required for async support. "
8+
"Install with: pip install python-amazon-paapi[async]"
9+
)
10+
raise ImportError(msg) from exc
11+
12+
from amazon_creatorsapi.aio.api import AsyncAmazonCreatorsApi
13+
14+
__all__ = ["AsyncAmazonCreatorsApi"]
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
)
2424

2525
try:
26-
from amazon_creatorsapi.core.async_auth import AsyncOAuth2TokenManager
27-
from amazon_creatorsapi.core.async_client import AsyncHttpClient
26+
from .auth import AsyncOAuth2TokenManager
27+
from .client import AsyncHttpClient
2828
except ImportError as exc: # pragma: no cover
2929
msg = (
3030
"httpx is required for async support. "
@@ -103,7 +103,7 @@ class AsyncAmazonCreatorsApi:
103103
104104
"""
105105

106-
def __init__( # noqa: PLR0913
106+
def __init__(
107107
self,
108108
credential_id: str,
109109
credential_secret: str,
@@ -213,7 +213,7 @@ async def get_items(
213213

214214
return self._deserialize_items(items_result["items"])
215215

216-
async def search_items( # noqa: PLR0912, PLR0913, C901
216+
async def search_items( # noqa: PLR0912, C901
217217
self,
218218
keywords: str | None = None,
219219
actor: str | None = None,
@@ -323,7 +323,7 @@ async def search_items( # noqa: PLR0912, PLR0913, C901
323323

324324
return self._deserialize_search_result(search_result)
325325

326-
async def get_variations( # noqa: PLR0913
326+
async def get_variations(
327327
self,
328328
asin: str,
329329
variation_count: int | None = None,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Empty init file for async test module

tests/amazon_creatorsapi/async_api_test.py renamed to tests/amazon_creatorsapi/aio/api_test.py

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import unittest
44
from unittest.mock import AsyncMock, MagicMock, patch
55

6-
from amazon_creatorsapi.async_api import (
6+
from amazon_creatorsapi.aio import (
77
AsyncAmazonCreatorsApi,
88
)
99
from amazon_creatorsapi.errors import (
@@ -20,7 +20,7 @@
2020
class TestAsyncAmazonCreatorsApiInit(unittest.TestCase):
2121
"""Tests for AsyncAmazonCreatorsApi initialization."""
2222

23-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
23+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
2424
def test_with_country_code(self, mock_token_manager: MagicMock) -> None:
2525
"""Test initialization with country code."""
2626
api = AsyncAmazonCreatorsApi(
@@ -35,7 +35,7 @@ def test_with_country_code(self, mock_token_manager: MagicMock) -> None:
3535
self.assertEqual(api.marketplace, "www.amazon.es")
3636
self.assertEqual(api.throttling, 1.0)
3737

38-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
38+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
3939
def test_with_marketplace(self, mock_token_manager: MagicMock) -> None:
4040
"""Test initialization with explicit marketplace."""
4141
api = AsyncAmazonCreatorsApi(
@@ -48,7 +48,7 @@ def test_with_marketplace(self, mock_token_manager: MagicMock) -> None:
4848

4949
self.assertEqual(api.marketplace, "www.amazon.co.uk")
5050

51-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
51+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
5252
def test_with_custom_throttling(self, mock_token_manager: MagicMock) -> None:
5353
"""Test initialization with custom throttling value."""
5454
api = AsyncAmazonCreatorsApi(
@@ -62,7 +62,7 @@ def test_with_custom_throttling(self, mock_token_manager: MagicMock) -> None:
6262

6363
self.assertEqual(api.throttling, 2.5)
6464

65-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
65+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
6666
def test_raises_error_when_no_country_or_marketplace(
6767
self, mock_token_manager: MagicMock
6868
) -> None:
@@ -77,7 +77,7 @@ def test_raises_error_when_no_country_or_marketplace(
7777

7878
self.assertIn("Either 'country' or 'marketplace'", str(context.exception))
7979

80-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
80+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
8181
def test_raises_error_for_invalid_country(
8282
self, mock_token_manager: MagicMock
8383
) -> None:
@@ -97,8 +97,8 @@ def test_raises_error_for_invalid_country(
9797
class TestAsyncAmazonCreatorsApiContextManager(unittest.IsolatedAsyncioTestCase):
9898
"""Tests for AsyncAmazonCreatorsApi async context manager."""
9999

100-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
101-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
100+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
101+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
102102
async def test_context_manager_creates_and_closes_client(
103103
self,
104104
mock_http_client_class: MagicMock,
@@ -124,8 +124,8 @@ async def test_context_manager_creates_and_closes_client(
124124
class TestAsyncAmazonCreatorsApiGetItems(unittest.IsolatedAsyncioTestCase):
125125
"""Tests for get_items() method."""
126126

127-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
128-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
127+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
128+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
129129
async def test_get_items_success(
130130
self,
131131
mock_http_client_class: MagicMock,
@@ -166,8 +166,8 @@ async def test_get_items_success(
166166

167167
self.assertEqual(len(items), 1)
168168

169-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
170-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
169+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
170+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
171171
async def test_get_items_not_found(
172172
self,
173173
mock_http_client_class: MagicMock,
@@ -198,8 +198,8 @@ async def test_get_items_not_found(
198198
with self.assertRaises(ItemsNotFoundError):
199199
await api.get_items(["B0DLFMFBJX"])
200200

201-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
202-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
201+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
202+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
203203
async def test_get_items_with_optional_params(
204204
self,
205205
mock_http_client_class: MagicMock,
@@ -242,8 +242,8 @@ async def test_get_items_with_optional_params(
242242
class TestAsyncAmazonCreatorsApiSearchItems(unittest.IsolatedAsyncioTestCase):
243243
"""Tests for search_items() method."""
244244

245-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
246-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
245+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
246+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
247247
async def test_search_items_success(
248248
self,
249249
mock_http_client_class: MagicMock,
@@ -284,8 +284,8 @@ async def test_search_items_success(
284284
class TestAsyncAmazonCreatorsApiErrorHandling(unittest.IsolatedAsyncioTestCase):
285285
"""Tests for error handling."""
286286

287-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
288-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
287+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
288+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
289289
async def test_handles_404_error(
290290
self,
291291
mock_http_client_class: MagicMock,
@@ -316,8 +316,8 @@ async def test_handles_404_error(
316316
with self.assertRaises(ItemsNotFoundError):
317317
await api.get_items(["B0DLFMFBJW"])
318318

319-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
320-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
319+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
320+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
321321
async def test_handles_429_error(
322322
self,
323323
mock_http_client_class: MagicMock,
@@ -348,8 +348,8 @@ async def test_handles_429_error(
348348
with self.assertRaises(TooManyRequestsError):
349349
await api.get_items(["B0DLFMFBJW"])
350350

351-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
352-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
351+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
352+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
353353
async def test_handles_invalid_associate_error(
354354
self,
355355
mock_http_client_class: MagicMock,
@@ -384,9 +384,9 @@ async def test_handles_invalid_associate_error(
384384
class TestAsyncAmazonCreatorsApiThrottling(unittest.IsolatedAsyncioTestCase):
385385
"""Tests for throttling mechanism."""
386386

387-
@patch("amazon_creatorsapi.async_api.asyncio.sleep")
388-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
389-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
387+
@patch("amazon_creatorsapi.aio.api.asyncio.sleep")
388+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
389+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
390390
async def test_throttling_waits_between_requests(
391391
self,
392392
mock_http_client_class: MagicMock,
@@ -429,8 +429,8 @@ async def test_throttling_waits_between_requests(
429429
class TestAsyncAmazonCreatorsApiGetVariations(unittest.IsolatedAsyncioTestCase):
430430
"""Tests for get_variations() method."""
431431

432-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
433-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
432+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
433+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
434434
async def test_get_variations_success(
435435
self,
436436
mock_http_client_class: MagicMock,
@@ -467,8 +467,8 @@ async def test_get_variations_success(
467467

468468
self.assertIsNotNone(result)
469469

470-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
471-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
470+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
471+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
472472
async def test_get_variations_with_params(
473473
self,
474474
mock_http_client_class: MagicMock,
@@ -512,8 +512,8 @@ async def test_get_variations_with_params(
512512

513513
self.assertIsNotNone(result)
514514

515-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
516-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
515+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
516+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
517517
async def test_get_variations_not_found(
518518
self,
519519
mock_http_client_class: MagicMock,
@@ -548,8 +548,8 @@ async def test_get_variations_not_found(
548548
class TestAsyncAmazonCreatorsApiGetBrowseNodes(unittest.IsolatedAsyncioTestCase):
549549
"""Tests for get_browse_nodes() method."""
550550

551-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
552-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
551+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
552+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
553553
async def test_get_browse_nodes_success(
554554
self,
555555
mock_http_client_class: MagicMock,
@@ -585,8 +585,8 @@ async def test_get_browse_nodes_success(
585585

586586
self.assertEqual(len(result), 1)
587587

588-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
589-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
588+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
589+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
590590
async def test_get_browse_nodes_with_languages(
591591
self,
592592
mock_http_client_class: MagicMock,
@@ -625,8 +625,8 @@ async def test_get_browse_nodes_with_languages(
625625

626626
self.assertEqual(len(result), 1)
627627

628-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
629-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
628+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
629+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
630630
async def test_get_browse_nodes_not_found(
631631
self,
632632
mock_http_client_class: MagicMock,
@@ -657,8 +657,8 @@ async def test_get_browse_nodes_not_found(
657657
with self.assertRaises(ItemsNotFoundError):
658658
await api.get_browse_nodes(["999999"])
659659

660-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
661-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
660+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
661+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
662662
async def test_get_browse_nodes_empty_nodes_list(
663663
self,
664664
mock_http_client_class: MagicMock,
@@ -693,8 +693,8 @@ async def test_get_browse_nodes_empty_nodes_list(
693693
class TestAsyncAmazonCreatorsApiErrorHandlingExtended(unittest.IsolatedAsyncioTestCase):
694694
"""Extended error handling tests."""
695695

696-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
697-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
696+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
697+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
698698
async def test_handles_invalid_parameter_value_error(
699699
self,
700700
mock_http_client_class: MagicMock,
@@ -725,8 +725,8 @@ async def test_handles_invalid_parameter_value_error(
725725
with self.assertRaises(InvalidArgumentError):
726726
await api.get_items(["B0DLFMFBJW"])
727727

728-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
729-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
728+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
729+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
730730
async def test_handles_invalid_partner_tag_error(
731731
self,
732732
mock_http_client_class: MagicMock,
@@ -757,8 +757,8 @@ async def test_handles_invalid_partner_tag_error(
757757
with self.assertRaises(InvalidArgumentError):
758758
await api.get_items(["B0DLFMFBJW"])
759759

760-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
761-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
760+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
761+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
762762
async def test_handles_generic_error(
763763
self,
764764
mock_http_client_class: MagicMock,
@@ -789,8 +789,8 @@ async def test_handles_generic_error(
789789
with self.assertRaises(RequestError):
790790
await api.get_items(["B0DLFMFBJW"])
791791

792-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
793-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
792+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
793+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
794794
async def test_handles_generic_error_with_empty_body(
795795
self,
796796
mock_http_client_class: MagicMock,
@@ -825,8 +825,8 @@ async def test_handles_generic_error_with_empty_body(
825825
class TestAsyncAmazonCreatorsApiSearchItemsExtended(unittest.IsolatedAsyncioTestCase):
826826
"""Extended tests for search_items() method."""
827827

828-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
829-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
828+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
829+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
830830
async def test_search_items_with_all_params(
831831
self,
832832
mock_http_client_class: MagicMock,
@@ -881,8 +881,8 @@ async def test_search_items_with_all_params(
881881

882882
self.assertIsNotNone(result)
883883

884-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
885-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
884+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
885+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
886886
async def test_search_items_not_found(
887887
self,
888888
mock_http_client_class: MagicMock,
@@ -913,8 +913,8 @@ async def test_search_items_not_found(
913913
with self.assertRaises(ItemsNotFoundError):
914914
await api.search_items(keywords="xyznonexistent123")
915915

916-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
917-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
916+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
917+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
918918
async def test_search_items_with_search_index(
919919
self,
920920
mock_http_client_class: MagicMock,
@@ -958,8 +958,8 @@ async def test_search_items_with_search_index(
958958
class TestAsyncAmazonCreatorsApiWithoutContextManager(unittest.IsolatedAsyncioTestCase):
959959
"""Tests for usage without context manager."""
960960

961-
@patch("amazon_creatorsapi.async_api.AsyncOAuth2TokenManager")
962-
@patch("amazon_creatorsapi.async_api.AsyncHttpClient")
961+
@patch("amazon_creatorsapi.aio.api.AsyncOAuth2TokenManager")
962+
@patch("amazon_creatorsapi.aio.api.AsyncHttpClient")
963963
async def test_request_without_context_manager(
964964
self,
965965
mock_http_client_class: MagicMock,

0 commit comments

Comments
 (0)