internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: docker/docker-py

Issue:
Can't use multiple of same host path in binds
Reference for issue is docker/compose#983

The `convert_volume_binds` is using the dict key to get the host path to bind.
Because of this, it is impossible to do the equivalent of `docker run -v /foo:/bar -v /foo:baz`.



Discussion:

Resolving patch:
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -174,6 +174,9 @@ def convert_port_bindings(port_bindings):
 
 
 def convert_volume_binds(binds):
+    if isinstance(binds, list):
+        return binds
+
     result = []
     for k, v in binds.items():
         if isinstance(v, dict):


Test patch:
diff --git a/tests/test.py b/tests/test.py
--- a/tests/test.py
+++ b/tests/test.py
@@ -808,6 +808,36 @@ def test_create_container_with_binds_rw(self):
             DEFAULT_TIMEOUT_SECONDS
         )
 
+    def test_create_container_with_binds_list(self):
+        try:
+            self.client.create_container(
+                'busybox', 'true', host_config=create_host_config(
+                    binds=[
+                        "/tmp:/mnt/1:ro",
+                        "/tmp:/mnt/2",
+                    ],
+                )
+            )
+        except Exception as e:
+            self.fail('Command should not raise exception: {0}'.format(e))
+
+        args = fake_request.call_args
+        self.assertEqual(args[0][0], url_prefix +
+                         'containers/create')
+        expected_payload = self.base_create_payload()
+        expected_payload['HostConfig'] = create_host_config()
+        expected_payload['HostConfig']['Binds'] = [
+            "/tmp:/mnt/1:ro",
+            "/tmp:/mnt/2",
+        ]
+        self.assertEqual(json.loads(args[1]['data']), expected_payload)
+        self.assertEqual(args[1]['headers'],
+                         {'Content-Type': 'application/json'})
+        self.assertEqual(
+            args[1]['timeout'],
+            DEFAULT_TIMEOUT_SECONDS
+        )
+
     def test_create_container_with_port_binds(self):
         self.maxDiff = None
         try:
Repository: docker/docker-py

Issue:
'None' timeout breaks with Client.stop
Hi,

We are wotking on a script to automate some stuff using docker and docker-py in some point we execute some code inside docker, this command can take from a few minutes to a 5 or 6 hours.

I realized that I could do something like this:

``` python
container_name = 'container_name_or_id'
cli = Client(timeout=None)
output = cli.execute(container_name, 'ls')
```

And works as expected, but then the container have to be stopped and started (here I just put the specific code for a PoC):

``` python
from docker import Client

container_name = 'container_name_or_id'
cli = Client(timeout=None)

# Here goes a command that take a lot of time, not just a ls
output = cli.execute(container_name, 'ls')
cli.stop(container_name)
# Do some more stuff
cli.start(container_name)
```

But we get this:

```
Traceback (most recent call last):
  File "test_resume.py", line 48, in <module>
    cli.stop(container_name)
  File "/home/truiz/deployer/venv/lib/python2.7/site-packages/docker/client.py", line 923, in stop
    timeout=(timeout + self.timeout))
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
```

Is there any other way of setting an "infinite" timeout? or just no timeout for this long runnig scripts?

Regards



Discussion:
Hm, that's definitely a bug. Thank you for reporting it.


Resolving patch:
diff --git a/docker/client.py b/docker/client.py
--- a/docker/client.py
+++ b/docker/client.py
@@ -1101,7 +1101,7 @@ def stop(self, container, timeout=10):
         url = self._url("/containers/{0}/stop".format(container))
 
         res = self._post(url, params=params,
-                         timeout=(timeout + self.timeout))
+                         timeout=(timeout + (self.timeout or 0)))
         self._raise_for_status(res)
 
     @check_resource


Test patch:
diff --git a/tests/integration_test.py b/tests/integration_test.py
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -1467,6 +1467,11 @@ def test_542(self):
         result = self.client.containers(all=True, trunc=True)
         self.assertEqual(len(result[0]['Id']), 12)
 
+    def test_649(self):
+        self.client.timeout = None
+        ctnr = self.client.create_container('busybox', ['sleep', '2'])
+        self.client.start(ctnr)
+        self.client.stop(ctnr)
 
 if __name__ == '__main__':
     c = docker.Client(base_url=DEFAULT_BASE_URL)
Repository: docker/docker-py

Issue:
Build does not accept ssh git repository paths
When using docker build directly, like this:

```
 docker build -t image git@gitlab.example.com:admin/docker-repo.git
```

It works. But using the same url with docker-py, I get this:

```
File "/usr/local/lib/python2.7/dist-packages/docker/client.py", line 332, in build
              raise TypeError("You must specify a directory to build in path")
          TypeError: You must specify a directory to build in path
```

I use docker-py 1.2.2 with docker 1.6.2.
(I don't use docker-py directly but via salt 2015.5.0, if that helps)



Discussion:

Resolving patch:
diff --git a/docker/client.py b/docker/client.py
--- a/docker/client.py
+++ b/docker/client.py
@@ -317,7 +317,7 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
         elif fileobj is not None:
             context = utils.mkbuildcontext(fileobj)
         elif path.startswith(('http://', 'https://',
-                              'git://', 'github.com/')):
+                              'git://', 'github.com/', 'git@')):
             remote = path
         elif not os.path.isdir(path):
             raise TypeError("You must specify a directory to build in path")


Test patch:
Repository: docker/docker-py

Issue:
client.py - exec_create - broken API
Missing 'Id' extraction from a container's dictionary representation:

Line #296 :
        `url = self._url('/containers/{0}/exec'.format(container))`

should add the following above it:

```
    if isinstance(container, dict):
        container = container.get('Id')
```



Discussion:

Resolving patch:
diff --git a/docker/client.py b/docker/client.py
--- a/docker/client.py
+++ b/docker/client.py
@@ -273,6 +273,7 @@ def events(self, since=None, until=None, filters=None, decode=None):
             decode=decode
         )
 
+    @check_resource
     def exec_create(self, container, cmd, stdout=True, stderr=True, tty=False,
                     privileged=False):
         if utils.compare_version('1.15', self._version) < 0:


Test patch:
Repository: docker/docker-py

Issue:
Slightly incorrect documentation for user parameter in create_container?
The documentation for `user` parameter in `create_container` says: 

`user (str or int): Username or UID`

However, supplying it as python's int(`client.create_container(user=1000, ...)`) gives 
`docker.errors.APIError: 500 Server Error: Internal Server Error ("json: cannot unmarshal number into Go value of type string")`

Supplying it as string works. So this code `client.create_container(user="1000", ...)` works fine. 

I guess it is a minor issue, though only because Celery printed the exception as "500 Server Error: Internal Server Error", so the critical piece of information was missing(`json: cannot unmarshal number into Go value of type string`). So I guess it is a minor issue, but I figured I should report it. 



Discussion:
Sounds like we should stringify the value you pass in.


Resolving patch:
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -715,7 +715,7 @@ def create_container_config(
         'Hostname': hostname,
         'Domainname': domainname,
         'ExposedPorts': ports,
-        'User': user,
+        'User': six.text_type(user) if user else None,
         'Tty': tty,
         'OpenStdin': stdin_open,
         'StdinOnce': stdin_once,


Test patch:
diff --git a/tests/integration_test.py b/tests/integration_test.py
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -1624,3 +1624,9 @@ def test_649(self):
         ctnr = self.client.create_container('busybox', ['sleep', '2'])
         self.client.start(ctnr)
         self.client.stop(ctnr)
+
+    def test_715(self):
+        ctnr = self.client.create_container('busybox', ['id', '-u'], user=1000)
+        self.client.start(ctnr)
+        self.client.wait(ctnr)
+        assert self.client.logs(ctnr) == '1000\n'
Repository: docker/docker-py

Issue:
Auth fails with long passwords
See https://github.com/docker/docker/issues/16840

docker-py is encoding `X-Registry-Auth` with regular base64 and not the url safe version of base64 that jwt tokens use.



Discussion:
Thanks for the report! It's not related to JWT however, see https://github.com/docker/docker/blob/master/api/server/router/local/image.go#L172-L179

I'll submit a patch.


Resolving patch:
diff --git a/docker/auth/auth.py b/docker/auth/auth.py
--- a/docker/auth/auth.py
+++ b/docker/auth/auth.py
@@ -102,7 +102,7 @@ def decode_auth(auth):
 
 def encode_header(auth):
     auth_json = json.dumps(auth).encode('ascii')
-    return base64.b64encode(auth_json)
+    return base64.urlsafe_b64encode(auth_json)
 
 
 def parse_auth(entries):


Test patch:
diff --git a/tests/utils_test.py b/tests/utils_test.py
--- a/tests/utils_test.py
+++ b/tests/utils_test.py
@@ -19,7 +19,9 @@
     exclude_paths, convert_volume_binds, decode_json_header
 )
 from docker.utils.ports import build_port_bindings, split_port
-from docker.auth import resolve_repository_name, resolve_authconfig
+from docker.auth import (
+    resolve_repository_name, resolve_authconfig, encode_header
+)
 
 from . import base
 from .helpers import make_tree
@@ -376,12 +378,21 @@ def test_decode_json_header(self):
         obj = {'a': 'b', 'c': 1}
         data = None
         if six.PY3:
-            data = base64.b64encode(bytes(json.dumps(obj), 'utf-8'))
+            data = base64.urlsafe_b64encode(bytes(json.dumps(obj), 'utf-8'))
         else:
-            data = base64.b64encode(json.dumps(obj))
+            data = base64.urlsafe_b64encode(json.dumps(obj))
         decoded_data = decode_json_header(data)
         self.assertEqual(obj, decoded_data)
 
+    def test_803_urlsafe_encode(self):
+        auth_data = {
+            'username': 'root',
+            'password': 'GR?XGR?XGR?XGR?X'
+        }
+        encoded = encode_header(auth_data)
+        assert b'/' not in encoded
+        assert b'_' in encoded
+
     def test_resolve_repository_name(self):
         # docker hub library image
         self.assertEqual(
Repository: docker/docker-py

Issue:
POST /volumes is now POST /volumes/create
https://github.com/docker/docker/pull/17136



Discussion:

Resolving patch:
diff --git a/docker/api/volume.py b/docker/api/volume.py
--- a/docker/api/volume.py
+++ b/docker/api/volume.py
@@ -12,7 +12,7 @@ def volumes(self, filters=None):
 
     @utils.minimum_version('1.21')
     def create_volume(self, name, driver=None, driver_opts=None):
-        url = self._url('/volumes')
+        url = self._url('/volumes/create')
         if driver_opts is not None and not isinstance(driver_opts, dict):
             raise TypeError('driver_opts must be a dictionary')
 


Test patch:
diff --git a/tests/fake_api.py b/tests/fake_api.py
--- a/tests/fake_api.py
+++ b/tests/fake_api.py
@@ -529,7 +529,7 @@ def fake_remove_volume():
     get_fake_events,
     ('{1}/{0}/volumes'.format(CURRENT_VERSION, prefix), 'GET'):
     get_fake_volume_list,
-    ('{1}/{0}/volumes'.format(CURRENT_VERSION, prefix), 'POST'):
+    ('{1}/{0}/volumes/create'.format(CURRENT_VERSION, prefix), 'POST'):
     get_fake_volume,
     ('{1}/{0}/volumes/{2}'.format(
         CURRENT_VERSION, prefix, FAKE_VOLUME_NAME
diff --git a/tests/test.py b/tests/test.py
--- a/tests/test.py
+++ b/tests/test.py
@@ -2208,7 +2208,7 @@ def test_create_volume(self):
         args = fake_request.call_args
 
         self.assertEqual(args[0][0], 'POST')
-        self.assertEqual(args[0][1], url_prefix + 'volumes')
+        self.assertEqual(args[0][1], url_prefix + 'volumes/create')
         self.assertEqual(json.loads(args[1]['data']), {'Name': name})
 
     @base.requires_api_version('1.21')
@@ -2219,7 +2219,7 @@ def test_create_volume_with_driver(self):
         args = fake_request.call_args
 
         self.assertEqual(args[0][0], 'POST')
-        self.assertEqual(args[0][1], url_prefix + 'volumes')
+        self.assertEqual(args[0][1], url_prefix + 'volumes/create')
         data = json.loads(args[1]['data'])
         self.assertIn('Driver', data)
         self.assertEqual(data['Driver'], driver_name)
Repository: docker/docker-py

Issue:
decode_auth function does not handle utf-8 logins or password
HI

I have found that the function **decode_auth** (line 96, [file](https://github.com/docker/docker-py/blob/master/docker/auth/auth.py)) fails when decoding UTF-8 passwords from the .dockercfg file, and **load_config** returning an empty config.

I have checked and docker hub can handle UTF-8 passwords, this code proves that:

``` python
# coding=utf-8
from docker import Client
cred = { 'username': <user>, 'password': <utf-8 password> }
c = Client(base_url='unix://var/run/docker.sock')
res = c.pull(repository='<private container>', tag='latest', auth_config=cred)
print(res)
```

Thank you



Discussion:

Resolving patch:
diff --git a/docker/auth/auth.py b/docker/auth/auth.py
--- a/docker/auth/auth.py
+++ b/docker/auth/auth.py
@@ -96,7 +96,7 @@ def decode_auth(auth):
         auth = auth.encode('ascii')
     s = base64.b64decode(auth)
     login, pwd = s.split(b':', 1)
-    return login.decode('ascii'), pwd.decode('ascii')
+    return login.decode('utf8'), pwd.decode('utf8')
 
 
 def encode_header(auth):


Test patch:
diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py
--- a/tests/unit/auth_test.py
+++ b/tests/unit/auth_test.py
@@ -316,3 +316,33 @@ def test_load_config_custom_config_env_with_auths(self):
             self.assertEqual(cfg['password'], 'izayoi')
             self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
             self.assertEqual(cfg.get('auth'), None)
+
+    def test_load_config_custom_config_env_utf8(self):
+        folder = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, folder)
+
+        dockercfg_path = os.path.join(folder, 'config.json')
+        registry = 'https://your.private.registry.io'
+        auth_ = base64.b64encode(
+            b'sakuya\xc3\xa6:izayoi\xc3\xa6').decode('ascii')
+        config = {
+            'auths': {
+                registry: {
+                    'auth': '{0}'.format(auth_),
+                    'email': 'sakuya@scarlet.net'
+                }
+            }
+        }
+
+        with open(dockercfg_path, 'w') as f:
+            json.dump(config, f)
+
+        with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
+            cfg = auth.load_config(None)
+            assert registry in cfg
+            self.assertNotEqual(cfg[registry], None)
+            cfg = cfg[registry]
+            self.assertEqual(cfg['username'], b'sakuya\xc3\xa6'.decode('utf8'))
+            self.assertEqual(cfg['password'], b'izayoi\xc3\xa6'.decode('utf8'))
+            self.assertEqual(cfg['email'], 'sakuya@scarlet.net')
+            self.assertEqual(cfg.get('auth'), None)
Repository: docker/docker-py

Issue:
Probably invalid version checking in container creation
Hi.
I've stumbled upon something i think is a not up-to-date code.

utils.py at line 794 in my distribution (1.6.0) states following:

```
if compare_version('1.10', version) >= 0:
    message = ('{0!r} parameter has no effect on create_container().'
               ' It has been moved to start()')
    if dns is not None:
        raise errors.InvalidVersion(message.format('dns'))
    if volumes_from is not None:
        raise errors.InvalidVersion(message.format('volumes_from'))
```

As far as i can read from docs, this is not true - both Dns and VolumesFrom are seen in HostConfig for API 1.21. Even if everything is okay with this code, there is still a problem i'm struggling to solve - some of the HostConfig options can be specified in `.container_create()`, but not on `.start()`, e.g. logging driver and options may be set only during creation, but if i need to use custom DNS, i have to specify it on `.start()` call, and this will override whole HostConfig, erasing my logging configuration. Since this code directly raise exceptions, i have no option but to create whole dict directly and pass it to `container_from_configuration()`.

I'm sorry i'm not doing direct pull request instead of issue, but i don't have enough time to figure out correct version constraints and get into projects conventions - sadly, i'm no pythonist and can't do it.



Discussion:
Yeah, the error message is obsolete - `dns` and `volumes_from` should be provided in the [host config](https://docker-py.readthedocs.org/en/latest/hostconfig/) - thanks for bringing it up.


Resolving patch:
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -889,7 +889,7 @@ def create_container_config(
 
     if compare_version('1.10', version) >= 0:
         message = ('{0!r} parameter has no effect on create_container().'
-                   ' It has been moved to start()')
+                   ' It has been moved to host_config')
         if dns is not None:
             raise errors.InvalidVersion(message.format('dns'))
         if volumes_from is not None:


Test patch:
Repository: docker/docker-py

Issue:
Support environment variables without values
See https://github.com/docker/compose/pull/2997 for some context.

docker-py assumes that all variables should have an `=` but the docker-cli passes environment variables without values as just the key, which is super weird, but apparently swarm is relying on that currently.



Discussion:

Resolving patch:
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -821,6 +821,14 @@ def split_command(command):
     return shlex.split(command)
 
 
+def format_environment(environment):
+    def format_env(key, value):
+        if not value:
+            return key
+        return '{key}={value}'.format(key=key, value=value)
+    return [format_env(*var) for var in six.iteritems(environment)]
+
+
 def create_container_config(
     version, image, command, hostname=None, user=None, detach=False,
     stdin_open=False, tty=False, mem_limit=None, ports=None, environment=None,
@@ -836,10 +844,7 @@ def create_container_config(
         entrypoint = split_command(entrypoint)
 
     if isinstance(environment, dict):
-        environment = [
-            six.text_type('{0}={1}').format(k, v)
-            for k, v in six.iteritems(environment)
-        ]
+        environment = format_environment(environment)
 
     if labels is not None and compare_version('1.18', version) < 0:
         raise errors.InvalidVersion(


Test patch:
diff --git a/tests/integration/container_test.py b/tests/integration/container_test.py
--- a/tests/integration/container_test.py
+++ b/tests/integration/container_test.py
@@ -370,6 +370,16 @@ def test_create_host_config_exception_raising(self):
         self.assertRaises(ValueError,
                           self.client.create_host_config, pid_mode='40')
 
+    def test_create_with_environment_variable_no_value(self):
+        container = self.client.create_container(
+            BUSYBOX,
+            ['echo'],
+            environment={'Foo': None, 'Other': 'one'},
+        )
+        self.tmp_containers.append(container['Id'])
+        config = self.client.inspect_container(container['Id'])
+        assert sorted(config['Config']['Env']) == sorted(['Foo', 'Other=one'])
+
 
 class VolumeBindTest(helpers.BaseTestCase):
     def setUp(self):
Repository: docker/docker-py

Issue:
Empty auth dictionary should be valid
docker/compose#3265



Discussion:

Resolving patch:
diff --git a/docker/auth/auth.py b/docker/auth/auth.py
--- a/docker/auth/auth.py
+++ b/docker/auth/auth.py
@@ -117,7 +117,7 @@ def parse_auth(entries, raise_on_error=False):
 
     conf = {}
     for registry, entry in six.iteritems(entries):
-        if not (isinstance(entry, dict) and 'auth' in entry):
+        if not isinstance(entry, dict):
             log.debug(
                 'Config entry for key {0} is not auth config'.format(registry)
             )
@@ -130,6 +130,16 @@ def parse_auth(entries, raise_on_error=False):
                     'Invalid configuration for registry {0}'.format(registry)
                 )
             return {}
+        if 'auth' not in entry:
+            # Starting with engine v1.11 (API 1.23), an empty dictionary is
+            # a valid value in the auths config.
+            # https://github.com/docker/compose/issues/3265
+            log.debug(
+                'Auth data for {0} is absent. Client might be using a '
+                'credentials store instead.'
+            )
+            return {}
+
         username, password = decode_auth(entry['auth'])
         log.debug(
             'Found entry (registry={0}, username={1})'
@@ -189,6 +199,9 @@ def load_config(config_path=None):
             if data.get('HttpHeaders'):
                 log.debug("Found 'HttpHeaders' section")
                 res.update({'HttpHeaders': data['HttpHeaders']})
+            if data.get('credsStore'):
+                log.debug("Found 'credsStore' section")
+                res.update({'credsStore': data['credsStore']})
             if res:
                 return res
             else:


Test patch:
diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py
--- a/tests/unit/auth_test.py
+++ b/tests/unit/auth_test.py
@@ -459,6 +459,5 @@ def test_load_config_invalid_auth_dict(self):
         with open(dockercfg_path, 'w') as f:
             json.dump(config, f)
 
-        self.assertRaises(
-            errors.InvalidConfigFile, auth.load_config, dockercfg_path
-        )
+        cfg = auth.load_config(dockercfg_path)
+        assert cfg == {}
Repository: docker/docker-py

Issue:
support PidsLimit in host config



Discussion:

Resolving patch:
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -620,7 +620,7 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
                        device_write_bps=None, device_read_iops=None,
                        device_write_iops=None, oom_kill_disable=False,
                        shm_size=None, version=None, tmpfs=None,
-                       oom_score_adj=None):
+                       oom_score_adj=None, pids_limit=None,):
 
     host_config = {}
 
@@ -853,6 +853,13 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
             raise host_config_version_error('tmpfs', '1.22')
         host_config["Tmpfs"] = convert_tmpfs_mounts(tmpfs)
 
+    if pids_limit:
+        if not isinstance(pids_limit, int):
+            raise host_config_type_error('pids_limit', pids_limit, 'int')
+        if version_lt(version, '1.23'):
+            raise host_config_version_error('pids_limit', '1.23')
+        host_config["PidsLimit"] = pids_limit
+
     return host_config
 
 


Test patch: