Source code for AccessLayer.RequestHandler
import os
import logging as log
from tornado import web, gen
from urllib2 import URLError
from mimetypes import types_map
from QueryLayer import UtilityLayer
from pkg_resources import resource_string
from concurrent.futures import ThreadPoolExecutor
[docs]class RequestHandler(web.RequestHandler):
""" Responds to requests from :data:`bfly.Webserver._webapp`
Attributes
------------
INPUT: :class:`INPUT`
Static input keywords for all in :mod:`AccessLayer`
RUNTIME: :class:`RUNTIME`
Static runtime keywords for all in :mod:`AccessLayer`
OUTPUT: :class:`OUTPUT`
Static output keywords for all in :mod:`AccessLayer`
BFLY_CONFIG: dict
All data from rh-config
_ex: ``concurrent.futures.ThreadPoolExecutor``
Allows handling of parallel requests
_core: :class:`CoreLayer.Core`
Converts a request into a response
_db: :class:`DatabaseLayer.Database`
Loads data for :class:`INPUT` ``.METHODS.FEATURE``
:h:`Methods`
"""
_basic_mime = 'text/plain'
INPUT = UtilityLayer.INPUT()
OUTPUT = UtilityLayer.OUTPUT()
RUNTIME = UtilityLayer.RUNTIME()
[docs] def initialize(self, _core, _db, _config, _root=''):
""" Bind Core, database, and configuration
Arguments
----------
_core: :class:`CoreLayer.Core`
set to :data:`_core`
_db: :class:`CoreLayer.Database`
set to :data:`_db`
_config: dict
set to :data:`BFLY_CONFIG`
"""
self._ex = ThreadPoolExecutor(max_workers=10)
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header('Access-Control-Allow-Methods', 'GET')
self._core = _core
self._root = _root
self._db = _db
# Set the config dict
self.BFLY_CONFIG = _config
[docs] def parse(self, request):
""" Extract details from any of the methods
Must be overridden by derived classes
Arguments
----------
request: str
The single method requested in the URL
Returns
---------
:class:`QueryLayer.Query`
contains standard details for each request
"""
pass
[docs] @gen.coroutine
def get(self, *args):
""" Asynchronously uses :data:`_ex` to call :meth:`handle`
The query is returned from :meth:`parse` and passed \
asynchronously to :meth:`handle`.
Arguments
----------
args: list
args[0] (str) method requested in the URL
"""
try:
query = self.parse(*args)
if isinstance(query, str):
yield self._ex.submit(self.handle_static, query)
else:
yield self._ex.submit(self.handle, query)
except URLError, u_error:
# Get error information
msg, status = u_error.args[0]
self.set_status(int(status))
self.set_header('Content-Type', 'text/plain')
# Log and write the error message
log.warning(msg)
self.write(msg)
[docs] def handle_static(self, filepath):
""" Serves a path in the `./bfly/static` directory
Arguments
----------
path: str
the actual path to a file on the server
"""
extension = os.path.splitext(filepath)[1]
# Get the mimetype from the requested extension
mime_type = types_map.get(extension, self._basic_mime)
self.set_header("Content-Type", mime_type)
data = resource_string(self._root, filepath)
self.write(data)
[docs] def handle(self, _query):
""" Sends response of :data:`_core` to ``_query``
Calls :meth:`Core.get_data` or :meth:`Core.get_info` \
based on the type of the query from :meth:`Query.is_data`
Arguments
----------
_query: :class:`Query`
The details formatted by :meth:`parse`
"""
self.set_header('Content-Type',_query.mime_type)
if _query.is_data:
# get an image formatted as a string
content = self._core.get_data(_query)
elif _query.is_group:
# get a list of groups formatted as a string
content = self._core.get_groups(_query)
elif _query.is_dataset:
# Get all the info for the dataset
content = self._core.get_dataset(_query)
else:
# get a list or dict of information as a string
content = self._core.get_info(_query)
# Return content
self.write(content)