Beispiel #1
0
class Core(object):
    """ The Core of the Framework

        This relies on Imagination Framework.
    """
    def __init__(self, locator=None):
        self.locator     = locator or Locator()
        self.transformer = Transformer(self.locator)
        self.assembler   = Assembler(self.transformer)

        self._cache_map = None

    @contextmanager
    def passive_mode(self):
        self.assembler.activate_passive_loading()
        yield
        self.assembler.deactivate_passive_loading()

    def get(self, id):
        """ Get the service container. """
        return self.locator.get(id)

    def load(self, *paths):
        """ Load service containers from multiple configuration files. """
        with self.passive_mode():
            [
                self.assembler.load(path)
                for path in paths
            ]

            self._cache_map = None

    def all(self):
        if not self._cache_map:
            self._cache_map = {
                i: self.locator.get(i)
                for i in self.locator.entity_identifiers
            }

        return self._cache_map

    def set_entity(self, entity_id, entity_fqcn, *args, **kwargs):
        try:
            entity = self._create_entity(entity_id, entity_fqcn, args, kwargs)

            self.locator.set(entity_id, entity)
        except ImportError as exception:
            raise ImportError('Failed to register {} ({})'.format(entity_id, entity_fqcn))

    def _create_entity(self, id, entity_fqcn, args, kwargs):
        loader = Loader(entity_fqcn)

        return Entity(id, loader, *args, **kwargs)
Beispiel #2
0
class Application(BaseApplication):
    """
    Interface to bootstrap a WSGI application with Tornado Web Server.

    `settings` is a dictionary of extra settings to Tornado engine. For more
    information, please read Tornado documentation.
    """

    _registered_routing_types = ['controller', 'proxy', 'redirection', 'resource']
    _default_services         = [
        ('finder',      'tori.common.Finder',                     [], {}),
        ('renderer',    'tori.template.service.RenderingService', [], {}),
        ('session',     'tori.session.repository.memory.Memory',  [], {}),
        ('routing_map', 'tori.navigation.RoutingMap',             [], {}),
        ('db',          'passerine.db.manager.ManagerFactory',    [], {})
    ]

    _data_transformer         = ImaginationTransformer(ImaginationLocator())

    def __init__(self, configuration_location, **settings):
        BaseApplication.__init__(self, **settings)

        self._service_assembler = ImaginationAssembler(ImaginationTransformer(AppServices))
        self._config_main_path = os.path.join(self._base_path, configuration_location)
        self._config_base_path = os.path.dirname(self._config_main_path)

        self._config = load_from_file(self._config_main_path)

        # Initialize the routing map
        self._routing_map = RoutingMap()

        # Default properties
        self._scope = settings['scope'] if 'scope' in settings else None
        self._port  = 8000

        # Register the default services.
        self._register_default_services()

        # Add the main configuration to the watch list.
        watch(self._config_main_path)

        # Configure with the configuration files
        self._service_assembler.activate_passive_loading()

        for inclusion in self._config.children('include'):
            self._load_inclusion(inclusion)

        self._configure(self._config)

        self._prepare_db_connections()
        self._prepare_session_manager()

        self._service_assembler.deactivate_passive_loading()

        # Override the properties with the parameters.
        if 'port' in settings:
            self._port = settings['port']
            self._logger.info('Changed the listening port: %s' % self._port)

        # Update the routing map
        AppServices.get('routing_map').update(self._routing_map)

        # Normal procedure
        self._update_routes(self._routing_map.export())
        self.listen(self._port)
        self._activate()

    def _prepare_session_manager(self):
        config = self._settings['session']

        self._set_service_entity('session', config['class'], **config['params'])

    def _prepare_db_connections(self):
        db_config = self._settings['db']
        manager_config = db_config['managers']
        em_factory = AppServices.get('db')

        for alias in manager_config:
            url = manager_config[alias]['url']

            em_factory.set(alias, url)

            def callback(em_factory, db_alias):
                return em_factory.get(db_alias)

            callback_proxy = CallbackProxy(callback, em_factory, alias)

            AppServices.set('db.{}'.format(alias), callback_proxy)

    def _load_inclusion(self, inclusion):
        source_location = inclusion.attribute('src')

        if source_location[0] != '/':
            source_location = os.path.join(self._config_base_path, source_location)

        pre_config = load_from_file(source_location)

        self._configure(pre_config, source_location)

        watch(source_location)

        self._logger.info('Included the configuration from %s' % source_location)

    def _load_new_style_configuration(self, configuration):
        # Load the data directly from a JSON file.
        for inclusion in configuration.children('use'):
            source_location = inclusion.attribute('src')

            if source_location[0] != '/':
                source_location = os.path.join(self._config_base_path, source_location)

            with open(source_location) as f:
                decoded_config = json.load(f)

                self._override_sub_config_tree(self._settings, decoded_config)

                watch(source_location)

    def _override_sub_config_tree(self, original_subtree, modified_subtree):
        for key in modified_subtree:
            if key not in original_subtree:
                original_subtree[key] = modified_subtree[key]
                continue

            original_node_type = type(original_subtree[key])
            modified_node_type = type(modified_subtree[key])

            if original_node_type is dict:
                if modified_node_type != original_node_type:
                    raise ValueError('The overriding configuration tree does not align with the predefined one.')

                self._override_sub_config_tree(original_subtree[key], modified_subtree[key])

                continue

            original_subtree[key] = modified_subtree[key]

    def _configure(self, configuration, config_path=None):
        if len(configuration.children('server')) > 1:
            raise InvalidConfigurationError('Too many server configuration.')

        if len(configuration.children('routes')) > 1:
            raise InvalidConfigurationError('Too many routing configuration.')

        if len(configuration.children('settings')) > 1:
            raise InvalidConfigurationError('Too many setting groups (limited to 1).')

        self._load_new_style_configuration(configuration)

        # Then, load the legacy configuration. (Deprecated in 3.1)
        # Load the general settings
        for config in configuration.find('server config'):
            key  = config.attribute('key')
            kind = config.attribute('type')

            if not key:
                raise InvalidConfigurationError('Invalid server configuration key')

            self._settings[key] = self._data_transformer.cast(config, kind)

        # Set the cookie secret for secure cookies.
        client_secret = configuration.find('server secret')

        if client_secret:
            self._settings['cookie_secret'] = client_secret.data()

        # Set the port number.
        port = configuration.find('server port')

        if len(port) > 1:
            raise DuplicatedPortError()
        elif port:
            self._port = port.data()

        # Find the debugging flag
        if (configuration.find('server debug')):
            self._settings['debug'] = configuration.find('server debug').data().lower() == 'true'
            self._logger.info('Debug Mode: {}'.format('On' if self._settings['debug'] else 'Off'))

        for setting in configuration.children('settings').children('setting'):
            setting_name = setting.attr('name')

            if not setting_name:
                raise InvalidConfigurationError('A setting block does not specify the name. ({})'.format(config_path or 'primary configuration'))

            self._settings[setting_name] = setting.data()

        # Exclusive procedures
        self._register_imagination_services(configuration, config_path or self._config_base_path)
        self._map_routing_table(configuration)
        self._set_error_delegate(configuration)

    def _set_error_delegate(self, configuration):
        """ Set a new error delegate based on the given configuration file if specified. """
        delegate = configuration.find('server error').data()

        if delegate:
            self._logger.info('Custom Error Handler: %s' % delegate)
            tornado.web.ErrorHandler = ImaginationLoader(delegate).package

    def _register_default_services(self):
        for id, package_path, args, kwargs in self._default_services:
            try:
                self._set_service_entity(id, package_path, *args, **kwargs)
            except ImportError as exception:
                if id == "db":
                    self._logger.info('Ignored {} as package "passerine" is neither available or loadable (containing errors).'.format(package_path))

                    continue

                raise ImportError('Failed to register {} ({})'.format(id, package_path))

    def _register_imagination_services(self, configuration, base_path):
        """ Register services. """
        service_blocks = configuration.children('service')

        for service_block in service_blocks:
            service_config_path = service_block.data()

            # If the file path contains ':', treat the leading part for the full
            # module name and re-assembles the file path.

            config_file_path = resolve_file_path(service_config_path)

            if service_config_path[0] != '/':
                config_file_path = os.path.join(base_path, service_config_path)

            self._logger.info('Loading services from {}'.format(config_file_path))

            self._service_assembler.load(config_file_path)

    def _map_routing_table(self, configuration):
        """
        Update a routing table based on the configuration.
        """
        routing_sequences = configuration.children('routes')

        self._logger.debug('Registering the routes from the configuration...')

        if not routing_sequences:
            return

        # Register the routes to controllers.
        for routing_sequence in routing_sequences:
            new_routing_map = RoutingMap.make(routing_sequence, self._base_path)

            self._routing_map.update(new_routing_map)

        self._logger.debug('Registered the routes from the configuration.')

    def _make_service_entity(self, id, package_path, *args, **kwargs):
        """
        Make and return a service entity.

        *id* is the ID of a new service entity.

        *package_path* is the package path.

        *args* and *kwargs* are parameters used to instantiate the service.
        """
        loader = ImaginationLoader(package_path)
        entity = ImaginationEntity(id, loader, *args, **kwargs)

        return entity

    def _set_service_entity(self, id, package_path, *args, **kwargs):
        """
        Set the given service entity.

        *id* is the ID of a new service entity.

        *package_path* is the package path.

        *args* and *kwargs* are parameters used to instantiate the service.
        """
        AppServices.set(id, self._make_service_entity(id, package_path, *args, **kwargs))

    def get_route(self, routing_pattern):
        """ Get the route. """
        return self._routing_map.find_by_pattern(routing_pattern)
Beispiel #3
0
class Container(object):
    def __init__(self, locator=None):
        self.logger      = get_logger('{}.{}'.format(__name__, self.__class__.__name__))
        self.locator     = Locator()
        self.transformer = Transformer(self.locator)
        self.assembler   = Assembler(self.transformer)

        self.default_services = [
            ('finder',   'tori.common.Finder',                     [], {}),
            ('renderer', 'tori.template.service.RenderingService', [], {}),
            ('db',       'passerine.db.manager.ManagerFactory',    [], {})
        ]

        self.cache_map = None

        self._register_default_services()

    @contextmanager
    def passive_mode(self):
        self.assembler.activate_passive_loading()
        yield
        self.assembler.deactivate_passive_loading()

    def get(self, id):
        return self.locator.get(id)

    def load(self, *paths):
        with self.passive_mode():
            [
                self.assembler.load(path)
                for path in paths
            ]

            self.cache_map = None

    def all(self):
        if not self.cache_map:
            self.cache_map = {
                i: self.locator.get(i)
                for i in self.locator.entity_identifiers
            }

        return self.cache_map

    def _register_default_services(self):
        for entity_id, package_path, args, kwargs in self.default_services:
            try:
                entity = self._create_entity(entity_id, package_path, *args, **kwargs)

                self.locator.set(entity_id, entity)
            except ImportError as exception:
                if entity_id == "db":
                    self.logger.info('Ignored {} as package "passerine" is neither available or loadable (containing errors).'.format(package_path))

                    continue

                raise ImportError('Failed to register {} ({})'.format(entity_id, package_path))

    def _create_entity(self, id, package_path, *args, **kwargs):
        loader = Loader(package_path)

        return Entity(id, loader, *args, **kwargs)