diff -Nru python-docker-1.8.0/.coveragerc python-docker-1.9.0/.coveragerc --- python-docker-1.8.0/.coveragerc 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/.coveragerc 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -[run] -branch = True -source = docker - -[report] -exclude_lines = - if __name__ == .__main__.: - -[html] -directory = html diff -Nru python-docker-1.8.0/debian/changelog python-docker-1.9.0/debian/changelog --- python-docker-1.8.0/debian/changelog 2016-04-11 15:11:40.000000000 +0000 +++ python-docker-1.9.0/debian/changelog 2017-03-23 09:12:17.000000000 +0000 @@ -1,10 +1,44 @@ -python-docker (1.8.0-0ubuntu1) xenial; urgency=medium +python-docker (1.9.0-1~16.04.1) xenial; urgency=medium - * Update to 1.8.0 upstream release. - - Dropped d/p/0002-Fix-Unix-adapter-bug-with-newer-versions-of-requests.patch - Applied upstream. + * Build for Xenial (LP: #1637223). + * Relax version of dependency on python-websocket. + * Relax version of dependency on python-backports.ssl-match-hostname. - -- Pierre-André MOREY Mon, 11 Apr 2016 17:11:12 +0200 + -- Michael Hudson-Doyle Thu, 23 Mar 2017 21:35:45 +1300 + +python-docker (1.9.0-1) unstable; urgency=medium + + [ Ondřej Nový ] + * New upstream release (Closes: #830172) + * Dropped + d/p/0002-Lower-Docker-API-version-to-1.20-for-Docker-1.8.3-co.patch + (Not needed anymore) + * Bump required version of python3-websocket to 0.32.0 + * Standards-Version is 3.9.8 now (no change) + * Fixed typo in description + + [ Andreas Henriksson ] + * autopkgtest: fix running of integration test + + [ Felipe Sateler ] + * Take over the package from the DPMT. Thanks for all the work! + - Update Vcs urls + * Switch to git-buildpackage for vcs + * python2: add ssl_match_hostname and ipaddress dependencies + * Restrict python3 package to python 3.5 + + -- Felipe Sateler Fri, 14 Oct 2016 21:15:59 -0300 + +python-docker (1.7.2-1) unstable; urgency=medium + + * Team upload. + * New upstream release (Closes: #816622). + * Standards-Version is 3.9.7 now (no change). + * Changed Vcs-Git URL to https. + * Added myself to d/copyright. + * Lower Docker API version to 1.20 for Docker 1.8.3 compatibility + + -- Ondřej Nový Wed, 23 Mar 2016 07:26:35 +0100 python-docker (1.5.0-1) unstable; urgency=medium diff -Nru python-docker-1.8.0/debian/control python-docker-1.9.0/debian/control --- python-docker-1.8.0/debian/control 2016-04-12 16:14:22.000000000 +0000 +++ python-docker-1.9.0/debian/control 2017-03-23 07:20:51.000000000 +0000 @@ -1,11 +1,8 @@ Source: python-docker Section: python Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian Python Modules Team -Uploaders: Paul Tagliamonte , - Docker Packaging Team , - Tianon Gravi +Maintainer: Felipe Sateler +Uploaders: Jason Pleau Build-Depends: debhelper (>= 9), dh-python, python-all (>= 2.6.6-3~), @@ -15,27 +12,29 @@ # requirements.txt python-requests (>= 2.5.3~), python-six (>= 1.4.0~), - python-websocket (>= 0.18.0~), + python-websocket, # requirements3.txt python3-requests (>= 2.5.3~), python3-six (>= 1.4.0~), - python3-websocket (>= 0.18.0~), + python3-websocket, # test-requirements.txt # python-mock (>= 1.0.1~), # python-coverage (>= 3.7.1~), # docker.io, -Standards-Version: 3.9.6 +Standards-Version: 3.9.8 Homepage: https://github.com/dotcloud/docker-py/ -Vcs-Git: git://anonscm.debian.org/python-modules/packages/python-docker.git -Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/python-docker.git +Vcs-Git: https://anonscm.debian.org/git/collab-maint/python-docker.git +Vcs-Browser: https://anonscm.debian.org/git/collab-maint/python-docker.git X-Python-Version: >= 2.6 -X-Python3-Version: >= 3.3 +X-Python3-Version: >= 3.5 Package: python-docker Architecture: all -Depends: ${misc:Depends}, ${python:Depends} +Depends: ${misc:Depends}, ${python:Depends}, + python-backports.ssl-match-hostname (>= 3.4), + python-ipaddress (>=1.0.16) Description: Python wrapper to access docker.io's control socket - This package contains oodles of routines that aid in controling + This package contains oodles of routines that aid in controlling docker.io over it's socket control, the same way the docker.io client controls the daemon. . @@ -45,7 +44,7 @@ Architecture: all Depends: ${misc:Depends}, ${python3:Depends} Description: Python 3 wrapper to access docker.io's control socket - This package contains oodles of routines that aid in controling + This package contains oodles of routines that aid in controlling docker.io over it's socket control, the same way the docker.io client controls the daemon. . diff -Nru python-docker-1.8.0/debian/copyright python-docker-1.9.0/debian/copyright --- python-docker-1.8.0/debian/copyright 2016-04-11 14:26:56.000000000 +0000 +++ python-docker-1.9.0/debian/copyright 2016-10-15 00:15:59.000000000 +0000 @@ -10,6 +10,7 @@ Files: debian/* Copyright: 2014 Paul R. Tagliamonte 2014 Tianon Gravi + 2016 Ondřej Nový License: Apache-2.0 License: Apache-2.0 diff -Nru python-docker-1.8.0/debian/gbp.conf python-docker-1.9.0/debian/gbp.conf --- python-docker-1.8.0/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/debian/gbp.conf 2016-10-15 00:15:59.000000000 +0000 @@ -0,0 +1,8 @@ +[DEFAULT] +pristine-tar = True +patch-numbers = False +debian-branch = master + +[dch] +full = True +multimaint-merge = True diff -Nru python-docker-1.8.0/debian/.git-dpm python-docker-1.9.0/debian/.git-dpm --- python-docker-1.8.0/debian/.git-dpm 2016-04-11 14:26:56.000000000 +0000 +++ python-docker-1.9.0/debian/.git-dpm 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -# see git-dpm(1) from git-dpm package -06f1e6fdcf6eb6a33f067d2b35ccc5c84cdbfaf5 -06f1e6fdcf6eb6a33f067d2b35ccc5c84cdbfaf5 -0db60a1e341b25c36bebb5903f0ce87126d7eee4 -0db60a1e341b25c36bebb5903f0ce87126d7eee4 -python-docker_1.5.0.orig.tar.gz -7361340e76f08ab6b9f6ab33c67fbe9f45dbeef4 -59883 -debianTag="debian/%e%v" -patchedTag="patched/%e%v" -upstreamTag="upstream/%e%u" diff -Nru python-docker-1.8.0/debian/patches/requirements.patch python-docker-1.9.0/debian/patches/requirements.patch --- python-docker-1.8.0/debian/patches/requirements.patch 2016-04-11 15:05:01.000000000 +0000 +++ python-docker-1.9.0/debian/patches/requirements.patch 2016-10-15 00:15:59.000000000 +0000 @@ -1,4 +1,3 @@ -From 45baca4b0272e2840e041d822315cf15f1343d49 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Sun, 8 Nov 2015 10:36:49 -0800 Subject: Unpin dependencies so newer versions satisfy them appropriately @@ -7,66 +6,36 @@ Patch-Name: requirements.patch --- - docker_py.egg-info/requires.txt | 4 ++-- - requirements.txt | 4 ++-- - setup.py | 4 ++-- - test-requirements.txt | 10 +++++----- - 4 files changed, 11 insertions(+), 11 deletions(-) + requirements.txt | 6 +++--- + test-requirements.txt | 10 +++++----- + 2 files changed, 8 insertions(+), 8 deletions(-) -Index: Ubuntu-1.8.0/docker_py.egg-info/requires.txt -=================================================================== ---- Ubuntu-1.8.0.orig/docker_py.egg-info/requires.txt -+++ Ubuntu-1.8.0/docker_py.egg-info/requires.txt -@@ -1,6 +1,6 @@ --requests >= 2.5.2 -+requests >= 2.5.3 - six >= 1.4.0 --websocket-client >= 0.32.0 -+websocket-client >= 0.18.0 - - [:python_version < "3"] - py2-ipaddress >= 3.4.1 -Index: Ubuntu-1.8.0/requirements.txt -=================================================================== ---- Ubuntu-1.8.0.orig/requirements.txt -+++ Ubuntu-1.8.0/requirements.txt -@@ -1,4 +1,4 @@ +diff --git a/requirements.txt b/requirements.txt +index a79b7bf..12b88f6 100644 +--- a/requirements.txt ++++ b/requirements.txt +@@ -1,5 +1,5 @@ -requests==2.5.3 +requests>=2.5.3 six>=1.4.0 -websocket-client==0.32.0 -+websocket-client>=0.18.0 - py2-ipaddress==3.4.1 ; python_version < '3.2' ++websocket-client>=0.32.0 + backports.ssl_match_hostname>=3.5 ; python_version < '3.5' +-ipaddress==1.0.16 ; python_version < '3.3' \ No newline at end of file -Index: Ubuntu-1.8.0/setup.py -=================================================================== ---- Ubuntu-1.8.0.orig/setup.py -+++ Ubuntu-1.8.0/setup.py -@@ -7,9 +7,9 @@ ROOT_DIR = os.path.dirname(__file__) - SOURCE_DIR = os.path.join(ROOT_DIR) - - requirements = [ -- 'requests >= 2.5.2', -+ 'requests >= 2.5.3', - 'six >= 1.4.0', -- 'websocket-client >= 0.32.0', -+ 'websocket-client >= 0.18.0', - ] - - extras_require = { -Index: Ubuntu-1.8.0/test-requirements.txt -=================================================================== ---- Ubuntu-1.8.0.orig/test-requirements.txt -+++ Ubuntu-1.8.0/test-requirements.txt ++ipaddress>=1.0.16 ; python_version < '3.3' +diff --git a/test-requirements.txt b/test-requirements.txt +index 460db10..9f9eb50 100644 +--- a/test-requirements.txt ++++ b/test-requirements.txt @@ -1,5 +1,5 @@ -mock==1.0.1 --pytest==2.7.2 +-pytest==2.9.1 -coverage==3.7.1 -pytest-cov==2.1.0 -flake8==2.4.1 -\ No newline at end of file +mock>=1.0.1 -+pytest>=2.7.2 ++pytest>=2.9.1 +coverage>=3.7.1 +pytest-cov>=2.1.0 +flake8>=2.4.1 diff -Nru python-docker-1.8.0/debian/tests/control python-docker-1.9.0/debian/tests/control --- python-docker-1.8.0/debian/tests/control 2016-04-11 14:26:56.000000000 +0000 +++ python-docker-1.9.0/debian/tests/control 2016-10-15 00:15:59.000000000 +0000 @@ -1,3 +1,3 @@ Tests: integration -Depends: docker.io, python-mock, python-pytest, python3-mock, python3-pytest, @ +Depends: docker.io, python-mock, python-pytest, python3-mock, python3-pytest, python-backports.ssl-match-hostname, @ Restrictions: isolation-machine needs-root diff -Nru python-docker-1.8.0/debian/tests/integration python-docker-1.9.0/debian/tests/integration --- python-docker-1.8.0/debian/tests/integration 2016-04-11 14:26:56.000000000 +0000 +++ python-docker-1.9.0/debian/tests/integration 2016-10-15 00:15:59.000000000 +0000 @@ -1,5 +1,5 @@ #!/bin/bash set -e -py.test-3 tests/integration_test.py -py.test tests/integration_test.py +py.test-3 tests/integration/ +py.test tests/integration/ diff -Nru python-docker-1.8.0/docker/api/container.py python-docker-1.9.0/docker/api/container.py --- python-docker-1.8.0/docker/api/container.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/api/container.py 2016-07-26 18:56:41.000000000 +0000 @@ -187,6 +187,8 @@ url = self._url("/containers/{0}/kill", container) params = {} if signal is not None: + if not isinstance(signal, six.string_types): + signal = int(signal) params['signal'] = signal res = self._post(url, params=params) diff -Nru python-docker-1.8.0/docker/api/daemon.py python-docker-1.9.0/docker/api/daemon.py --- python-docker-1.8.0/docker/api/daemon.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/api/daemon.py 2016-04-29 00:07:21.000000000 +0000 @@ -49,8 +49,6 @@ elif not self._auth_configs: self._auth_configs = auth.load_config() - registry = registry or auth.INDEX_URL - authcfg = auth.resolve_authconfig(self._auth_configs, registry) # If we found an existing auth config for this registry and username # combination, we can return it immediately unless reauth is requested. @@ -67,7 +65,7 @@ response = self._post_json(self._url('/auth'), data=req_data) if response.status_code == 200: - self._auth_configs[registry] = req_data + self._auth_configs[registry or auth.INDEX_NAME] = req_data return self._result(response, json=True) def ping(self): diff -Nru python-docker-1.8.0/docker/api/network.py python-docker-1.9.0/docker/api/network.py --- python-docker-1.8.0/docker/api/network.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/api/network.py 2016-07-26 18:56:41.000000000 +0000 @@ -1,7 +1,7 @@ import json from ..errors import InvalidVersion -from ..utils import check_resource, minimum_version, normalize_links +from ..utils import check_resource, minimum_version from ..utils import version_lt @@ -22,7 +22,7 @@ @minimum_version('1.21') def create_network(self, name, driver=None, options=None, ipam=None, - check_duplicate=None): + check_duplicate=None, internal=False): if options is not None and not isinstance(options, dict): raise TypeError('options must be a dictionary') @@ -33,6 +33,13 @@ 'IPAM': ipam, 'CheckDuplicate': check_duplicate } + + if internal: + if version_lt(self._version, '1.22'): + raise InvalidVersion('Internal networks are not ' + 'supported in API version < 1.22') + data['Internal'] = True + url = self._url("/networks/create") res = self._post_json(url, data=data) return self._result(res, json=True) @@ -56,26 +63,12 @@ aliases=None, links=None): data = { "Container": container, - "EndpointConfig": { - "Aliases": aliases, - "Links": normalize_links(links) if links else None, - }, + "EndpointConfig": self.create_endpoint_config( + aliases=aliases, links=links, ipv4_address=ipv4_address, + ipv6_address=ipv6_address + ), } - # IPv4 or IPv6 or neither: - if ipv4_address or ipv6_address: - if version_lt(self._version, '1.22'): - raise InvalidVersion('IP address assignment is not ' - 'supported in API version < 1.22') - - data['EndpointConfig']['IPAMConfig'] = dict() - if ipv4_address: - data['EndpointConfig']['IPAMConfig']['IPv4Address'] = \ - ipv4_address - if ipv6_address: - data['EndpointConfig']['IPAMConfig']['IPv6Address'] = \ - ipv6_address - url = self._url("/networks/{0}/connect", net_id) res = self._post_json(url, data=data) self._raise_for_status(res) diff -Nru python-docker-1.8.0/docker/client.py python-docker-1.9.0/docker/client.py --- python-docker-1.8.0/docker/client.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/client.py 2016-07-26 18:57:21.000000000 +0000 @@ -14,7 +14,6 @@ import json import struct -import sys import requests import requests.exceptions @@ -26,10 +25,14 @@ from . import constants from . import errors from .auth import auth -from .unixconn import unixconn from .ssladapter import ssladapter -from .utils import utils, check_resource, update_headers, kwargs_from_env from .tls import TLSConfig +from .transport import UnixAdapter +from .utils import utils, check_resource, update_headers, kwargs_from_env +try: + from .transport import NpipeAdapter +except ImportError: + pass def from_env(**kwargs): @@ -46,7 +49,8 @@ api.VolumeApiMixin, api.NetworkApiMixin): def __init__(self, base_url=None, version=None, - timeout=constants.DEFAULT_TIMEOUT_SECONDS, tls=False): + timeout=constants.DEFAULT_TIMEOUT_SECONDS, tls=False, + user_agent=constants.DEFAULT_USER_AGENT): super(Client, self).__init__() if tls and not base_url: @@ -56,14 +60,30 @@ self.base_url = base_url self.timeout = timeout + self.headers['User-Agent'] = user_agent self._auth_configs = auth.load_config() - base_url = utils.parse_host(base_url, sys.platform, tls=bool(tls)) + base_url = utils.parse_host( + base_url, constants.IS_WINDOWS_PLATFORM, tls=bool(tls) + ) if base_url.startswith('http+unix://'): - self._custom_adapter = unixconn.UnixAdapter(base_url, timeout) + self._custom_adapter = UnixAdapter(base_url, timeout) self.mount('http+docker://', self._custom_adapter) self.base_url = 'http+docker://localunixsocket' + elif base_url.startswith('npipe://'): + if not constants.IS_WINDOWS_PLATFORM: + raise errors.DockerException( + 'The npipe:// protocol is only supported on Windows' + ) + try: + self._custom_adapter = NpipeAdapter(base_url, timeout) + except NameError: + raise errors.DockerException( + 'Install pypiwin32 package to enable npipe:// support' + ) + self.mount('http+docker://', self._custom_adapter) + self.base_url = 'http+docker://localnpipe' else: # Use SSLAdapter for the ability to specify SSL version if isinstance(tls, TLSConfig): @@ -291,14 +311,29 @@ """ Depending on the combination of python version and whether we're connecting over http or https, we might need to access _sock, which may or may not exist; or we may need to just settimeout on socket - itself, which also may or may not have settimeout on it. + itself, which also may or may not have settimeout on it. To avoid + missing the correct one, we try both. - To avoid missing the correct one, we try both. + We also do not want to set the timeout if it is already disabled, as + you run the risk of changing a socket that was non-blocking to + blocking, for example when using gevent. """ - if hasattr(socket, "settimeout"): - socket.settimeout(None) - if hasattr(socket, "_sock") and hasattr(socket._sock, "settimeout"): - socket._sock.settimeout(None) + sockets = [socket, getattr(socket, '_sock', None)] + + for s in sockets: + if not hasattr(s, 'settimeout'): + continue + + timeout = -1 + + if hasattr(s, 'gettimeout'): + timeout = s.gettimeout() + + # Don't change the timeout if it is already disabled. + if timeout is None or timeout == 0.0: + continue + + s.settimeout(None) def _get_result(self, container, stream, res): cont = self.inspect_container(container) diff -Nru python-docker-1.8.0/docker/constants.py python-docker-1.9.0/docker/constants.py --- python-docker-1.8.0/docker/constants.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/constants.py 2016-07-26 18:57:21.000000000 +0000 @@ -1,3 +1,6 @@ +import sys +from .version import version + DEFAULT_DOCKER_API_VERSION = '1.22' DEFAULT_TIMEOUT_SECONDS = 60 STREAM_HEADER_SIZE_BYTES = 8 @@ -8,3 +11,7 @@ INSECURE_REGISTRY_DEPRECATION_WARNING = \ 'The `insecure_registry` argument to {} ' \ 'is deprecated and non-functional. Please remove it.' + +IS_WINDOWS_PLATFORM = (sys.platform == 'win32') + +DEFAULT_USER_AGENT = "docker-py/{0}".format(version) diff -Nru python-docker-1.8.0/docker/ssladapter/ssladapter.py python-docker-1.9.0/docker/ssladapter/ssladapter.py --- python-docker-1.8.0/docker/ssladapter/ssladapter.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/ssladapter/ssladapter.py 2016-04-22 22:00:53.000000000 +0000 @@ -18,7 +18,7 @@ # Monkey-patching match_hostname with a version that supports # IP-address checking. Not necessary for Python 3.5 and above if sys.version_info[0] < 3 or sys.version_info[1] < 5: - from .ssl_match_hostname import match_hostname + from backports.ssl_match_hostname import match_hostname urllib3.connection.match_hostname = match_hostname diff -Nru python-docker-1.8.0/docker/ssladapter/ssl_match_hostname.py python-docker-1.9.0/docker/ssladapter/ssl_match_hostname.py --- python-docker-1.8.0/docker/ssladapter/ssl_match_hostname.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/ssladapter/ssl_match_hostname.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -# Slightly modified version of match_hostname in python's ssl library -# https://hg.python.org/cpython/file/tip/Lib/ssl.py -# Changed to make code python 2.x compatible (unicode strings for ip_address -# and 3.5-specific var assignment syntax) - -import ipaddress -import re - -try: - from ssl import CertificateError -except ImportError: - CertificateError = ValueError - -import six - - -def _ipaddress_match(ipname, host_ip): - """Exact matching of IP addresses. - - RFC 6125 explicitly doesn't define an algorithm for this - (section 1.7.2 - "Out of Scope"). - """ - # OpenSSL may add a trailing newline to a subjectAltName's IP address - ip = ipaddress.ip_address(six.text_type(ipname.rstrip())) - return ip == host_ip - - -def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - http://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - split_dn = dn.split(r'.') - leftmost, remainder = split_dn[0], split_dn[1:] - - wildcards = leftmost.count('*') - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == '*': - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append('[^.]+') - elif leftmost.startswith('xn--') or hostname.startswith('xn--'): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) - return pat.match(hostname) - - -def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError("empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED") - try: - host_ip = ipaddress.ip_address(six.text_type(hostname)) - except ValueError: - # Not an IP address (common case) - host_ip = None - dnsnames = [] - san = cert.get('subjectAltName', ()) - for key, value in san: - if key == 'DNS': - if host_ip is None and _dnsname_match(value, hostname): - return - dnsnames.append(value) - elif key == 'IP Address': - if host_ip is not None and _ipaddress_match(value, host_ip): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get('subject', ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == 'commonName': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError( - "hostname %r doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) - elif len(dnsnames) == 1: - raise CertificateError( - "hostname %r doesn't match %r" - % (hostname, dnsnames[0]) - ) - else: - raise CertificateError( - "no appropriate commonName or " - "subjectAltName fields were found" - ) diff -Nru python-docker-1.8.0/docker/transport/__init__.py python-docker-1.9.0/docker/transport/__init__.py --- python-docker-1.8.0/docker/transport/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/docker/transport/__init__.py 2016-06-15 21:28:31.000000000 +0000 @@ -0,0 +1,6 @@ +# flake8: noqa +from .unixconn import UnixAdapter +try: + from .npipeconn import NpipeAdapter +except ImportError: + pass \ No newline at end of file diff -Nru python-docker-1.8.0/docker/transport/npipeconn.py python-docker-1.9.0/docker/transport/npipeconn.py --- python-docker-1.8.0/docker/transport/npipeconn.py 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/docker/transport/npipeconn.py 2016-06-15 21:28:31.000000000 +0000 @@ -0,0 +1,80 @@ +import six +import requests.adapters + +from .npipesocket import NpipeSocket + +if six.PY3: + import http.client as httplib +else: + import httplib + +try: + import requests.packages.urllib3 as urllib3 +except ImportError: + import urllib3 + + +RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer + + +class NpipeHTTPConnection(httplib.HTTPConnection, object): + def __init__(self, npipe_path, timeout=60): + super(NpipeHTTPConnection, self).__init__( + 'localhost', timeout=timeout + ) + self.npipe_path = npipe_path + self.timeout = timeout + + def connect(self): + sock = NpipeSocket() + sock.settimeout(self.timeout) + sock.connect(self.npipe_path) + self.sock = sock + + +class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): + def __init__(self, npipe_path, timeout=60): + super(NpipeHTTPConnectionPool, self).__init__( + 'localhost', timeout=timeout + ) + self.npipe_path = npipe_path + self.timeout = timeout + + def _new_conn(self): + return NpipeHTTPConnection( + self.npipe_path, self.timeout + ) + + +class NpipeAdapter(requests.adapters.HTTPAdapter): + def __init__(self, base_url, timeout=60): + self.npipe_path = base_url.replace('npipe://', '') + self.timeout = timeout + self.pools = RecentlyUsedContainer( + 10, dispose_func=lambda p: p.close() + ) + super(NpipeAdapter, self).__init__() + + def get_connection(self, url, proxies=None): + with self.pools.lock: + pool = self.pools.get(url) + if pool: + return pool + + pool = NpipeHTTPConnectionPool( + self.npipe_path, self.timeout + ) + self.pools[url] = pool + + return pool + + def request_url(self, request, proxies): + # The select_proxy utility in requests errors out when the provided URL + # doesn't have a hostname, like is the case when using a UNIX socket. + # Since proxies are an irrelevant notion in the case of UNIX sockets + # anyway, we simply return the path URL directly. + # See also: https://github.com/docker/docker-py/issues/811 + return request.path_url + + def close(self): + self.pools.clear() diff -Nru python-docker-1.8.0/docker/transport/npipesocket.py python-docker-1.9.0/docker/transport/npipesocket.py --- python-docker-1.8.0/docker/transport/npipesocket.py 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/docker/transport/npipesocket.py 2016-06-15 21:28:31.000000000 +0000 @@ -0,0 +1,191 @@ +import functools +import io + +import win32file +import win32pipe + +cSECURITY_SQOS_PRESENT = 0x100000 +cSECURITY_ANONYMOUS = 0 +cPIPE_READMODE_MESSAGE = 2 + + +def check_closed(f): + @functools.wraps(f) + def wrapped(self, *args, **kwargs): + if self._closed: + raise RuntimeError( + 'Can not reuse socket after connection was closed.' + ) + return f(self, *args, **kwargs) + return wrapped + + +class NpipeSocket(object): + """ Partial implementation of the socket API over windows named pipes. + This implementation is only designed to be used as a client socket, + and server-specific methods (bind, listen, accept...) are not + implemented. + """ + def __init__(self, handle=None): + self._timeout = win32pipe.NMPWAIT_USE_DEFAULT_WAIT + self._handle = handle + self._closed = False + + def accept(self): + raise NotImplementedError() + + def bind(self, address): + raise NotImplementedError() + + def close(self): + self._handle.Close() + self._closed = True + + @check_closed + def connect(self, address): + win32pipe.WaitNamedPipe(address, self._timeout) + handle = win32file.CreateFile( + address, + win32file.GENERIC_READ | win32file.GENERIC_WRITE, + 0, + None, + win32file.OPEN_EXISTING, + cSECURITY_ANONYMOUS | cSECURITY_SQOS_PRESENT, + 0 + ) + self.flags = win32pipe.GetNamedPipeInfo(handle)[0] + + self._handle = handle + self._address = address + + @check_closed + def connect_ex(self, address): + return self.connect(address) + + @check_closed + def detach(self): + self._closed = True + return self._handle + + @check_closed + def dup(self): + return NpipeSocket(self._handle) + + @check_closed + def fileno(self): + return int(self._handle) + + def getpeername(self): + return self._address + + def getsockname(self): + return self._address + + def getsockopt(self, level, optname, buflen=None): + raise NotImplementedError() + + def ioctl(self, control, option): + raise NotImplementedError() + + def listen(self, backlog): + raise NotImplementedError() + + def makefile(self, mode=None, bufsize=None): + if mode.strip('b') != 'r': + raise NotImplementedError() + rawio = NpipeFileIOBase(self) + if bufsize is None: + bufsize = io.DEFAULT_BUFFER_SIZE + return io.BufferedReader(rawio, buffer_size=bufsize) + + @check_closed + def recv(self, bufsize, flags=0): + err, data = win32file.ReadFile(self._handle, bufsize) + return data + + @check_closed + def recvfrom(self, bufsize, flags=0): + data = self.recv(bufsize, flags) + return (data, self._address) + + @check_closed + def recvfrom_into(self, buf, nbytes=0, flags=0): + return self.recv_into(buf, nbytes, flags), self._address + + @check_closed + def recv_into(self, buf, nbytes=0): + readbuf = buf + if not isinstance(buf, memoryview): + readbuf = memoryview(buf) + + err, data = win32file.ReadFile( + self._handle, + readbuf[:nbytes] if nbytes else readbuf + ) + return len(data) + + @check_closed + def send(self, string, flags=0): + err, nbytes = win32file.WriteFile(self._handle, string) + return nbytes + + @check_closed + def sendall(self, string, flags=0): + return self.send(string, flags) + + @check_closed + def sendto(self, string, address): + self.connect(address) + return self.send(string) + + def setblocking(self, flag): + if flag: + return self.settimeout(None) + return self.settimeout(0) + + def settimeout(self, value): + if value is None: + self._timeout = win32pipe.NMPWAIT_NOWAIT + elif not isinstance(value, (float, int)) or value < 0: + raise ValueError('Timeout value out of range') + elif value == 0: + self._timeout = win32pipe.NMPWAIT_USE_DEFAULT_WAIT + else: + self._timeout = value + + def gettimeout(self): + return self._timeout + + def setsockopt(self, level, optname, value): + raise NotImplementedError() + + @check_closed + def shutdown(self, how): + return self.close() + + +class NpipeFileIOBase(io.RawIOBase): + def __init__(self, npipe_socket): + self.sock = npipe_socket + + def close(self): + super(NpipeFileIOBase, self).close() + self.sock = None + + def fileno(self): + return self.sock.fileno() + + def isatty(self): + return False + + def readable(self): + return True + + def readinto(self, buf): + return self.sock.recv_into(buf) + + def seekable(self): + return False + + def writable(self): + return False diff -Nru python-docker-1.8.0/docker/transport/unixconn.py python-docker-1.9.0/docker/transport/unixconn.py --- python-docker-1.8.0/docker/transport/unixconn.py 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/docker/transport/unixconn.py 2016-06-15 21:28:31.000000000 +0000 @@ -0,0 +1,94 @@ +# Copyright 2013 dotCloud inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import six +import requests.adapters +import socket + +if six.PY3: + import http.client as httplib +else: + import httplib + +try: + import requests.packages.urllib3 as urllib3 +except ImportError: + import urllib3 + +RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer + + +class UnixHTTPConnection(httplib.HTTPConnection, object): + def __init__(self, base_url, unix_socket, timeout=60): + super(UnixHTTPConnection, self).__init__( + 'localhost', timeout=timeout + ) + self.base_url = base_url + self.unix_socket = unix_socket + self.timeout = timeout + + def connect(self): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.settimeout(self.timeout) + sock.connect(self.unix_socket) + self.sock = sock + + +class UnixHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): + def __init__(self, base_url, socket_path, timeout=60): + super(UnixHTTPConnectionPool, self).__init__( + 'localhost', timeout=timeout + ) + self.base_url = base_url + self.socket_path = socket_path + self.timeout = timeout + + def _new_conn(self): + return UnixHTTPConnection(self.base_url, self.socket_path, + self.timeout) + + +class UnixAdapter(requests.adapters.HTTPAdapter): + def __init__(self, socket_url, timeout=60): + socket_path = socket_url.replace('http+unix://', '') + if not socket_path.startswith('/'): + socket_path = '/' + socket_path + self.socket_path = socket_path + self.timeout = timeout + self.pools = RecentlyUsedContainer(10, + dispose_func=lambda p: p.close()) + super(UnixAdapter, self).__init__() + + def get_connection(self, url, proxies=None): + with self.pools.lock: + pool = self.pools.get(url) + if pool: + return pool + + pool = UnixHTTPConnectionPool( + url, self.socket_path, self.timeout + ) + self.pools[url] = pool + + return pool + + def request_url(self, request, proxies): + # The select_proxy utility in requests errors out when the provided URL + # doesn't have a hostname, like is the case when using a UNIX socket. + # Since proxies are an irrelevant notion in the case of UNIX sockets + # anyway, we simply return the path URL directly. + # See also: https://github.com/docker/docker-py/issues/811 + return request.path_url + + def close(self): + self.pools.clear() diff -Nru python-docker-1.8.0/docker/unixconn/__init__.py python-docker-1.9.0/docker/unixconn/__init__.py --- python-docker-1.8.0/docker/unixconn/__init__.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/unixconn/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -from .unixconn import UnixAdapter # flake8: noqa diff -Nru python-docker-1.8.0/docker/unixconn/unixconn.py python-docker-1.9.0/docker/unixconn/unixconn.py --- python-docker-1.8.0/docker/unixconn/unixconn.py 2016-04-11 14:49:54.000000000 +0000 +++ python-docker-1.9.0/docker/unixconn/unixconn.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -# Copyright 2013 dotCloud inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import six -import requests.adapters -import socket - -if six.PY3: - import http.client as httplib -else: - import httplib - -try: - import requests.packages.urllib3 as urllib3 -except ImportError: - import urllib3 - -RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer - - -class UnixHTTPConnection(httplib.HTTPConnection, object): - def __init__(self, base_url, unix_socket, timeout=60): - super(UnixHTTPConnection, self).__init__( - 'localhost', timeout=timeout - ) - self.base_url = base_url - self.unix_socket = unix_socket - self.timeout = timeout - - def connect(self): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(self.timeout) - sock.connect(self.unix_socket) - self.sock = sock - - -class UnixHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): - def __init__(self, base_url, socket_path, timeout=60): - super(UnixHTTPConnectionPool, self).__init__( - 'localhost', timeout=timeout - ) - self.base_url = base_url - self.socket_path = socket_path - self.timeout = timeout - - def _new_conn(self): - return UnixHTTPConnection(self.base_url, self.socket_path, - self.timeout) - - -class UnixAdapter(requests.adapters.HTTPAdapter): - def __init__(self, socket_url, timeout=60): - socket_path = socket_url.replace('http+unix://', '') - if not socket_path.startswith('/'): - socket_path = '/' + socket_path - self.socket_path = socket_path - self.timeout = timeout - self.pools = RecentlyUsedContainer(10, - dispose_func=lambda p: p.close()) - super(UnixAdapter, self).__init__() - - def get_connection(self, url, proxies=None): - with self.pools.lock: - pool = self.pools.get(url) - if pool: - return pool - - pool = UnixHTTPConnectionPool( - url, self.socket_path, self.timeout - ) - self.pools[url] = pool - - return pool - - def request_url(self, request, proxies): - # The select_proxy utility in requests errors out when the provided URL - # doesn't have a hostname, like is the case when using a UNIX socket. - # Since proxies are an irrelevant notion in the case of UNIX sockets - # anyway, we simply return the path URL directly. - # See also: https://github.com/docker/docker-py/issues/811 - return request.path_url - - def close(self): - self.pools.clear() diff -Nru python-docker-1.8.0/docker/utils/utils.py python-docker-1.9.0/docker/utils/utils.py --- python-docker-1.8.0/docker/utils/utils.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/utils/utils.py 2016-07-26 18:56:41.000000000 +0000 @@ -199,6 +199,9 @@ def match_path(path, pattern): pattern = pattern.rstrip('/') + if pattern: + pattern = os.path.relpath(pattern) + pattern_components = pattern.split('/') path_components = path.split('/')[:len(pattern_components)] return fnmatch('/'.join(path_components), pattern) @@ -380,13 +383,13 @@ # fd:// protocol unsupported (for obvious reasons) # Added support for http and https # Protocol translation: tcp -> http, unix -> http+unix -def parse_host(addr, platform=None, tls=False): +def parse_host(addr, is_win32=False, tls=False): proto = "http+unix" host = DEFAULT_HTTP_HOST port = None path = '' - if not addr and platform == 'win32': + if not addr and is_win32: addr = '{0}:{1}'.format(DEFAULT_HTTP_HOST, 2375) if not addr or addr.strip() == 'unix://': @@ -410,6 +413,9 @@ elif addr.startswith('https://'): proto = "https" addr = addr[8:] + elif addr.startswith('npipe://'): + proto = 'npipe' + addr = addr[8:] elif addr.startswith('fd://'): raise errors.DockerException("fd protocol is not implemented") else: @@ -445,7 +451,7 @@ else: host = addr - if proto == "http+unix": + if proto == "http+unix" or proto == 'npipe': return "{0}://{1}".format(proto, host) return "{0}://{1}:{2}{3}".format(proto, host, port, path) @@ -543,12 +549,6 @@ return delta.seconds + delta.days * 24 * 3600 -def longint(n): - if six.PY3: - return int(n) - return long(n) - - def parse_bytes(s): if isinstance(s, six.integer_types + (float,)): return s @@ -571,7 +571,7 @@ if suffix in units.keys() or suffix.isdigit(): try: - digits = longint(digits_part) + digits = int(digits_part) except ValueError: raise errors.DockerException( 'Failed converting the string value for memory ({0}) to' @@ -579,7 +579,7 @@ ) # Reconvert to long for the final result - s = longint(digits * units[suffix]) + s = int(digits * units[suffix]) else: raise errors.DockerException( 'The specified value for memory ({0}) should specify the' @@ -615,8 +615,12 @@ security_opt=None, ulimits=None, log_config=None, mem_limit=None, memswap_limit=None, mem_swappiness=None, cgroup_parent=None, group_add=None, cpu_quota=None, - cpu_period=None, oom_kill_disable=False, shm_size=None, - version=None, tmpfs=None, oom_score_adj=None): + cpu_period=None, blkio_weight=None, + blkio_weight_device=None, device_read_bps=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): host_config = {} @@ -792,6 +796,58 @@ host_config['CpuPeriod'] = cpu_period + if blkio_weight: + if not isinstance(blkio_weight, int): + raise host_config_type_error('blkio_weight', blkio_weight, 'int') + if version_lt(version, '1.22'): + raise host_config_version_error('blkio_weight', '1.22') + host_config["BlkioWeight"] = blkio_weight + + if blkio_weight_device: + if not isinstance(blkio_weight_device, list): + raise host_config_type_error( + 'blkio_weight_device', blkio_weight_device, 'list' + ) + if version_lt(version, '1.22'): + raise host_config_version_error('blkio_weight_device', '1.22') + host_config["BlkioWeightDevice"] = blkio_weight_device + + if device_read_bps: + if not isinstance(device_read_bps, list): + raise host_config_type_error( + 'device_read_bps', device_read_bps, 'list' + ) + if version_lt(version, '1.22'): + raise host_config_version_error('device_read_bps', '1.22') + host_config["BlkioDeviceReadBps"] = device_read_bps + + if device_write_bps: + if not isinstance(device_write_bps, list): + raise host_config_type_error( + 'device_write_bps', device_write_bps, 'list' + ) + if version_lt(version, '1.22'): + raise host_config_version_error('device_write_bps', '1.22') + host_config["BlkioDeviceWriteBps"] = device_write_bps + + if device_read_iops: + if not isinstance(device_read_iops, list): + raise host_config_type_error( + 'device_read_iops', device_read_iops, 'list' + ) + if version_lt(version, '1.22'): + raise host_config_version_error('device_read_iops', '1.22') + host_config["BlkioDeviceReadIOps"] = device_read_iops + + if device_write_iops: + if not isinstance(device_write_iops, list): + raise host_config_type_error( + 'device_write_iops', device_write_iops, 'list' + ) + if version_lt(version, '1.22'): + raise host_config_version_error('device_write_iops', '1.22') + host_config["BlkioDeviceWriteIOps"] = device_write_iops + if tmpfs: if version_lt(version, '1.22'): raise host_config_version_error('tmpfs', '1.22') @@ -816,19 +872,30 @@ return networking_config -def create_endpoint_config(version, aliases=None, links=None): +def create_endpoint_config(version, aliases=None, links=None, + ipv4_address=None, ipv6_address=None): + if version_lt(version, '1.22'): + raise errors.InvalidVersion( + 'Endpoint config is not supported for API version < 1.22' + ) endpoint_config = {} if aliases: - if version_lt(version, '1.22'): - raise host_config_version_error('endpoint_config.aliases', '1.22') endpoint_config["Aliases"] = aliases if links: - if version_lt(version, '1.22'): - raise host_config_version_error('endpoint_config.links', '1.22') endpoint_config["Links"] = normalize_links(links) + ipam_config = {} + if ipv4_address: + ipam_config['IPv4Address'] = ipv4_address + + if ipv6_address: + ipam_config['IPv6Address'] = ipv6_address + + if ipam_config: + endpoint_config['IPAMConfig'] = ipam_config + return endpoint_config @@ -845,7 +912,7 @@ if line[0] == '#': continue - parse_line = line.strip().split('=') + parse_line = line.strip().split('=', 1) if len(parse_line) == 2: k, v = parse_line environment[k] = v diff -Nru python-docker-1.8.0/docker/version.py python-docker-1.9.0/docker/version.py --- python-docker-1.8.0/docker/version.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/docker/version.py 2016-07-26 19:21:26.000000000 +0000 @@ -1,2 +1,2 @@ -version = "1.8.0" +version = "1.9.0" version_info = tuple([int(d) for d in version.split("-")[0].split(".")]) diff -Nru python-docker-1.8.0/.dockerignore python-docker-1.9.0/.dockerignore --- python-docker-1.8.0/.dockerignore 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/.dockerignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -.git/ - -build -dist -*.egg-info -*.egg/ -*.pyc -*.swp - -.tox -.coverage -html/* -__pycache__ - -# Compiled Documentation -site/ -Makefile diff -Nru python-docker-1.8.0/docker_py.egg-info/PKG-INFO python-docker-1.9.0/docker_py.egg-info/PKG-INFO --- python-docker-1.8.0/docker_py.egg-info/PKG-INFO 2016-04-11 14:36:29.000000000 +0000 +++ python-docker-1.9.0/docker_py.egg-info/PKG-INFO 2016-07-26 19:21:37.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: docker-py -Version: 1.8.0 +Version: 1.9.0 Summary: Python client for Docker. Home-page: https://github.com/docker/docker-py/ Author: UNKNOWN @@ -13,9 +13,12 @@ Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Utilities Classifier: License :: OSI Approved :: Apache Software License diff -Nru python-docker-1.8.0/docker_py.egg-info/requires.txt python-docker-1.9.0/docker_py.egg-info/requires.txt --- python-docker-1.8.0/docker_py.egg-info/requires.txt 2016-04-11 15:02:27.000000000 +0000 +++ python-docker-1.9.0/docker_py.egg-info/requires.txt 2016-07-26 19:21:37.000000000 +0000 @@ -2,5 +2,8 @@ six >= 1.4.0 websocket-client >= 0.32.0 -[:python_version < "3"] -py2-ipaddress >= 3.4.1 +[:python_version < "3.3"] +ipaddress >= 1.0.16 + +[:python_version < "3.5"] +backports.ssl_match_hostname >= 3.5 diff -Nru python-docker-1.8.0/docker_py.egg-info/SOURCES.txt python-docker-1.9.0/docker_py.egg-info/SOURCES.txt --- python-docker-1.8.0/docker_py.egg-info/SOURCES.txt 2016-04-11 14:36:29.000000000 +0000 +++ python-docker-1.9.0/docker_py.egg-info/SOURCES.txt 2016-07-26 19:21:37.000000000 +0000 @@ -1,6 +1,7 @@ LICENSE MANIFEST.in README.md +README.rst requirements.txt setup.cfg setup.py @@ -22,10 +23,11 @@ docker/auth/__init__.py docker/auth/auth.py docker/ssladapter/__init__.py -docker/ssladapter/ssl_match_hostname.py docker/ssladapter/ssladapter.py -docker/unixconn/__init__.py -docker/unixconn/unixconn.py +docker/transport/__init__.py +docker/transport/npipeconn.py +docker/transport/npipesocket.py +docker/transport/unixconn.py docker/utils/__init__.py docker/utils/decorators.py docker/utils/types.py diff -Nru python-docker-1.8.0/.editorconfig python-docker-1.9.0/.editorconfig --- python-docker-1.8.0/.editorconfig 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/.editorconfig 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 4 -insert_final_newline = true -trim_trailing_whitespace = true -max_line_length = 80 - -[*.md] -trim_trailing_whitespace = false diff -Nru python-docker-1.8.0/.gitignore python-docker-1.9.0/.gitignore --- python-docker-1.8.0/.gitignore 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -build -dist -*.egg-info -*.egg/ -*.pyc -*.swp - -.tox -.coverage -html/* - -# Compiled Documentation -site/ -README.rst - -env/ -venv/ -.idea/ diff -Nru python-docker-1.8.0/PKG-INFO python-docker-1.9.0/PKG-INFO --- python-docker-1.8.0/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/PKG-INFO 2016-07-26 19:21:37.000000000 +0000 @@ -0,0 +1,24 @@ +Metadata-Version: 1.1 +Name: docker-py +Version: 1.9.0 +Summary: Python client for Docker. +Home-page: https://github.com/docker/docker-py/ +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Other Environment +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Topic :: Utilities +Classifier: License :: OSI Approved :: Apache Software License diff -Nru python-docker-1.8.0/README.md python-docker-1.9.0/README.md --- python-docker-1.8.0/README.md 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/README.md 2016-06-14 00:58:10.000000000 +0000 @@ -17,7 +17,7 @@ [![Documentation Status](https://readthedocs.org/projects/docker-py/badge/?version=latest)](https://readthedocs.org/projects/docker-py/?badge=latest) -[Read the full documentation here](http://docker-py.readthedocs.org/en/latest/). +[Read the full documentation here](https://docker-py.readthedocs.io/en/latest/). The source is available in the `docs/` directory. diff -Nru python-docker-1.8.0/README.rst python-docker-1.9.0/README.rst --- python-docker-1.8.0/README.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-docker-1.9.0/README.rst 2016-07-26 19:21:36.000000000 +0000 @@ -0,0 +1,37 @@ +docker-py +========= + +|Build Status| + +A Python library for the Docker Remote API. It does everything the +``docker`` command does, but from within Python – run containers, manage +them, pull/push images, etc. + +Installation +------------ + +The latest stable version is always available on PyPi. + +:: + + pip install docker-py + +Documentation +------------- + +|Documentation Status| + +`Read the full documentation +here `__. The source is +available in the ``docs/`` directory. + +License +------- + +Docker is licensed under the Apache License, Version 2.0. See LICENSE +for full license text + +.. |Build Status| image:: https://travis-ci.org/docker/docker-py.png + :target: https://travis-ci.org/docker/docker-py +.. |Documentation Status| image:: https://readthedocs.org/projects/docker-py/badge/?version=latest + :target: https://readthedocs.org/projects/docker-py/?badge=latest diff -Nru python-docker-1.8.0/requirements.txt python-docker-1.9.0/requirements.txt --- python-docker-1.8.0/requirements.txt 2016-04-11 15:02:27.000000000 +0000 +++ python-docker-1.9.0/requirements.txt 2016-04-22 22:00:53.000000000 +0000 @@ -1,4 +1,5 @@ requests==2.5.3 six>=1.4.0 websocket-client==0.32.0 -py2-ipaddress==3.4.1 ; python_version < '3.2' \ No newline at end of file +backports.ssl_match_hostname>=3.5 ; python_version < '3.5' +ipaddress==1.0.16 ; python_version < '3.3' \ No newline at end of file diff -Nru python-docker-1.8.0/scripts/release.sh python-docker-1.9.0/scripts/release.sh --- python-docker-1.8.0/scripts/release.sh 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/scripts/release.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -#!/bin/bash -# -# Create the official release -# - -if [ -z "$(command -v pandoc 2> /dev/null)" ]; then - >&2 echo "$0 requires http://pandoc.org/" - >&2 echo "Please install it and make sure it is available on your \$PATH." - exit 2 -fi - -VERSION=$1 -REPO=docker/docker-py -GITHUB_REPO=git@github.com:$REPO - -if [ -z $VERSION ]; then - echo "Usage: $0 VERSION [upload]" - exit 1 -fi - -echo "##> Tagging the release as $VERSION" -git tag $VERSION || exit 1 -if [[ $2 == 'upload' ]]; then - echo "##> Pushing tag to github" - git push $GITHUB_REPO $VERSION || exit 1 -fi - - -pandoc -f markdown -t rst README.md -o README.rst || exit 1 -if [[ $2 == 'upload' ]]; then - echo "##> Uploading sdist to pypi" - python setup.py sdist bdist_wheel upload -fi diff -Nru python-docker-1.8.0/setup.cfg python-docker-1.9.0/setup.cfg --- python-docker-1.8.0/setup.cfg 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/setup.cfg 2016-07-26 19:21:37.000000000 +0000 @@ -1,3 +1,8 @@ [bdist_wheel] - universal = 1 + +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff -Nru python-docker-1.8.0/setup.py python-docker-1.9.0/setup.py --- python-docker-1.8.0/setup.py 2016-04-11 15:02:27.000000000 +0000 +++ python-docker-1.9.0/setup.py 2016-06-15 21:28:31.000000000 +0000 @@ -1,8 +1,10 @@ #!/usr/bin/env python import os import sys + from setuptools import setup + ROOT_DIR = os.path.dirname(__file__) SOURCE_DIR = os.path.join(ROOT_DIR) @@ -12,10 +14,15 @@ 'websocket-client >= 0.32.0', ] +if sys.platform == 'win32': + requirements.append('pypiwin32 >= 219') + extras_require = { - ':python_version < "3"': 'py2-ipaddress >= 3.4.1', + ':python_version < "3.5"': 'backports.ssl_match_hostname >= 3.5', + ':python_version < "3.3"': 'ipaddress >= 1.0.16', } +version = None exec(open('docker/version.py').read()) with open('./test-requirements.txt') as test_reqs_txt: @@ -28,7 +35,7 @@ description="Python client for Docker.", url='https://github.com/docker/docker-py/', packages=[ - 'docker', 'docker.api', 'docker.auth', 'docker.unixconn', + 'docker', 'docker.api', 'docker.auth', 'docker.transport', 'docker.utils', 'docker.utils.ports', 'docker.ssladapter' ], install_requires=requirements, @@ -42,10 +49,13 @@ 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Topic :: Utilities', 'License :: OSI Approved :: Apache Software License', ], diff -Nru python-docker-1.8.0/test-requirements.txt python-docker-1.9.0/test-requirements.txt --- python-docker-1.8.0/test-requirements.txt 2016-04-11 15:02:27.000000000 +0000 +++ python-docker-1.9.0/test-requirements.txt 2016-06-03 00:59:50.000000000 +0000 @@ -1,5 +1,5 @@ mock==1.0.1 -pytest==2.7.2 +pytest==2.9.1 coverage==3.7.1 pytest-cov==2.1.0 -flake8==2.4.1 \ No newline at end of file +flake8==2.4.1 diff -Nru python-docker-1.8.0/tests/Dockerfile-dind-certs python-docker-1.9.0/tests/Dockerfile-dind-certs --- python-docker-1.8.0/tests/Dockerfile-dind-certs 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/Dockerfile-dind-certs 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -FROM python:2.7 -RUN mkdir /tmp/certs -VOLUME /certs - -WORKDIR /tmp/certs -RUN openssl genrsa -aes256 -passout pass:foobar -out ca-key.pem 4096 -RUN echo "[req]\nprompt=no\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\ncountryName=AU" > /tmp/config -RUN openssl req -new -x509 -passin pass:foobar -config /tmp/config -days 365 -key ca-key.pem -sha256 -out ca.pem -RUN openssl genrsa -out server-key.pem -passout pass:foobar 4096 -RUN openssl req -subj "/CN=docker" -sha256 -new -key server-key.pem -out server.csr -RUN echo subjectAltName = DNS:docker,DNS:localhost > extfile.cnf -RUN openssl x509 -req -days 365 -passin pass:foobar -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf -RUN openssl genrsa -out key.pem 4096 -RUN openssl req -passin pass:foobar -subj '/CN=client' -new -key key.pem -out client.csr -RUN echo extendedKeyUsage = clientAuth > extfile.cnf -RUN openssl x509 -req -passin pass:foobar -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf -RUN chmod -v 0400 ca-key.pem key.pem server-key.pem -RUN chmod -v 0444 ca.pem server-cert.pem cert.pem - -CMD cp -R /tmp/certs/* /certs && while true; do sleep 1; done diff -Nru python-docker-1.8.0/tests/integration/container_test.py python-docker-1.9.0/tests/integration/container_test.py --- python-docker-1.8.0/tests/integration/container_test.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/integration/container_test.py 2016-07-26 18:56:41.000000000 +0000 @@ -840,6 +840,36 @@ self.assertIn('Running', state) self.assertEqual(state['Running'], False, state) + def test_kill_with_signal_name(self): + id = self.client.create_container(BUSYBOX, ['sleep', '60']) + self.client.start(id) + self.tmp_containers.append(id) + self.client.kill(id, signal='SIGKILL') + exitcode = self.client.wait(id) + self.assertNotEqual(exitcode, 0) + container_info = self.client.inspect_container(id) + self.assertIn('State', container_info) + state = container_info['State'] + self.assertIn('ExitCode', state) + self.assertNotEqual(state['ExitCode'], 0) + self.assertIn('Running', state) + self.assertEqual(state['Running'], False, state) + + def test_kill_with_signal_integer(self): + id = self.client.create_container(BUSYBOX, ['sleep', '60']) + self.client.start(id) + self.tmp_containers.append(id) + self.client.kill(id, signal=9) + exitcode = self.client.wait(id) + self.assertNotEqual(exitcode, 0) + container_info = self.client.inspect_container(id) + self.assertIn('State', container_info) + state = container_info['State'] + self.assertIn('ExitCode', state) + self.assertNotEqual(state['ExitCode'], 0) + self.assertIn('Running', state) + self.assertEqual(state['Running'], False, state) + class PortTest(helpers.BaseTestCase): def test_port(self): diff -Nru python-docker-1.8.0/tests/integration/network_test.py python-docker-1.9.0/tests/integration/network_test.py --- python-docker-1.8.0/tests/integration/network_test.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/integration/network_test.py 2016-07-26 18:56:41.000000000 +0000 @@ -138,9 +138,11 @@ self.client.connect_container_to_network( container, net_id, aliases=['foo', 'bar']) container_data = self.client.inspect_container(container) - self.assertEqual( - container_data['NetworkSettings']['Networks'][net_name]['Aliases'], - ['foo', 'bar']) + aliases = ( + container_data['NetworkSettings']['Networks'][net_name]['Aliases'] + ) + assert 'foo' in aliases + assert 'bar' in aliases @requires_api_version('1.21') def test_connect_on_container_create(self): @@ -183,9 +185,69 @@ self.client.start(container) container_data = self.client.inspect_container(container) + aliases = ( + container_data['NetworkSettings']['Networks'][net_name]['Aliases'] + ) + assert 'foo' in aliases + assert 'bar' in aliases + + @requires_api_version('1.22') + def test_create_with_ipv4_address(self): + net_name, net_id = self.create_network( + ipam=create_ipam_config( + driver='default', + pool_configs=[create_ipam_pool(subnet="132.124.0.0/16")], + ), + ) + container = self.client.create_container( + image='busybox', command='top', + host_config=self.client.create_host_config(network_mode=net_name), + networking_config=self.client.create_networking_config({ + net_name: self.client.create_endpoint_config( + ipv4_address='132.124.0.23' + ) + }) + ) + self.tmp_containers.append(container) + self.client.start(container) + + container_data = self.client.inspect_container(container) self.assertEqual( - container_data['NetworkSettings']['Networks'][net_name]['Aliases'], - ['foo', 'bar']) + container_data[ + 'NetworkSettings']['Networks'][net_name]['IPAMConfig'][ + 'IPv4Address' + ], + '132.124.0.23' + ) + + @requires_api_version('1.22') + def test_create_with_ipv6_address(self): + net_name, net_id = self.create_network( + ipam=create_ipam_config( + driver='default', + pool_configs=[create_ipam_pool(subnet="2001:389::1/64")], + ), + ) + container = self.client.create_container( + image='busybox', command='top', + host_config=self.client.create_host_config(network_mode=net_name), + networking_config=self.client.create_networking_config({ + net_name: self.client.create_endpoint_config( + ipv6_address='2001:389::f00d' + ) + }) + ) + self.tmp_containers.append(container) + self.client.start(container) + + container_data = self.client.inspect_container(container) + self.assertEqual( + container_data[ + 'NetworkSettings']['Networks'][net_name]['IPAMConfig'][ + 'IPv6Address' + ], + '2001:389::f00d' + ) @requires_api_version('1.22') def test_create_with_links(self): @@ -298,3 +360,9 @@ self.assertEqual( net_data['IPAMConfig']['IPv6Address'], '2001:389::f00d' ) + + @requires_api_version('1.23') + def test_create_internal_networks(self): + _, net_id = self.create_network(internal=True) + net = self.client.inspect_network(net_id) + assert net['Internal'] is True diff -Nru python-docker-1.8.0/tests/unit/api_test.py python-docker-1.9.0/tests/unit/api_test.py --- python-docker-1.8.0/tests/unit/api_test.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/unit/api_test.py 2016-07-26 18:57:21.000000000 +0000 @@ -415,3 +415,33 @@ self.assertEqual(list(stream), [ str(i).encode() for i in range(50)]) + + +class UserAgentTest(base.BaseTestCase): + def setUp(self): + self.patcher = mock.patch.object( + docker.Client, + 'send', + return_value=fake_resp("GET", "%s/version" % fake_api.prefix) + ) + self.mock_send = self.patcher.start() + + def tearDown(self): + self.patcher.stop() + + def test_default_user_agent(self): + client = docker.Client() + client.version() + + self.assertEqual(self.mock_send.call_count, 1) + headers = self.mock_send.call_args[0][0].headers + expected = 'docker-py/%s' % docker.__version__ + self.assertEqual(headers['User-Agent'], expected) + + def test_custom_user_agent(self): + client = docker.Client(user_agent='foo/bar') + client.version() + + self.assertEqual(self.mock_send.call_count, 1) + headers = self.mock_send.call_args[0][0].headers + self.assertEqual(headers['User-Agent'], 'foo/bar') diff -Nru python-docker-1.8.0/tests/unit/client_test.py python-docker-1.9.0/tests/unit/client_test.py --- python-docker-1.8.0/tests/unit/client_test.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/unit/client_test.py 2016-06-03 00:59:07.000000000 +0000 @@ -24,3 +24,47 @@ DOCKER_TLS_VERIFY='1') client = Client.from_env() self.assertEqual(client.base_url, "https://192.168.59.103:2376") + + +class DisableSocketTest(base.BaseTestCase): + class DummySocket(object): + def __init__(self, timeout=60): + self.timeout = timeout + + def settimeout(self, timeout): + self.timeout = timeout + + def gettimeout(self): + return self.timeout + + def setUp(self): + self.client = Client() + + def test_disable_socket_timeout(self): + """Test that the timeout is disabled on a generic socket object.""" + socket = self.DummySocket() + + self.client._disable_socket_timeout(socket) + + self.assertEqual(socket.timeout, None) + + def test_disable_socket_timeout2(self): + """Test that the timeouts are disabled on a generic socket object + and it's _sock object if present.""" + socket = self.DummySocket() + socket._sock = self.DummySocket() + + self.client._disable_socket_timeout(socket) + + self.assertEqual(socket.timeout, None) + self.assertEqual(socket._sock.timeout, None) + + def test_disable_socket_timout_non_blocking(self): + """Test that a non-blocking socket does not get set to blocking.""" + socket = self.DummySocket() + socket._sock = self.DummySocket(0.0) + + self.client._disable_socket_timeout(socket) + + self.assertEqual(socket.timeout, None) + self.assertEqual(socket._sock.timeout, 0.0) diff -Nru python-docker-1.8.0/tests/unit/ssladapter_test.py python-docker-1.9.0/tests/unit/ssladapter_test.py --- python-docker-1.8.0/tests/unit/ssladapter_test.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/unit/ssladapter_test.py 2016-04-22 22:00:53.000000000 +0000 @@ -1,7 +1,13 @@ from docker.ssladapter import ssladapter -from docker.ssladapter.ssl_match_hostname import ( - match_hostname, CertificateError -) + +try: + from backports.ssl_match_hostname import ( + match_hostname, CertificateError + ) +except ImportError: + from ssl import ( + match_hostname, CertificateError + ) try: from ssl import OP_NO_SSLv3, OP_NO_SSLv2, OP_NO_TLSv1 diff -Nru python-docker-1.8.0/tests/unit/utils_test.py python-docker-1.9.0/tests/unit/utils_test.py --- python-docker-1.8.0/tests/unit/utils_test.py 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/tests/unit/utils_test.py 2016-06-15 21:28:31.000000000 +0000 @@ -64,6 +64,25 @@ config = create_host_config(version='1.20', cpu_period=1999) self.assertEqual(config.get('CpuPeriod'), 1999) + def test_create_host_config_with_blkio_constraints(self): + blkio_rate = [{"Path": "/dev/sda", "Rate": 1000}] + config = create_host_config(version='1.22', + blkio_weight=1999, + blkio_weight_device=blkio_rate, + device_read_bps=blkio_rate, + device_write_bps=blkio_rate, + device_read_iops=blkio_rate, + device_write_iops=blkio_rate) + + self.assertEqual(config.get('BlkioWeight'), 1999) + self.assertTrue(config.get('BlkioWeightDevice') is blkio_rate) + self.assertTrue(config.get('BlkioDeviceReadBps') is blkio_rate) + self.assertTrue(config.get('BlkioDeviceWriteBps') is blkio_rate) + self.assertTrue(config.get('BlkioDeviceReadIOps') is blkio_rate) + self.assertTrue(config.get('BlkioDeviceWriteIOps') is blkio_rate) + self.assertEqual(blkio_rate[0]['Path'], "/dev/sda") + self.assertEqual(blkio_rate[0]['Rate'], 1000) + def test_create_host_config_with_shm_size(self): config = create_host_config(version='1.22', shm_size=67108864) self.assertEqual(config.get('ShmSize'), 67108864) @@ -299,56 +318,30 @@ self.assertEqual(convert_volume_binds(data), ['/mnt/vol1:/data:rw']) def test_convert_volume_binds_unicode_bytes_input(self): - if six.PY2: - expected = [unicode('/mnt/지연:/unicode/박:rw', 'utf-8')] - - data = { - '/mnt/지연': { - 'bind': '/unicode/박', - 'mode': 'rw' - } - } - self.assertEqual( - convert_volume_binds(data), expected - ) - else: - expected = ['/mnt/지연:/unicode/박:rw'] + expected = [u'/mnt/지연:/unicode/박:rw'] - data = { - bytes('/mnt/지연', 'utf-8'): { - 'bind': bytes('/unicode/박', 'utf-8'), - 'mode': 'rw' - } + data = { + u'/mnt/지연'.encode('utf-8'): { + 'bind': u'/unicode/박'.encode('utf-8'), + 'mode': 'rw' } - self.assertEqual( - convert_volume_binds(data), expected - ) + } + self.assertEqual( + convert_volume_binds(data), expected + ) def test_convert_volume_binds_unicode_unicode_input(self): - if six.PY2: - expected = [unicode('/mnt/지연:/unicode/박:rw', 'utf-8')] - - data = { - unicode('/mnt/지연', 'utf-8'): { - 'bind': unicode('/unicode/박', 'utf-8'), - 'mode': 'rw' - } - } - self.assertEqual( - convert_volume_binds(data), expected - ) - else: - expected = ['/mnt/지연:/unicode/박:rw'] + expected = [u'/mnt/지연:/unicode/박:rw'] - data = { - '/mnt/지연': { - 'bind': '/unicode/박', - 'mode': 'rw' - } + data = { + u'/mnt/지연': { + 'bind': u'/unicode/박', + 'mode': 'rw' } - self.assertEqual( - convert_volume_binds(data), expected - ) + } + self.assertEqual( + convert_volume_binds(data), expected + ) class ParseEnvFileTest(base.BaseTestCase): @@ -371,6 +364,14 @@ {'USER': 'jdoe', 'PASS': 'secret'}) os.unlink(env_file) + def test_parse_env_file_with_equals_character(self): + env_file = self.generate_tempfile( + file_content='USER=jdoe\nPASS=sec==ret') + get_parse_env_file = parse_env_file(env_file) + self.assertEqual(get_parse_env_file, + {'USER': 'jdoe', 'PASS': 'sec==ret'}) + os.unlink(env_file) + def test_parse_env_file_commented_line(self): env_file = self.generate_tempfile( file_content='USER=jdoe\n#PASS=secret') @@ -406,6 +407,7 @@ 'somehost.net:80/service/swarm': ( 'http://somehost.net:80/service/swarm' ), + 'npipe:////./pipe/docker_engine': 'npipe:////./pipe/docker_engine', } for host in invalid_hosts: @@ -420,10 +422,8 @@ tcp_port = 'http://127.0.0.1:2375' for val in [None, '']: - for platform in ['darwin', 'linux2', None]: - assert parse_host(val, platform) == unix_socket - - assert parse_host(val, 'win32') == tcp_port + assert parse_host(val, is_win32=False) == unix_socket + assert parse_host(val, is_win32=True) == tcp_port def test_parse_host_tls(self): host_value = 'myhost.docker.net:3348' @@ -604,13 +604,7 @@ class SplitCommandTest(base.BaseTestCase): def test_split_command_with_unicode(self): - if six.PY2: - self.assertEqual( - split_command(unicode('echo μμ', 'utf-8')), - ['echo', 'μμ'] - ) - else: - self.assertEqual(split_command('echo μμ'), ['echo', 'μμ']) + self.assertEqual(split_command(u'echo μμ'), ['echo', 'μμ']) @pytest.mark.skipif(six.PY3, reason="shlex doesn't support bytes in py3") def test_split_command_with_bytes(self): @@ -794,6 +788,9 @@ def test_single_filename(self): assert self.exclude(['a.py']) == self.all_paths - set(['a.py']) + def test_single_filename_leading_dot_slash(self): + assert self.exclude(['./a.py']) == self.all_paths - set(['a.py']) + # As odd as it sounds, a filename pattern with a trailing slash on the # end *will* result in that file being excluded. def test_single_filename_trailing_slash(self): @@ -823,6 +820,11 @@ def test_single_subdir_single_filename(self): assert self.exclude(['foo/a.py']) == self.all_paths - set(['foo/a.py']) + def test_single_subdir_with_path_traversal(self): + assert self.exclude(['foo/whoops/../a.py']) == self.all_paths - set([ + 'foo/a.py', + ]) + def test_single_subdir_wildcard_filename(self): assert self.exclude(['foo/*.py']) == self.all_paths - set([ 'foo/a.py', 'foo/b.py', diff -Nru python-docker-1.8.0/.travis.yml python-docker-1.9.0/.travis.yml --- python-docker-1.8.0/.travis.yml 2016-04-11 14:18:22.000000000 +0000 +++ python-docker-1.9.0/.travis.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -sudo: false -language: python -python: - - "2.7" -env: - - TOX_ENV=py26 - - TOX_ENV=py27 - - TOX_ENV=py33 - - TOX_ENV=py34 - - TOX_ENV=flake8 -install: - - pip install tox -script: - - tox -e $TOX_ENV