From 63aaee0b3f6cd088a55527ef7f7dc746eb2412bf Mon Sep 17 00:00:00 2001 From: Thomas Stephens Date: Tue, 25 Jul 2017 13:18:57 -0500 Subject: [PATCH 1/2] Update code to work in Python 2.7 as well as 3.6, and add tox.ini to test it in both environments --- circle.yml | 9 ++---- hmacauth/client/authenticated_request.py | 5 ++- hmacauth/digest.py | 11 +++++-- tests/client/test_authenticated_request.py | 36 +++++++++++----------- tests/example_server.py | 5 ++- tox.ini | 12 ++++++++ 6 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 tox.ini diff --git a/circle.yml b/circle.yml index 34f2642..2a8e108 100644 --- a/circle.yml +++ b/circle.yml @@ -1,12 +1,9 @@ machine: - python: - version: 3.6.0 -dependencies: - pre: - - pip install -r dev-requirements.txt + post: + - pyenv global 3.6.1 2.7.12 test: override: - - nosetests + - tox deployment: release: tag: /v[0-9]+(\.[0-9]+)*/ diff --git a/hmacauth/client/authenticated_request.py b/hmacauth/client/authenticated_request.py index 371e41b..5aabe2e 100644 --- a/hmacauth/client/authenticated_request.py +++ b/hmacauth/client/authenticated_request.py @@ -1,6 +1,9 @@ from tornado.httpclient import HTTPRequest -from urllib.parse import urlparse +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse from hmacauth.digest import generate_digest diff --git a/hmacauth/digest.py b/hmacauth/digest.py index a0ac26a..0d11c16 100644 --- a/hmacauth/digest.py +++ b/hmacauth/digest.py @@ -1,16 +1,21 @@ import hmac import hashlib -import urllib.parse + +try: + from urllib.parse import parse_qs, quote +except ImportError: + from urlparse import parse_qs + from urllib import quote def generate_digest(secret, method, path, query, body): - parsed_query = urllib.parse.parse_qs(query, keep_blank_values=True) + parsed_query = parse_qs(query, keep_blank_values=True) canonical_query = [] for key in sorted(parsed_query.keys()): for value in sorted(parsed_query[key]): - canonical_query.append("=".join((key, urllib.parse.quote(value)))) + canonical_query.append("=".join((key, quote(value)))) return hmac.new( secret.encode("utf-8"), diff --git a/tests/client/test_authenticated_request.py b/tests/client/test_authenticated_request.py index 7947bf6..a9e8ff9 100644 --- a/tests/client/test_authenticated_request.py +++ b/tests/client/test_authenticated_request.py @@ -7,8 +7,8 @@ class TestAuthenticatedRequest(BaseHMACTestCase): @gen_test - async def test_signs_post_with_bytestring_body(self): - response = await self.http_client.fetch(authenticated_request( + def test_signs_post_with_bytestring_body(self): + response = yield self.http_client.fetch(authenticated_request( self.get_url("/authorized/argument"), method="POST", body=b"Some Body", @@ -18,8 +18,8 @@ async def test_signs_post_with_bytestring_body(self): self.assertEqual(200, response.code) @gen_test - async def test_signs_post_with_unicode_body(self): - response = await self.http_client.fetch(authenticated_request( + def test_signs_post_with_unicode_body(self): + response = yield self.http_client.fetch(authenticated_request( self.get_url("/authorized/argument"), method="POST", body="Some Body", @@ -29,8 +29,8 @@ async def test_signs_post_with_unicode_body(self): self.assertEqual(200, response.code) @gen_test - async def test_signs_explicit_get(self): - response = await self.http_client.fetch(authenticated_request( + def test_signs_explicit_get(self): + response = yield self.http_client.fetch(authenticated_request( self.get_url("/authorized/argument"), method="GET", hmac_key="correct-key", @@ -39,8 +39,8 @@ async def test_signs_explicit_get(self): self.assertEqual(200, response.code) @gen_test - async def test_signs_implicit_get(self): - response = await self.http_client.fetch(authenticated_request( + def test_signs_implicit_get(self): + response = yield self.http_client.fetch(authenticated_request( self.get_url("/authorized/argument"), hmac_key="correct-key", hmac_secret="secret")) @@ -48,7 +48,7 @@ async def test_signs_implicit_get(self): self.assertEqual(200, response.code) @gen_test - async def test_handles_path_only_url(self): + def test_handles_path_only_url(self): request = authenticated_request( "/authorized/argument", hmac_key="correct-key", @@ -56,13 +56,13 @@ async def test_handles_path_only_url(self): request.url = self.get_url(request.url) - response = await self.http_client.fetch(request) + response = yield self.http_client.fetch(request) self.assertEqual(200, response.code) @gen_test - async def test_includes_existing_headers_in_request(self): - response = await self.http_client.fetch(authenticated_request( + def test_includes_existing_headers_in_request(self): + response = yield self.http_client.fetch(authenticated_request( self.get_url("/authorized/argument"), headers={ "X-Ping": "Pong" @@ -74,8 +74,8 @@ async def test_includes_existing_headers_in_request(self): self.assertEqual("Pong", response.body.decode("utf8")) @gen_test - async def test_signs_url_as_keyword_argument(self): - response = await self.http_client.fetch(authenticated_request( + def test_signs_url_as_keyword_argument(self): + response = yield self.http_client.fetch(authenticated_request( url=self.get_url("/authorized/argument"), hmac_key="correct-key", hmac_secret="secret")) @@ -83,15 +83,15 @@ async def test_signs_url_as_keyword_argument(self): self.assertEqual(200, response.code) @gen_test - async def test_raises_exception_without_url_argument(self): + def test_raises_exception_without_url_argument(self): with self.assertRaises(TypeError): - await self.http_client.fetch(authenticated_request( + yield self.http_client.fetch(authenticated_request( hmac_key="correct-key", hmac_secret="secret")) @gen_test - async def test_normalizes_and_signs_query_arguments(self): - response = await self.http_client.fetch(authenticated_request( + def test_normalizes_and_signs_query_arguments(self): + response = yield self.http_client.fetch(authenticated_request( url=self.get_url("/authorized/argument?bar=value%202&Foo=value%3f1&blank"), hmac_key="correct-key", hmac_secret="secret")) diff --git a/tests/example_server.py b/tests/example_server.py index 69fdb6c..a8cc22a 100644 --- a/tests/example_server.py +++ b/tests/example_server.py @@ -1,12 +1,15 @@ from tornado.testing import AsyncHTTPTestCase from tornado.web import RequestHandler, Application +from tornado import gen from hmacauth.server import hmac_authorized class AuthorizedRoute(RequestHandler): @hmac_authorized - async def get(self, arg): + @gen.coroutine + def get(self, arg): + yield gen.moment self.finish(self.request.headers.get("X-Ping", "")) @hmac_authorized diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..fb858a3 --- /dev/null +++ b/tox.ini @@ -0,0 +1,12 @@ +# Tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py27, py36 + +[testenv] +commands = nosetests +deps = + nose From 19d664c5e549aa76b714de91cc92f1295a3c8639 Mon Sep 17 00:00:00 2001 From: Thomas Stephens Date: Tue, 25 Jul 2017 13:19:48 -0500 Subject: [PATCH 2/2] Bumping version number for Python 2.7 support --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f36a359..007b9c9 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ ] setup(name="ustudio-hmac-tornado", - version="0.2.0", + version="0.3.0", description="Simple HMAC Client/Server authentication for Tornado", url="https://github.com/ustudio/ustudio-hmac-tornado", packages=find_packages(exclude=["tests", "dist"]),