Skip to content

Commit 8ca9c63

Browse files
authored
Merge branch 'docker:main' into main
2 parents e011ff5 + bc4c0d7 commit 8ca9c63

File tree

5 files changed

+92
-22
lines changed

5 files changed

+92
-22
lines changed

docker/api/container.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,8 +1164,9 @@ def stats(self, container, decode=None, stream=True, one_shot=None):
11641164
'one_shot is only available in conjunction with '
11651165
'stream=False'
11661166
)
1167-
return self._stream_helper(self._get(url, params=params),
1168-
decode=decode)
1167+
return self._stream_helper(
1168+
self._get(url, stream=True, params=params), decode=decode
1169+
)
11691170
else:
11701171
if decode:
11711172
raise errors.InvalidArgument(

docker/transport/npipesocket.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import win32file
66
import win32pipe
7+
import pywintypes
8+
import win32event
9+
import win32api
710

811
cERROR_PIPE_BUSY = 0xe7
912
cSECURITY_SQOS_PRESENT = 0x100000
@@ -54,7 +57,9 @@ def connect(self, address, retry_count=0):
5457
0,
5558
None,
5659
win32file.OPEN_EXISTING,
57-
cSECURITY_ANONYMOUS | cSECURITY_SQOS_PRESENT,
60+
(cSECURITY_ANONYMOUS
61+
| cSECURITY_SQOS_PRESENT
62+
| win32file.FILE_FLAG_OVERLAPPED),
5863
0
5964
)
6065
except win32pipe.error as e:
@@ -131,22 +136,37 @@ def recv_into(self, buf, nbytes=0):
131136
if not isinstance(buf, memoryview):
132137
readbuf = memoryview(buf)
133138

134-
err, data = win32file.ReadFile(
135-
self._handle,
136-
readbuf[:nbytes] if nbytes else readbuf
137-
)
138-
return len(data)
139-
140-
def _recv_into_py2(self, buf, nbytes):
141-
err, data = win32file.ReadFile(self._handle, nbytes or len(buf))
142-
n = len(data)
143-
buf[:n] = data
144-
return n
139+
event = win32event.CreateEvent(None, True, True, None)
140+
try:
141+
overlapped = pywintypes.OVERLAPPED()
142+
overlapped.hEvent = event
143+
err, data = win32file.ReadFile(
144+
self._handle,
145+
readbuf[:nbytes] if nbytes else readbuf,
146+
overlapped
147+
)
148+
wait_result = win32event.WaitForSingleObject(event, self._timeout)
149+
if wait_result == win32event.WAIT_TIMEOUT:
150+
win32file.CancelIo(self._handle)
151+
raise TimeoutError
152+
return win32file.GetOverlappedResult(self._handle, overlapped, 0)
153+
finally:
154+
win32api.CloseHandle(event)
145155

146156
@check_closed
147157
def send(self, string, flags=0):
148-
err, nbytes = win32file.WriteFile(self._handle, string)
149-
return nbytes
158+
event = win32event.CreateEvent(None, True, True, None)
159+
try:
160+
overlapped = pywintypes.OVERLAPPED()
161+
overlapped.hEvent = event
162+
win32file.WriteFile(self._handle, string, overlapped)
163+
wait_result = win32event.WaitForSingleObject(event, self._timeout)
164+
if wait_result == win32event.WAIT_TIMEOUT:
165+
win32file.CancelIo(self._handle)
166+
raise TimeoutError
167+
return win32file.GetOverlappedResult(self._handle, overlapped, 0)
168+
finally:
169+
win32api.CloseHandle(event)
150170

151171
@check_closed
152172
def sendall(self, string, flags=0):
@@ -165,15 +185,12 @@ def setblocking(self, flag):
165185
def settimeout(self, value):
166186
if value is None:
167187
# Blocking mode
168-
self._timeout = win32pipe.NMPWAIT_WAIT_FOREVER
188+
self._timeout = win32event.INFINITE
169189
elif not isinstance(value, (float, int)) or value < 0:
170190
raise ValueError('Timeout value out of range')
171-
elif value == 0:
172-
# Non-blocking mode
173-
self._timeout = win32pipe.NMPWAIT_NO_WAIT
174191
else:
175192
# Timeout mode - Value converted to milliseconds
176-
self._timeout = value * 1000
193+
self._timeout = int(value * 1000)
177194

178195
def gettimeout(self):
179196
return self._timeout

docker/utils/socket.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def read(socket, n=4096):
3737
select.select([socket], [], [])
3838
else:
3939
poll = select.poll()
40-
poll.register(socket)
40+
poll.register(socket, select.POLLIN | select.POLLPRI)
4141
poll.poll()
4242

4343
try:

docs/change-log.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,47 @@
11
Changelog
22
==========
33

4+
6.1.2
5+
-----
6+
7+
#### Bugfixes
8+
- Fix for socket timeouts on long `docker exec` calls
9+
10+
6.1.1
11+
-----
12+
13+
#### Bugfixes
14+
- Fix `containers.stats()` hanging with `stream=True`
15+
- Correct return type in docs for `containers.diff()` method
16+
17+
18+
6.1.0
19+
-----
20+
21+
### Upgrade Notes
22+
- Errors are no longer returned during client initialization if the credential helper cannot be found. A warning will be emitted instead, and an error is returned if the credential helper is used.
23+
24+
### Features
25+
- Python 3.11 support
26+
- Use `poll()` instead of `select()` on non-Windows platforms
27+
- New API fields
28+
- `network_driver_opt` on container run / create
29+
- `one-shot` on container stats
30+
- `status` on services list
31+
32+
### Bugfixes
33+
- Support for requests 2.29.0+ and urllib3 2.x
34+
- Do not strip characters from volume names
35+
- Fix connection leak on container.exec_* operations
36+
- Fix errors closing named pipes on Windows
37+
38+
6.0.1
39+
-----
40+
41+
### Bugfixes
42+
- Fix for `The pipe has been ended errors` on Windows
43+
- Support floats for container log filtering by timestamp (`since` / `until`)
44+
445
6.0.0
546
-----
647

tests/unit/api_container_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,10 +1528,21 @@ def test_container_stats(self):
15281528
fake_request.assert_called_with(
15291529
'GET',
15301530
url_prefix + 'containers/' + fake_api.FAKE_CONTAINER_ID + '/stats',
1531+
stream=True,
15311532
timeout=60,
15321533
params={'stream': True}
15331534
)
15341535

1536+
def test_container_stats_without_streaming(self):
1537+
self.client.stats(fake_api.FAKE_CONTAINER_ID, stream=False)
1538+
1539+
fake_request.assert_called_with(
1540+
'GET',
1541+
url_prefix + 'containers/' + fake_api.FAKE_CONTAINER_ID + '/stats',
1542+
timeout=60,
1543+
params={'stream': False}
1544+
)
1545+
15351546
def test_container_stats_with_one_shot(self):
15361547
self.client.stats(
15371548
fake_api.FAKE_CONTAINER_ID, stream=False, one_shot=True)

0 commit comments

Comments
 (0)