confini

Parse and merge multiple ini files in python3
git clone git://git.defalsify.org/python-confini.git
Log | Files | Refs | README | LICENSE

commit ec9f3f61aacac58352b011d1a288741d86eb9489
Author: nolash <dev@holbrook.no>
Date:   Tue, 18 Aug 2020 10:46:41 +0200

Initial commit

Diffstat:
Aconfini/__init__.py | 15+++++++++++++++
Aconfini/config.py | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asetup.py | 17+++++++++++++++++
Atest/files/bar.ini | 5+++++
Atest/files/foo.ini | 3+++
Atest/test_basic.py | 46++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 227 insertions(+), 0 deletions(-)

diff --git a/confini/__init__.py b/confini/__init__.py @@ -0,0 +1,15 @@ +from confini.config import Config, set_current, config_from_environment + + +# TODO workaround for global sempo code smell, can't get around it unless manipulate attributes in the package itself +#SENTRY_SERVER_DSN = None +#ETH_CHAIN_ID = 42 +#ETH_GAS_PRICE = 1000000000 +#ETH_GAS_LIMIT = 60000 +#ETH_CHECK_TRANSACTION_RETRIES = 0 +#ETH_CHECK_TRANSACTION_RETRIES_TIME_LIMIT = 0 +#ETH_CHECK_TRANSACTION_BASE_TIME = 0 +#REDIS_URL = 'redis://' +#ETH_HTTP_PROVIDER = 'http://localhost:8545' +#ETH_DATABASE_URI = 'postgresql://postgres:postgres@localhost:8545/eth_worker' +#SYNCRONOUS_TASK_TIMEOUT = 1 diff --git a/confini/config.py b/confini/config.py @@ -0,0 +1,141 @@ +#!/usr/bin/python + +import logging +import sys +import os +import tempfile +import configparser + +logg = logging.getLogger() + +current_config = None + + +def set_current(conf, description=''): + global current_config + logg.debug('setting current config ({})'.format(description)) + current_config = conf + + +class Config: + + parser = configparser.ConfigParser(strict=True) + + def __init__(self, config_dir): + if not os.path.isdir(config_dir): + raise OSError('{} is not a directory'.format(config_dir)) + self.dir = os.path.realpath(config_dir) + self.required = {} + self.censored = {} + self.store = {} + + + def add(self, value, constant_name): + self.store[constant_name] = value + + + def censor(self, identifier, section=None): + constant_name = '' + if section != None: + constant_name = Config.to_constant_name(identifier, section) + else: + constant_name = identifier + self.censored[constant_name] = True + + + def require(self, directive, section): + if self.required.get(section) == None: + self.required[section] = [] + self.required[section].append(directive) + + + def validate(self): + for k in self.required.keys(): + for v in self.required[k]: + try: + _ = self.parser[k][v] + except: + return False + return True + + + @staticmethod + def to_constant_name(directive, section): + return '{}_{}'.format(section.upper(), directive.upper()) + + + def _process_env(self): + for s in self.parser.sections(): + for k in self.parser[s]: + cn = Config.to_constant_name(k, s) + self.add(os.environ.get(cn, self.parser[s][k]), cn) + + + def process(self, set_as_current=False): + """Concatenates all .ini files in the config directory attribute and parses them to memory + """ + tmp = tempfile.NamedTemporaryFile(delete=False) + tmpname = tmp.name + for filename in os.listdir(self.dir): + if not '.ini' in filename: + continue + f = open(os.path.join(self.dir, filename), 'rb') + while 1: + data = f.read() + if not data: + break + tmp.write(data) + f.close() + tmp.close() + self.parser.read(tmpname) + os.unlink(tmpname) + self._process_env() + if set_as_current: + set_current(self, description=self.dir) + + + def get(self, k): + return self.store.get(k) + + + def __str__(self): + ls = [] + for k in self.store.keys(): + v = '' + try: + _ = self.censored[k] + v = '***' + except: + v = self.store[k] + + ls.append('{} = {}'.format(k, v)) + + return '\n'.join(ls) + + + def __repr__(self): + return "<Config '{}'>".format(self.dir) + + + +def config_from_environment(): + config_dir = config_dir_from_environment() + c = Config(config_dir) + c.process() + return c + + +def config_dir_from_environment(): + return os.environ.get('CIC_CONFIG_DIR') + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.stderr.write('usage: config.py <config_dir>') + sys.exit(1) + c = Config(sys.argv[1]) + c.process() + print(repr(c)) + print(c) + + diff --git a/setup.py b/setup.py @@ -0,0 +1,17 @@ +from setuptools import setup + +setup( + name='confini', + version='0.0.1', + description='Parse, verify and merge all ini files in a single directory', + author='Louis Holbrook', + author_email='dev@holbrook.no', + packages=[ + 'confini', + ], + scripts = [ + 'scripts/parse.py', + ], + data_files = [('', ['LICENSE.txt'])], + url='https://holbrook.no', + ) diff --git a/test/files/bar.ini b/test/files/bar.ini @@ -0,0 +1,5 @@ +[BAR] +foo = oof + +[XYZZY] +bert = ernie diff --git a/test/files/foo.ini b/test/files/foo.ini @@ -0,0 +1,3 @@ +[FOO] +bar = 42 +baz = 029a diff --git a/test/test_basic.py b/test/test_basic.py @@ -0,0 +1,46 @@ +#!/usr/bin/python + +import os +import unittest +import logging + +from confini import Config + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +class TestBasic(unittest.TestCase): + + wd = os.path.dirname(__file__) + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_parse_two_files(self): + inidir = os.path.join(self.wd, 'files') + c = Config(inidir) + c.process() + c.require('BERT', 'XYZZY') + expect = { + 'FOO_BAR': '42', + 'FOO_BAZ': '029a', + 'BAR_FOO': 'oof', + 'XYZZY_BERT': 'ernie', + } + self.assertDictEqual(expect, c.store) + + + def test_require(self): + inidir = os.path.join(self.wd, 'files') + c = Config(inidir) + c.require('BERT', 'XYZZY') + self.assertTrue(c.validate()) + c.require('ERNIE', 'XYZZY') + self.assertFalse(c.validate()) + logg.debug(c) + +if __name__ == '__main__': + unittest.main()