thrustlab 0.1.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.
- thrustlab/__init__.py +23 -0
- thrustlab/_async_polling.py +47 -0
- thrustlab/_http.py +166 -0
- thrustlab/_models/__init__.py +211 -0
- thrustlab/_models/api_key.py +184 -0
- thrustlab/_models/api_key_create.py +74 -0
- thrustlab/_models/api_key_with_secret.py +192 -0
- thrustlab/_models/billing_portal_session_resource.py +74 -0
- thrustlab/_models/checkout_session_resource.py +94 -0
- thrustlab/_models/component_brand.py +80 -0
- thrustlab/_models/component_brands_response.py +108 -0
- thrustlab/_models/component_create_body.py +83 -0
- thrustlab/_models/component_create_body_spec_json.py +46 -0
- thrustlab/_models/component_list_response.py +108 -0
- thrustlab/_models/component_patch.py +111 -0
- thrustlab/_models/component_patch_spec_json_type_0.py +46 -0
- thrustlab/_models/component_resource.py +188 -0
- thrustlab/_models/component_resource_spec_json.py +46 -0
- thrustlab/_models/create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post.py +50 -0
- thrustlab/_models/credit_balance_resource.py +135 -0
- thrustlab/_models/credit_bucket_breakdown.py +92 -0
- thrustlab/_models/credit_bucket_breakdown_type.py +9 -0
- thrustlab/_models/credit_usage_event_resource.py +154 -0
- thrustlab/_models/credit_usage_event_resource_credit_type.py +9 -0
- thrustlab/_models/credit_usage_event_resource_type.py +16 -0
- thrustlab/_models/credit_usage_list_response.py +116 -0
- thrustlab/_models/credit_usage_related_resource.py +71 -0
- thrustlab/_models/credit_usage_related_resource_object.py +9 -0
- thrustlab/_models/fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post.py +48 -0
- thrustlab/_models/get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get.py +49 -0
- thrustlab/_models/get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get.py +46 -0
- thrustlab/_models/get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get.py +48 -0
- thrustlab/_models/http_validation_error.py +79 -0
- thrustlab/_models/list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get.py +49 -0
- thrustlab/_models/list_events_v1_events_get_response_list_events_v1_events_get.py +46 -0
- thrustlab/_models/list_response_api_key.py +116 -0
- thrustlab/_models/list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get.py +46 -0
- thrustlab/_models/patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch.py +49 -0
- thrustlab/_models/portal_session_in.py +42 -0
- thrustlab/_models/project_create_body.py +103 -0
- thrustlab/_models/project_list_response.py +108 -0
- thrustlab/_models/project_patch.py +113 -0
- thrustlab/_models/project_resource.py +127 -0
- thrustlab/_models/retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post.py +49 -0
- thrustlab/_models/rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post.py +49 -0
- thrustlab/_models/rotor_group_in.py +107 -0
- thrustlab/_models/rotor_group_resource.py +97 -0
- thrustlab/_models/simulation_cancel_body.py +24 -0
- thrustlab/_models/simulation_create_body.py +128 -0
- thrustlab/_models/simulation_inputs.py +66 -0
- thrustlab/_models/simulation_list_response.py +88 -0
- thrustlab/_models/simulation_patch.py +121 -0
- thrustlab/_models/simulation_patch_plot_config_json_type_0_item.py +46 -0
- thrustlab/_models/simulation_resource.py +283 -0
- thrustlab/_models/simulation_resource_error_type_0.py +46 -0
- thrustlab/_models/simulation_resource_plot_config_json_type_0_item.py +46 -0
- thrustlab/_models/simulation_resource_result_type_0.py +46 -0
- thrustlab/_models/starred_component_create_body.py +80 -0
- thrustlab/_models/starred_component_list_response.py +108 -0
- thrustlab/_models/starred_component_resource.py +106 -0
- thrustlab/_models/submission_create_body.py +121 -0
- thrustlab/_models/submission_create_body_data_json.py +46 -0
- thrustlab/_models/submission_list_response.py +108 -0
- thrustlab/_models/submission_patch.py +174 -0
- thrustlab/_models/submission_patch_data_json_type_0.py +46 -0
- thrustlab/_models/submission_resource.py +197 -0
- thrustlab/_models/submission_resource_data_json.py +46 -0
- thrustlab/_models/subscription_create.py +58 -0
- thrustlab/_models/subscription_resource.py +168 -0
- thrustlab/_models/subscription_resource_status.py +12 -0
- thrustlab/_models/subscription_resource_tier.py +12 -0
- thrustlab/_models/sweep_cancel_body.py +42 -0
- thrustlab/_models/sweep_config_in.py +172 -0
- thrustlab/_models/sweep_create_body.py +138 -0
- thrustlab/_models/sweep_inputs.py +66 -0
- thrustlab/_models/sweep_list_response.py +88 -0
- thrustlab/_models/sweep_param_range_in.py +136 -0
- thrustlab/_models/sweep_param_range_in_mode.py +9 -0
- thrustlab/_models/sweep_patch.py +121 -0
- thrustlab/_models/sweep_patch_plot_config_json_type_0_item.py +46 -0
- thrustlab/_models/sweep_point_list_response.py +88 -0
- thrustlab/_models/sweep_point_resource.py +103 -0
- thrustlab/_models/sweep_point_resource_inputs.py +46 -0
- thrustlab/_models/sweep_point_resource_rotors_type_0.py +46 -0
- thrustlab/_models/sweep_resource.py +327 -0
- thrustlab/_models/sweep_resource_error_type_0.py +46 -0
- thrustlab/_models/sweep_resource_plot_config_json_type_0_item.py +46 -0
- thrustlab/_models/sweep_resource_summary_type_0.py +46 -0
- thrustlab/_models/sweep_resource_sweep_config_type_0.py +46 -0
- thrustlab/_models/sweep_rotor_group_in.py +95 -0
- thrustlab/_models/types.py +55 -0
- thrustlab/_models/user_patch.py +73 -0
- thrustlab/_models/user_resource.py +170 -0
- thrustlab/_models/v1_health_v1_health_get_response_v1_health_v1_health_get.py +46 -0
- thrustlab/_models/validation_error.py +90 -0
- thrustlab/_models/webhook_endpoint_create.py +91 -0
- thrustlab/_models/webhook_endpoint_patch.py +144 -0
- thrustlab/_pagination.py +109 -0
- thrustlab/_version.py +3 -0
- thrustlab/client.py +100 -0
- thrustlab/exceptions.py +121 -0
- thrustlab/resources/__init__.py +1 -0
- thrustlab/resources/_base.py +12 -0
- thrustlab/resources/api_keys.py +43 -0
- thrustlab/resources/components.py +60 -0
- thrustlab/resources/credits.py +40 -0
- thrustlab/resources/projects.py +54 -0
- thrustlab/resources/simulations.py +74 -0
- thrustlab/resources/starred_components.py +57 -0
- thrustlab/resources/submissions.py +44 -0
- thrustlab/resources/subscriptions.py +42 -0
- thrustlab/resources/sweeps.py +96 -0
- thrustlab/resources/users.py +13 -0
- thrustlab/resources/webhook_endpoints.py +138 -0
- thrustlab/webhooks.py +121 -0
- thrustlab-0.1.0.dist-info/METADATA +190 -0
- thrustlab-0.1.0.dist-info/RECORD +119 -0
- thrustlab-0.1.0.dist-info/WHEEL +4 -0
- thrustlab-0.1.0.dist-info/licenses/LICENSE +21 -0
thrustlab/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""ThrustLab — official Python SDK for the ThrustLab API.
|
|
2
|
+
|
|
3
|
+
See https://thrustlab.com/docs/sdk/python for usage.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from thrustlab._version import __version__
|
|
7
|
+
from thrustlab.client import Client
|
|
8
|
+
from thrustlab.exceptions import (
|
|
9
|
+
APIError, APIServerError, AuthenticationError, ConfigurationError,
|
|
10
|
+
ConflictError, ForbiddenError, NetworkError, NotFoundError, RateLimitError,
|
|
11
|
+
SignatureVerificationError, ThrustlabError, ValidationError,
|
|
12
|
+
)
|
|
13
|
+
from thrustlab.webhooks import Event, Webhook
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"__version__",
|
|
17
|
+
"Client",
|
|
18
|
+
"APIError", "APIServerError", "AuthenticationError", "ConfigurationError",
|
|
19
|
+
"ConflictError", "ForbiddenError", "NetworkError", "NotFoundError",
|
|
20
|
+
"RateLimitError", "SignatureVerificationError", "ThrustlabError",
|
|
21
|
+
"ValidationError",
|
|
22
|
+
"Event", "Webhook",
|
|
23
|
+
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Polling helpers for async resources (simulations, sweeps).
|
|
2
|
+
|
|
3
|
+
The terminal states are: 'succeeded' | 'failed' | 'cancelled'.
|
|
4
|
+
'timed_out' is an SDK-side sentinel set when wait() exceeds its timeout budget
|
|
5
|
+
— it is never returned by the server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Callable
|
|
12
|
+
|
|
13
|
+
TERMINAL_STATES = {"succeeded", "failed", "cancelled"}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def poll_until_terminal(
|
|
17
|
+
fetch: Callable[[], dict[str, Any]],
|
|
18
|
+
*,
|
|
19
|
+
timeout: float = 600.0,
|
|
20
|
+
poll_interval: float = 2.0,
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
"""Poll ``fetch`` until the returned object reaches a terminal state.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
fetch: Zero-argument callable that returns the latest resource dict.
|
|
26
|
+
timeout: Maximum seconds to wait. If exceeded, returns the last dict
|
|
27
|
+
with ``status`` set to ``"timed_out"``.
|
|
28
|
+
poll_interval: Seconds between polls (``time.sleep``).
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
The resource dict when status is in ``TERMINAL_STATES``, or a copy
|
|
32
|
+
with ``status="timed_out"`` if the deadline was exceeded.
|
|
33
|
+
"""
|
|
34
|
+
deadline = time.monotonic() + timeout
|
|
35
|
+
attempt = 0
|
|
36
|
+
while True:
|
|
37
|
+
obj = fetch()
|
|
38
|
+
status = obj.get("status")
|
|
39
|
+
if status in TERMINAL_STATES:
|
|
40
|
+
return obj
|
|
41
|
+
if time.monotonic() >= deadline:
|
|
42
|
+
obj = dict(obj)
|
|
43
|
+
obj["status"] = "timed_out"
|
|
44
|
+
return obj
|
|
45
|
+
sleep_for = min(poll_interval * (2 ** attempt), 30.0)
|
|
46
|
+
time.sleep(sleep_for)
|
|
47
|
+
attempt += 1
|
thrustlab/_http.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Internal HTTP transport for the ThrustLab SDK.
|
|
2
|
+
|
|
3
|
+
Wraps httpx.Client with auth, idempotency-key generation, retries with
|
|
4
|
+
exponential backoff + jitter, and error-envelope → exception mapping.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import platform
|
|
11
|
+
import random
|
|
12
|
+
import time
|
|
13
|
+
import uuid
|
|
14
|
+
from typing import Any, Optional
|
|
15
|
+
|
|
16
|
+
import httpx
|
|
17
|
+
|
|
18
|
+
from thrustlab._version import __version__
|
|
19
|
+
from thrustlab.exceptions import (
|
|
20
|
+
APIError, NetworkError, RateLimitError, from_response_status,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger("thrustlab")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _user_agent() -> str:
|
|
28
|
+
return (
|
|
29
|
+
f"thrustlab-python/{__version__} "
|
|
30
|
+
f"(Python/{platform.python_version()}; {platform.system()})"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_RETRYABLE_STATUSES = {429, 500, 502, 503, 504}
|
|
35
|
+
_MUTATING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Transport:
|
|
39
|
+
"""HTTP transport for the SDK Client.
|
|
40
|
+
|
|
41
|
+
Responsibilities:
|
|
42
|
+
- inject Authorization + User-Agent on every request
|
|
43
|
+
- auto-generate Idempotency-Key on every mutating request unless caller passes one
|
|
44
|
+
- retry on 429 + 5xx + connection errors with exponential backoff + jitter
|
|
45
|
+
- map error responses to the right APIError subclass
|
|
46
|
+
- parse JSON responses
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
api_key: str,
|
|
52
|
+
base_url: str,
|
|
53
|
+
timeout: float = 30.0,
|
|
54
|
+
max_retries: int = 3,
|
|
55
|
+
client: Optional[httpx.Client] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
self._api_key = api_key
|
|
58
|
+
self._base_url = base_url.rstrip("/")
|
|
59
|
+
self._max_retries = max_retries
|
|
60
|
+
self._client = client or httpx.Client(timeout=timeout)
|
|
61
|
+
|
|
62
|
+
def request(
|
|
63
|
+
self,
|
|
64
|
+
method: str,
|
|
65
|
+
path: str,
|
|
66
|
+
*,
|
|
67
|
+
params: Optional[dict[str, Any]] = None,
|
|
68
|
+
json: Optional[Any] = None,
|
|
69
|
+
idempotency_key: Optional[str] = None,
|
|
70
|
+
) -> Any:
|
|
71
|
+
url = self._base_url + path
|
|
72
|
+
headers = {
|
|
73
|
+
"Authorization": f"Bearer {self._api_key}",
|
|
74
|
+
"User-Agent": _user_agent(),
|
|
75
|
+
"Accept": "application/json",
|
|
76
|
+
}
|
|
77
|
+
if json is not None:
|
|
78
|
+
headers["Content-Type"] = "application/json"
|
|
79
|
+
if method.upper() in _MUTATING_METHODS:
|
|
80
|
+
headers["Idempotency-Key"] = idempotency_key or str(uuid.uuid4())
|
|
81
|
+
|
|
82
|
+
last_exc: Optional[Exception] = None
|
|
83
|
+
for attempt in range(self._max_retries + 1):
|
|
84
|
+
try:
|
|
85
|
+
resp = self._client.request(method, url, params=params, json=json, headers=headers)
|
|
86
|
+
except httpx.HTTPError as exc:
|
|
87
|
+
net_err = NetworkError(f"network error: {exc}")
|
|
88
|
+
net_err.__cause__ = exc
|
|
89
|
+
last_exc = net_err
|
|
90
|
+
if attempt < self._max_retries:
|
|
91
|
+
self._sleep(attempt, retry_after=None)
|
|
92
|
+
continue
|
|
93
|
+
raise last_exc
|
|
94
|
+
else:
|
|
95
|
+
if resp.status_code in _RETRYABLE_STATUSES and attempt < self._max_retries:
|
|
96
|
+
retry_after = _parse_retry_after(resp)
|
|
97
|
+
self._sleep(attempt, retry_after=retry_after)
|
|
98
|
+
continue
|
|
99
|
+
return self._handle_response(resp)
|
|
100
|
+
|
|
101
|
+
# Unreachable in practice (loop returns or raises), but pacifies type checker.
|
|
102
|
+
if last_exc is not None:
|
|
103
|
+
raise last_exc
|
|
104
|
+
raise NetworkError("unreachable: exhausted retries with no exception")
|
|
105
|
+
|
|
106
|
+
def _sleep(self, attempt: int, retry_after: Optional[float]) -> None:
|
|
107
|
+
if retry_after is not None:
|
|
108
|
+
delay = retry_after
|
|
109
|
+
else:
|
|
110
|
+
base = 0.5 * (2 ** attempt)
|
|
111
|
+
jitter = random.uniform(0, 0.5)
|
|
112
|
+
delay = base + jitter
|
|
113
|
+
logger.debug("thrustlab: retrying after %.2fs (attempt %d)", delay, attempt + 1)
|
|
114
|
+
time.sleep(delay)
|
|
115
|
+
|
|
116
|
+
def _handle_response(self, resp: httpx.Response) -> Any:
|
|
117
|
+
if 200 <= resp.status_code < 300:
|
|
118
|
+
if resp.status_code == 204 or not resp.content:
|
|
119
|
+
return None
|
|
120
|
+
return resp.json()
|
|
121
|
+
|
|
122
|
+
# Error path. Parse envelope: { "error": { "type": ..., "code": ..., "message": ..., "param": ... } }.
|
|
123
|
+
request_id = resp.headers.get("X-Request-ID")
|
|
124
|
+
body: dict[str, Any] = {}
|
|
125
|
+
try:
|
|
126
|
+
body = resp.json()
|
|
127
|
+
except ValueError:
|
|
128
|
+
pass
|
|
129
|
+
env = body.get("error", {}) if isinstance(body, dict) else {}
|
|
130
|
+
|
|
131
|
+
kwargs = dict(
|
|
132
|
+
code=env.get("code"),
|
|
133
|
+
type=env.get("type"),
|
|
134
|
+
request_id=request_id,
|
|
135
|
+
http_status=resp.status_code,
|
|
136
|
+
param=env.get("param"),
|
|
137
|
+
)
|
|
138
|
+
message = env.get("message") or f"HTTP {resp.status_code}"
|
|
139
|
+
|
|
140
|
+
cls = from_response_status(resp.status_code)
|
|
141
|
+
if cls is RateLimitError:
|
|
142
|
+
return self._raise_rate_limit(message, kwargs, resp)
|
|
143
|
+
raise cls(message, **kwargs)
|
|
144
|
+
|
|
145
|
+
def _raise_rate_limit(self, message: str, kwargs: dict[str, Any], resp: httpx.Response) -> None:
|
|
146
|
+
retry_after = _parse_retry_after(resp)
|
|
147
|
+
raise RateLimitError(message, retry_after=retry_after, **kwargs)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _parse_retry_after(resp: httpx.Response) -> Optional[float]:
|
|
151
|
+
raw = resp.headers.get("Retry-After")
|
|
152
|
+
if not raw:
|
|
153
|
+
return None
|
|
154
|
+
try:
|
|
155
|
+
return float(raw)
|
|
156
|
+
except (TypeError, ValueError):
|
|
157
|
+
pass
|
|
158
|
+
try:
|
|
159
|
+
from email.utils import parsedate_to_datetime
|
|
160
|
+
dt = parsedate_to_datetime(raw)
|
|
161
|
+
if dt is not None:
|
|
162
|
+
delta = dt.timestamp() - time.time()
|
|
163
|
+
return max(0.0, delta)
|
|
164
|
+
except (TypeError, ValueError):
|
|
165
|
+
pass
|
|
166
|
+
return None
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Contains all the data models used in inputs/outputs"""
|
|
2
|
+
|
|
3
|
+
from thrustlab._models.api_key import ApiKey
|
|
4
|
+
from thrustlab._models.api_key_create import ApiKeyCreate
|
|
5
|
+
from thrustlab._models.api_key_with_secret import ApiKeyWithSecret
|
|
6
|
+
from thrustlab._models.billing_portal_session_resource import BillingPortalSessionResource
|
|
7
|
+
from thrustlab._models.checkout_session_resource import CheckoutSessionResource
|
|
8
|
+
from thrustlab._models.component_brand import ComponentBrand
|
|
9
|
+
from thrustlab._models.component_brands_response import ComponentBrandsResponse
|
|
10
|
+
from thrustlab._models.component_create_body import ComponentCreateBody
|
|
11
|
+
from thrustlab._models.component_create_body_spec_json import ComponentCreateBodySpecJson
|
|
12
|
+
from thrustlab._models.component_list_response import ComponentListResponse
|
|
13
|
+
from thrustlab._models.component_patch import ComponentPatch
|
|
14
|
+
from thrustlab._models.component_patch_spec_json_type_0 import ComponentPatchSpecJsonType0
|
|
15
|
+
from thrustlab._models.component_resource import ComponentResource
|
|
16
|
+
from thrustlab._models.component_resource_spec_json import ComponentResourceSpecJson
|
|
17
|
+
from thrustlab._models.create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post import (
|
|
18
|
+
CreateWebhookEndpointV1WebhookEndpointsPostResponseCreateWebhookEndpointV1WebhookEndpointsPost,
|
|
19
|
+
)
|
|
20
|
+
from thrustlab._models.credit_balance_resource import CreditBalanceResource
|
|
21
|
+
from thrustlab._models.credit_bucket_breakdown import CreditBucketBreakdown
|
|
22
|
+
from thrustlab._models.credit_bucket_breakdown_type import CreditBucketBreakdownType
|
|
23
|
+
from thrustlab._models.credit_usage_event_resource import CreditUsageEventResource
|
|
24
|
+
from thrustlab._models.credit_usage_event_resource_credit_type import CreditUsageEventResourceCreditType
|
|
25
|
+
from thrustlab._models.credit_usage_event_resource_type import CreditUsageEventResourceType
|
|
26
|
+
from thrustlab._models.credit_usage_list_response import CreditUsageListResponse
|
|
27
|
+
from thrustlab._models.credit_usage_related_resource import CreditUsageRelatedResource
|
|
28
|
+
from thrustlab._models.credit_usage_related_resource_object import CreditUsageRelatedResourceObject
|
|
29
|
+
from thrustlab._models.fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post import (
|
|
30
|
+
FireTestEventV1WebhookEndpointsPublicIdTestPostResponseFireTestEventV1WebhookEndpointsPublicIdTestPost,
|
|
31
|
+
)
|
|
32
|
+
from thrustlab._models.get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get import (
|
|
33
|
+
GetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGetResponseGetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGet,
|
|
34
|
+
)
|
|
35
|
+
from thrustlab._models.get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get import (
|
|
36
|
+
GetEventV1EventsEventIdGetResponseGetEventV1EventsEventIdGet,
|
|
37
|
+
)
|
|
38
|
+
from thrustlab._models.get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get import (
|
|
39
|
+
GetWebhookEndpointV1WebhookEndpointsPublicIdGetResponseGetWebhookEndpointV1WebhookEndpointsPublicIdGet,
|
|
40
|
+
)
|
|
41
|
+
from thrustlab._models.http_validation_error import HTTPValidationError
|
|
42
|
+
from thrustlab._models.list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get import (
|
|
43
|
+
ListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGetResponseListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGet,
|
|
44
|
+
)
|
|
45
|
+
from thrustlab._models.list_events_v1_events_get_response_list_events_v1_events_get import (
|
|
46
|
+
ListEventsV1EventsGetResponseListEventsV1EventsGet,
|
|
47
|
+
)
|
|
48
|
+
from thrustlab._models.list_response_api_key import ListResponseApiKey
|
|
49
|
+
from thrustlab._models.list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get import (
|
|
50
|
+
ListWebhookEndpointsV1WebhookEndpointsGetResponseListWebhookEndpointsV1WebhookEndpointsGet,
|
|
51
|
+
)
|
|
52
|
+
from thrustlab._models.patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch import (
|
|
53
|
+
PatchWebhookEndpointV1WebhookEndpointsPublicIdPatchResponsePatchWebhookEndpointV1WebhookEndpointsPublicIdPatch,
|
|
54
|
+
)
|
|
55
|
+
from thrustlab._models.portal_session_in import PortalSessionIn
|
|
56
|
+
from thrustlab._models.project_create_body import ProjectCreateBody
|
|
57
|
+
from thrustlab._models.project_list_response import ProjectListResponse
|
|
58
|
+
from thrustlab._models.project_patch import ProjectPatch
|
|
59
|
+
from thrustlab._models.project_resource import ProjectResource
|
|
60
|
+
from thrustlab._models.retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post import (
|
|
61
|
+
RetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPostResponseRetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPost,
|
|
62
|
+
)
|
|
63
|
+
from thrustlab._models.rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post import (
|
|
64
|
+
RotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPostResponseRotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPost,
|
|
65
|
+
)
|
|
66
|
+
from thrustlab._models.rotor_group_in import RotorGroupIn
|
|
67
|
+
from thrustlab._models.rotor_group_resource import RotorGroupResource
|
|
68
|
+
from thrustlab._models.simulation_cancel_body import SimulationCancelBody
|
|
69
|
+
from thrustlab._models.simulation_create_body import SimulationCreateBody
|
|
70
|
+
from thrustlab._models.simulation_inputs import SimulationInputs
|
|
71
|
+
from thrustlab._models.simulation_list_response import SimulationListResponse
|
|
72
|
+
from thrustlab._models.simulation_patch import SimulationPatch
|
|
73
|
+
from thrustlab._models.simulation_patch_plot_config_json_type_0_item import SimulationPatchPlotConfigJsonType0Item
|
|
74
|
+
from thrustlab._models.simulation_resource import SimulationResource
|
|
75
|
+
from thrustlab._models.simulation_resource_error_type_0 import SimulationResourceErrorType0
|
|
76
|
+
from thrustlab._models.simulation_resource_plot_config_json_type_0_item import SimulationResourcePlotConfigJsonType0Item
|
|
77
|
+
from thrustlab._models.simulation_resource_result_type_0 import SimulationResourceResultType0
|
|
78
|
+
from thrustlab._models.starred_component_create_body import StarredComponentCreateBody
|
|
79
|
+
from thrustlab._models.starred_component_list_response import StarredComponentListResponse
|
|
80
|
+
from thrustlab._models.starred_component_resource import StarredComponentResource
|
|
81
|
+
from thrustlab._models.submission_create_body import SubmissionCreateBody
|
|
82
|
+
from thrustlab._models.submission_create_body_data_json import SubmissionCreateBodyDataJson
|
|
83
|
+
from thrustlab._models.submission_list_response import SubmissionListResponse
|
|
84
|
+
from thrustlab._models.submission_patch import SubmissionPatch
|
|
85
|
+
from thrustlab._models.submission_patch_data_json_type_0 import SubmissionPatchDataJsonType0
|
|
86
|
+
from thrustlab._models.submission_resource import SubmissionResource
|
|
87
|
+
from thrustlab._models.submission_resource_data_json import SubmissionResourceDataJson
|
|
88
|
+
from thrustlab._models.subscription_create import SubscriptionCreate
|
|
89
|
+
from thrustlab._models.subscription_resource import SubscriptionResource
|
|
90
|
+
from thrustlab._models.subscription_resource_status import SubscriptionResourceStatus
|
|
91
|
+
from thrustlab._models.subscription_resource_tier import SubscriptionResourceTier
|
|
92
|
+
from thrustlab._models.sweep_cancel_body import SweepCancelBody
|
|
93
|
+
from thrustlab._models.sweep_config_in import SweepConfigIn
|
|
94
|
+
from thrustlab._models.sweep_create_body import SweepCreateBody
|
|
95
|
+
from thrustlab._models.sweep_inputs import SweepInputs
|
|
96
|
+
from thrustlab._models.sweep_list_response import SweepListResponse
|
|
97
|
+
from thrustlab._models.sweep_param_range_in import SweepParamRangeIn
|
|
98
|
+
from thrustlab._models.sweep_param_range_in_mode import SweepParamRangeInMode
|
|
99
|
+
from thrustlab._models.sweep_patch import SweepPatch
|
|
100
|
+
from thrustlab._models.sweep_patch_plot_config_json_type_0_item import SweepPatchPlotConfigJsonType0Item
|
|
101
|
+
from thrustlab._models.sweep_point_list_response import SweepPointListResponse
|
|
102
|
+
from thrustlab._models.sweep_point_resource import SweepPointResource
|
|
103
|
+
from thrustlab._models.sweep_point_resource_inputs import SweepPointResourceInputs
|
|
104
|
+
from thrustlab._models.sweep_point_resource_rotors_type_0 import SweepPointResourceRotorsType0
|
|
105
|
+
from thrustlab._models.sweep_resource import SweepResource
|
|
106
|
+
from thrustlab._models.sweep_resource_error_type_0 import SweepResourceErrorType0
|
|
107
|
+
from thrustlab._models.sweep_resource_plot_config_json_type_0_item import SweepResourcePlotConfigJsonType0Item
|
|
108
|
+
from thrustlab._models.sweep_resource_summary_type_0 import SweepResourceSummaryType0
|
|
109
|
+
from thrustlab._models.sweep_resource_sweep_config_type_0 import SweepResourceSweepConfigType0
|
|
110
|
+
from thrustlab._models.sweep_rotor_group_in import SweepRotorGroupIn
|
|
111
|
+
from thrustlab._models.user_patch import UserPatch
|
|
112
|
+
from thrustlab._models.user_resource import UserResource
|
|
113
|
+
from thrustlab._models.v1_health_v1_health_get_response_v1_health_v1_health_get import V1HealthV1HealthGetResponseV1HealthV1HealthGet
|
|
114
|
+
from thrustlab._models.validation_error import ValidationError
|
|
115
|
+
from thrustlab._models.webhook_endpoint_create import WebhookEndpointCreate
|
|
116
|
+
from thrustlab._models.webhook_endpoint_patch import WebhookEndpointPatch
|
|
117
|
+
|
|
118
|
+
__all__ = (
|
|
119
|
+
"ApiKey",
|
|
120
|
+
"ApiKeyCreate",
|
|
121
|
+
"ApiKeyWithSecret",
|
|
122
|
+
"BillingPortalSessionResource",
|
|
123
|
+
"CheckoutSessionResource",
|
|
124
|
+
"ComponentBrand",
|
|
125
|
+
"ComponentBrandsResponse",
|
|
126
|
+
"ComponentCreateBody",
|
|
127
|
+
"ComponentCreateBodySpecJson",
|
|
128
|
+
"ComponentListResponse",
|
|
129
|
+
"ComponentPatch",
|
|
130
|
+
"ComponentPatchSpecJsonType0",
|
|
131
|
+
"ComponentResource",
|
|
132
|
+
"ComponentResourceSpecJson",
|
|
133
|
+
"CreateWebhookEndpointV1WebhookEndpointsPostResponseCreateWebhookEndpointV1WebhookEndpointsPost",
|
|
134
|
+
"CreditBalanceResource",
|
|
135
|
+
"CreditBucketBreakdown",
|
|
136
|
+
"CreditBucketBreakdownType",
|
|
137
|
+
"CreditUsageEventResource",
|
|
138
|
+
"CreditUsageEventResourceCreditType",
|
|
139
|
+
"CreditUsageEventResourceType",
|
|
140
|
+
"CreditUsageListResponse",
|
|
141
|
+
"CreditUsageRelatedResource",
|
|
142
|
+
"CreditUsageRelatedResourceObject",
|
|
143
|
+
"FireTestEventV1WebhookEndpointsPublicIdTestPostResponseFireTestEventV1WebhookEndpointsPublicIdTestPost",
|
|
144
|
+
"GetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGetResponseGetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGet",
|
|
145
|
+
"GetEventV1EventsEventIdGetResponseGetEventV1EventsEventIdGet",
|
|
146
|
+
"GetWebhookEndpointV1WebhookEndpointsPublicIdGetResponseGetWebhookEndpointV1WebhookEndpointsPublicIdGet",
|
|
147
|
+
"HTTPValidationError",
|
|
148
|
+
"ListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGetResponseListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGet",
|
|
149
|
+
"ListEventsV1EventsGetResponseListEventsV1EventsGet",
|
|
150
|
+
"ListResponseApiKey",
|
|
151
|
+
"ListWebhookEndpointsV1WebhookEndpointsGetResponseListWebhookEndpointsV1WebhookEndpointsGet",
|
|
152
|
+
"PatchWebhookEndpointV1WebhookEndpointsPublicIdPatchResponsePatchWebhookEndpointV1WebhookEndpointsPublicIdPatch",
|
|
153
|
+
"PortalSessionIn",
|
|
154
|
+
"ProjectCreateBody",
|
|
155
|
+
"ProjectListResponse",
|
|
156
|
+
"ProjectPatch",
|
|
157
|
+
"ProjectResource",
|
|
158
|
+
"RetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPostResponseRetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPost",
|
|
159
|
+
"RotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPostResponseRotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPost",
|
|
160
|
+
"RotorGroupIn",
|
|
161
|
+
"RotorGroupResource",
|
|
162
|
+
"SimulationCancelBody",
|
|
163
|
+
"SimulationCreateBody",
|
|
164
|
+
"SimulationInputs",
|
|
165
|
+
"SimulationListResponse",
|
|
166
|
+
"SimulationPatch",
|
|
167
|
+
"SimulationPatchPlotConfigJsonType0Item",
|
|
168
|
+
"SimulationResource",
|
|
169
|
+
"SimulationResourceErrorType0",
|
|
170
|
+
"SimulationResourcePlotConfigJsonType0Item",
|
|
171
|
+
"SimulationResourceResultType0",
|
|
172
|
+
"StarredComponentCreateBody",
|
|
173
|
+
"StarredComponentListResponse",
|
|
174
|
+
"StarredComponentResource",
|
|
175
|
+
"SubmissionCreateBody",
|
|
176
|
+
"SubmissionCreateBodyDataJson",
|
|
177
|
+
"SubmissionListResponse",
|
|
178
|
+
"SubmissionPatch",
|
|
179
|
+
"SubmissionPatchDataJsonType0",
|
|
180
|
+
"SubmissionResource",
|
|
181
|
+
"SubmissionResourceDataJson",
|
|
182
|
+
"SubscriptionCreate",
|
|
183
|
+
"SubscriptionResource",
|
|
184
|
+
"SubscriptionResourceStatus",
|
|
185
|
+
"SubscriptionResourceTier",
|
|
186
|
+
"SweepCancelBody",
|
|
187
|
+
"SweepConfigIn",
|
|
188
|
+
"SweepCreateBody",
|
|
189
|
+
"SweepInputs",
|
|
190
|
+
"SweepListResponse",
|
|
191
|
+
"SweepParamRangeIn",
|
|
192
|
+
"SweepParamRangeInMode",
|
|
193
|
+
"SweepPatch",
|
|
194
|
+
"SweepPatchPlotConfigJsonType0Item",
|
|
195
|
+
"SweepPointListResponse",
|
|
196
|
+
"SweepPointResource",
|
|
197
|
+
"SweepPointResourceInputs",
|
|
198
|
+
"SweepPointResourceRotorsType0",
|
|
199
|
+
"SweepResource",
|
|
200
|
+
"SweepResourceErrorType0",
|
|
201
|
+
"SweepResourcePlotConfigJsonType0Item",
|
|
202
|
+
"SweepResourceSummaryType0",
|
|
203
|
+
"SweepResourceSweepConfigType0",
|
|
204
|
+
"SweepRotorGroupIn",
|
|
205
|
+
"UserPatch",
|
|
206
|
+
"UserResource",
|
|
207
|
+
"V1HealthV1HealthGetResponseV1HealthV1HealthGet",
|
|
208
|
+
"ValidationError",
|
|
209
|
+
"WebhookEndpointCreate",
|
|
210
|
+
"WebhookEndpointPatch",
|
|
211
|
+
)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from typing import Any, Literal, TypeVar, cast
|
|
6
|
+
|
|
7
|
+
from attrs import define as _attrs_define
|
|
8
|
+
from attrs import field as _attrs_field
|
|
9
|
+
from dateutil.parser import isoparse
|
|
10
|
+
|
|
11
|
+
from thrustlab._models.types import UNSET, Unset
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T", bound="ApiKey")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@_attrs_define
|
|
17
|
+
class ApiKey:
|
|
18
|
+
"""API key resource as returned by LIST and GET.
|
|
19
|
+
|
|
20
|
+
NEVER includes the raw ``key`` field — that field only appears in the
|
|
21
|
+
POST response body via :class:`ApiKeyWithSecret` below.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
created_at (datetime.datetime):
|
|
25
|
+
id (str):
|
|
26
|
+
key_prefix (str):
|
|
27
|
+
key_suffix (str):
|
|
28
|
+
last_used_at (datetime.datetime | None | Unset):
|
|
29
|
+
name (None | str | Unset):
|
|
30
|
+
object_ (Literal['api_key'] | Unset): Default: 'api_key'.
|
|
31
|
+
revoked_at (datetime.datetime | None | Unset):
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
created_at: datetime.datetime
|
|
35
|
+
id: str
|
|
36
|
+
key_prefix: str
|
|
37
|
+
key_suffix: str
|
|
38
|
+
last_used_at: datetime.datetime | None | Unset = UNSET
|
|
39
|
+
name: None | str | Unset = UNSET
|
|
40
|
+
object_: Literal["api_key"] | Unset = "api_key"
|
|
41
|
+
revoked_at: datetime.datetime | None | Unset = UNSET
|
|
42
|
+
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> dict[str, Any]:
|
|
45
|
+
created_at = self.created_at.isoformat()
|
|
46
|
+
|
|
47
|
+
id = self.id
|
|
48
|
+
|
|
49
|
+
key_prefix = self.key_prefix
|
|
50
|
+
|
|
51
|
+
key_suffix = self.key_suffix
|
|
52
|
+
|
|
53
|
+
last_used_at: None | str | Unset
|
|
54
|
+
if isinstance(self.last_used_at, Unset):
|
|
55
|
+
last_used_at = UNSET
|
|
56
|
+
elif isinstance(self.last_used_at, datetime.datetime):
|
|
57
|
+
last_used_at = self.last_used_at.isoformat()
|
|
58
|
+
else:
|
|
59
|
+
last_used_at = self.last_used_at
|
|
60
|
+
|
|
61
|
+
name: None | str | Unset
|
|
62
|
+
if isinstance(self.name, Unset):
|
|
63
|
+
name = UNSET
|
|
64
|
+
else:
|
|
65
|
+
name = self.name
|
|
66
|
+
|
|
67
|
+
object_ = self.object_
|
|
68
|
+
|
|
69
|
+
revoked_at: None | str | Unset
|
|
70
|
+
if isinstance(self.revoked_at, Unset):
|
|
71
|
+
revoked_at = UNSET
|
|
72
|
+
elif isinstance(self.revoked_at, datetime.datetime):
|
|
73
|
+
revoked_at = self.revoked_at.isoformat()
|
|
74
|
+
else:
|
|
75
|
+
revoked_at = self.revoked_at
|
|
76
|
+
|
|
77
|
+
field_dict: dict[str, Any] = {}
|
|
78
|
+
field_dict.update(self.additional_properties)
|
|
79
|
+
field_dict.update(
|
|
80
|
+
{
|
|
81
|
+
"created_at": created_at,
|
|
82
|
+
"id": id,
|
|
83
|
+
"key_prefix": key_prefix,
|
|
84
|
+
"key_suffix": key_suffix,
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
if last_used_at is not UNSET:
|
|
88
|
+
field_dict["last_used_at"] = last_used_at
|
|
89
|
+
if name is not UNSET:
|
|
90
|
+
field_dict["name"] = name
|
|
91
|
+
if object_ is not UNSET:
|
|
92
|
+
field_dict["object"] = object_
|
|
93
|
+
if revoked_at is not UNSET:
|
|
94
|
+
field_dict["revoked_at"] = revoked_at
|
|
95
|
+
|
|
96
|
+
return field_dict
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
100
|
+
d = dict(src_dict)
|
|
101
|
+
created_at = isoparse(d.pop("created_at"))
|
|
102
|
+
|
|
103
|
+
id = d.pop("id")
|
|
104
|
+
|
|
105
|
+
key_prefix = d.pop("key_prefix")
|
|
106
|
+
|
|
107
|
+
key_suffix = d.pop("key_suffix")
|
|
108
|
+
|
|
109
|
+
def _parse_last_used_at(data: object) -> datetime.datetime | None | Unset:
|
|
110
|
+
if data is None:
|
|
111
|
+
return data
|
|
112
|
+
if isinstance(data, Unset):
|
|
113
|
+
return data
|
|
114
|
+
try:
|
|
115
|
+
if not isinstance(data, str):
|
|
116
|
+
raise TypeError()
|
|
117
|
+
last_used_at_type_0 = isoparse(data)
|
|
118
|
+
|
|
119
|
+
return last_used_at_type_0
|
|
120
|
+
except (TypeError, ValueError, AttributeError, KeyError):
|
|
121
|
+
pass
|
|
122
|
+
return cast(datetime.datetime | None | Unset, data)
|
|
123
|
+
|
|
124
|
+
last_used_at = _parse_last_used_at(d.pop("last_used_at", UNSET))
|
|
125
|
+
|
|
126
|
+
def _parse_name(data: object) -> None | str | Unset:
|
|
127
|
+
if data is None:
|
|
128
|
+
return data
|
|
129
|
+
if isinstance(data, Unset):
|
|
130
|
+
return data
|
|
131
|
+
return cast(None | str | Unset, data)
|
|
132
|
+
|
|
133
|
+
name = _parse_name(d.pop("name", UNSET))
|
|
134
|
+
|
|
135
|
+
object_ = cast(Literal["api_key"] | Unset, d.pop("object", UNSET))
|
|
136
|
+
if object_ != "api_key" and not isinstance(object_, Unset):
|
|
137
|
+
raise ValueError(f"object must match const 'api_key', got '{object_}'")
|
|
138
|
+
|
|
139
|
+
def _parse_revoked_at(data: object) -> datetime.datetime | None | Unset:
|
|
140
|
+
if data is None:
|
|
141
|
+
return data
|
|
142
|
+
if isinstance(data, Unset):
|
|
143
|
+
return data
|
|
144
|
+
try:
|
|
145
|
+
if not isinstance(data, str):
|
|
146
|
+
raise TypeError()
|
|
147
|
+
revoked_at_type_0 = isoparse(data)
|
|
148
|
+
|
|
149
|
+
return revoked_at_type_0
|
|
150
|
+
except (TypeError, ValueError, AttributeError, KeyError):
|
|
151
|
+
pass
|
|
152
|
+
return cast(datetime.datetime | None | Unset, data)
|
|
153
|
+
|
|
154
|
+
revoked_at = _parse_revoked_at(d.pop("revoked_at", UNSET))
|
|
155
|
+
|
|
156
|
+
api_key = cls(
|
|
157
|
+
created_at=created_at,
|
|
158
|
+
id=id,
|
|
159
|
+
key_prefix=key_prefix,
|
|
160
|
+
key_suffix=key_suffix,
|
|
161
|
+
last_used_at=last_used_at,
|
|
162
|
+
name=name,
|
|
163
|
+
object_=object_,
|
|
164
|
+
revoked_at=revoked_at,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
api_key.additional_properties = d
|
|
168
|
+
return api_key
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def additional_keys(self) -> list[str]:
|
|
172
|
+
return list(self.additional_properties.keys())
|
|
173
|
+
|
|
174
|
+
def __getitem__(self, key: str) -> Any:
|
|
175
|
+
return self.additional_properties[key]
|
|
176
|
+
|
|
177
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
178
|
+
self.additional_properties[key] = value
|
|
179
|
+
|
|
180
|
+
def __delitem__(self, key: str) -> None:
|
|
181
|
+
del self.additional_properties[key]
|
|
182
|
+
|
|
183
|
+
def __contains__(self, key: str) -> bool:
|
|
184
|
+
return key in self.additional_properties
|