We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Unable to use docker to run containers started from Jupyter Notebook
Hello,

I'm currently following this tutorial: https://github.com/aws/amazon-sagemaker-examples/blob/main/sagemaker-pipelines/tabular/local-mode/sagemaker-pipelines-local-mode.ipynb

I'm getting the following error from trying to run it (it uses docker in the background to run contaiiners). It executes everything when the following command is run: 

``` python
execution = pipeline.start()

```

I get the following error:

```python
Creating q0r36pja78-algo-1-ywafn ... 
Creating q0r36pja78-algo-1-ywafn ... done
Attaching to q0r36pja78-algo-1-ywafn
Traceback (most recent call last):
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\Scripts\docker-compose.exe\__main__.py", line 7, in <module>
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\cli\main.py", line 81, in main
    command_func()
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\cli\main.py", line 203, in perform_command
    handler(command, command_options)
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\metrics\decorator.py", line 18, in wrapper
    result = fn(*args, **kwargs)
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\cli\main.py", line 1216, in up
    cascade_starter = log_printer.run()
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\cli\log_printer.py", line 88, in run
    for line in consume_queue(queue, self.cascade_stop):
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\cli\log_printer.py", line 250, in consume_queue
    raise item.exc
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\cli\log_printer.py", line 162, in tail_container_logs
    for item in build_log_generator(container, log_args):
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\utils.py", line 50, in split_buffer
    for data in stream_as_text(stream):
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\compose\utils.py", line 26, in stream_as_text
    for data in stream:
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\docker\types\daemon.py", line 32, in __next__
    return next(self._stream)
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\docker\api\client.py", line 418, in <genexpr>
    gen = (data for (_, data) in gen)
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\docker\utils\socket.py", line 95, in <genexpr>
    return ((STDOUT, frame) for frame in frames_iter_tty(socket))
  File "C:\Users\franc\anaconda3\envs\sm-pipelines-modelbuild\lib\site-packages\docker\utils\socket.py", line 128, in frames_iter_tty
    if len(result) == 0:
TypeError: object of type 'int' has no len()
Pipeline step 'AbaloneProcess' FAILED. Failure message is: RuntimeError: Failed to run: ['docker-compose', '-f', 'C:\\Users\\franc\\AppData\\Local\\Temp\\tmpga4umz96\\docker-compose.yaml', 'up', '--build', '--abort-on-container-exit']
Pipeline execution c0a11456-aec5-48ec-adde-4ee45085efa8 FAILED because step 'AbaloneProcess' failed.

```

Version of the modules:
Python 3.8.16
docker 6.0.1
docker-compose 1.29.2
docker desktop 4.16.3

Thanks


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `docker/utils/socket.py`
Content:
```
1 import errno
2 import os
3 import select
4 import socket as pysocket
5 import struct
6 
7 try:
8     from ..transport import NpipeSocket
9 except ImportError:
10     NpipeSocket = type(None)
11 
12 
13 STDOUT = 1
14 STDERR = 2
15 
16 
17 class SocketError(Exception):
18     pass
19 
20 
21 # NpipeSockets have their own error types
22 # pywintypes.error: (109, 'ReadFile', 'The pipe has been ended.')
23 NPIPE_ENDED = 109
24 
25 
26 def read(socket, n=4096):
27     """
28     Reads at most n bytes from socket
29     """
30 
31     recoverable_errors = (errno.EINTR, errno.EDEADLK, errno.EWOULDBLOCK)
32 
33     if not isinstance(socket, NpipeSocket):
34         select.select([socket], [], [])
35 
36     try:
37         if hasattr(socket, 'recv'):
38             return socket.recv(n)
39         if isinstance(socket, getattr(pysocket, 'SocketIO')):
40             return socket.read(n)
41         return os.read(socket.fileno(), n)
42     except OSError as e:
43         if e.errno not in recoverable_errors:
44             raise
45     except Exception as e:
46         is_pipe_ended = (isinstance(socket, NpipeSocket) and
47                          len(e.args) > 0 and
48                          e.args[0] == NPIPE_ENDED)
49         if is_pipe_ended:
50             # npipes don't support duplex sockets, so we interpret
51             # a PIPE_ENDED error as a close operation (0-length read).
52             return 0
53         raise
54 
55 
56 def read_exactly(socket, n):
57     """
58     Reads exactly n bytes from socket
59     Raises SocketError if there isn't enough data
60     """
61     data = bytes()
62     while len(data) < n:
63         next_data = read(socket, n - len(data))
64         if not next_data:
65             raise SocketError("Unexpected EOF")
66         data += next_data
67     return data
68 
69 
70 def next_frame_header(socket):
71     """
72     Returns the stream and size of the next frame of data waiting to be read
73     from socket, according to the protocol defined here:
74 
75     https://docs.docker.com/engine/api/v1.24/#attach-to-a-container
76     """
77     try:
78         data = read_exactly(socket, 8)
79     except SocketError:
80         return (-1, -1)
81 
82     stream, actual = struct.unpack('>BxxxL', data)
83     return (stream, actual)
84 
85 
86 def frames_iter(socket, tty):
87     """
88     Return a generator of frames read from socket. A frame is a tuple where
89     the first item is the stream number and the second item is a chunk of data.
90 
91     If the tty setting is enabled, the streams are multiplexed into the stdout
92     stream.
93     """
94     if tty:
95         return ((STDOUT, frame) for frame in frames_iter_tty(socket))
96     else:
97         return frames_iter_no_tty(socket)
98 
99 
100 def frames_iter_no_tty(socket):
101     """
102     Returns a generator of data read from the socket when the tty setting is
103     not enabled.
104     """
105     while True:
106         (stream, n) = next_frame_header(socket)
107         if n < 0:
108             break
109         while n > 0:
110             result = read(socket, n)
111             if result is None:
112                 continue
113             data_length = len(result)
114             if data_length == 0:
115                 # We have reached EOF
116                 return
117             n -= data_length
118             yield (stream, result)
119 
120 
121 def frames_iter_tty(socket):
122     """
123     Return a generator of data read from the socket when the tty setting is
124     enabled.
125     """
126     while True:
127         result = read(socket)
128         if len(result) == 0:
129             # We have reached EOF
130             return
131         yield result
132 
133 
134 def consume_socket_output(frames, demux=False):
135     """
136     Iterate through frames read from the socket and return the result.
137 
138     Args:
139 
140         demux (bool):
141             If False, stdout and stderr are multiplexed, and the result is the
142             concatenation of all the frames. If True, the streams are
143             demultiplexed, and the result is a 2-tuple where each item is the
144             concatenation of frames belonging to the same stream.
145     """
146     if demux is False:
147         # If the streams are multiplexed, the generator returns strings, that
148         # we just need to concatenate.
149         return bytes().join(frames)
150 
151     # If the streams are demultiplexed, the generator yields tuples
152     # (stdout, stderr)
153     out = [None, None]
154     for frame in frames:
155         # It is guaranteed that for each frame, one and only one stream
156         # is not None.
157         assert frame != (None, None)
158         if frame[0] is not None:
159             if out[0] is None:
160                 out[0] = frame[0]
161             else:
162                 out[0] += frame[0]
163         else:
164             if out[1] is None:
165                 out[1] = frame[1]
166             else:
167                 out[1] += frame[1]
168     return tuple(out)
169 
170 
171 def demux_adaptor(stream_id, data):
172     """
173     Utility to demultiplex stdout and stderr when reading frames from the
174     socket.
175     """
176     if stream_id == STDOUT:
177         return (data, None)
178     elif stream_id == STDERR:
179         return (None, data)
180     else:
181         raise ValueError(f'{stream_id} is not a valid stream')
182
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/docker/utils/socket.py b/docker/utils/socket.py
--- a/docker/utils/socket.py
+++ b/docker/utils/socket.py
@@ -49,7 +49,7 @@
         if is_pipe_ended:
             # npipes don't support duplex sockets, so we interpret
             # a PIPE_ENDED error as a close operation (0-length read).
-            return 0
+            return ''
         raise

{"golden_diff": "diff --git a/docker/utils/socket.py b/docker/utils/socket.py\n--- a/docker/utils/socket.py\n+++ b/docker/utils/socket.py\n@@ -49,7 +49,7 @@\n         if is_pipe_ended:\n             # npipes don't support duplex sockets, so we interpret\n             # a PIPE_ENDED error as a close operation (0-length read).\n-            return 0\n+            return ''\n         raise\n", "issue": "Unable to use docker to run containers started from Jupyter Notebook\nHello,\r\n\r\nI'm currently following this tutorial: https://github.com/aws/amazon-sagemaker-examples/blob/main/sagemaker-pipelines/tabular/local-mode/sagemaker-pipelines-local-mode.ipynb\r\n\r\nI'm getting the following error from trying to run it (it uses docker in the background to run contaiiners). It executes everything when the following command is run: \r\n\r\n``` python\r\nexecution = pipeline.start()\r\n\r\n```\r\n\r\nI get the following error:\r\n\r\n```python\r\nCreating q0r36pja78-algo-1-ywafn ... \r\nCreating q0r36pja78-algo-1-ywafn ... done\r\nAttaching to q0r36pja78-algo-1-ywafn\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\runpy.py\", line 194, in _run_module_as_main\r\n    return _run_code(code, main_globals, None,\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\runpy.py\", line 87, in _run_code\r\n    exec(code, run_globals)\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\Scripts\\docker-compose.exe\\__main__.py\", line 7, in <module>\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\cli\\main.py\", line 81, in main\r\n    command_func()\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\cli\\main.py\", line 203, in perform_command\r\n    handler(command, command_options)\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\metrics\\decorator.py\", line 18, in wrapper\r\n    result = fn(*args, **kwargs)\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\cli\\main.py\", line 1216, in up\r\n    cascade_starter = log_printer.run()\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\cli\\log_printer.py\", line 88, in run\r\n    for line in consume_queue(queue, self.cascade_stop):\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\cli\\log_printer.py\", line 250, in consume_queue\r\n    raise item.exc\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\cli\\log_printer.py\", line 162, in tail_container_logs\r\n    for item in build_log_generator(container, log_args):\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\utils.py\", line 50, in split_buffer\r\n    for data in stream_as_text(stream):\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\compose\\utils.py\", line 26, in stream_as_text\r\n    for data in stream:\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\docker\\types\\daemon.py\", line 32, in __next__\r\n    return next(self._stream)\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\docker\\api\\client.py\", line 418, in <genexpr>\r\n    gen = (data for (_, data) in gen)\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\docker\\utils\\socket.py\", line 95, in <genexpr>\r\n    return ((STDOUT, frame) for frame in frames_iter_tty(socket))\r\n  File \"C:\\Users\\franc\\anaconda3\\envs\\sm-pipelines-modelbuild\\lib\\site-packages\\docker\\utils\\socket.py\", line 128, in frames_iter_tty\r\n    if len(result) == 0:\r\nTypeError: object of type 'int' has no len()\r\nPipeline step 'AbaloneProcess' FAILED. Failure message is: RuntimeError: Failed to run: ['docker-compose', '-f', 'C:\\\\Users\\\\franc\\\\AppData\\\\Local\\\\Temp\\\\tmpga4umz96\\\\docker-compose.yaml', 'up', '--build', '--abort-on-container-exit']\r\nPipeline execution c0a11456-aec5-48ec-adde-4ee45085efa8 FAILED because step 'AbaloneProcess' failed.\r\n\r\n```\r\n\r\nVersion of the modules:\r\nPython 3.8.16\r\ndocker 6.0.1\r\ndocker-compose 1.29.2\r\ndocker desktop 4.16.3\r\n\r\nThanks\r\n\n", "before_files": [{"content": "import errno\nimport os\nimport select\nimport socket as pysocket\nimport struct\n\ntry:\n    from ..transport import NpipeSocket\nexcept ImportError:\n    NpipeSocket = type(None)\n\n\nSTDOUT = 1\nSTDERR = 2\n\n\nclass SocketError(Exception):\n    pass\n\n\n# NpipeSockets have their own error types\n# pywintypes.error: (109, 'ReadFile', 'The pipe has been ended.')\nNPIPE_ENDED = 109\n\n\ndef read(socket, n=4096):\n    \"\"\"\n    Reads at most n bytes from socket\n    \"\"\"\n\n    recoverable_errors = (errno.EINTR, errno.EDEADLK, errno.EWOULDBLOCK)\n\n    if not isinstance(socket, NpipeSocket):\n        select.select([socket], [], [])\n\n    try:\n        if hasattr(socket, 'recv'):\n            return socket.recv(n)\n        if isinstance(socket, getattr(pysocket, 'SocketIO')):\n            return socket.read(n)\n        return os.read(socket.fileno(), n)\n    except OSError as e:\n        if e.errno not in recoverable_errors:\n            raise\n    except Exception as e:\n        is_pipe_ended = (isinstance(socket, NpipeSocket) and\n                         len(e.args) > 0 and\n                         e.args[0] == NPIPE_ENDED)\n        if is_pipe_ended:\n            # npipes don't support duplex sockets, so we interpret\n            # a PIPE_ENDED error as a close operation (0-length read).\n            return 0\n        raise\n\n\ndef read_exactly(socket, n):\n    \"\"\"\n    Reads exactly n bytes from socket\n    Raises SocketError if there isn't enough data\n    \"\"\"\n    data = bytes()\n    while len(data) < n:\n        next_data = read(socket, n - len(data))\n        if not next_data:\n            raise SocketError(\"Unexpected EOF\")\n        data += next_data\n    return data\n\n\ndef next_frame_header(socket):\n    \"\"\"\n    Returns the stream and size of the next frame of data waiting to be read\n    from socket, according to the protocol defined here:\n\n    https://docs.docker.com/engine/api/v1.24/#attach-to-a-container\n    \"\"\"\n    try:\n        data = read_exactly(socket, 8)\n    except SocketError:\n        return (-1, -1)\n\n    stream, actual = struct.unpack('>BxxxL', data)\n    return (stream, actual)\n\n\ndef frames_iter(socket, tty):\n    \"\"\"\n    Return a generator of frames read from socket. A frame is a tuple where\n    the first item is the stream number and the second item is a chunk of data.\n\n    If the tty setting is enabled, the streams are multiplexed into the stdout\n    stream.\n    \"\"\"\n    if tty:\n        return ((STDOUT, frame) for frame in frames_iter_tty(socket))\n    else:\n        return frames_iter_no_tty(socket)\n\n\ndef frames_iter_no_tty(socket):\n    \"\"\"\n    Returns a generator of data read from the socket when the tty setting is\n    not enabled.\n    \"\"\"\n    while True:\n        (stream, n) = next_frame_header(socket)\n        if n < 0:\n            break\n        while n > 0:\n            result = read(socket, n)\n            if result is None:\n                continue\n            data_length = len(result)\n            if data_length == 0:\n                # We have reached EOF\n                return\n            n -= data_length\n            yield (stream, result)\n\n\ndef frames_iter_tty(socket):\n    \"\"\"\n    Return a generator of data read from the socket when the tty setting is\n    enabled.\n    \"\"\"\n    while True:\n        result = read(socket)\n        if len(result) == 0:\n            # We have reached EOF\n            return\n        yield result\n\n\ndef consume_socket_output(frames, demux=False):\n    \"\"\"\n    Iterate through frames read from the socket and return the result.\n\n    Args:\n\n        demux (bool):\n            If False, stdout and stderr are multiplexed, and the result is the\n            concatenation of all the frames. If True, the streams are\n            demultiplexed, and the result is a 2-tuple where each item is the\n            concatenation of frames belonging to the same stream.\n    \"\"\"\n    if demux is False:\n        # If the streams are multiplexed, the generator returns strings, that\n        # we just need to concatenate.\n        return bytes().join(frames)\n\n    # If the streams are demultiplexed, the generator yields tuples\n    # (stdout, stderr)\n    out = [None, None]\n    for frame in frames:\n        # It is guaranteed that for each frame, one and only one stream\n        # is not None.\n        assert frame != (None, None)\n        if frame[0] is not None:\n            if out[0] is None:\n                out[0] = frame[0]\n            else:\n                out[0] += frame[0]\n        else:\n            if out[1] is None:\n                out[1] = frame[1]\n            else:\n                out[1] += frame[1]\n    return tuple(out)\n\n\ndef demux_adaptor(stream_id, data):\n    \"\"\"\n    Utility to demultiplex stdout and stderr when reading frames from the\n    socket.\n    \"\"\"\n    if stream_id == STDOUT:\n        return (data, None)\n    elif stream_id == STDERR:\n        return (None, data)\n    else:\n        raise ValueError(f'{stream_id} is not a valid stream')\n", "path": "docker/utils/socket.py"}], "after_files": [{"content": "import errno\nimport os\nimport select\nimport socket as pysocket\nimport struct\n\ntry:\n    from ..transport import NpipeSocket\nexcept ImportError:\n    NpipeSocket = type(None)\n\n\nSTDOUT = 1\nSTDERR = 2\n\n\nclass SocketError(Exception):\n    pass\n\n\n# NpipeSockets have their own error types\n# pywintypes.error: (109, 'ReadFile', 'The pipe has been ended.')\nNPIPE_ENDED = 109\n\n\ndef read(socket, n=4096):\n    \"\"\"\n    Reads at most n bytes from socket\n    \"\"\"\n\n    recoverable_errors = (errno.EINTR, errno.EDEADLK, errno.EWOULDBLOCK)\n\n    if not isinstance(socket, NpipeSocket):\n        select.select([socket], [], [])\n\n    try:\n        if hasattr(socket, 'recv'):\n            return socket.recv(n)\n        if isinstance(socket, getattr(pysocket, 'SocketIO')):\n            return socket.read(n)\n        return os.read(socket.fileno(), n)\n    except OSError as e:\n        if e.errno not in recoverable_errors:\n            raise\n    except Exception as e:\n        is_pipe_ended = (isinstance(socket, NpipeSocket) and\n                         len(e.args) > 0 and\n                         e.args[0] == NPIPE_ENDED)\n        if is_pipe_ended:\n            # npipes don't support duplex sockets, so we interpret\n            # a PIPE_ENDED error as a close operation (0-length read).\n            return ''\n        raise\n\n\ndef read_exactly(socket, n):\n    \"\"\"\n    Reads exactly n bytes from socket\n    Raises SocketError if there isn't enough data\n    \"\"\"\n    data = bytes()\n    while len(data) < n:\n        next_data = read(socket, n - len(data))\n        if not next_data:\n            raise SocketError(\"Unexpected EOF\")\n        data += next_data\n    return data\n\n\ndef next_frame_header(socket):\n    \"\"\"\n    Returns the stream and size of the next frame of data waiting to be read\n    from socket, according to the protocol defined here:\n\n    https://docs.docker.com/engine/api/v1.24/#attach-to-a-container\n    \"\"\"\n    try:\n        data = read_exactly(socket, 8)\n    except SocketError:\n        return (-1, -1)\n\n    stream, actual = struct.unpack('>BxxxL', data)\n    return (stream, actual)\n\n\ndef frames_iter(socket, tty):\n    \"\"\"\n    Return a generator of frames read from socket. A frame is a tuple where\n    the first item is the stream number and the second item is a chunk of data.\n\n    If the tty setting is enabled, the streams are multiplexed into the stdout\n    stream.\n    \"\"\"\n    if tty:\n        return ((STDOUT, frame) for frame in frames_iter_tty(socket))\n    else:\n        return frames_iter_no_tty(socket)\n\n\ndef frames_iter_no_tty(socket):\n    \"\"\"\n    Returns a generator of data read from the socket when the tty setting is\n    not enabled.\n    \"\"\"\n    while True:\n        (stream, n) = next_frame_header(socket)\n        if n < 0:\n            break\n        while n > 0:\n            result = read(socket, n)\n            if result is None:\n                continue\n            data_length = len(result)\n            if data_length == 0:\n                # We have reached EOF\n                return\n            n -= data_length\n            yield (stream, result)\n\n\ndef frames_iter_tty(socket):\n    \"\"\"\n    Return a generator of data read from the socket when the tty setting is\n    enabled.\n    \"\"\"\n    while True:\n        result = read(socket)\n        if len(result) == 0:\n            # We have reached EOF\n            return\n        yield result\n\n\ndef consume_socket_output(frames, demux=False):\n    \"\"\"\n    Iterate through frames read from the socket and return the result.\n\n    Args:\n\n        demux (bool):\n            If False, stdout and stderr are multiplexed, and the result is the\n            concatenation of all the frames. If True, the streams are\n            demultiplexed, and the result is a 2-tuple where each item is the\n            concatenation of frames belonging to the same stream.\n    \"\"\"\n    if demux is False:\n        # If the streams are multiplexed, the generator returns strings, that\n        # we just need to concatenate.\n        return bytes().join(frames)\n\n    # If the streams are demultiplexed, the generator yields tuples\n    # (stdout, stderr)\n    out = [None, None]\n    for frame in frames:\n        # It is guaranteed that for each frame, one and only one stream\n        # is not None.\n        assert frame != (None, None)\n        if frame[0] is not None:\n            if out[0] is None:\n                out[0] = frame[0]\n            else:\n                out[0] += frame[0]\n        else:\n            if out[1] is None:\n                out[1] = frame[1]\n            else:\n                out[1] += frame[1]\n    return tuple(out)\n\n\ndef demux_adaptor(stream_id, data):\n    \"\"\"\n    Utility to demultiplex stdout and stderr when reading frames from the\n    socket.\n    \"\"\"\n    if stream_id == STDOUT:\n        return (data, None)\n    elif stream_id == STDERR:\n        return (None, data)\n    else:\n        raise ValueError(f'{stream_id} is not a valid stream')\n", "path": "docker/utils/socket.py"}]}