proxyhat 0.2.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.
@@ -0,0 +1,140 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from proxyhat.types.analytics import DomainBreakdownResponse, TimeSeriesResponse, TotalResponse
6
+
7
+ if TYPE_CHECKING:
8
+ from proxyhat.async_client import AsyncProxyHat
9
+ from proxyhat.client import ProxyHat
10
+
11
+
12
+ def _analytics_body(
13
+ period: str = "24h",
14
+ start_date: str | None = None,
15
+ end_date: str | None = None,
16
+ ) -> dict[str, Any]:
17
+ body: dict[str, Any] = {"period": period}
18
+ body["start_date"] = start_date
19
+ body["end_date"] = end_date
20
+ return body
21
+
22
+
23
+ class AnalyticsResource:
24
+ def __init__(self, client: ProxyHat) -> None:
25
+ self._client = client
26
+
27
+ def traffic(
28
+ self,
29
+ *,
30
+ period: str = "24h",
31
+ start_date: str | None = None,
32
+ end_date: str | None = None,
33
+ ) -> TimeSeriesResponse:
34
+ body = _analytics_body(period, start_date, end_date)
35
+ data = self._client.request("POST", "/traffic", json=body)
36
+ return TimeSeriesResponse.from_dict(data)
37
+
38
+ def traffic_total(
39
+ self,
40
+ *,
41
+ period: str = "24h",
42
+ start_date: str | None = None,
43
+ end_date: str | None = None,
44
+ ) -> TotalResponse:
45
+ body = _analytics_body(period, start_date, end_date)
46
+ data = self._client.request("POST", "/traffic/period-total", json=body)
47
+ return TotalResponse.from_dict(data)
48
+
49
+ def requests(
50
+ self,
51
+ *,
52
+ period: str = "24h",
53
+ start_date: str | None = None,
54
+ end_date: str | None = None,
55
+ ) -> TimeSeriesResponse:
56
+ body = _analytics_body(period, start_date, end_date)
57
+ data = self._client.request("POST", "/requests", json=body)
58
+ return TimeSeriesResponse.from_dict(data)
59
+
60
+ def requests_total(
61
+ self,
62
+ *,
63
+ period: str = "24h",
64
+ start_date: str | None = None,
65
+ end_date: str | None = None,
66
+ ) -> TotalResponse:
67
+ body = _analytics_body(period, start_date, end_date)
68
+ data = self._client.request("POST", "/requests/period-total", json=body)
69
+ return TotalResponse.from_dict(data)
70
+
71
+ def domain_breakdown(
72
+ self,
73
+ *,
74
+ period: str = "24h",
75
+ start_date: str | None = None,
76
+ end_date: str | None = None,
77
+ ) -> DomainBreakdownResponse:
78
+ body = _analytics_body(period, start_date, end_date)
79
+ data = self._client.request("POST", "/domain-breakdown", json=body)
80
+ return DomainBreakdownResponse.from_dict(data)
81
+
82
+
83
+ class AsyncAnalyticsResource:
84
+ def __init__(self, client: AsyncProxyHat) -> None:
85
+ self._client = client
86
+
87
+ async def traffic(
88
+ self,
89
+ *,
90
+ period: str = "24h",
91
+ start_date: str | None = None,
92
+ end_date: str | None = None,
93
+ ) -> TimeSeriesResponse:
94
+ body = _analytics_body(period, start_date, end_date)
95
+ data = await self._client.request("POST", "/traffic", json=body)
96
+ return TimeSeriesResponse.from_dict(data)
97
+
98
+ async def traffic_total(
99
+ self,
100
+ *,
101
+ period: str = "24h",
102
+ start_date: str | None = None,
103
+ end_date: str | None = None,
104
+ ) -> TotalResponse:
105
+ body = _analytics_body(period, start_date, end_date)
106
+ data = await self._client.request("POST", "/traffic/period-total", json=body)
107
+ return TotalResponse.from_dict(data)
108
+
109
+ async def requests(
110
+ self,
111
+ *,
112
+ period: str = "24h",
113
+ start_date: str | None = None,
114
+ end_date: str | None = None,
115
+ ) -> TimeSeriesResponse:
116
+ body = _analytics_body(period, start_date, end_date)
117
+ data = await self._client.request("POST", "/requests", json=body)
118
+ return TimeSeriesResponse.from_dict(data)
119
+
120
+ async def requests_total(
121
+ self,
122
+ *,
123
+ period: str = "24h",
124
+ start_date: str | None = None,
125
+ end_date: str | None = None,
126
+ ) -> TotalResponse:
127
+ body = _analytics_body(period, start_date, end_date)
128
+ data = await self._client.request("POST", "/requests/period-total", json=body)
129
+ return TotalResponse.from_dict(data)
130
+
131
+ async def domain_breakdown(
132
+ self,
133
+ *,
134
+ period: str = "24h",
135
+ start_date: str | None = None,
136
+ end_date: str | None = None,
137
+ ) -> DomainBreakdownResponse:
138
+ body = _analytics_body(period, start_date, end_date)
139
+ data = await self._client.request("POST", "/domain-breakdown", json=body)
140
+ return DomainBreakdownResponse.from_dict(data)
@@ -0,0 +1,155 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from proxyhat.types.auth import LoginResponse, RegisterResponse, SocialAccount, SupportedProvider, User
6
+
7
+ if TYPE_CHECKING:
8
+ from proxyhat.async_client import AsyncProxyHat
9
+ from proxyhat.client import ProxyHat
10
+
11
+
12
+ class AuthResource:
13
+ def __init__(self, client: ProxyHat) -> None:
14
+ self._client = client
15
+
16
+ def register(
17
+ self,
18
+ *,
19
+ name: str,
20
+ email: str,
21
+ password: str,
22
+ password_confirmation: str,
23
+ referral_code: str | None = None,
24
+ utm_source: str | None = None,
25
+ utm_medium: str | None = None,
26
+ utm_campaign: str | None = None,
27
+ ) -> RegisterResponse:
28
+ body: dict[str, Any] = {
29
+ "name": name,
30
+ "email": email,
31
+ "password": password,
32
+ "password_confirmation": password_confirmation,
33
+ }
34
+ if referral_code is not None:
35
+ body["referral_code"] = referral_code
36
+ if utm_source is not None:
37
+ body["utm_source"] = utm_source
38
+ if utm_medium is not None:
39
+ body["utm_medium"] = utm_medium
40
+ if utm_campaign is not None:
41
+ body["utm_campaign"] = utm_campaign
42
+
43
+ data = self._client.request("POST", "/auth/register", json=body)
44
+ return RegisterResponse.from_dict(data)
45
+
46
+ def login(
47
+ self,
48
+ *,
49
+ email: str,
50
+ password: str,
51
+ twofa_code: str | None = None,
52
+ ) -> LoginResponse:
53
+ body: dict[str, Any] = {"email": email, "password": password}
54
+ if twofa_code is not None:
55
+ body["twofa_code"] = twofa_code
56
+ data = self._client.request("POST", "/auth/login", json=body)
57
+ return LoginResponse.from_dict(data)
58
+
59
+ def user(self) -> User:
60
+ data = self._client.request("GET", "/auth/user")
61
+ return User.from_dict(data)
62
+
63
+ def logout(self) -> None:
64
+ self._client.request("POST", "/auth/logout")
65
+
66
+ def supported_providers(self) -> list[SupportedProvider]:
67
+ data = self._client.request("GET", "/auth/supported-providers")
68
+ if isinstance(data, list):
69
+ return [SupportedProvider.from_dict(item) for item in data]
70
+ return []
71
+
72
+ def social_accounts(self) -> list[SocialAccount]:
73
+ data = self._client.request("GET", "/auth/social-accounts")
74
+ if isinstance(data, list):
75
+ return [SocialAccount.from_dict(item) for item in data]
76
+ return []
77
+
78
+ def disconnect_social(self, provider: str) -> None:
79
+ self._client.request("DELETE", f"/auth/social-accounts/{provider}")
80
+
81
+ def oauth_redirect(self, provider: str) -> Any:
82
+ return self._client.request("GET", f"/auth/{provider}/redirect")
83
+
84
+
85
+ class AsyncAuthResource:
86
+ def __init__(self, client: AsyncProxyHat) -> None:
87
+ self._client = client
88
+
89
+ async def register(
90
+ self,
91
+ *,
92
+ name: str,
93
+ email: str,
94
+ password: str,
95
+ password_confirmation: str,
96
+ referral_code: str | None = None,
97
+ utm_source: str | None = None,
98
+ utm_medium: str | None = None,
99
+ utm_campaign: str | None = None,
100
+ ) -> RegisterResponse:
101
+ body: dict[str, Any] = {
102
+ "name": name,
103
+ "email": email,
104
+ "password": password,
105
+ "password_confirmation": password_confirmation,
106
+ }
107
+ if referral_code is not None:
108
+ body["referral_code"] = referral_code
109
+ if utm_source is not None:
110
+ body["utm_source"] = utm_source
111
+ if utm_medium is not None:
112
+ body["utm_medium"] = utm_medium
113
+ if utm_campaign is not None:
114
+ body["utm_campaign"] = utm_campaign
115
+
116
+ data = await self._client.request("POST", "/auth/register", json=body)
117
+ return RegisterResponse.from_dict(data)
118
+
119
+ async def login(
120
+ self,
121
+ *,
122
+ email: str,
123
+ password: str,
124
+ twofa_code: str | None = None,
125
+ ) -> LoginResponse:
126
+ body: dict[str, Any] = {"email": email, "password": password}
127
+ if twofa_code is not None:
128
+ body["twofa_code"] = twofa_code
129
+ data = await self._client.request("POST", "/auth/login", json=body)
130
+ return LoginResponse.from_dict(data)
131
+
132
+ async def user(self) -> User:
133
+ data = await self._client.request("GET", "/auth/user")
134
+ return User.from_dict(data)
135
+
136
+ async def logout(self) -> None:
137
+ await self._client.request("POST", "/auth/logout")
138
+
139
+ async def supported_providers(self) -> list[SupportedProvider]:
140
+ data = await self._client.request("GET", "/auth/supported-providers")
141
+ if isinstance(data, list):
142
+ return [SupportedProvider.from_dict(item) for item in data]
143
+ return []
144
+
145
+ async def social_accounts(self) -> list[SocialAccount]:
146
+ data = await self._client.request("GET", "/auth/social-accounts")
147
+ if isinstance(data, list):
148
+ return [SocialAccount.from_dict(item) for item in data]
149
+ return []
150
+
151
+ async def disconnect_social(self, provider: str) -> None:
152
+ await self._client.request("DELETE", f"/auth/social-accounts/{provider}")
153
+
154
+ async def oauth_redirect(self, provider: str) -> Any:
155
+ return await self._client.request("GET", f"/auth/{provider}/redirect")
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from proxyhat.types.coupons import CouponResponse
6
+
7
+ if TYPE_CHECKING:
8
+ from proxyhat.async_client import AsyncProxyHat
9
+ from proxyhat.client import ProxyHat
10
+
11
+
12
+ class CouponsResource:
13
+ def __init__(self, client: ProxyHat) -> None:
14
+ self._client = client
15
+
16
+ def validate(
17
+ self,
18
+ *,
19
+ code: str,
20
+ plan_id: str | None = None,
21
+ order_sum: float | None = None,
22
+ currency: str | None = None,
23
+ ) -> CouponResponse:
24
+ body: dict[str, Any] = {"code": code}
25
+ if plan_id is not None:
26
+ body["plan_id"] = plan_id
27
+ if order_sum is not None:
28
+ body["order_sum"] = order_sum
29
+ if currency is not None:
30
+ body["currency"] = currency
31
+ data = self._client.request("POST", "/coupon/validate", json=body)
32
+ return CouponResponse.from_dict(data)
33
+
34
+ def apply(
35
+ self,
36
+ *,
37
+ code: str,
38
+ plan_id: str | None = None,
39
+ order_sum: float | None = None,
40
+ currency: str | None = None,
41
+ ) -> CouponResponse:
42
+ body: dict[str, Any] = {"code": code}
43
+ if plan_id is not None:
44
+ body["plan_id"] = plan_id
45
+ if order_sum is not None:
46
+ body["order_sum"] = order_sum
47
+ if currency is not None:
48
+ body["currency"] = currency
49
+ data = self._client.request("POST", "/coupon/apply", json=body)
50
+ return CouponResponse.from_dict(data)
51
+
52
+ def redeem(self, *, code: str) -> CouponResponse:
53
+ data = self._client.request("POST", "/coupon/redeem", json={"code": code})
54
+ return CouponResponse.from_dict(data)
55
+
56
+
57
+ class AsyncCouponsResource:
58
+ def __init__(self, client: AsyncProxyHat) -> None:
59
+ self._client = client
60
+
61
+ async def validate(
62
+ self,
63
+ *,
64
+ code: str,
65
+ plan_id: str | None = None,
66
+ order_sum: float | None = None,
67
+ currency: str | None = None,
68
+ ) -> CouponResponse:
69
+ body: dict[str, Any] = {"code": code}
70
+ if plan_id is not None:
71
+ body["plan_id"] = plan_id
72
+ if order_sum is not None:
73
+ body["order_sum"] = order_sum
74
+ if currency is not None:
75
+ body["currency"] = currency
76
+ data = await self._client.request("POST", "/coupon/validate", json=body)
77
+ return CouponResponse.from_dict(data)
78
+
79
+ async def apply(
80
+ self,
81
+ *,
82
+ code: str,
83
+ plan_id: str | None = None,
84
+ order_sum: float | None = None,
85
+ currency: str | None = None,
86
+ ) -> CouponResponse:
87
+ body: dict[str, Any] = {"code": code}
88
+ if plan_id is not None:
89
+ body["plan_id"] = plan_id
90
+ if order_sum is not None:
91
+ body["order_sum"] = order_sum
92
+ if currency is not None:
93
+ body["currency"] = currency
94
+ data = await self._client.request("POST", "/coupon/apply", json=body)
95
+ return CouponResponse.from_dict(data)
96
+
97
+ async def redeem(self, *, code: str) -> CouponResponse:
98
+ data = await self._client.request("POST", "/coupon/redeem", json={"code": code})
99
+ return CouponResponse.from_dict(data)
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from proxyhat.types.email import EmailChangeResponse
6
+
7
+ if TYPE_CHECKING:
8
+ from proxyhat.async_client import AsyncProxyHat
9
+ from proxyhat.client import ProxyHat
10
+
11
+
12
+ class EmailResource:
13
+ def __init__(self, client: ProxyHat) -> None:
14
+ self._client = client
15
+
16
+ def request_change(self, *, email: str, twofa_code: str | None = None) -> EmailChangeResponse:
17
+ body: dict[str, Any] = {"email": email}
18
+ if twofa_code is not None:
19
+ body["twofa_code"] = twofa_code
20
+ data = self._client.request("POST", "/profile/email/request-change", json=body)
21
+ return EmailChangeResponse.from_dict(data)
22
+
23
+ def confirm_change(self, *, token: str) -> EmailChangeResponse:
24
+ data = self._client.request("POST", "/profile/email/confirm-change", json={"token": token})
25
+ return EmailChangeResponse.from_dict(data)
26
+
27
+ def cancel_change(self) -> EmailChangeResponse:
28
+ data = self._client.request("POST", "/profile/email/cancel-change")
29
+ return EmailChangeResponse.from_dict(data)
30
+
31
+ def resend_verification(self) -> EmailChangeResponse:
32
+ data = self._client.request("POST", "/profile/email/resend-verification")
33
+ return EmailChangeResponse.from_dict(data)
34
+
35
+
36
+ class AsyncEmailResource:
37
+ def __init__(self, client: AsyncProxyHat) -> None:
38
+ self._client = client
39
+
40
+ async def request_change(self, *, email: str, twofa_code: str | None = None) -> EmailChangeResponse:
41
+ body: dict[str, Any] = {"email": email}
42
+ if twofa_code is not None:
43
+ body["twofa_code"] = twofa_code
44
+ data = await self._client.request("POST", "/profile/email/request-change", json=body)
45
+ return EmailChangeResponse.from_dict(data)
46
+
47
+ async def confirm_change(self, *, token: str) -> EmailChangeResponse:
48
+ data = await self._client.request("POST", "/profile/email/confirm-change", json={"token": token})
49
+ return EmailChangeResponse.from_dict(data)
50
+
51
+ async def cancel_change(self) -> EmailChangeResponse:
52
+ data = await self._client.request("POST", "/profile/email/cancel-change")
53
+ return EmailChangeResponse.from_dict(data)
54
+
55
+ async def resend_verification(self) -> EmailChangeResponse:
56
+ data = await self._client.request("POST", "/profile/email/resend-verification")
57
+ return EmailChangeResponse.from_dict(data)
@@ -0,0 +1,227 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from proxyhat.types.locations import ISP, City, Country, Region, Zipcode
6
+
7
+ if TYPE_CHECKING:
8
+ from proxyhat.async_client import AsyncProxyHat
9
+ from proxyhat.client import ProxyHat
10
+
11
+
12
+ def _location_params(
13
+ limit: int | None = None,
14
+ offset: int | None = None,
15
+ name: str | None = None,
16
+ connection_type: str | None = None,
17
+ **kwargs: Any,
18
+ ) -> dict[str, Any]:
19
+ params: dict[str, Any] = {}
20
+ if limit is not None:
21
+ params["limit"] = limit
22
+ if offset is not None:
23
+ params["offset"] = offset
24
+ if name is not None:
25
+ params["name"] = name
26
+ if connection_type is not None:
27
+ params["connection_type"] = connection_type
28
+ params.update({k: v for k, v in kwargs.items() if v is not None})
29
+ return params
30
+
31
+
32
+ class LocationsResource:
33
+ def __init__(self, client: ProxyHat) -> None:
34
+ self._client = client
35
+
36
+ def countries(
37
+ self,
38
+ *,
39
+ limit: int | None = None,
40
+ offset: int | None = None,
41
+ name: str | None = None,
42
+ connection_type: str | None = None,
43
+ ) -> list[Country]:
44
+ params = _location_params(limit=limit, offset=offset, name=name, connection_type=connection_type)
45
+ data = self._client.request("GET", "/locations/countries", params=params)
46
+ if isinstance(data, list):
47
+ return [Country.from_dict(item) for item in data]
48
+ return []
49
+
50
+ def regions(
51
+ self,
52
+ *,
53
+ limit: int | None = None,
54
+ offset: int | None = None,
55
+ name: str | None = None,
56
+ connection_type: str | None = None,
57
+ country__code: str | None = None,
58
+ ) -> list[Region]:
59
+ params = _location_params(
60
+ limit=limit, offset=offset, name=name, connection_type=connection_type, country__code=country__code
61
+ )
62
+ data = self._client.request("GET", "/locations/regions", params=params)
63
+ if isinstance(data, list):
64
+ return [Region.from_dict(item) for item in data]
65
+ return []
66
+
67
+ def cities(
68
+ self,
69
+ *,
70
+ limit: int | None = None,
71
+ offset: int | None = None,
72
+ name: str | None = None,
73
+ connection_type: str | None = None,
74
+ country__code: str | None = None,
75
+ region__code: str | None = None,
76
+ ) -> list[City]:
77
+ params = _location_params(
78
+ limit=limit,
79
+ offset=offset,
80
+ name=name,
81
+ connection_type=connection_type,
82
+ country__code=country__code,
83
+ region__code=region__code,
84
+ )
85
+ data = self._client.request("GET", "/locations/cities", params=params)
86
+ if isinstance(data, list):
87
+ return [City.from_dict(item) for item in data]
88
+ return []
89
+
90
+ def isps(
91
+ self,
92
+ *,
93
+ limit: int | None = None,
94
+ offset: int | None = None,
95
+ name: str | None = None,
96
+ connection_type: str | None = None,
97
+ country__code: str | None = None,
98
+ ) -> list[ISP]:
99
+ params = _location_params(
100
+ limit=limit, offset=offset, name=name, connection_type=connection_type, country__code=country__code
101
+ )
102
+ data = self._client.request("GET", "/locations/isps", params=params)
103
+ if isinstance(data, list):
104
+ return [ISP.from_dict(item) for item in data]
105
+ return []
106
+
107
+ def zipcodes(
108
+ self,
109
+ *,
110
+ limit: int | None = None,
111
+ offset: int | None = None,
112
+ name: str | None = None,
113
+ connection_type: str | None = None,
114
+ country__code: str | None = None,
115
+ city__code: str | None = None,
116
+ ) -> list[Zipcode]:
117
+ params = _location_params(
118
+ limit=limit,
119
+ offset=offset,
120
+ name=name,
121
+ connection_type=connection_type,
122
+ country__code=country__code,
123
+ city__code=city__code,
124
+ )
125
+ data = self._client.request("GET", "/locations/zipcodes", params=params)
126
+ if isinstance(data, list):
127
+ return [Zipcode.from_dict(item) for item in data]
128
+ return []
129
+
130
+
131
+ class AsyncLocationsResource:
132
+ def __init__(self, client: AsyncProxyHat) -> None:
133
+ self._client = client
134
+
135
+ async def countries(
136
+ self,
137
+ *,
138
+ limit: int | None = None,
139
+ offset: int | None = None,
140
+ name: str | None = None,
141
+ connection_type: str | None = None,
142
+ ) -> list[Country]:
143
+ params = _location_params(limit=limit, offset=offset, name=name, connection_type=connection_type)
144
+ data = await self._client.request("GET", "/locations/countries", params=params)
145
+ if isinstance(data, list):
146
+ return [Country.from_dict(item) for item in data]
147
+ return []
148
+
149
+ async def regions(
150
+ self,
151
+ *,
152
+ limit: int | None = None,
153
+ offset: int | None = None,
154
+ name: str | None = None,
155
+ connection_type: str | None = None,
156
+ country__code: str | None = None,
157
+ ) -> list[Region]:
158
+ params = _location_params(
159
+ limit=limit, offset=offset, name=name, connection_type=connection_type, country__code=country__code
160
+ )
161
+ data = await self._client.request("GET", "/locations/regions", params=params)
162
+ if isinstance(data, list):
163
+ return [Region.from_dict(item) for item in data]
164
+ return []
165
+
166
+ async def cities(
167
+ self,
168
+ *,
169
+ limit: int | None = None,
170
+ offset: int | None = None,
171
+ name: str | None = None,
172
+ connection_type: str | None = None,
173
+ country__code: str | None = None,
174
+ region__code: str | None = None,
175
+ ) -> list[City]:
176
+ params = _location_params(
177
+ limit=limit,
178
+ offset=offset,
179
+ name=name,
180
+ connection_type=connection_type,
181
+ country__code=country__code,
182
+ region__code=region__code,
183
+ )
184
+ data = await self._client.request("GET", "/locations/cities", params=params)
185
+ if isinstance(data, list):
186
+ return [City.from_dict(item) for item in data]
187
+ return []
188
+
189
+ async def isps(
190
+ self,
191
+ *,
192
+ limit: int | None = None,
193
+ offset: int | None = None,
194
+ name: str | None = None,
195
+ connection_type: str | None = None,
196
+ country__code: str | None = None,
197
+ ) -> list[ISP]:
198
+ params = _location_params(
199
+ limit=limit, offset=offset, name=name, connection_type=connection_type, country__code=country__code
200
+ )
201
+ data = await self._client.request("GET", "/locations/isps", params=params)
202
+ if isinstance(data, list):
203
+ return [ISP.from_dict(item) for item in data]
204
+ return []
205
+
206
+ async def zipcodes(
207
+ self,
208
+ *,
209
+ limit: int | None = None,
210
+ offset: int | None = None,
211
+ name: str | None = None,
212
+ connection_type: str | None = None,
213
+ country__code: str | None = None,
214
+ city__code: str | None = None,
215
+ ) -> list[Zipcode]:
216
+ params = _location_params(
217
+ limit=limit,
218
+ offset=offset,
219
+ name=name,
220
+ connection_type=connection_type,
221
+ country__code=country__code,
222
+ city__code=city__code,
223
+ )
224
+ data = await self._client.request("GET", "/locations/zipcodes", params=params)
225
+ if isinstance(data, list):
226
+ return [Zipcode.from_dict(item) for item in data]
227
+ return []