Source code for stoqlib.lib.asyncrequests
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
##
## Copyright (C) 2016 Async Open Source
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU Lesser General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
"""Asynchronous requests utilities.
This module tries to mimic the `requests` api, providing functions with
the same signature as it. The only difference is that the requests
here are executed asynchronously on a separated thread.
"""
import json
import requests
import threading
_requests_version = requests.__version__.split('.')
[docs]class AsyncRequest(threading.Thread):
"""Async request implementation.
Responsible for doing the request asynchronously, and optionally
call a callback/errback on success/failure.
"""
#: The default request timeout if none is defined
DEFAULT_TIMEOUT = 5
def __init__(self, method, url, callback=None, errback=None, **kwargs):
super(AsyncRequest, self).__init__()
self.daemon = True
self._method = method
self._url = url
self._callback = callback
self._errback = errback
self._kwargs = kwargs
self.retval = None
self.error = None
#
# threading.Thread
#
[docs] def run(self):
"""Do the real request.
This will be called by the thread api. Do not call this
directly, use :meth:`.start` instead.
"""
kwargs = self._kwargs.copy()
kwargs.setdefault('timeout', self.DEFAULT_TIMEOUT)
# FIXME: json support was added in requests 2.5. For older versions,
# do the same as requests would do
if _requests_version < (2, 5) and 'json' in kwargs:
assert 'data' not in kwargs
kwargs['data'] = json.dumps(kwargs.pop('json'))
headers = kwargs.setdefault('headers', {})
headers['Content-type'] = 'application/json'
try:
self.retval = requests.request(self._method, self._url, **kwargs)
except Exception as e:
self.error = e
if self._errback is not None:
self._errback(e)
else:
if self._callback is not None:
self._callback(self.retval)
#
# Public API
#
[docs] def get_response(self):
"""Get the response from the object.
Joins the thread and returns its retval. Note that,
if the thread raised an error, it will be reraise here.
:return: the `requests.Response` object or `None` if
the an error happened
"""
self.join()
if self.error:
raise self.error
return self.retval
[docs]def request(method, url, callback=None, errback=None, **kwargs):
"""Do a request asynchronously.
:param method: The http method to use on the request
:param url: The url to do the request
:parm callback: An optional callback to be called after
the request has been sucessfully executed. The request's
retval will be passed as an argument to it
:param errback: An optional callback to be called if
the request produces any error. The traceback object will
be passed as an argument to it
:return: The :class:`.AsyncRequest` that is running the request.
One can :meth:`threading.Thread.join` it when wanting to
wait for it to finish
"""
request = AsyncRequest(method, url,
callback=callback, errback=errback, **kwargs)
request.start()
return request
[docs]def get(url, **kwargs):
"""Do a GET request asynchronously.
:param url: The url to do the request
:param kwargs: Extra keywords that will be passed to :func:`.request`
:return: The :class:`.AsyncRequest` that is running the request.
One can :meth:`threading.Thread.join` it when wanting to
wait for it to finish
"""
return request('get', url, **kwargs)
[docs]def post(url, **kwargs):
"""Do a POST request asynchronously.
:param url: The url to do the request
:param kwargs: Extra keywords that will be passed to :func:`.request`
:return: The :class:`.AsyncRequest` that is running the request.
One can :meth:`threading.Thread.join` it when wanting to
wait for it to finish
"""
return request('post', url, **kwargs)