zillow-rapidapi-client 0.1.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. zillow_rapidapi_client/__init__.py +18 -0
  2. zillow_rapidapi_client/_hooks/__init__.py +5 -0
  3. zillow_rapidapi_client/_hooks/registration.py +13 -0
  4. zillow_rapidapi_client/_hooks/sdkhooks.py +76 -0
  5. zillow_rapidapi_client/_hooks/types.py +106 -0
  6. zillow_rapidapi_client/_version.py +15 -0
  7. zillow_rapidapi_client/basesdk.py +358 -0
  8. zillow_rapidapi_client/httpclient.py +134 -0
  9. zillow_rapidapi_client/models/__init__.py +40 -0
  10. zillow_rapidapi_client/models/apierror.py +22 -0
  11. zillow_rapidapi_client/models/property.py +163 -0
  12. zillow_rapidapi_client/models/propertyextendedsearchop.py +106 -0
  13. zillow_rapidapi_client/models/propertysearchresponse.py +32 -0
  14. zillow_rapidapi_client/properties.py +221 -0
  15. zillow_rapidapi_client/py.typed +1 -0
  16. zillow_rapidapi_client/sdk.py +114 -0
  17. zillow_rapidapi_client/sdkconfiguration.py +52 -0
  18. zillow_rapidapi_client/types/__init__.py +21 -0
  19. zillow_rapidapi_client/types/basemodel.py +39 -0
  20. zillow_rapidapi_client/utils/__init__.py +99 -0
  21. zillow_rapidapi_client/utils/annotations.py +55 -0
  22. zillow_rapidapi_client/utils/enums.py +34 -0
  23. zillow_rapidapi_client/utils/eventstreaming.py +238 -0
  24. zillow_rapidapi_client/utils/forms.py +202 -0
  25. zillow_rapidapi_client/utils/headers.py +136 -0
  26. zillow_rapidapi_client/utils/logger.py +27 -0
  27. zillow_rapidapi_client/utils/metadata.py +118 -0
  28. zillow_rapidapi_client/utils/queryparams.py +205 -0
  29. zillow_rapidapi_client/utils/requestbodies.py +66 -0
  30. zillow_rapidapi_client/utils/retries.py +217 -0
  31. zillow_rapidapi_client/utils/security.py +174 -0
  32. zillow_rapidapi_client/utils/serializers.py +215 -0
  33. zillow_rapidapi_client/utils/url.py +155 -0
  34. zillow_rapidapi_client/utils/values.py +137 -0
  35. zillow_rapidapi_client-0.1.3.dist-info/METADATA +419 -0
  36. zillow_rapidapi_client-0.1.3.dist-info/RECORD +37 -0
  37. zillow_rapidapi_client-0.1.3.dist-info/WHEEL +4 -0
@@ -0,0 +1,118 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from typing import Optional, Type, TypeVar, Union
4
+ from dataclasses import dataclass
5
+ from pydantic.fields import FieldInfo
6
+
7
+
8
+ T = TypeVar("T")
9
+
10
+
11
+ @dataclass
12
+ class SecurityMetadata:
13
+ option: bool = False
14
+ scheme: bool = False
15
+ scheme_type: Optional[str] = None
16
+ sub_type: Optional[str] = None
17
+ field_name: Optional[str] = None
18
+
19
+ def get_field_name(self, default: str) -> str:
20
+ return self.field_name or default
21
+
22
+
23
+ @dataclass
24
+ class ParamMetadata:
25
+ serialization: Optional[str] = None
26
+ style: str = "simple"
27
+ explode: bool = False
28
+
29
+
30
+ @dataclass
31
+ class PathParamMetadata(ParamMetadata):
32
+ pass
33
+
34
+
35
+ @dataclass
36
+ class QueryParamMetadata(ParamMetadata):
37
+ style: str = "form"
38
+ explode: bool = True
39
+
40
+
41
+ @dataclass
42
+ class HeaderMetadata(ParamMetadata):
43
+ pass
44
+
45
+
46
+ @dataclass
47
+ class RequestMetadata:
48
+ media_type: str = "application/octet-stream"
49
+
50
+
51
+ @dataclass
52
+ class MultipartFormMetadata:
53
+ file: bool = False
54
+ content: bool = False
55
+ json: bool = False
56
+
57
+
58
+ @dataclass
59
+ class FormMetadata:
60
+ json: bool = False
61
+ style: str = "form"
62
+ explode: bool = True
63
+
64
+
65
+ class FieldMetadata:
66
+ security: Optional[SecurityMetadata] = None
67
+ path: Optional[PathParamMetadata] = None
68
+ query: Optional[QueryParamMetadata] = None
69
+ header: Optional[HeaderMetadata] = None
70
+ request: Optional[RequestMetadata] = None
71
+ form: Optional[FormMetadata] = None
72
+ multipart: Optional[MultipartFormMetadata] = None
73
+
74
+ def __init__(
75
+ self,
76
+ security: Optional[SecurityMetadata] = None,
77
+ path: Optional[Union[PathParamMetadata, bool]] = None,
78
+ query: Optional[Union[QueryParamMetadata, bool]] = None,
79
+ header: Optional[Union[HeaderMetadata, bool]] = None,
80
+ request: Optional[Union[RequestMetadata, bool]] = None,
81
+ form: Optional[Union[FormMetadata, bool]] = None,
82
+ multipart: Optional[Union[MultipartFormMetadata, bool]] = None,
83
+ ):
84
+ self.security = security
85
+ self.path = PathParamMetadata() if isinstance(path, bool) else path
86
+ self.query = QueryParamMetadata() if isinstance(query, bool) else query
87
+ self.header = HeaderMetadata() if isinstance(header, bool) else header
88
+ self.request = RequestMetadata() if isinstance(request, bool) else request
89
+ self.form = FormMetadata() if isinstance(form, bool) else form
90
+ self.multipart = (
91
+ MultipartFormMetadata() if isinstance(multipart, bool) else multipart
92
+ )
93
+
94
+
95
+ def find_field_metadata(field_info: FieldInfo, metadata_type: Type[T]) -> Optional[T]:
96
+ metadata = find_metadata(field_info, FieldMetadata)
97
+ if not metadata:
98
+ return None
99
+
100
+ fields = metadata.__dict__
101
+
102
+ for field in fields:
103
+ if isinstance(fields[field], metadata_type):
104
+ return fields[field]
105
+
106
+ return None
107
+
108
+
109
+ def find_metadata(field_info: FieldInfo, metadata_type: Type[T]) -> Optional[T]:
110
+ metadata = field_info.metadata
111
+ if not metadata:
112
+ return None
113
+
114
+ for md in metadata:
115
+ if isinstance(md, metadata_type):
116
+ return md
117
+
118
+ return None
@@ -0,0 +1,205 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from typing import (
4
+ Any,
5
+ Dict,
6
+ get_type_hints,
7
+ List,
8
+ Optional,
9
+ )
10
+
11
+ from pydantic import BaseModel
12
+ from pydantic.fields import FieldInfo
13
+
14
+ from .metadata import (
15
+ QueryParamMetadata,
16
+ find_field_metadata,
17
+ )
18
+ from .values import (
19
+ _get_serialized_params,
20
+ _is_set,
21
+ _populate_from_globals,
22
+ _val_to_string,
23
+ )
24
+ from .forms import _populate_form
25
+
26
+
27
+ def get_query_params(
28
+ query_params: Any,
29
+ gbls: Optional[Any] = None,
30
+ ) -> Dict[str, List[str]]:
31
+ params: Dict[str, List[str]] = {}
32
+
33
+ globals_already_populated = _populate_query_params(query_params, gbls, params, [])
34
+ if _is_set(gbls):
35
+ _populate_query_params(gbls, None, params, globals_already_populated)
36
+
37
+ return params
38
+
39
+
40
+ def _populate_query_params(
41
+ query_params: Any,
42
+ gbls: Any,
43
+ query_param_values: Dict[str, List[str]],
44
+ skip_fields: List[str],
45
+ ) -> List[str]:
46
+ globals_already_populated: List[str] = []
47
+
48
+ if not isinstance(query_params, BaseModel):
49
+ return globals_already_populated
50
+
51
+ param_fields: Dict[str, FieldInfo] = query_params.__class__.model_fields
52
+ param_field_types = get_type_hints(query_params.__class__)
53
+ for name in param_fields:
54
+ if name in skip_fields:
55
+ continue
56
+
57
+ field = param_fields[name]
58
+
59
+ metadata = find_field_metadata(field, QueryParamMetadata)
60
+ if not metadata:
61
+ continue
62
+
63
+ value = getattr(query_params, name) if _is_set(query_params) else None
64
+
65
+ value, global_found = _populate_from_globals(
66
+ name, value, QueryParamMetadata, gbls
67
+ )
68
+ if global_found:
69
+ globals_already_populated.append(name)
70
+
71
+ f_name = field.alias if field.alias is not None else name
72
+ serialization = metadata.serialization
73
+ if serialization is not None:
74
+ serialized_parms = _get_serialized_params(
75
+ metadata, f_name, value, param_field_types[name]
76
+ )
77
+ for key, value in serialized_parms.items():
78
+ if key in query_param_values:
79
+ query_param_values[key].extend(value)
80
+ else:
81
+ query_param_values[key] = [value]
82
+ else:
83
+ style = metadata.style
84
+ if style == "deepObject":
85
+ _populate_deep_object_query_params(f_name, value, query_param_values)
86
+ elif style == "form":
87
+ _populate_delimited_query_params(
88
+ metadata, f_name, value, ",", query_param_values
89
+ )
90
+ elif style == "pipeDelimited":
91
+ _populate_delimited_query_params(
92
+ metadata, f_name, value, "|", query_param_values
93
+ )
94
+ else:
95
+ raise NotImplementedError(
96
+ f"query param style {style} not yet supported"
97
+ )
98
+
99
+ return globals_already_populated
100
+
101
+
102
+ def _populate_deep_object_query_params(
103
+ field_name: str,
104
+ obj: Any,
105
+ params: Dict[str, List[str]],
106
+ ):
107
+ if not _is_set(obj):
108
+ return
109
+
110
+ if isinstance(obj, BaseModel):
111
+ _populate_deep_object_query_params_basemodel(field_name, obj, params)
112
+ elif isinstance(obj, Dict):
113
+ _populate_deep_object_query_params_dict(field_name, obj, params)
114
+
115
+
116
+ def _populate_deep_object_query_params_basemodel(
117
+ prior_params_key: str,
118
+ obj: Any,
119
+ params: Dict[str, List[str]],
120
+ ):
121
+ if not _is_set(obj) or not isinstance(obj, BaseModel):
122
+ return
123
+
124
+ obj_fields: Dict[str, FieldInfo] = obj.__class__.model_fields
125
+ for name in obj_fields:
126
+ obj_field = obj_fields[name]
127
+
128
+ f_name = obj_field.alias if obj_field.alias is not None else name
129
+
130
+ params_key = f"{prior_params_key}[{f_name}]"
131
+
132
+ obj_param_metadata = find_field_metadata(obj_field, QueryParamMetadata)
133
+ if not _is_set(obj_param_metadata):
134
+ continue
135
+
136
+ obj_val = getattr(obj, name)
137
+ if not _is_set(obj_val):
138
+ continue
139
+
140
+ if isinstance(obj_val, BaseModel):
141
+ _populate_deep_object_query_params_basemodel(params_key, obj_val, params)
142
+ elif isinstance(obj_val, Dict):
143
+ _populate_deep_object_query_params_dict(params_key, obj_val, params)
144
+ elif isinstance(obj_val, List):
145
+ _populate_deep_object_query_params_list(params_key, obj_val, params)
146
+ else:
147
+ params[params_key] = [_val_to_string(obj_val)]
148
+
149
+
150
+ def _populate_deep_object_query_params_dict(
151
+ prior_params_key: str,
152
+ value: Dict,
153
+ params: Dict[str, List[str]],
154
+ ):
155
+ if not _is_set(value):
156
+ return
157
+
158
+ for key, val in value.items():
159
+ if not _is_set(val):
160
+ continue
161
+
162
+ params_key = f"{prior_params_key}[{key}]"
163
+
164
+ if isinstance(val, BaseModel):
165
+ _populate_deep_object_query_params_basemodel(params_key, val, params)
166
+ elif isinstance(val, Dict):
167
+ _populate_deep_object_query_params_dict(params_key, val, params)
168
+ elif isinstance(val, List):
169
+ _populate_deep_object_query_params_list(params_key, val, params)
170
+ else:
171
+ params[params_key] = [_val_to_string(val)]
172
+
173
+
174
+ def _populate_deep_object_query_params_list(
175
+ params_key: str,
176
+ value: List,
177
+ params: Dict[str, List[str]],
178
+ ):
179
+ if not _is_set(value):
180
+ return
181
+
182
+ for val in value:
183
+ if not _is_set(val):
184
+ continue
185
+
186
+ if params.get(params_key) is None:
187
+ params[params_key] = []
188
+
189
+ params[params_key].append(_val_to_string(val))
190
+
191
+
192
+ def _populate_delimited_query_params(
193
+ metadata: QueryParamMetadata,
194
+ field_name: str,
195
+ obj: Any,
196
+ delimiter: str,
197
+ query_param_values: Dict[str, List[str]],
198
+ ):
199
+ _populate_form(
200
+ field_name,
201
+ metadata.explode,
202
+ obj,
203
+ delimiter,
204
+ query_param_values,
205
+ )
@@ -0,0 +1,66 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ import io
4
+ from dataclasses import dataclass
5
+ import re
6
+ from typing import (
7
+ Any,
8
+ Optional,
9
+ )
10
+
11
+ from .forms import serialize_form_data, serialize_multipart_form
12
+
13
+ from .serializers import marshal_json
14
+
15
+ SERIALIZATION_METHOD_TO_CONTENT_TYPE = {
16
+ "json": "application/json",
17
+ "form": "application/x-www-form-urlencoded",
18
+ "multipart": "multipart/form-data",
19
+ "raw": "application/octet-stream",
20
+ "string": "text/plain",
21
+ }
22
+
23
+
24
+ @dataclass
25
+ class SerializedRequestBody:
26
+ media_type: Optional[str] = None
27
+ content: Optional[Any] = None
28
+ data: Optional[Any] = None
29
+ files: Optional[Any] = None
30
+
31
+
32
+ def serialize_request_body(
33
+ request_body: Any,
34
+ nullable: bool,
35
+ optional: bool,
36
+ serialization_method: str,
37
+ request_body_type,
38
+ ) -> Optional[SerializedRequestBody]:
39
+ if request_body is None:
40
+ if not nullable and optional:
41
+ return None
42
+
43
+ media_type = SERIALIZATION_METHOD_TO_CONTENT_TYPE[serialization_method]
44
+
45
+ serialized_request_body = SerializedRequestBody(media_type)
46
+
47
+ if re.match(r"(application|text)\/.*?\+*json.*", media_type) is not None:
48
+ serialized_request_body.content = marshal_json(request_body, request_body_type)
49
+ elif re.match(r"multipart\/.*", media_type) is not None:
50
+ (
51
+ serialized_request_body.media_type,
52
+ serialized_request_body.data,
53
+ serialized_request_body.files,
54
+ ) = serialize_multipart_form(media_type, request_body)
55
+ elif re.match(r"application\/x-www-form-urlencoded.*", media_type) is not None:
56
+ serialized_request_body.data = serialize_form_data(request_body)
57
+ elif isinstance(request_body, (bytes, bytearray, io.BytesIO, io.BufferedReader)):
58
+ serialized_request_body.content = request_body
59
+ elif isinstance(request_body, str):
60
+ serialized_request_body.content = request_body
61
+ else:
62
+ raise TypeError(
63
+ f"invalid request body type {type(request_body)} for mediaType {media_type}"
64
+ )
65
+
66
+ return serialized_request_body
@@ -0,0 +1,217 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ import asyncio
4
+ import random
5
+ import time
6
+ from typing import List
7
+
8
+ import httpx
9
+
10
+
11
+ class BackoffStrategy:
12
+ initial_interval: int
13
+ max_interval: int
14
+ exponent: float
15
+ max_elapsed_time: int
16
+
17
+ def __init__(
18
+ self,
19
+ initial_interval: int,
20
+ max_interval: int,
21
+ exponent: float,
22
+ max_elapsed_time: int,
23
+ ):
24
+ self.initial_interval = initial_interval
25
+ self.max_interval = max_interval
26
+ self.exponent = exponent
27
+ self.max_elapsed_time = max_elapsed_time
28
+
29
+
30
+ class RetryConfig:
31
+ strategy: str
32
+ backoff: BackoffStrategy
33
+ retry_connection_errors: bool
34
+
35
+ def __init__(
36
+ self, strategy: str, backoff: BackoffStrategy, retry_connection_errors: bool
37
+ ):
38
+ self.strategy = strategy
39
+ self.backoff = backoff
40
+ self.retry_connection_errors = retry_connection_errors
41
+
42
+
43
+ class Retries:
44
+ config: RetryConfig
45
+ status_codes: List[str]
46
+
47
+ def __init__(self, config: RetryConfig, status_codes: List[str]):
48
+ self.config = config
49
+ self.status_codes = status_codes
50
+
51
+
52
+ class TemporaryError(Exception):
53
+ response: httpx.Response
54
+
55
+ def __init__(self, response: httpx.Response):
56
+ self.response = response
57
+
58
+
59
+ class PermanentError(Exception):
60
+ inner: Exception
61
+
62
+ def __init__(self, inner: Exception):
63
+ self.inner = inner
64
+
65
+
66
+ def retry(func, retries: Retries):
67
+ if retries.config.strategy == "backoff":
68
+
69
+ def do_request() -> httpx.Response:
70
+ res: httpx.Response
71
+ try:
72
+ res = func()
73
+
74
+ for code in retries.status_codes:
75
+ if "X" in code.upper():
76
+ code_range = int(code[0])
77
+
78
+ status_major = res.status_code / 100
79
+
80
+ if code_range <= status_major < code_range + 1:
81
+ raise TemporaryError(res)
82
+ else:
83
+ parsed_code = int(code)
84
+
85
+ if res.status_code == parsed_code:
86
+ raise TemporaryError(res)
87
+ except httpx.ConnectError as exception:
88
+ if retries.config.retry_connection_errors:
89
+ raise
90
+
91
+ raise PermanentError(exception) from exception
92
+ except httpx.TimeoutException as exception:
93
+ if retries.config.retry_connection_errors:
94
+ raise
95
+
96
+ raise PermanentError(exception) from exception
97
+ except TemporaryError:
98
+ raise
99
+ except Exception as exception:
100
+ raise PermanentError(exception) from exception
101
+
102
+ return res
103
+
104
+ return retry_with_backoff(
105
+ do_request,
106
+ retries.config.backoff.initial_interval,
107
+ retries.config.backoff.max_interval,
108
+ retries.config.backoff.exponent,
109
+ retries.config.backoff.max_elapsed_time,
110
+ )
111
+
112
+ return func()
113
+
114
+
115
+ async def retry_async(func, retries: Retries):
116
+ if retries.config.strategy == "backoff":
117
+
118
+ async def do_request() -> httpx.Response:
119
+ res: httpx.Response
120
+ try:
121
+ res = await func()
122
+
123
+ for code in retries.status_codes:
124
+ if "X" in code.upper():
125
+ code_range = int(code[0])
126
+
127
+ status_major = res.status_code / 100
128
+
129
+ if code_range <= status_major < code_range + 1:
130
+ raise TemporaryError(res)
131
+ else:
132
+ parsed_code = int(code)
133
+
134
+ if res.status_code == parsed_code:
135
+ raise TemporaryError(res)
136
+ except httpx.ConnectError as exception:
137
+ if retries.config.retry_connection_errors:
138
+ raise
139
+
140
+ raise PermanentError(exception) from exception
141
+ except httpx.TimeoutException as exception:
142
+ if retries.config.retry_connection_errors:
143
+ raise
144
+
145
+ raise PermanentError(exception) from exception
146
+ except TemporaryError:
147
+ raise
148
+ except Exception as exception:
149
+ raise PermanentError(exception) from exception
150
+
151
+ return res
152
+
153
+ return await retry_with_backoff_async(
154
+ do_request,
155
+ retries.config.backoff.initial_interval,
156
+ retries.config.backoff.max_interval,
157
+ retries.config.backoff.exponent,
158
+ retries.config.backoff.max_elapsed_time,
159
+ )
160
+
161
+ return await func()
162
+
163
+
164
+ def retry_with_backoff(
165
+ func,
166
+ initial_interval=500,
167
+ max_interval=60000,
168
+ exponent=1.5,
169
+ max_elapsed_time=3600000,
170
+ ):
171
+ start = round(time.time() * 1000)
172
+ retries = 0
173
+
174
+ while True:
175
+ try:
176
+ return func()
177
+ except PermanentError as exception:
178
+ raise exception.inner
179
+ except Exception as exception: # pylint: disable=broad-exception-caught
180
+ now = round(time.time() * 1000)
181
+ if now - start > max_elapsed_time:
182
+ if isinstance(exception, TemporaryError):
183
+ return exception.response
184
+
185
+ raise
186
+ sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
187
+ sleep = min(sleep, max_interval / 1000)
188
+ time.sleep(sleep)
189
+ retries += 1
190
+
191
+
192
+ async def retry_with_backoff_async(
193
+ func,
194
+ initial_interval=500,
195
+ max_interval=60000,
196
+ exponent=1.5,
197
+ max_elapsed_time=3600000,
198
+ ):
199
+ start = round(time.time() * 1000)
200
+ retries = 0
201
+
202
+ while True:
203
+ try:
204
+ return await func()
205
+ except PermanentError as exception:
206
+ raise exception.inner
207
+ except Exception as exception: # pylint: disable=broad-exception-caught
208
+ now = round(time.time() * 1000)
209
+ if now - start > max_elapsed_time:
210
+ if isinstance(exception, TemporaryError):
211
+ return exception.response
212
+
213
+ raise
214
+ sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
215
+ sleep = min(sleep, max_interval / 1000)
216
+ await asyncio.sleep(sleep)
217
+ retries += 1