Source code for supercell.api.environment

# vim: set fileencoding=utf-8 :
#
# Copyright (c) 2013 Daniel Truemper <truemped at googlemail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
'''The :class:`Environment` is a container for request handlers, managed
objects and other runtime settings as well as the
:class:`tornado.web.Application` settings.

There are two cases where you will need to work with the it: during the
bootstrapping phase you may change paths to look for configuration files and
you will add the request handlers to the environment.  In addition to that you
can also use it from within a request handler in and access managed objects,
such as HTTP clients that can be used accross a number of client libraries for
connection pooling, e.g.
'''
from __future__ import absolute_import, division, print_function, with_statement

from collections import namedtuple

from tornado.web import Application as _TAPP


__all__ = ['Environment']


Handler = namedtuple('Handler', ['host_pattern', 'path', 'handler_class',
                                 'init_dict', 'name'])


class Application(_TAPP):
    '''Overwrite :class:`tornado.web.Application` in order to give access to
    environment and configuration instances.'''

    def __init__(self, environment, config, **kwargs):
        '''Initialize the tornado Application and add the config and
        environment.
        '''
        self.environment = environment
        self.config = config
        super(Application, self).__init__(**kwargs)


[docs]class Environment(object): '''Environment for **supercell** processes. ''' def __init__(self): '''Initialize the handlers and health checks variables.''' self._handlers = [] self._managed_objects = {} self._health_checks = {} self._finalized = False
[docs] def add_handler(self, path, handler_class, init_dict, name=None, host_pattern='.*$'): '''Add a handler to the :class:`tornado.web.Application`. The environment will manage the available request handlers and managed objects. So in the :py:func:`Service.run()` method you will add the handlers:: class MyService(s.Service): def run(): self.environment.add_handler('/', Handler, {}) :param path: The regular expression for the URL path the handler should be bound to. :type path: str or re.pattern :param handler_class: The request handler class :type handler_class: supercell.api.RequestHandler :param init_dict: The initialization dict that is passed to the `RequestHandler.initialize()` method. :type init_dict: dict :param name: If set the handler and its URL will be available in the `RequestHandler.reverse_url()` method. :type name: str :param host_pattern: A regular expression for matching the hostname the handler will be bound to. By default this will match all hosts ('.*$') :type host_pattern: str ''' assert not self._finalized, 'Do not change the environment at runtime' handler = Handler(host_pattern=host_pattern, path=path, handler_class=handler_class, init_dict=init_dict, name=name) self._handlers.append(handler)
[docs] def add_managed_object(self, name, instance): '''Add a managed instance to the environment. A managed object is identified by a name and you can then access it from the environment as an attribute, so in your request handler you may:: class MyService(s.Service): def run(self): managed = HeavyObjectFactory.get_heavy_object() self.environment.add_managed_object('managed', managed) class MyHandler(s.RequestHandler): def get(self): self.environment.managed :param name: The managed object identifier :type name: str :param instance: Some arbitrary instance :type instance: object ''' assert not self._finalized assert name not in self._managed_objects self._managed_objects[name] = instance
def _finalize(self): '''When called it is not possible to add more managed objects. When the `Service.main()` method starts, it will call `_finalize()` in order to not be able to change the environment with respect to managed objects and request handlers.''' self._finalized = True def __getattr__(self, name): '''Retrieve a managed object from `self._managed_objects`.''' if name not in self._managed_objects: raise AttributeError('%s not a managed object' % name) return self._managed_objects[name]
[docs] def add_health_check(self, name, check): '''Add a health check to the API. :param name: The name for the health check to add :type name: str :param check: The request handler performing the health check :type check: A :class:`supercell.api.RequestHandler` ''' assert not self._finalized assert name not in self._health_checks self._health_checks[name] = check
@property
[docs] def health_checks(self): '''Simple property access for health checks.''' return self._health_checks
@property
[docs] def config_file_paths(self): '''The list containing all paths to look for config files. In order to manipulate the paths looked for configuration files just manipulate this list:: class MyService(s.Service): def bootstrap(self): self.environment.config_file_paths.append('/etc/myservice/') self.environment.config_file_paths.append('./etc/') ''' if not hasattr(self, '_config_file_paths'): self._config_file_paths = [] return self._config_file_paths
@property
[docs] def tornado_settings(self): '''The dictionary passed to the :class:`tornado.web.Application` containing all relevant tornado server settings.''' if not hasattr(self, '_tornado_settings'): self._tornado_settings = {} return self._tornado_settings
def _application(self, config): '''Create the tornado application. :param config: The configuration that will be added to the app ''' if not hasattr(self, '_app'): self._app = Application(self, config, **self.tornado_settings) for handler in self._handlers: self._app.add_handlers(handler.host_pattern, [(handler.path, handler.handler_class, handler.init_dict)]) return self._app @property
[docs] def config_name(self): '''Determine the configuration file name for the machine this application is running on. The filenames are generated using a combination of username and machine name. If you deploy the application as user **webapp** on host **fe01.local.dev** the configuration name would be **webapp_fe01.local.dev.cfg**. ''' if not hasattr(self, '_config_name'): import getpass import socket self._config_name = '%s_%s.cfg' % (getpass.getuser(), socket.gethostname()) return self._config_name
[docs] def tornado_log_function(self, logger): '''Return a function that will log tornado requests. :param logger: the logger that will be used for logging the requests :type logger: logging.logger ''' if not hasattr(self, '_log_function'): def req_log(handler): if handler.get_status() < 400: log_method = logger.info elif handler.get_status() < 500: log_method = logger.warning else: log_method = logger.error request_time = 1000.0 * handler.request.request_time() log_method("%d %s %.2fms", handler.get_status(), handler._request_summary(), request_time) self._log_function = req_log return self._log_function

Project Versions

This Page