Skip to content

Commit 6b24ea1

Browse files
Merge pull request #3097 from VWS-Python/adamtheturtle/issue-3092-falsy-transport
Retain explicitly provided falsy custom transports
2 parents d5f5abd + 9a9bf49 commit 6b24ea1

9 files changed

Lines changed: 220 additions & 6 deletions

File tree

newsfragments/3092.change.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Retain explicitly provided falsy custom transports instead of replacing them with defaults.

spelling_private_dict.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ AuthenticationFailure
22
BadImage
33
ConnectionErrorPossiblyImageTooLarge
44
DateRangeError
5+
Falsy
56
ImageTooLarge
67
InactiveProject
78
JSONDecodeError
@@ -45,6 +46,7 @@ dev
4546
dict
4647
docstring
4748
enum
49+
falsy
4850
filename
4951
foo
5052
formdata

src/vws/async_query.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ def __init__(
6060
self._client_secret_key = client_secret_key
6161
self._base_vwq_url = base_vwq_url
6262
self._request_timeout_seconds = request_timeout_seconds
63-
self._transport = transport or AsyncHTTPXTransport()
63+
self._transport = (
64+
transport if transport is not None else AsyncHTTPXTransport()
65+
)
6466

6567
async def aclose(self) -> None:
6668
"""Close the underlying transport if it supports closing."""

src/vws/async_vumark_service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ def __init__(
4646
self._server_secret_key = server_secret_key
4747
self._base_vws_url = base_vws_url
4848
self._request_timeout_seconds = request_timeout_seconds
49-
self._transport = transport or AsyncHTTPXTransport()
49+
self._transport = (
50+
transport if transport is not None else AsyncHTTPXTransport()
51+
)
5052

5153
async def aclose(self) -> None:
5254
"""Close the underlying transport if it supports closing."""

src/vws/async_vws.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def __init__(
5757
self._server_secret_key = server_secret_key
5858
self._base_vws_url = base_vws_url
5959
self._request_timeout_seconds = request_timeout_seconds
60-
self._transport = transport or AsyncHTTPXTransport()
60+
self._transport = (
61+
transport if transport is not None else AsyncHTTPXTransport()
62+
)
6163

6264
async def aclose(self) -> None:
6365
"""Close the underlying transport if it supports closing."""

src/vws/query.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ def __init__(
5656
self._client_secret_key = client_secret_key
5757
self._base_vwq_url = base_vwq_url
5858
self._request_timeout_seconds = request_timeout_seconds
59-
self._transport = transport or RequestsTransport()
59+
self._transport = (
60+
transport if transport is not None else RequestsTransport()
61+
)
6062

6163
def query(
6264
self,

src/vws/vumark_service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ def __init__(
4343
self._server_secret_key = server_secret_key
4444
self._base_vws_url = base_vws_url
4545
self._request_timeout_seconds = request_timeout_seconds
46-
self._transport = transport or RequestsTransport()
46+
self._transport = (
47+
transport if transport is not None else RequestsTransport()
48+
)
4749

4850
def generate_vumark_instance(
4951
self,

src/vws/vws.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ def __init__(
5656
self._server_secret_key = server_secret_key
5757
self._base_vws_url = base_vws_url
5858
self._request_timeout_seconds = request_timeout_seconds
59-
self._transport = transport or RequestsTransport()
59+
self._transport = (
60+
transport if transport is not None else RequestsTransport()
61+
)
6062

6163
def make_request(
6264
self,

tests/test_transports.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
"""Tests for HTTP transport implementations."""
22

3+
import io
4+
import uuid
35
from http import HTTPStatus
46

57
import httpx
68
import pytest
79
import respx
810

11+
from vws import (
12+
VWS,
13+
AsyncCloudRecoService,
14+
AsyncVuMarkService,
15+
AsyncVWS,
16+
CloudRecoService,
17+
VuMarkService,
18+
)
919
from vws.response import Response
1020
from vws.transports import AsyncHTTPXTransport, HTTPXTransport
21+
from vws.vumark_accept import VuMarkAccept
1122

1223

1324
class TestHTTPXTransport:
@@ -206,3 +217,191 @@ async def test_context_manager() -> None:
206217
assert route.called
207218
assert isinstance(response, Response)
208219
assert response.status_code == HTTPStatus.OK
220+
221+
222+
class _FalsyTransport:
223+
"""A sync transport that is falsy but protocol-conforming."""
224+
225+
def __bool__(self) -> bool:
226+
"""Return ``False`` so truthiness checks would skip this
227+
transport.
228+
"""
229+
return False
230+
231+
def close(self) -> None:
232+
"""Close the transport."""
233+
234+
def __call__(
235+
self,
236+
*,
237+
method: str,
238+
url: str,
239+
headers: dict[str, str],
240+
data: bytes,
241+
request_timeout: float | tuple[float, float],
242+
) -> Response:
243+
"""Return a successful API response for the requested URL."""
244+
del method, headers, request_timeout
245+
if url.endswith("/query"):
246+
body = '{"result_code":"Success","results":[]}'
247+
return Response(
248+
text=body,
249+
url=url,
250+
status_code=HTTPStatus.OK,
251+
headers={},
252+
request_body=data,
253+
tell_position=0,
254+
content=body.encode(),
255+
)
256+
if "/instances" in url:
257+
content = b"vumark-bytes"
258+
return Response(
259+
text="",
260+
url=url,
261+
status_code=HTTPStatus.OK,
262+
headers={},
263+
request_body=data,
264+
tell_position=0,
265+
content=content,
266+
)
267+
body = '{"result_code":"Success","results":[]}'
268+
return Response(
269+
text=body,
270+
url=url,
271+
status_code=HTTPStatus.OK,
272+
headers={},
273+
request_body=data,
274+
tell_position=0,
275+
content=body.encode(),
276+
)
277+
278+
279+
class _FalsyAsyncTransport:
280+
"""An async transport that is falsy but protocol-conforming."""
281+
282+
def __bool__(self) -> bool:
283+
"""Return ``False`` so truthiness checks would skip this
284+
transport.
285+
"""
286+
return False
287+
288+
async def aclose(self) -> None:
289+
"""Close the transport."""
290+
291+
async def __call__(
292+
self,
293+
*,
294+
method: str,
295+
url: str,
296+
headers: dict[str, str],
297+
data: bytes,
298+
request_timeout: float | tuple[float, float],
299+
) -> Response:
300+
"""Return a successful API response for the requested URL."""
301+
del method, headers, request_timeout
302+
if url.endswith("/query"):
303+
body = '{"result_code":"Success","results":[]}'
304+
return Response(
305+
text=body,
306+
url=url,
307+
status_code=HTTPStatus.OK,
308+
headers={},
309+
request_body=data,
310+
tell_position=0,
311+
content=body.encode(),
312+
)
313+
if "/instances" in url:
314+
content = b"vumark-bytes"
315+
return Response(
316+
text="",
317+
url=url,
318+
status_code=HTTPStatus.OK,
319+
headers={},
320+
request_body=data,
321+
tell_position=0,
322+
content=content,
323+
)
324+
body = '{"result_code":"Success","results":[]}'
325+
return Response(
326+
text=body,
327+
url=url,
328+
status_code=HTTPStatus.OK,
329+
headers={},
330+
request_body=data,
331+
tell_position=0,
332+
content=body.encode(),
333+
)
334+
335+
336+
def test_falsy_sync_transport_is_retained(
337+
high_quality_image: io.BytesIO,
338+
) -> None:
339+
"""Falsy custom sync transports are not replaced by the default."""
340+
access_key = uuid.uuid4().hex
341+
secret_key = uuid.uuid4().hex
342+
transport = _FalsyTransport()
343+
assert not transport
344+
345+
targets = VWS(
346+
server_access_key=access_key,
347+
server_secret_key=secret_key,
348+
transport=transport,
349+
).list_targets()
350+
assert not targets
351+
352+
query_results = CloudRecoService(
353+
client_access_key=access_key,
354+
client_secret_key=secret_key,
355+
transport=transport,
356+
).query(image=high_quality_image)
357+
assert not query_results
358+
359+
vumark_bytes = VuMarkService(
360+
server_access_key=access_key,
361+
server_secret_key=secret_key,
362+
transport=transport,
363+
).generate_vumark_instance(
364+
target_id="target",
365+
instance_id="instance",
366+
accept=VuMarkAccept.PNG,
367+
)
368+
assert vumark_bytes == b"vumark-bytes"
369+
370+
371+
@pytest.mark.asyncio
372+
async def test_falsy_async_transport_is_retained(
373+
high_quality_image: io.BytesIO,
374+
) -> None:
375+
"""Falsy custom async transports are not replaced by the default."""
376+
access_key = uuid.uuid4().hex
377+
secret_key = uuid.uuid4().hex
378+
transport = _FalsyAsyncTransport()
379+
assert not transport
380+
381+
async with AsyncVWS(
382+
server_access_key=access_key,
383+
server_secret_key=secret_key,
384+
transport=transport,
385+
) as vws_client:
386+
assert not await vws_client.list_targets()
387+
388+
async with AsyncCloudRecoService(
389+
client_access_key=access_key,
390+
client_secret_key=secret_key,
391+
transport=transport,
392+
) as cloud_reco_client:
393+
assert not await cloud_reco_client.query(image=high_quality_image)
394+
395+
async with AsyncVuMarkService(
396+
server_access_key=access_key,
397+
server_secret_key=secret_key,
398+
transport=transport,
399+
) as vumark_client:
400+
assert (
401+
await vumark_client.generate_vumark_instance(
402+
target_id="target",
403+
instance_id="instance",
404+
accept=VuMarkAccept.PNG,
405+
)
406+
== b"vumark-bytes"
407+
)

0 commit comments

Comments
 (0)