Skip to content

Commit b13c9da

Browse files
Add connection and read timeout CLI options (#1952)
Users can now override the default 30-second HTTP timeout via --connection-timeout-seconds and --read-timeout-seconds. These options are available on all VWS commands and the Cloud Reco command, and are passed as a (connect, read) tuple to the underlying vws-python library. Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 6766455 commit b13c9da

15 files changed

Lines changed: 480 additions & 99 deletions

src/vws_cli/commands.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
target_name_option,
4949
target_width_option,
5050
)
51+
from vws_cli.options.timeout import (
52+
connection_timeout_seconds_option,
53+
read_timeout_seconds_option,
54+
)
5155

5256

5357
@beartype
@@ -131,13 +135,18 @@ def _base_vws_url_option(command: Callable[..., None]) -> Callable[..., None]:
131135
@server_secret_key_option
132136
@target_id_option
133137
@_base_vws_url_option
138+
@connection_timeout_seconds_option
139+
@read_timeout_seconds_option
134140
@_handle_vws_exceptions()
135141
@beartype
136142
def get_target_record(
143+
*,
137144
server_access_key: str,
138145
server_secret_key: str,
139146
target_id: str,
140147
base_vws_url: str,
148+
connection_timeout_seconds: float,
149+
read_timeout_seconds: float,
141150
) -> None:
142151
"""Get a target record.
143152
@@ -149,6 +158,10 @@ def get_target_record(
149158
server_access_key=server_access_key,
150159
server_secret_key=server_secret_key,
151160
base_vws_url=base_vws_url,
161+
request_timeout_seconds=(
162+
connection_timeout_seconds,
163+
read_timeout_seconds,
164+
),
152165
)
153166
record = vws_client.get_target_record(target_id=target_id).target_record
154167

@@ -161,11 +174,15 @@ def get_target_record(
161174
@server_secret_key_option
162175
@_handle_vws_exceptions()
163176
@_base_vws_url_option
177+
@connection_timeout_seconds_option
178+
@read_timeout_seconds_option
164179
@beartype
165180
def list_targets(
166181
server_access_key: str,
167182
server_secret_key: str,
168183
base_vws_url: str,
184+
connection_timeout_seconds: float,
185+
read_timeout_seconds: float,
169186
) -> None:
170187
"""List targets.
171188
@@ -177,6 +194,10 @@ def list_targets(
177194
server_access_key=server_access_key,
178195
server_secret_key=server_secret_key,
179196
base_vws_url=base_vws_url,
197+
request_timeout_seconds=(
198+
connection_timeout_seconds,
199+
read_timeout_seconds,
200+
),
180201
)
181202
targets = vws_client.list_targets()
182203
yaml_list = yaml.dump(data=targets)
@@ -189,12 +210,17 @@ def list_targets(
189210
@target_id_option
190211
@_handle_vws_exceptions()
191212
@_base_vws_url_option
213+
@connection_timeout_seconds_option
214+
@read_timeout_seconds_option
192215
@beartype
193216
def get_duplicate_targets(
217+
*,
194218
server_access_key: str,
195219
server_secret_key: str,
196220
target_id: str,
197221
base_vws_url: str,
222+
connection_timeout_seconds: float,
223+
read_timeout_seconds: float,
198224
) -> None:
199225
"""Get a list of potential duplicate targets.
200226
@@ -206,6 +232,10 @@ def get_duplicate_targets(
206232
server_access_key=server_access_key,
207233
server_secret_key=server_secret_key,
208234
base_vws_url=base_vws_url,
235+
request_timeout_seconds=(
236+
connection_timeout_seconds,
237+
read_timeout_seconds,
238+
),
209239
)
210240
record = vws_client.get_duplicate_targets(target_id=target_id)
211241

@@ -218,11 +248,15 @@ def get_duplicate_targets(
218248
@server_secret_key_option
219249
@_handle_vws_exceptions()
220250
@_base_vws_url_option
251+
@connection_timeout_seconds_option
252+
@read_timeout_seconds_option
221253
@beartype
222254
def get_database_summary_report(
223255
server_access_key: str,
224256
server_secret_key: str,
225257
base_vws_url: str,
258+
connection_timeout_seconds: float,
259+
read_timeout_seconds: float,
226260
) -> None:
227261
"""Get a database summary report.
228262
@@ -234,6 +268,10 @@ def get_database_summary_report(
234268
server_access_key=server_access_key,
235269
server_secret_key=server_secret_key,
236270
base_vws_url=base_vws_url,
271+
request_timeout_seconds=(
272+
connection_timeout_seconds,
273+
read_timeout_seconds,
274+
),
237275
)
238276
report = vws_client.get_database_summary_report()
239277
yaml_report = yaml.dump(data=dataclasses.asdict(obj=report))
@@ -246,12 +284,17 @@ def get_database_summary_report(
246284
@target_id_option
247285
@_handle_vws_exceptions()
248286
@_base_vws_url_option
287+
@connection_timeout_seconds_option
288+
@read_timeout_seconds_option
249289
@beartype
250290
def get_target_summary_report(
291+
*,
251292
server_access_key: str,
252293
server_secret_key: str,
253294
target_id: str,
254295
base_vws_url: str,
296+
connection_timeout_seconds: float,
297+
read_timeout_seconds: float,
255298
) -> None:
256299
"""Get a target summary report.
257300
@@ -263,6 +306,10 @@ def get_target_summary_report(
263306
server_access_key=server_access_key,
264307
server_secret_key=server_secret_key,
265308
base_vws_url=base_vws_url,
309+
request_timeout_seconds=(
310+
connection_timeout_seconds,
311+
read_timeout_seconds,
312+
),
266313
)
267314
report = vws_client.get_target_summary_report(target_id=target_id)
268315
report_dict = dataclasses.asdict(obj=report)
@@ -278,12 +325,17 @@ def get_target_summary_report(
278325
@target_id_option
279326
@_handle_vws_exceptions()
280327
@_base_vws_url_option
328+
@connection_timeout_seconds_option
329+
@read_timeout_seconds_option
281330
@beartype
282331
def delete_target(
332+
*,
283333
server_access_key: str,
284334
server_secret_key: str,
285335
target_id: str,
286336
base_vws_url: str,
337+
connection_timeout_seconds: float,
338+
read_timeout_seconds: float,
287339
) -> None:
288340
"""Delete a target.
289341
@@ -295,6 +347,10 @@ def delete_target(
295347
server_access_key=server_access_key,
296348
server_secret_key=server_secret_key,
297349
base_vws_url=base_vws_url,
350+
request_timeout_seconds=(
351+
connection_timeout_seconds,
352+
read_timeout_seconds,
353+
),
298354
)
299355

300356
vws_client.delete_target(target_id=target_id)
@@ -310,6 +366,8 @@ def delete_target(
310366
@active_flag_option(allow_none=False)
311367
@_handle_vws_exceptions()
312368
@_base_vws_url_option
369+
@connection_timeout_seconds_option
370+
@read_timeout_seconds_option
313371
@beartype
314372
def add_target(
315373
*,
@@ -320,6 +378,8 @@ def add_target(
320378
image_file_path: Path,
321379
active_flag_choice: ActiveFlagChoice,
322380
base_vws_url: str,
381+
connection_timeout_seconds: float,
382+
read_timeout_seconds: float,
323383
application_metadata: str | None = None,
324384
) -> None:
325385
"""Add a target.
@@ -332,6 +392,10 @@ def add_target(
332392
server_access_key=server_access_key,
333393
server_secret_key=server_secret_key,
334394
base_vws_url=base_vws_url,
395+
request_timeout_seconds=(
396+
connection_timeout_seconds,
397+
read_timeout_seconds,
398+
),
335399
)
336400

337401
image_bytes = image_file_path.read_bytes()
@@ -364,6 +428,8 @@ def add_target(
364428
@target_id_option
365429
@_handle_vws_exceptions()
366430
@_base_vws_url_option
431+
@connection_timeout_seconds_option
432+
@read_timeout_seconds_option
367433
@beartype
368434
def update_target(
369435
*,
@@ -372,6 +438,8 @@ def update_target(
372438
target_id: str,
373439
image_file_path: Path | None,
374440
base_vws_url: str,
441+
connection_timeout_seconds: float,
442+
read_timeout_seconds: float,
375443
name: str | None = None,
376444
application_metadata: str | None = None,
377445
active_flag_choice: ActiveFlagChoice | None = None,
@@ -387,6 +455,10 @@ def update_target(
387455
server_access_key=server_access_key,
388456
server_secret_key=server_secret_key,
389457
base_vws_url=base_vws_url,
458+
request_timeout_seconds=(
459+
connection_timeout_seconds,
460+
read_timeout_seconds,
461+
),
390462
)
391463

392464
if image_file_path is None:
@@ -445,6 +517,8 @@ def update_target(
445517
@server_secret_key_option
446518
@target_id_option
447519
@_base_vws_url_option
520+
@connection_timeout_seconds_option
521+
@read_timeout_seconds_option
448522
@_handle_vws_exceptions()
449523
@beartype
450524
def wait_for_target_processed(
@@ -454,6 +528,8 @@ def wait_for_target_processed(
454528
target_id: str,
455529
seconds_between_requests: float,
456530
base_vws_url: str,
531+
connection_timeout_seconds: float,
532+
read_timeout_seconds: float,
457533
timeout_seconds: float,
458534
) -> None:
459535
"""Wait for a target to be "processed". This is done by polling the VWS
@@ -463,6 +539,10 @@ def wait_for_target_processed(
463539
server_access_key=server_access_key,
464540
server_secret_key=server_secret_key,
465541
base_vws_url=base_vws_url,
542+
request_timeout_seconds=(
543+
connection_timeout_seconds,
544+
read_timeout_seconds,
545+
),
466546
)
467547

468548
try:

src/vws_cli/options/timeout.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""``click`` options regarding timeouts."""
2+
3+
from collections.abc import Callable
4+
from typing import Any
5+
6+
import click
7+
from beartype import beartype
8+
9+
10+
@beartype
11+
def connection_timeout_seconds_option(
12+
command: Callable[..., Any],
13+
) -> Callable[..., Any]:
14+
"""An option decorator for the connection timeout."""
15+
return click.option(
16+
"--connection-timeout-seconds",
17+
type=click.FloatRange(min=0.05),
18+
default=30,
19+
help="The connection timeout for HTTP requests, in seconds.",
20+
show_default=True,
21+
)(command)
22+
23+
24+
@beartype
25+
def read_timeout_seconds_option(
26+
command: Callable[..., Any],
27+
) -> Callable[..., Any]:
28+
"""An option decorator for the read timeout."""
29+
return click.option(
30+
"--read-timeout-seconds",
31+
type=click.FloatRange(min=0.05),
32+
default=30,
33+
help="The read timeout for HTTP requests, in seconds.",
34+
show_default=True,
35+
)(command)

src/vws_cli/query.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
client_access_key_option,
2828
client_secret_key_option,
2929
)
30+
from vws_cli.options.timeout import (
31+
connection_timeout_seconds_option,
32+
read_timeout_seconds_option,
33+
)
3034

3135

3236
@beartype
@@ -116,6 +120,8 @@ def _handle_vwq_exceptions() -> Iterator[None]:
116120
@_include_target_data_option
117121
@_max_num_results_option
118122
@_base_vwq_url_option
123+
@connection_timeout_seconds_option
124+
@read_timeout_seconds_option
119125
@_handle_vwq_exceptions()
120126
# We set the ``version`` parameter because in PyInstaller binaries,
121127
# ``pkg_resources`` is not available.
@@ -131,12 +137,18 @@ def vuforia_cloud_reco(
131137
max_num_results: int,
132138
include_target_data: CloudRecoIncludeTargetData,
133139
base_vwq_url: str,
140+
connection_timeout_seconds: float,
141+
read_timeout_seconds: float,
134142
) -> None:
135143
"""Make a request to the Vuforia Cloud Recognition Service API."""
136144
client = CloudRecoService(
137145
client_access_key=client_access_key,
138146
client_secret_key=client_secret_key,
139147
base_vwq_url=base_vwq_url,
148+
request_timeout_seconds=(
149+
connection_timeout_seconds,
150+
read_timeout_seconds,
151+
),
140152
)
141153
query_result = client.query(
142154
image=io.BytesIO(initial_bytes=image.read_bytes()),

tests/test_help/test_query_help.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,11 @@ Options:
2222
1<=x<=50]
2323
--base-vwq-url TEXT The base URL for the VWQ API. [default:
2424
https://cloudreco.vuforia.com]
25+
--connection-timeout-seconds FLOAT RANGE
26+
The connection timeout for HTTP requests, in
27+
seconds. [default: 30; x>=0.05]
28+
--read-timeout-seconds FLOAT RANGE
29+
The read timeout for HTTP requests, in
30+
seconds. [default: 30; x>=0.05]
2531
--version Show the version and exit.
2632
--help Show this message and exit.

0 commit comments

Comments
 (0)