diff --git a/.travis.yml b/.travis.yml index 6b48142f72..86f2a1bfde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,19 @@ sudo: false language: python +cache: pip python: - - "3.5" -env: - - TOX_ENV=py27 - - TOX_ENV=py33 - - TOX_ENV=py34 - - TOX_ENV=py35 - - TOX_ENV=flake8 + - 2.7 + - 3.3 + - 3.4 + - 3.5 + - 3.6 + - pypy + - pypy3 +matrix: + allow_failures: + - python: pypy + - python: pypy3 install: - - pip install tox + - travis_retry pip install tox-travis script: - - tox -e $TOX_ENV + - tox diff --git a/Dockerfile-pypy b/Dockerfile-pypy new file mode 100644 index 0000000000..7c26e8a80e --- /dev/null +++ b/Dockerfile-pypy @@ -0,0 +1,14 @@ +FROM pypy:2 +MAINTAINER Joffrey F + +RUN mkdir /home/docker-py +WORKDIR /home/docker-py + +ADD requirements.txt /home/docker-py/requirements.txt +RUN pip install -r requirements.txt + +ADD test-requirements.txt /home/docker-py/test-requirements.txt +RUN pip install -r test-requirements.txt + +ADD . /home/docker-py +RUN pip install . diff --git a/Dockerfile-pypy3 b/Dockerfile-pypy3 new file mode 100644 index 0000000000..8826f7e4cb --- /dev/null +++ b/Dockerfile-pypy3 @@ -0,0 +1,14 @@ +FROM pypy:3 +MAINTAINER Joffrey F + +RUN mkdir /home/docker-py +WORKDIR /home/docker-py + +ADD requirements.txt /home/docker-py/requirements.txt +RUN pip install -r requirements.txt + +ADD test-requirements.txt /home/docker-py/test-requirements.txt +RUN pip install -r test-requirements.txt + +ADD . /home/docker-py +RUN pip install . diff --git a/Makefile b/Makefile index e8fa711aba..c8f25476e5 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,14 @@ build: build-py3: docker build -t docker-sdk-python3 -f Dockerfile-py3 . +.PHONY: build-pypy +build-pypy: + docker build -t docker-sdk-pypy -f Dockerfile-pypy . + +.PHONY: build-pypy3 +build-pypy3: + docker build -t docker-sdk-pypy3 -f Dockerfile-pypy3 . + .PHONY: build-docs build-docs: docker build -t docker-sdk-python-docs -f Dockerfile-docs . @@ -23,7 +31,7 @@ build-dind-certs: docker build -t dpy-dind-certs -f tests/Dockerfile-dind-certs . .PHONY: test -test: flake8 unit-test unit-test-py3 integration-dind integration-dind-ssl +test: flake8 unit-test unit-test-py3 unit-test-pypy unit-test-pypy3 integration-dind integration-dind-ssl .PHONY: unit-test unit-test: build @@ -33,6 +41,14 @@ unit-test: build unit-test-py3: build-py3 docker run --rm docker-sdk-python3 py.test tests/unit +.PHONY: unit-test-pypy +unit-test-pypy: build-pypy + docker run docker-sdk-pypy py.test tests/unit + +.PHONY: unit-test-pypy3 +unit-test-pypy3: build-pypy3 + docker run docker-sdk-pypy3 py.test tests/unit + .PHONY: integration-test integration-test: build docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python py.test tests/integration/${file} @@ -41,6 +57,14 @@ integration-test: build integration-test-py3: build-py3 docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python3 py.test tests/integration/${file} +.PHONY: integration-test-pypy +integration-test-pypy: build-pypy + docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-pypy py.test tests/integration/${file} + +.PHONY: integration-test-pypy3 +integration-test-pypy3: build-pypy3 + docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-pypy3 py.test tests/integration/${file} + .PHONY: integration-dind integration-dind: build build-py3 docker rm -vf dpy-dind || : diff --git a/README.md b/README.md index 38963b325c..55fec226d1 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://travis-ci.org/docker/docker-py.svg?branch=master)](https://travis-ci.org/docker/docker-py) A Python library for the Docker Engine API. It lets you do anything the `docker` command does, but from within Python apps – run containers, manage containers, manage Swarms, etc. +It supports Python 2 and Python 3 implemented by CPython and PyPy. ## Installation diff --git a/docker/utils/ports.py b/docker/utils/ports.py index e2aeb8cc52..3708958d4e 100644 --- a/docker/utils/ports.py +++ b/docker/utils/ports.py @@ -67,6 +67,8 @@ def split_port(port): if len(parts) == 1: internal_port, = parts + if not internal_port: + _raise_invalid_port(port) return to_port_range(internal_port), None if len(parts) == 2: external_port, internal_port = parts diff --git a/setup.py b/setup.py index 9fc4ad66e9..92f211f552 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,9 @@ 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', 'License :: OSI Approved :: Apache Software License', ], diff --git a/tests/integration/api_client_test.py b/tests/integration/api_client_test.py index 1fef783b4d..cc641582c0 100644 --- a/tests/integration/api_client_test.py +++ b/tests/integration/api_client_test.py @@ -8,7 +8,7 @@ import docker from docker.utils import kwargs_from_env -from .base import BaseAPIIntegrationTest, BUSYBOX +from .base import BaseAPIIntegrationTest class InformationTest(BaseAPIIntegrationTest): @@ -25,48 +25,6 @@ def test_info(self): self.assertIn('Debug', res) -class LinkTest(BaseAPIIntegrationTest): - def test_remove_link(self): - # Create containers - container1 = self.client.create_container( - BUSYBOX, 'cat', detach=True, stdin_open=True - ) - container1_id = container1['Id'] - self.tmp_containers.append(container1_id) - self.client.start(container1_id) - - # Create Link - # we don't want the first / - link_path = self.client.inspect_container(container1_id)['Name'][1:] - link_alias = 'mylink' - - container2 = self.client.create_container( - BUSYBOX, 'cat', host_config=self.client.create_host_config( - links={link_path: link_alias} - ) - ) - container2_id = container2['Id'] - self.tmp_containers.append(container2_id) - self.client.start(container2_id) - - # Remove link - linked_name = self.client.inspect_container(container2_id)['Name'][1:] - link_name = '%s/%s' % (linked_name, link_alias) - self.client.remove_container(link_name, link=True) - - # Link is gone - containers = self.client.containers(all=True) - retrieved = [x for x in containers if link_name in x['Names']] - self.assertEqual(len(retrieved), 0) - - # Containers are still there - retrieved = [ - x for x in containers if x['Id'].startswith(container1_id) or - x['Id'].startswith(container2_id) - ] - self.assertEqual(len(retrieved), 2) - - class LoadConfigTest(BaseAPIIntegrationTest): def test_load_legacy_config(self): folder = tempfile.mkdtemp() diff --git a/tests/integration/api_container_test.py b/tests/integration/api_container_test.py index f539697e9e..9514261512 100644 --- a/tests/integration/api_container_test.py +++ b/tests/integration/api_container_test.py @@ -1253,3 +1253,45 @@ def test_container_cpuset(self): self.client.start(container) inspect_data = self.client.inspect_container(container) self.assertEqual(inspect_data['HostConfig']['CpusetCpus'], cpuset_cpus) + + +class LinkTest(BaseAPIIntegrationTest): + def test_remove_link(self): + # Create containers + container1 = self.client.create_container( + BUSYBOX, 'cat', detach=True, stdin_open=True + ) + container1_id = container1['Id'] + self.tmp_containers.append(container1_id) + self.client.start(container1_id) + + # Create Link + # we don't want the first / + link_path = self.client.inspect_container(container1_id)['Name'][1:] + link_alias = 'mylink' + + container2 = self.client.create_container( + BUSYBOX, 'cat', host_config=self.client.create_host_config( + links={link_path: link_alias} + ) + ) + container2_id = container2['Id'] + self.tmp_containers.append(container2_id) + self.client.start(container2_id) + + # Remove link + linked_name = self.client.inspect_container(container2_id)['Name'][1:] + link_name = '%s/%s' % (linked_name, link_alias) + self.client.remove_container(link_name, link=True) + + # Link is gone + containers = self.client.containers(all=True) + retrieved = [x for x in containers if link_name in x['Names']] + self.assertEqual(len(retrieved), 0) + + # Containers are still there + retrieved = [ + x for x in containers if x['Id'].startswith(container1_id) or + x['Id'].startswith(container2_id) + ] + self.assertEqual(len(retrieved), 2) diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py index ed84b3a1fd..4c3c3664a2 100644 --- a/tests/unit/utils_test.py +++ b/tests/unit/utils_test.py @@ -578,6 +578,9 @@ def test_with_no_container_port(self): self.assertRaises(ValueError, lambda: split_port("localhost:80:")) + def test_split_port_empty_string(self): + self.assertRaises(ValueError, lambda: split_port("")) + def test_build_port_bindings_with_one_port(self): port_bindings = build_port_bindings(["127.0.0.1:1000:1000"]) self.assertEqual(port_bindings["1000"], [("127.0.0.1", "1000")]) diff --git a/tox.ini b/tox.ini index 1a41c6edac..a1eba5288f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] -envlist = py27, py33, py34, py35, flake8 -skipsdist=True +envlist = py27, py33, py34, py35, py36, pypy, pypy3, flake8 +skipsdist = True [testenv] -usedevelop=True +usedevelop = True commands = py.test --cov=docker {posargs:tests/unit} deps =