|
13 | 13 | from typing_extensions import Self |
14 | 14 |
|
15 | 15 | from amazon_creatorsapi.core.constants import DEFAULT_THROTTLING |
16 | | -from amazon_creatorsapi.core.marketplaces import MARKETPLACES |
| 16 | +from amazon_creatorsapi.core.error_handling import handle_api_error |
17 | 17 | from amazon_creatorsapi.core.parsers import get_asin, get_items_ids |
18 | | -from amazon_creatorsapi.errors import ( |
19 | | - AssociateValidationError, |
20 | | - InvalidArgumentError, |
21 | | - ItemsNotFoundError, |
22 | | - RequestError, |
23 | | - TooManyRequestsError, |
24 | | -) |
| 18 | +from amazon_creatorsapi.core.resources import get_all_resources |
| 19 | +from amazon_creatorsapi.core.validation import validate_and_get_marketplace |
| 20 | +from amazon_creatorsapi.errors import ItemsNotFoundError |
25 | 21 |
|
26 | 22 | try: |
27 | 23 | from .auth import VERSION_ENDPOINTS, AsyncOAuth2TokenManager |
@@ -132,21 +128,12 @@ def __init__( |
132 | 128 | self._credential_secret = credential_secret |
133 | 129 | self._version = version |
134 | 130 | self._last_query_time = time.time() - throttling |
135 | | - self._throttle_lock = asyncio.Lock() |
| 131 | + self._throttle_lock: asyncio.Lock | None = None |
136 | 132 | self.tag = tag |
137 | 133 | self.throttling = float(throttling) |
138 | 134 |
|
139 | 135 | # Determine marketplace from country or direct value |
140 | | - if marketplace: |
141 | | - self.marketplace = marketplace |
142 | | - elif country: |
143 | | - if country not in MARKETPLACES: |
144 | | - msg = f"Country code '{country}' is not valid" |
145 | | - raise InvalidArgumentError(msg) |
146 | | - self.marketplace = MARKETPLACES[country] |
147 | | - else: |
148 | | - msg = "Either 'country' or 'marketplace' must be provided" |
149 | | - raise InvalidArgumentError(msg) |
| 136 | + self.marketplace = validate_and_get_marketplace(country, marketplace) |
150 | 137 |
|
151 | 138 | # HTTP client and token manager (initialized lazily or via context manager) |
152 | 139 | self._http_client: AsyncHttpClient | None = None |
@@ -218,7 +205,7 @@ async def get_items( |
218 | 205 |
|
219 | 206 | """ |
220 | 207 | if resources is None: |
221 | | - resources = self._get_all_resources(GetItemsResource) |
| 208 | + resources = get_all_resources(GetItemsResource) |
222 | 209 |
|
223 | 210 | item_ids = get_items_ids(items) |
224 | 211 |
|
@@ -299,7 +286,7 @@ async def search_items( # noqa: PLR0912, C901 |
299 | 286 |
|
300 | 287 | """ |
301 | 288 | if resources is None: |
302 | | - resources = self._get_all_resources(SearchItemsResource) |
| 289 | + resources = get_all_resources(SearchItemsResource) |
303 | 290 |
|
304 | 291 | request_body: dict[str, Any] = { |
305 | 292 | "partnerTag": self.tag, |
@@ -382,7 +369,7 @@ async def get_variations( |
382 | 369 |
|
383 | 370 | """ |
384 | 371 | if resources is None: |
385 | | - resources = self._get_all_resources(GetVariationsResource) |
| 372 | + resources = get_all_resources(GetVariationsResource) |
386 | 373 |
|
387 | 374 | asin = get_asin(asin) |
388 | 375 |
|
@@ -433,7 +420,7 @@ async def get_browse_nodes( |
433 | 420 |
|
434 | 421 | """ |
435 | 422 | if resources is None: |
436 | | - resources = self._get_all_resources(GetBrowseNodesResource) |
| 423 | + resources = get_all_resources(GetBrowseNodesResource) |
437 | 424 |
|
438 | 425 | request_body: dict[str, Any] = { |
439 | 426 | "partnerTag": self.tag, |
@@ -462,6 +449,10 @@ async def _throttle(self) -> None: |
462 | 449 | Uses asyncio.Lock to prevent race conditions when multiple coroutines |
463 | 450 | attempt to make concurrent requests. |
464 | 451 | """ |
| 452 | + # Lazy initialization of the lock (ensures event loop is active) |
| 453 | + if self._throttle_lock is None: |
| 454 | + self._throttle_lock = asyncio.Lock() |
| 455 | + |
465 | 456 | async with self._throttle_lock: |
466 | 457 | wait_time = self.throttling - (time.time() - self._last_query_time) |
467 | 458 | if wait_time > 0: |
@@ -525,45 +516,7 @@ def _handle_error_response(self, status_code: int, body: str) -> None: |
525 | 516 | RequestError: For other errors. |
526 | 517 |
|
527 | 518 | """ |
528 | | - http_not_found = 404 |
529 | | - http_too_many_requests = 429 |
530 | | - |
531 | | - if status_code == http_not_found: |
532 | | - msg = "No items found for the request" |
533 | | - raise ItemsNotFoundError(msg) |
534 | | - |
535 | | - if status_code == http_too_many_requests: |
536 | | - msg = "Rate limit exceeded, try increasing throttling" |
537 | | - raise TooManyRequestsError(msg) |
538 | | - |
539 | | - if "InvalidParameterValue" in body: |
540 | | - msg = "Invalid parameter value provided in the request" |
541 | | - raise InvalidArgumentError(msg) |
542 | | - |
543 | | - if "InvalidPartnerTag" in body: |
544 | | - msg = "The partner tag is invalid or not present" |
545 | | - raise InvalidArgumentError(msg) |
546 | | - |
547 | | - if "InvalidAssociate" in body: |
548 | | - msg = "Credentials are not valid for the selected marketplace" |
549 | | - raise AssociateValidationError(msg) |
550 | | - |
551 | | - # Generic error |
552 | | - body_info = f" - {body[:200]}" if body else "" |
553 | | - msg = f"Request failed with status {status_code}{body_info}" |
554 | | - raise RequestError(msg) |
555 | | - |
556 | | - def _get_all_resources(self, resource_class: type[ResourceT]) -> list[ResourceT]: |
557 | | - """Extract all resource values from a resource enum class. |
558 | | -
|
559 | | - Args: |
560 | | - resource_class: Enum class containing resource definitions. |
561 | | -
|
562 | | - Returns: |
563 | | - List of all enum members from the resource class. |
564 | | -
|
565 | | - """ |
566 | | - return list(resource_class) |
| 519 | + handle_api_error(status_code, body) |
567 | 520 |
|
568 | 521 | def _deserialize_items(self, items_data: list[dict[str, Any]]) -> list[Item]: |
569 | 522 | """Deserialize item data from API response to Item models.""" |
|
0 commit comments