Browse files

refactored method decorator to be class based

  • Loading branch information...
1 parent cb29942 commit 05822707b165922d48c531eae58fd37f10d17a76 @imtwebhosting imtwebhosting committed Apr 20, 2012
Showing with 80 additions and 32 deletions.
  1. +66 −32 apyclient.py
  2. +14 −0 test_apyclient.py
View
98 apyclient.py
@@ -54,49 +54,83 @@ def content(self):
return self._content
-def api_request(endpoint, method="GET", timeout=socket._GLOBAL_DEFAULT_TIMEOUT, response_class=None):
+class APIRequest(object):
"""
- Decorator to turn method into api call.
+ API method decorator to turn method into easy API call.
Assumes parent class has "HOST_NAME" defined.
+ """
- Check tests / docs for usage.
-
- :param endpoint:
- URL endpoint for request.
-
- :param method:
- HTTP method for request.
-
- :param timeout:
- Timeout in seconds.
+ def __init__(self, endpoint, method="GET", timeout=socket._GLOBAL_DEFAULT_TIMEOUT, response_class=None):
+ """
+ :param endpoint:
+ URL endpoint for request.
+ :param method:
+ HTTP method for request.
+ :param timeout:
+ Timeout in seconds.
+ :param response_class:
+ Response class to wrap response in. If not provided will use standard response from urlopen,
+ or standard HttpError if received.
+ """
+ self.endpoint = endpoint
+ self.method = method
+ self.timeout = timeout
+ self.response_class = response_class
- :param response_class:
- Response class to wrap response in. If not provided will use standard response from urlopen,
- or standard HttpError if received.
- """
+ def __call__(self, method):
+ """
+ Method being wrapped should only return data to be used for
+ API call. The api_request takes that data, urlencodes it and
+ makes the proper get or post request to the specified endpoint.
+ """
- def _outter(func):
- @wraps(func)
+ @wraps(method)
def _inner(cls, *args, **kwargs):
try:
- url = cls.HOST_NAME + endpoint
- func_data = func(cls, *args, **kwargs)
- query_data = urlencode(func_data, doseq=1)
- if method == "GET":
- url += "?" + query_data
- query_data = None
-
- response = urllib2.urlopen(url, data=query_data, timeout=timeout)
+ method_data = method(cls, *args, **kwargs)
+ url, query_data = self._get_url_and_data(method_data, cls)
+ response = self._open_url(url, query_data)
except urllib2.HTTPError as e:
response = e
+ return self.prepare_response(response, cls)
- custom_response = response_class or getattr(cls, "RESPONSE_CLASS", None)
- if custom_response:
- return custom_response(response)
- else:
- return response
return _inner
- return _outter
+
+ def _get_url_and_data(self, method_data, cls):
+ """
+ Returns url and data ready to make request.
+
+ :param method_data:
+ The data returned from the wrapped method call
+ :param cls:
+ The API class object being decorated.
+ """
+ url = cls.HOST_NAME + self.endpoint
+ query_data = method_data and urlencode(method_data, doseq=1)
+ if self.method == "GET" and query_data:
+ url += "?" + query_data
+ query_data = None
+ return url, query_data
+
+ def _open_url(self, url, query_data):
+ return urllib2.urlopen(url, data=query_data, timeout=self.timeout)
+
+ def prepare_response(self, response, cls):
+ """
+ Prepares API response for final return.
+
+ :param response:
+ The raw response returned from ``urlopen``
+ :param cls:
+ The API class object being decorated.
+ """
+ custom_response = self.response_class or getattr(cls, "RESPONSE_CLASS", None)
+ if custom_response:
+ return custom_response(response)
+ else:
+ return response
+api_request = APIRequest
+
View
14 test_apyclient.py
@@ -39,6 +39,10 @@ class ApiStub(object):
def do_something(self):
return {'times': 5}
+ @apyclient.api_request("/do-simple/", timeout=10)
+ def do_simple(self):
+ pass
+
@apyclient.api_request("/do-multiple/", timeout=3)
def do_multiple(self):
return {'times': [5, 3]}
@@ -123,6 +127,16 @@ def test_calls_urlopen_with_post_content(self, urlopen):
)
@mock.patch("urllib2.urlopen")
+ def test_makes_correct_call_when_no_data_to_pass(self, urlopen):
+ api = ApiStub()
+ api.do_simple()
+
+ urlopen.assert_called_once_with("http://www.example.com/do-simple/",
+ data=None,
+ timeout=10,
+ )
+
+ @mock.patch("urllib2.urlopen")
def test_returns_response_when_successful_response(self, urlopen):
resp = urllib2.addinfourl(StringIO("mock_content"), "mock headers", url="http://www.example.com/", code="200")
urlopen.return_value = resp

0 comments on commit 0582270

Please sign in to comment.