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,238 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ import re
4
+ import json
5
+ from typing import (
6
+ Callable,
7
+ Generic,
8
+ TypeVar,
9
+ Optional,
10
+ Generator,
11
+ AsyncGenerator,
12
+ Tuple,
13
+ )
14
+ import httpx
15
+
16
+ T = TypeVar("T")
17
+
18
+
19
+ class EventStream(Generic[T]):
20
+ response: httpx.Response
21
+ generator: Generator[T, None, None]
22
+
23
+ def __init__(
24
+ self,
25
+ response: httpx.Response,
26
+ decoder: Callable[[str], T],
27
+ sentinel: Optional[str] = None,
28
+ ):
29
+ self.response = response
30
+ self.generator = stream_events(response, decoder, sentinel)
31
+
32
+ def __iter__(self):
33
+ return self
34
+
35
+ def __next__(self):
36
+ return next(self.generator)
37
+
38
+ def __enter__(self):
39
+ return self
40
+
41
+ def __exit__(self, exc_type, exc_val, exc_tb):
42
+ self.response.close()
43
+
44
+
45
+ class EventStreamAsync(Generic[T]):
46
+ response: httpx.Response
47
+ generator: AsyncGenerator[T, None]
48
+
49
+ def __init__(
50
+ self,
51
+ response: httpx.Response,
52
+ decoder: Callable[[str], T],
53
+ sentinel: Optional[str] = None,
54
+ ):
55
+ self.response = response
56
+ self.generator = stream_events_async(response, decoder, sentinel)
57
+
58
+ def __aiter__(self):
59
+ return self
60
+
61
+ async def __anext__(self):
62
+ return await self.generator.__anext__()
63
+
64
+ async def __aenter__(self):
65
+ return self
66
+
67
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
68
+ await self.response.aclose()
69
+
70
+
71
+ class ServerEvent:
72
+ id: Optional[str] = None
73
+ event: Optional[str] = None
74
+ data: Optional[str] = None
75
+ retry: Optional[int] = None
76
+
77
+
78
+ MESSAGE_BOUNDARIES = [
79
+ b"\r\n\r\n",
80
+ b"\n\n",
81
+ b"\r\r",
82
+ ]
83
+
84
+
85
+ async def stream_events_async(
86
+ response: httpx.Response,
87
+ decoder: Callable[[str], T],
88
+ sentinel: Optional[str] = None,
89
+ ) -> AsyncGenerator[T, None]:
90
+ buffer = bytearray()
91
+ position = 0
92
+ discard = False
93
+ async for chunk in response.aiter_bytes():
94
+ # We've encountered the sentinel value and should no longer process
95
+ # incoming data. Instead we throw new data away until the server closes
96
+ # the connection.
97
+ if discard:
98
+ continue
99
+
100
+ buffer += chunk
101
+ for i in range(position, len(buffer)):
102
+ char = buffer[i : i + 1]
103
+ seq: Optional[bytes] = None
104
+ if char in [b"\r", b"\n"]:
105
+ for boundary in MESSAGE_BOUNDARIES:
106
+ seq = _peek_sequence(i, buffer, boundary)
107
+ if seq is not None:
108
+ break
109
+ if seq is None:
110
+ continue
111
+
112
+ block = buffer[position:i]
113
+ position = i + len(seq)
114
+ event, discard = _parse_event(block, decoder, sentinel)
115
+ if event is not None:
116
+ yield event
117
+
118
+ if position > 0:
119
+ buffer = buffer[position:]
120
+ position = 0
121
+
122
+ event, discard = _parse_event(buffer, decoder, sentinel)
123
+ if event is not None:
124
+ yield event
125
+
126
+
127
+ def stream_events(
128
+ response: httpx.Response,
129
+ decoder: Callable[[str], T],
130
+ sentinel: Optional[str] = None,
131
+ ) -> Generator[T, None, None]:
132
+ buffer = bytearray()
133
+ position = 0
134
+ discard = False
135
+ for chunk in response.iter_bytes():
136
+ # We've encountered the sentinel value and should no longer process
137
+ # incoming data. Instead we throw new data away until the server closes
138
+ # the connection.
139
+ if discard:
140
+ continue
141
+
142
+ buffer += chunk
143
+ for i in range(position, len(buffer)):
144
+ char = buffer[i : i + 1]
145
+ seq: Optional[bytes] = None
146
+ if char in [b"\r", b"\n"]:
147
+ for boundary in MESSAGE_BOUNDARIES:
148
+ seq = _peek_sequence(i, buffer, boundary)
149
+ if seq is not None:
150
+ break
151
+ if seq is None:
152
+ continue
153
+
154
+ block = buffer[position:i]
155
+ position = i + len(seq)
156
+ event, discard = _parse_event(block, decoder, sentinel)
157
+ if event is not None:
158
+ yield event
159
+
160
+ if position > 0:
161
+ buffer = buffer[position:]
162
+ position = 0
163
+
164
+ event, discard = _parse_event(buffer, decoder, sentinel)
165
+ if event is not None:
166
+ yield event
167
+
168
+
169
+ def _parse_event(
170
+ raw: bytearray, decoder: Callable[[str], T], sentinel: Optional[str] = None
171
+ ) -> Tuple[Optional[T], bool]:
172
+ block = raw.decode()
173
+ lines = re.split(r"\r?\n|\r", block)
174
+ publish = False
175
+ event = ServerEvent()
176
+ data = ""
177
+ for line in lines:
178
+ if not line:
179
+ continue
180
+
181
+ delim = line.find(":")
182
+ if delim <= 0:
183
+ continue
184
+
185
+ field = line[0:delim]
186
+ value = line[delim + 1 :] if delim < len(line) - 1 else ""
187
+ if len(value) and value[0] == " ":
188
+ value = value[1:]
189
+
190
+ if field == "event":
191
+ event.event = value
192
+ publish = True
193
+ elif field == "data":
194
+ data += value + "\n"
195
+ publish = True
196
+ elif field == "id":
197
+ event.id = value
198
+ publish = True
199
+ elif field == "retry":
200
+ event.retry = int(value) if value.isdigit() else None
201
+ publish = True
202
+
203
+ if sentinel and data == f"{sentinel}\n":
204
+ return None, True
205
+
206
+ if data:
207
+ data = data[:-1]
208
+ event.data = data
209
+
210
+ data_is_primitive = (
211
+ data.isnumeric() or data == "true" or data == "false" or data == "null"
212
+ )
213
+ data_is_json = (
214
+ data.startswith("{") or data.startswith("[") or data.startswith('"')
215
+ )
216
+
217
+ if data_is_primitive or data_is_json:
218
+ try:
219
+ event.data = json.loads(data)
220
+ except Exception:
221
+ pass
222
+
223
+ out = None
224
+ if publish:
225
+ out = decoder(json.dumps(event.__dict__))
226
+
227
+ return out, False
228
+
229
+
230
+ def _peek_sequence(position: int, buffer: bytearray, sequence: bytes):
231
+ if len(sequence) > (len(buffer) - position):
232
+ return None
233
+
234
+ for i, seq in enumerate(sequence):
235
+ if buffer[position + i] != seq:
236
+ return None
237
+
238
+ return sequence
@@ -0,0 +1,202 @@
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
+ Tuple,
9
+ )
10
+ from pydantic import BaseModel
11
+ from pydantic.fields import FieldInfo
12
+
13
+ from .serializers import marshal_json
14
+
15
+ from .metadata import (
16
+ FormMetadata,
17
+ MultipartFormMetadata,
18
+ find_field_metadata,
19
+ )
20
+ from .values import _is_set, _val_to_string
21
+
22
+
23
+ def _populate_form(
24
+ field_name: str,
25
+ explode: bool,
26
+ obj: Any,
27
+ delimiter: str,
28
+ form: Dict[str, List[str]],
29
+ ):
30
+ if not _is_set(obj):
31
+ return form
32
+
33
+ if isinstance(obj, BaseModel):
34
+ items = []
35
+
36
+ obj_fields: Dict[str, FieldInfo] = obj.__class__.model_fields
37
+ for name in obj_fields:
38
+ obj_field = obj_fields[name]
39
+ obj_field_name = obj_field.alias if obj_field.alias is not None else name
40
+ if obj_field_name == "":
41
+ continue
42
+
43
+ val = getattr(obj, name)
44
+ if not _is_set(val):
45
+ continue
46
+
47
+ if explode:
48
+ form[obj_field_name] = [_val_to_string(val)]
49
+ else:
50
+ items.append(f"{obj_field_name}{delimiter}{_val_to_string(val)}")
51
+
52
+ if len(items) > 0:
53
+ form[field_name] = [delimiter.join(items)]
54
+ elif isinstance(obj, Dict):
55
+ items = []
56
+ for key, value in obj.items():
57
+ if not _is_set(value):
58
+ continue
59
+
60
+ if explode:
61
+ form[key] = [_val_to_string(value)]
62
+ else:
63
+ items.append(f"{key}{delimiter}{_val_to_string(value)}")
64
+
65
+ if len(items) > 0:
66
+ form[field_name] = [delimiter.join(items)]
67
+ elif isinstance(obj, List):
68
+ items = []
69
+
70
+ for value in obj:
71
+ if not _is_set(value):
72
+ continue
73
+
74
+ if explode:
75
+ if not field_name in form:
76
+ form[field_name] = []
77
+ form[field_name].append(_val_to_string(value))
78
+ else:
79
+ items.append(_val_to_string(value))
80
+
81
+ if len(items) > 0:
82
+ form[field_name] = [delimiter.join([str(item) for item in items])]
83
+ else:
84
+ form[field_name] = [_val_to_string(obj)]
85
+
86
+ return form
87
+
88
+
89
+ def serialize_multipart_form(
90
+ media_type: str, request: Any
91
+ ) -> Tuple[str, Dict[str, Any], Dict[str, Any]]:
92
+ form: Dict[str, Any] = {}
93
+ files: Dict[str, Any] = {}
94
+
95
+ if not isinstance(request, BaseModel):
96
+ raise TypeError("invalid request body type")
97
+
98
+ request_fields: Dict[str, FieldInfo] = request.__class__.model_fields
99
+ request_field_types = get_type_hints(request.__class__)
100
+
101
+ for name in request_fields:
102
+ field = request_fields[name]
103
+
104
+ val = getattr(request, name)
105
+ if not _is_set(val):
106
+ continue
107
+
108
+ field_metadata = find_field_metadata(field, MultipartFormMetadata)
109
+ if not field_metadata:
110
+ continue
111
+
112
+ f_name = field.alias if field.alias else name
113
+
114
+ if field_metadata.file:
115
+ file_fields: Dict[str, FieldInfo] = val.__class__.model_fields
116
+
117
+ file_name = ""
118
+ content = None
119
+ content_type = None
120
+
121
+ for file_field_name in file_fields:
122
+ file_field = file_fields[file_field_name]
123
+
124
+ file_metadata = find_field_metadata(file_field, MultipartFormMetadata)
125
+ if file_metadata is None:
126
+ continue
127
+
128
+ if file_metadata.content:
129
+ content = getattr(val, file_field_name, None)
130
+ elif file_field_name == "content_type":
131
+ content_type = getattr(val, file_field_name, None)
132
+ else:
133
+ file_name = getattr(val, file_field_name)
134
+
135
+ if file_name == "" or content is None:
136
+ raise ValueError("invalid multipart/form-data file")
137
+
138
+ if content_type is not None:
139
+ files[f_name] = (file_name, content, content_type)
140
+ else:
141
+ files[f_name] = (file_name, content)
142
+ elif field_metadata.json:
143
+ files[f_name] = (
144
+ None,
145
+ marshal_json(val, request_field_types[name]),
146
+ "application/json",
147
+ )
148
+ else:
149
+ if isinstance(val, List):
150
+ values = []
151
+
152
+ for value in val:
153
+ if not _is_set(value):
154
+ continue
155
+ values.append(_val_to_string(value))
156
+
157
+ form[f_name + "[]"] = values
158
+ else:
159
+ form[f_name] = _val_to_string(val)
160
+ return media_type, form, files
161
+
162
+
163
+ def serialize_form_data(data: Any) -> Dict[str, Any]:
164
+ form: Dict[str, List[str]] = {}
165
+
166
+ if isinstance(data, BaseModel):
167
+ data_fields: Dict[str, FieldInfo] = data.__class__.model_fields
168
+ data_field_types = get_type_hints(data.__class__)
169
+ for name in data_fields:
170
+ field = data_fields[name]
171
+
172
+ val = getattr(data, name)
173
+ if not _is_set(val):
174
+ continue
175
+
176
+ metadata = find_field_metadata(field, FormMetadata)
177
+ if metadata is None:
178
+ continue
179
+
180
+ f_name = field.alias if field.alias is not None else name
181
+
182
+ if metadata.json:
183
+ form[f_name] = [marshal_json(val, data_field_types[name])]
184
+ else:
185
+ if metadata.style == "form":
186
+ _populate_form(
187
+ f_name,
188
+ metadata.explode,
189
+ val,
190
+ ",",
191
+ form,
192
+ )
193
+ else:
194
+ raise ValueError(f"Invalid form style for field {name}")
195
+ elif isinstance(data, Dict):
196
+ for key, value in data.items():
197
+ if _is_set(value):
198
+ form[key] = [_val_to_string(value)]
199
+ else:
200
+ raise TypeError(f"Invalid request body type {type(data)} for form data")
201
+
202
+ return form
@@ -0,0 +1,136 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from typing import (
4
+ Any,
5
+ Dict,
6
+ List,
7
+ Optional,
8
+ )
9
+ from httpx import Headers
10
+ from pydantic import BaseModel
11
+ from pydantic.fields import FieldInfo
12
+
13
+ from .metadata import (
14
+ HeaderMetadata,
15
+ find_field_metadata,
16
+ )
17
+
18
+ from .values import _is_set, _populate_from_globals, _val_to_string
19
+
20
+
21
+ def get_headers(headers_params: Any, gbls: Optional[Any] = None) -> Dict[str, str]:
22
+ headers: Dict[str, str] = {}
23
+
24
+ globals_already_populated = []
25
+ if _is_set(headers_params):
26
+ globals_already_populated = _populate_headers(headers_params, gbls, headers, [])
27
+ if _is_set(gbls):
28
+ _populate_headers(gbls, None, headers, globals_already_populated)
29
+
30
+ return headers
31
+
32
+
33
+ def _populate_headers(
34
+ headers_params: Any,
35
+ gbls: Any,
36
+ header_values: Dict[str, str],
37
+ skip_fields: List[str],
38
+ ) -> List[str]:
39
+ globals_already_populated: List[str] = []
40
+
41
+ if not isinstance(headers_params, BaseModel):
42
+ return globals_already_populated
43
+
44
+ param_fields: Dict[str, FieldInfo] = headers_params.__class__.model_fields
45
+ for name in param_fields:
46
+ if name in skip_fields:
47
+ continue
48
+
49
+ field = param_fields[name]
50
+ f_name = field.alias if field.alias is not None else name
51
+
52
+ metadata = find_field_metadata(field, HeaderMetadata)
53
+ if metadata is None:
54
+ continue
55
+
56
+ value, global_found = _populate_from_globals(
57
+ name, getattr(headers_params, name), HeaderMetadata, gbls
58
+ )
59
+ if global_found:
60
+ globals_already_populated.append(name)
61
+ value = _serialize_header(metadata.explode, value)
62
+
63
+ if value != "":
64
+ header_values[f_name] = value
65
+
66
+ return globals_already_populated
67
+
68
+
69
+ def _serialize_header(explode: bool, obj: Any) -> str:
70
+ if not _is_set(obj):
71
+ return ""
72
+
73
+ if isinstance(obj, BaseModel):
74
+ items = []
75
+ obj_fields: Dict[str, FieldInfo] = obj.__class__.model_fields
76
+ for name in obj_fields:
77
+ obj_field = obj_fields[name]
78
+ obj_param_metadata = find_field_metadata(obj_field, HeaderMetadata)
79
+
80
+ if not obj_param_metadata:
81
+ continue
82
+
83
+ f_name = obj_field.alias if obj_field.alias is not None else name
84
+
85
+ val = getattr(obj, name)
86
+ if not _is_set(val):
87
+ continue
88
+
89
+ if explode:
90
+ items.append(f"{f_name}={_val_to_string(val)}")
91
+ else:
92
+ items.append(f_name)
93
+ items.append(_val_to_string(val))
94
+
95
+ if len(items) > 0:
96
+ return ",".join(items)
97
+ elif isinstance(obj, Dict):
98
+ items = []
99
+
100
+ for key, value in obj.items():
101
+ if not _is_set(value):
102
+ continue
103
+
104
+ if explode:
105
+ items.append(f"{key}={_val_to_string(value)}")
106
+ else:
107
+ items.append(key)
108
+ items.append(_val_to_string(value))
109
+
110
+ if len(items) > 0:
111
+ return ",".join([str(item) for item in items])
112
+ elif isinstance(obj, List):
113
+ items = []
114
+
115
+ for value in obj:
116
+ if not _is_set(value):
117
+ continue
118
+
119
+ items.append(_val_to_string(value))
120
+
121
+ if len(items) > 0:
122
+ return ",".join(items)
123
+ elif _is_set(obj):
124
+ return f"{_val_to_string(obj)}"
125
+
126
+ return ""
127
+
128
+
129
+ def get_response_headers(headers: Headers) -> Dict[str, List[str]]:
130
+ res: Dict[str, List[str]] = {}
131
+ for k, v in headers.items():
132
+ if not k in res:
133
+ res[k] = []
134
+
135
+ res[k].append(v)
136
+ return res
@@ -0,0 +1,27 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ import httpx
4
+ import logging
5
+ import os
6
+ from typing import Any, Protocol
7
+
8
+
9
+ class Logger(Protocol):
10
+ def debug(self, msg: str, *args: Any, **kwargs: Any) -> None:
11
+ pass
12
+
13
+
14
+ class NoOpLogger:
15
+ def debug(self, msg: str, *args: Any, **kwargs: Any) -> None:
16
+ pass
17
+
18
+
19
+ def get_body_content(req: httpx.Request) -> str:
20
+ return "<streaming body>" if not hasattr(req, "_content") else str(req.content)
21
+
22
+
23
+ def get_default_logger() -> Logger:
24
+ if os.getenv("ZILLOWRAPIDAPICLIENT_DEBUG"):
25
+ logging.basicConfig(level=logging.DEBUG)
26
+ return logging.getLogger("zillow_rapidapi_client")
27
+ return NoOpLogger()