view pyloader/factory.py @ 18:d303a5883991

even more stubbing
author Jeff Hammel <jhammel@mozilla.com>
date Fri, 27 May 2011 19:12:56 -0700
parents edecb6fbd5a7
children 8987867698ee
line wrap: on
line source

"""
abstract factories
"""

import loader
import os
from ConfigParser import SafeConfigParser as ConfigParser

__all__ = ['CircularReferenceError', 'PyFactory', 'IniFactory']

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)
        return value
                                                            
class IniFactory(PyFactory):

    def __init__(self, inifile, main=''):
        assert os.path.exists(inifile), "File not found: %s" % inifile

    def read(self):
        """reads configuration from an .ini file"""

        here = os.path.dirname(os.path.abspath(self.inifile))
        
        # read configuration
        defaults={'here': here,
                  'this': os.path.abspath(self.inifile)}
        parser = ConfigParser(defaults=defaults)
        parser.optionxform = str # use whole case
        parser.read(self.inifile)

        # parse configuration
        config = {}
        for section in parser.sections():

            # sanity check
            assert ':' in section, "No : in section: %s" % section
            name, path = section.split(':', 1)
            sect = config[name] = dict(path=path)
            for option in parser.options(section):
                pass