From 9987c1bc42bc36160bc7fbdf17849a32fcf11808 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 15:05:22 -0800 Subject: [PATCH 01/11] Fix docs examples to work with Python 3 Signed-off-by: Joffrey F --- docker/api/daemon.py | 6 ++--- docker/api/image.py | 16 ++++++------ docker/api/mixin.py | 57 ++++++++++++++++++++++++++++++++++++++++++ docker/types/daemon.py | 2 +- 4 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 docker/api/mixin.py diff --git a/docker/api/daemon.py b/docker/api/daemon.py index 76a94cf034..431e7d41cd 100644 --- a/docker/api/daemon.py +++ b/docker/api/daemon.py @@ -42,8 +42,8 @@ def events(self, since=None, until=None, filters=None, decode=None): Example: - >>> for event in client.events() - ... print event + >>> for event in client.events(decode=True) + ... print(event) {u'from': u'image/with:tag', u'id': u'container-id', u'status': u'start', @@ -54,7 +54,7 @@ def events(self, since=None, until=None, filters=None, decode=None): >>> events = client.events() >>> for event in events: - ... print event + ... print(event) >>> # and cancel from another thread >>> events.close() """ diff --git a/docker/api/image.py b/docker/api/image.py index 5f05d8877e..1c1fcde84c 100644 --- a/docker/api/image.py +++ b/docker/api/image.py @@ -352,8 +352,8 @@ def pull(self, repository, tag=None, stream=False, auth_config=None, Example: - >>> for line in cli.pull('busybox', stream=True): - ... print(json.dumps(json.loads(line), indent=4)) + >>> for line in cli.pull('busybox', stream=True, decode=True): + ... print(json.dumps(line, indent=4)) { "status": "Pulling image (latest) from busybox", "progressDetail": {}, @@ -428,12 +428,12 @@ def push(self, repository, tag=None, stream=False, auth_config=None, If the server returns an error. Example: - >>> for line in cli.push('yourname/app', stream=True): - ... print line - {"status":"Pushing repository yourname/app (1 tags)"} - {"status":"Pushing","progressDetail":{},"id":"511136ea3c5a"} - {"status":"Image already pushed, skipping","progressDetail":{}, - "id":"511136ea3c5a"} + >>> for line in cli.push('yourname/app', stream=True, decode=True): + ... print(line) + {'status': 'Pushing repository yourname/app (1 tags)'} + {'status': 'Pushing','progressDetail': {}, 'id': '511136ea3c5a'} + {'status': 'Image already pushed, skipping', 'progressDetail':{}, + 'id': '511136ea3c5a'} ... """ diff --git a/docker/api/mixin.py b/docker/api/mixin.py new file mode 100644 index 0000000000..1353568167 --- /dev/null +++ b/docker/api/mixin.py @@ -0,0 +1,57 @@ +from typing import Any, Dict, Iterable, List, Optional, Union + +import requests + +from ..constants import DEFAULT_DOCKER_API_VERSION, DEFAULT_TIMEOUT_SECONDS + +class BaseMixin(object): + base_url: str = '' + credstore_env: Optional[Dict[str, str]] = None + timeout: int = DEFAULT_TIMEOUT_SECONDS + _auth_configs: Dict[str, Dict] + _general_configs: Dict[str, Dict] + _version: str = DEFAULT_DOCKER_API_VERSION + + def _url(self, pathfmt: str, *args, **kwargs) -> str: + raise NotImplemented + + def _post(self, url: str, **kwargs) -> requests.Response: + raise NotImplemented + + def _get(self, url: str, **kwargs) -> requests.Response: + raise NotImplemented + + def _put(self, url: str, **kwargs) -> requests.Response: + raise NotImplemented + + def _delete(self, url: str, **kwargs) -> requests.Response: + raise NotImplemented + + def _post_json(self, url: str, data: Optional[Union[Dict[str, Any], List[Any]]], **kwargs) -> requests.Response: + raise NotImplemented + + def _raise_for_status(self, response: requests.Response) -> None: + raise NotImplemented + + def _result(self, response: requests.Response, json: bool=False, binary: bool=False) -> Any: + raise NotImplemented + + def _stream_helper(self, response: requests.Response, decode: bool = False) -> Iterable: + raise NotImplemented + + def _get_raw_response_socket(self, response: requests.Response) -> Iterable: + raise NotImplemented + + def _read_from_socket( + self, + response: requests.Response, + stream: bool, + tty: bool = False) -> Union[Iterable[bytes], bytes]: + raise NotImplemented + + def _stream_raw_result( + self, + response: requests.Response, + chunk_size: int = 1, + decode: bool = True) -> Iterable[bytes]: + raise NotImplemented diff --git a/docker/types/daemon.py b/docker/types/daemon.py index 700f9a90c4..af3e5bcb5e 100644 --- a/docker/types/daemon.py +++ b/docker/types/daemon.py @@ -15,7 +15,7 @@ class CancellableStream(object): Example: >>> events = client.events() >>> for event in events: - ... print event + ... print(event) >>> # and cancel from another thread >>> events.close() """ From 1d124a1262c73a85ff6601e8fdef9c7e48b33543 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 15:32:10 -0800 Subject: [PATCH 02/11] Improve ulimits documentation Signed-off-by: Joffrey F --- docker/api/container.py | 2 +- docker/models/containers.py | 4 ++-- docker/types/containers.py | 17 +++++++++++++++++ docs/api.rst | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docker/api/container.py b/docker/api/container.py index c59a6d01a5..6967a13acf 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -543,7 +543,7 @@ def create_host_config(self, *args, **kwargs): } ulimits (:py:class:`list`): Ulimits to set inside the container, - as a list of dicts. + as a list of :py:class:`docker.types.Ulimit` instances. userns_mode (str): Sets the user namespace mode for the container when user namespace remapping option is enabled. Supported values are: ``host`` diff --git a/docker/models/containers.py b/docker/models/containers.py index f60ba6e225..98c717421f 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -691,8 +691,8 @@ def run(self, image, command=None, stdout=True, stderr=False, } tty (bool): Allocate a pseudo-TTY. - ulimits (:py:class:`list`): Ulimits to set inside the container, as - a list of dicts. + ulimits (:py:class:`list`): Ulimits to set inside the container, + as a list of :py:class:`docker.types.Ulimit` instances. user (str or int): Username or UID to run commands as inside the container. userns_mode (str): Sets the user namespace mode for the container diff --git a/docker/types/containers.py b/docker/types/containers.py index 9dfea8ceb8..13eb4ef37b 100644 --- a/docker/types/containers.py +++ b/docker/types/containers.py @@ -58,6 +58,23 @@ def unset_config(self, key): class Ulimit(DictType): + """ + Create a ulimit declaration to be used with + :py:meth:`~docker.api.container.ContainerApiMixin.create_host_config`. + + Args: + + name (str): Which ulimit will this apply to. A list of valid names can + be found `here `_. + soft (int): The soft limit for this ulimit. Optional. + hard (int): The hard limit for this ulimit. Optional. + + Example: + + nproc_limit = docker.types.Ulimit(name='nproc', soft=1024) + hc = client.create_host_config(ulimits=[nproc_limit]) + container = client.create_container('busybox', 'true', host_config=hc) + """ def __init__(self, **kwargs): name = kwargs.get('name', kwargs.get('Name')) soft = kwargs.get('soft', kwargs.get('Soft')) diff --git a/docs/api.rst b/docs/api.rst index 6931245716..2c2391a803 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -151,4 +151,5 @@ Configuration types .. autoclass:: SwarmExternalCA .. autoclass:: SwarmSpec(*args, **kwargs) .. autoclass:: TaskTemplate +.. autoclass:: Ulimit .. autoclass:: UpdateConfig From d5bc46ad456ca7f8c008baa31f6623d4f58ccdcd Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 16:20:28 -0800 Subject: [PATCH 03/11] Improved LogConfig documentation Signed-off-by: Joffrey F --- docker/api/container.py | 8 +------ docker/models/containers.py | 8 +------ docker/types/containers.py | 45 ++++++++++++++++++++++++++++++++++--- docs/api.rst | 1 + 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/docker/api/container.py b/docker/api/container.py index 6967a13acf..39478329ae 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -476,13 +476,7 @@ def create_host_config(self, *args, **kwargs): isolation (str): Isolation technology to use. Default: `None`. links (dict or list of tuples): Either a dictionary mapping name to alias or as a list of ``(name, alias)`` tuples. - log_config (dict): Logging configuration, as a dictionary with - keys: - - - ``type`` The logging driver name. - - ``config`` A dictionary of configuration for the logging - driver. - + log_config (LogConfig): Logging configuration lxc_conf (dict): LXC config. mem_limit (float or str): Memory limit. Accepts float values (which represent the memory limit of the created container in diff --git a/docker/models/containers.py b/docker/models/containers.py index 98c717421f..4cd7d13f41 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -576,13 +576,7 @@ def run(self, image, command=None, stdout=True, stderr=False, ``["label1", "label2"]``) links (dict or list of tuples): Either a dictionary mapping name to alias or as a list of ``(name, alias)`` tuples. - log_config (dict): Logging configuration, as a dictionary with - keys: - - - ``type`` The logging driver name. - - ``config`` A dictionary of configuration for the logging - driver. - + log_config (LogConfig): Logging configuration. mac_address (str): MAC address to assign to the container. mem_limit (int or str): Memory limit. Accepts float values (which represent the memory limit of the created container in diff --git a/docker/types/containers.py b/docker/types/containers.py index 13eb4ef37b..d040c0fb5e 100644 --- a/docker/types/containers.py +++ b/docker/types/containers.py @@ -23,6 +23,36 @@ class LogConfigTypesEnum(object): class LogConfig(DictType): + """ + Configure logging for a container, when provided as an argument to + :py:meth:`~docker.api.container.ContainerApiMixin.create_host_config`. + You may refer to the + `official logging driver documentation `_ + for more information. + + Args: + type (str): Indicate which log driver to use. A set of valid drivers + is provided as part of the :py:attr:`LogConfig.types` + enum. Other values may be accepted depending on the engine version + and available logging plugins. + config (dict): A driver-dependent configuration dictionary. Please + refer to the driver's documentation for a list of valid config + keys. + + Example: + + >>> from docker.types import LogConfig + >>> lc = LogConfig(type=LogConfig.types.JSON, config={ + ... 'max-size': '1g', + ... 'labels': 'production_status,geo' + ... }) + >>> hc = client.create_host_config(log_config=lc) + >>> container = client.create_container('busybox', 'true', + ... host_config=hc) + >>> client.inspect_container(container)['HostConfig']['LogConfig'] + {'Type': 'json-file', 'Config': {'labels': 'production_status,geo', 'max-size': '1g'}} + + """ # flake8: noqa types = LogConfigTypesEnum def __init__(self, **kwargs): @@ -50,9 +80,13 @@ def config(self): return self['Config'] def set_config_value(self, key, value): + """ Set a the value for ``key`` to ``value`` inside the ``config`` + dict. + """ self.config[key] = value def unset_config(self, key): + """ Remove the ``key`` property from the ``config`` dict. """ if key in self.config: del self.config[key] @@ -71,9 +105,14 @@ class Ulimit(DictType): Example: - nproc_limit = docker.types.Ulimit(name='nproc', soft=1024) - hc = client.create_host_config(ulimits=[nproc_limit]) - container = client.create_container('busybox', 'true', host_config=hc) + >>> nproc_limit = docker.types.Ulimit(name='nproc', soft=1024) + >>> hc = client.create_host_config(ulimits=[nproc_limit]) + >>> container = client.create_container( + 'busybox', 'true', host_config=hc + ) + >>> client.inspect_container(container)['HostConfig']['Ulimits'] + [{'Name': 'nproc', 'Hard': 0, 'Soft': 1024}] + """ def __init__(self, **kwargs): name = kwargs.get('name', kwargs.get('Name')) diff --git a/docs/api.rst b/docs/api.rst index 2c2391a803..1682128951 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -140,6 +140,7 @@ Configuration types .. autoclass:: Healthcheck .. autoclass:: IPAMConfig .. autoclass:: IPAMPool +.. autoclass:: LogConfig .. autoclass:: Mount .. autoclass:: Placement .. autoclass:: Privileges From 6064947431dd36b8aa33a5f2eb850b03302de5ee Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 16:52:32 -0800 Subject: [PATCH 04/11] Update links docs and fix bug in normalize_links Signed-off-by: Joffrey F --- docker/api/container.py | 17 ++++++++++------- docker/models/containers.py | 6 ++++-- docker/utils/utils.py | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/docker/api/container.py b/docker/api/container.py index 39478329ae..8858aa68a3 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -473,9 +473,11 @@ def create_host_config(self, *args, **kwargs): signals and reaps processes init_path (str): Path to the docker-init binary ipc_mode (str): Set the IPC mode for the container. - isolation (str): Isolation technology to use. Default: `None`. - links (dict or list of tuples): Either a dictionary mapping name - to alias or as a list of ``(name, alias)`` tuples. + isolation (str): Isolation technology to use. Default: ``None``. + links (dict): Mapping of links using the + ``{'container': 'alias'}`` format. The alias is optional. + Containers declared in this dict will be linked to the new + container using the provided alias. Default: ``None``. log_config (LogConfig): Logging configuration lxc_conf (dict): LXC config. mem_limit (float or str): Memory limit. Accepts float values @@ -605,9 +607,10 @@ def create_endpoint_config(self, *args, **kwargs): aliases (:py:class:`list`): A list of aliases for this endpoint. Names in that list can be used within the network to reach the container. Defaults to ``None``. - links (:py:class:`list`): A list of links for this endpoint. - Containers declared in this list will be linked to this - container. Defaults to ``None``. + links (dict): Mapping of links for this endpoint using the + ``{'container': 'alias'}`` format. The alias is optional. + Containers declared in this dict will be linked to this + container using the provided alias. Defaults to ``None``. ipv4_address (str): The IP address of this container on the network, using the IPv4 protocol. Defaults to ``None``. ipv6_address (str): The IP address of this container on the @@ -622,7 +625,7 @@ def create_endpoint_config(self, *args, **kwargs): >>> endpoint_config = client.create_endpoint_config( aliases=['web', 'app'], - links=['app_db'], + links={'app_db': 'db', 'another': None}, ipv4_address='132.65.0.123' ) diff --git a/docker/models/containers.py b/docker/models/containers.py index 4cd7d13f41..bba0395eed 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -574,8 +574,10 @@ def run(self, image, command=None, stdout=True, stderr=False, ``{"label1": "value1", "label2": "value2"}``) or a list of names of labels to set with empty values (e.g. ``["label1", "label2"]``) - links (dict or list of tuples): Either a dictionary mapping name - to alias or as a list of ``(name, alias)`` tuples. + links (dict): Mapping of links using the + ``{'container': 'alias'}`` format. The alias is optional. + Containers declared in this dict will be linked to the new + container using the provided alias. Default: ``None``. log_config (LogConfig): Logging configuration. mac_address (str): MAC address to assign to the container. mem_limit (int or str): Memory limit. Accepts float values diff --git a/docker/utils/utils.py b/docker/utils/utils.py index 4e04cafdb4..a8e814d7d5 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -441,7 +441,7 @@ def normalize_links(links): if isinstance(links, dict): links = six.iteritems(links) - return ['{0}:{1}'.format(k, v) for k, v in sorted(links)] + return ['{0}:{1}'.format(k, v) if v else k for k, v in sorted(links)] def parse_env_file(env_file): From 6bfe4c90906b3ff737b256825b5059c893a6a4d1 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:05:42 -0800 Subject: [PATCH 05/11] Document attr caching for Container objects Signed-off-by: Joffrey F --- docker/models/containers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/models/containers.py b/docker/models/containers.py index bba0395eed..a3fd1a8edf 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -15,7 +15,12 @@ class Container(Model): - + """ Local representation of a container object. Detailed configuration may + be accessed through the :py:attr:`attrs` attribute. Note that local + attributes are cached; users may call :py:meth:`reload` to + query the Docker daemon for the current properties, causing + :py:attr:`attrs` to be refreshed. + """ @property def name(self): """ From b927a5f62cf2d66209244129ed3434575c4045f5 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:08:41 -0800 Subject: [PATCH 06/11] Fix incorrect return info for inspect_service Signed-off-by: Joffrey F --- docker/api/service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/api/service.py b/docker/api/service.py index 8b956b63e1..08e2591730 100644 --- a/docker/api/service.py +++ b/docker/api/service.py @@ -197,7 +197,8 @@ def inspect_service(self, service, insert_defaults=None): into the service inspect output. Returns: - ``True`` if successful. + (dict): A dictionary of the server-side representation of the + service, including all relevant properties. Raises: :py:class:`docker.errors.APIError` From 89ee08f511d8a0882d5b034fc5be670f8987a802 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:13:19 -0800 Subject: [PATCH 07/11] Disallow incompatible combination stats(decode=True, stream=False) Signed-off-by: Joffrey F --- docker/api/container.py | 7 ++++++- docker/models/containers.py | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docker/api/container.py b/docker/api/container.py index 8858aa68a3..753e0a57fe 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -1071,7 +1071,8 @@ def stats(self, container, decode=None, stream=True): Args: container (str): The container to stream statistics from decode (bool): If set to true, stream will be decoded into dicts - on the fly. False by default. + on the fly. Only applicable if ``stream`` is True. + False by default. stream (bool): If set to false, only the current stats will be returned instead of a stream. True by default. @@ -1085,6 +1086,10 @@ def stats(self, container, decode=None, stream=True): return self._stream_helper(self._get(url, stream=True), decode=decode) else: + if decode: + raise errors.InvalidArgument( + "decode is only available in conjuction with stream=True" + ) return self._result(self._get(url, params={'stream': False}), json=True) diff --git a/docker/models/containers.py b/docker/models/containers.py index a3fd1a8edf..493b9fc732 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -385,7 +385,8 @@ def stats(self, **kwargs): Args: decode (bool): If set to true, stream will be decoded into dicts - on the fly. False by default. + on the fly. Only applicable if ``stream`` is True. + False by default. stream (bool): If set to false, only the current stats will be returned instead of a stream. True by default. From f83fe7c9594e72cddf1d89031603c3d246c4c101 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:22:24 -0800 Subject: [PATCH 08/11] Properly convert non-string filters to expected string format Signed-off-by: Joffrey F --- docker/utils/utils.py | 5 ++++- tests/unit/utils_test.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docker/utils/utils.py b/docker/utils/utils.py index a8e814d7d5..61e307adc7 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -386,7 +386,10 @@ def convert_filters(filters): v = 'true' if v else 'false' if not isinstance(v, list): v = [v, ] - result[k] = v + result[k] = [ + str(item) if not isinstance(item, six.string_types) else item + for item in v + ] return json.dumps(result) diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py index c862a1cec9..a4e9c9c53e 100644 --- a/tests/unit/utils_test.py +++ b/tests/unit/utils_test.py @@ -457,8 +457,8 @@ def test_convert_filters(self): tests = [ ({'dangling': True}, '{"dangling": ["true"]}'), ({'dangling': "true"}, '{"dangling": ["true"]}'), - ({'exited': 0}, '{"exited": [0]}'), - ({'exited': [0, 1]}, '{"exited": [0, 1]}'), + ({'exited': 0}, '{"exited": ["0"]}'), + ({'exited': [0, 1]}, '{"exited": ["0", "1"]}'), ] for filters, expected in tests: From cebdee4aefd6c17eeb3542f1c53f1c461b95ea61 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:31:22 -0800 Subject: [PATCH 09/11] Add doc example for get_archive Signed-off-by: Joffrey F --- docker/api/container.py | 12 ++++++++++++ docker/models/containers.py | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/docker/api/container.py b/docker/api/container.py index 753e0a57fe..fce73af640 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -694,6 +694,18 @@ def get_archive(self, container, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE): Raises: :py:class:`docker.errors.APIError` If the server returns an error. + + Example: + + >>> c = docker.APIClient() + >>> f = open('./sh_bin.tar', 'wb') + >>> bits, stat = c.get_archive(container, '/bin/sh') + >>> print(stat) + {'name': 'sh', 'size': 1075464, 'mode': 493, + 'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''} + >>> for chunk in bits: + ... f.write(chunk) + >>> f.close() """ params = { 'path': path diff --git a/docker/models/containers.py b/docker/models/containers.py index 493b9fc732..9d6f2cc6af 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -233,6 +233,17 @@ def get_archive(self, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE): Raises: :py:class:`docker.errors.APIError` If the server returns an error. + + Example: + + >>> f = open('./sh_bin.tar', 'wb') + >>> bits, stat = container.get_archive('/bin/sh') + >>> print(stat) + {'name': 'sh', 'size': 1075464, 'mode': 493, + 'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''} + >>> for chunk in bits: + ... f.write(chunk) + >>> f.close() """ return self.client.api.get_archive(self.id, path, chunk_size) From 852d79b08d708ff25c1f8bee3b68ffb11ea98dc0 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:32:33 -0800 Subject: [PATCH 10/11] Fix file mode in image.save examples Signed-off-by: Joffrey F --- docker/api/image.py | 2 +- docker/models/images.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/api/image.py b/docker/api/image.py index 1c1fcde84c..a9f801e93b 100644 --- a/docker/api/image.py +++ b/docker/api/image.py @@ -32,7 +32,7 @@ def get_image(self, image, chunk_size=DEFAULT_DATA_CHUNK_SIZE): Example: >>> image = cli.get_image("busybox:latest") - >>> f = open('/tmp/busybox-latest.tar', 'w') + >>> f = open('/tmp/busybox-latest.tar', 'wb') >>> for chunk in image: >>> f.write(chunk) >>> f.close() diff --git a/docker/models/images.py b/docker/models/images.py index 28b1fd3ffd..4578c0bd89 100644 --- a/docker/models/images.py +++ b/docker/models/images.py @@ -84,7 +84,7 @@ def save(self, chunk_size=DEFAULT_DATA_CHUNK_SIZE, named=False): Example: >>> image = cli.get_image("busybox:latest") - >>> f = open('/tmp/busybox-latest.tar', 'w') + >>> f = open('/tmp/busybox-latest.tar', 'wb') >>> for chunk in image: >>> f.write(chunk) >>> f.close() From 35b9460748d792670d815496767fab8b396d4300 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 8 Nov 2018 17:38:59 -0800 Subject: [PATCH 11/11] Remove prematurely committed file Signed-off-by: Joffrey F --- docker/api/mixin.py | 57 --------------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 docker/api/mixin.py diff --git a/docker/api/mixin.py b/docker/api/mixin.py deleted file mode 100644 index 1353568167..0000000000 --- a/docker/api/mixin.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import Any, Dict, Iterable, List, Optional, Union - -import requests - -from ..constants import DEFAULT_DOCKER_API_VERSION, DEFAULT_TIMEOUT_SECONDS - -class BaseMixin(object): - base_url: str = '' - credstore_env: Optional[Dict[str, str]] = None - timeout: int = DEFAULT_TIMEOUT_SECONDS - _auth_configs: Dict[str, Dict] - _general_configs: Dict[str, Dict] - _version: str = DEFAULT_DOCKER_API_VERSION - - def _url(self, pathfmt: str, *args, **kwargs) -> str: - raise NotImplemented - - def _post(self, url: str, **kwargs) -> requests.Response: - raise NotImplemented - - def _get(self, url: str, **kwargs) -> requests.Response: - raise NotImplemented - - def _put(self, url: str, **kwargs) -> requests.Response: - raise NotImplemented - - def _delete(self, url: str, **kwargs) -> requests.Response: - raise NotImplemented - - def _post_json(self, url: str, data: Optional[Union[Dict[str, Any], List[Any]]], **kwargs) -> requests.Response: - raise NotImplemented - - def _raise_for_status(self, response: requests.Response) -> None: - raise NotImplemented - - def _result(self, response: requests.Response, json: bool=False, binary: bool=False) -> Any: - raise NotImplemented - - def _stream_helper(self, response: requests.Response, decode: bool = False) -> Iterable: - raise NotImplemented - - def _get_raw_response_socket(self, response: requests.Response) -> Iterable: - raise NotImplemented - - def _read_from_socket( - self, - response: requests.Response, - stream: bool, - tty: bool = False) -> Union[Iterable[bytes], bytes]: - raise NotImplemented - - def _stream_raw_result( - self, - response: requests.Response, - chunk_size: int = 1, - decode: bool = True) -> Iterable[bytes]: - raise NotImplemented