From bf66cdaa7f78d1836cdb8ee57ad8249dcd559b88 Mon Sep 17 00:00:00 2001 From: Chris Bridge Date: Mon, 23 Mar 2026 12:17:59 -0400 Subject: [PATCH 1/2] Add timeout to all requests --- src/dicomweb_client/web.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/dicomweb_client/web.py b/src/dicomweb_client/web.py index f8e0d0e..b1a4b4f 100644 --- a/src/dicomweb_client/web.py +++ b/src/dicomweb_client/web.py @@ -205,7 +205,8 @@ def __init__( headers: Optional[Dict[str, str]] = None, callback: Optional[Callable] = None, chunk_size: int = 10**6, - permissive_uid: bool = False + permissive_uid: bool = False, + timeout: float | tuple = 20, ) -> None: """Instatiate client. @@ -248,6 +249,10 @@ def __init__( this flag is **not** recommended, since non-conformant UIDs may lead to unexpected errors downstream, e.g., rejection by a DICOMweb server, etc. + timeout: float | tuple, optional + Timeout parameter used for all requests in the format used by the + requests library. May provide a single number or a tuple of + (connect timeout, read timeout). Warning ------- @@ -285,6 +290,7 @@ def __init__( 'Argument "delete_url_prefix" must not be a zero length string.' ) self.delete_url_prefix = delete_url_prefix + self._timeout = timeout # This regular expression extracts the scheme and host name from the URL # and optionally the port number and prefix: @@ -537,7 +543,12 @@ def _invoke_get_request( # encoding. The iter_content() method can be used to iterate over # chunks. If stream is not set, iter_content() will return the # full payload at once. - return self._session.get(url=url, headers=headers, stream=stream) + return self._session.get( + url=url, + headers=headers, + stream=stream, + timeout=self._timeout, + ) if headers is None: headers = {} @@ -1522,7 +1533,12 @@ def _invoke_post_request( headers: Optional[Dict[str, str]] = None ) -> requests.models.Response: logger.debug(f'POST: {url} {headers}') - return self._session.post(url, data=data, headers=headers) + return self._session.post( + url, + data=data, + headers=headers, + timeout=self._timeout, + ) if len(data) > self._chunk_size: logger.info('store data in chunks using chunked transfer encoding') @@ -1623,7 +1639,7 @@ def _http_delete(self, url: str): stop_max_attempt_number=self._max_attempts ) def _invoke_delete_request(url: str) -> requests.models.Response: - return self._session.delete(url) + return self._session.delete(url, timeout=self._timeout) response = _invoke_delete_request(url) if response.status_code == HTTPStatus.METHOD_NOT_ALLOWED: From 6e9fccd22bbd2c6c1b7cf34cd0b465ddd54edeb0 Mon Sep 17 00:00:00 2001 From: Chris Bridge Date: Mon, 23 Mar 2026 12:25:25 -0400 Subject: [PATCH 2/2] Clarify that units are seconds --- src/dicomweb_client/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dicomweb_client/web.py b/src/dicomweb_client/web.py index b1a4b4f..29ef7c6 100644 --- a/src/dicomweb_client/web.py +++ b/src/dicomweb_client/web.py @@ -252,7 +252,7 @@ def __init__( timeout: float | tuple, optional Timeout parameter used for all requests in the format used by the requests library. May provide a single number or a tuple of - (connect timeout, read timeout). + (connect timeout, read timeout), using seconds in both cases. Warning -------