gr4vy 1.7.16__py3-none-any.whl → 1.7.17__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.
gr4vy/_version.py CHANGED
@@ -3,10 +3,10 @@
3
3
  import importlib.metadata
4
4
 
5
5
  __title__: str = "gr4vy"
6
- __version__: str = "1.7.16"
6
+ __version__: str = "1.7.17"
7
7
  __openapi_doc_version__: str = "1.0.0"
8
- __gen_version__: str = "2.760.2"
9
- __user_agent__: str = "speakeasy-sdk/python 1.7.16 2.760.2 1.0.0 gr4vy"
8
+ __gen_version__: str = "2.763.2"
9
+ __user_agent__: str = "speakeasy-sdk/python 1.7.17 2.763.2 1.0.0 gr4vy"
10
10
 
11
11
  try:
12
12
  if __package__ is not None:
@@ -16,9 +16,10 @@ from gr4vy.utils import (
16
16
  HeaderMetadata,
17
17
  PathParamMetadata,
18
18
  RequestMetadata,
19
+ get_discriminator,
19
20
  )
20
21
  import pydantic
21
- from pydantic import model_serializer
22
+ from pydantic import Discriminator, Tag, model_serializer
22
23
  from typing import List, Optional, Union
23
24
  from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
24
25
 
@@ -109,7 +110,22 @@ ResponseCaptureTransactionTypedDict = TypeAliasType(
109
110
  r"""Successful Response"""
110
111
 
111
112
 
112
- ResponseCaptureTransaction = TypeAliasType(
113
- "ResponseCaptureTransaction", Union[TransactionCaptureOutput, TransactionOutput]
114
- )
113
+ ResponseCaptureTransaction = Annotated[
114
+ Union[
115
+ Annotated[TransactionOutput, Tag("processing")],
116
+ Annotated[TransactionOutput, Tag("authorization_succeeded")],
117
+ Annotated[TransactionOutput, Tag("authorization_declined")],
118
+ Annotated[TransactionOutput, Tag("authorization_failed")],
119
+ Annotated[TransactionOutput, Tag("authorization_voided")],
120
+ Annotated[TransactionOutput, Tag("authorization_void_pending")],
121
+ Annotated[TransactionOutput, Tag("capture_succeeded")],
122
+ Annotated[TransactionOutput, Tag("capture_pending")],
123
+ Annotated[TransactionOutput, Tag("buyer_approval_pending")],
124
+ Annotated[TransactionCaptureOutput, Tag("succeeded")],
125
+ Annotated[TransactionCaptureOutput, Tag("pending")],
126
+ Annotated[TransactionCaptureOutput, Tag("declined")],
127
+ Annotated[TransactionCaptureOutput, Tag("failed")],
128
+ ],
129
+ Discriminator(lambda m: get_discriminator(m, "status", "status")),
130
+ ]
115
131
  r"""Successful Response"""
@@ -14,6 +14,8 @@ class CybersourceOptionsTypedDict(TypedDict):
14
14
  r"""A list of merchant defined data to be passed to the Cybersource. Each key needs to be a numeric string."""
15
15
  ship_to_method: NotRequired[Nullable[str]]
16
16
  r"""The shipping method for this transaction."""
17
+ comments: NotRequired[Nullable[str]]
18
+ r"""Brief description of the order or any comment you wish to add to the order."""
17
19
 
18
20
 
19
21
  class CybersourceOptions(BaseModel):
@@ -26,17 +28,22 @@ class CybersourceOptions(BaseModel):
26
28
  ship_to_method: OptionalNullable[str] = UNSET
27
29
  r"""The shipping method for this transaction."""
28
30
 
31
+ comments: OptionalNullable[str] = UNSET
32
+ r"""Brief description of the order or any comment you wish to add to the order."""
33
+
29
34
  @model_serializer(mode="wrap")
30
35
  def serialize_model(self, handler):
31
36
  optional_fields = [
32
37
  "meta_key_merchant_id",
33
38
  "merchant_defined_information",
34
39
  "ship_to_method",
40
+ "comments",
35
41
  ]
36
42
  nullable_fields = [
37
43
  "meta_key_merchant_id",
38
44
  "merchant_defined_information",
39
45
  "ship_to_method",
46
+ "comments",
40
47
  ]
41
48
  null_default_fields = []
42
49
 
@@ -7,9 +7,14 @@ from .transactionvoid_output import (
7
7
  TransactionVoidOutputTypedDict,
8
8
  )
9
9
  from gr4vy.types import BaseModel, Nullable, OptionalNullable, UNSET, UNSET_SENTINEL
10
- from gr4vy.utils import FieldMetadata, HeaderMetadata, PathParamMetadata
10
+ from gr4vy.utils import (
11
+ FieldMetadata,
12
+ HeaderMetadata,
13
+ PathParamMetadata,
14
+ get_discriminator,
15
+ )
11
16
  import pydantic
12
- from pydantic import model_serializer
17
+ from pydantic import Discriminator, Tag, model_serializer
13
18
  from typing import List, Optional, Union
14
19
  from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
15
20
 
@@ -94,7 +99,22 @@ ResponseVoidTransactionTypedDict = TypeAliasType(
94
99
  r"""Successful Response"""
95
100
 
96
101
 
97
- ResponseVoidTransaction = TypeAliasType(
98
- "ResponseVoidTransaction", Union[TransactionVoidOutput, TransactionOutput]
99
- )
102
+ ResponseVoidTransaction = Annotated[
103
+ Union[
104
+ Annotated[TransactionOutput, Tag("processing")],
105
+ Annotated[TransactionOutput, Tag("authorization_succeeded")],
106
+ Annotated[TransactionOutput, Tag("authorization_declined")],
107
+ Annotated[TransactionOutput, Tag("authorization_failed")],
108
+ Annotated[TransactionOutput, Tag("authorization_voided")],
109
+ Annotated[TransactionOutput, Tag("authorization_void_pending")],
110
+ Annotated[TransactionOutput, Tag("capture_succeeded")],
111
+ Annotated[TransactionOutput, Tag("capture_pending")],
112
+ Annotated[TransactionOutput, Tag("buyer_approval_pending")],
113
+ Annotated[TransactionVoidOutput, Tag("succeeded")],
114
+ Annotated[TransactionVoidOutput, Tag("pending")],
115
+ Annotated[TransactionVoidOutput, Tag("declined")],
116
+ Annotated[TransactionVoidOutput, Tag("failed")],
117
+ ],
118
+ Discriminator(lambda m: get_discriminator(m, "status", "status")),
119
+ ]
100
120
  r"""Successful Response"""
gr4vy/utils/retries.py CHANGED
@@ -3,7 +3,9 @@
3
3
  import asyncio
4
4
  import random
5
5
  import time
6
- from typing import List
6
+ from datetime import datetime
7
+ from email.utils import parsedate_to_datetime
8
+ from typing import List, Optional
7
9
 
8
10
  import httpx
9
11
 
@@ -51,9 +53,11 @@ class Retries:
51
53
 
52
54
  class TemporaryError(Exception):
53
55
  response: httpx.Response
56
+ retry_after: Optional[int]
54
57
 
55
58
  def __init__(self, response: httpx.Response):
56
59
  self.response = response
60
+ self.retry_after = _parse_retry_after_header(response)
57
61
 
58
62
 
59
63
  class PermanentError(Exception):
@@ -63,6 +67,62 @@ class PermanentError(Exception):
63
67
  self.inner = inner
64
68
 
65
69
 
70
+ def _parse_retry_after_header(response: httpx.Response) -> Optional[int]:
71
+ """Parse Retry-After header from response.
72
+
73
+ Returns:
74
+ Retry interval in milliseconds, or None if header is missing or invalid.
75
+ """
76
+ retry_after_header = response.headers.get("retry-after")
77
+ if not retry_after_header:
78
+ return None
79
+
80
+ try:
81
+ seconds = float(retry_after_header)
82
+ return round(seconds * 1000)
83
+ except ValueError:
84
+ pass
85
+
86
+ try:
87
+ retry_date = parsedate_to_datetime(retry_after_header)
88
+ delta = (retry_date - datetime.now(retry_date.tzinfo)).total_seconds()
89
+ return round(max(0, delta) * 1000)
90
+ except (ValueError, TypeError):
91
+ pass
92
+
93
+ return None
94
+
95
+
96
+ def _get_sleep_interval(
97
+ exception: Exception,
98
+ initial_interval: int,
99
+ max_interval: int,
100
+ exponent: float,
101
+ retries: int,
102
+ ) -> float:
103
+ """Get sleep interval for retry with exponential backoff.
104
+
105
+ Args:
106
+ exception: The exception that triggered the retry.
107
+ initial_interval: Initial retry interval in milliseconds.
108
+ max_interval: Maximum retry interval in milliseconds.
109
+ exponent: Base for exponential backoff calculation.
110
+ retries: Current retry attempt count.
111
+
112
+ Returns:
113
+ Sleep interval in seconds.
114
+ """
115
+ if (
116
+ isinstance(exception, TemporaryError)
117
+ and exception.retry_after is not None
118
+ and exception.retry_after > 0
119
+ ):
120
+ return exception.retry_after / 1000
121
+
122
+ sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
123
+ return min(sleep, max_interval / 1000)
124
+
125
+
66
126
  def retry(func, retries: Retries):
67
127
  if retries.config.strategy == "backoff":
68
128
 
@@ -183,8 +243,10 @@ def retry_with_backoff(
183
243
  return exception.response
184
244
 
185
245
  raise
186
- sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
187
- sleep = min(sleep, max_interval / 1000)
246
+
247
+ sleep = _get_sleep_interval(
248
+ exception, initial_interval, max_interval, exponent, retries
249
+ )
188
250
  time.sleep(sleep)
189
251
  retries += 1
190
252
 
@@ -211,7 +273,9 @@ async def retry_with_backoff_async(
211
273
  return exception.response
212
274
 
213
275
  raise
214
- sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
215
- sleep = min(sleep, max_interval / 1000)
276
+
277
+ sleep = _get_sleep_interval(
278
+ exception, initial_interval, max_interval, exponent, retries
279
+ )
216
280
  await asyncio.sleep(sleep)
217
281
  retries += 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gr4vy
3
- Version: 1.7.16
3
+ Version: 1.7.17
4
4
  Summary: Python Client SDK Generated by Speakeasy.
5
5
  Author: Gr4vy
6
6
  Requires-Python: >=3.9.2
@@ -2,7 +2,7 @@ gr4vy/__init__.py,sha256=w2u919V3Tzv4zEPQ-OYJ79gQ_4_SyW7GOFFoHtqXDFA,401
2
2
  gr4vy/_hooks/__init__.py,sha256=p5J13DeYuISQyQWirjJAObHIf2VtIlOtFqnIpvjjVwk,118
3
3
  gr4vy/_hooks/sdkhooks.py,sha256=3jKTs2B1lcAxBMJge9C-qL0RGbKGLcrHvikzi67Tbdo,2493
4
4
  gr4vy/_hooks/types.py,sha256=0O7dbbolkiFAnHkNULvwoLsiXJu0_Wmhev163bvZbW8,3039
5
- gr4vy/_version.py,sha256=6e5v79drDtyfUzE2ZcxTX37i-PnIrILNHshb_4Ac4LM,454
5
+ gr4vy/_version.py,sha256=e74QfN3Lul59RScJDtHmTZInJPHWNoZQJaZVCL4BTD0,454
6
6
  gr4vy/account_updater.py,sha256=mmTd25Oap80PBqQ3p4MvZ_buT5VS0zWc8s8cqfI7iyA,607
7
7
  gr4vy/all.py,sha256=jZtRqz8D1xGrylI95KA2XNwWLVAID1o_AYxA9wpXTzs,15176
8
8
  gr4vy/audit_logs.py,sha256=U16uKNF7H514ZBM0cAeSz2U2JT4P-SPGIJU6d6KOH34,17087
@@ -83,7 +83,7 @@ gr4vy/models/buyers.py,sha256=QJLFFT-l3RUGww78h-BqYTt7Mr-fLUs9VDrK2wl3naU,2133
83
83
  gr4vy/models/buyerupdate.py,sha256=tiPp2oKZYemrRp0uCt0sO6x-nlKNmtvIIzTmRCBe_h4,2567
84
84
  gr4vy/models/cancel_transactionop.py,sha256=RBsqNruvMIWAHkhPT_s4GPu3pWlf_NfKJWNXc9HrvxI,1511
85
85
  gr4vy/models/cancelstatus.py,sha256=9yb-oK8IL6apWdp1wmZBAqP9TYrz8vXG4Bbc3rQrXGE,307
86
- gr4vy/models/capture_transactionop.py,sha256=3qN-bZnkJeL9Vt9rANCRxTSsfg8F3gqOQn6CqfgNzQw,3801
86
+ gr4vy/models/capture_transactionop.py,sha256=KcXJIc7eaHt2GMSWRUCE-r0m1eVS-f9U2yJXowgZ4cY,4688
87
87
  gr4vy/models/capturestatus.py,sha256=hyWAsFmt13M-veZi2e44itOuA-1GT3XUw3ybzYANoH0,328
88
88
  gr4vy/models/cardpaymentmethodcreate.py,sha256=WQPt3NYDIPV0lCDQ8rB4Tqzu9JNkh0_UrgwJZeCI6SQ,3498
89
89
  gr4vy/models/cardscheme.py,sha256=-kRYwYRl4KUkTuUR23IJCOs3EHp35RHryp7769xruvg,698
@@ -128,7 +128,7 @@ gr4vy/models/cryptogram.py,sha256=dBVn-OSRioaHJMiIfxmBmoVlgWyLc3s30HV6icB0c3o,94
128
128
  gr4vy/models/cryptogramcreate.py,sha256=pmsbZ39jzdrRAnWR0flBcITzPT9QcIJOYp9k0znxic8,451
129
129
  gr4vy/models/cvvresponsecode.py,sha256=SQpPGT-7mO9xwsDiIjGAZ-zQjXvKmH_jfvg0q7RroIU,336
130
130
  gr4vy/models/cybersourceantifraudoptions.py,sha256=bIW9BcHTBrRYHwLehdwbIu50x-1ZMfNVkz0D5qevf6A,2622
131
- gr4vy/models/cybersourceoptions.py,sha256=tXg4iptc9zWnPabNOqZm1pfKeSGOq3NahAu4dTP2On4,2552
131
+ gr4vy/models/cybersourceoptions.py,sha256=gcbFy0IKmu3DB2RYlabsnYhyurCyaWl9P5GREYEdr3Y,2860
132
132
  gr4vy/models/definitionfield.py,sha256=qi8JYRDSDgHLz3Hcug0M95ZAvm8SyPT-qldy-YD9MPI,1606
133
133
  gr4vy/models/definitionfieldformat.py,sha256=iFx1ro4waAIZtqp_ouwfHVILF1erz_Hh7nzfpeIb4o0,352
134
134
  gr4vy/models/delete_buyer_shipping_detailsop.py,sha256=hdFEhMJ-tZGm0y-dcXzFpjq84zoxa728DIgrrji0H6g,1847
@@ -393,7 +393,7 @@ gr4vy/models/userstatus.py,sha256=bmlF_Mt656z9o2ibhQ5_dohFs6nEKsuAoNdToCe4vE4,30
393
393
  gr4vy/models/validationerror.py,sha256=4wRWnXDL-O7GxCdKzmEuWyv-xTywx124gpf65A1OyVc,522
394
394
  gr4vy/models/verify_payment_service_credentialsop.py,sha256=IEO3QA3Nn6n-EcfuovKKr5Y-WFJfnU0lxRSvwJ-F7Yc,1623
395
395
  gr4vy/models/verifycredentials.py,sha256=M4VoMBhJxmtTf9LXiL_7v3K6y5Gte-H3YGGpkIqB_n8,2278
396
- gr4vy/models/void_transactionop.py,sha256=neqJjtbAtl3BGcDQiC3gAJhkiPbQ9mjrgzzBXeESEuI,3384
396
+ gr4vy/models/void_transactionop.py,sha256=fMhJfpM-xtBBLNTahhf61fD7iWeqvEPY0Fqrt9bd72k,4282
397
397
  gr4vy/models/voidablefield.py,sha256=uApDDXV7oryN_PtHh1YH7dhBpFtPF4t5UqYzO4Hf1Sk,316
398
398
  gr4vy/models/voidstatus.py,sha256=d1Xfuz1p1H3zCg11TWn-SPHxtULvy8HjmSKbGIHGW78,325
399
399
  gr4vy/models/walletpaymentoptioncontext.py,sha256=aQfoRvTnYyNzpJfgpXvkoPbpUZGGEHg0ERjQq3udmCk,422
@@ -433,13 +433,13 @@ gr4vy/utils/logger.py,sha256=WdT856mADqljmNjIW_Y1ntFJrIWz3CCOowhK8kcGobk,669
433
433
  gr4vy/utils/metadata.py,sha256=Per2KFXXOqOtoUWXrlIfjrSrBg199KrRW0nKQDgHIBU,3136
434
434
  gr4vy/utils/queryparams.py,sha256=MTK6inMS1_WwjmMJEJmAn67tSHHJyarpdGRlorRHEtI,5899
435
435
  gr4vy/utils/requestbodies.py,sha256=ySjEyjcLi731LNUahWvLOrES2HihuA8VrOJx4eQ7Qzg,2101
436
- gr4vy/utils/retries.py,sha256=6yhfZifqIat9i76xF0lTR2jLj1IN9BNGyqqxATlEFPU,6348
436
+ gr4vy/utils/retries.py,sha256=stPJEFtmK8gOM6aT0DpEJp9Z39oXX1-8I69jpa2n3Ww,8130
437
437
  gr4vy/utils/security.py,sha256=VfMDCDFcnxNgffthL8icp_bjRV9FxtJbhK2DgNkSZMk,6016
438
438
  gr4vy/utils/serializers.py,sha256=Hndks5M_rJXVub_N5lu0gKZQUoEmWrn6PN7R-0HwvOE,5999
439
439
  gr4vy/utils/unmarshal_json_response.py,sha256=G4h8gLOK09kjhPiUZjgIMm6zntcbxpvb_mCpMR3mLhQ,870
440
440
  gr4vy/utils/url.py,sha256=BgGPgcTA6MRK4bF8fjP2dUopN3NzEzxWMXPBVg8NQUA,5254
441
441
  gr4vy/utils/values.py,sha256=CcaCXEa3xHhkUDROyXZocN8f0bdITftv9Y0P9lTf0YM,3517
442
442
  gr4vy/webhooks.py,sha256=2L-ZhdK-XU2X0AkVqgZvhfRqDCKUVs7R4UNCmZJR78w,1359
443
- gr4vy-1.7.16.dist-info/METADATA,sha256=OjP5oY0ucTbT6ubuLZq_DYMNBcCih9OQUpnZKQgnph4,44093
444
- gr4vy-1.7.16.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
445
- gr4vy-1.7.16.dist-info/RECORD,,
443
+ gr4vy-1.7.17.dist-info/METADATA,sha256=0IgmtxvB_ym5DiJCg36pUvbopC804T9D7rsEZHua2Dg,44093
444
+ gr4vy-1.7.17.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
445
+ gr4vy-1.7.17.dist-info/RECORD,,
File without changes