diff --git a/deepomatic/api/exceptions.py b/deepomatic/api/exceptions.py index 7587ee9..dc1ef71 100644 --- a/deepomatic/api/exceptions.py +++ b/deepomatic/api/exceptions.py @@ -50,6 +50,9 @@ def __init__(self): ############################################################################### class BadStatus(DeepomaticException): + """ + Thrown when HTTP response status_code < 200 or status_code >= 300. + """ def __init__(self, response): self.response = response @@ -68,6 +71,22 @@ def __str__(self): return "Bad status code %s with body %s" % (self.response.status_code, self.response.content) +class ClientError(BadStatus): + """ + Thrown when HTTP response status_code is a 4xx. + The client sent a bad request. + """ + pass + + +class ServerError(BadStatus): + """ + Thrown when HTTP response status_code is a 5xx. + The request seems to be valid but the server couldn't handle it for some reason. + """ + pass + + ############################################################################### class TaskError(DeepomaticException): diff --git a/deepomatic/api/http_helper.py b/deepomatic/api/http_helper.py index 02dd1b8..817e6bf 100644 --- a/deepomatic/api/http_helper.py +++ b/deepomatic/api/http_helper.py @@ -29,7 +29,8 @@ import sys import requests -from deepomatic.api.exceptions import BadStatus, DeepomaticException +from deepomatic.api.exceptions import (BadStatus, ServerError, + ClientError, DeepomaticException) from deepomatic.api.http_retry import HTTPRetry from deepomatic.api.version import __title__, __version__ from requests.structures import CaseInsensitiveDict @@ -256,11 +257,17 @@ def make_request(self, func, resource, params=None, data=None, for file in opened_files: file.close() - if response.status_code == 204: # delete + status_code = response.status_code + if status_code == 204: # delete return None - if response.status_code < 200 or response.status_code >= 300: - raise BadStatus(response) + if status_code < 200 or status_code >= 300: + if status_code >= 400 and status_code < 500: + raise ClientError(response) + elif status_code >= 500 and status_code < 600: + raise ServerError(response) + else: + raise BadStatus(response) if stream: # we asked for a stream, we let the user download it as he wants or it will load everything in RAM diff --git a/tests/test_client.py b/tests/test_client.py index e58e24d..7931691 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -14,7 +14,7 @@ import requests import six from deepomatic.api.client import Client -from deepomatic.api.exceptions import BadStatus, TaskTimeout, HTTPRetryError, TaskRetryError +from deepomatic.api.exceptions import ServerError, ClientError, TaskTimeout, HTTPRetryError, TaskRetryError from deepomatic.api.http_retry import HTTPRetry from deepomatic.api.inputs import ImageInput from deepomatic.api.version import __title__, __version__ @@ -299,6 +299,14 @@ def test_batch_wait(self, client): assert(tasks[pos].pk == success.pk) assert inference_schema(2, 0, 'golden retriever', 0.8) == success['data'] + def test_client_error(self, client): + spec = client.RecognitionSpec.retrieve('imagenet-inception-v3') + with pytest.raises(ClientError) as exc: + spec.inference(inputs=[]) + + assert 400 == exc.value.status_code + assert 'error' in exc.value.json() + class TestClientRetry(object): DEFAULT_TIMEOUT = 2 @@ -355,12 +363,12 @@ def test_no_retry_create_network(self): client = self.get_client_with_retry() # Creating network doesn't retry, we directly get a 502 t = time.time() - with pytest.raises(BadStatus) as exc: + with pytest.raises(ServerError) as exc: client.Network.create(name="My first network", framework='tensorflow-1.x', preprocessing=["useless"], files=["useless"]) - assert 502 == exc.status_code + assert 502 == exc.value.status_code assert time.time() - t < 0.3 def test_retry_task_with_http_errors(self):