diff --git a/CHANGES/900.feature b/CHANGES/900.feature new file mode 100644 index 00000000..4c8fde02 --- /dev/null +++ b/CHANGES/900.feature @@ -0,0 +1 @@ +Introduce a sentinel value to `_do_query()` and its friend methods to allow configuring per-request infinite timeouts instead of always falling back to the session-level default timeout when setting the timeout argument to `None`, and add the timeout arguments to image-related API wrappers diff --git a/aiodocker/docker.py b/aiodocker/docker.py index d69968ae..687f2568 100644 --- a/aiodocker/docker.py +++ b/aiodocker/docker.py @@ -40,6 +40,7 @@ from .swarm import DockerSwarm from .system import DockerSystem from .tasks import DockerTasks +from .types import SENTINEL, Sentinel from .utils import httpize, parse_result from .volumes import DockerVolume, DockerVolumes @@ -218,7 +219,7 @@ async def _query( params: Optional[JSONObject] = None, data: Optional[Any] = None, headers=None, - timeout=None, + timeout: Union[float, aiohttp.ClientTimeout, Sentinel, None] = SENTINEL, chunked=None, read_until_eof: bool = True, versioned_api: bool = True, @@ -248,7 +249,7 @@ async def _do_query( params: Optional[JSONObject], data: Any, headers, - timeout, + timeout: Union[float, aiohttp.ClientTimeout, Sentinel, None] = SENTINEL, chunked, read_until_eof: bool, versioned_api: bool, @@ -260,8 +261,11 @@ async def _do_query( headers = CIMultiDict(headers) if "Content-Type" not in headers: headers["Content-Type"] = "application/json" - if timeout is None: + if timeout is SENTINEL: timeout = self.session.timeout + assert not isinstance(timeout, Sentinel) + if not isinstance(timeout, aiohttp.ClientTimeout): + timeout = aiohttp.ClientTimeout(timeout) try: real_params = httpize(params) response = await self.session.request( @@ -304,7 +308,7 @@ async def _query_json( params: Optional[JSONObject] = None, data: Optional[Any] = None, headers=None, - timeout=None, + timeout: Union[float, aiohttp.ClientTimeout, Sentinel, None] = SENTINEL, read_until_eof: bool = True, versioned_api: bool = True, ) -> Any: @@ -337,7 +341,7 @@ def _query_chunked_post( params: Optional[JSONObject] = None, data: Optional[Any] = None, headers=None, - timeout=None, + timeout: Union[float, aiohttp.ClientTimeout, Sentinel, None] = SENTINEL, read_until_eof: bool = True, versioned_api: bool = True, ) -> AbstractAsyncContextManager[aiohttp.ClientResponse]: diff --git a/aiodocker/images.py b/aiodocker/images.py index 488ecc8c..d62a7127 100644 --- a/aiodocker/images.py +++ b/aiodocker/images.py @@ -17,7 +17,7 @@ ) from .jsonstream import json_stream_list, json_stream_stream -from .types import JSONObject, SupportsRead +from .types import SENTINEL, JSONObject, Sentinel, SupportsRead from .utils import clean_map, compose_auth_header @@ -69,6 +69,7 @@ async def pull( repo: Optional[str] = None, platform: Optional[str] = None, stream: Literal[False] = False, + timeout: Union[float, Sentinel, None] = SENTINEL, ) -> Dict[str, Any]: ... @overload @@ -81,6 +82,7 @@ def pull( repo: Optional[str] = None, platform: Optional[str] = None, stream: Literal[True], + timeout: Union[float, Sentinel, None] = SENTINEL, ) -> AsyncIterator[Dict[str, Any]]: ... def pull( @@ -92,6 +94,7 @@ def pull( repo: Optional[str] = None, platform: Optional[str] = None, stream: bool = False, + timeout: Union[float, Sentinel, None] = SENTINEL, ) -> Any: """ Similar to `docker pull`, pull an image locally @@ -122,7 +125,13 @@ def pull( ) # TODO: assert registry == repo? headers["X-Registry-Auth"] = compose_auth_header(auth, registry) - cm = self.docker._query("images/create", "POST", params=params, headers=headers) + cm = self.docker._query( + "images/create", + "POST", + params=params, + headers=headers, + timeout=timeout, + ) return self._handle_response(cm, stream) def _handle_response(self, cm, stream): diff --git a/aiodocker/types.py b/aiodocker/types.py index 72c55656..560fc721 100644 --- a/aiodocker/types.py +++ b/aiodocker/types.py @@ -1,5 +1,6 @@ from __future__ import annotations +import enum import sys from typing import ( TYPE_CHECKING, @@ -45,3 +46,10 @@ def read(self, length: int = ..., /) -> _T_co: ... class PortInfo(TypedDict): HostIp: str HostPort: str + + +class Sentinel(enum.Enum): + TOKEN = enum.auto() + + +SENTINEL = Sentinel.TOKEN