[Python-modules-commits] [python-docker] 02/13: Import python-docker_1.9.0.orig.tar.gz
Ondřej Nový
onovy at moszumanska.debian.org
Thu Sep 8 12:38:53 UTC 2016
This is an automated email from the git hooks/post-receive script.
onovy pushed a commit to branch master
in repository python-docker.
commit 49556cb01423a89a6d2ce7a58b1f5cb64dde0ff3
Author: Ondřej Nový <onovy at debian.org>
Date: Thu Sep 8 14:22:11 2016 +0200
Import python-docker_1.9.0.orig.tar.gz
---
MANIFEST.in | 2 +
PKG-INFO | 5 +-
README.md | 8 +-
README.rst | 37 +++++
docker/__init__.py | 2 +-
docker/api/build.py | 11 +-
docker/api/container.py | 46 ++++-
docker/api/daemon.py | 4 +-
docker/api/image.py | 8 +-
docker/api/network.py | 25 ++-
docker/auth/auth.py | 15 +-
docker/client.py | 67 ++++++--
docker/constants.py | 9 +-
docker/ssladapter/ssladapter.py | 9 +
docker/tls.py | 7 +-
docker/transport/__init__.py | 6 +
docker/transport/npipeconn.py | 80 +++++++++
docker/transport/npipesocket.py | 191 +++++++++++++++++++++
docker/{unixconn => transport}/unixconn.py | 8 +-
docker/unixconn/__init__.py | 1 -
docker/utils/utils.py | 258 +++++++++++++++++++++--------
docker/version.py | 2 +-
docker_py.egg-info/PKG-INFO | 5 +-
docker_py.egg-info/SOURCES.txt | 15 +-
docker_py.egg-info/pbr.json | 1 -
docker_py.egg-info/requires.txt | 6 +
requirements.txt | 2 +
setup.py | 17 +-
test-requirements.txt | 4 +-
tests/integration/api_test.py | 2 +-
tests/integration/build_test.py | 28 ++++
tests/integration/container_test.py | 92 +++++++++-
tests/integration/image_test.py | 14 ++
tests/integration/network_test.py | 141 +++++++++++++++-
tests/unit/api_test.py | 30 ++++
tests/unit/auth_test.py | 5 +-
tests/unit/client_test.py | 70 ++++++++
tests/unit/container_test.py | 129 ++++++++++++++-
tests/unit/fake_api.py | 7 +
tests/unit/image_test.py | 3 +-
tests/unit/ssladapter_test.py | 79 +++++++++
tests/unit/testdata/certs/ca.pem | 0
tests/unit/testdata/certs/cert.pem | 0
tests/unit/testdata/certs/key.pem | 0
tests/unit/utils_test.py | 174 +++++++++----------
45 files changed, 1391 insertions(+), 234 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index ab64732..ee6cdbb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,7 @@
include test-requirements.txt
include requirements.txt
include README.md
+include README.rst
include LICENSE
recursive-include tests *.py
+recursive-include tests/unit/testdata *
diff --git a/PKG-INFO b/PKG-INFO
index fd5c3e7..79c1598 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: docker-py
-Version: 1.7.2
+Version: 1.9.0
Summary: Python client for Docker.
Home-page: https://github.com/docker/docker-py/
Author: UNKNOWN
@@ -13,9 +13,12 @@ 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 --git a/README.md b/README.md
index 385193a..bdec785 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,12 @@ docker-py
[](https://travis-ci.org/docker/docker-py)
-An API client for docker written in Python
+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
------------
-Our latest stable is always available on PyPi.
+The latest stable version is always available on PyPi.
pip install docker-py
@@ -17,8 +17,8 @@ Documentation
[](https://readthedocs.org/projects/docker-py/?badge=latest)
-Full documentation is hosted on [ReadTheDocs](http://docker-py.readthedocs.org/en/latest/).
-Sources are available in the `docs/` directory.
+[Read the full documentation here](https://docker-py.readthedocs.io/en/latest/).
+The source is available in the `docs/` directory.
License
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..757b82c
--- /dev/null
+++ b/README.rst
@@ -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 <https://docker-py.readthedocs.io/en/latest/>`__. 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 --git a/docker/__init__.py b/docker/__init__.py
index 3844c81..84d0734 100644
--- a/docker/__init__.py
+++ b/docker/__init__.py
@@ -17,4 +17,4 @@ from .version import version, version_info
__version__ = version
__title__ = 'docker-py'
-from .client import Client, AutoVersionClient # flake8: noqa
+from .client import Client, AutoVersionClient, from_env # flake8: noqa
diff --git a/docker/api/build.py b/docker/api/build.py
index 6bfaba1..971a50e 100644
--- a/docker/api/build.py
+++ b/docker/api/build.py
@@ -17,11 +17,15 @@ class BuildApiMixin(object):
nocache=False, rm=False, stream=False, timeout=None,
custom_context=False, encoding=None, pull=False,
forcerm=False, dockerfile=None, container_limits=None,
- decode=False, buildargs=None):
+ decode=False, buildargs=None, gzip=False):
remote = context = headers = None
container_limits = container_limits or {}
if path is None and fileobj is None:
raise TypeError("Either path or fileobj needs to be provided.")
+ if gzip and encoding is not None:
+ raise errors.DockerException(
+ 'Can not use custom encoding if gzip is enabled'
+ )
for key in container_limits.keys():
if key not in constants.CONTAINER_LIMITS_KEYS:
@@ -46,7 +50,10 @@ class BuildApiMixin(object):
if os.path.exists(dockerignore):
with open(dockerignore, 'r') as f:
exclude = list(filter(bool, f.read().splitlines()))
- context = utils.tar(path, exclude=exclude, dockerfile=dockerfile)
+ context = utils.tar(
+ path, exclude=exclude, dockerfile=dockerfile, gzip=gzip
+ )
+ encoding = 'gzip' if gzip else encoding
if utils.compare_version('1.8', self._version) >= 0:
stream = True
diff --git a/docker/api/container.py b/docker/api/container.py
index ceac173..9cc14db 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -40,13 +40,14 @@ class ContainerApiMixin(object):
@utils.check_resource
def commit(self, container, repository=None, tag=None, message=None,
- author=None, conf=None):
+ author=None, changes=None, conf=None):
params = {
'container': container,
'repo': repository,
'tag': tag,
'comment': message,
- 'author': author
+ 'author': author,
+ 'changes': changes
}
u = self._url("/commit")
return self._result(self._post_json(u, data=conf, params=params),
@@ -186,6 +187,8 @@ class ContainerApiMixin(object):
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)
@@ -193,12 +196,14 @@ class ContainerApiMixin(object):
@utils.check_resource
def logs(self, container, stdout=True, stderr=True, stream=False,
- timestamps=False, tail='all', since=None):
+ timestamps=False, tail='all', since=None, follow=None):
if utils.compare_version('1.11', self._version) >= 0:
+ if follow is None:
+ follow = stream
params = {'stderr': stderr and 1 or 0,
'stdout': stdout and 1 or 0,
'timestamps': timestamps and 1 or 0,
- 'follow': stream and 1 or 0,
+ 'follow': follow and 1 or 0,
}
if utils.compare_version('1.13', self._version) >= 0:
if tail != 'all' and (not isinstance(tail, int) or tail < 0):
@@ -396,6 +401,39 @@ class ContainerApiMixin(object):
res = self._post(url)
self._raise_for_status(res)
+ @utils.minimum_version('1.22')
+ @utils.check_resource
+ def update_container(
+ self, container, blkio_weight=None, cpu_period=None, cpu_quota=None,
+ cpu_shares=None, cpuset_cpus=None, cpuset_mems=None, mem_limit=None,
+ mem_reservation=None, memswap_limit=None, kernel_memory=None
+ ):
+ url = self._url('/containers/{0}/update', container)
+ data = {}
+ if blkio_weight:
+ data['BlkioWeight'] = blkio_weight
+ if cpu_period:
+ data['CpuPeriod'] = cpu_period
+ if cpu_shares:
+ data['CpuShares'] = cpu_shares
+ if cpu_quota:
+ data['CpuQuota'] = cpu_quota
+ if cpuset_cpus:
+ data['CpusetCpus'] = cpuset_cpus
+ if cpuset_mems:
+ data['CpusetMems'] = cpuset_mems
+ if mem_limit:
+ data['Memory'] = utils.parse_bytes(mem_limit)
+ if mem_reservation:
+ data['MemoryReservation'] = utils.parse_bytes(mem_reservation)
+ if memswap_limit:
+ data['MemorySwap'] = utils.parse_bytes(memswap_limit)
+ if kernel_memory:
+ data['KernelMemory'] = utils.parse_bytes(kernel_memory)
+
+ res = self._post_json(url, data=data)
+ return self._result(res, True)
+
@utils.check_resource
def wait(self, container, timeout=None):
url = self._url("/containers/{0}/wait", container)
diff --git a/docker/api/daemon.py b/docker/api/daemon.py
index a149e5e..9ebe73c 100644
--- a/docker/api/daemon.py
+++ b/docker/api/daemon.py
@@ -49,8 +49,6 @@ class DaemonApiMixin(object):
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 @@ class DaemonApiMixin(object):
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 --git a/docker/api/image.py b/docker/api/image.py
index 8493b38..3e66347 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -148,7 +148,7 @@ class ImageApiMixin(object):
self._raise_for_status(res)
def pull(self, repository, tag=None, stream=False,
- insecure_registry=False, auth_config=None):
+ insecure_registry=False, auth_config=None, decode=False):
if insecure_registry:
warnings.warn(
INSECURE_REGISTRY_DEPRECATION_WARNING.format('pull()'),
@@ -200,12 +200,12 @@ class ImageApiMixin(object):
self._raise_for_status(response)
if stream:
- return self._stream_helper(response)
+ return self._stream_helper(response, decode=decode)
return self._result(response)
def push(self, repository, tag=None, stream=False,
- insecure_registry=False):
+ insecure_registry=False, decode=False):
if insecure_registry:
warnings.warn(
INSECURE_REGISTRY_DEPRECATION_WARNING.format('push()'),
@@ -241,7 +241,7 @@ class ImageApiMixin(object):
self._raise_for_status(response)
if stream:
- return self._stream_helper(response)
+ return self._stream_helper(response, decode=decode)
return self._result(response)
diff --git a/docker/api/network.py b/docker/api/network.py
index d9a6128..a35f0a4 100644
--- a/docker/api/network.py
+++ b/docker/api/network.py
@@ -1,6 +1,8 @@
import json
-from ..utils import check_resource, minimum_version, normalize_links
+from ..errors import InvalidVersion
+from ..utils import check_resource, minimum_version
+from ..utils import version_lt
class NetworkApiMixin(object):
@@ -19,7 +21,8 @@ class NetworkApiMixin(object):
return self._result(res, json=True)
@minimum_version('1.21')
- def create_network(self, name, driver=None, options=None, ipam=None):
+ def create_network(self, name, driver=None, options=None, ipam=None,
+ check_duplicate=None, internal=False):
if options is not None and not isinstance(options, dict):
raise TypeError('options must be a dictionary')
@@ -28,7 +31,15 @@ class NetworkApiMixin(object):
'Driver': driver,
'Options': options,
'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)
@@ -48,14 +59,16 @@ class NetworkApiMixin(object):
@check_resource
@minimum_version('1.21')
def connect_container_to_network(self, container, net_id,
+ ipv4_address=None, ipv6_address=None,
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
+ ),
}
+
url = self._url("/networks/{0}/connect", net_id)
res = self._post_json(url, data=data)
self._raise_for_status(res)
diff --git a/docker/auth/auth.py b/docker/auth/auth.py
index eedb794..d23e6f3 100644
--- a/docker/auth/auth.py
+++ b/docker/auth/auth.py
@@ -117,7 +117,7 @@ def parse_auth(entries, raise_on_error=False):
conf = {}
for registry, entry in six.iteritems(entries):
- if not (isinstance(entry, dict) and 'auth' in entry):
+ if not isinstance(entry, dict):
log.debug(
'Config entry for key {0} is not auth config'.format(registry)
)
@@ -130,6 +130,16 @@ def parse_auth(entries, raise_on_error=False):
'Invalid configuration for registry {0}'.format(registry)
)
return {}
+ if 'auth' not in entry:
+ # Starting with engine v1.11 (API 1.23), an empty dictionary is
+ # a valid value in the auths config.
+ # https://github.com/docker/compose/issues/3265
+ log.debug(
+ 'Auth data for {0} is absent. Client might be using a '
+ 'credentials store instead.'
+ )
+ return {}
+
username, password = decode_auth(entry['auth'])
log.debug(
'Found entry (registry={0}, username={1})'
@@ -189,6 +199,9 @@ def load_config(config_path=None):
if data.get('HttpHeaders'):
log.debug("Found 'HttpHeaders' section")
res.update({'HttpHeaders': data['HttpHeaders']})
+ if data.get('credsStore'):
+ log.debug("Found 'credsStore' section")
+ res.update({'credsStore': data['credsStore']})
if res:
return res
else:
diff --git a/docker/client.py b/docker/client.py
index 7d1f7c4..81e9de9 100644
--- a/docker/client.py
+++ b/docker/client.py
@@ -14,7 +14,6 @@
import json
import struct
-import sys
import requests
import requests.exceptions
@@ -26,10 +25,18 @@ from . import api
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
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):
+ return Client.from_env(**kwargs)
class Client(
@@ -42,7 +49,8 @@ class Client(
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:
@@ -52,14 +60,30 @@ class Client(
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):
@@ -84,6 +108,10 @@ class Client(
)
)
+ @classmethod
+ def from_env(cls, **kwargs):
+ return cls(**kwargs_from_env(**kwargs))
+
def _retrieve_server_version(self):
try:
return self.version(api_version=False)["ApiVersion"]
@@ -283,14 +311,29 @@ class Client(
""" 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 --git a/docker/constants.py b/docker/constants.py
index 0627ba0..904d50e 100644
--- a/docker/constants.py
+++ b/docker/constants.py
@@ -1,4 +1,7 @@
-DEFAULT_DOCKER_API_VERSION = '1.21'
+import sys
+from .version import version
+
+DEFAULT_DOCKER_API_VERSION = '1.22'
DEFAULT_TIMEOUT_SECONDS = 60
STREAM_HEADER_SIZE_BYTES = 8
CONTAINER_LIMITS_KEYS = [
@@ -8,3 +11,7 @@ CONTAINER_LIMITS_KEYS = [
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 --git a/docker/ssladapter/ssladapter.py b/docker/ssladapter/ssladapter.py
index 5b43aa2..e17dfad 100644
--- a/docker/ssladapter/ssladapter.py
+++ b/docker/ssladapter/ssladapter.py
@@ -2,6 +2,8 @@
https://lukasa.co.uk/2013/01/Choosing_SSL_Version_In_Requests/
https://github.com/kennethreitz/requests/pull/799
"""
+import sys
+
from distutils.version import StrictVersion
from requests.adapters import HTTPAdapter
@@ -10,8 +12,15 @@ try:
except ImportError:
import urllib3
+
PoolManager = urllib3.poolmanager.PoolManager
+# 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 backports.ssl_match_hostname import match_hostname
+ urllib3.connection.match_hostname = match_hostname
+
class SSLAdapter(HTTPAdapter):
'''An HTTPS Transport Adapter that uses an arbitrary SSL version.'''
diff --git a/docker/tls.py b/docker/tls.py
index 83b0ff7..7abfa60 100644
--- a/docker/tls.py
+++ b/docker/tls.py
@@ -1,4 +1,5 @@
import os
+import ssl
from . import errors
from .ssladapter import ssladapter
@@ -19,10 +20,14 @@ class TLSConfig(object):
# here, but also disable any public/default CA pool verification by
# leaving tls_verify=False
- self.ssl_version = ssl_version
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint
+ # TLS v1.0 seems to be the safest default; SSLv23 fails in mysterious
+ # ways: https://github.com/docker/docker-py/issues/963
+
+ self.ssl_version = ssl_version or ssl.PROTOCOL_TLSv1
+
# "tls" and "tls_verify" must have both or neither cert/key files
# In either case, Alert the user when both are expected, but any are
# missing.
diff --git a/docker/transport/__init__.py b/docker/transport/__init__.py
new file mode 100644
index 0000000..d647483
--- /dev/null
+++ b/docker/transport/__init__.py
@@ -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 --git a/docker/transport/npipeconn.py b/docker/transport/npipeconn.py
new file mode 100644
index 0000000..736ddf6
--- /dev/null
+++ b/docker/transport/npipeconn.py
@@ -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 --git a/docker/transport/npipesocket.py b/docker/transport/npipesocket.py
new file mode 100644
index 0000000..35418ef
--- /dev/null
+++ b/docker/transport/npipesocket.py
@@ -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 --git a/docker/unixconn/unixconn.py b/docker/transport/unixconn.py
similarity index 93%
rename from docker/unixconn/unixconn.py
rename to docker/transport/unixconn.py
index d7e249e..f4d83ef 100644
--- a/docker/unixconn/unixconn.py
+++ b/docker/transport/unixconn.py
@@ -30,7 +30,9 @@ RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer
class UnixHTTPConnection(httplib.HTTPConnection, object):
def __init__(self, base_url, unix_socket, timeout=60):
- httplib.HTTPConnection.__init__(self, 'localhost', timeout=timeout)
+ super(UnixHTTPConnection, self).__init__(
+ 'localhost', timeout=timeout
+ )
self.base_url = base_url
self.unix_socket = unix_socket
self.timeout = timeout
@@ -44,8 +46,8 @@ class UnixHTTPConnection(httplib.HTTPConnection, object):
class UnixHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
def __init__(self, base_url, socket_path, timeout=60):
- urllib3.connectionpool.HTTPConnectionPool.__init__(
- self, 'localhost', timeout=timeout
+ super(UnixHTTPConnectionPool, self).__init__(
+ 'localhost', timeout=timeout
)
self.base_url = base_url
self.socket_path = socket_path
diff --git a/docker/unixconn/__init__.py b/docker/unixconn/__init__.py
deleted file mode 100644
index 53711fc..0000000
--- a/docker/unixconn/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .unixconn import UnixAdapter # flake8: noqa
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 6fcf037..2ef8ef0 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -91,10 +91,10 @@ def decode_json_header(header):
return json.loads(data)
-def tar(path, exclude=None, dockerfile=None, fileobj=None):
+def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False):
if not fileobj:
fileobj = tempfile.NamedTemporaryFile()
- t = tarfile.open(mode='w', fileobj=fileobj)
+ t = tarfile.open(mode='w:gz' if gzip else 'w', fileobj=fileobj)
root = os.path.abspath(path)
... 1714 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-docker.git
More information about the Python-modules-commits
mailing list