mpt-extension-sdk 5.6.1__py3-none-any.whl → 5.8.0__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.
Files changed (40) hide show
  1. mpt_extension_sdk/airtable/wrap_http_error.py +7 -4
  2. mpt_extension_sdk/constants.py +2 -0
  3. mpt_extension_sdk/core/events/dataclasses.py +1 -0
  4. mpt_extension_sdk/core/events/registry.py +5 -1
  5. mpt_extension_sdk/core/extension.py +1 -0
  6. mpt_extension_sdk/core/security.py +33 -26
  7. mpt_extension_sdk/core/utils.py +2 -0
  8. mpt_extension_sdk/flows/context.py +11 -1
  9. mpt_extension_sdk/flows/pipeline.py +7 -1
  10. mpt_extension_sdk/key_vault/base.py +35 -25
  11. mpt_extension_sdk/mpt_http/base.py +7 -3
  12. mpt_extension_sdk/mpt_http/mpt.py +79 -24
  13. mpt_extension_sdk/mpt_http/utils.py +1 -0
  14. mpt_extension_sdk/mpt_http/wrap_http_error.py +13 -6
  15. mpt_extension_sdk/runtime/__init__.py +1 -0
  16. mpt_extension_sdk/runtime/commands/django.py +3 -4
  17. mpt_extension_sdk/runtime/commands/run.py +0 -2
  18. mpt_extension_sdk/runtime/djapp/apps.py +5 -1
  19. mpt_extension_sdk/runtime/djapp/conf/__init__.py +2 -3
  20. mpt_extension_sdk/runtime/djapp/conf/default.py +10 -10
  21. mpt_extension_sdk/runtime/djapp/management/commands/consume_events.py +11 -7
  22. mpt_extension_sdk/runtime/djapp/middleware.py +4 -3
  23. mpt_extension_sdk/runtime/errors.py +4 -0
  24. mpt_extension_sdk/runtime/events/dispatcher.py +19 -11
  25. mpt_extension_sdk/runtime/events/producers.py +17 -6
  26. mpt_extension_sdk/runtime/events/utils.py +4 -2
  27. mpt_extension_sdk/runtime/initializer.py +3 -2
  28. mpt_extension_sdk/runtime/logging.py +9 -2
  29. mpt_extension_sdk/runtime/master.py +24 -13
  30. mpt_extension_sdk/runtime/swoext.py +9 -7
  31. mpt_extension_sdk/runtime/tracer.py +1 -0
  32. mpt_extension_sdk/runtime/utils.py +33 -18
  33. mpt_extension_sdk/runtime/workers.py +11 -6
  34. mpt_extension_sdk/swo_rql/query_builder.py +15 -12
  35. {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.8.0.dist-info}/METADATA +1 -1
  36. mpt_extension_sdk-5.8.0.dist-info/RECORD +54 -0
  37. mpt_extension_sdk-5.6.1.dist-info/RECORD +0 -53
  38. {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.8.0.dist-info}/WHEEL +0 -0
  39. {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.8.0.dist-info}/entry_points.txt +0 -0
  40. {mpt_extension_sdk-5.6.1.dist-info → mpt_extension_sdk-5.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -18,21 +18,22 @@ def _has_more_pages(page):
18
18
 
19
19
 
20
20
  def _paginated(mpt_client, url, limit=10):
21
- items = []
21
+ pages_items = []
22
22
  page = None
23
23
  offset = 0
24
24
  while _has_more_pages(page):
25
25
  response = mpt_client.get(f"{url}&limit={limit}&offset={offset}")
26
26
  response.raise_for_status()
27
27
  page = response.json()
28
- items.extend(page["data"])
28
+ pages_items.extend(page["data"])
29
29
  offset += limit
30
30
 
31
- return items
31
+ return pages_items
32
32
 
33
33
 
34
34
  @wrap_mpt_http_error
35
35
  def get_agreement(mpt_client, agreement_id):
36
+ """Retrieve an agreement by ID."""
36
37
  response = mpt_client.get(
37
38
  f"/commerce/agreements/{agreement_id}?select=seller,buyer,listing,product,subscriptions"
38
39
  )
@@ -42,6 +43,7 @@ def get_agreement(mpt_client, agreement_id):
42
43
 
43
44
  @wrap_mpt_http_error
44
45
  def get_licensee(mpt_client, licensee_id):
46
+ """Retrieve a licensee by ID."""
45
47
  response = mpt_client.get(f"/accounts/licensees/{licensee_id}")
46
48
  response.raise_for_status()
47
49
  return response.json()
@@ -49,6 +51,7 @@ def get_licensee(mpt_client, licensee_id):
49
51
 
50
52
  @wrap_mpt_http_error
51
53
  def update_order(mpt_client, order_id, **kwargs):
54
+ """Update an order."""
52
55
  response = mpt_client.put(
53
56
  f"/commerce/orders/{order_id}",
54
57
  json=kwargs,
@@ -59,6 +62,7 @@ def update_order(mpt_client, order_id, **kwargs):
59
62
 
60
63
  @wrap_mpt_http_error
61
64
  def query_order(mpt_client, order_id, **kwargs):
65
+ """Query an order."""
62
66
  response = mpt_client.post(
63
67
  f"/commerce/orders/{order_id}/query",
64
68
  json=kwargs,
@@ -69,6 +73,7 @@ def query_order(mpt_client, order_id, **kwargs):
69
73
 
70
74
  @wrap_mpt_http_error
71
75
  def fail_order(mpt_client, order_id, status_notes, **kwargs):
76
+ """Fail an order."""
72
77
  response = mpt_client.post(
73
78
  f"/commerce/orders/{order_id}/fail",
74
79
  json={
@@ -82,6 +87,7 @@ def fail_order(mpt_client, order_id, status_notes, **kwargs):
82
87
 
83
88
  @wrap_mpt_http_error
84
89
  def complete_order(mpt_client, order_id, template, **kwargs):
90
+ """Complete an order."""
85
91
  response = mpt_client.post(
86
92
  f"/commerce/orders/{order_id}/complete",
87
93
  json={"template": template, **kwargs},
@@ -92,6 +98,7 @@ def complete_order(mpt_client, order_id, template, **kwargs):
92
98
 
93
99
  @wrap_mpt_http_error
94
100
  def set_processing_template(mpt_client, order_id, template):
101
+ """Set the processing template for an order."""
95
102
  response = mpt_client.put(
96
103
  f"/commerce/orders/{order_id}",
97
104
  json={"template": template},
@@ -100,8 +107,25 @@ def set_processing_template(mpt_client, order_id, template):
100
107
  return response.json()
101
108
 
102
109
 
110
+ @wrap_mpt_http_error
111
+ def create_asset(mpt_client, order_id, asset):
112
+ """Create a new asset for an order."""
113
+ response = mpt_client.post(f"/commerce/orders/{order_id}/assets", json=asset)
114
+ response.raise_for_status()
115
+ return response.json()
116
+
117
+
118
+ @wrap_mpt_http_error
119
+ def update_asset(mpt_client, order_id, asset_id, **kwargs):
120
+ """Update an order asset."""
121
+ response = mpt_client.put(f"/commerce/orders/{order_id}/assets/{asset_id}", json=kwargs)
122
+ response.raise_for_status()
123
+ return response.json()
124
+
125
+
103
126
  @wrap_mpt_http_error
104
127
  def create_subscription(mpt_client, order_id, subscription):
128
+ """Create a new subscription for an order."""
105
129
  response = mpt_client.post(
106
130
  f"/commerce/orders/{order_id}/subscriptions",
107
131
  json=subscription,
@@ -112,6 +136,7 @@ def create_subscription(mpt_client, order_id, subscription):
112
136
 
113
137
  @wrap_mpt_http_error
114
138
  def update_subscription(mpt_client, order_id, subscription_id, **kwargs):
139
+ """Update an order subscription."""
115
140
  response = mpt_client.put(
116
141
  f"/commerce/orders/{order_id}/subscriptions/{subscription_id}",
117
142
  json=kwargs,
@@ -121,9 +146,8 @@ def update_subscription(mpt_client, order_id, subscription_id, **kwargs):
121
146
 
122
147
 
123
148
  @wrap_mpt_http_error
124
- def get_order_subscription_by_external_id(
125
- mpt_client, order_id, subscription_external_id
126
- ):
149
+ def get_order_subscription_by_external_id(mpt_client, order_id, subscription_external_id):
150
+ """Retrieve an order subscription by its external ID."""
127
151
  response = mpt_client.get(
128
152
  f"/commerce/orders/{order_id}/subscriptions?eq(externalIds.vendor,{subscription_external_id})&limit=1",
129
153
  )
@@ -131,13 +155,14 @@ def get_order_subscription_by_external_id(
131
155
  subscriptions = response.json()
132
156
  if subscriptions["$meta"]["pagination"]["total"] == 1:
133
157
  return subscriptions["data"][0]
158
+ return None
134
159
 
135
160
 
136
161
  @wrap_mpt_http_error
137
162
  def get_product_items_by_skus(mpt_client, product_id, skus):
138
- rql_query = (
139
- f"and(eq(product.id,{product_id}),in(externalIds.vendor,({','.join(skus)})))"
140
- )
163
+ """Retrieve product items by their SKUs."""
164
+ skus_str = ",".join(skus)
165
+ rql_query = f"and(eq(product.id,{product_id}),in(externalIds.vendor,({skus_str})))"
141
166
  url = f"/catalog/items?{rql_query}"
142
167
  return _paginated(mpt_client, url)
143
168
 
@@ -145,6 +170,7 @@ def get_product_items_by_skus(mpt_client, product_id, skus):
145
170
  @cache
146
171
  @wrap_mpt_http_error
147
172
  def get_webhook(mpt_client, webhook_id):
173
+ """Retrieve a webhook by ID."""
148
174
  response = mpt_client.get(f"/notifications/webhooks/{webhook_id}?select=criteria")
149
175
  response.raise_for_status()
150
176
 
@@ -153,6 +179,7 @@ def get_webhook(mpt_client, webhook_id):
153
179
 
154
180
  @wrap_mpt_http_error
155
181
  def get_product_template_or_default(mpt_client, product_id, status, name=None):
182
+ """Retrieve a product template by ID or return the default template."""
156
183
  name_or_default_filter = "eq(default,true)"
157
184
  if name:
158
185
  name_or_default_filter = f"or({name_or_default_filter},eq(name,{name}))"
@@ -165,6 +192,7 @@ def get_product_template_or_default(mpt_client, product_id, status, name=None):
165
192
 
166
193
 
167
194
  def get_template_by_name(mpt_client, product_id, template_name):
195
+ """Retrieve a product template by name."""
168
196
  url = f"/catalog/products/{product_id}/templates?eq(name,{template_name})"
169
197
  response = mpt_client.get(url)
170
198
  response.raise_for_status()
@@ -174,6 +202,7 @@ def get_template_by_name(mpt_client, product_id, template_name):
174
202
 
175
203
  @wrap_mpt_http_error
176
204
  def update_agreement(mpt_client, agreement_id, **kwargs):
205
+ """Update an agreement."""
177
206
  response = mpt_client.put(
178
207
  f"/commerce/agreements/{agreement_id}",
179
208
  json=kwargs,
@@ -184,6 +213,7 @@ def update_agreement(mpt_client, agreement_id, **kwargs):
184
213
 
185
214
  @wrap_mpt_http_error
186
215
  def get_agreements_by_query(mpt_client: MPTClient, query: str, limit: int = 10):
216
+ """Retrieve agreements by query."""
187
217
  url = f"/commerce/agreements?{query}"
188
218
  return _paginated(mpt_client, url, limit=limit)
189
219
 
@@ -194,12 +224,14 @@ def get_subscriptions_by_query(
194
224
  query: str,
195
225
  limit: int = 10,
196
226
  ) -> list[dict]:
227
+ """Retrieve subscriptions by query."""
197
228
  url = f"/commerce/subscriptions?{query}"
198
229
  return _paginated(mpt_client, url, limit=limit)
199
230
 
200
231
 
201
232
  @wrap_mpt_http_error
202
233
  def update_agreement_subscription(mpt_client, subscription_id, **kwargs):
234
+ """Update an agreement subscription."""
203
235
  response = mpt_client.put(
204
236
  f"/commerce/subscriptions/{subscription_id}",
205
237
  json=kwargs,
@@ -210,6 +242,7 @@ def update_agreement_subscription(mpt_client, subscription_id, **kwargs):
210
242
 
211
243
  @wrap_mpt_http_error
212
244
  def get_agreement_subscription(mpt_client, subscription_id):
245
+ """Retrieve an agreement subscription by ID."""
213
246
  response = mpt_client.get(
214
247
  f"/commerce/subscriptions/{subscription_id}",
215
248
  )
@@ -219,6 +252,7 @@ def get_agreement_subscription(mpt_client, subscription_id):
219
252
 
220
253
  @wrap_mpt_http_error
221
254
  def get_rendered_template(mpt_client, order_id):
255
+ """Retrieve the rendered template for an order."""
222
256
  response = mpt_client.get(
223
257
  f"/commerce/orders/{order_id}/template",
224
258
  )
@@ -228,17 +262,31 @@ def get_rendered_template(mpt_client, order_id):
228
262
 
229
263
  @wrap_mpt_http_error
230
264
  def get_product_onetime_items_by_ids(mpt_client, product_id, item_ids):
265
+ """Retrieve one-time product items by their IDs."""
266
+ item_ids_str = ",".join(item_ids)
231
267
  product_cond = f"eq(product.id,{product_id})"
232
- items_cond = f"in(id,({','.join(item_ids)}))"
268
+ items_cond = f"in(id,({item_ids_str}))"
233
269
  rql_query = f"and({product_cond},{items_cond},eq(terms.period,one-time))"
234
270
  url = f"/catalog/items?{rql_query}"
235
271
 
236
272
  return _paginated(mpt_client, url)
237
273
 
238
274
 
275
+ @wrap_mpt_http_error
276
+ def get_product_items_by_period(mpt_client, product_id, period):
277
+ """Fetches one-time items for a product."""
278
+ product_cond = f"eq(product.id,{product_id})"
279
+ rql_query = f"and({product_cond},eq(terms.period,{period}))"
280
+ url = f"/catalog/items?{rql_query}"
281
+
282
+ return _paginated(mpt_client, url)
283
+
284
+
239
285
  def get_agreements_by_ids(mpt_client, ids):
286
+ """Retrieve agreements by their IDs."""
287
+ ids_str = ",".join(ids)
240
288
  rql_query = (
241
- f"and(in(id,({','.join(ids)})),eq(status,Active))"
289
+ f"and(in(id,({ids_str})),eq(status,Active))"
242
290
  "&select=lines,parameters,subscriptions,product,listing"
243
291
  )
244
292
  return get_agreements_by_query(mpt_client, rql_query)
@@ -247,7 +295,9 @@ def get_agreements_by_ids(mpt_client, ids):
247
295
  def get_all_agreements(
248
296
  mpt_client,
249
297
  ):
250
- product_condition = f"in(product.id,({','.join(settings.MPT_PRODUCTS_IDS)}))"
298
+ """Retrieve all active agreements for specific products."""
299
+ product_ids_str = ",".join(settings.MPT_PRODUCTS_IDS)
300
+ product_condition = f"in(product.id,({product_ids_str}))"
251
301
 
252
302
  return get_agreements_by_query(
253
303
  mpt_client,
@@ -256,9 +306,8 @@ def get_all_agreements(
256
306
 
257
307
 
258
308
  @wrap_mpt_http_error
259
- def get_authorizations_by_currency_and_seller_id(
260
- mpt_client, product_id, currency, owner_id
261
- ):
309
+ def get_authorizations_by_currency_and_seller_id(mpt_client, product_id, currency, owner_id):
310
+ """Retrieve authorizations by product ID, currency, and owner ID."""
262
311
  authorization_filter = (
263
312
  f"eq(product.id,{product_id})&eq(currency,{currency})&eq(owner.id,{owner_id})"
264
313
  )
@@ -269,6 +318,7 @@ def get_authorizations_by_currency_and_seller_id(
269
318
 
270
319
  @wrap_mpt_http_error
271
320
  def get_gc_price_list_by_currency(mpt_client, product_id, currency):
321
+ """Retrieve a GC price list by product ID and currency."""
272
322
  response = mpt_client.get(
273
323
  f"/catalog/price-lists?eq(product.id,{product_id})&eq(currency,{currency})"
274
324
  )
@@ -280,6 +330,7 @@ def get_gc_price_list_by_currency(mpt_client, product_id, currency):
280
330
  def get_listings_by_price_list_and_seller_and_authorization(
281
331
  mpt_client, product_id, price_list_id, seller_id, authorization_id
282
332
  ):
333
+ """Retrieve listings by price list, seller, and authorization."""
283
334
  response = mpt_client.get(
284
335
  f"/catalog/listings?eq(product.id,{product_id})&eq(priceList.id,{price_list_id})"
285
336
  f"&eq(seller.id,{seller_id})"
@@ -291,6 +342,7 @@ def get_listings_by_price_list_and_seller_and_authorization(
291
342
 
292
343
  @wrap_mpt_http_error
293
344
  def create_listing(mpt_client, listing):
345
+ """Create a new listing."""
294
346
  response = mpt_client.post(
295
347
  "/catalog/listings",
296
348
  json=listing,
@@ -301,6 +353,7 @@ def create_listing(mpt_client, listing):
301
353
 
302
354
  @wrap_mpt_http_error
303
355
  def create_agreement(mpt_client, agreement):
356
+ """Create a new agreement."""
304
357
  response = mpt_client.post(
305
358
  "/commerce/agreements",
306
359
  json=agreement,
@@ -311,6 +364,7 @@ def create_agreement(mpt_client, agreement):
311
364
 
312
365
  @wrap_mpt_http_error
313
366
  def create_agreement_subscription(mpt_client, subscription):
367
+ """Create a new agreement subscription."""
314
368
  response = mpt_client.post(
315
369
  "/commerce/subscriptions",
316
370
  json=subscription,
@@ -321,15 +375,15 @@ def create_agreement_subscription(mpt_client, subscription):
321
375
 
322
376
  @wrap_mpt_http_error
323
377
  def get_listing_by_id(mpt_client, listing_id):
378
+ """Retrieve a listing by ID."""
324
379
  response = mpt_client.get(f"/catalog/listings/{listing_id}")
325
380
  response.raise_for_status()
326
381
  return response.json()
327
382
 
328
383
 
329
384
  @wrap_mpt_http_error
330
- def get_agreement_subscription_by_external_id(
331
- mpt_client, agreement_id, subscription_external_id
332
- ):
385
+ def get_agreement_subscription_by_external_id(mpt_client, agreement_id, subscription_external_id):
386
+ """Retrieve an agreement subscription by external ID."""
333
387
  response = mpt_client.get(
334
388
  f"/commerce/subscriptions?eq(externalIds.vendor,{subscription_external_id})"
335
389
  f"&eq(agreement.id,{agreement_id})"
@@ -344,6 +398,7 @@ def get_agreement_subscription_by_external_id(
344
398
 
345
399
  @wrap_mpt_http_error
346
400
  def get_agreements_by_external_id_values(mpt_client, external_id, display_values):
401
+ """Retrieve agreements by external ID and display values."""
347
402
  display_values_list = ",".join(display_values)
348
403
  rql_query = (
349
404
  f"any(parameters.fulfillment,and("
@@ -358,9 +413,8 @@ def get_agreements_by_external_id_values(mpt_client, external_id, display_values
358
413
 
359
414
 
360
415
  @wrap_mpt_http_error
361
- def get_agreements_by_customer_deployments(
362
- mpt_client, deployment_id_parameter, deployment_ids
363
- ):
416
+ def get_agreements_by_customer_deployments(mpt_client, deployment_id_parameter, deployment_ids):
417
+ """Retrieve agreements by customer deployments."""
364
418
  deployments_list = ",".join(deployment_ids)
365
419
  rql_query = (
366
420
  f"any(parameters.fulfillment,and("
@@ -376,6 +430,7 @@ def get_agreements_by_customer_deployments(
376
430
 
377
431
  @wrap_mpt_http_error
378
432
  def get_buyer(mpt_client, buyer_id):
433
+ """Retrieve a buyer by ID."""
379
434
  response = mpt_client.get(f"/accounts/buyers/{buyer_id}")
380
435
  response.raise_for_status()
381
436
  return response.json()
@@ -392,6 +447,8 @@ def notify(
392
447
  limit: int = 1000,
393
448
  ) -> None:
394
449
  """
450
+ Sends notification to multiple recipients for a specific buyer.
451
+
395
452
  Sends notifications to multiple recipients in batches for a specific buyer and
396
453
  category through the MPTClient service. The function retrieves recipients,
397
454
  groups them into manageable batches, and sends notifications using the provided
@@ -422,9 +479,7 @@ def notify(
422
479
 
423
480
 
424
481
  @wrap_mpt_http_error
425
- def terminate_subscription(
426
- mpt_client: MPTClient, subscription_id: str, reason: str
427
- ) -> dict:
482
+ def terminate_subscription(mpt_client: MPTClient, subscription_id: str, reason: str) -> dict:
428
483
  """
429
484
  Terminates a subscription by calling the MPT API.
430
485
 
@@ -1,2 +1,3 @@
1
1
  def find_first(func, iterable, default=None):
2
+ """Find the first item in an iterable that matches a predicate."""
2
3
  return next(filter(func, iterable), default)
@@ -5,10 +5,11 @@ from requests import HTTPError, JSONDecodeError
5
5
 
6
6
 
7
7
  class MPTError(Exception):
8
- pass
8
+ """Represents a generic MPT error."""
9
9
 
10
10
 
11
11
  class MPTHttpError(MPTError):
12
+ """Represents an HTTP error."""
12
13
  def __init__(self, status_code: int, content: str):
13
14
  self.status_code = status_code
14
15
  self.content = content
@@ -16,6 +17,7 @@ class MPTHttpError(MPTError):
16
17
 
17
18
 
18
19
  class MPTAPIError(MPTHttpError):
20
+ """Represents an API error."""
19
21
  def __init__(self, status_code, payload):
20
22
  super().__init__(status_code, json.dumps(payload))
21
23
  self.payload = payload
@@ -37,25 +39,30 @@ class MPTAPIError(MPTHttpError):
37
39
 
38
40
 
39
41
  def wrap_mpt_http_error(func):
42
+ """Wrap a function to catch MPT HTTP errors."""
40
43
  @wraps(func)
41
44
  def _wrapper(*args, **kwargs):
42
45
  try:
43
46
  return func(*args, **kwargs)
44
- except HTTPError as e:
47
+ except HTTPError as err:
48
+ response = err.response
45
49
  try:
46
- raise MPTAPIError(e.response.status_code, e.response.json())
50
+ payload = response.json()
47
51
  except JSONDecodeError:
48
- raise MPTHttpError(e.response.status_code, e.response.content.decode())
52
+ raise MPTHttpError(response.status_code, response.content.decode()) from err
53
+ raise MPTAPIError(response.status_code, payload) from err
49
54
 
50
55
  return _wrapper
51
56
 
52
57
 
53
58
  class ValidationError:
54
- def __init__(self, id, message):
55
- self.id = id
59
+ """Represents a validation error."""
60
+ def __init__(self, err_id, message):
61
+ self.id = err_id
56
62
  self.message = message
57
63
 
58
64
  def to_dict(self, **kwargs):
65
+ """Convert the validation error to a dictionary."""
59
66
  return {
60
67
  "id": self.id,
61
68
  "message": self.message.format(**kwargs),
@@ -7,4 +7,5 @@ except Exception: # pragma: no cover
7
7
 
8
8
 
9
9
  def get_version():
10
+ """Get the current version of the package."""
10
11
  return __version__
@@ -19,9 +19,8 @@ from mpt_extension_sdk.runtime.utils import initialize_extension
19
19
  @click.argument("management_args", nargs=-1, type=click.UNPROCESSED)
20
20
  @click.pass_context
21
21
  def django(ctx, management_args): # pragma: no cover
22
- "Execute Django subcommands."
23
-
24
- from django.conf import settings
22
+ """Execute Django subcommands."""
23
+ from django.conf import settings # noqa: PLC0415
25
24
 
26
25
  options = {
27
26
  "group": DEFAULT_APP_CONFIG_GROUP,
@@ -40,4 +39,4 @@ def django(ctx, management_args): # pragma: no cover
40
39
  tracer_context = nullcontext()
41
40
 
42
41
  with tracer_context:
43
- execute_from_command_line(argv=[ctx.command_path] + list(management_args))
42
+ execute_from_command_line(argv=[ctx.command_path, *management_args])
@@ -19,13 +19,11 @@ from mpt_extension_sdk.runtime.master import Master
19
19
  def run(component, color, debug, reload, debug_py):
20
20
  """Run the extension.
21
21
 
22
- \b
23
22
  COMPONENT is the the name of the component to run. Possible values:
24
23
  * all - run both API and Event Consumer threads (default)
25
24
  * api - run only API thread
26
25
  * consumer - run only Event Consumer thread
27
26
  """
28
-
29
27
  if debug_py:
30
28
  host, port = debug_py.split(":")
31
29
  debugpy.listen((host, int(port))) # noqa
@@ -8,9 +8,11 @@ ext = Extension()
8
8
 
9
9
 
10
10
  class DjAppConfig(AppConfig):
11
+ """Django AppConfig for the extension."""
11
12
  name = "mpt_extension_sdk.runtime.djapp"
12
13
 
13
14
  def ready(self):
15
+ """Check if the extension is properly configured."""
14
16
  if not hasattr(settings, "MPT_PRODUCTS_IDS") or not settings.MPT_PRODUCTS_IDS:
15
17
  raise ImproperlyConfigured(
16
18
  f"Extension {self.verbose_name} is not properly configured."
@@ -20,15 +22,17 @@ class DjAppConfig(AppConfig):
20
22
  self.extension_ready()
21
23
 
22
24
  def extension_ready(self):
23
- pass
25
+ """Perform actions when the extension is ready."""
24
26
 
25
27
 
26
28
  class ExtensionConfig(DjAppConfig):
29
+ """Configuration for the extension."""
27
30
  name = "mpt_extension_sdk"
28
31
  verbose_name = "SWO Extension SDK"
29
32
  extension = ext
30
33
 
31
34
  def extension_ready(self):
35
+ """Perform actions when the extension is ready."""
32
36
  error_msgs = []
33
37
 
34
38
  for product_id in settings.MPT_PRODUCTS_IDS:
@@ -2,11 +2,10 @@ from typing import Any
2
2
 
3
3
 
4
4
  def extract_product_ids(product_ids: str) -> list[str]:
5
+ """Extract product IDs from a comma-separated string."""
5
6
  return product_ids.split(",")
6
7
 
7
8
 
8
9
  def get_for_product(settings, variable_name: str, product_id: str) -> Any:
9
- """
10
- A shortcut to return product scoped variable from the extension settings.
11
- """
10
+ """A shortcut to return product scoped variable from the extension settings."""
12
11
  return settings.EXTENSION_CONFIG[variable_name][product_id]
@@ -14,9 +14,9 @@ import os
14
14
  from pathlib import Path
15
15
 
16
16
  from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
17
- from opentelemetry._logs import set_logger_provider
18
- from opentelemetry.sdk._logs import LoggerProvider
19
- from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
17
+ from opentelemetry._logs import set_logger_provider # noqa: PLC2701
18
+ from opentelemetry.sdk._logs import LoggerProvider # noqa: PLC2701
19
+ from opentelemetry.sdk._logs.export import BatchLogRecordProcessor # noqa: PLC2701
20
20
 
21
21
  # Build paths inside the project like this: BASE_DIR / 'subdir'.
22
22
  BASE_DIR = Path(__file__).resolve().parent.parent
@@ -34,12 +34,12 @@ SECRET_KEY = os.getenv(
34
34
  # SECURITY WARNING: don't run with debug turned on in production!
35
35
  DEBUG = True
36
36
 
37
- ALLOWED_HOSTS = ["*"]
37
+ ALLOWED_HOSTS = ("*")
38
38
 
39
39
 
40
40
  # Application definition
41
41
 
42
- INSTALLED_APPS = [
42
+ INSTALLED_APPS = (
43
43
  "django.contrib.admin",
44
44
  "django.contrib.auth",
45
45
  "django.contrib.contenttypes",
@@ -47,9 +47,9 @@ INSTALLED_APPS = [
47
47
  "django.contrib.messages",
48
48
  "django.contrib.staticfiles",
49
49
  "mpt_extension_sdk.runtime.djapp.apps.DjAppConfig",
50
- ]
50
+ )
51
51
 
52
- MIDDLEWARE = [
52
+ MIDDLEWARE = (
53
53
  "django.middleware.security.SecurityMiddleware",
54
54
  "django.contrib.sessions.middleware.SessionMiddleware",
55
55
  "django.middleware.common.CommonMiddleware",
@@ -58,7 +58,7 @@ MIDDLEWARE = [
58
58
  "django.contrib.messages.middleware.MessageMiddleware",
59
59
  "django.middleware.clickjacking.XFrameOptionsMiddleware",
60
60
  "mpt_extension_sdk.runtime.djapp.middleware.MPTClientMiddleware",
61
- ]
61
+ )
62
62
 
63
63
  ROOT_URLCONF = "mpt_extension_sdk.runtime.djapp.conf.urls"
64
64
 
@@ -130,7 +130,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
130
130
  APPLICATIONINSIGHTS_CONNECTION_STRING = os.getenv(
131
131
  "APPLICATIONINSIGHTS_CONNECTION_STRING", ""
132
132
  )
133
- USE_APPLICATIONINSIGHTS = APPLICATIONINSIGHTS_CONNECTION_STRING != ""
133
+ USE_APPLICATIONINSIGHTS = bool(APPLICATIONINSIGHTS_CONNECTION_STRING)
134
134
 
135
135
 
136
136
  if USE_APPLICATIONINSIGHTS: # pragma: no cover
@@ -166,7 +166,7 @@ LOGGING = {
166
166
  "rich": {
167
167
  "class": "mpt_extension_sdk.runtime.logging.RichHandler",
168
168
  "formatter": "rich",
169
- "log_time_format": lambda x: x.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
169
+ "log_time_format": lambda log_time: log_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
170
170
  "rich_tracebacks": True,
171
171
  },
172
172
  "opentelemetry": {
@@ -1,5 +1,6 @@
1
1
  import signal # pragma: no cover
2
2
  from threading import Event # pragma: no cover
3
+ from typing import ClassVar # pragma: no cover
3
4
 
4
5
  from django.core.management.base import BaseCommand # pragma: no cover
5
6
 
@@ -11,22 +12,21 @@ from mpt_extension_sdk.runtime.events.producers import (
11
12
 
12
13
 
13
14
  class Command(BaseCommand): # pragma: no cover
15
+ """Command to consume events."""
14
16
  help = CONSUME_EVENTS_HELP_TEXT
15
- producer_classes = [
17
+ producer_classes: ClassVar[list] = [
16
18
  OrderEventProducer,
17
19
  ]
18
- producers = []
20
+ producers: ClassVar[list] = []
19
21
 
20
22
  def handle(self, *args, **options):
23
+ """Handle the command."""
21
24
  self.shutdown_event = Event()
22
25
  self.dispatcher = Dispatcher()
23
26
  self.dispatcher.start()
24
27
 
25
- def shutdown(signum, frame):
26
- self.shutdown_event.set()
27
-
28
- signal.signal(signal.SIGTERM, shutdown)
29
- signal.signal(signal.SIGINT, shutdown)
28
+ signal.signal(signal.SIGTERM, self.shutdown)
29
+ signal.signal(signal.SIGINT, self.shutdown)
30
30
  for producer_cls in self.producer_classes:
31
31
  producer = producer_cls(self.dispatcher)
32
32
  self.producers.append(producer)
@@ -36,3 +36,7 @@ class Command(BaseCommand): # pragma: no cover
36
36
  for producer in self.producers:
37
37
  producer.stop()
38
38
  self.dispatcher.stop()
39
+
40
+ def shutdown(self, signum, frame):
41
+ """Shutdown the command."""
42
+ self.shutdown_event.set()
@@ -6,16 +6,17 @@ _CLIENT = None
6
6
 
7
7
 
8
8
  class MPTClientMiddleware: # pragma: no cover
9
+ """Middleware to set up MPTClient for each request."""
9
10
  def __init__(self, get_response):
10
11
  self.get_response = get_response
11
12
 
12
13
  def __call__(self, request):
13
- global _CLIENT
14
+ """Set up MPTClient for the request."""
15
+ global _CLIENT # noqa: PLW0603
14
16
  if not _CLIENT:
15
17
  _CLIENT = MPTClient(
16
18
  f"{settings.MPT_API_BASE_URL}/v1/",
17
19
  settings.MPT_API_TOKEN,
18
20
  )
19
21
  request.client = _CLIENT
20
- response = self.get_response(request)
21
- return response
22
+ return self.get_response(request)
@@ -0,0 +1,4 @@
1
+ # Custom exception for variable format errors
2
+
3
+ class VariableNotWellFormedError(Exception):
4
+ """Raised when a variable is not well-formed."""