bookalimo 1.0.1__py3-none-any.whl → 1.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.
@@ -1,9 +1,18 @@
1
1
  import logging
2
- from typing import Any, Optional
2
+ from contextlib import contextmanager
3
+ from time import perf_counter
4
+ from typing import Any, Callable, Generator, NoReturn, Optional, Union, cast, overload
3
5
 
4
6
  import httpx
5
7
 
6
- from ..exceptions import BookalimoError, BookalimoHTTPError, BookalimoTimeout
8
+ from ..exceptions import (
9
+ BookalimoConnectionError,
10
+ BookalimoError,
11
+ BookalimoHTTPError,
12
+ BookalimoRequestError,
13
+ BookalimoTimeout,
14
+ )
15
+ from ..logging import configure_httpx_logging, redact_url
7
16
 
8
17
  logger = logging.getLogger("bookalimo.transport")
9
18
 
@@ -31,7 +40,7 @@ def handle_api_errors(
31
40
 
32
41
  def handle_http_error(
33
42
  response: httpx.Response, req_id: Optional[str], path: str
34
- ) -> None:
43
+ ) -> NoReturn:
35
44
  """Handle HTTP status errors."""
36
45
  status = response.status_code
37
46
 
@@ -57,3 +66,195 @@ def handle_http_error(
57
66
  status_code=status,
58
67
  payload=payload if isinstance(payload, dict) else {"raw": payload},
59
68
  )
69
+
70
+
71
+ def build_url(base_url: str, path: str) -> str:
72
+ """Build full URL ensuring a single leading slash on path and no trailing slash on base."""
73
+ norm_base = base_url.rstrip("/")
74
+ norm_path = path if path.startswith("/") else f"/{path}"
75
+ return f"{norm_base}{norm_path}"
76
+
77
+
78
+ def get_request_id(resp: httpx.Response) -> Optional[str]:
79
+ """Extract a request id header if present."""
80
+ return cast(Optional[str], resp.headers.get("x-request-id")) or cast(
81
+ Optional[str], resp.headers.get("request-id")
82
+ )
83
+
84
+
85
+ def parse_json_or_raise(
86
+ response: httpx.Response, path: str, req_id: Optional[str]
87
+ ) -> Any:
88
+ """Parse response JSON or raise a helpful error with preview.
89
+
90
+ Returns parsed JSON value on success; raises BookalimoError on invalid JSON.
91
+ """
92
+ try:
93
+ return response.json()
94
+ except ValueError as e:
95
+ if logger.isEnabledFor(logging.DEBUG):
96
+ logger.warning("× [%s] %s invalid JSON", req_id or "-", path)
97
+ preview = (response.text or "")[:256] if hasattr(response, "text") else ""
98
+ raise BookalimoError(f"Invalid JSON response: {preview}") from e
99
+
100
+
101
+ def raise_if_retryable_status(
102
+ response: httpx.Response, should_retry_status: Callable[[int], bool]
103
+ ) -> None:
104
+ """Raise httpx.HTTPStatusError for retryable status codes so retry logic can handle it."""
105
+ if should_retry_status(response.status_code):
106
+ raise httpx.HTTPStatusError(
107
+ message=f"Retryable HTTP status: {response.status_code}",
108
+ request=response.request,
109
+ response=response,
110
+ )
111
+
112
+
113
+ def pre_log(url: str, body_keys: list[str], req_id: str) -> None:
114
+ """Standard pre-request debug log."""
115
+ logger.debug("→ [%s] POST %s body_keys=%s", req_id, redact_url(url), body_keys)
116
+
117
+
118
+ def post_log(url: str, response: httpx.Response, start: float, req_id: str) -> None:
119
+ """Standard post-response debug log."""
120
+ dur_ms = (perf_counter() - start) * 1000.0
121
+ content_len = len(response.content) if hasattr(response, "content") else None
122
+ logger.debug(
123
+ "← [%s] %s %s in %.1f ms len=%s reqid=%s",
124
+ req_id,
125
+ response.status_code,
126
+ redact_url(url),
127
+ dur_ms,
128
+ content_len,
129
+ get_request_id(response),
130
+ )
131
+
132
+
133
+ @contextmanager
134
+ def map_httpx_exceptions(
135
+ req_id: Optional[str], path: str
136
+ ) -> Generator[None, None, None]:
137
+ """Map common httpx exceptions to domain-specific exceptions.
138
+
139
+ Ensures we consistently translate low-level httpx errors.
140
+ """
141
+ try:
142
+ yield
143
+ except httpx.TimeoutException:
144
+ if logger.isEnabledFor(logging.DEBUG):
145
+ logger.warning("× [%s] %s timeout", req_id or "-", path)
146
+ raise BookalimoTimeout("Request timeout") from None
147
+ except httpx.ConnectError:
148
+ if logger.isEnabledFor(logging.DEBUG):
149
+ logger.warning("× [%s] %s connection error", req_id or "-", path)
150
+ raise BookalimoConnectionError(
151
+ "Connection error - unable to reach Book-A-Limo API"
152
+ ) from None
153
+ except httpx.RequestError as e:
154
+ if logger.isEnabledFor(logging.DEBUG):
155
+ logger.warning(
156
+ "× [%s] %s request error: %s", req_id or "-", path, e.__class__.__name__
157
+ )
158
+ # Preserve underlying message
159
+ raise BookalimoRequestError(f"Request Error: {e}") from e
160
+ except httpx.HTTPStatusError as e:
161
+ if logger.isEnabledFor(logging.DEBUG):
162
+ logger.warning(
163
+ "× [%s] %s HTTP error: %s", req_id or "-", path, e.__class__.__name__
164
+ )
165
+ status_code = getattr(getattr(e, "response", None), "status_code", None)
166
+ raise BookalimoHTTPError(f"HTTP Error: {e}", status_code=status_code) from e
167
+
168
+
169
+ def log_request(request: httpx.Request) -> None:
170
+ """Event hook to log HTTP requests with redacted URLs (sync version)."""
171
+ if logger.isEnabledFor(logging.DEBUG):
172
+ logger.debug(
173
+ "HTTP Request: %s %s",
174
+ request.method,
175
+ redact_url(str(request.url)),
176
+ )
177
+
178
+
179
+ async def log_request_async(request: httpx.Request) -> None:
180
+ """Event hook to log HTTP requests with redacted URLs (async version)."""
181
+ if logger.isEnabledFor(logging.DEBUG):
182
+ logger.debug(
183
+ "HTTP Request: %s %s",
184
+ request.method,
185
+ redact_url(str(request.url)),
186
+ )
187
+
188
+
189
+ def _log_response(response: httpx.Response) -> None:
190
+ """Event hook to log HTTP responses with redacted URLs."""
191
+ if logger.isEnabledFor(logging.DEBUG):
192
+ request = response.request
193
+ logger.debug(
194
+ "HTTP Response: %s %s -> %d",
195
+ request.method,
196
+ redact_url(str(request.url)),
197
+ response.status_code,
198
+ )
199
+
200
+
201
+ def log_response(response: httpx.Response) -> None:
202
+ """Event hook to log HTTP responses with redacted URLs."""
203
+ _log_response(response)
204
+
205
+
206
+ async def log_response_async(response: httpx.Response) -> None:
207
+ """Event hook to log HTTP responses with redacted URLs."""
208
+ _log_response(response)
209
+
210
+
211
+ @overload
212
+ def setup_secure_logging_and_client(
213
+ *,
214
+ is_async: bool,
215
+ timeout: Any,
216
+ client: Optional[httpx.AsyncClient],
217
+ ) -> tuple[httpx.AsyncClient, bool]: ...
218
+
219
+
220
+ @overload
221
+ def setup_secure_logging_and_client(
222
+ *,
223
+ is_async: bool,
224
+ timeout: Any,
225
+ client: Optional[httpx.Client],
226
+ ) -> tuple[httpx.Client, bool]: ...
227
+
228
+
229
+ def setup_secure_logging_and_client(
230
+ *,
231
+ is_async: bool,
232
+ timeout: Any,
233
+ client: Optional[Union[httpx.Client, httpx.AsyncClient]] = None,
234
+ ) -> tuple[Union[httpx.Client, httpx.AsyncClient], bool]:
235
+ """
236
+ Configure secure logging and create httpx client with event hooks if needed.
237
+
238
+ Returns:
239
+ Tuple of (client, owns_client_flag)
240
+ """
241
+ # Configure httpx logging to prevent sensitive data exposure
242
+ configure_httpx_logging()
243
+
244
+ if client is not None:
245
+ return client, False
246
+
247
+ # Create client with secure logging event hooks
248
+ event_hooks: dict[str, list[Callable[..., Any]]] = (
249
+ {
250
+ "request": [log_request_async if is_async else log_request],
251
+ "response": [log_response_async if is_async else log_response],
252
+ }
253
+ if logger.isEnabledFor(logging.DEBUG)
254
+ else {}
255
+ )
256
+
257
+ if is_async:
258
+ return httpx.AsyncClient(timeout=timeout, event_hooks=event_hooks), True
259
+ else:
260
+ return httpx.Client(timeout=timeout, event_hooks=event_hooks), True
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: bookalimo
3
+ Version: 1.0.2
4
+ Summary: Python wrapper for the Book-A-Limo API
5
+ Author-email: Jonathan Oren <jonathan@bookalimo.com>
6
+ Maintainer-email: Jonathan Oren <jonathan@bookalimo.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/asparagusbeef/bookalimo-python
9
+ Project-URL: Documentation, https://asparagusbeef.github.io/bookalimo-python
10
+ Project-URL: Repository, https://github.com/asparagusbeef/bookalimo-python
11
+ Project-URL: Issues, https://github.com/asparagusbeef/bookalimo-python/issues
12
+ Project-URL: Changelog, https://github.com/asparagusbeef/bookalimo-python/blob/main/CHANGELOG.md
13
+ Keywords: bookalimo,api,transportation,booking
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: httpx>=0.25.0
30
+ Requires-Dist: pydantic>=2.11.0
31
+ Requires-Dist: pycountry>=22.0.0
32
+ Requires-Dist: us>=3.0.0
33
+ Requires-Dist: airportsdata>=20230101
34
+ Provides-Extra: places
35
+ Requires-Dist: google-maps-places>=0.1.0; extra == "places"
36
+ Requires-Dist: google-api-core>=2.0.0; extra == "places"
37
+ Requires-Dist: numpy>=2.0.0; extra == "places"
38
+ Requires-Dist: rapidfuzz>=3.0.0; extra == "places"
39
+ Requires-Dist: typing-extensions>=4.15.0; python_version < "3.11" and extra == "places"
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
42
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
43
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
44
+ Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
45
+ Requires-Dist: httpx[mock]>=0.25.0; extra == "dev"
46
+ Requires-Dist: respx>=0.20.0; extra == "dev"
47
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
48
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
49
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
50
+ Requires-Dist: build>=1.0.0; extra == "dev"
51
+ Requires-Dist: twine>=4.0.0; extra == "dev"
52
+ Requires-Dist: python-dotenv>=1.0.0; extra == "dev"
53
+ Requires-Dist: types-protobuf>=6.0.0; extra == "dev"
54
+ Provides-Extra: test
55
+ Requires-Dist: pytest>=7.0.0; extra == "test"
56
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
57
+ Requires-Dist: pytest-cov>=4.0.0; extra == "test"
58
+ Requires-Dist: pytest-mock>=3.10.0; extra == "test"
59
+ Requires-Dist: httpx[mock]>=0.25.0; extra == "test"
60
+ Requires-Dist: respx>=0.20.0; extra == "test"
61
+ Dynamic: license-file
62
+
63
+ # Bookalimo Python SDK
64
+
65
+ [![codecov](https://codecov.io/gh/asparagusbeef/bookalimo-python/branch/main/graph/badge.svg?token=H588J8Q1M8)](https://codecov.io/gh/asparagusbeef/bookalimo-python)
66
+ [![Docs](https://img.shields.io/github/deployments/asparagusbeef/bookalimo-python/github-pages?label=docs&logo=github)](https://asparagusbeef.github.io/bookalimo-python)
67
+ [![PyPI version](https://badge.fury.io/py/bookalimo.svg)](https://badge.fury.io/py/bookalimo)
68
+ [![Python Support](https://img.shields.io/pypi/pyversions/bookalimo.svg)](https://pypi.org/project/bookalimo/)
69
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
70
+ [![Code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
71
+
72
+ Python client library for the Book-A-Limo transportation booking API with async/sync support, type safety, and Google Places integration.
73
+
74
+ ## Design philosophy: IDE-first DX
75
+
76
+ The library is **comprehensively typed** and **richly documented** via docstrings. Most users can rely on IDE hints, docstrings, and autocomplete without reading the docs.
77
+
78
+ ## Features
79
+
80
+ - **Async & Sync Support** – `AsyncBookalimo` and `Bookalimo` clients
81
+ - **Type Safety** – Full Pydantic models with validation
82
+ - **Google Places Integration** – Location search and geocoding
83
+ - **Automatic Retry** – Built-in exponential backoff
84
+ - **Resource Management** – Context managers for proper cleanup
85
+
86
+ ## Installation
87
+
88
+ ```bash
89
+ pip install bookalimo
90
+
91
+ # With Google Places integration
92
+ pip install bookalimo[places]
93
+ ```
94
+
95
+ ## Quick Example
96
+
97
+ ```python
98
+ import asyncio
99
+ from bookalimo import (
100
+ AsyncBookalimo,
101
+ )
102
+ from bookalimo.transport.auth import (
103
+ Credentials,
104
+ )
105
+ from bookalimo.schemas import (
106
+ RateType,
107
+ Location,
108
+ LocationType,
109
+ Address,
110
+ City,
111
+ Airport,
112
+ PriceRequest,
113
+ BookRequest,
114
+ )
115
+
116
+
117
+ async def book_ride():
118
+ credentials = Credentials.create(
119
+ "your_id",
120
+ "your_password",
121
+ is_customer=False,
122
+ )
123
+
124
+ pickup = Location(
125
+ type=LocationType.ADDRESS,
126
+ address=Address(
127
+ place_name="Empire State Building",
128
+ city=City(
129
+ city_name="New York",
130
+ country_code="US",
131
+ state_code="NY",
132
+ ),
133
+ ),
134
+ )
135
+ dropoff = Location(
136
+ type=LocationType.AIRPORT,
137
+ airport=Airport(iata_code="JFK"),
138
+ )
139
+
140
+ async with AsyncBookalimo(credentials=credentials) as client:
141
+ # Get pricing
142
+ quote = await client.pricing.quote(
143
+ PriceRequest(
144
+ rate_type=RateType.P2P,
145
+ date_time="12/25/2024 03:00 PM",
146
+ pickup=pickup,
147
+ dropoff=dropoff,
148
+ passengers=2,
149
+ luggage=2,
150
+ )
151
+ )
152
+
153
+ # Book reservation
154
+ booking = await client.reservations.book(
155
+ BookRequest(
156
+ token=quote.token,
157
+ method="charge",
158
+ )
159
+ )
160
+ return booking.reservation_id
161
+
162
+
163
+ confirmation = asyncio.run(book_ride())
164
+ ```
165
+
166
+ ## Sync Usage
167
+
168
+ ```python
169
+ from bookalimo import (
170
+ Bookalimo,
171
+ )
172
+
173
+ with Bookalimo(credentials=credentials) as client:
174
+ quote = client.pricing.quote(PriceRequest(...))
175
+ booking = client.reservations.book(
176
+ BookRequest(
177
+ token=quote.token,
178
+ method="charge",
179
+ )
180
+ )
181
+ ```
182
+
183
+ ## Google Places Integration
184
+
185
+ ```python
186
+ async with AsyncBookalimo(
187
+ credentials=credentials,
188
+ google_places_api_key="your-google-places-key",
189
+ ) as client:
190
+ # Search locations
191
+ results = await client.places.search("Hilton Miami Beach")
192
+
193
+ # Resolve airports near landmarks
194
+ airports = await client.places.resolve_airport(query="eiffel tower")
195
+ top_airport = airports[0] # Closest airport with confidence scoring
196
+
197
+ # Use in booking flow
198
+ quote = await client.pricing.quote(...)
199
+ ```
200
+
201
+ ## Error Handling
202
+
203
+ ```python
204
+ from bookalimo.exceptions import (
205
+ BookalimoHTTPError,
206
+ BookalimoValidationError,
207
+ )
208
+
209
+ try:
210
+ booking = await client.reservations.book(...)
211
+ except BookalimoValidationError as e:
212
+ print(f"Invalid input: {e.message}")
213
+ except BookalimoHTTPError as e:
214
+ if e.status_code == 401:
215
+ print("Authentication failed")
216
+ ```
217
+
218
+ ## Environment
219
+
220
+ ```bash
221
+ export GOOGLE_PLACES_API_KEY="your_google_places_key"
222
+ export BOOKALIMO_LOG_LEVEL="DEBUG"
223
+ ```
224
+
225
+ ## Documentation
226
+
227
+ **📖 Complete Documentation:** [https://asparagusbeef.github.io/bookalimo-python](https://asparagusbeef.github.io/bookalimo-python)
228
+
229
+ ## Requirements
230
+
231
+ * Python 3.9+
232
+ * Book-A-Limo API credentials
233
+ * Dependencies: httpx, pydantic, pycountry, us, airportsdata, typing-extensions for Python 3.9-3.10
234
+ - Optional: google-maps-places, google-api-core, numpy, rapidfuzz
235
+
236
+ ## Support & Resources
237
+
238
+ * GitHub: [https://github.com/asparagusbeef/bookalimo-python](https://github.com/asparagusbeef/bookalimo-python)
239
+ * PyPI: [https://pypi.org/project/bookalimo/](https://pypi.org/project/bookalimo/)
240
+ * Issues: [https://github.com/asparagusbeef/bookalimo-python/issues](https://github.com/asparagusbeef/bookalimo-python/issues)
241
+ * Changelog: [CHANGELOG.md](./CHANGELOG.md)
242
+
243
+ ## License
244
+
245
+ MIT License — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,40 @@
1
+ bookalimo/__init__.py,sha256=ZFDpnSOo5VQEgusbVAxyF-neFxBptPZANZ-eE1PeIsU,516
2
+ bookalimo/_version.py,sha256=KcHRbOssHorjmpk3Xs1RTqGNSA-sw9tjYqwV19Zfa9c,259
3
+ bookalimo/client.py,sha256=g_Mep6yjwCE8l5cYTKsvuy0eW78TpP9NSsRmnqNdUDY,11414
4
+ bookalimo/config.py,sha256=K3HlCj8v34kFM2MaG9evGFomU95hRmenRi1zqe4h8EY,456
5
+ bookalimo/exceptions.py,sha256=pShnuvfMZEP_cChczWhNBLIqa7EOgr4ZNnWj6kphARU,4001
6
+ bookalimo/logging.py,sha256=Z6it7PxjdwQ5P6C8VtyKn14Lftt19WwwZAQ1n5zrMqM,10585
7
+ bookalimo/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
8
+ bookalimo/integrations/__init__.py,sha256=edmQP2Uo8JiNIVaeB4rxM8vagz7BQBhyGEJo70OVS0M,51
9
+ bookalimo/integrations/google_places/__init__.py,sha256=A6jBfGk8ZLYgFiTCGBytBOsyzKVWHI_kjnuZOgdCONc,866
10
+ bookalimo/integrations/google_places/client_async.py,sha256=ai-tZJLsUpmgklRBt2-5n1_nDsBY34dzr2k_ZoqenH4,9144
11
+ bookalimo/integrations/google_places/client_sync.py,sha256=ZfACzI01IEH8w6LGdMui-_t1Kc8lV-VRrV3XOP06M_k,8958
12
+ bookalimo/integrations/google_places/common.py,sha256=lLRRgJnZ-uDincxv9vW5il3_LHhY0yY8eGYhkYItkNA,16140
13
+ bookalimo/integrations/google_places/proto_adapter.py,sha256=XuVDr-PH9fopykRwNZxQTS_kIBCdwPwcNYBIHCZjmRo,6809
14
+ bookalimo/integrations/google_places/resolve_airport.py,sha256=tnsZ5rFteu9HxOj72AQ7O9TJLQPKO3i0oLA_7hNNHDI,14580
15
+ bookalimo/integrations/google_places/transports.py,sha256=O3JB73kRm4BU7MT0eUFQhe5jocZ8Z_gI94bvgvjifJU,3342
16
+ bookalimo/schemas/__init__.py,sha256=7utetaurplWJ-w1PtsbARl2HkuGHhBq3EMorTUuF8Vg,4075
17
+ bookalimo/schemas/base.py,sha256=Qyq72xycTF1TR5ZZIOclUENJ6M9FYhXmnVBSabJ_XJc,3802
18
+ bookalimo/schemas/requests.py,sha256=EoB5GIMCd7eiddoHeV4uPjERRrK0iCXWB1dNNGvuneM,5490
19
+ bookalimo/schemas/responses.py,sha256=3tVfoOblezKcoz5s_CLm4LJwqe9qpO2W9jMVZysZHsQ,4691
20
+ bookalimo/schemas/shared.py,sha256=loXKjpbsTz1-LH17V_IeQ38bXQIplPz_E1Zkgj0gNRM,11053
21
+ bookalimo/schemas/places/__init__.py,sha256=EZvAPG69VDzrtmduvOkMA8mHiOf_bQh4XEsK7777ikk,1451
22
+ bookalimo/schemas/places/common.py,sha256=SjY3FbsAQKM5o6DDtLIBx1yvJJGc29UUVVtWglQoSh8,10927
23
+ bookalimo/schemas/places/field_mask.py,sha256=L6bDkGYpkX63drm4HgDtlNr-GLmgELGCCEWSznlNgWU,6628
24
+ bookalimo/schemas/places/google.py,sha256=_9HU67Coj2UAJyac5F7Cp0GflyjfLRso4tyIWa2rNns,35392
25
+ bookalimo/schemas/places/place.py,sha256=5jBqMbPHbRTtXeoFaUtRoIUdzvJeCgMRdeyVt88Axe0,10681
26
+ bookalimo/services/__init__.py,sha256=4wom7MpoeqKy5y99LYFKkl-CAJ7faDN9ZlXUhKVqlAg,303
27
+ bookalimo/services/pricing.py,sha256=MDgvYZGDuYYncEr8d2sZumVW3phFbj6GFXMGIaz3kuc,2095
28
+ bookalimo/services/reservations.py,sha256=CLfvHKl81jb50yk9kPuD37O1F4rmYzjoJ6aioQw-byY,4101
29
+ bookalimo/transport/__init__.py,sha256=yD5TDnoqg1xtex41jqJ7Hes849efiYgvW3nkaYr6ZjE,221
30
+ bookalimo/transport/auth.py,sha256=Uq9ybiBg51FMs4NWJ50idX8v2MOOomawluZ7Ca2Xs4I,1257
31
+ bookalimo/transport/base.py,sha256=qeX2HfLaE6gUT5evjOdPiBh8x7z7A84UuWVsgBK3Pyo,1394
32
+ bookalimo/transport/httpx_async.py,sha256=feVBMPzWBUA_bBT_oHLK1-lhYSuhWSGjW_E6s9ZhWZ4,4717
33
+ bookalimo/transport/httpx_sync.py,sha256=XVGlt4HTpNHFndPQi4cy0V2XzRhZ7G6VjZi-3yXv8fg,4868
34
+ bookalimo/transport/retry.py,sha256=YiJtcdNZUbkrLa1CtKsRtcFO273XJX8naKd8d6ht0LU,2688
35
+ bookalimo/transport/utils.py,sha256=YPAPRCC2FIPpR-AFuccQ0IZDvg5r3aNSvHWyXdYw-BM,8472
36
+ bookalimo-1.0.2.dist-info/licenses/LICENSE,sha256=dW9UYOWGfs9eFlJvwSSkJBDhPRic2mrgFkg6aA0xkMM,1070
37
+ bookalimo-1.0.2.dist-info/METADATA,sha256=DD52pPu_uOQVpq6PVgjP-tQ3ztO9eHfaouBKfpTlX-4,8136
38
+ bookalimo-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
+ bookalimo-1.0.2.dist-info/top_level.txt,sha256=ZgYiDX2GfZCp4pevWn4X2qyEr-Dh7-cq5Y1j2jMhB1s,10
40
+ bookalimo-1.0.2.dist-info/RECORD,,