Repository: docker/docker-py

Issue:
attach fails with no _sock attribute
### System details
```
python --version
Python 2.7.13

pip freeze && python --version && docker version
backports.ssl-match-hostname==3.5.0.1
certifi==2017.7.27.1
chardet==3.0.4
click==6.7
docker==2.5.0
docker-pycreds==0.2.1
Flask==0.12.2
idna==2.6
ipaddress==1.0.18
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
requests==2.18.4
six==1.10.0
urllib3==1.22
websocket-client==0.44.0
Werkzeug==0.12.2
Python 2.7.13
Client:
 Version:      17.03.1-ce
 API version:  1.27
 Go version:   go1.8.3
 Git commit:   c6d412e
 Built:        Tue Jun 13 13:05:42 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.03.1-ce
 API version:  1.27 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   c6d412e
 Built:        Tue Jun 13 13:05:42 2017
 OS/Arch:      linux/amd64
 Experimental: false
```

### Reproducer  
```
import docker
import pdb

class Test(docker.APIClient):
    def __init__(self, containerid="867cb24efc3490357904b10e56e40e5621ab18157a9cd2665380a94a46db4759",
            port=0, tag=None, name=None,
            archived=False, image=None, cport=4873):
        self.containerid = containerid
        super(Test, self).__init__(base_url='unix://var/run/docker.sock')
        self.exec_start(
                self.exec_create(self.containerid,
                    cmd="sh -c '/bin/chown -R nobody /verdaccio/storage " +
                    "&& /bin/chgrp -R nogroup /verdaccio/storage "+
                    "&& /bin/chmod -R ugo+rws /verdaccio/storage'")
                )
        at=self.attach(self.containerid, logs=True)

t = Test()
```

### Stack trace
```
Traceback (most recent call last):
  File "reproducer.py", line 18, in <module>
    t = Test()
  File "reproducer.py", line 16, in __init__
    at=self.attach(self.containerid, logs=True)
  File "/home/jonny/Src/foo/foo/npmvirt/lib/python2.7/site-packages/docker/utils/decorators.py", line 19, in wrapped
    return f(self, resource_id, *args, **kwargs)
  File "/home/jonny/Src/foo/foo/npmvirt/lib/python2.7/site-packages/docker/api/container.py", line 56, in attach
    response, stream, self._check_is_tty(container)
  File "/home/jonny/Src/foo/foo/npmvirt/lib/python2.7/site-packages/docker/api/client.py", line 366, in _read_from_socket
    socket = self._get_raw_response_socket(response)
  File "/home/jonny/Src/foo/foo/npmvirt/lib/python2.7/site-packages/docker/api/client.py", line 278, in _get_raw_response_socket
    sock = response.raw._fp.fp._sock
AttributeError: 'NoneType' object has no attribute '_sock'
```


Discussion:
I reduced the reproducer further the exec command executed previous is irrelevant 

```
import docker
import pdb

class Test(docker.APIClient):
    def __init__(self, containerid="867cb24efc3490357904b10e56e40e5621ab18157a9cd2665380a94a46db4759",
            port=0, tag=None, name=None,
            archived=False, image=None, cport=4873):
        self.containerid = containerid
        super(Test, self).__init__(base_url='unix://var/run/docker.sock')
        at=self.attach(self.containerid, logs=True)

t = Test()
```
I'm guessing there is something with my setup because even basic usage fails with the same error -
```
import docker
import pdb

cid = "867cb24efc3490357904b10e56e40e5621ab18157a9cd2665380a94a46db4759"
class Test(docker.APIClient):
    def __init__(self, containerid=cid):
        self.containerid = containerid
        super(Test, self).__init__(base_url='unix://var/run/docker.sock')
        at=self.attach(self.containerid)

#Simple test
client = docker.from_env()
ca = client.containers.get(cid)
ca.attach()

#With inheritance
t = Test()
```
 
@jonathanchristison Does it work if you downgrade the `docker` python package to 2.4.2?
Also can you share the result of `docker inspect` on your test container?
@shin- thanks for the suggestions, downgrading unfortunately does not work and the docker container being attached to seems not to be relevant, it happens with both latest alpine and centos images, started with `docker run -it alpine`  

```
import docker

cid="2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028" #centos

print docker.__version__
#Simple test
client = docker.from_env()
ca = client.containers.get(cid)
ca.attach()
```

### Output 
```
python reproducer.py 
2.4.2
Traceback (most recent call last):
  File "reproducer.py", line 9, in <module>
    ca.attach()
  File "/home/jonny/Src/JBoss/jboss-prod/npmvirt/lib/python2.7/site-packages/docker/models/containers.py", line 70, in attach
    return self.client.api.attach(self.id, **kwargs)
  File "/home/jonny/Src/JBoss/jboss-prod/npmvirt/lib/python2.7/site-packages/docker/utils/decorators.py", line 19, in wrapped
    return f(self, resource_id, *args, **kwargs)
  File "/home/jonny/Src/JBoss/jboss-prod/npmvirt/lib/python2.7/site-packages/docker/api/container.py", line 55, in attach
    return self._read_from_socket(response, stream)
  File "/home/jonny/Src/JBoss/jboss-prod/npmvirt/lib/python2.7/site-packages/docker/api/client.py", line 366, in _read_from_socket
    socket = self._get_raw_response_socket(response)
  File "/home/jonny/Src/JBoss/jboss-prod/npmvirt/lib/python2.7/site-packages/docker/api/client.py", line 278, in _get_raw_response_socket
    sock = response.raw._fp.fp._sock
```
### Docker inspect output
```
docker inspect 2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028
[
    {
        "Id": "2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028",
        "Created": "2017-08-19T19:18:03.704144478Z",
        "Path": "/bin/bash",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 29442,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2017-08-19T19:18:04.350601839Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:328edcd84f1bbf868bc88e4ae37afe421ef19be71890f59b4b2d8ba48414b84d",
        "ResolvConfPath": "/var/lib/docker/containers/2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028/hostname",
        "HostsPath": "/var/lib/docker/containers/2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028/hosts",
        "LogPath": "/var/lib/docker/containers/2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028/2412ca14994c4e28eaa2a9afe8608e151a03d1e383ba831502d4a07450eb8028-json.log",
        "Name": "/competent_poitras",
        "RestartCount": 0,
        "Driver": "devicemapper",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": -1,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0
        },
        "GraphDriver": {
            "Data": {
                "DeviceId": "1354",
                "DeviceName": "docker-253:1-67446110-80ebf34456fa7c34607f8d8f741dcc46394dd496ec866f3afb568148892d25f5",
                "DeviceSize": "10737418240"
            },
            "Name": "devicemapper"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "2412ca14994c",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "ArgsEscaped": true,
            "Image": "centos",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "build-date": "20170801",
                "license": "GPLv2",
                "name": "CentOS Base Image",
                "vendor": "CentOS"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "5986a62f03576171df6d479cacc90c1ff111f01c0915fa362fd2b4e2bf775660",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/5986a62f0357",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "e90d66213d6fa33582c95678a3c39063707e042802221481ead7fc25a85ac194",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.4",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:04",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "504ebd5a2b07b7a869b9dc3e36c6092a1721238d9a7bca8f274ac5963df8cce2",
                    "EndpointID": "e90d66213d6fa33582c95678a3c39063707e042802221481ead7fc25a85ac194",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:04",
                    "DriverOpts": null
                }
            }
        }
    }
]
```

I'll do a bit more digging on Monday and also try and reproduce this on another system, its likely to be something odd with my setup if you've not seen this before. 
I'm able to reproduce this on another system with an older version of docker, docker-py & python (RHEL7), so maybe the problem has been there for a while but no one has used attach with python 2.7 and docker-py >=2.0.0 ? 
```
python --version && docker version
Python 2.7.5
Client:
 Version:         1.10.3
 API version:     1.22
 Package version: docker-common-1.10.3-46.el7.10.x86_64
 Go version:      go1.6.2
 Git commit:      2a93377-unsupported
 Built:           Fri Jul 29 13:45:25 2016
 OS/Arch:         linux/amd64

Server:
 Version:         1.10.3
 API version:     1.22
 Package version: docker-common-1.10.3-46.el7.10.x86_64
 Go version:      go1.6.2
 Git commit:      2a93377-unsupported
 Built:           Fri Jul 29 13:45:25 2016
 OS/Arch:         linux/amd64

python -c "import docker; print docker.__version__"
2.0.0
```
Ok i think I figured why this is happening, I call attach without `stream=True` which means the requests request object will not create the raw socket object which response.raw._fp.fp._sock expects - 

see https://github.com/requests/requests/blob/205755834d34a8a6ecf2b0b5b2e9c3e6a7f4e4b6/requests/models.py#L602

So maybe `def attach` needs to change to `stream=True` as default and `_read_from_socket` and `_get_raw_response_socket` altered to handle request objects without the raw socket data
@jonathanchristison Thank you for the update, I'll look into tackling this in 2.5.1

Resolving patch:
diff --git a/docker/api/container.py b/docker/api/container.py
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -50,7 +50,7 @@ def attach(self, container, stdout=True, stderr=True,
         }
 
         u = self._url("/containers/{0}/attach", container)
-        response = self._post(u, headers=headers, params=params, stream=stream)
+        response = self._post(u, headers=headers, params=params, stream=True)
 
         return self._read_from_socket(
             response, stream, self._check_is_tty(container)


Test patch:
diff --git a/tests/integration/api_container_test.py b/tests/integration/api_container_test.py
--- a/tests/integration/api_container_test.py
+++ b/tests/integration/api_container_test.py
@@ -1092,20 +1092,28 @@ def test_run_container_reading_socket(self):
         command = "printf '{0}'".format(line)
         container = self.client.create_container(BUSYBOX, command,
                                                  detach=True, tty=False)
-        ident = container['Id']
-        self.tmp_containers.append(ident)
+        self.tmp_containers.append(container)
 
         opts = {"stdout": 1, "stream": 1, "logs": 1}
-        pty_stdout = self.client.attach_socket(ident, opts)
+        pty_stdout = self.client.attach_socket(container, opts)
         self.addCleanup(pty_stdout.close)
 
-        self.client.start(ident)
+        self.client.start(container)
 
         next_size = next_frame_size(pty_stdout)
         self.assertEqual(next_size, len(line))
         data = read_exactly(pty_stdout, next_size)
         self.assertEqual(data.decode('utf-8'), line)
 
+    def test_attach_no_stream(self):
+        container = self.client.create_container(
+            BUSYBOX, 'echo hello'
+        )
+        self.tmp_containers.append(container)
+        self.client.start(container)
+        output = self.client.attach(container, stream=False, logs=True)
+        assert output == 'hello\n'.encode(encoding='ascii')
+
 
 class PauseTest(BaseAPIIntegrationTest):
     def test_pause_unpause(self):