amazon-sp-cli 0.2.3__tar.gz → 0.2.5__tar.gz

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.
Files changed (31) hide show
  1. {amazon_sp_cli-0.2.3/amazon_sp_cli.egg-info → amazon_sp_cli-0.2.5}/PKG-INFO +1 -1
  2. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/README.md +2 -3
  3. amazon_sp_cli-0.2.5/amazon_sp_cli/__init__.py +2 -0
  4. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/models/a_plus.py +72 -19
  5. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5/amazon_sp_cli.egg-info}/PKG-INFO +1 -1
  6. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_a_plus.py +107 -4
  7. amazon_sp_cli-0.2.3/amazon_sp_cli/__init__.py +0 -2
  8. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/LICENSE +0 -0
  9. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/MANIFEST.in +0 -0
  10. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/__main__.py +0 -0
  11. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/auth.py +0 -0
  12. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/cli.py +0 -0
  13. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/client.py +0 -0
  14. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/__init__.py +0 -0
  15. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/a_plus.py +0 -0
  16. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/auth.py +0 -0
  17. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/pricing.py +0 -0
  18. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/main.py +0 -0
  19. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/models/__init__.py +0 -0
  20. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/SOURCES.txt +0 -0
  21. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/dependency_links.txt +0 -0
  22. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/entry_points.txt +0 -0
  23. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/requires.txt +0 -0
  24. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/top_level.txt +0 -0
  25. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/pyproject.toml +0 -0
  26. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/setup.cfg +0 -0
  27. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/setup.py +0 -0
  28. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/__init__.py +0 -0
  29. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_auth.py +0 -0
  30. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_client.py +0 -0
  31. {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_pricing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: CLI tool for Amazon Selling Partner API (SP-API) operations
5
5
  Home-page: https://github.com/stellaraether/amazon-sp-cli
6
6
  Author: Lunan Li
@@ -120,10 +120,9 @@ python3 -m amazon_sp_cli get-price PAW2603190101-BLU
120
120
 
121
121
  This repository uses automated releases. To publish a new version:
122
122
 
123
- 1. Open a **standalone PR** that only bumps `version` in `setup.py`.
123
+ 1. Open a **standalone PR** that only bumps `__version__` in `amazon_sp_cli/__init__.py`.
124
124
  2. The PR must not include code, test, or documentation changes — a CI check enforces this.
125
- 3. Once the PR is merged to `main`, the `Auto Release` workflow detects the new version, creates a tag (e.g. `v0.2.1`), and pushes it.
126
- 4. The tag push triggers the `Publish to PyPI` workflow, which runs the full test suite and publishes the package.
125
+ 3. Once the PR is merged to `main`, the `Auto Release` workflow runs the full test suite, creates a tag (e.g. `v0.2.1`), creates a GitHub release, and publishes to PyPI.
127
126
 
128
127
  Do not create tags manually.
129
128
 
@@ -0,0 +1,2 @@
1
+ # Amazon SP-API CLI
2
+ __version__ = "0.2.5"
@@ -18,14 +18,26 @@ class TextComponent:
18
18
  class ImageComponent:
19
19
  """Image component for A+ Content modules."""
20
20
 
21
- def __init__(self, upload_destination_id: str, image_crop: dict = None):
21
+ def __init__(
22
+ self,
23
+ upload_destination_id: str,
24
+ image_crop: dict = None,
25
+ alt_text: str = None,
26
+ image_crop_specification: dict = None,
27
+ ):
22
28
  self.upload_destination_id = upload_destination_id
23
29
  self.image_crop = image_crop
30
+ self.alt_text = alt_text
31
+ self.image_crop_specification = image_crop_specification
24
32
 
25
33
  def to_dict(self) -> dict:
26
34
  result = {"uploadDestinationId": self.upload_destination_id}
27
35
  if self.image_crop:
28
36
  result["imageCrop"] = self.image_crop
37
+ if self.alt_text:
38
+ result["altText"] = self.alt_text
39
+ if self.image_crop_specification:
40
+ result["imageCropSpecification"] = self.image_crop_specification
29
41
  return result
30
42
 
31
43
 
@@ -141,19 +153,23 @@ class StandardTextModule:
141
153
  class StandardImageTextOverlayModule:
142
154
  """Standard Image with Text Overlay module."""
143
155
 
144
- def __init__(self, headline=None, image=None, body=None):
156
+ def __init__(self, headline=None, image=None, body=None, overlay_color_type="DARK"):
145
157
  self.headline = headline
146
158
  self.image = image
147
159
  self.body = body
160
+ self.overlay_color_type = overlay_color_type
148
161
 
149
162
  def to_dict(self) -> dict:
150
- result = {}
163
+ result = {"overlayColorType": self.overlay_color_type}
164
+ block = {}
151
165
  if self.headline:
152
- result["headline"] = self.headline.to_dict()
166
+ block["headline"] = self.headline.to_dict()
153
167
  if self.image:
154
- result["image"] = self.image.to_dict()
168
+ block["image"] = self.image.to_dict()
155
169
  if self.body:
156
- result["body"] = self.body.to_dict()
170
+ block["body"] = self.body.to_dict()
171
+ if block:
172
+ result["block"] = block
157
173
  return result
158
174
 
159
175
 
@@ -166,7 +182,7 @@ class StandardCompanyLogoModule:
166
182
  def to_dict(self) -> dict:
167
183
  result = {}
168
184
  if self.company_logo:
169
- result["companyLogo"] = {"image": self.company_logo.to_dict()}
185
+ result["companyLogo"] = self.company_logo.to_dict()
170
186
  return result
171
187
 
172
188
 
@@ -174,7 +190,7 @@ class ContentModule:
174
190
  """A+ Content module wrapper."""
175
191
 
176
192
  MODULE_TYPES = {
177
- "STANDARD_IMAGE_TEXT": "standardImageText",
193
+ "STANDARD_IMAGE_TEXT": "standardImageTextOverlay",
178
194
  "STANDARD_SINGLE_IMAGE": "standardSingleImage",
179
195
  "STANDARD_MULTIPLE_IMAGE_TEXT": "standardMultipleImageText",
180
196
  "STANDARD_FOUR_IMAGE_TEXT": "standardFourImageText",
@@ -210,8 +226,8 @@ class ContentModule:
210
226
  result = {"contentModuleType": self.module_type}
211
227
  field_name = self.MODULE_TYPES.get(self.module_type)
212
228
 
213
- if field_name == "standardImageText" and self.standard_image_text:
214
- result["standardImageText"] = self.standard_image_text.to_dict()
229
+ if field_name == "standardImageTextOverlay" and self.standard_image_text_overlay:
230
+ result["standardImageTextOverlay"] = self.standard_image_text_overlay.to_dict()
215
231
  elif field_name == "standardSingleImage" and self.standard_single_image:
216
232
  result["standardSingleImage"] = self.standard_single_image.to_dict()
217
233
  elif field_name == "standardMultipleImageText" and self.standard_multiple_image_text:
@@ -222,8 +238,6 @@ class ContentModule:
222
238
  result["standardComparisonTable"] = self.standard_comparison_table.to_dict()
223
239
  elif field_name == "standardText" and self.standard_text:
224
240
  result["standardText"] = self.standard_text.to_dict()
225
- elif field_name == "standardImageTextOverlay" and self.standard_image_text_overlay:
226
- result["standardImageTextOverlay"] = self.standard_image_text_overlay.to_dict()
227
241
  elif field_name == "standardCompanyLogo" and self.standard_company_logo:
228
242
  result["standardCompanyLogo"] = self.standard_company_logo.to_dict()
229
243
 
@@ -308,19 +322,38 @@ def build_module_from_json(data: dict) -> ContentModule:
308
322
  module_type = data.get("contentModuleType") or data.get("moduleType", "STANDARD_TEXT")
309
323
 
310
324
  if module_type == "STANDARD_IMAGE_TEXT":
325
+ body = None
326
+ if data.get("body"):
327
+ body = ParagraphComponent(text_list=[TextComponent(data["body"])])
311
328
  return ContentModule(
312
329
  module_type=module_type,
313
- standard_image_text=StandardImageTextModule(
330
+ standard_image_text_overlay=StandardImageTextOverlayModule(
314
331
  headline=TextComponent(data["headline"]) if data.get("headline") else None,
315
- body=TextComponent(data["body"]) if data.get("body") else None,
316
- image=ImageComponent(data["imageId"]) if data.get("imageId") else None,
332
+ body=body,
333
+ image=(
334
+ ImageComponent(
335
+ data["imageId"],
336
+ alt_text=data.get("altText"),
337
+ image_crop_specification=data.get("imageCropSpecification"),
338
+ )
339
+ if data.get("imageId")
340
+ else None
341
+ ),
317
342
  ),
318
343
  )
319
344
  elif module_type == "STANDARD_SINGLE_IMAGE":
320
345
  return ContentModule(
321
346
  module_type=module_type,
322
347
  standard_single_image=StandardSingleImageModule(
323
- image=ImageComponent(data["imageId"]) if data.get("imageId") else None,
348
+ image=(
349
+ ImageComponent(
350
+ data["imageId"],
351
+ alt_text=data.get("altText"),
352
+ image_crop_specification=data.get("imageCropSpecification"),
353
+ )
354
+ if data.get("imageId")
355
+ else None
356
+ ),
324
357
  image_caption=TextComponent(data["caption"]) if data.get("caption") else None,
325
358
  ),
326
359
  )
@@ -360,19 +393,39 @@ def build_module_from_json(data: dict) -> ContentModule:
360
393
  ),
361
394
  )
362
395
  elif module_type == "STANDARD_IMAGE_TEXT_OVERLAY":
396
+ body = None
397
+ if data.get("body"):
398
+ body = ParagraphComponent(text_list=[TextComponent(data["body"])])
363
399
  return ContentModule(
364
400
  module_type=module_type,
365
401
  standard_image_text_overlay=StandardImageTextOverlayModule(
366
402
  headline=TextComponent(data["headline"]) if data.get("headline") else None,
367
- body=TextComponent(data["body"]) if data.get("body") else None,
368
- image=ImageComponent(data["imageId"]) if data.get("imageId") else None,
403
+ body=body,
404
+ image=(
405
+ ImageComponent(
406
+ data["imageId"],
407
+ alt_text=data.get("altText"),
408
+ image_crop_specification=data.get("imageCropSpecification"),
409
+ )
410
+ if data.get("imageId")
411
+ else None
412
+ ),
413
+ overlay_color_type=data.get("overlayColorType", "DARK"),
369
414
  ),
370
415
  )
371
416
  elif module_type == "STANDARD_COMPANY_LOGO":
372
417
  return ContentModule(
373
418
  module_type=module_type,
374
419
  standard_company_logo=StandardCompanyLogoModule(
375
- company_logo=ImageComponent(data["imageId"]) if data.get("imageId") else None,
420
+ company_logo=(
421
+ ImageComponent(
422
+ data["imageId"],
423
+ alt_text=data.get("altText"),
424
+ image_crop_specification=data.get("imageCropSpecification"),
425
+ )
426
+ if data.get("imageId")
427
+ else None
428
+ ),
376
429
  ),
377
430
  )
378
431
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: CLI tool for Amazon Selling Partner API (SP-API) operations
5
5
  Home-page: https://github.com/stellaraether/amazon-sp-cli
6
6
  Author: Lunan Li
@@ -75,7 +75,7 @@ class TestDataModels:
75
75
  assert "has no content" in doc.validate()[0]
76
76
 
77
77
  def test_module_validate_empty_content(self):
78
- mod = ContentModule("STANDARD_IMAGE_TEXT")
78
+ mod = ContentModule("STANDARD_TEXT")
79
79
  issues = mod.validate(0)
80
80
  assert len(issues) == 1
81
81
  assert "has no content" in issues[0]
@@ -113,11 +113,24 @@ class TestBuildFromJson:
113
113
  data = {
114
114
  "contentModuleType": "STANDARD_COMPANY_LOGO",
115
115
  "imageId": "logo-123",
116
+ "altText": "Company Logo",
117
+ "imageCropSpecification": {
118
+ "size": {
119
+ "width": {"value": 600, "units": "pixels"},
120
+ "height": {"value": 180, "units": "pixels"},
121
+ },
122
+ "offset": {
123
+ "x": {"value": 0, "units": "pixels"},
124
+ "y": {"value": 0, "units": "pixels"},
125
+ },
126
+ },
116
127
  }
117
128
  mod = build_module_from_json(data)
118
129
  assert mod.module_type == "STANDARD_COMPANY_LOGO"
119
130
  result = mod.to_dict()
120
- assert result["standardCompanyLogo"]["companyLogo"]["image"]["uploadDestinationId"] == "logo-123"
131
+ assert result["standardCompanyLogo"]["companyLogo"]["uploadDestinationId"] == "logo-123"
132
+ assert result["standardCompanyLogo"]["companyLogo"]["altText"] == "Company Logo"
133
+ assert result["standardCompanyLogo"]["companyLogo"]["imageCropSpecification"]["size"]["width"]["value"] == 600
121
134
 
122
135
  def test_standard_company_logo_module_to_dict_empty(self):
123
136
  mod = StandardCompanyLogoModule()
@@ -133,8 +146,25 @@ class TestBuildFromJson:
133
146
  mod = build_module_from_json(data)
134
147
  assert mod.module_type == "STANDARD_IMAGE_TEXT"
135
148
  result = mod.to_dict()
136
- assert result["standardImageText"]["headline"]["value"] == "H"
137
- assert result["standardImageText"]["image"]["uploadDestinationId"] == "img-1"
149
+ assert result["standardImageTextOverlay"]["overlayColorType"] == "DARK"
150
+ assert result["standardImageTextOverlay"]["block"]["headline"]["value"] == "H"
151
+ assert result["standardImageTextOverlay"]["block"]["body"]["textList"][0]["value"] == "B"
152
+ assert result["standardImageTextOverlay"]["block"]["image"]["uploadDestinationId"] == "img-1"
153
+
154
+ def test_build_module_image_text_overlay(self):
155
+ data = {
156
+ "moduleType": "STANDARD_IMAGE_TEXT_OVERLAY",
157
+ "headline": "H",
158
+ "body": "B",
159
+ "imageId": "img-1",
160
+ "overlayColorType": "LIGHT",
161
+ }
162
+ mod = build_module_from_json(data)
163
+ assert mod.module_type == "STANDARD_IMAGE_TEXT_OVERLAY"
164
+ result = mod.to_dict()
165
+ assert result["standardImageTextOverlay"]["overlayColorType"] == "LIGHT"
166
+ assert result["standardImageTextOverlay"]["block"]["headline"]["value"] == "H"
167
+ assert result["standardImageTextOverlay"]["block"]["body"]["textList"][0]["value"] == "B"
138
168
 
139
169
  def test_build_module_comparison_table(self):
140
170
  data = {
@@ -148,6 +178,79 @@ class TestBuildFromJson:
148
178
  assert result["standardComparisonTable"]["headline"]["value"] == "Compare"
149
179
  assert result["standardComparisonTable"]["comparisonTableRows"][0]["name"] == "Feature"
150
180
 
181
+ def test_integration_full_document_from_json(self):
182
+ data = {
183
+ "modules": [
184
+ {
185
+ "moduleType": "STANDARD_COMPANY_LOGO",
186
+ "imageId": "aplus-media/sc/76a7fb65-607b-4e96-a3fa-a145d1397725.jpg",
187
+ "altText": "Pawified Logo",
188
+ "imageCropSpecification": {
189
+ "size": {
190
+ "width": {"value": 600, "units": "pixels"},
191
+ "height": {"value": 180, "units": "pixels"},
192
+ },
193
+ "offset": {
194
+ "x": {"value": 0, "units": "pixels"},
195
+ "y": {"value": 0, "units": "pixels"},
196
+ },
197
+ },
198
+ },
199
+ {
200
+ "moduleType": "STANDARD_IMAGE_TEXT",
201
+ "headline": "Pure Joy in Every Bite",
202
+ "body": "Treat your feline friend to the finest premium catnip...",
203
+ "imageId": "aplus-media/sc/77967e76-a03c-4514-85e1-371e7f2395d1.jpg",
204
+ },
205
+ {
206
+ "moduleType": "STANDARD_IMAGE_TEXT",
207
+ "headline": "100% Natural & Safe",
208
+ "body": "Made in the USA with all-natural...",
209
+ "imageId": "aplus-media/sc/215948ec-b306-4a0b-8491-f1342dc83f91.jpg",
210
+ },
211
+ {
212
+ "moduleType": "STANDARD_IMAGE_TEXT",
213
+ "headline": "Perfect for Playtime",
214
+ "body": "Each 2-pack includes convenient 5g sachets...",
215
+ "imageId": "aplus-media/sc/2e6493d5-e7a4-4c3a-8e47-a78e5f5b4802.jpg",
216
+ },
217
+ {
218
+ "moduleType": "STANDARD_IMAGE_TEXT",
219
+ "headline": "Premium Quality You Can Trust",
220
+ "body": "Pawified is committed to delivering premium pet products...",
221
+ "imageId": "aplus-media/sc/ef864286-0fec-4472-ad20-8b7dfee4922e.jpg",
222
+ },
223
+ ]
224
+ }
225
+ doc = build_content_from_json("pawified-catnip", data)
226
+ assert doc.name == "pawified-catnip"
227
+ assert len(doc.content_module_list) == 5
228
+ assert doc.content_module_list[0].module_type == "STANDARD_COMPANY_LOGO"
229
+ assert doc.content_module_list[1].module_type == "STANDARD_IMAGE_TEXT"
230
+
231
+ result = doc.to_dict()
232
+ modules = result["contentModuleList"]
233
+ assert len(modules) == 5
234
+
235
+ logo_module = modules[0]["standardCompanyLogo"]
236
+ assert "image" not in logo_module["companyLogo"]
237
+ assert (
238
+ logo_module["companyLogo"]["uploadDestinationId"]
239
+ == "aplus-media/sc/76a7fb65-607b-4e96-a3fa-a145d1397725.jpg"
240
+ )
241
+ assert logo_module["companyLogo"]["altText"] == "Pawified Logo"
242
+ assert logo_module["companyLogo"]["imageCropSpecification"]["size"]["width"]["value"] == 600
243
+
244
+ for i in range(1, 5):
245
+ mod = modules[i]["standardImageTextOverlay"]
246
+ assert mod["overlayColorType"] == "DARK"
247
+ assert "block" in mod
248
+ assert "headline" in mod["block"]
249
+ assert "body" in mod["block"]
250
+ assert "textList" in mod["block"]["body"]
251
+ assert "image" in mod["block"]
252
+ assert mod["block"]["image"]["uploadDestinationId"].startswith("aplus-media/sc/")
253
+
151
254
 
152
255
  class TestAPlusCLI:
153
256
  @pytest.fixture
@@ -1,2 +0,0 @@
1
- # Amazon SP-API CLI
2
- __version__ = "0.2.3"
File without changes
File without changes
File without changes
File without changes