metaconfig-0.1.4a1/0000755000175000017500000000000011663025504015206 5ustar amckinstryamckinstrymetaconfig-0.1.4a1/setup.py0000644000175000017500000000323311663025402016716 0ustar amckinstryamckinstry# BSD Licence # Copyright (c) 2010, Science & Technology Facilities Council (STFC) # All rights reserved. # # See the LICENSE file in the source distribution of this software for # the full license text. from setuptools import setup, find_packages import sys, os __version__ = '0.1.4' __description__ = """ Metaconfig ========== Metaconfig is a library for centralising your Python's ConfigParser files. It is inspired by the logging module where it is increadibly easy to start writing code that depends on logging whilst deferring how log messages will be handled until later. To get started with metaconfig just do:: import metaconfig conf = metaconfig.get_config(__name__) conf will be a ConfigParser instance. You can create a centralised config file to configure multiple applications and libraries. Further documentation is available at http://packages.python.org/metaconfig/. """ setup(name='metaconfig', version=__version__, description="A ConfigParser bootstraping library", long_description=__description__, classifiers=[ 'Development Status :: 3 - Alpha', 'License :: OSI Approved :: BSD License', 'Topic :: Software Development :: Libraries', ], keywords='', author='Stephen Pascoe', author_email='Stephen.Pascoe@stfc.ac.uk', url='', download_url='http://ndg.nerc.ac.uk/dist', license='BSD', packages=find_packages(exclude=['ez_setup', 'examples', 'test']), include_package_data=True, zip_safe=False, install_requires=[ # -*- Extra requirements: -*- ], entry_points= { }, test_suite='nose.collector', ) metaconfig-0.1.4a1/setup.cfg0000644000175000017500000000021611663025402017023 0ustar amckinstryamckinstry[egg_info] tag_build = a1 tag_date = 0 tag_svn_revision = 0 [nosetests] tests = test,doc/source doctest-extension = rst with-doctest = true metaconfig-0.1.4a1/test/0000755000175000017500000000000011663025402016162 5ustar amckinstryamckinstrymetaconfig-0.1.4a1/test/test_metaconfig.py0000644000175000017500000000535411663025402021716 0ustar amckinstryamckinstry# BSD Licence # Copyright (c) 2010, Science & Technology Facilities Council (STFC) # All rights reserved. # # See the LICENSE file in the source distribution of this software for # the full license text. """ Metaconfig tests. Requires nose. """ import os from unittest import TestCase import tempfile from cStringIO import StringIO import ConfigParser from metaconfig import MetaConfig, Error def _make_test_config(**kwargs): conf = ConfigParser.RawConfigParser() conf.add_section('foo') conf.set('foo', 'a', '42') conf.set('foo', 'b', 'line 1\nline 2') conf.add_section('bar') for k, v in kwargs.items(): conf.set('bar', k, v) return conf _metaconfig_pat = """ [metaconfig] configs = p1 p1.p2 [p1:foo] a = 42 b = line 1 line 2 [p1:bar] %s [p1.p2:foo] a = 12 b = no c = yes """ def _make_metaconfig(**kwargs): args = '\n'.join('%s = %s' % (k, kwargs[k]) for k in kwargs) text = _metaconfig_pat % args fh = StringIO() fh.write(text) fh.seek(0) mf = ConfigParser.ConfigParser() mf.readfp(fh) return mf class Test1(TestCase): def setUp(self): self.mf = MetaConfig() self.mf.add_config('p1', _make_test_config(x='1')) self.mf.add_config('p1.p2', _make_test_config(x='2')) def test_1(self): conf = self.mf.get_config('p1') assert conf.get('foo', 'a') == '42' def test_2(self): conf = self.mf.get_config('x') assert conf.sections() == [] def test_3(self): conf = self.mf.get_config('p1.p2') assert conf.get('bar', 'x') == '2' def test_4(self): # Request non-existent conf with existing parent. conf = self.mf.get_config('p1.p3') assert conf.get('bar', 'x') == '1' class Test2(TestCase): def setUp(self): self.mf = MetaConfig() fd, self.config_file = tempfile.mkstemp() fh = os.fdopen(fd, 'w') conf = _make_test_config(x='1') conf.write(fh) fh.close() def tearDown(self): os.remove(self.config_file) def test_1(self): self.mf.add_config_fh('p1', open(self.config_file)) conf = self.mf.get_config('p1') assert conf.get('foo', 'a') == '42' assert conf.get('bar', 'x') == '1' def test_2(self): self.mf.add_config_file('p1', self.config_file) conf = self.mf.get_config('p1') assert conf.get('foo', 'a') == '42' assert conf.get('bar', 'x') == '1' class Test3(TestCase): def setUp(self): self.mf = MetaConfig.from_config(_make_metaconfig(x='1')) def test_1(self): conf = self.mf.get_config('p1') assert conf.get('foo', 'a') == '42' assert conf.get('bar', 'x') == '1' metaconfig-0.1.4a1/test/test_global.py0000644000175000017500000000126311663025402021035 0ustar amckinstryamckinstry# BSD Licence # Copyright (c) 2010, Science & Technology Facilities Council (STFC) # All rights reserved. # # See the LICENSE file in the source distribution of this software for # the full license text. import tempfile import os from test_metaconfig import _make_metaconfig from metaconfig import get_config, reset def setup(): global config_file fd, config_file = tempfile.mkstemp() mconf = _make_metaconfig(x='1') fh = os.fdopen(fd, 'w') mconf.write(fh) fh.close() os.environ['METACONFIG_CONF'] = config_file def teardown(): reset() os.remove(config_file) def test_1(): config = get_config('p1') assert config.get('foo', 'a') == '42' metaconfig-0.1.4a1/metaconfig/0000755000175000017500000000000011663025402017317 5ustar amckinstryamckinstrymetaconfig-0.1.4a1/metaconfig/__init__.py0000644000175000017500000000711311663025402021432 0ustar amckinstryamckinstry# BSD Licence # Copyright (c) 2010, Science & Technology Facilities Council (STFC) # All rights reserved. # # See the LICENSE file in the source distribution of this software for # the full license text. """ State global to the whole package. """ import sys, re, os from ConfigParser import ConfigParser from metaconfig.mconf import MetaConfig, Error import logging log = logging.getLogger(__name__) _metaconfig = None def _default_search_path(): search_path = [ './metaconfig.conf', os.path.join(os.environ.get('HOME', ''), '.metaconfig.conf'), os.path.join(sys.prefix, 'etc', 'metaconfig.conf') ] if 'METACONFIG_CONF' in os.environ: search_path[0:] = [(os.environ['METACONFIG_CONF'])] return search_path def init_from_config(config): """ Initialise metaconfig from a :mod:`ConfigParser.ConfigParser` instance. An exception will be raised if metaconfig has already been initialised. """ global _metaconfig if _metaconfig is not None: raise Exception("Metaconfig is already initialised") _metaconfig = MetaConfig.from_config(config) def init_from_string(config_str): """ Initialise metaconfig from a string. An exception will be raised if metaconfig has already been initialised. """ from StringIO import StringIO mconf = ConfigParser() mconf.readfp(StringIO(config_str)) init_from_config(mconf) def init(search_path=None): """ Initialise metaconfig. An exception will be raised if metaconfig has already been initialised. :param search_path: A sequence of file paths to search for a metaconfig configuration. If ``search_path`` is None it defaults to: 1. The value of the ``METACONFIG_CONF`` environment variable if set. 2. ``metaconfig.conf`` in the current directory. 3. ``$HOME/.metaconfig.conf`` 4. ``/etc/metaconfig.conf`` """ global _metaconfig if _metaconfig is not None: raise Exception("Metaconfig is already initialised") if search_path is None: search_path = _default_search_path() for config in search_path: if os.path.exists(config): log.debug('Selected %s as metaconfig.conf' % config) _metaconfig = MetaConfig.from_config_file(config) return else: _metaconfig = MetaConfig() def get_config(name, inherit=True): """ Returns the :mod:`ConfigParser.ConfigParser` for the given name. :param name: The name of the config object to return. This is interpreted as a '.'-separated hierarchical namespace. :param inherit: If ``True`` and cofig ``name`` does not exist each config above ``name`` in the hierarchy will be tried before returning an empty config object. For instance ``get_config("x.y.z")`` would try to return existing configs ``x.y.z``, ``x.y`` and ``x`` before returning a new, empty config ``x.y.z``. """ if _metaconfig is None: init() return _metaconfig.get_config(name, inherit=inherit) def add_config(name, config_parser): """ Add a config_parser object to metaconfig. """ if _metaconfig is None: init() return _metaconfig.add_config(name, config_parser) def add_config_file(name, config, ConfigClass=None): """ Read a config file and add it to metaconfig. """ if _metaconfig is None: init() return _metaconfig.add_config_file(name, config, ConfigClass) def reset(): global _metaconfig log.warn("Reseting metaconfig. Existing configs will remain.") _metaconfig = None metaconfig-0.1.4a1/metaconfig/mconf.py0000644000175000017500000001262011663025402020774 0ustar amckinstryamckinstry# BSD Licence # Copyright (c) 2010, Science & Technology Facilities Council (STFC) # All rights reserved. # # See the LICENSE file in the source distribution of this software for # the full license text. """ metaconfig ---------- We want to do: {{{ import metaconfig # Config is a ConfigParser instance (or subclass) # Returns the lowest level config file available (e.g. if __name__ == 'foo.bar.baz' and there is a config # defined for 'foo.bar' use that. config = metaconfig.get_config(__name__) }}} These options are bootstraped on entry into Python as: {{{ import metaconfig metaconfig.add_config_file(name, path) metaconfig.add_config(name, configParser) metaconfig.metaconfig(metaconfig) metaconfig.metaconfig_file(metaconfig_file) metaconfig.from_argv() or something like that }}} """ import sys import ConfigParser import re import logging import logging.config log = logging.getLogger(__name__) class Error(Exception): pass DEFAULT_CONFIG_PARSER = ConfigParser.ConfigParser class MetaConfig(object): def __init__(self): self._configs = {} def add_config_file(self, name, path, ConfigClass=DEFAULT_CONFIG_PARSER): log.info('Adding config %s from path %s' % (name, path)) conf = ConfigClass() conf.read([path]) return self.add_config(name, conf) def add_config_fh(self, name, fileobj, ConfigClass=DEFAULT_CONFIG_PARSER): log.info('Adding config %s from file object' % name) conf = ConfigClass() conf.readfp(fileobj) return self.add_config(name, conf) def add_config(self, name, config_parser): if name in self._configs: Error("Config %s already exists" % name) else: config_parser.__config_name__ = name self._configs[name] = config_parser log.info('Config %s added' % name) return config_parser def get_config(self, name, ConfigClass=DEFAULT_CONFIG_PARSER, inherit=True): log.debug('Requested config %s, inherit=%s' % (name, inherit)) if inherit: parts = name.split('.') while parts: name1 = '.'.join(parts) log.debug("Looking for config %s" % name1) try: config = self._configs[name1] log.debug("Selected config %s" % name1) return config except KeyError: parts = parts[:-1] if name in self._configs: log.debug("Selecting config %s" % name) return self._configs[name] else: config = self.add_config(name, ConfigClass()) log.debug("New config %s" % name) return config @classmethod def from_config(klass, config_parser): mf = klass() mf._setup_logging(config_parser) mf._parse_nested_configs(config_parser) mf._parse_external_configs(config_parser) return mf @classmethod def from_config_file(klass, config_file): cnf = DEFAULT_CONFIG_PARSER() cnf.read(config_file) return klass.from_config(cnf) @classmethod def from_config_fh(klass, config_fh): cnf = DEFAULT_CONFIG_PARSER() cnf.readfp(config_fh) return klass.from_config(cnf) def _parse_nested_configs(self, config_parser): """ Parse configs embedded in the metaconfig file. """ if not config_parser.has_option('metaconfig', 'configs'): return configs = config_parser.get('metaconfig', 'configs').split() D = {} for section in config_parser.sections(): mo = re.match(r'(.+?):(.+)', section) if not mo: continue prefix, ssec = mo.groups() D.setdefault(prefix, []).append(ssec) for config in configs: cp = DEFAULT_CONFIG_PARSER() for ssec in D[config]: sec = '%s:%s' % (config, ssec) if ssec.lower() == 'default': defaults = cp.defaults() for option in config_parser.options(sec): defaults[option] = config_parser.get(sec, option, raw=True) else: cp.add_section(ssec) for option in config_parser.options(sec): cp.set(ssec, option, config_parser.get(sec, option, raw=True)) self.add_config(config, cp) def _parse_external_configs(self, config_parser): """ Parse external config files referenced in metaconfig.conf. """ pass #!FIXME: need to name each config. #if not config_parser.has_option('metaconfig', 'config-files'): # return # #config_files = config_parser.get('metaconfig', 'config-files').split() #for cf in config_files: # self.add_config_file(cf) def _setup_logging(self, config_parser): """ Initialise logging from a nested config. """ if not config_parser.has_option('metaconfig', 'logging'): return logging_file = config_parser.get('metaconfig', 'logging') logging.config.fileConfig(logging_file) log.info('Logging configuration initialised from %s' % logging_file) metaconfig-0.1.4a1/metaconfig.egg-info/0000755000175000017500000000000011663025402021011 5ustar amckinstryamckinstrymetaconfig-0.1.4a1/metaconfig.egg-info/not-zip-safe0000644000175000017500000000000111663025402023237 0ustar amckinstryamckinstry metaconfig-0.1.4a1/metaconfig.egg-info/dependency_links.txt0000644000175000017500000000000111663025402025057 0ustar amckinstryamckinstry metaconfig-0.1.4a1/metaconfig.egg-info/PKG-INFO0000644000175000017500000000221611663025402022107 0ustar amckinstryamckinstryMetadata-Version: 1.0 Name: metaconfig Version: 0.1.4a1 Summary: A ConfigParser bootstraping library Home-page: UNKNOWN Author: Stephen Pascoe Author-email: Stephen.Pascoe@stfc.ac.uk License: BSD Download-URL: http://ndg.nerc.ac.uk/dist Description: Metaconfig ========== Metaconfig is a library for centralising your Python's ConfigParser files. It is inspired by the logging module where it is increadibly easy to start writing code that depends on logging whilst deferring how log messages will be handled until later. To get started with metaconfig just do:: import metaconfig conf = metaconfig.get_config(__name__) conf will be a ConfigParser instance. You can create a centralised config file to configure multiple applications and libraries. Further documentation is available at http://packages.python.org/metaconfig/. Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Software Development :: Libraries metaconfig-0.1.4a1/metaconfig.egg-info/top_level.txt0000644000175000017500000000001311663025402023535 0ustar amckinstryamckinstrymetaconfig metaconfig-0.1.4a1/metaconfig.egg-info/SOURCES.txt0000644000175000017500000000042211663025402022673 0ustar amckinstryamckinstrysetup.cfg setup.py metaconfig/__init__.py metaconfig/mconf.py metaconfig.egg-info/PKG-INFO metaconfig.egg-info/SOURCES.txt metaconfig.egg-info/dependency_links.txt metaconfig.egg-info/not-zip-safe metaconfig.egg-info/top_level.txt test/test_global.py test/test_metaconfig.pymetaconfig-0.1.4a1/PKG-INFO0000644000175000017500000000221611663025402016301 0ustar amckinstryamckinstryMetadata-Version: 1.0 Name: metaconfig Version: 0.1.4a1 Summary: A ConfigParser bootstraping library Home-page: UNKNOWN Author: Stephen Pascoe Author-email: Stephen.Pascoe@stfc.ac.uk License: BSD Download-URL: http://ndg.nerc.ac.uk/dist Description: Metaconfig ========== Metaconfig is a library for centralising your Python's ConfigParser files. It is inspired by the logging module where it is increadibly easy to start writing code that depends on logging whilst deferring how log messages will be handled until later. To get started with metaconfig just do:: import metaconfig conf = metaconfig.get_config(__name__) conf will be a ConfigParser instance. You can create a centralised config file to configure multiple applications and libraries. Further documentation is available at http://packages.python.org/metaconfig/. Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Software Development :: Libraries