python-amazon-sp-api 1.7.5__py3-none-any.whl → 2.0.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_amazon_sp_api-1.7.5.data → python_amazon_sp_api-2.0.10.data}/scripts/make_endpoint +2 -2
- {python_amazon_sp_api-1.7.5.dist-info → python_amazon_sp_api-2.0.10.dist-info}/METADATA +58 -8
- python_amazon_sp_api-2.0.10.dist-info/RECORD +253 -0
- {python_amazon_sp_api-1.7.5.dist-info → python_amazon_sp_api-2.0.10.dist-info}/WHEEL +1 -1
- {python_amazon_sp_api-1.7.5.dist-info → python_amazon_sp_api-2.0.10.dist-info}/top_level.txt +0 -1
- sp_api/__version__.py +1 -1
- sp_api/api/__init__.py +48 -41
- sp_api/api/amazon_warehousing_and_distribu/amazon_warehousing_and_distribu.py +82 -76
- sp_api/api/aplus_content/aplus_content.py +76 -45
- sp_api/api/application_integrations/application_integrations.py +118 -0
- sp_api/api/application_management/application_management.py +6 -7
- sp_api/api/authorization/authorization.py +5 -5
- sp_api/api/catalog/catalog.py +9 -10
- sp_api/api/catalog_items/catalog_items.py +10 -13
- sp_api/api/customer_feedback/customer_feedback.py +110 -0
- sp_api/api/data_kiosk/data_kiosk.py +59 -39
- sp_api/api/easy_ship/easy_ship.py +190 -0
- sp_api/api/external_fulfillment/external_fulfillment.py +706 -0
- sp_api/api/fba_inbound_eligibility/fba_inbound_eligibility.py +23 -13
- sp_api/api/fba_small_and_light/fba_small_and_light.py +46 -20
- sp_api/api/feeds/feeds.py +60 -39
- sp_api/api/finances/finances.py +40 -9
- sp_api/api/fulfillment_inbound/fulfillment_inbound.py +844 -619
- sp_api/api/fulfillment_outbound/fulfillment_outbound.py +63 -57
- sp_api/api/inventories/inventories.py +13 -11
- sp_api/api/listings_items/listings_items.py +38 -25
- sp_api/api/listings_restrictions/listings_restrictions.py +6 -7
- sp_api/api/merchant_fulfillment/merchant_fulfillment.py +49 -36
- sp_api/api/messaging/messaging.py +129 -25
- sp_api/api/notifications/notifications.py +85 -45
- sp_api/api/orders/orders.py +123 -38
- sp_api/api/orders/orders_2026_01_01.py +54 -0
- sp_api/api/product_fees/product_fees.py +75 -67
- sp_api/api/product_type_definitions/product_type_definitions.py +9 -10
- sp_api/api/products/products.py +177 -53
- sp_api/api/products/products_definitions.py +11 -82
- sp_api/api/replenishment/replenishment.py +13 -11
- sp_api/api/reports/reports.py +113 -95
- sp_api/api/sales/sales.py +23 -13
- sp_api/api/sellers/sellers.py +3 -3
- sp_api/api/services/services.py +41 -30
- sp_api/api/shipping/shipping.py +39 -37
- sp_api/api/shipping/shippingV2.py +46 -30
- sp_api/api/solicitations/solicitations.py +20 -11
- sp_api/api/supply_sources/supply_sources.py +45 -37
- sp_api/api/tokens/tokens.py +4 -6
- sp_api/api/upload/upload.py +10 -8
- sp_api/api/vendor_direct_fulfillment_inventory/vendor_direct_fulfillment_inventory.py +10 -6
- sp_api/api/vendor_direct_fulfillment_orders/vendor_direct_fulfillment_orders.py +12 -14
- sp_api/api/vendor_direct_fulfillment_payments/vendor_direct_fulfillment_payments.py +4 -6
- sp_api/api/vendor_direct_fulfillment_shipping/vendor_direct_fulfillment_shipping.py +42 -37
- sp_api/api/vendor_direct_fulfillment_transactions/vendor_direct_fulfillment_transactions.py +8 -6
- sp_api/api/vendor_invoices/vendor_invoices.py +6 -4
- sp_api/api/vendor_orders/vendor_orders.py +16 -19
- sp_api/api/vendor_shipments/vendor_shipments.py +91 -262
- sp_api/api/vendor_transaction_status/vendor_transaction_status.py +6 -6
- sp_api/asyncio/api/__init__.py +167 -0
- sp_api/asyncio/api/amazon_warehousing_and_distribu/__init__.py +9 -0
- sp_api/asyncio/api/amazon_warehousing_and_distribu/amazon_warehousing_and_distribu.py +130 -0
- sp_api/asyncio/api/aplus_content/__init__.py +5 -0
- sp_api/asyncio/api/aplus_content/aplus_content.py +330 -0
- sp_api/asyncio/api/application_integrations/__init__.py +5 -0
- sp_api/asyncio/api/application_integrations/application_integrations.py +119 -0
- sp_api/asyncio/api/application_management/__init__.py +5 -0
- sp_api/asyncio/api/application_management/application_management.py +36 -0
- sp_api/asyncio/api/authorization/__init__.py +5 -0
- sp_api/asyncio/api/authorization/authorization.py +54 -0
- sp_api/asyncio/api/catalog/__init__.py +5 -0
- sp_api/asyncio/api/catalog/catalog.py +111 -0
- sp_api/asyncio/api/catalog_items/__init__.py +6 -0
- sp_api/asyncio/api/catalog_items/catalog_items.py +93 -0
- sp_api/asyncio/api/clients/__init__.py +1 -0
- sp_api/asyncio/api/customer_feedback/__init__.py +5 -0
- sp_api/asyncio/api/customer_feedback/customer_feedback.py +111 -0
- sp_api/asyncio/api/data_kiosk/__init__.py +5 -0
- sp_api/asyncio/api/data_kiosk/data_kiosk.py +236 -0
- sp_api/asyncio/api/easy_ship/__init__.py +5 -0
- sp_api/asyncio/api/easy_ship/easy_ship.py +191 -0
- sp_api/asyncio/api/external_fulfillment/__init__.py +5 -0
- sp_api/asyncio/api/external_fulfillment/external_fulfillment.py +706 -0
- sp_api/asyncio/api/fba_inbound_eligibility/__init__.py +5 -0
- sp_api/asyncio/api/fba_inbound_eligibility/fba_inbound_eligibility.py +96 -0
- sp_api/asyncio/api/fba_small_and_light/__init__.py +5 -0
- sp_api/asyncio/api/fba_small_and_light/fba_small_and_light.py +213 -0
- sp_api/asyncio/api/feeds/feeds.py +260 -0
- sp_api/asyncio/api/finances/finances.py +100 -0
- sp_api/asyncio/api/fulfillment_inbound/fulfillment_inbound.py +1798 -0
- sp_api/asyncio/api/fulfillment_outbound/fulfillment_outbound.py +736 -0
- sp_api/asyncio/api/inventories/inventories.py +74 -0
- sp_api/asyncio/api/listings_items/__init__.py +0 -0
- sp_api/asyncio/api/listings_items/listings_items.py +170 -0
- sp_api/asyncio/api/listings_restrictions/__init__.py +0 -0
- sp_api/asyncio/api/listings_restrictions/listings_restrictions.py +36 -0
- sp_api/asyncio/api/merchant_fulfillment/__init__.py +0 -0
- sp_api/asyncio/api/merchant_fulfillment/merchant_fulfillment.py +384 -0
- sp_api/asyncio/api/messaging/__init__.py +0 -0
- sp_api/asyncio/api/messaging/messaging.py +511 -0
- sp_api/asyncio/api/models/__init__.py +4 -0
- sp_api/asyncio/api/notifications/__init__.py +0 -0
- sp_api/asyncio/api/notifications/notifications.py +295 -0
- sp_api/asyncio/api/orders/__init__.py +0 -0
- sp_api/asyncio/api/orders/orders.py +412 -0
- sp_api/asyncio/api/orders/orders_2026_01_01.py +40 -0
- sp_api/asyncio/api/overrides/__init__.py +1 -0
- sp_api/asyncio/api/product_fees/__init__.py +0 -0
- sp_api/asyncio/api/product_fees/product_fees.py +194 -0
- sp_api/asyncio/api/product_type_definitions/__init__.py +0 -0
- sp_api/asyncio/api/product_type_definitions/product_type_definitions.py +75 -0
- sp_api/asyncio/api/products/__init__.py +0 -0
- sp_api/asyncio/api/products/products.py +405 -0
- sp_api/asyncio/api/products/products_definitions.py +11 -0
- sp_api/asyncio/api/replenishment/__init__.py +0 -0
- sp_api/asyncio/api/replenishment/replenishment.py +121 -0
- sp_api/asyncio/api/reports/__init__.py +0 -0
- sp_api/asyncio/api/reports/reports.py +439 -0
- sp_api/asyncio/api/sales/__init__.py +0 -0
- sp_api/asyncio/api/sales/sales.py +93 -0
- sp_api/asyncio/api/sellers/__init__.py +0 -0
- sp_api/asyncio/api/sellers/sellers.py +70 -0
- sp_api/asyncio/api/services/__init__.py +0 -0
- sp_api/asyncio/api/services/services.py +218 -0
- sp_api/asyncio/api/shipping/__init__.py +0 -0
- sp_api/asyncio/api/shipping/shipping.py +459 -0
- sp_api/asyncio/api/shipping/shippingV2.py +651 -0
- sp_api/asyncio/api/solicitations/__init__.py +0 -0
- sp_api/asyncio/api/solicitations/solicitations.py +78 -0
- sp_api/asyncio/api/supply_sources/__init__.py +0 -0
- sp_api/asyncio/api/supply_sources/supply_sources.py +138 -0
- sp_api/asyncio/api/tokens/__init__.py +0 -0
- sp_api/asyncio/api/tokens/tokens.py +65 -0
- sp_api/asyncio/api/upload/__init__.py +0 -0
- sp_api/asyncio/api/upload/upload.py +18 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_inventory/__init__.py +0 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_inventory/vendor_direct_fulfillment_inventory.py +64 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_orders/__init__.py +0 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_orders/vendor_direct_fulfillment_orders.py +196 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_payments/__init__.py +0 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_payments/vendor_direct_fulfillment_payments.py +254 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_shipping/__init__.py +0 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_shipping/vendor_direct_fulfillment_shipping.py +627 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_transactions/__init__.py +0 -0
- sp_api/asyncio/api/vendor_direct_fulfillment_transactions/vendor_direct_fulfillment_transactions.py +43 -0
- sp_api/asyncio/api/vendor_invoices/__init__.py +0 -0
- sp_api/asyncio/api/vendor_invoices/vendor_invoices.py +295 -0
- sp_api/asyncio/api/vendor_orders/__init__.py +0 -0
- sp_api/asyncio/api/vendor_orders/vendor_orders.py +210 -0
- sp_api/asyncio/api/vendor_shipments/__init__.py +0 -0
- sp_api/asyncio/api/vendor_shipments/vendor_shipments.py +118 -0
- sp_api/asyncio/api/vendor_transaction_status/__init__.py +0 -0
- sp_api/asyncio/api/vendor_transaction_status/vendor_transaction_status.py +41 -0
- sp_api/asyncio/auth/__init__.py +12 -0
- sp_api/asyncio/auth/access_token_client.py +145 -0
- sp_api/asyncio/auth/exceptions.py +5 -0
- sp_api/asyncio/base/__init__.py +53 -0
- sp_api/asyncio/base/_transport_httpx.py +50 -0
- sp_api/asyncio/base/base_client.py +8 -0
- sp_api/asyncio/base/client.py +169 -0
- sp_api/asyncio/util/__init__.py +29 -0
- sp_api/asyncio/util/key_maker.py +5 -0
- sp_api/asyncio/util/load_all_pages.py +55 -0
- sp_api/asyncio/util/load_date_bound.py +53 -0
- sp_api/asyncio/util/retry.py +88 -0
- sp_api/auth/__init__.py +3 -3
- sp_api/auth/_core.py +39 -0
- sp_api/auth/access_token_client.py +20 -31
- sp_api/auth/access_token_response.py +4 -4
- sp_api/base/ApiResponse.py +5 -4
- sp_api/base/__init__.py +53 -42
- sp_api/base/_core.py +110 -0
- sp_api/base/_transport_httpx.py +39 -0
- sp_api/base/base_client.py +4 -4
- sp_api/base/client.py +131 -112
- sp_api/base/credential_provider.py +41 -34
- sp_api/base/exceptions.py +14 -3
- sp_api/base/feedTypes.py +44 -32
- sp_api/base/fulfillment_channel.py +2 -2
- sp_api/base/helpers.py +17 -16
- sp_api/base/identifiersType.py +8 -8
- sp_api/base/included_data.py +12 -12
- sp_api/base/marketplaces.py +5 -1
- sp_api/base/notifications.py +1 -1
- sp_api/base/processing_status.py +5 -5
- sp_api/base/reportTypes.py +198 -111
- sp_api/base/sales_enum.py +11 -13
- sp_api/util/__init__.py +42 -6
- sp_api/util/key_maker.py +4 -2
- sp_api/util/load_all_pages.py +16 -5
- sp_api/util/load_date_bound.py +28 -13
- sp_api/util/params.py +57 -0
- sp_api/util/product_fees.py +40 -0
- sp_api/util/products_definitions.py +169 -0
- sp_api/util/report_document.py +154 -0
- sp_api/util/retry.py +16 -15
- python_amazon_sp_api-1.7.5.dist-info/RECORD +0 -144
- tests/api/finances/test_finances.py +0 -19
- tests/api/notifications/test_notifications.py +0 -26
- tests/api/orders/test_orders.py +0 -122
- tests/api/product_fees/product_fees.py +0 -49
- tests/api/reports/test_reports.py +0 -127
- tests/client/test_auth.py +0 -59
- tests/client/test_base.py +0 -163
- tests/client/test_credential_provider.py +0 -45
- tests/client/test_helpers.py +0 -142
- {python_amazon_sp_api-1.7.5.dist-info → python_amazon_sp_api-2.0.10.dist-info/licenses}/LICENSE +0 -0
- {tests → sp_api/api/application_integrations}/__init__.py +0 -0
- {tests/api → sp_api/api/customer_feedback}/__init__.py +0 -0
- {tests/api/finances → sp_api/api/easy_ship}/__init__.py +0 -0
- {tests/api/notifications → sp_api/api/external_fulfillment}/__init__.py +0 -0
- {tests/api/orders → sp_api/asyncio}/__init__.py +0 -0
- {tests/api/product_fees → sp_api/asyncio/api/feeds}/__init__.py +0 -0
- {tests/api/reports → sp_api/asyncio/api/finances}/__init__.py +0 -0
- {tests/api/sellers → sp_api/asyncio/api/fulfillment_inbound}/__init__.py +0 -0
- {tests/client → sp_api/asyncio/api/fulfillment_outbound}/__init__.py +0 -0
- /tests/api/sellers/test_sellers.py → /sp_api/asyncio/api/inventories/__init__.py +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def retry(exception_classes=None, tries=10, delay=5, rate=1.3):
|
|
5
|
+
"""
|
|
6
|
+
retry(exception_classes=None, tries=10, delay=5, rate=1.3)
|
|
7
|
+
|
|
8
|
+
Retry a call against an endpoint <tries> time
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
exception_classes: tuple | The Exceptions to be caught
|
|
12
|
+
tries: int | How often the call should be retried
|
|
13
|
+
delay: float | The delay after an error was caught
|
|
14
|
+
rate: float | The rate to increment delay by
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
if exception_classes is None:
|
|
20
|
+
exception_classes = (Exception,)
|
|
21
|
+
|
|
22
|
+
tries_counter = {"count": 1, "last_delay": delay}
|
|
23
|
+
|
|
24
|
+
def decorator(function):
|
|
25
|
+
async def wrapper(*args, **kwargs):
|
|
26
|
+
try:
|
|
27
|
+
return await function(*args, **kwargs)
|
|
28
|
+
except exception_classes as e:
|
|
29
|
+
if tries_counter.get("count") + 1 > tries:
|
|
30
|
+
raise e
|
|
31
|
+
|
|
32
|
+
delay_now = (
|
|
33
|
+
delay
|
|
34
|
+
if tries_counter.get("count") == 1
|
|
35
|
+
else tries_counter.get("last_delay") * rate
|
|
36
|
+
)
|
|
37
|
+
tries_counter.update(
|
|
38
|
+
{"count": tries_counter.get("count") + 1, "last_delay": delay_now}
|
|
39
|
+
)
|
|
40
|
+
await asyncio.sleep(delay_now)
|
|
41
|
+
return await wrapper(*args, **kwargs)
|
|
42
|
+
finally:
|
|
43
|
+
tries_counter.update({"count": 1, "last_delay": delay})
|
|
44
|
+
|
|
45
|
+
wrapper.__doc__ = function.__doc__
|
|
46
|
+
return wrapper
|
|
47
|
+
|
|
48
|
+
return decorator
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def sp_retry(exception_classes=(), tries=10, delay=5, rate=1.3):
|
|
52
|
+
"""
|
|
53
|
+
This is a shorthand for retry that catches all exceptions thrown by this library
|
|
54
|
+
|
|
55
|
+
Retry a call against an endpoint <tries> time
|
|
56
|
+
Args:
|
|
57
|
+
exception_classes:
|
|
58
|
+
tries:
|
|
59
|
+
delay:
|
|
60
|
+
rate:
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
from sp_api.base import SellingApiException
|
|
66
|
+
|
|
67
|
+
return retry((SellingApiException,) + exception_classes, tries, delay, rate)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def throttle_retry(exception_classes=(), tries=10, delay=5, rate=1.3):
|
|
71
|
+
"""
|
|
72
|
+
This is a shorthand for retry that catches SellingApiRequestThrottledException
|
|
73
|
+
|
|
74
|
+
Retry a call against an endpoint <tries> time
|
|
75
|
+
Args:
|
|
76
|
+
exception_classes:
|
|
77
|
+
tries:
|
|
78
|
+
delay:
|
|
79
|
+
rate:
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
from sp_api.base import SellingApiRequestThrottledException
|
|
85
|
+
|
|
86
|
+
return retry(
|
|
87
|
+
(SellingApiRequestThrottledException,) + exception_classes, tries, delay, rate
|
|
88
|
+
)
|
sp_api/auth/__init__.py
CHANGED
sp_api/auth/_core.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def build_cache_key(refresh_token, token_flavor=""):
|
|
5
|
+
return "access_token_" + hashlib.md5(
|
|
6
|
+
(token_flavor + (refresh_token or "__grantless__")).encode("utf-8"),
|
|
7
|
+
usedforsecurity=False,
|
|
8
|
+
).hexdigest()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_refresh_token_data(credentials, grant_type):
|
|
12
|
+
return {
|
|
13
|
+
"grant_type": grant_type,
|
|
14
|
+
"client_id": credentials.client_id,
|
|
15
|
+
"refresh_token": credentials.refresh_token,
|
|
16
|
+
"client_secret": credentials.client_secret,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build_grantless_data(credentials, scope_value):
|
|
21
|
+
return {
|
|
22
|
+
"grant_type": "client_credentials",
|
|
23
|
+
"client_id": credentials.client_id,
|
|
24
|
+
"scope": scope_value,
|
|
25
|
+
"client_secret": credentials.client_secret,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def build_auth_code_request_body(credentials, auth_code):
|
|
30
|
+
return {
|
|
31
|
+
"grant_type": "authorization_code",
|
|
32
|
+
"code": auth_code,
|
|
33
|
+
"client_id": credentials.client_id,
|
|
34
|
+
"client_secret": credentials.client_secret,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_headers(user_agent, content_type):
|
|
39
|
+
return {"User-Agent": user_agent, "content-type": content_type}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
import requests
|
|
4
|
-
import hashlib
|
|
5
3
|
import logging
|
|
6
4
|
from cachetools import TTLCache
|
|
7
5
|
from sp_api.base import BaseClient
|
|
6
|
+
from sp_api.base._transport_httpx import HttpxTransport
|
|
8
7
|
|
|
9
8
|
from .credentials import Credentials
|
|
10
9
|
from .access_token_response import AccessTokenResponse
|
|
11
10
|
from .exceptions import AuthorizationError
|
|
11
|
+
from ._core import (
|
|
12
|
+
build_auth_code_request_body,
|
|
13
|
+
build_cache_key,
|
|
14
|
+
build_grantless_data,
|
|
15
|
+
build_headers,
|
|
16
|
+
build_refresh_token_data,
|
|
17
|
+
)
|
|
12
18
|
|
|
13
|
-
cache = TTLCache(maxsize=int(os.environ.get('SP_API_AUTH_CACHE_SIZE', 10)), ttl=3200)
|
|
14
|
-
grantless_cache = TTLCache(maxsize=int(os.environ.get('SP_API_AUTH_CACHE_SIZE', 10)), ttl=3200)
|
|
19
|
+
cache = TTLCache(maxsize=int(os.environ.get('SP_API_AUTH_CACHE_SIZE', 10)), ttl=int(os.environ.get('SP_API_AUTH_CACHE_TTL', 3200)))
|
|
20
|
+
grantless_cache = TTLCache(maxsize=int(os.environ.get('SP_API_AUTH_CACHE_SIZE', 10)), ttl=int(os.environ.get('SP_API_AUTH_CACHE_TTL', 3200)))
|
|
15
21
|
|
|
16
22
|
logger = logging.getLogger(__name__)
|
|
17
23
|
|
|
@@ -25,9 +31,13 @@ class AccessTokenClient(BaseClient):
|
|
|
25
31
|
self.cred = Credentials(refresh_token, credentials)
|
|
26
32
|
self.proxies = proxies
|
|
27
33
|
self.verify = verify
|
|
34
|
+
self._transport = HttpxTransport(
|
|
35
|
+
proxies=proxies,
|
|
36
|
+
verify=verify,
|
|
37
|
+
)
|
|
28
38
|
|
|
29
39
|
def _request(self, url, data, headers):
|
|
30
|
-
response =
|
|
40
|
+
response = self._transport.request("POST", url, data=data, headers=headers)
|
|
31
41
|
response_data = response.json()
|
|
32
42
|
if response.status_code != 200:
|
|
33
43
|
error_message = response_data.get('error_description')
|
|
@@ -93,39 +103,18 @@ class AccessTokenClient(BaseClient):
|
|
|
93
103
|
return res
|
|
94
104
|
|
|
95
105
|
def _auth_code_request_body(self, auth_code):
|
|
96
|
-
return
|
|
97
|
-
'grant_type': 'authorization_code',
|
|
98
|
-
'code': auth_code,
|
|
99
|
-
'client_id': self.cred.client_id,
|
|
100
|
-
'client_secret': self.cred.client_secret
|
|
101
|
-
}
|
|
106
|
+
return build_auth_code_request_body(self.cred, auth_code)
|
|
102
107
|
|
|
103
108
|
def grantless_data(self, scope_value: str):
|
|
104
|
-
return
|
|
105
|
-
'grant_type': 'client_credentials',
|
|
106
|
-
'client_id': self.cred.client_id,
|
|
107
|
-
'scope': scope_value,
|
|
108
|
-
'client_secret': self.cred.client_secret
|
|
109
|
-
}
|
|
109
|
+
return build_grantless_data(self.cred, scope_value)
|
|
110
110
|
|
|
111
111
|
@property
|
|
112
112
|
def data(self):
|
|
113
|
-
return
|
|
114
|
-
'grant_type': self.grant_type,
|
|
115
|
-
'client_id': self.cred.client_id,
|
|
116
|
-
'refresh_token': self.cred.refresh_token,
|
|
117
|
-
'client_secret': self.cred.client_secret
|
|
118
|
-
}
|
|
113
|
+
return build_refresh_token_data(self.cred, self.grant_type)
|
|
119
114
|
|
|
120
115
|
@property
|
|
121
116
|
def headers(self):
|
|
122
|
-
return
|
|
123
|
-
'User-Agent': self.user_agent,
|
|
124
|
-
'content-type': self.content_type
|
|
125
|
-
}
|
|
117
|
+
return build_headers(self.user_agent, self.content_type)
|
|
126
118
|
|
|
127
119
|
def _get_cache_key(self, token_flavor=''):
|
|
128
|
-
return
|
|
129
|
-
(token_flavor + (self.cred.refresh_token or '__grantless__')).encode('utf-8')
|
|
130
|
-
).hexdigest()
|
|
131
|
-
|
|
120
|
+
return build_cache_key(self.cred.refresh_token, token_flavor)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class AccessTokenResponse:
|
|
2
2
|
def __init__(self, **kwargs):
|
|
3
|
-
self.access_token = kwargs.get(
|
|
4
|
-
self.refresh_token = kwargs.get(
|
|
5
|
-
self.expires_in = kwargs.get(
|
|
6
|
-
self.token_type = kwargs.get(
|
|
3
|
+
self.access_token = kwargs.get("access_token")
|
|
4
|
+
self.refresh_token = kwargs.get("refresh_token")
|
|
5
|
+
self.expires_in = kwargs.get("expires_in")
|
|
6
|
+
self.token_type = kwargs.get("token_type")
|
sp_api/base/ApiResponse.py
CHANGED
|
@@ -44,14 +44,15 @@ class ApiResponse:
|
|
|
44
44
|
self.errors = errors
|
|
45
45
|
self.pagination = pagination
|
|
46
46
|
self.headers = headers
|
|
47
|
-
self.rate_limit = headers.get("x-amzn-RateLimit-Limit")
|
|
47
|
+
self.rate_limit = (headers or {}).get("x-amzn-RateLimit-Limit")
|
|
48
48
|
try:
|
|
49
49
|
self.next_token = (
|
|
50
50
|
nextToken
|
|
51
|
-
or self.payload.get(
|
|
51
|
+
or self.payload.get("pagination", {}).get("nextToken", None)
|
|
52
52
|
or self.payload.get("NextToken", None)
|
|
53
|
-
or self.pagination.get("nextToken", None)
|
|
54
|
-
or self.payload.get(
|
|
53
|
+
or (self.pagination or {}).get("nextToken", None)
|
|
54
|
+
or self.payload.get("nextPageToken", None)
|
|
55
|
+
or self.payload.get("nextToken", None)
|
|
55
56
|
)
|
|
56
57
|
|
|
57
58
|
except AttributeError:
|
sp_api/base/__init__.py
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
from .base_client import BaseClient
|
|
2
2
|
from .client import Client
|
|
3
|
-
from .helpers import
|
|
3
|
+
from .helpers import (
|
|
4
|
+
fill_query_params,
|
|
5
|
+
sp_endpoint,
|
|
6
|
+
create_md5,
|
|
7
|
+
nest_dict,
|
|
8
|
+
_nest_dict_rec,
|
|
9
|
+
deprecated,
|
|
10
|
+
)
|
|
4
11
|
from .marketplaces import Marketplaces
|
|
5
12
|
from .exceptions import SellingApiException
|
|
6
13
|
from .exceptions import SellingApiBadRequestException
|
|
@@ -16,7 +23,11 @@ from .schedules import Schedules
|
|
|
16
23
|
from .report_status import ReportStatus
|
|
17
24
|
from .sales_enum import FirstDayOfWeek, Granularity, BuyerType
|
|
18
25
|
from .fulfillment_channel import FulfillmentChannel
|
|
19
|
-
from .included_data import
|
|
26
|
+
from .included_data import (
|
|
27
|
+
IncludedData,
|
|
28
|
+
ListingItemsIncludedData,
|
|
29
|
+
CatalogItemsIncludedData,
|
|
30
|
+
)
|
|
20
31
|
from .notifications import NotificationType
|
|
21
32
|
from .credential_provider import CredentialProvider, MissingCredentials
|
|
22
33
|
from .ApiResponse import ApiResponse
|
|
@@ -30,44 +41,44 @@ from .marketplaces import AwsEnv
|
|
|
30
41
|
|
|
31
42
|
|
|
32
43
|
__all__ = [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
44
|
+
"Credentials",
|
|
45
|
+
"AuthorizationError",
|
|
46
|
+
"AccessTokenClient",
|
|
47
|
+
"ReportType",
|
|
48
|
+
"FeedType",
|
|
49
|
+
"ProcessingStatus",
|
|
50
|
+
"ApiResponse",
|
|
51
|
+
"Client",
|
|
52
|
+
"BaseClient",
|
|
53
|
+
"Marketplaces",
|
|
54
|
+
"fill_query_params",
|
|
55
|
+
"sp_endpoint",
|
|
56
|
+
"SellingApiException",
|
|
57
|
+
"SellingApiBadRequestException",
|
|
58
|
+
"SellingApiNotFoundException",
|
|
59
|
+
"SellingApiServerException",
|
|
60
|
+
"SellingApiForbiddenException",
|
|
61
|
+
"SellingApiBadRequestException",
|
|
62
|
+
"SellingApiRequestThrottledException",
|
|
63
|
+
"SellingApiTemporarilyUnavailableException",
|
|
64
|
+
"SellingApiTooLargeException",
|
|
65
|
+
"SellingApiStateConflictException",
|
|
66
|
+
"SellingApiUnsupportedFormatException",
|
|
67
|
+
"Schedules",
|
|
68
|
+
"ReportStatus",
|
|
69
|
+
"FirstDayOfWeek",
|
|
70
|
+
"Granularity",
|
|
71
|
+
"BuyerType",
|
|
72
|
+
"FulfillmentChannel",
|
|
73
|
+
"deprecated",
|
|
74
|
+
"NotificationType",
|
|
75
|
+
"CredentialProvider",
|
|
76
|
+
"MissingCredentials",
|
|
77
|
+
"nest_dict",
|
|
78
|
+
"_nest_dict_rec",
|
|
79
|
+
"IneligibilityReasonList",
|
|
80
|
+
"IncludedData",
|
|
81
|
+
"ListingItemsIncludedData",
|
|
82
|
+
"CatalogItemsIncludedData",
|
|
83
|
+
"AwsEnv",
|
|
73
84
|
]
|
sp_api/base/_core.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from json import JSONDecodeError
|
|
4
|
+
|
|
5
|
+
from .ApiResponse import ApiResponse
|
|
6
|
+
from .exceptions import get_exception_for_code
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def resolve_method(params, data):
|
|
10
|
+
if params is None:
|
|
11
|
+
params = {}
|
|
12
|
+
if data is None:
|
|
13
|
+
data = {}
|
|
14
|
+
method = params.pop(
|
|
15
|
+
"method", data.pop("method", "GET") if isinstance(data, dict) else "GET"
|
|
16
|
+
)
|
|
17
|
+
return method, params, data
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def check_version(path, version):
|
|
21
|
+
if "<version>" not in path:
|
|
22
|
+
return path
|
|
23
|
+
return path.replace("<version>", version)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def add_marketplaces(method, marketplace_id, data):
|
|
27
|
+
post_keys = ["marketplaceIds", "MarketplaceIds"]
|
|
28
|
+
get_keys = ["MarketplaceId", "MarketplaceIds", "marketplace_ids", "marketplaceIds"]
|
|
29
|
+
|
|
30
|
+
if method == "POST":
|
|
31
|
+
if any(key in data.keys() for key in post_keys):
|
|
32
|
+
return
|
|
33
|
+
data.update(
|
|
34
|
+
{
|
|
35
|
+
key: marketplace_id if not key.endswith("s") else [marketplace_id]
|
|
36
|
+
for key in post_keys
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
if any(key in data.keys() for key in get_keys):
|
|
41
|
+
return
|
|
42
|
+
data.update(
|
|
43
|
+
{
|
|
44
|
+
key: marketplace_id if not key.endswith("s") else [marketplace_id]
|
|
45
|
+
for key in get_keys
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_headers(endpoint, user_agent, access_token, content_type="application/json"):
|
|
51
|
+
return {
|
|
52
|
+
"host": endpoint[8:],
|
|
53
|
+
"user-agent": user_agent,
|
|
54
|
+
"x-amz-access-token": access_token,
|
|
55
|
+
"x-amz-date": datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"),
|
|
56
|
+
"content-type": content_type,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def prepare_request(
|
|
61
|
+
*,
|
|
62
|
+
method,
|
|
63
|
+
endpoint,
|
|
64
|
+
path,
|
|
65
|
+
params,
|
|
66
|
+
data,
|
|
67
|
+
headers,
|
|
68
|
+
add_marketplace,
|
|
69
|
+
marketplace_id,
|
|
70
|
+
version,
|
|
71
|
+
):
|
|
72
|
+
if add_marketplace:
|
|
73
|
+
add_marketplaces(method, marketplace_id, data if method in ("POST", "PUT") else params)
|
|
74
|
+
url = endpoint + check_version(path, version)
|
|
75
|
+
content = (
|
|
76
|
+
json.dumps(data) if data and method in ("POST", "PUT", "PATCH") else None
|
|
77
|
+
)
|
|
78
|
+
return {
|
|
79
|
+
"method": method,
|
|
80
|
+
"url": url,
|
|
81
|
+
"params": params,
|
|
82
|
+
"content": content,
|
|
83
|
+
"headers": headers,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def parse_response(res, *, method, res_no_data=False, bulk=False, wrap_list=False):
|
|
88
|
+
if (method == "DELETE" or res_no_data) and 200 <= res.status_code < 300:
|
|
89
|
+
try:
|
|
90
|
+
js = res.json() or {}
|
|
91
|
+
except JSONDecodeError:
|
|
92
|
+
js = {"status_code": res.status_code}
|
|
93
|
+
else:
|
|
94
|
+
try:
|
|
95
|
+
js = res.json() or {}
|
|
96
|
+
except JSONDecodeError:
|
|
97
|
+
js = {}
|
|
98
|
+
|
|
99
|
+
if isinstance(js, list):
|
|
100
|
+
if wrap_list:
|
|
101
|
+
js = dict(payload=js)
|
|
102
|
+
else:
|
|
103
|
+
js = js[0]
|
|
104
|
+
|
|
105
|
+
error = js.get("errors", None)
|
|
106
|
+
if error:
|
|
107
|
+
exception = get_exception_for_code(res.status_code)
|
|
108
|
+
raise exception(error, headers=res.headers)
|
|
109
|
+
|
|
110
|
+
return ApiResponse(**js, headers=res.headers)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class HttpxTransport:
|
|
5
|
+
def __init__(
|
|
6
|
+
self, *, timeout=None, proxies=None, proxy=None, verify=True, client=None
|
|
7
|
+
):
|
|
8
|
+
proxy_config = proxy if proxy is not None else proxies
|
|
9
|
+
self._client = client or httpx.Client(
|
|
10
|
+
timeout=timeout,
|
|
11
|
+
proxy=proxy_config,
|
|
12
|
+
verify=verify,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def request(self, method, url, *, params=None, data=None, content=None, headers=None):
|
|
16
|
+
return self._client.request(
|
|
17
|
+
method,
|
|
18
|
+
url,
|
|
19
|
+
params=params,
|
|
20
|
+
data=data,
|
|
21
|
+
content=content,
|
|
22
|
+
headers=headers,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def stream(
|
|
26
|
+
self, method, url, *, params=None, data=None, content=None, headers=None, timeout=None
|
|
27
|
+
):
|
|
28
|
+
return self._client.stream(
|
|
29
|
+
method,
|
|
30
|
+
url,
|
|
31
|
+
params=params,
|
|
32
|
+
data=data,
|
|
33
|
+
content=content,
|
|
34
|
+
headers=headers,
|
|
35
|
+
timeout=timeout,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def close(self):
|
|
39
|
+
self._client.close()
|
sp_api/base/base_client.py
CHANGED
|
@@ -2,7 +2,7 @@ from sp_api.__version__ import __version__
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class BaseClient:
|
|
5
|
-
scheme =
|
|
6
|
-
method =
|
|
7
|
-
content_type =
|
|
8
|
-
user_agent = f
|
|
5
|
+
scheme = "https://"
|
|
6
|
+
method = "GET"
|
|
7
|
+
content_type = "application/x-www-form-urlencoded;charset=UTF-8"
|
|
8
|
+
user_agent = f"python-sp-api-{__version__}"
|