1
+ from __future__ import annotations
2
+
1
3
import json
2
4
import shlex
3
5
import 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
6
22
from multidict import MultiDict
7
23
from yarl import URL
8
24
25
+ from aiodocker .types import JSONObject
26
+
9
27
from .exceptions import DockerContainerError , DockerError
10
28
from .execs import Exec
11
29
from .jsonstream import json_stream_list , json_stream_stream
12
30
from .logs import DockerLog
13
31
from .multiplexed import multiplexed_result_list , multiplexed_result_stream
14
32
from .stream import Stream
33
+ from .types import PortInfo
15
34
from .utils import identical , parse_result
16
35
17
36
37
+ if TYPE_CHECKING :
38
+ from .docker import Docker
39
+
40
+
18
41
class DockerContainers :
19
- def __init__ (self , docker ) :
42
+ def __init__ (self , docker : Docker ) -> None :
20
43
self .docker = docker
21
44
22
- async def list (self , ** kwargs ):
45
+ async def list (self , ** kwargs ) -> List [ DockerContainer ] :
23
46
data = await self .docker ._query_json (
24
47
"containers/json" , method = "GET" , params = kwargs
25
48
)
26
49
return [DockerContainer (self .docker , ** x ) for x in data ]
27
50
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 :
29
56
container = None
30
57
31
58
try :
@@ -44,25 +71,29 @@ async def create_or_replace(self, name, config):
44
71
45
72
return container
46
73
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 :
48
80
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" )
51
82
kwargs = {}
52
83
if name :
53
84
kwargs ["name" ] = name
54
85
data = await self .docker ._query_json (
55
- url , method = "POST" , data = config , params = kwargs
86
+ url , method = "POST" , data = encoded_config , params = kwargs
56
87
)
57
88
return DockerContainer (self .docker , id = data ["Id" ])
58
89
59
90
async def run (
60
91
self ,
61
- config ,
92
+ config : JSONObject ,
62
93
* ,
63
94
auth : Optional [Union [Mapping , str , bytes ]] = None ,
64
95
name : Optional [str ] = None ,
65
- ):
96
+ ) -> DockerContainer :
66
97
"""
67
98
Create and start a container.
68
99
@@ -77,7 +108,7 @@ async def run(
77
108
except DockerError as err :
78
109
# image not found, try pulling it
79
110
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 )
81
112
container = await self .create (config , name = name )
82
113
else :
83
114
raise err
@@ -91,26 +122,28 @@ async def run(
91
122
92
123
return container
93
124
94
- async def get (self , container , ** kwargs ):
125
+ async def get (self , container_id : str , ** kwargs ) -> DockerContainer :
95
126
data = await self .docker ._query_json (
96
- f"containers/{ container } /json" ,
127
+ f"containers/{ container_id } /json" ,
97
128
method = "GET" ,
98
129
params = kwargs ,
99
130
)
100
131
return DockerContainer (self .docker , ** data )
101
132
102
- def container (self , container_id , ** kwargs ):
133
+ def container (self , container_id : str , ** kwargs ) -> DockerContainer :
103
134
data = {"id" : container_id }
104
135
data .update (kwargs )
105
136
return DockerContainer (self .docker , ** data )
106
137
107
138
def exec (self , exec_id : str ) -> Exec :
108
139
"""Return Exec instance for already created exec object."""
109
- return Exec (self .docker , exec_id , None )
140
+ return Exec (self .docker , exec_id )
110
141
111
142
112
143
class DockerContainer :
113
- def __init__ (self , docker , ** kwargs ):
144
+ _container : Dict [str , Any ]
145
+
146
+ def __init__ (self , docker : Docker , ** kwargs ) -> None :
114
147
self .docker = docker
115
148
self ._container = kwargs
116
149
self ._id = self ._container .get (
@@ -122,17 +155,42 @@ def __init__(self, docker, **kwargs):
122
155
def id (self ) -> str :
123
156
return self ._id
124
157
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 :
126
186
if stdout is False and stderr is False :
127
187
raise TypeError ("Need one of stdout or stderr" )
128
188
129
189
params = {"stdout" : stdout , "stderr" : stderr , "follow" : follow }
130
190
params .update (kwargs )
131
-
132
191
cm = self .docker ._query (
133
192
f"containers/{ self ._id } /logs" , method = "GET" , params = params
134
193
)
135
-
136
194
if follow :
137
195
return self ._logs_stream (cm )
138
196
else :
@@ -154,7 +212,6 @@ async def _logs_list(self, cm):
154
212
try :
155
213
inspect_info = await self .show ()
156
214
except DockerError :
157
- cm .cancel ()
158
215
raise
159
216
is_tty = inspect_info ["Config" ]["Tty" ]
160
217
@@ -181,20 +238,20 @@ async def put_archive(self, path, data):
181
238
data = await parse_result (response )
182
239
return data
183
240
184
- async def show (self , ** kwargs ):
241
+ async def show (self , ** kwargs ) -> Dict [ str , Any ] :
185
242
data = await self .docker ._query_json (
186
243
f"containers/{ self ._id } /json" , method = "GET" , params = kwargs
187
244
)
188
245
self ._container = data
189
246
return data
190
247
191
- async def stop (self , ** kwargs ):
248
+ async def stop (self , ** kwargs ) -> None :
192
249
async with self .docker ._query (
193
250
f"containers/{ self ._id } /stop" , method = "POST" , params = kwargs
194
251
):
195
252
pass
196
253
197
- async def start (self , ** kwargs ):
254
+ async def start (self , ** kwargs ) -> None :
198
255
async with self .docker ._query (
199
256
f"containers/{ self ._id } /start" ,
200
257
method = "POST" ,
@@ -203,7 +260,7 @@ async def start(self, **kwargs):
203
260
):
204
261
pass
205
262
206
- async def restart (self , timeout = None ):
263
+ async def restart (self , timeout = None ) -> None :
207
264
params = {}
208
265
if timeout is not None :
209
266
params ["t" ] = timeout
@@ -214,13 +271,13 @@ async def restart(self, timeout=None):
214
271
):
215
272
pass
216
273
217
- async def kill (self , ** kwargs ):
274
+ async def kill (self , ** kwargs ) -> None :
218
275
async with self .docker ._query (
219
276
f"containers/{ self ._id } /kill" , method = "POST" , params = kwargs
220
277
):
221
278
pass
222
279
223
- async def wait (self , * , timeout = None , ** kwargs ):
280
+ async def wait (self , * , timeout = None , ** kwargs ) -> Dict [ str , Any ] :
224
281
data = await self .docker ._query_json (
225
282
f"containers/{ self ._id } /wait" ,
226
283
method = "POST" ,
@@ -229,13 +286,13 @@ async def wait(self, *, timeout=None, **kwargs):
229
286
)
230
287
return data
231
288
232
- async def delete (self , ** kwargs ):
289
+ async def delete (self , ** kwargs ) -> None :
233
290
async with self .docker ._query (
234
291
f"containers/{ self ._id } " , method = "DELETE" , params = kwargs
235
292
):
236
293
pass
237
294
238
- async def rename (self , newname ):
295
+ async def rename (self , newname ) -> None :
239
296
async with self .docker ._query (
240
297
f"containers/{ self ._id } /rename" ,
241
298
method = "POST" ,
@@ -244,7 +301,7 @@ async def rename(self, newname):
244
301
):
245
302
pass
246
303
247
- async def websocket (self , ** params ):
304
+ async def websocket (self , ** params ) -> ClientWebSocketResponse :
248
305
if not params :
249
306
params = {"stdin" : True , "stdout" : True , "stderr" : True , "stream" : True }
250
307
path = f"containers/{ self ._id } /attach/ws"
@@ -280,7 +337,7 @@ async def setup() -> Tuple[URL, Optional[bytes], bool]:
280
337
281
338
return Stream (self .docker , setup , None )
282
339
283
- async def port (self , private_port ) :
340
+ async def port (self , private_port : int | str ) -> List [ PortInfo ] | None :
284
341
if "NetworkSettings" not in self ._container :
285
342
await self .show ()
286
343
@@ -302,7 +359,25 @@ async def port(self, private_port):
302
359
303
360
return h_ports
304
361
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 :
306
381
cm = self .docker ._query (
307
382
f"containers/{ self ._id } /stats" ,
308
383
params = {"stream" : "1" if stream else "0" },
@@ -333,7 +408,7 @@ async def exec(
333
408
environment : Optional [Union [Mapping [str , str ], Sequence [str ]]] = None ,
334
409
workdir : Optional [str ] = None ,
335
410
detach_keys : Optional [str ] = None ,
336
- ):
411
+ ) -> Exec :
337
412
if isinstance (cmd , str ):
338
413
cmd = shlex .split (cmd )
339
414
if environment is None :
@@ -418,8 +493,8 @@ async def unpause(self) -> None:
418
493
async with self .docker ._query (f"containers/{ self ._id } /unpause" , method = "POST" ):
419
494
pass
420
495
421
- def __getitem__ (self , key ) :
496
+ def __getitem__ (self , key : str ) -> Any :
422
497
return self ._container [key ]
423
498
424
- def __hasitem__ (self , key ) :
499
+ def __hasitem__ (self , key : str ) -> bool :
425
500
return key in self ._container
0 commit comments