motor-python-sdk 0.0.2__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 (60) hide show
  1. motor_python_sdk-0.0.2.dist-info/METADATA +230 -0
  2. motor_python_sdk-0.0.2.dist-info/RECORD +60 -0
  3. motor_python_sdk-0.0.2.dist-info/WHEEL +4 -0
  4. yasminaai/__init__.py +113 -0
  5. yasminaai/_default_clients.py +32 -0
  6. yasminaai/client.py +255 -0
  7. yasminaai/core/__init__.py +127 -0
  8. yasminaai/core/api_error.py +23 -0
  9. yasminaai/core/client_wrapper.py +119 -0
  10. yasminaai/core/datetime_utils.py +70 -0
  11. yasminaai/core/file.py +67 -0
  12. yasminaai/core/force_multipart.py +18 -0
  13. yasminaai/core/http_client.py +839 -0
  14. yasminaai/core/http_response.py +59 -0
  15. yasminaai/core/http_sse/__init__.py +42 -0
  16. yasminaai/core/http_sse/_api.py +170 -0
  17. yasminaai/core/http_sse/_decoders.py +61 -0
  18. yasminaai/core/http_sse/_exceptions.py +7 -0
  19. yasminaai/core/http_sse/_models.py +17 -0
  20. yasminaai/core/jsonable_encoder.py +120 -0
  21. yasminaai/core/logging.py +107 -0
  22. yasminaai/core/parse_error.py +36 -0
  23. yasminaai/core/pydantic_utilities.py +508 -0
  24. yasminaai/core/query_encoder.py +58 -0
  25. yasminaai/core/remove_none_from_dict.py +11 -0
  26. yasminaai/core/request_options.py +35 -0
  27. yasminaai/core/serialization.py +347 -0
  28. yasminaai/environment.py +7 -0
  29. yasminaai/errors/__init__.py +42 -0
  30. yasminaai/errors/bad_request_error.py +10 -0
  31. yasminaai/errors/not_found_error.py +10 -0
  32. yasminaai/errors/unauthorized_error.py +10 -0
  33. yasminaai/errors/unprocessable_entity_error.py +10 -0
  34. yasminaai/ot_ps/__init__.py +4 -0
  35. yasminaai/ot_ps/client.py +278 -0
  36. yasminaai/ot_ps/raw_client.py +355 -0
  37. yasminaai/policies/__init__.py +4 -0
  38. yasminaai/policies/client.py +393 -0
  39. yasminaai/policies/raw_client.py +493 -0
  40. yasminaai/py.typed +0 -0
  41. yasminaai/quotes/__init__.py +49 -0
  42. yasminaai/quotes/client.py +438 -0
  43. yasminaai/quotes/raw_client.py +548 -0
  44. yasminaai/quotes/types/__init__.py +47 -0
  45. yasminaai/quotes/types/delete_quote_requests_id_response.py +19 -0
  46. yasminaai/quotes/types/get_quote_requests_response.py +37 -0
  47. yasminaai/quotes/types/get_quote_requests_response_links_item.py +21 -0
  48. yasminaai/quotes/types/post_quote_requests_request_drivers_item.py +33 -0
  49. yasminaai/types/__init__.py +65 -0
  50. yasminaai/types/bad_request_error_body.py +20 -0
  51. yasminaai/types/benefit.py +24 -0
  52. yasminaai/types/company_quote.py +23 -0
  53. yasminaai/types/error.py +20 -0
  54. yasminaai/types/policy.py +33 -0
  55. yasminaai/types/quote_price.py +24 -0
  56. yasminaai/types/quote_response.py +90 -0
  57. yasminaai/types/quote_response_drivers_item.py +33 -0
  58. yasminaai/types/quote_response_quotes_item.py +30 -0
  59. yasminaai/types/unauthorized_error_body.py +20 -0
  60. yasminaai/version.py +3 -0
@@ -0,0 +1,347 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import collections
4
+ import inspect
5
+ import typing
6
+
7
+ import pydantic
8
+ import typing_extensions
9
+
10
+
11
+ class FieldMetadata:
12
+ """
13
+ Metadata class used to annotate fields to provide additional information.
14
+
15
+ Example:
16
+ class MyDict(TypedDict):
17
+ field: typing.Annotated[str, FieldMetadata(alias="field_name")]
18
+
19
+ Will serialize: `{"field": "value"}`
20
+ To: `{"field_name": "value"}`
21
+ """
22
+
23
+ alias: str
24
+
25
+ def __init__(self, *, alias: str) -> None:
26
+ self.alias = alias
27
+
28
+
29
+ # Resolving type hints (typing.get_type_hints) is expensive because it eval/compiles
30
+ # forward-reference annotations. The result is constant for a given type, so we cache it.
31
+ # This is critical for hot paths like SSE event parsing, where the same (often large
32
+ # discriminated-union) type is converted on every single event.
33
+ _type_hints_cache: typing.Dict[typing.Any, typing.Dict[str, typing.Any]] = {}
34
+
35
+
36
+ def _get_cached_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
37
+ try:
38
+ cached = _type_hints_cache.get(expected_type)
39
+ except TypeError:
40
+ # Unhashable type; resolve without caching.
41
+ return _resolve_type_hints(expected_type)
42
+ if cached is None:
43
+ cached = _resolve_type_hints(expected_type)
44
+ _type_hints_cache[expected_type] = cached
45
+ return cached
46
+
47
+
48
+ def _resolve_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
49
+ try:
50
+ return typing_extensions.get_type_hints(expected_type, include_extras=True)
51
+ except NameError:
52
+ # The type contains a circular reference, so we use the __annotations__ attribute directly.
53
+ return getattr(expected_type, "__annotations__", {})
54
+
55
+
56
+ # Whether convert_and_respect_annotation_metadata can possibly rewrite anything for a given
57
+ # annotation, i.e. whether any reachable model/TypedDict field carries a FieldMetadata alias.
58
+ # This is constant per type, so we cache it and use it to short-circuit the recursive walk.
59
+ _requires_conversion_cache: typing.Dict[typing.Any, bool] = {}
60
+
61
+
62
+ def _requires_conversion(type_: typing.Any) -> bool:
63
+ try:
64
+ cached = _requires_conversion_cache.get(type_)
65
+ except TypeError:
66
+ # Unhashable annotation; compute without caching.
67
+ return _compute_requires_conversion(type_, set())
68
+ if cached is None:
69
+ cached = _compute_requires_conversion(type_, set())
70
+ _requires_conversion_cache[type_] = cached
71
+ return cached
72
+
73
+
74
+ def _compute_requires_conversion(type_: typing.Any, seen: typing.Set[typing.Any]) -> bool:
75
+ clean_type = _remove_annotations(type_)
76
+
77
+ try:
78
+ if clean_type in seen:
79
+ return False
80
+ seen = seen | {clean_type}
81
+ except TypeError:
82
+ # Unhashable type; skip cycle tracking (the type graph is finite in practice).
83
+ pass
84
+
85
+ # Models / TypedDicts: a field alias here means we must dealias; otherwise recurse into fields.
86
+ if (inspect.isclass(clean_type) and issubclass(clean_type, pydantic.BaseModel)) or typing_extensions.is_typeddict(
87
+ clean_type
88
+ ):
89
+ annotations = _get_cached_type_hints(clean_type)
90
+ if _get_alias_to_field_name(annotations):
91
+ return True
92
+ return any(_compute_requires_conversion(hint, seen) for hint in annotations.values())
93
+
94
+ # Containers / unions: recurse into the type arguments (List/Set/Sequence/Dict/Union/etc.).
95
+ return any(_compute_requires_conversion(arg, seen) for arg in typing_extensions.get_args(clean_type))
96
+
97
+
98
+ def convert_and_respect_annotation_metadata(
99
+ *,
100
+ object_: typing.Any,
101
+ annotation: typing.Any,
102
+ inner_type: typing.Optional[typing.Any] = None,
103
+ direction: typing.Literal["read", "write"],
104
+ ) -> typing.Any:
105
+ """
106
+ Respect the metadata annotations on a field, such as aliasing. This function effectively
107
+ manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for
108
+ TypedDicts, which cannot support aliasing out of the box, and can be extended for additional
109
+ utilities, such as defaults.
110
+
111
+ Parameters
112
+ ----------
113
+ object_ : typing.Any
114
+
115
+ annotation : type
116
+ The type we're looking to apply typing annotations from
117
+
118
+ inner_type : typing.Optional[type]
119
+
120
+ Returns
121
+ -------
122
+ typing.Any
123
+ """
124
+
125
+ if object_ is None:
126
+ return None
127
+ if inner_type is None:
128
+ inner_type = annotation
129
+ # The only thing this function ever rewrites is keys that carry a FieldMetadata
130
+ # alias. If nothing in the (cached) type graph has such an alias, the conversion is
131
+ # a content-identity transform, so we can skip the entire recursive walk. This is
132
+ # the hot path for SSE streaming, where a large discriminated union would otherwise
133
+ # be traversed on every single event.
134
+ if not _requires_conversion(annotation):
135
+ return object_
136
+
137
+ clean_type = _remove_annotations(inner_type)
138
+ # Pydantic models
139
+ if (
140
+ inspect.isclass(clean_type)
141
+ and issubclass(clean_type, pydantic.BaseModel)
142
+ and isinstance(object_, typing.Mapping)
143
+ ):
144
+ return _convert_mapping(object_, clean_type, direction)
145
+ # TypedDicts
146
+ if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping):
147
+ return _convert_mapping(object_, clean_type, direction)
148
+
149
+ if (
150
+ typing_extensions.get_origin(clean_type) == typing.Dict
151
+ or typing_extensions.get_origin(clean_type) == dict
152
+ or clean_type == typing.Dict
153
+ ) and isinstance(object_, typing.Dict):
154
+ key_type = typing_extensions.get_args(clean_type)[0]
155
+ value_type = typing_extensions.get_args(clean_type)[1]
156
+
157
+ return {
158
+ key: convert_and_respect_annotation_metadata(
159
+ object_=value,
160
+ annotation=annotation,
161
+ inner_type=value_type,
162
+ direction=direction,
163
+ )
164
+ for key, value in object_.items()
165
+ }
166
+
167
+ # If you're iterating on a string, do not bother to coerce it to a sequence.
168
+ if not isinstance(object_, str):
169
+ if (
170
+ typing_extensions.get_origin(clean_type) == typing.Set
171
+ or typing_extensions.get_origin(clean_type) == set
172
+ or clean_type == typing.Set
173
+ ) and isinstance(object_, typing.Set):
174
+ inner_type = typing_extensions.get_args(clean_type)[0]
175
+ return {
176
+ convert_and_respect_annotation_metadata(
177
+ object_=item,
178
+ annotation=annotation,
179
+ inner_type=inner_type,
180
+ direction=direction,
181
+ )
182
+ for item in object_
183
+ }
184
+ elif (
185
+ (
186
+ typing_extensions.get_origin(clean_type) == typing.List
187
+ or typing_extensions.get_origin(clean_type) == list
188
+ or clean_type == typing.List
189
+ )
190
+ and isinstance(object_, typing.List)
191
+ ) or (
192
+ (
193
+ typing_extensions.get_origin(clean_type) == typing.Sequence
194
+ or typing_extensions.get_origin(clean_type) == collections.abc.Sequence
195
+ or clean_type == typing.Sequence
196
+ )
197
+ and isinstance(object_, typing.Sequence)
198
+ ):
199
+ inner_type = typing_extensions.get_args(clean_type)[0]
200
+ return [
201
+ convert_and_respect_annotation_metadata(
202
+ object_=item,
203
+ annotation=annotation,
204
+ inner_type=inner_type,
205
+ direction=direction,
206
+ )
207
+ for item in object_
208
+ ]
209
+
210
+ if typing_extensions.get_origin(clean_type) == typing.Union:
211
+ # We should be able to ~relatively~ safely try to convert keys against all
212
+ # member types in the union, the edge case here is if one member aliases a field
213
+ # of the same name to a different name from another member
214
+ # Or if another member aliases a field of the same name that another member does not.
215
+ for member in typing_extensions.get_args(clean_type):
216
+ object_ = convert_and_respect_annotation_metadata(
217
+ object_=object_,
218
+ annotation=annotation,
219
+ inner_type=member,
220
+ direction=direction,
221
+ )
222
+ return object_
223
+
224
+ annotated_type = _get_annotation(annotation)
225
+ if annotated_type is None:
226
+ return object_
227
+
228
+ # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.)
229
+ # Then we can safely call it on the recursive conversion.
230
+ return object_
231
+
232
+
233
+ def _convert_mapping(
234
+ object_: typing.Mapping[str, object],
235
+ expected_type: typing.Any,
236
+ direction: typing.Literal["read", "write"],
237
+ ) -> typing.Mapping[str, object]:
238
+ converted_object: typing.Dict[str, object] = {}
239
+ annotations = _get_cached_type_hints(expected_type)
240
+ aliases_to_field_names = _get_alias_to_field_name(annotations)
241
+ for key, value in object_.items():
242
+ if direction == "read" and key in aliases_to_field_names:
243
+ dealiased_key = aliases_to_field_names.get(key)
244
+ if dealiased_key is not None:
245
+ type_ = annotations.get(dealiased_key)
246
+ else:
247
+ type_ = annotations.get(key)
248
+ # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map
249
+ #
250
+ # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias
251
+ # then we can just pass the value through as is
252
+ if type_ is None:
253
+ converted_object[key] = value
254
+ elif direction == "read" and key not in aliases_to_field_names:
255
+ converted_object[key] = convert_and_respect_annotation_metadata(
256
+ object_=value, annotation=type_, direction=direction
257
+ )
258
+ else:
259
+ converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = (
260
+ convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction)
261
+ )
262
+ return converted_object
263
+
264
+
265
+ def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]:
266
+ maybe_annotated_type = typing_extensions.get_origin(type_)
267
+ if maybe_annotated_type is None:
268
+ return None
269
+
270
+ if maybe_annotated_type == typing_extensions.NotRequired:
271
+ type_ = typing_extensions.get_args(type_)[0]
272
+ maybe_annotated_type = typing_extensions.get_origin(type_)
273
+
274
+ if maybe_annotated_type == typing_extensions.Annotated:
275
+ return type_
276
+
277
+ return None
278
+
279
+
280
+ def _remove_annotations(type_: typing.Any) -> typing.Any:
281
+ maybe_annotated_type = typing_extensions.get_origin(type_)
282
+ if maybe_annotated_type is None:
283
+ return type_
284
+
285
+ if maybe_annotated_type == typing_extensions.NotRequired:
286
+ return _remove_annotations(typing_extensions.get_args(type_)[0])
287
+
288
+ if maybe_annotated_type == typing_extensions.Annotated:
289
+ return _remove_annotations(typing_extensions.get_args(type_)[0])
290
+
291
+ return type_
292
+
293
+
294
+ def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]:
295
+ annotations = _get_cached_type_hints(type_)
296
+ return _get_alias_to_field_name(annotations)
297
+
298
+
299
+ def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]:
300
+ annotations = _get_cached_type_hints(type_)
301
+ return _get_field_to_alias_name(annotations)
302
+
303
+
304
+ def _get_alias_to_field_name(
305
+ field_to_hint: typing.Dict[str, typing.Any],
306
+ ) -> typing.Dict[str, str]:
307
+ aliases = {}
308
+ for field, hint in field_to_hint.items():
309
+ maybe_alias = _get_alias_from_type(hint)
310
+ if maybe_alias is not None:
311
+ aliases[maybe_alias] = field
312
+ return aliases
313
+
314
+
315
+ def _get_field_to_alias_name(
316
+ field_to_hint: typing.Dict[str, typing.Any],
317
+ ) -> typing.Dict[str, str]:
318
+ aliases = {}
319
+ for field, hint in field_to_hint.items():
320
+ maybe_alias = _get_alias_from_type(hint)
321
+ if maybe_alias is not None:
322
+ aliases[field] = maybe_alias
323
+ return aliases
324
+
325
+
326
+ def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]:
327
+ maybe_annotated_type = _get_annotation(type_)
328
+
329
+ if maybe_annotated_type is not None:
330
+ # The actual annotations are 1 onward, the first is the annotated type
331
+ annotations = typing_extensions.get_args(maybe_annotated_type)[1:]
332
+
333
+ for annotation in annotations:
334
+ if isinstance(annotation, FieldMetadata) and annotation.alias is not None:
335
+ return annotation.alias
336
+ return None
337
+
338
+
339
+ def _alias_key(
340
+ key: str,
341
+ type_: typing.Any,
342
+ direction: typing.Literal["read", "write"],
343
+ aliases_to_field_names: typing.Dict[str, str],
344
+ ) -> str:
345
+ if direction == "read":
346
+ return aliases_to_field_names.get(key, key)
347
+ return _get_alias_from_type(type_=type_) or key
@@ -0,0 +1,7 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import enum
4
+
5
+
6
+ class YasminaaiApiEnvironment(enum.Enum):
7
+ DEFAULT = "https://staging.yasmina.ai/api/v1/car-comp"
@@ -0,0 +1,42 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+
5
+ import typing
6
+ from importlib import import_module
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from .bad_request_error import BadRequestError
10
+ from .not_found_error import NotFoundError
11
+ from .unauthorized_error import UnauthorizedError
12
+ from .unprocessable_entity_error import UnprocessableEntityError
13
+ _dynamic_imports: typing.Dict[str, str] = {
14
+ "BadRequestError": ".bad_request_error",
15
+ "NotFoundError": ".not_found_error",
16
+ "UnauthorizedError": ".unauthorized_error",
17
+ "UnprocessableEntityError": ".unprocessable_entity_error",
18
+ }
19
+
20
+
21
+ def __getattr__(attr_name: str) -> typing.Any:
22
+ module_name = _dynamic_imports.get(attr_name)
23
+ if module_name is None:
24
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
25
+ try:
26
+ module = import_module(module_name, __package__)
27
+ if module_name == f".{attr_name}":
28
+ return module
29
+ else:
30
+ return getattr(module, attr_name)
31
+ except ImportError as e:
32
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
33
+ except AttributeError as e:
34
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
35
+
36
+
37
+ def __dir__():
38
+ lazy_attrs = list(_dynamic_imports.keys())
39
+ return sorted(lazy_attrs)
40
+
41
+
42
+ __all__ = ["BadRequestError", "NotFoundError", "UnauthorizedError", "UnprocessableEntityError"]
@@ -0,0 +1,10 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+
7
+
8
+ class BadRequestError(ApiError):
9
+ def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=400, headers=headers, body=body)
@@ -0,0 +1,10 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+
7
+
8
+ class NotFoundError(ApiError):
9
+ def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=404, headers=headers, body=body)
@@ -0,0 +1,10 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+
7
+
8
+ class UnauthorizedError(ApiError):
9
+ def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=401, headers=headers, body=body)
@@ -0,0 +1,10 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+
7
+
8
+ class UnprocessableEntityError(ApiError):
9
+ def __init__(self, body: typing.Dict[str, typing.Any], headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=422, headers=headers, body=body)
@@ -0,0 +1,4 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+