view pyloader/factory.py @ 11:ff272dcd5cd8

we have a passing test
author Jeff Hammel <jhammel@mozilla.com>
date Fri, 27 May 2011 07:02:27 -0700
parents ba2355d57998
children 30979d82c95d
line wrap: on
line source

"""
abstract factories
"""

import loader

class CircularReferenceError(Exception):
    """factory has detected a circular reference"""

class PyFactory(object):

    delimeters = ('%(', ')s')

    def __init__(self, config=None, main=''):
        self.main = main  # main section
        self.configure(config or {})

    def configure(self, config):
        """load a new configuration"""
        # TODO: this should really be a configuration update.  If you keep
        # track of all "apps" and their parents (i.e. as a ADG)
        # you should be able to update only relevent apps
        self.config = config
        self.seen = set() # already seen apps to note cyclic dependencies
        self.parsed = {}  # instantiated apps

    def load(self, name=None):
        """load an object"""
        
        name = name or self.main # load main section by default
        assert name in self.config, "'%s' not found in configuration"
        if name in self.parsed:
            return self.parsed[name]
        if name in self.seen:
            raise CircularReferenceError('Circular reference! : %s' % name)
        self.seen.add(name)

        # get section
        section = self.config[name]
        assert 'path' in section

        # load object
        obj = loader.load(section['path'])

        # get the object's arguments (if any)
        args = section.get('args', None)
        kwargs = section.get('kwargs', None)

        # if args and kwargs aren't there, you're done!
        if args is None and kwargs is None:
            self.parsed[name] = obj
            return obj

        # interpolate arguments
        if args:
            args = [self.interpolate(arg) for arg in args]
        else:
            args = []
        if kwargs:
            kwargs = dict([(key, self.interpolate(value))
                           for key, value in kwargs.items()])
        else:
            kwargs = {}

        # invoke
        self.parsed[name] = obj(*args, **kwargs)
        return self.parsed[name]

    def interpolate(self, value):

        # only interpolate strings
        if not isinstance(value, basestring):
            return value

        if value.startswith(self.delimeters[0]) and value.endswith(self.delimeters[1]):
            value = value[len(self.delimeters[0]):-len(self.delimeters[1])]
            if value in self.config:
                return self.load(value)
            elif value == 'self':
                # reference this factory
                # tricky and dangerous
                return self
        return value