Skip to content

Commit 2af9a0d

Browse files
committed
Add custom Adafruit IO error types and raise them when requests fail.
1 parent 395e3f6 commit 2af9a0d

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

Adafruit_IO/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .client import Client
1+
from .client import Client, AdafruitIOError, RequestError, ThrottlingError

Adafruit_IO/client.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@
33
from urllib3 import connection_from_url
44
from urllib import urlencode, quote
55

6+
7+
class AdafruitIOError(Exception):
8+
"""Base class for all Adafruit IO request failures."""
9+
pass
10+
11+
class RequestError(Exception):
12+
"""General error for a failed Adafruit IO request."""
13+
def __init__(self, response):
14+
super(RequestError, self).__init__("Adafruit IO request failed: {0} {1}".format(
15+
response.status, response.reason))
16+
17+
class ThrottlingError(AdafruitIOError):
18+
"""Too many requests have been made to Adafruit IO in a short period of time.
19+
Reduce the rate of requests and try again later.
20+
"""
21+
def __init__(self):
22+
super(ThrottlingError, self).__init__("Exceeded the limit of Adafruit IO " \
23+
"requests in a short period of time. Please reduce the rate of requests " \
24+
"and try again later.")
25+
626
#fork of ApiClient Class: https://github.com/shazow/apiclient
727
class Client(object):
828
BASE_URL = 'https://io.adafruit.com/'
@@ -21,7 +41,17 @@ def _compose_url(self, path):
2141
def _compose_get_url(self, path, params=None):
2242
return self.BASE_URL + path + '?' + urlencode(params)
2343

44+
def _handle_error(sefl, response):
45+
# Handle explicit errors.
46+
if response.status == 429:
47+
raise ThrottlingError()
48+
# Handle all other errors (400 & 500 level HTTP responses)
49+
elif response.status >= 400:
50+
raise RequestError(response)
51+
# Else do nothing if there was no error.
52+
2453
def _handle_response(self, response):
54+
self._handle_error(response)
2555
return json.loads(response.data)
2656

2757
def _request(self, method, path, params=None):

tests/README.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Adafruit IO Python Client Test README
2+
3+
To run these tests you must have the pytest module installed. You can install
4+
this (assuming you have pip installed) by executing:
5+
sudo pip install pytest
6+
7+
Some tests require a valid Adafruit IO account to run, and they key for this
8+
account is provided in the ADAFRUIT_IO_KEY environment variable. Make sure to
9+
set this envirionment variable before running the tests, for example to run all
10+
the tests with a key execute in this directory:
11+
ADAFRUIT_IO_KEY=my_io_key_value py.test

tests/test_errors.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import os
2+
import time
3+
4+
import pytest
5+
6+
import Adafruit_IO
7+
8+
9+
def _get_client():
10+
"""Return an Adafruit IO client instance configured with the key specified in
11+
the ADAFRUIT_IO_KEY environment variable.
12+
"""
13+
key = os.environ.get('ADAFRUIT_IO_KEY', None)
14+
if key is None:
15+
raise RuntimeError("ADAFRUIT_IO_KEY environment variable must be set with " \
16+
"valid Adafruit IO key to run this test!")
17+
return Adafruit_IO.Client(key)
18+
19+
20+
class TestErrors:
21+
def test_request_error_from_bad_key(self):
22+
io = Adafruit_IO.Client("this is a bad key from a test")
23+
with pytest.raises(Adafruit_IO.RequestError):
24+
io.send("TestStream", 42)
25+
26+
def test_throttling_error_after_6_requests_in_short_period(self):
27+
io = _get_client()
28+
with pytest.raises(Adafruit_IO.ThrottlingError):
29+
for i in range(6):
30+
io.send("TestStream", 42)
31+
time.sleep(0.1) # Small delay to keep from hammering network.

0 commit comments

Comments
 (0)