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)
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)
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)