commit edc8d9e5d761e61c5e4e747f7883bb06c183bd50
parent 346f6c8f15645ed89efc5deca61b138ebca8862d
Author: nolash <dev@holbrook.no>
Date: Wed, 3 Nov 2021 05:42:21 +0100
Add doc capability to dumper
Diffstat:
5 files changed, 122 insertions(+), 35 deletions(-)
diff --git a/confini/config.py b/confini/config.py
@@ -27,7 +27,7 @@ class Config:
default_censor_string = '***'
- def __init__(self, default_dir, env_prefix=None, override_dirs=[]):
+ def __init__(self, default_dir, env_prefix=None, override_dirs=[], skip_doc=False):
self.parser = configparser.ConfigParser(strict=True)
self.__target_tmpdir = None
if default_dir == None:
@@ -44,6 +44,8 @@ class Config:
if not os.path.isdir(d):
raise OSError('{} is not a directory'.format(override_dirs))
self.dirs.append(os.path.realpath(d))
+ self.skip_doc = skip_doc
+ self.doc = None
self.required = {}
self.censored = {}
self.store = {}
@@ -60,9 +62,13 @@ class Config:
self.dirs = [self.__target_tmpdir.name]
for i, d in enumerate(dirs):
for filename_in in os.listdir(d):
- if re.match(r'.+\.ini$', filename_in) == None:
+ filename_out = None
+ if filename_in == '.confini':
+ filename_out = filename_in
+ elif re.match(r'.+\.ini$', filename_in) == None:
continue
- filename_out = '{}_{}'.format(i, filename_in)
+ else:
+ filename_out = '{}_{}'.format(i, filename_in)
in_filepath = os.path.join(d, filename_in)
out_filepath = os.path.join(self.dirs[0], filename_out)
fr = open(in_filepath, 'rb')
@@ -114,7 +120,7 @@ class Config:
return True
- def _sections_override(self, dct, dct_description):
+ def __sections_override(self, dct, dct_description):
for s in self.parser.sections():
for k in self.parser[s]:
cn = to_constant_name(k, s)
@@ -146,18 +152,27 @@ class Config:
self.src_dirs[k] = d
- def process(self, set_as_current=False):
- """Concatenates all .ini files in the config directory attribute and parses them to memory
- """
- tmp_dir = tempfile.mkdtemp()
- logg.debug('using tmp processing dir {}'.format(tmp_dir))
+ def __process_doc_(self, d):
+ if self.skip_doc:
+ return
+ doc_fp = os.path.join(d, '.confini')
+ if self.doc == None:
+ from confini.doc import ConfigDoc
+ self.doc = ConfigDoc()
+ try:
+ self.doc.process(doc_fp)
+ except FileNotFoundError:
+ pass
+
+
+ def __collect_dir(self, out_dir):
for i, d in enumerate(self.dirs):
d = os.path.realpath(d)
if i == 0:
d_label = 'default'
else:
d_label = 'override #' + str(i)
- tmp_out_dir = os.path.join(tmp_dir, str(i))
+ tmp_out_dir = os.path.join(out_dir, str(i))
os.makedirs(tmp_out_dir)
logg.debug('processing dir {} ({})'.format(d, d_label))
tmp_out = open(os.path.join(tmp_out_dir, 'config.ini'), 'ab')
@@ -174,13 +189,18 @@ class Config:
tmp_out.write(data)
f.close()
tmp_out.close()
- d = os.listdir(tmp_dir)
+
+ self.__process_doc_(d)
+
+
+ def __process_schema_dir(self, in_dir):
+ d = os.listdir(in_dir)
d.sort()
c = 0
# TODO: this will fail of sections/options are repeated. should first use individual parser instances to flatten to single file (perhaps in collect_from_dirs already)
for i, tmp_config_dir in enumerate(d):
- tmp_config_dir = os.path.join(tmp_dir, tmp_config_dir)
+ tmp_config_dir = os.path.join(in_dir, tmp_config_dir)
for tmp_file in os.listdir(os.path.join(tmp_config_dir)):
tmp_config_file_path = os.path.join(tmp_config_dir, tmp_file)
if c == 0:
@@ -209,7 +229,20 @@ class Config:
self.add(v, k, exists_ok=True)
self.set_dir(k, self.dirs[i])
c += 1
- self._sections_override(os.environ, 'environment variable')
+
+
+ def process(self, set_as_current=False):
+ """Concatenates all .ini files in the config directory attribute and parses them to memory
+ """
+ tmp_dir = tempfile.mkdtemp()
+ logg.debug('using tmp processing dir {}'.format(tmp_dir))
+
+ self.__collect_dir(tmp_dir)
+
+ self.__process_schema_dir(tmp_dir)
+
+ self.__sections_override(os.environ, 'environment variable')
+
if set_as_current:
set_current(self, description=self.dir)
diff --git a/confini/doc.py b/confini/doc.py
@@ -11,30 +11,37 @@ logg = logging.getLogger(__name__)
class ConfigDoc:
- def __init__(self, src):
- fp = os.path.join(src, '.confini')
- logg.debug('attempting doc parser with src {}'.format(fp))
+ def __init__(self, src=None):
+ fp = None
+ if src != None:
+ fp = os.path.join(src, '.confini')
+ logg.debug('attempting doc parser with src {}'.format(fp))
self.src = fp
self.docs = {}
self.docs_flat = {}
+ if self.src != None:
+ self.process(self.src)
+
+
+ def process(self, src):
try:
- self.process_as_ini()
+ self.process_as_ini(src)
except Exception:
- self.process_as_env()
+ self.process_as_env(src)
- def process_as_ini(self):
+ def process_as_ini(self, src):
p = configparser.ConfigParser()
- p.read_file(self.src)
+ p.read_file(src)
return self.process_parser(p)
- def process_as_env(self):
+ def process_as_env(self, src):
from confini.env import ConfigEnvParser
c = ConfigEnvParser()
- p = c.from_file(self.src)
+ p = c.from_file(src)
return self.process_parser(p)
@@ -56,7 +63,13 @@ class ConfigDoc:
else:
return self.docs[k][o]
+
+ def all(self):
+ return list(self.docs_flat.keys())
+
@staticmethod
def from_config(config):
+ if config.doc != None:
+ return config.doc
return ConfigDoc(config.dirs[0])
diff --git a/confini/env.py b/confini/env.py
@@ -2,6 +2,7 @@
import configparser
import io
import logging
+import sys
logg = logging.getLogger(__name__)
@@ -42,3 +43,22 @@ class ConfigEnvParser:
self.parser.add_section(ks)
self.parser.set(ks, ko, v)
return self.parser
+
+
+def export_env(config, prefix=None, empty_all=False, skip_empty=False, doc=False, w=sys.stdout):
+ for k in config.all():
+ v = config.get(k)
+ if empty_all or v == None:
+ v = ''
+ if v == '' and skip_empty:
+ logg.debug('skipping empty directive {}'.format(k))
+ continue
+ if doc:
+ try:
+ doc_s = config.doc.get(k)
+ w.write('# ' + doc_s + "\n")
+ except KeyError:
+ pass
+ if prefix != None:
+ w.write(prefix + ' ')
+ w.write('{}={}\n'.format(k, v))
diff --git a/confini/export.py b/confini/export.py
@@ -1,4 +1,4 @@
-# import
+# standard imports
import sys
import configparser
import os
@@ -26,7 +26,9 @@ class ConfigExporter:
self.target = None
self.make_doc = doc
self.doc = None
- if isinstance(target, io.IOBase):
+ if target == None:
+ self.target = sys.stdout
+ elif isinstance(target, io.IOBase):
self.target = target
else:
st = os.stat(target)
@@ -40,7 +42,14 @@ class ConfigExporter:
if self.make_doc:
from confini.doc import ConfigDoc
- self.doc = ConfigDoc.from_config(config)
+ if isinstance(doc, ConfigDoc):
+ self.doc = doc
+ else:
+ try:
+ self.doc = ConfigDoc.from_config(config)
+ except FileNotFoundError as e:
+ logg.warning('doc set but no doc file found: {}'.format(e))
+ self.make_doc = False
def scan(self):
diff --git a/confini/runnable/dump.py b/confini/runnable/dump.py
@@ -8,6 +8,8 @@ import stat
# local imports
from confini import Config
+from confini.env import export_env
+from confini.export import ConfigExporter
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
@@ -20,6 +22,8 @@ argparser.add_argument('--prefix', type=str, help='Prefix every line with given
argparser.add_argument('--skip-empty', action='store_true', dest='skip_empty', help='Skip defined directives that are missing a value')
argparser.add_argument('--schema-dir', dest='schema_dir', action='append', type=str, help='Configuation directory to merge with schema definitions')
argparser.add_argument('--schema-module', dest='schema_module', action='append', type=str, default=[], help='Module path to merge with schema definitions')
+argparser.add_argument('--ini', action='store_true', help='Output as ini file')
+argparser.add_argument('--doc', action='store_true', help='Add matching documentation strings to output')
argparser.add_argument('config_dir', nargs='*', type=str, help='Configuation directories to parse')
args = argparser.parse_args()
@@ -53,16 +57,24 @@ def main():
c = Config(schema_dirs, override_dirs=args.config_dir)
c.process()
- for k in c.store.keys():
- v = c.get(k)
- if args.z or v == None:
- v = ''
- if v == '' and args.skip_empty:
- logg.debug('skipping empty directive {}'.format(k))
- continue
- if args.prefix != None:
- sys.stdout.write(args.prefix + ' ')
- sys.stdout.write('{}={}\n'.format(k, v))
+
+ if args.ini:
+ e = ConfigExporter(c, doc=args.doc)
+ e.export()
+ else:
+ export_env(c, prefix=args.prefix, empty_all=args.z, skip_empty=args.skip_empty, doc=args.doc)
+
+
+# for k in c.store.keys():
+# v = c.get(k)
+# if args.z or v == None:
+# v = ''
+# if v == '' and args.skip_empty:
+# logg.debug('skipping empty directive {}'.format(k))
+# continue
+# if args.prefix != None:
+# sys.stdout.write(args.prefix + ' ')
+# sys.stdout.write('{}={}\n'.format(k, v))
if __name__ == "__main__":