diff --git a/docker/__init__.py b/docker/__init__.py index 5388e728..e10a5761 100644 --- a/docker/__init__.py +++ b/docker/__init__.py @@ -15,4 +15,4 @@ __title__ = 'docker-py' __version__ = '0.3.0' -from .client import Client, APIError # flake8: noqa +from .client import Client # flake8: noqa diff --git a/docker/auth/auth.py b/docker/auth/auth.py index d62705f0..871996de 100644 --- a/docker/auth/auth.py +++ b/docker/auth/auth.py @@ -20,6 +20,7 @@ import os import six from ..utils import utils +from docker import errors INDEX_URL = 'https://index.docker.io/v1/' DOCKER_CONFIG_FILENAME = '.dockercfg' @@ -45,18 +46,19 @@ def expand_registry_url(hostname): def resolve_repository_name(repo_name): if '://' in repo_name: - raise ValueError('Repository name cannot contain a ' - 'scheme ({0})'.format(repo_name)) + raise errors.InvalidRepository( + 'Repository name cannot contain a scheme ({0})'.format(repo_name)) parts = repo_name.split('/', 1) if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost': # This is a docker index repo (ex: foo/bar or ubuntu) return INDEX_URL, repo_name if len(parts) < 2: - raise ValueError('Invalid repository name ({0})'.format(repo_name)) + raise errors.InvalidRepository( + 'Invalid repository name ({0})'.format(repo_name)) if 'index.docker.io' in parts[0]: - raise ValueError('Invalid repository name,' - 'try "{0}" instead'.format(parts[1])) + raise errors.InvalidRepository( + 'Invalid repository name, try "{0}" instead'.format(parts[1])) return expand_registry_url(parts[0]), parts[1] @@ -147,7 +149,8 @@ def load_config(root=None): data.append(line.strip().split(' = ')[1]) if len(data) < 2: # Not enough data - raise Exception('Invalid or empty configuration file!') + raise errors.InvalidConfigFile( + 'Invalid or empty configuration file!') username, password = decode_auth(data[0]) conf[INDEX_URL] = { diff --git a/docker/client.py b/docker/client.py index 5dfa889d..813902d8 100644 --- a/docker/client.py +++ b/docker/client.py @@ -24,6 +24,7 @@ import six from .auth import auth from .unixconn import unixconn from .utils import utils +from docker import errors if not six.PY3: import websocket @@ -33,41 +34,6 @@ DEFAULT_TIMEOUT_SECONDS = 60 STREAM_HEADER_SIZE_BYTES = 8 -class APIError(requests.exceptions.HTTPError): - def __init__(self, message, response, explanation=None): - # requests 1.2 supports response as a keyword argument, but - # requests 1.1 doesn't - super(APIError, self).__init__(message) - self.response = response - - self.explanation = explanation - - if self.explanation is None and response.content: - self.explanation = response.content.strip() - - def __str__(self): - message = super(APIError, self).__str__() - - if self.is_client_error(): - message = '%s Client Error: %s' % ( - self.response.status_code, self.response.reason) - - elif self.is_server_error(): - message = '%s Server Error: %s' % ( - self.response.status_code, self.response.reason) - - if self.explanation: - message = '%s ("%s")' % (message, self.explanation) - - return message - - def is_client_error(self): - return 400 <= self.response.status_code < 500 - - def is_server_error(self): - return 500 <= self.response.status_code < 600 - - class Client(requests.Session): def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION, timeout=DEFAULT_TIMEOUT_SECONDS): @@ -112,7 +78,7 @@ class Client(requests.Session): try: response.raise_for_status() except requests.exceptions.HTTPError as e: - raise APIError(e, response, explanation=explanation) + raise errors.APIError(e, response, explanation=explanation) def _result(self, response, json=False, binary=False): assert not (json and binary) @@ -341,7 +307,7 @@ class Client(requests.Session): nocache=False, rm=False, stream=False, timeout=None): remote = context = headers = None if path is None and fileobj is None: - raise Exception("Either path or fileobj needs to be provided.") + raise TypeError("Either path or fileobj needs to be provided.") if fileobj is not None: context = utils.mkbuildcontext(fileobj) diff --git a/docker/errors.py b/docker/errors.py new file mode 100644 index 00000000..9aad700d --- /dev/null +++ b/docker/errors.py @@ -0,0 +1,61 @@ +# Copyright 2014 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 requests + + +class APIError(requests.exceptions.HTTPError): + def __init__(self, message, response, explanation=None): + # requests 1.2 supports response as a keyword argument, but + # requests 1.1 doesn't + super(APIError, self).__init__(message) + self.response = response + + self.explanation = explanation + + if self.explanation is None and response.content: + self.explanation = response.content.strip() + + def __str__(self): + message = super(APIError, self).__str__() + + if self.is_client_error(): + message = '%s Client Error: %s' % ( + self.response.status_code, self.response.reason) + + elif self.is_server_error(): + message = '%s Server Error: %s' % ( + self.response.status_code, self.response.reason) + + if self.explanation: + message = '%s ("%s")' % (message, self.explanation) + + return message + + def is_client_error(self): + return 400 <= self.response.status_code < 500 + + def is_server_error(self): + return 500 <= self.response.status_code < 600 + + +class DockerException(Exception): + pass + + +class InvalidRepository(DockerException): + pass + + +class InvalidConfigFile(DockerException): + pass diff --git a/tests/integration_test.py b/tests/integration_test.py index c1cd1f42..42dcf458 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -41,13 +41,13 @@ class BaseTestCase(unittest.TestCase): for img in self.tmp_imgs: try: self.client.remove_image(img) - except docker.APIError: + except docker.errors.APIError: pass for container in self.tmp_containers: try: self.client.stop(container, timeout=1) self.client.remove_container(container) - except docker.APIError: + except docker.errors.APIError: pass ######################### @@ -641,7 +641,7 @@ class TestPull(BaseTestCase): try: self.client.remove_image('joffrey/test001') self.client.remove_image('376968a23351') - except docker.APIError: + except docker.errors.APIError: pass info = self.client.info() self.assertIn('Images', info) @@ -660,7 +660,7 @@ class TestPullStream(BaseTestCase): try: self.client.remove_image('joffrey/test001') self.client.remove_image('376968a23351') - except docker.APIError: + except docker.errors.APIError: pass info = self.client.info() self.assertIn('Images', info)