view pyloader/factory.py @ 22:b16d6a204ac1

stub a command line entry point
author Jeff Hammel <jhammel@mozilla.com>
date Mon, 06 Jun 2011 07:41:23 -0700
parents 4f7c05630f36
children 9b2ca32e7a36
line wrap: on
line source

"""
abstract factories
"""

import cast
import loader
import os
import sys
from ConfigParser import InterpolationMissingOptionError
from ConfigParser import InterpolationSyntaxError
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
        self.inifile = inifile
        config = self.read(inifile)
        PyFactory.__init__(self, config, main)
        
    @classmethod
    def read(cls, inifile):
        """reads configuration from an .ini file"""

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

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

            # sanity check
            assert ':' in section, "No : in section: %s" % section

            # make a dict for the section
            name, path = section.split(':', 1)
            sect = config[name] = dict(path=path)

            # read the options
            for option in parser.options(section):

                if option in parser.defaults():
                    # don't include the defaults
                    continue

                # try to interpolate the option
                # otherwise, use the raw value
                try:
                    value = parser.get(section, option)
                except (InterpolationMissingOptionError, InterpolationSyntaxError):
                    value = parser.get(section, option, raw=True)

                if option = '.': # positional arguments
                    sect['args'] = cast.str2list(value)
                else:
                    sect.setdefault('kwargs', {})[option] = value

        return config

def main(args=sys.argv[1:]):
    """command line entry point"""

if __name__ == '__main__':
    main()