1+ from __future__ import annotations
2+
13import json
24import shlex
35import tarfile
4- from typing import Any , Dict , Mapping , Optional , Sequence , Tuple , Union
5-
6+ from typing import (
7+ TYPE_CHECKING ,
8+ Any ,
9+ AsyncIterator ,
10+ Dict ,
11+ List ,
12+ Literal ,
13+ Mapping ,
14+ Optional ,
15+ Sequence ,
16+ Tuple ,
17+ Union ,
18+ overload ,
19+ )
20+
21+ from aiohttp import ClientWebSocketResponse
622from multidict import MultiDict
723from yarl import URL
824
25+ from aiodocker .types import JSONObject
26+
927from .exceptions import DockerContainerError , DockerError
1028from .execs import Exec
1129from .jsonstream import json_stream_list , json_stream_stream
1230from .logs import DockerLog
1331from .multiplexed import multiplexed_result_list , multiplexed_result_stream
1432from .stream import Stream
33+ from .types import PortInfo
1534from .utils import identical , parse_result
1635
1736
37+ if TYPE_CHECKING :
38+ from .docker import Docker
39+
40+
1841class DockerContainers :
19- def __init__ (self , docker ) :
42+ def __init__ (self , docker : Docker ) -> None :
2043 self .docker = docker
2144
22- async def list (self , ** kwargs ):
45+ async def list (self , ** kwargs ) -> List [ DockerContainer ] :
2346 data = await self .docker ._query_json (
2447 "containers/json" , method = "GET" , params = kwargs
2548 )
2649 return [DockerContainer (self .docker , ** x ) for x in data ]
2750
28- async def create_or_replace (self , name , config ):
51+ async def create_or_replace (
52+ self ,
53+ name : str ,
54+ config : JSONObject ,
55+ ) -> DockerContainer :
2956 container = None
3057
3158 try :
@@ -44,25 +71,29 @@ async def create_or_replace(self, name, config):
4471
4572 return container
4673
47- async def create (self , config , * , name = None ):
74+ async def create (
75+ self ,
76+ config : JSONObject ,
77+ * ,
78+ name : Optional [str ] = None ,
79+ ) -> DockerContainer :
4880 url = "containers/create"
49-
50- config = json .dumps (config , sort_keys = True ).encode ("utf-8" )
81+ encoded_config = json .dumps (config , sort_keys = True ).encode ("utf-8" )
5182 kwargs = {}
5283 if name :
5384 kwargs ["name" ] = name
5485 data = await self .docker ._query_json (
55- url , method = "POST" , data = config , params = kwargs
86+ url , method = "POST" , data = encoded_config , params = kwargs
5687 )
5788 return DockerContainer (self .docker , id = data ["Id" ])
5889
5990 async def run (
6091 self ,
61- config ,
92+ config : JSONObject ,
6293 * ,
6394 auth : Optional [Union [Mapping , str , bytes ]] = None ,
6495 name : Optional [str ] = None ,
65- ):
96+ ) -> DockerContainer :
6697 """
6798 Create and start a container.
6899
@@ -77,7 +108,7 @@ async def run(
77108 except DockerError as err :
78109 # image not found, try pulling it
79110 if err .status == 404 and "Image" in config :
80- await self .docker .pull (config ["Image" ], auth = auth )
111+ await self .docker .pull (str ( config ["Image" ]) , auth = auth )
81112 container = await self .create (config , name = name )
82113 else :
83114 raise err
@@ -91,26 +122,28 @@ async def run(
91122
92123 return container
93124
94- async def get (self , container , ** kwargs ):
125+ async def get (self , container_id : str , ** kwargs ) -> DockerContainer :
95126 data = await self .docker ._query_json (
96- f"containers/{ container } /json" ,
127+ f"containers/{ container_id } /json" ,
97128 method = "GET" ,
98129 params = kwargs ,
99130 )
100131 return DockerContainer (self .docker , ** data )
101132
102- def container (self , container_id , ** kwargs ):
133+ def container (self , container_id : str , ** kwargs ) -> DockerContainer :
103134 data = {"id" : container_id }
104135 data .update (kwargs )
105136 return DockerContainer (self .docker , ** data )
106137
107138 def exec (self , exec_id : str ) -> Exec :
108139 """Return Exec instance for already created exec object."""
109- return Exec (self .docker , exec_id , None )
140+ return Exec (self .docker , exec_id )
110141
111142
112143class DockerContainer :
113- def __init__ (self , docker , ** kwargs ):
144+ _container : Dict [str , Any ]
145+
146+ def __init__ (self , docker : Docker , ** kwargs ) -> None :
114147 self .docker = docker
115148 self ._container = kwargs
116149 self ._id = self ._container .get (
@@ -122,17 +155,42 @@ def __init__(self, docker, **kwargs):
122155 def id (self ) -> str :
123156 return self ._id
124157
125- def log (self , * , stdout = False , stderr = False , follow = False , ** kwargs ):
158+ @overload
159+ async def log (
160+ self ,
161+ * ,
162+ stdout : bool = False ,
163+ stderr : bool = False ,
164+ follow : Literal [False ] = False ,
165+ ** kwargs ,
166+ ) -> List [str ]: ...
167+
168+ @overload
169+ def log (
170+ self ,
171+ * ,
172+ stdout : bool = False ,
173+ stderr : bool = False ,
174+ follow : Literal [True ],
175+ ** kwargs ,
176+ ) -> AsyncIterator [str ]: ...
177+
178+ def log (
179+ self ,
180+ * ,
181+ stdout : bool = False ,
182+ stderr : bool = False ,
183+ follow : bool = False ,
184+ ** kwargs ,
185+ ) -> Any :
126186 if stdout is False and stderr is False :
127187 raise TypeError ("Need one of stdout or stderr" )
128188
129189 params = {"stdout" : stdout , "stderr" : stderr , "follow" : follow }
130190 params .update (kwargs )
131-
132191 cm = self .docker ._query (
133192 f"containers/{ self ._id } /logs" , method = "GET" , params = params
134193 )
135-
136194 if follow :
137195 return self ._logs_stream (cm )
138196 else :
@@ -154,7 +212,6 @@ async def _logs_list(self, cm):
154212 try :
155213 inspect_info = await self .show ()
156214 except DockerError :
157- cm .cancel ()
158215 raise
159216 is_tty = inspect_info ["Config" ]["Tty" ]
160217
@@ -181,20 +238,20 @@ async def put_archive(self, path, data):
181238 data = await parse_result (response )
182239 return data
183240
184- async def show (self , ** kwargs ):
241+ async def show (self , ** kwargs ) -> Dict [ str , Any ] :
185242 data = await self .docker ._query_json (
186243 f"containers/{ self ._id } /json" , method = "GET" , params = kwargs
187244 )
188245 self ._container = data
189246 return data
190247
191- async def stop (self , ** kwargs ):
248+ async def stop (self , ** kwargs ) -> None :
192249 async with self .docker ._query (
193250 f"containers/{ self ._id } /stop" , method = "POST" , params = kwargs
194251 ):
195252 pass
196253
197- async def start (self , ** kwargs ):
254+ async def start (self , ** kwargs ) -> None :
198255 async with self .docker ._query (
199256 f"containers/{ self ._id } /start" ,
200257 method = "POST" ,
@@ -203,7 +260,7 @@ async def start(self, **kwargs):
203260 ):
204261 pass
205262
206- async def restart (self , timeout = None ):
263+ async def restart (self , timeout = None ) -> None :
207264 params = {}
208265 if timeout is not None :
209266 params ["t" ] = timeout
@@ -214,13 +271,13 @@ async def restart(self, timeout=None):
214271 ):
215272 pass
216273
217- async def kill (self , ** kwargs ):
274+ async def kill (self , ** kwargs ) -> None :
218275 async with self .docker ._query (
219276 f"containers/{ self ._id } /kill" , method = "POST" , params = kwargs
220277 ):
221278 pass
222279
223- async def wait (self , * , timeout = None , ** kwargs ):
280+ async def wait (self , * , timeout = None , ** kwargs ) -> Dict [ str , Any ] :
224281 data = await self .docker ._query_json (
225282 f"containers/{ self ._id } /wait" ,
226283 method = "POST" ,
@@ -229,13 +286,13 @@ async def wait(self, *, timeout=None, **kwargs):
229286 )
230287 return data
231288
232- async def delete (self , ** kwargs ):
289+ async def delete (self , ** kwargs ) -> None :
233290 async with self .docker ._query (
234291 f"containers/{ self ._id } " , method = "DELETE" , params = kwargs
235292 ):
236293 pass
237294
238- async def rename (self , newname ):
295+ async def rename (self , newname ) -> None :
239296 async with self .docker ._query (
240297 f"containers/{ self ._id } /rename" ,
241298 method = "POST" ,
@@ -244,7 +301,7 @@ async def rename(self, newname):
244301 ):
245302 pass
246303
247- async def websocket (self , ** params ):
304+ async def websocket (self , ** params ) -> ClientWebSocketResponse :
248305 if not params :
249306 params = {"stdin" : True , "stdout" : True , "stderr" : True , "stream" : True }
250307 path = f"containers/{ self ._id } /attach/ws"
@@ -280,7 +337,7 @@ async def setup() -> Tuple[URL, Optional[bytes], bool]:
280337
281338 return Stream (self .docker , setup , None )
282339
283- async def port (self , private_port ) :
340+ async def port (self , private_port : int | str ) -> List [ PortInfo ] | None :
284341 if "NetworkSettings" not in self ._container :
285342 await self .show ()
286343
@@ -302,7 +359,25 @@ async def port(self, private_port):
302359
303360 return h_ports
304361
305- def stats (self , * , stream = True ):
362+ @overload
363+ def stats (
364+ self ,
365+ * ,
366+ stream : Literal [True ] = True ,
367+ ) -> AsyncIterator [Dict [str , Any ]]: ...
368+
369+ @overload
370+ async def stats (
371+ self ,
372+ * ,
373+ stream : Literal [False ],
374+ ) -> List [Dict [str , Any ]]: ...
375+
376+ def stats (
377+ self ,
378+ * ,
379+ stream : bool = True ,
380+ ) -> Any :
306381 cm = self .docker ._query (
307382 f"containers/{ self ._id } /stats" ,
308383 params = {"stream" : "1" if stream else "0" },
@@ -333,7 +408,7 @@ async def exec(
333408 environment : Optional [Union [Mapping [str , str ], Sequence [str ]]] = None ,
334409 workdir : Optional [str ] = None ,
335410 detach_keys : Optional [str ] = None ,
336- ):
411+ ) -> Exec :
337412 if isinstance (cmd , str ):
338413 cmd = shlex .split (cmd )
339414 if environment is None :
@@ -418,8 +493,8 @@ async def unpause(self) -> None:
418493 async with self .docker ._query (f"containers/{ self ._id } /unpause" , method = "POST" ):
419494 pass
420495
421- def __getitem__ (self , key ) :
496+ def __getitem__ (self , key : str ) -> Any :
422497 return self ._container [key ]
423498
424- def __hasitem__ (self , key ) :
499+ def __hasitem__ (self , key : str ) -> bool :
425500 return key in self ._container
0 commit comments