Repository: docker/docker-py

Issue:
support PidsLimit in host config

Discussion:

Patch:
diff --git a/docker/client.py b/docker/client.py
index d1c6ee5f..75867536 100644
--- a/docker/client.py
+++ b/docker/client.py
@@ -268,7 +268,7 @@ class Client(
         else:
             # Response isn't chunked, meaning we probably
             # encountered an error immediately
-            yield self._result(response, json=decode)
+            yield self._result(response)
 
     def _multiplexed_buffer_helper(self, response):
         """A generator of multiplexed data blocks read from a buffered
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index a5fbe0ba..bc6c7a64 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -623,7 +623,7 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
                        device_write_iops=None, oom_kill_disable=False,
                        shm_size=None, sysctls=None, version=None, tmpfs=None,
                        oom_score_adj=None, dns_opt=None, cpu_shares=None,
-                       cpuset_cpus=None, userns_mode=None):
+                       cpuset_cpus=None, userns_mode=None, pids_limit=None):
 
     host_config = {}
 
@@ -904,6 +904,13 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
             raise host_config_value_error("userns_mode", userns_mode)
         host_config['UsernsMode'] = userns_mode
 
+    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
 
 
diff --git a/docs/hostconfig.md b/docs/hostconfig.md
index 6645bd1f..008d5cf2 100644
--- a/docs/hostconfig.md
+++ b/docs/hostconfig.md
@@ -111,11 +111,12 @@ for example:
   CPU period.
 * cpu_shares (int): CPU shares (relative weight)
 * cpuset_cpus (str): CPUs in which to allow execution (0-3, 0,1)
-* blkio_weight: Block IO weight (relative weight), accepts a weight value between 10 and 1000.
+* blkio_weight: Block IO weight (relative weight), accepts a weight value
+  between 10 and 1000.
 * blkio_weight_device: Block IO weight (relative device weight) in the form of:
   `[{"Path": "device_path", "Weight": weight}]`
-* device_read_bps: Limit read rate (bytes per second) from a device in the form of:
-  `[{"Path": "device_path", "Rate": rate}]`
+* device_read_bps: Limit read rate (bytes per second) from a device in the
+  form of: `[{"Path": "device_path", "Rate": rate}]`
 * device_write_bps: Limit write rate (bytes per second) from a device.
 * device_read_iops: Limit read rate (IO per second) from a device.
 * device_write_iops: Limit write rate (IO per second) from a device.
@@ -128,6 +129,7 @@ for example:
 * sysctls (dict): Kernel parameters to set in the container.
 * userns_mode (str): Sets the user namespace mode for the container when user
   namespace remapping option is enabled. Supported values are: `host`
+* pids_limit (int): Tune a container’s pids limit. Set -1 for unlimited.
 
 **Returns** (dict) HostConfig dictionary
 


Test patch:
diff --git a/tests/unit/api_test.py b/tests/unit/api_test.py
index 5850afa2..712f57e0 100644
--- a/tests/unit/api_test.py
+++ b/tests/unit/api_test.py
@@ -22,11 +22,9 @@ import sys
 import tempfile
 import threading
 import time
-import io
 
 import docker
 import requests
-from requests.packages import urllib3
 import six
 
 from .. import base
@@ -44,7 +42,7 @@ DEFAULT_TIMEOUT_SECONDS = docker.constants.DEFAULT_TIMEOUT_SECONDS
 
 
 def response(status_code=200, content='', headers=None, reason=None, elapsed=0,
-             request=None, raw=None):
+             request=None):
     res = requests.Response()
     res.status_code = status_code
     if not isinstance(content, six.binary_type):
@@ -54,7 +52,6 @@ def response(status_code=200, content='', headers=None, reason=None, elapsed=0,
     res.reason = reason
     res.elapsed = datetime.timedelta(elapsed)
     res.request = request
-    res.raw = raw
     return res
 
 
@@ -331,43 +328,6 @@ class DockerApiTest(DockerClientTest):
             TypeError, self.client.create_host_config, security_opt='wrong'
         )
 
-    def test_stream_helper_decoding(self):
-        status_code, content = fake_api.fake_responses[url_prefix + 'events']()
-        content_str = json.dumps(content)
-        if six.PY3:
-            content_str = content_str.encode('utf-8')
-        body = io.BytesIO(content_str)
-
-        # mock a stream interface
-        raw_resp = urllib3.HTTPResponse(body=body)
-        setattr(raw_resp._fp, 'chunked', True)
-        setattr(raw_resp._fp, 'chunk_left', len(body.getvalue())-1)
-
-        # pass `decode=False` to the helper
-        raw_resp._fp.seek(0)
-        resp = response(status_code=status_code, content=content, raw=raw_resp)
-        result = next(self.client._stream_helper(resp))
-        self.assertEqual(result, content_str)
-
-        # pass `decode=True` to the helper
-        raw_resp._fp.seek(0)
-        resp = response(status_code=status_code, content=content, raw=raw_resp)
-        result = next(self.client._stream_helper(resp, decode=True))
-        self.assertEqual(result, content)
-
-        # non-chunked response, pass `decode=False` to the helper
-        setattr(raw_resp._fp, 'chunked', False)
-        raw_resp._fp.seek(0)
-        resp = response(status_code=status_code, content=content, raw=raw_resp)
-        result = next(self.client._stream_helper(resp))
-        self.assertEqual(result, content_str.decode('utf-8'))
-
-        # non-chunked response, pass `decode=True` to the helper
-        raw_resp._fp.seek(0)
-        resp = response(status_code=status_code, content=content, raw=raw_resp)
-        result = next(self.client._stream_helper(resp, decode=True))
-        self.assertEqual(result, content)
-
 
 class StreamTest(base.Cleanup, base.BaseTestCase):
     def setUp(self):
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index 3476f041..2a2759d0 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -185,6 +185,15 @@ class HostConfigTest(base.BaseTestCase):
             InvalidVersion, lambda: create_host_config(
                 version='1.20', kernel_memory=67108864))
 
+    def test_create_host_config_with_pids_limit(self):
+        config = create_host_config(version='1.23', pids_limit=1024)
+        self.assertEqual(config.get('PidsLimit'), 1024)
+
+        with pytest.raises(InvalidVersion):
+            create_host_config(version='1.22', pids_limit=1024)
+        with pytest.raises(TypeError):
+            create_host_config(version='1.22', pids_limit='1024')
+
 
 class UlimitTest(base.BaseTestCase):
     def test_create_host_config_dict_ulimit(self):