gr4vy 1.5.2__py3-none-any.whl → 1.10.9__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 +3 -3
- gr4vy/actions.py +307 -0
- gr4vy/all.py +4 -2
- gr4vy/audit_logs.py +4 -2
- gr4vy/balances.py +4 -2
- gr4vy/basesdk.py +6 -0
- gr4vy/buyers_gift_cards.py +4 -2
- gr4vy/buyers_payment_methods.py +4 -2
- gr4vy/buyers_sdk.py +28 -18
- gr4vy/buyers_shipping_details.py +20 -10
- gr4vy/card_scheme_definitions_sdk.py +4 -2
- gr4vy/checkout_sessions.py +40 -14
- gr4vy/digital_wallets_sdk.py +20 -10
- gr4vy/domains.py +8 -4
- gr4vy/errors/apierror.py +1 -1
- gr4vy/errors/error400.py +1 -5
- gr4vy/errors/error401.py +1 -5
- gr4vy/errors/error403.py +1 -5
- gr4vy/errors/error404.py +1 -5
- gr4vy/errors/error405.py +1 -5
- gr4vy/errors/error409.py +1 -5
- gr4vy/errors/error425.py +1 -5
- gr4vy/errors/error429.py +1 -5
- gr4vy/errors/error500.py +1 -5
- gr4vy/errors/error502.py +1 -5
- gr4vy/errors/error504.py +1 -5
- gr4vy/errors/gr4vyerror.py +1 -1
- gr4vy/errors/httpvalidationerror.py +1 -1
- gr4vy/errors/no_response_error.py +1 -1
- gr4vy/errors/responsevalidationerror.py +1 -1
- gr4vy/events.py +54 -7
- gr4vy/executions.py +12 -6
- gr4vy/gift_cards_sdk.py +16 -8
- gr4vy/jobs.py +4 -2
- gr4vy/merchant_accounts_sdk.py +16 -8
- gr4vy/models/__init__.py +103 -29
- gr4vy/models/adyencardoptions.py +1 -6
- gr4vy/models/adyensepaoptions.py +1 -6
- gr4vy/models/airline.py +2 -7
- gr4vy/models/airlineleg.py +2 -6
- gr4vy/models/airlinepassenger.py +2 -6
- gr4vy/models/auditlogentry.py +3 -3
- gr4vy/models/auditlogentryuser.py +3 -3
- gr4vy/models/{billingdetails_input.py → billingdetails.py} +2 -2
- gr4vy/models/braintreedynamicdatafieldsoptions.py +2 -7
- gr4vy/models/browserinfo.py +2 -4
- gr4vy/models/buyer.py +3 -3
- gr4vy/models/buyercreate.py +3 -3
- gr4vy/models/buyerupdate.py +3 -3
- gr4vy/models/cartitem.py +9 -6
- gr4vy/models/chaseoptions.py +46 -0
- gr4vy/models/checkoutsession.py +24 -3
- gr4vy/models/checkoutsessioncreate.py +39 -5
- gr4vy/models/checkoutsessionpaymentmethod.py +3 -5
- gr4vy/models/checkoutsessionpaymentmethoddetails.py +2 -6
- gr4vy/models/create_payment_methodop.py +6 -0
- gr4vy/models/createsession.py +3 -3
- gr4vy/models/cybersourceoptions.py +7 -0
- gr4vy/models/definitionfield.py +1 -6
- gr4vy/models/digitalwallet.py +3 -5
- gr4vy/models/digitalwalletcreate.py +2 -6
- gr4vy/models/dlocalupirecurringinfooptions.py +13 -15
- gr4vy/models/errordetail.py +2 -4
- gr4vy/models/flow.py +16 -0
- gr4vy/models/flowaction.py +16 -0
- gr4vy/models/forterantifraudoptions.py +2 -7
- gr4vy/models/forterantifraudoptionscartitembasicitemdata.py +4 -8
- gr4vy/models/forterantifraudoptionscartitemdeliverydetails.py +3 -6
- gr4vy/models/giftcardredemption.py +4 -8
- gr4vy/models/giftcardservice.py +3 -5
- gr4vy/models/giftcardsummary.py +3 -5
- gr4vy/models/{guestbuyer_input.py → guestbuyer.py} +5 -5
- gr4vy/models/instrumenttype.py +1 -0
- gr4vy/models/integrationclient.py +16 -0
- gr4vy/models/list_all_report_executionsop.py +2 -14
- gr4vy/models/list_audit_logsop.py +2 -10
- gr4vy/models/list_buyer_payment_methodsop.py +2 -3
- gr4vy/models/list_payment_methodsop.py +2 -14
- gr4vy/models/list_payment_servicesop.py +2 -8
- gr4vy/models/list_reportsop.py +2 -10
- gr4vy/models/list_transaction_actionsop.py +43 -0
- gr4vy/models/list_transaction_eventsop.py +12 -1
- gr4vy/models/list_transactionsop.py +4 -22
- gr4vy/models/merchantaccount.py +3 -5
- gr4vy/models/merchantaccountcreate.py +2 -6
- gr4vy/models/merchantaccountupdate.py +2 -6
- gr4vy/models/method.py +3 -0
- gr4vy/models/mockcardoptions.py +7 -2
- gr4vy/models/mode.py +1 -0
- gr4vy/models/monatospeioptions.py +15 -0
- gr4vy/models/networktoken.py +3 -3
- gr4vy/models/networktokenpaymentmethodcreate.py +4 -8
- gr4vy/models/nuveiidealoptions.py +49 -0
- gr4vy/models/paymentlink.py +18 -7
- gr4vy/models/paymentlinkcreate.py +23 -37
- gr4vy/models/paymentmethod.py +7 -13
- gr4vy/models/paymentmethodcard.py +3 -5
- gr4vy/models/paymentmethoddetailscard.py +2 -6
- gr4vy/models/paymentmethodsummary.py +6 -12
- gr4vy/models/paymentoption.py +3 -3
- gr4vy/models/paymentservice.py +4 -6
- gr4vy/models/paymentserviceconfiguration.py +2 -6
- gr4vy/models/paymentservicedefinition.py +11 -5
- gr4vy/models/paymentservicetoken.py +3 -3
- gr4vy/models/payoutcreate.py +5 -9
- gr4vy/models/payoutsummary.py +4 -6
- gr4vy/models/paypaloptions.py +8 -2
- gr4vy/models/paypalshippingoptions.py +51 -0
- gr4vy/models/paypalshippingoptionsitem.py +89 -0
- gr4vy/models/paypalshippingoptionsitemamount.py +20 -0
- gr4vy/models/plaidpaymentmethodcreate.py +106 -0
- gr4vy/models/redirectpaymentmethodcreate.py +3 -6
- gr4vy/models/refund.py +4 -4
- gr4vy/models/report.py +4 -6
- gr4vy/models/reportcreate.py +2 -3
- gr4vy/models/reportexecution.py +3 -3
- gr4vy/models/reportexecutionsummary.py +3 -3
- gr4vy/models/reportspec.py +2 -4
- gr4vy/models/reportsummary.py +3 -5
- gr4vy/models/taxid.py +2 -4
- gr4vy/models/taxidkind.py +6 -0
- gr4vy/models/threedsecuredatav1.py +2 -6
- gr4vy/models/threedsecuredatav2.py +2 -6
- gr4vy/models/transaction.py +74 -25
- gr4vy/models/transactionaction.py +48 -0
- gr4vy/models/transactionactions.py +17 -0
- gr4vy/models/transactionbuyer.py +3 -3
- gr4vy/models/transactioncancel.py +3 -3
- gr4vy/models/transactioncapture.py +3 -3
- gr4vy/models/transactionconnectionoptions.py +30 -0
- gr4vy/models/transactioncreate.py +27 -12
- gr4vy/models/transactionevent.py +5 -3
- gr4vy/models/transactionpaymentmethod.py +6 -12
- gr4vy/models/transactionpaymentservice.py +3 -3
- gr4vy/models/transactionrefundcreate.py +2 -6
- gr4vy/models/transactionsummary.py +6 -10
- gr4vy/models/transactionthreedsecuresummary.py +3 -9
- gr4vy/models/transactionvoid.py +3 -3
- gr4vy/models/trustlyoptions.py +2 -9
- gr4vy/network_tokens_cryptogram.py +4 -2
- gr4vy/payment_links_sdk.py +34 -14
- gr4vy/payment_methods_network_tokens.py +20 -10
- gr4vy/payment_methods_payment_service_tokens.py +12 -6
- gr4vy/payment_methods_sdk.py +16 -8
- gr4vy/payment_options_sdk.py +4 -2
- gr4vy/payment_service_definitions_sdk.py +12 -6
- gr4vy/payment_services_sdk.py +28 -14
- gr4vy/payouts.py +16 -10
- gr4vy/refunds_sdk.py +4 -2
- gr4vy/report_executions_sdk.py +4 -2
- gr4vy/reports_sdk.py +16 -8
- gr4vy/sessions.py +12 -6
- gr4vy/transactions.py +99 -28
- gr4vy/transactions_refunds.py +12 -6
- gr4vy/transactions_settlements.py +8 -4
- gr4vy/types/basemodel.py +41 -3
- gr4vy/utils/__init__.py +0 -3
- gr4vy/utils/enums.py +60 -0
- gr4vy/utils/forms.py +21 -10
- gr4vy/utils/queryparams.py +14 -2
- gr4vy/utils/requestbodies.py +1 -1
- gr4vy/utils/retries.py +69 -5
- gr4vy/utils/serializers.py +0 -20
- gr4vy/utils/unmarshal_json_response.py +15 -1
- {gr4vy-1.5.2.dist-info → gr4vy-1.10.9.dist-info}/METADATA +39 -39
- {gr4vy-1.5.2.dist-info → gr4vy-1.10.9.dist-info}/RECORD +167 -155
- gr4vy/models/billingdetails_output.py +0 -87
- gr4vy/models/guestbuyer_output.py +0 -80
- {gr4vy-1.5.2.dist-info → gr4vy-1.10.9.dist-info}/WHEEL +0 -0
|
@@ -65,6 +65,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
65
65
|
merchant_account_id=self.sdk_configuration.globals.merchant_account_id,
|
|
66
66
|
),
|
|
67
67
|
security=self.sdk_configuration.security,
|
|
68
|
+
allow_empty_value=None,
|
|
68
69
|
timeout_ms=timeout_ms,
|
|
69
70
|
)
|
|
70
71
|
|
|
@@ -85,7 +86,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
85
86
|
config=self.sdk_configuration,
|
|
86
87
|
base_url=base_url or "",
|
|
87
88
|
operation_id="get_transaction_settlement",
|
|
88
|
-
oauth2_scopes=
|
|
89
|
+
oauth2_scopes=None,
|
|
89
90
|
security_source=get_security_from_env(
|
|
90
91
|
self.sdk_configuration.security, models.Security
|
|
91
92
|
),
|
|
@@ -215,6 +216,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
215
216
|
merchant_account_id=self.sdk_configuration.globals.merchant_account_id,
|
|
216
217
|
),
|
|
217
218
|
security=self.sdk_configuration.security,
|
|
219
|
+
allow_empty_value=None,
|
|
218
220
|
timeout_ms=timeout_ms,
|
|
219
221
|
)
|
|
220
222
|
|
|
@@ -235,7 +237,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
235
237
|
config=self.sdk_configuration,
|
|
236
238
|
base_url=base_url or "",
|
|
237
239
|
operation_id="get_transaction_settlement",
|
|
238
|
-
oauth2_scopes=
|
|
240
|
+
oauth2_scopes=None,
|
|
239
241
|
security_source=get_security_from_env(
|
|
240
242
|
self.sdk_configuration.security, models.Security
|
|
241
243
|
),
|
|
@@ -362,6 +364,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
362
364
|
merchant_account_id=self.sdk_configuration.globals.merchant_account_id,
|
|
363
365
|
),
|
|
364
366
|
security=self.sdk_configuration.security,
|
|
367
|
+
allow_empty_value=None,
|
|
365
368
|
timeout_ms=timeout_ms,
|
|
366
369
|
)
|
|
367
370
|
|
|
@@ -382,7 +385,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
382
385
|
config=self.sdk_configuration,
|
|
383
386
|
base_url=base_url or "",
|
|
384
387
|
operation_id="list_transaction_settlements",
|
|
385
|
-
oauth2_scopes=
|
|
388
|
+
oauth2_scopes=None,
|
|
386
389
|
security_source=get_security_from_env(
|
|
387
390
|
self.sdk_configuration.security, models.Security
|
|
388
391
|
),
|
|
@@ -509,6 +512,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
509
512
|
merchant_account_id=self.sdk_configuration.globals.merchant_account_id,
|
|
510
513
|
),
|
|
511
514
|
security=self.sdk_configuration.security,
|
|
515
|
+
allow_empty_value=None,
|
|
512
516
|
timeout_ms=timeout_ms,
|
|
513
517
|
)
|
|
514
518
|
|
|
@@ -529,7 +533,7 @@ class TransactionsSettlements(BaseSDK):
|
|
|
529
533
|
config=self.sdk_configuration,
|
|
530
534
|
base_url=base_url or "",
|
|
531
535
|
operation_id="list_transaction_settlements",
|
|
532
|
-
oauth2_scopes=
|
|
536
|
+
oauth2_scopes=None,
|
|
533
537
|
security_source=get_security_from_env(
|
|
534
538
|
self.sdk_configuration.security, models.Security
|
|
535
539
|
),
|
gr4vy/types/basemodel.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from pydantic import ConfigDict, model_serializer
|
|
4
4
|
from pydantic import BaseModel as PydanticBaseModel
|
|
5
|
-
from
|
|
5
|
+
from pydantic_core import core_schema
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union
|
|
6
7
|
from typing_extensions import TypeAliasType, TypeAlias
|
|
7
8
|
|
|
8
9
|
|
|
@@ -35,5 +36,42 @@ else:
|
|
|
35
36
|
"OptionalNullable", Union[Optional[Nullable[T]], Unset], type_params=(T,)
|
|
36
37
|
)
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
UnrecognizedStr:
|
|
39
|
+
|
|
40
|
+
class UnrecognizedStr(str):
|
|
41
|
+
@classmethod
|
|
42
|
+
def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema:
|
|
43
|
+
# Make UnrecognizedStr only work in lax mode, not strict mode
|
|
44
|
+
# This makes it a "fallback" option when more specific types (like Literals) don't match
|
|
45
|
+
def validate_lax(v: Any) -> 'UnrecognizedStr':
|
|
46
|
+
if isinstance(v, cls):
|
|
47
|
+
return v
|
|
48
|
+
return cls(str(v))
|
|
49
|
+
|
|
50
|
+
# Use lax_or_strict_schema where strict always fails
|
|
51
|
+
# This forces Pydantic to prefer other union members in strict mode
|
|
52
|
+
# and only fall back to UnrecognizedStr in lax mode
|
|
53
|
+
return core_schema.lax_or_strict_schema(
|
|
54
|
+
lax_schema=core_schema.chain_schema([
|
|
55
|
+
core_schema.str_schema(),
|
|
56
|
+
core_schema.no_info_plain_validator_function(validate_lax)
|
|
57
|
+
]),
|
|
58
|
+
strict_schema=core_schema.none_schema(), # Always fails in strict mode
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class UnrecognizedInt(int):
|
|
63
|
+
@classmethod
|
|
64
|
+
def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema:
|
|
65
|
+
# Make UnrecognizedInt only work in lax mode, not strict mode
|
|
66
|
+
# This makes it a "fallback" option when more specific types (like Literals) don't match
|
|
67
|
+
def validate_lax(v: Any) -> 'UnrecognizedInt':
|
|
68
|
+
if isinstance(v, cls):
|
|
69
|
+
return v
|
|
70
|
+
return cls(int(v))
|
|
71
|
+
return core_schema.lax_or_strict_schema(
|
|
72
|
+
lax_schema=core_schema.chain_schema([
|
|
73
|
+
core_schema.int_schema(),
|
|
74
|
+
core_schema.no_info_plain_validator_function(validate_lax)
|
|
75
|
+
]),
|
|
76
|
+
strict_schema=core_schema.none_schema(), # Always fails in strict mode
|
|
77
|
+
)
|
gr4vy/utils/__init__.py
CHANGED
|
@@ -42,7 +42,6 @@ if TYPE_CHECKING:
|
|
|
42
42
|
validate_decimal,
|
|
43
43
|
validate_float,
|
|
44
44
|
validate_int,
|
|
45
|
-
validate_open_enum,
|
|
46
45
|
)
|
|
47
46
|
from .url import generate_url, template_url, remove_suffix
|
|
48
47
|
from .values import (
|
|
@@ -104,7 +103,6 @@ __all__ = [
|
|
|
104
103
|
"validate_const",
|
|
105
104
|
"validate_float",
|
|
106
105
|
"validate_int",
|
|
107
|
-
"validate_open_enum",
|
|
108
106
|
"cast_partial",
|
|
109
107
|
]
|
|
110
108
|
|
|
@@ -158,7 +156,6 @@ _dynamic_imports: dict[str, str] = {
|
|
|
158
156
|
"validate_const": ".serializers",
|
|
159
157
|
"validate_float": ".serializers",
|
|
160
158
|
"validate_int": ".serializers",
|
|
161
|
-
"validate_open_enum": ".serializers",
|
|
162
159
|
"cast_partial": ".values",
|
|
163
160
|
}
|
|
164
161
|
|
gr4vy/utils/enums.py
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
import sys
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic_core import core_schema
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class OpenEnumMeta(enum.EnumMeta):
|
|
7
11
|
# The __call__ method `boundary` kwarg was added in 3.11 and must be present
|
|
@@ -72,3 +76,59 @@ class OpenEnumMeta(enum.EnumMeta):
|
|
|
72
76
|
)
|
|
73
77
|
except ValueError:
|
|
74
78
|
return value
|
|
79
|
+
|
|
80
|
+
def __new__(mcs, name, bases, namespace, **kwargs):
|
|
81
|
+
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
|
|
82
|
+
|
|
83
|
+
# Add __get_pydantic_core_schema__ to make open enums work correctly
|
|
84
|
+
# in union discrimination. In strict mode (used by Pydantic for unions),
|
|
85
|
+
# only known enum values match. In lax mode, unknown values are accepted.
|
|
86
|
+
def __get_pydantic_core_schema__(
|
|
87
|
+
cls_inner: Any, _source_type: Any, _handler: Any
|
|
88
|
+
) -> core_schema.CoreSchema:
|
|
89
|
+
# Create a validator that only accepts known enum values (for strict mode)
|
|
90
|
+
def validate_strict(v: Any) -> Any:
|
|
91
|
+
if isinstance(v, cls_inner):
|
|
92
|
+
return v
|
|
93
|
+
# Use the parent EnumMeta's __call__ which raises ValueError for unknown values
|
|
94
|
+
return enum.EnumMeta.__call__(cls_inner, v)
|
|
95
|
+
|
|
96
|
+
# Create a lax validator that accepts unknown values
|
|
97
|
+
def validate_lax(v: Any) -> Any:
|
|
98
|
+
if isinstance(v, cls_inner):
|
|
99
|
+
return v
|
|
100
|
+
try:
|
|
101
|
+
return enum.EnumMeta.__call__(cls_inner, v)
|
|
102
|
+
except ValueError:
|
|
103
|
+
# Return the raw value for unknown enum values
|
|
104
|
+
return v
|
|
105
|
+
|
|
106
|
+
# Determine the base type schema (str or int)
|
|
107
|
+
is_int_enum = False
|
|
108
|
+
for base in cls_inner.__mro__:
|
|
109
|
+
if base is int:
|
|
110
|
+
is_int_enum = True
|
|
111
|
+
break
|
|
112
|
+
if base is str:
|
|
113
|
+
break
|
|
114
|
+
|
|
115
|
+
base_schema = (
|
|
116
|
+
core_schema.int_schema()
|
|
117
|
+
if is_int_enum
|
|
118
|
+
else core_schema.str_schema()
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Use lax_or_strict_schema:
|
|
122
|
+
# - strict mode: only known enum values match (raises ValueError for unknown)
|
|
123
|
+
# - lax mode: accept any value, return enum member or raw value
|
|
124
|
+
return core_schema.lax_or_strict_schema(
|
|
125
|
+
lax_schema=core_schema.chain_schema(
|
|
126
|
+
[base_schema, core_schema.no_info_plain_validator_function(validate_lax)]
|
|
127
|
+
),
|
|
128
|
+
strict_schema=core_schema.chain_schema(
|
|
129
|
+
[base_schema, core_schema.no_info_plain_validator_function(validate_strict)]
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
setattr(cls, "__get_pydantic_core_schema__", classmethod(__get_pydantic_core_schema__))
|
|
134
|
+
return cls
|
gr4vy/utils/forms.py
CHANGED
|
@@ -142,16 +142,21 @@ def serialize_multipart_form(
|
|
|
142
142
|
if field_metadata.file:
|
|
143
143
|
if isinstance(val, List):
|
|
144
144
|
# Handle array of files
|
|
145
|
+
array_field_name = f_name + "[]"
|
|
145
146
|
for file_obj in val:
|
|
146
147
|
if not _is_set(file_obj):
|
|
147
148
|
continue
|
|
148
|
-
|
|
149
|
-
file_name, content, content_type = _extract_file_properties(
|
|
149
|
+
|
|
150
|
+
file_name, content, content_type = _extract_file_properties(
|
|
151
|
+
file_obj
|
|
152
|
+
)
|
|
150
153
|
|
|
151
154
|
if content_type is not None:
|
|
152
|
-
files.append(
|
|
155
|
+
files.append(
|
|
156
|
+
(array_field_name, (file_name, content, content_type))
|
|
157
|
+
)
|
|
153
158
|
else:
|
|
154
|
-
files.append((
|
|
159
|
+
files.append((array_field_name, (file_name, content)))
|
|
155
160
|
else:
|
|
156
161
|
# Handle single file
|
|
157
162
|
file_name, content, content_type = _extract_file_properties(val)
|
|
@@ -161,11 +166,16 @@ def serialize_multipart_form(
|
|
|
161
166
|
else:
|
|
162
167
|
files.append((f_name, (file_name, content)))
|
|
163
168
|
elif field_metadata.json:
|
|
164
|
-
files.append(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
files.append(
|
|
170
|
+
(
|
|
171
|
+
f_name,
|
|
172
|
+
(
|
|
173
|
+
None,
|
|
174
|
+
marshal_json(val, request_field_types[name]),
|
|
175
|
+
"application/json",
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
)
|
|
169
179
|
else:
|
|
170
180
|
if isinstance(val, List):
|
|
171
181
|
values = []
|
|
@@ -175,7 +185,8 @@ def serialize_multipart_form(
|
|
|
175
185
|
continue
|
|
176
186
|
values.append(_val_to_string(value))
|
|
177
187
|
|
|
178
|
-
|
|
188
|
+
array_field_name = f_name + "[]"
|
|
189
|
+
form[array_field_name] = values
|
|
179
190
|
else:
|
|
180
191
|
form[f_name] = _val_to_string(val)
|
|
181
192
|
return media_type, form, files
|
gr4vy/utils/queryparams.py
CHANGED
|
@@ -27,12 +27,13 @@ from .forms import _populate_form
|
|
|
27
27
|
def get_query_params(
|
|
28
28
|
query_params: Any,
|
|
29
29
|
gbls: Optional[Any] = None,
|
|
30
|
+
allow_empty_value: Optional[List[str]] = None,
|
|
30
31
|
) -> Dict[str, List[str]]:
|
|
31
32
|
params: Dict[str, List[str]] = {}
|
|
32
33
|
|
|
33
|
-
globals_already_populated = _populate_query_params(query_params, gbls, params, [])
|
|
34
|
+
globals_already_populated = _populate_query_params(query_params, gbls, params, [], allow_empty_value)
|
|
34
35
|
if _is_set(gbls):
|
|
35
|
-
_populate_query_params(gbls, None, params, globals_already_populated)
|
|
36
|
+
_populate_query_params(gbls, None, params, globals_already_populated, allow_empty_value)
|
|
36
37
|
|
|
37
38
|
return params
|
|
38
39
|
|
|
@@ -42,6 +43,7 @@ def _populate_query_params(
|
|
|
42
43
|
gbls: Any,
|
|
43
44
|
query_param_values: Dict[str, List[str]],
|
|
44
45
|
skip_fields: List[str],
|
|
46
|
+
allow_empty_value: Optional[List[str]] = None,
|
|
45
47
|
) -> List[str]:
|
|
46
48
|
globals_already_populated: List[str] = []
|
|
47
49
|
|
|
@@ -69,6 +71,16 @@ def _populate_query_params(
|
|
|
69
71
|
globals_already_populated.append(name)
|
|
70
72
|
|
|
71
73
|
f_name = field.alias if field.alias is not None else name
|
|
74
|
+
|
|
75
|
+
allow_empty_set = set(allow_empty_value or [])
|
|
76
|
+
should_include_empty = f_name in allow_empty_set and (
|
|
77
|
+
value is None or value == [] or value == ""
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if should_include_empty:
|
|
81
|
+
query_param_values[f_name] = [""]
|
|
82
|
+
continue
|
|
83
|
+
|
|
72
84
|
serialization = metadata.serialization
|
|
73
85
|
if serialization is not None:
|
|
74
86
|
serialized_parms = _get_serialized_params(
|
gr4vy/utils/requestbodies.py
CHANGED
|
@@ -44,7 +44,7 @@ def serialize_request_body(
|
|
|
44
44
|
|
|
45
45
|
serialized_request_body = SerializedRequestBody(media_type)
|
|
46
46
|
|
|
47
|
-
if re.match(r"(application|text)
|
|
47
|
+
if re.match(r"(application|text)\/([^+]+\+)*json.*", media_type) is not None:
|
|
48
48
|
serialized_request_body.content = marshal_json(request_body, request_body_type)
|
|
49
49
|
elif re.match(r"multipart\/.*", media_type) is not None:
|
|
50
50
|
(
|
gr4vy/utils/retries.py
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import random
|
|
5
5
|
import time
|
|
6
|
-
from
|
|
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
|
-
|
|
187
|
-
sleep =
|
|
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
|
-
|
|
215
|
-
sleep =
|
|
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
|
gr4vy/utils/serializers.py
CHANGED
|
@@ -102,26 +102,6 @@ def validate_int(b):
|
|
|
102
102
|
return int(b)
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def validate_open_enum(is_int: bool):
|
|
106
|
-
def validate(e):
|
|
107
|
-
if e is None:
|
|
108
|
-
return None
|
|
109
|
-
|
|
110
|
-
if isinstance(e, Unset):
|
|
111
|
-
return e
|
|
112
|
-
|
|
113
|
-
if is_int:
|
|
114
|
-
if not isinstance(e, int):
|
|
115
|
-
raise ValueError("Expected int")
|
|
116
|
-
else:
|
|
117
|
-
if not isinstance(e, str):
|
|
118
|
-
raise ValueError("Expected string")
|
|
119
|
-
|
|
120
|
-
return e
|
|
121
|
-
|
|
122
|
-
return validate
|
|
123
|
-
|
|
124
|
-
|
|
125
105
|
def validate_const(v):
|
|
126
106
|
def validate(c):
|
|
127
107
|
# Optional[T] is a Union[T, None]
|
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Optional
|
|
3
|
+
from typing import Any, Optional, Type, TypeVar, overload
|
|
4
4
|
|
|
5
5
|
import httpx
|
|
6
6
|
|
|
7
7
|
from .serializers import unmarshal_json
|
|
8
8
|
from gr4vy import errors
|
|
9
9
|
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@overload
|
|
14
|
+
def unmarshal_json_response(
|
|
15
|
+
typ: Type[T], http_res: httpx.Response, body: Optional[str] = None
|
|
16
|
+
) -> T: ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@overload
|
|
20
|
+
def unmarshal_json_response(
|
|
21
|
+
typ: Any, http_res: httpx.Response, body: Optional[str] = None
|
|
22
|
+
) -> Any: ...
|
|
23
|
+
|
|
10
24
|
|
|
11
25
|
def unmarshal_json_response(
|
|
12
26
|
typ: Any, http_res: httpx.Response, body: Optional[str] = None
|