Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: hynek/txStatHat
base: 4bf8113964
...
head fork: hynek/txStatHat
compare: 561483bb9d
Checking mergeability… Don't worry, you can still create the pull request.
  • 7 commits
  • 6 files changed
  • 0 commit comments
  • 1 contributor
View
12 HISTORY.rst
@@ -0,0 +1,12 @@
+.. :changelog:
+
+History
+-------
+
+0.2.0 (2012-04-05)
+++++++++++++++++++
+
+* txStatHat now ignores errors by default in order to minimize disruption and
+ boilerplate code due to possible StatHat outages.
+* On the other hand, if one checks for errors, txStatHat also checks the return
+ value of the API and throws a txStatHatApiException on errors.
View
2  MANIFEST.in
@@ -1 +1 @@
-include README.rst LICENSE
+include README.rst LICENSE HISTORY.rst test/*.py
View
30 README.rst
@@ -1,7 +1,7 @@
txStatHat
=========
-A Twisted_ API wrapper for StatHat_.
+A Twisted_ API wrapper for StatHat_’s `EZ API`_.
The usage is as simple as::
@@ -12,7 +12,7 @@ The usage is as simple as::
@inlineCallbacks
def doSomeStats():
- sh = txStatHat('keyOrEmail')
+ sh = txStatHat('ezKeyOrEmail')
yield sh.count('aCounter') # Counts by 1 by default
yield sh.count('anotherCounter', 42)
yield sh.value('aValue', 0.42)
@@ -21,8 +21,30 @@ The usage is as simple as::
reactor.callLater(1, doSomeStats)
reactor.run()
-Depending on the availability of pyOpenSSL, txStatHat uses https for API calls
-if possible.
+The ``ezKeyOrEmail`` is your e-mail address in the beginning, but can be
+changed in the account settings to something more safe. There is no such thing
+as a password.
+
+By default, errors are swallowed silently so disruptions at StatHat don’t lead
+to disruption in your services by accident. To get network exceptions as well
+as API error messages, set ``ignore_errors=False`` when instantiating
+txStatHat. You should only do so if you have really good reasons.
+
+ **Please note**: At the moment, StatHat.com does *not* report an error when
+ an incorrect EZ API key is submitted. Therefore the above example will work
+ without any effect even if you don’t replace the API key.
+
+StatHat.com seems to have generally a similar attitude towards errors as
+txStatHat. They return an OK except if you use the API incorrectly (don’t
+supply an API key for example). The difference is that if ``ignore_errors`` is
+left at the default ``True``, network problems accessing the API are ignored as
+well.
+
+Depending on the availability of pyOpenSSL_, txStatHat uses HTTPS for API calls
+if possible. While there isn’t much real damage an attacker can do to you if
+(s)he hijacks your API key, I strongly suggest to install and use it.
.. _Twisted: http://twistedmatrix.com/
.. _StatHat: http://www.stathat.com/
+.. _`EZ API`: http://www.stathat.com/docs/api
+.. _pyOpenSSL: http://pypi.python.org/pypi/pyOpenSSL/
View
2  setup.py
@@ -9,7 +9,7 @@
setup(
name="txStatHat",
- version='0.1',
+ version='0.2.0',
description="Twisted wrapper for StatHat.com",
long_description=open('README.rst').read(),
classifiers=[
View
29 test/test_txstathat.py
@@ -8,6 +8,7 @@
import os
+from twisted.internet import defer
from twisted.trial import unittest
from twisted.web.client import getPage
@@ -34,20 +35,27 @@ class RemoteTestCase(unittest.TestCase):
TEST_VALUE = b'txSHTestValue'
TEST_CALL = b'txSHTestCall'
+ MSG_OK = b'{"status":200,"msg":"ok"}'
+ MSG_NO_EZKEY = b'{"status":500,"msg":"no ezkey specified"}'
+
def setUp(self):
self.sh = txstathat.txStatHat(EZ_KEY)
+ # some tests tinker with the API URI
+ self.old_API_URI = txstathat.API_URI
+
+ def tearDown(self):
+ txstathat.API_URI = self.old_API_URI
def _add_ok_check(self, deferred):
"""Add callback that checks whether the API returned a success."""
deferred.addCallback(
- lambda s: self.assertEqual(s, b'{"status":200,"msg":"ok"}')
+ lambda s: self.assertEqual(s, self.MSG_OK)
)
return deferred
def test__whether_stathat_is_reachable(self):
d = getPage(txstathat.API_URI)
- d.addCallback(lambda s: self.assertEqual(
- s, b'{"status":500,"msg":"no ezkey specified"}'))
+ d.addCallback(lambda s: self.assertEqual(s, self.MSG_NO_EZKEY))
return d
def test_ssl_detection(self):
@@ -73,3 +81,18 @@ def test_value_float(self):
def test_value_decimal(self):
d = self.sh.value(self.TEST_VALUE, Decimal('0.42'))
return self._add_ok_check(d)
+
+ def test_ignore_errors_really_ignores(self):
+ d1 = txstathat.txStatHat(b'').count(self.TEST_COUNT, 42)
+ d1.addCallback(lambda s: self.assertEqual(s, self.MSG_NO_EZKEY))
+
+ txstathat.API_URI = b'http://invalid.invalid'
+ d2 = self.sh.count(b'does not matter')
+ d2.addCallback(lambda rv: self.assertIsNone(rv))
+
+ return defer.DeferredList([d1, d2])
+
+ def test_check_api_result(self):
+ d = txstathat.txStatHat(b'', ignore_errors=False) \
+ .count(self.TEST_COUNT, 42)
+ return self.assertFailure(d, txstathat.txStatHatApiException)
View
58 txstathat.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
-"""StatHat bindings"""
+"""StatHat bindings for Twisted"""
from __future__ import division, print_function, unicode_literals
+import json
import urllib
from twisted.web.client import getPage
@@ -18,20 +19,43 @@
API_URI = b'http{}://api.stathat.com/ez'.format(b's' if have_ssl else b'')
+class txStatHatApiException(Exception):
+
+ """Raised whenever the API returns anything else than a 200.
+
+ Extends the base Exception class by a status field, the error message of
+ the API is used as the Exception message.
+
+ """
+
+ def __str__(self):
+ return b'status={0.status}, msg="{0.message}"'.format(self)
+
+ def __init__(self, status, msg):
+ self.status = status
+ Exception.__init__(self, msg)
+
+
class txStatHat(object):
- """An API wrapper for StatHat.com."""
+ """An API wrapper for StatHat.com’s EZ API."""
- def __init__(self, ezkey):
+ def __init__(self, ezkey, ignore_errors=True):
"""Initialize a txStatHat instance.
Does no network activity.
+ One usually doesn’t want an outage at StatHat break your application.
+ Therefore, errors while submitting stats are blissfully ignored by
+ default.
+
:param ezkey: your API key, i.e. your e-mail address by default.
+ :param ignore_errors: indicator whether errors should be ignored.
"""
self.default_args = {'ezkey': ezkey}
+ self.ignore_errors = ignore_errors
def _make_call(self, args):
"""Build postdata using ezkey and supplied dict *args* and post it."""
@@ -45,8 +69,30 @@ def _make_call(self, args):
b'Content-Type': b'application/x-www-form-urlencoded'
},
)
+
+ if self.ignore_errors:
+ d.addErrback(self._swallow_errors)
+ else:
+ d.addCallback(self._check_api_result)
+
return d
+ def _swallow_errors(self, failure):
+ """Swallow and ignore any exception."""
+
+ def _check_api_result(self, result):
+ """Check whether the API call was successful."""
+ try:
+ res = json.loads(result)
+ status = res['status']
+ msg = res['msg']
+ except:
+ raise txStatHatApiException(
+ 500,
+ b'Could not parse result: "{}"'.format(result))
+ if status != 200:
+ raise txStatHatApiException(status, msg)
+
def count(self, stat, count=1):
"""Add *count* to *stat*.
@@ -54,6 +100,10 @@ def count(self, stat, count=1):
:param count: the value to add to the counter. 1 by default.
:type count: integer
:rtype: twisted.internet.defer.Deferred
+ :raises txstathat.txStatHatApiException: If ignore_errors is False and
+ StatHat doesn’t return a 200.
+ :raises Twisted’s network related exceptions: If ignore_errors is False
+ Twisted’s network exceptions are let through.
"""
@@ -62,6 +112,8 @@ def count(self, stat, count=1):
def value(self, stat, value):
"""Submit *value* to *stat*.
+ Raises the same exceptions as :py:func:txStatHat.count().
+
:param stat: a StatHat value stat
:param value: the value to submit
:type value: float or decimal.Decimal

No commit comments for this range

Something went wrong with that request. Please try again.