Repository: docker/docker-py

Issue:
exec_create not work with detach=True
Ubuntu 14.04, Python 2.7.12

docker-py 1.5.0 - Ok

```
In [28]: import docker

In [29]: docker.version
Out[29]: '1.5.0'

In [30]: cli = docker.Client()

In [31]: container = cli.create_container('python:2.7.11', command='sleep 1h')

In [32]: cli.start(container['Id'])

In [33]: e = cli.exec_create(container['Id'], 'echo "123"')

In [34]: cli.exec_start(e['Id'], detach=True)
Out[34]: ''
```

docker 1.10.4 - Not working

```
In [9]: import docker

In [10]: docker.version
Out[10]: '1.10.4'

In [11]: cli = docker.Client()

In [12]: container = cli.create_container('python:2.7.11', command='sleep 1h')

In [13]: cli.start(container['Id'])

In [14]: e = cli.exec_create(container['Id'], 'echo "123"')

In [15]: cli.exec_start(e['Id'], detach=True)
^C---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-15-d3ef5f6326d6> in <module>()
----> 1 cli.exec_start(e['Id'], detach=True)

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/utils/decorators.pyc in wrapper(self, *args, **kwargs)
     33                     )
     34                 )
---> 35             return f(self, *args, **kwargs)
     36         return wrapper
     37     return decorator

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/api/exec_api.pyc in exec_start(self, exec_id, detach, tty, stream, socket)
     79         if socket:
     80             return self._get_raw_response_socket(res)
---> 81         return self._read_from_socket(res, stream)

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/client.pyc in _read_from_socket(self, response, stream)
    324             return frames_iter(socket)
    325         else:
--> 326             return six.binary_type().join(frames_iter(socket))
    327 
    328     def _disable_socket_timeout(self, socket):

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/utils/socket.pyc in frames_iter(socket)
     70     Returns a generator of frames read from socket
     71     """
---> 72     n = next_frame_size(socket)
     73     while n > 0:
     74         yield read(socket, n)

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/utils/socket.pyc in next_frame_size(socket)
     58     """
     59     try:
---> 60         data = read_exactly(socket, 8)
     61     except SocketError:
     62         return 0

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/utils/socket.pyc in read_exactly(socket, n)
     43     data = six.binary_type()
     44     while len(data) < n:
---> 45         next_data = read(socket, n - len(data))
     46         if not next_data:
     47             raise SocketError("Unexpected EOF")

/usr/local/pyenv/versions/2.7.12/lib/python2.7/site-packages/docker/utils/socket.pyc in read(socket, n)
     25     # wait for data to become available
     26     if not isinstance(socket, NpipeSocket):
---> 27         select.select([socket], [], [])
     28 
     29     try:

KeyboardInterrupt: 

```



Discussion:
good catch; fix incoming


Resolving patch:
diff --git a/docker/api/exec_api.py b/docker/api/exec_api.py
--- a/docker/api/exec_api.py
+++ b/docker/api/exec_api.py
@@ -137,7 +137,8 @@ def exec_start(self, exec_id, detach=False, tty=False, stream=False,
             data=data,
             stream=True
         )
-
+        if detach:
+            return self._result(res)
         if socket:
             return self._get_raw_response_socket(res)
         return self._read_from_socket(res, stream)


Test patch:
diff --git a/tests/integration/api_exec_test.py b/tests/integration/api_exec_test.py
--- a/tests/integration/api_exec_test.py
+++ b/tests/integration/api_exec_test.py
@@ -93,6 +93,21 @@ def test_exec_start_socket(self):
         data = read_exactly(socket, next_size)
         self.assertEqual(data.decode('utf-8'), line)
 
+    def test_exec_start_detached(self):
+        container = self.client.create_container(BUSYBOX, 'cat',
+                                                 detach=True, stdin_open=True)
+        container_id = container['Id']
+        self.client.start(container_id)
+        self.tmp_containers.append(container_id)
+
+        exec_id = self.client.exec_create(
+            container_id, ['printf', "asdqwe"])
+        self.assertIn('Id', exec_id)
+
+        response = self.client.exec_start(exec_id, detach=True)
+
+        self.assertEqual(response, "")
+
     def test_exec_inspect(self):
         container = self.client.create_container(BUSYBOX, 'cat',
                                                  detach=True, stdin_open=True)