Пример #1
0
class TestLazyLoading(TestCase):
    ''' Test concentrating on lazy-loading and actions via the assembler. '''
    def setUp(self):
        self.locator = Locator()
        self.transformer = Transformer(self.locator)
        self.assembler   = Assembler(self.transformer)

        filename = 'locator-lazy-action.xml'
        filepath = abspath(join(dirname(__file__), '..', 'data', filename))

        self.assembler.load(filepath)

    def tearDown(self):
        del self.locator
        del self.transformer
        del self.assembler

    def test_lazy_initialization(self):
        self.assertIsInstance(self.locator.get('alpha').call_accompany(), Beta)
        self.assertIsInstance(self.locator.get('beta'), Beta)

        self.assertEquals(self.locator.get('alpha').call_accompany(), self.locator.get('beta'))

    # Test for actionable events... due to be in a seperate files.

    def test_actions_without_events(self):
        c = self.locator.get('charlie')
        self.assertIsInstance(c.introduce, Action, c.introduce.__class__)

    def test_actions(self):
        self.locator.get('charlie').cook()
Пример #2
0
    def _register_imagination_services(self, configuration, base_path):
        """ Register services. """
        service_blocks = configuration.children('service')

        assembler = ImaginationAssembler(ImaginationTransformer(AppServices))

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

            assembler.load(config_file_path)
Пример #3
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)
class test_locator_with_proxy_and_factorization(TestCase):
    '''
    Test the locator via the assembler for factorization.
    '''

    def setUp(self):
        self.locator     = Locator()
        self.transformer = Transformer(self.locator)
        self.assembler   = Assembler(self.transformer)

    def tearDown(self):
        del self.locator
        del self.transformer
        del self.assembler

    def test_good_locator_xml_on_entity_registration(self):
        self.__prepare_good_locator_from_xml()

        # Normal entity
        self.assertTrue(self.locator.has('manager'), self.locator._entities)

        # Factorized entities
        self.assertTrue(self.locator.has('worker.alpha'))
        self.assertTrue(self.locator.has('worker.bravo'))
        self.assertTrue(self.locator.has('def.doubler'))
        self.assertTrue(self.locator.has('def.trippler'))

    def test_get_object(self):
        self.__prepare_good_locator_from_xml()

        ticker  = self.locator.get('ticker')
        trigger = self.locator.get('something')
        entity  = self.locator.get('worker.alpha')

        self.assertIsInstance(entity, Worker)
        self.assertEqual(entity.name, 'Alpha')
        self.assertEqual(0, len(ticker.sequence))

        trigger.alpha()

        self.assertEqual(1, len(ticker.sequence))

    def test_get_callback(self):
        self.__prepare_good_locator_from_xml()

        ticker  = self.locator.get('ticker')
        trigger = self.locator.get('something')
        doubler = self.locator.get('def.doubler')

        self.assertTrue(callable(doubler))
        self.assertEqual(12, doubler(6))
        self.assertEqual(0, len(ticker.sequence))

        trigger.doubler()

        # No interception for callable object as of 1.9.
        self.assertEqual(0, len(ticker.sequence))

    def __get_data_path(self, filename):
        return abspath(join(dirname(__file__), '..', 'data', filename))

    def __load_from_xml(self, path_from_data):
        test_file = self.__get_data_path(path_from_data)

        self.assembler.load(test_file)

    def __prepare_good_locator_from_xml(self):
        self.__load_from_xml('locator-factorization.xml')
Пример #5
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)
Пример #6
0
class TestAction(TestCase):
    ''' Test concentrating on lazy-loading and actions via the assembler. '''
    def setUp(self):
        self.locator = Locator()
        self.transformer = Transformer(self.locator)
        self.assembler   = Assembler(self.transformer)

        filename = 'locator-lazy-action.xml'
        filepath = abspath(join(dirname(__file__), '..', 'data', filename))

        self.assembler.load(filepath)

    def tearDown(self):
        del self.locator
        del self.transformer
        del self.assembler

    def test_interceptable_method(self):
        c = self.locator.get('charlie')

        self.assertIsInstance(c.introduce, Action)
        self.assertEquals(c.name, c.introduce())

    def test_normal_execution(self):
        ''' This is a sanity test. '''

        expected_log_sequence = [
            # sequence as charlie cooks
            'Charlie: introduce itself as "Charlie"',
            'Alpha: order "egg and becon"',
            'Charlie: repeat "egg and becon"',
            'Alpha: confirm for egg and becon',
            'Charlie: respond "wilco"',
            'Charlie: cook',

            # sequence as charlie serves
            'Alpha: speak to Beta, "watch your hand"',
            'Beta: acknowledge',
            'Alpha: wash hands',
            'Charlie: serve'
        ]

        alpha   = self.locator.get('alpha')
        charlie = self.locator.get('charlie')

        charlie.cook()
        charlie.serve()

        self.assertEquals(
            len(expected_log_sequence),
            len(Conversation.logs),
            'The number of sequences in the mock scenario must be the same.'
        )

        for step in range(len(Conversation.logs)):
            expectation = expected_log_sequence[step]
            actual      = Conversation.logs[step]
            self.assertEquals(
                expectation,
                actual,
                'Failed at step {step}: should be "{expectation}", not "{actual}"'.format(
                    step=step,
                    expectation=expectation,
                    actual=actual
                )
            )

        #print '\n'.join(Conversation.logs)
Пример #7
0
class TestLocator(TestCase):
    '''
    Test the locator via the assembler.
    '''
    class UnknownEntity(object): pass

    def setUp(self):
        self.locator     = Locator()
        self.transformer = Transformer(self.locator)
        self.assembler   = Assembler(self.transformer)

    def tearDown(self):
        del self.locator
        del self.transformer
        del self.assembler

    def test_checker(self):
        entity = self.__make_good_entity()

        self.assertFalse(self.locator.has('poo'))
        self.assertRaises(UnknownEntityError, self.locator.get, ('poo'))

    def test_before_activation(self):
        entity = self.__make_good_entity()

        self.locator.set('poo', entity)

        self.assertTrue(self.locator.has('poo'))
        self.assertFalse(entity.activated)

    def test_after_activation(self):
        entity = self.__make_good_entity()

        self.locator.set('poo', entity)

        self.assertIsInstance(self.locator.get('poo'), PlainOldObject)
        self.assertTrue(entity.activated)

    def test_good_locator_xml_on_entity_registration(self):
        self.__prepare_good_locator_from_xml()

        self.assertTrue(self.locator.has('poo'))
        self.assertTrue(self.locator.has('poow-1'))
        self.assertTrue(self.locator.has('poow-2'))
        self.assertTrue(self.locator.has('dioc'))
        self.assertTrue(self.locator.has('dioe'))

    def test_good_locator_xml_on_class_injection(self):
        self.__prepare_good_locator_from_xml()

        self.assertEqual(self.locator.get('dioc').r, PlainOldObject)

    def test_good_locator_xml_on_class_injection(self):
        self.__prepare_good_locator_from_xml()

        self.assertIsInstance(self.locator.get('dioe').e, PlainOldObjectWithParameters)

    def test_entities_with_same_class(self):
        self.__prepare_good_locator_from_xml()

        self.assertIsInstance(self.locator.get('poow-1'), PlainOldObjectWithParameters)
        self.assertIsInstance(self.locator.get('poow-2'), PlainOldObjectWithParameters)

        self.assertNotEquals(self.locator.get('poow-1').method(), self.locator.get('poow-2').method())
        self.assertEquals('%.2f' % self.locator.get('poow-1').method(), '0.67')
        self.assertEquals(self.locator.get('poow-2').method(), 35)

    def __make_good_entity(self):
        return Entity(
            'poo', Loader('dummy.core.PlainOldObject')
        )

    def __get_data_path(self, filename):
        return abspath(join(dirname(__file__), '..', 'data', filename))

    def __load_from_xml(self, path_from_data):
        test_file = self.__get_data_path(path_from_data)

        self.assembler.load(test_file)

    def __prepare_good_locator_from_xml(self):
        self.__load_from_xml('locator.xml')
Пример #8
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)