Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 43 additions & 37 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,57 @@
# Migrating from 0.1.x
# Migrating from 0.1.x to 1.0

Version 0.2 replaces the hand-written `requests` client with an SDK generated
Version 1.0 replaces the hand-written `requests` client with an SDK generated
from Amazon's API models by [oagen](https://github.com/workos/oagen), inside
the same `amzn_selling_partner` package. The old `amzn_selling_partner` entry
points keep working as thin wrappers, but several things changed.
the same `amzn_selling_partner` package. The 0.1.x entry points (`client`,
`reports`, `vendor`, `utils`) are gone; this page maps them to the new API.

## Breaking changes
## What changed

| Area | 0.1.x | 0.2 |
| Area | 0.1.x | 1.0 |
|---|---|---|
| Python | 3.10+ | 3.10+ (unchanged) |
| HTTP | `requests` | `httpx2` (sync and async), generated `sdk/_http.py` |
| Python | 3.10+ | 3.10+ |
| HTTP | `requests` | `httpx2`, sync and async, in the generated `sdk/_http.py` |
| Models | pydantic 1 (`.dict()`, `class Config`) | pydantic 2, generated from the specs (`.model_dump()`, frozen, `extra="allow"`), under `amzn_selling_partner.sdk.models.<api>_<version>` |
| Auth | LWA + AWS Signature V4 (boto3, `requests_aws4auth`) | **LWA only.** The `aws_*` constructor arguments are accepted and ignored with a `DeprecationWarning`; `ClientSessionAuth` / `ClientSessionAuthTemporaryCredentials` raise `NotImplementedError` |
| Exceptions | `requests.HTTPError` | `amzn_selling_partner.APIStatusError` and subclasses (`RateLimitExceededError`, `NotFoundError`, `AuthenticationError` (401), `AuthorizationError` (403), `ServerError`, ...), `APIConnectionError`, `APITimeoutError`, `APIResponseValidationError` |
| `http_session` attribute | `requests.Session` | removed; use `client.sp.http_client` (`httpx2.Client`) |
| Model field names | wire casing (`order.purchaseOrderNumber`) | snake_case attributes with wire aliases (`order.purchase_order_number`; `Order(purchaseOrderNumber=...)` still works thanks to `populate_by_name`) |
| Enum fields on models | Python enums (`PurchaseOrderState.NEW`) | generated `str` enums (`PurchaseOrderState.NEW == "New"`); the old enum classes still exist and compare equal |
| `reports.Client.get_reports` | slept `x-amzn-RateLimit-Limit * 100` seconds after every call | token-bucket throttling from the spec's rate table |
| Distribution deps | `requests`, `requests_aws4auth`, `boto3`, `pydantic<2` | `httpx2`, `pydantic>=2.9` |
| Auth | LWA + AWS Signature V4 (boto3, `requests_aws4auth`) | LWA only; Restricted Data Tokens and grantless scopes are obtained automatically |
| Errors | `requests.HTTPError` | `amzn_selling_partner.APIStatusError` and subclasses (`RateLimitExceededError`, `NotFoundError`, `AuthenticationError` for 401, `AuthorizationError` for 403, `ServerError`, ...), `APIConnectionError`, `APITimeoutError`, `APIResponseValidationError` |
| Model fields | wire casing (`order.purchaseOrderNumber`) | snake_case attributes with wire aliases (`order.purchase_order_number`; `Order(purchaseOrderNumber=...)` still validates) |
| Enums | hand-written Python enums | generated `str` enums (`PurchaseOrderState.NEW == "New"`) |
| Rate limits | `reports.Client.get_reports` slept `x-amzn-RateLimit-Limit * 100` seconds after every call | token-bucket throttling from the spec's rate table, retries with `Retry-After` |
| Dependencies | `requests`, `requests_aws4auth`, `boto3`, `pydantic<2` | `httpx2`, `pydantic>=2.9` |

## Kept entry points
## Entry points

| Old | Now |
| 0.1.x | 1.0 |
|---|---|
| `amzn_selling_partner.client.SellingPartnerRegion` | alias of `amzn_selling_partner.plugins.amazon_spapi.Region` (same members and properties) |
| `amzn_selling_partner.client.BaseClient` | wraps `amzn_selling_partner.SellingPartner` (available as `.sp`) |
| `amzn_selling_partner.vendor.orders.Client` | `get_purchase_orders(query=)`, `get_purchase_order(id)` (+ `get_purchase_orders_status`, `submit_acknowledgement`) |
| `amzn_selling_partner.vendor.orders.Order`, `OrderDetails`, ... | spec-generated models (`amzn_selling_partner.sdk.models.vendor_orders_v1`) |
| `amzn_selling_partner.reports.Client` | all seven public methods, same signatures |
| `amzn_selling_partner.reports.Report`, `ReportDocument`, ... | spec-generated models |
| `*Query` / `CreateReport*Specification` / `ReportOptions` | kept (pydantic 2) |
| `amzn_selling_partner.utils.date`, `utils.file` | unchanged |

Environment variables `SELLING_PARTNER_APP_CLIENT_ID`, `..._CLIENT_SECRET`,
`..._REFRESH_TOKEN` are still honoured (`AMZN_SELLING_PARTNER_*` are the new names).

## New API
| `amzn_selling_partner.client.BaseClient(selling_partner_region=..., selling_partner_app_client_id=..., ...)` | `amzn_selling_partner.AsyncSellingPartner(region=..., client_id=..., client_secret=..., refresh_token=...)` or the synchronous `SellingPartner`; one client serves every API |
| `amzn_selling_partner.client.SellingPartnerRegion.NORTH_AMERICA` / `EUROPE` / `FAR_EAST` | `amzn_selling_partner.Region.NA` / `EU` / `FE` |
| `aws_access_key_id`, `aws_secret_access_key`, `aws_selling_partner_role`, `aws_selling_partner_role_session_name` | removed; the Selling Partner API no longer uses AWS Signature V4 |
| `amzn_selling_partner.client.auth.ClientSessionAuthAccessToken` | `amzn_selling_partner.plugins.amazon_spapi.LWAAuth` (created by the client; pass `token_store=` for a custom cache) |
| `amzn_selling_partner.client.auth.ClientSessionAuth`, `ClientSessionAuthTemporaryCredentials` | removed |
| `amzn_selling_partner.vendor.orders.Client().get_purchase_orders(query=...)` | `client.vendor_orders_v1.list_purchase_orders(created_after=..., ...)` (`iter_list_purchase_orders` follows the pages) |
| `amzn_selling_partner.vendor.orders.Client().get_purchase_order(id)` | `client.vendor_orders_v1.get_purchase_order(id)` |
| `amzn_selling_partner.vendor.orders.Order`, `OrderDetails`, ... | `amzn_selling_partner.sdk.models.vendor_orders_v1` |
| `amzn_selling_partner.reports.Client().get_reports(query=...)` | `client.reports.list_reports(report_types=[...], ...)` (`iter_list_reports` follows the pages) |
| `amzn_selling_partner.reports.Client().create_report(...)` | `client.reports.create_report(CreateReportSpecification(...))` |
| `amzn_selling_partner.reports.Client().get_report(id)` | `client.reports.get_report(id)` |
| `amzn_selling_partner.reports.Client().get_report_document(id)` / download | `client.reports.get_document(id)`; `client.documents.download_report(id)` downloads and decompresses it |
| `amzn_selling_partner.reports.Report`, `ReportDocument`, `*Query`, `CreateReport*Specification`, `ReportOptions` | `amzn_selling_partner.sdk.models.reports_v2021_06_30` |
| `amzn_selling_partner.utils.date.amazon_isoformat(value)` | pass a `datetime` directly; the client serialises it as ISO 8601 with `Z` |
| `amzn_selling_partner.utils.date.datetime_utcnow()` / `datetime_utcpast(...)` | `datetime.datetime.now(datetime.timezone.utc)` and `datetime.timedelta` |
| `amzn_selling_partner.utils.file.write_binary_file(path, content)` | `client.documents.download_report(id, path=...)` streams to a file; otherwise `pathlib.Path(path).write_bytes(content)` |
| `SELLING_PARTNER_APP_CLIENT_ID`, `..._CLIENT_SECRET`, `..._REFRESH_TOKEN` | still read; `AMZN_SELLING_PARTNER_CLIENT_ID`, `..._CLIENT_SECRET`, `..._REFRESH_TOKEN` are the new names |

`amzn_selling_partner.sdk.resources.OPERATIONS` maps Amazon's operationIds
(`getPurchaseOrders`) to the generated method names.

## The 1.0 API in short

One resource per API version (`client.orders_v0`, `client.orders_v2026_01_01`;
`client.orders` is the newest version), one method per operation. Method names
are derived by oagen from the operation (`list_orders`, `get_order`,
`create_feed`); `amzn_selling_partner.sdk.resources.OPERATIONS` maps Amazon's
operationIds to them.
`client.orders` is the newest version), one method per operation, every
paginated operation with an `iter_<method>` twin.

```python
from amzn_selling_partner import AsyncSellingPartner
from amzn_selling_partner import AsyncSellingPartner, Region

async with AsyncSellingPartner(region=Region.NA) as client:
async for order in client.vendor_orders_v1.iter_list_purchase_orders(created_after="2024-01-01T00:00:00Z"):
Expand Down Expand Up @@ -78,8 +84,8 @@ assert client.orders_v0.get_order("1").payload.order_status is OrderOrderStatus.

The same transport serves the LWA token endpoint, the Tokens API and
pre-signed document URLs, so a single handler can emulate a whole flow
(see `tests/_amazon_mock.py`). `amzn_selling_partner.sandbox_tests` runs every
operation through the examples embedded in Amazon's models the same way.
(see `tests/_amazon_mock.py`). `tests/sandbox.py` runs every operation through
the examples embedded in Amazon's models the same way.

If your application still imports `httpx` elsewhere, `httpx2.alias_httpx()`
(called once at start-up, before anything imports `httpx`) makes both names
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ notification models) lives in `amzn_selling_partner.plugins.amazon_spapi`.
- **Bug reports:** https://github.com/dbritto-dev/amzn-selling-partner-python/issues
- **Migration from 0.1.x:** [MIGRATION.md](MIGRATION.md)
- **Updating the bundled specs:** [docs/UPDATING_SPECS.md](docs/UPDATING_SPECS.md)
- **Design notes:** [docs/PLAN.md](docs/PLAN.md)

## Installation

Expand Down Expand Up @@ -277,7 +276,7 @@ uv run pytest
uv run ty check
uv run pytest benchmarks
uvx nox -s security_test
uv run python -m amzn_selling_partner.sandbox_tests
uv run python -m tests.sandbox

cd codegen && npm ci --ignore-scripts && npm run generate
cd codegen && npm test && npm run typecheck
Expand Down
6 changes: 3 additions & 3 deletions codegen/src/amazon.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Amazon-specific generation policy: API attribute names for the model files
* (docs/PLAN.md §3), aliases, and the pagination overrides the heuristic cannot
* settle (§9). Restricted-operation and grantless tables stay in Python
* Amazon-specific generation policy: API attribute names for the model files,
* aliases, and the pagination overrides the heuristic cannot settle.
* Restricted-operation and grantless tables stay in Python
* (`plugins/_amazon/rdt.py`) because the auth hook reads them at run time.
*/
import type { PaginationDescriptor } from './python/pagination.js';
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function collectRefs(node: unknown, out: Set<string>): void {
}

/**
* Repair the two irregularities found in the pinned models (docs/PLAN.md §10):
* Repair the two irregularities found in the pinned models:
* `#ref` instead of `$ref`, and references to definitions that do not exist
* (replaced by an empty schema, with a warning).
*/
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/policy/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* `schemaNameTransform` and `operationIdTransform`.
*
* `transformSpec` is the pre-IR overlay that keeps oagen's parser from losing
* information present in the Amazon files (docs/PLAN.md):
* information present in the Amazon files:
*
* 1. Named schemas that are not objects (`OrderList: array of Order`,
* `MarketplaceId: string`, bare `oneOf` unions) are inlined at every
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/python/ratelimits.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Parse the "Rate (requests per second) | Burst" usage-plan tables (docs/PLAN.md §8). */
/** Parse the "Rate (requests per second) | Burst" usage-plan tables of the operation descriptions. */

export interface RateLimit {
rate: number;
Expand Down
Loading
Loading