@@ -59,19 +59,28 @@ class Tesseract:
5959 _lastlog : str | None = None
6060 _client : HTTPClient | LocalClient | None = None
6161 _stream_logs : BoolOrCallable = False
62+ _timeout : float | tuple [float , float ] | None = None
6263
63- def __init__ (self , url : str , server_output_path : str | Path | None = None ) -> None :
64+ def __init__ (
65+ self ,
66+ url : str ,
67+ server_output_path : str | Path | None = None ,
68+ timeout : float | tuple [float , float ] | None = None ,
69+ ) -> None :
6470 warnings .warn (
6571 "Direct instantiation of Tesseract is deprecated. "
6672 "Use Tesseract.from_url(), Tesseract.from_image(), or Tesseract.from_tesseract_api() instead." ,
6773 UserWarning ,
6874 stacklevel = 2 ,
6975 )
70- self ._client = HTTPClient (url , output_path = server_output_path )
76+ self ._client = HTTPClient (url , output_path = server_output_path , timeout = timeout )
7177
7278 @classmethod
7379 def from_url (
74- cls , url : str , server_output_path : str | Path | None = None
80+ cls ,
81+ url : str ,
82+ server_output_path : str | Path | None = None ,
83+ timeout : float | tuple [float , float ] | None = None ,
7584 ) -> Tesseract :
7685 """Create a Tesseract instance from a URL.
7786
@@ -84,12 +93,17 @@ def from_url(
8493 Must be a path accessible from the client machine (e.g., via a shared or
8594 mounted filesystem), since the server writes .bin files there and the
8695 client reads them from the same path.
96+ timeout: Request timeout in seconds. Can be a float for both connect and
97+ read timeouts, or a ``(connect, read)`` tuple for separate control.
98+ ``None`` (the default) disables timeouts. See the `requests documentation
99+ <https://requests.readthedocs.io/en/latest/user/advanced/#timeouts>`_
100+ for details.
87101
88102 Returns:
89103 A Tesseract instance.
90104 """
91105 obj = cls .__new__ (cls )
92- obj ._client = HTTPClient (url , output_path = server_output_path )
106+ obj ._client = HTTPClient (url , output_path = server_output_path , timeout = timeout )
93107 return obj
94108
95109 @classmethod
@@ -113,6 +127,7 @@ def from_image(
113127 docker_args : list [str ] | None = None ,
114128 runtime_config : dict [str , Any ] | None = None ,
115129 stream_logs : BoolOrCallable = False ,
130+ timeout : float | tuple [float , float ] | None = None ,
116131 ) -> Tesseract :
117132 """Create a Tesseract instance from a Docker image.
118133
@@ -151,6 +166,12 @@ def from_image(
151166 `{"profiling": True}` enables profiling via TESSERACT_PROFILING=true.
152167 stream_logs: If True, stream logs to stdout while endpoints run.
153168 If a callable, stream logs to that callable instead.
169+ timeout: Request timeout in seconds for HTTP calls to the Tesseract.
170+ Can be a float for both connect and read timeouts, or a
171+ ``(connect, read)`` tuple for separate control. ``None`` (the default)
172+ disables timeouts. See the `requests documentation
173+ <https://requests.readthedocs.io/en/latest/user/advanced/#timeouts>`_
174+ for details.
154175
155176 Returns:
156177 A Tesseract instance.
@@ -171,6 +192,7 @@ def from_image(
171192 output_path = Path (tempfile .mkdtemp (prefix = "tesseract_output_" ))
172193
173194 obj ._stream_logs = stream_logs
195+ obj ._timeout = timeout
174196 obj ._spawn_config = dict (
175197 image_name = image_name ,
176198 volumes = volumes ,
@@ -321,6 +343,7 @@ def serve(self) -> None:
321343 self ._client = HTTPClient (
322344 f"http://{ host_ip } :{ container .host_port } " ,
323345 output_path = Path (output_path ) if output_path else None ,
346+ timeout = self ._timeout ,
324347 )
325348
326349 # Ensure that the Tesseract is torn down once the object is garbage collected,
@@ -666,9 +689,15 @@ def _decode_array(
666689class HTTPClient :
667690 """HTTP Client for Tesseracts."""
668691
669- def __init__ (self , url : str , output_path : str | Path | None = None ) -> None :
692+ def __init__ (
693+ self ,
694+ url : str ,
695+ output_path : str | Path | None = None ,
696+ timeout : float | tuple [float , float ] | None = None ,
697+ ) -> None :
670698 self ._url = self ._sanitize_url (url )
671699 self ._output_path = output_path
700+ self ._timeout = timeout
672701 self ._session = requests .Session ()
673702 self ._session .headers ["Content-Type" ] = "application/json"
674703
@@ -709,15 +738,15 @@ def _request(
709738 data = orjson .dumps (encoded_payload )
710739 try :
711740 response = self ._session .request (
712- method = method , url = url , data = data , params = params
741+ method = method , url = url , data = data , params = params , timeout = self . _timeout
713742 )
714743 except requests .ConnectionError :
715744 # Retry once on stale keep-alive connections. There is a race
716745 # between urllib3's is_connection_dropped check and the server
717746 # closing idle connections (uvicorn timeout_keep_alive) that
718747 # can cause ConnectionError on an otherwise healthy server.
719748 response = self ._session .request (
720- method = method , url = url , data = data , params = params
749+ method = method , url = url , data = data , params = params , timeout = self . _timeout
721750 )
722751
723752 if response .status_code == requests .codes .unprocessable_entity :
0 commit comments