Package: iniherit
Adds INI-file inheritance to ConfigParser. Note that although it
effectively behaves very similarly to passing multiple files to
ConfigParser’sread()method, that requires changing the code at
that point. If that is not feasible, or the INI files should dictate
inheritance themselves, then theiniheritpackage is a better
alternative.Oh,iniheritalso adds support for environment variable expansion
via%(ENV:VARNAME)s. This really shouldn’t be here, but sinceiniheritsupports the%(SUPER)sexpansion, it was “just too
easy” to add envvar expansion as well…Project InfoProject Page:https://github.com/cadithealth/iniheritBug Tracking:https://github.com/cadithealth/iniherit/issuesTL;DRGiven the following two files,base.ini:[app:main]name=My Application Nameandconfig.ini:[DEFAULT]# the following will cause both "base.ini" and "override.ini" to be# inherited. the "?" indicates that "override.ini" will be ignored# if not found; otherwise an error would occur.%inherit=base.ini ?override.iniThen the following code will pass the assert:importiniheritcfg=iniherit.SafeConfigParser()cfg.read('config.ini')assertcfg.get('app:main','name')=='My Application Name'Alternatively, the global ConfigParser can be altered to
support inheritance:importiniheritiniherit.mixin.install_globally()importConfigParsercfg=ConfigParser.SafeConfigParser()cfg.read('config.ini')assertcfg.get('app:main','name')=='My Application Name'Note that the call toinstall_globally()must be invokedbeforeany other module imports ConfigParser for the global override to have
an effect.The command-line programiniheritallows flattening of INI files
(i.e. collapsing all inheritance rules), optionally in “watch” mode:$iniherit--watch--interval2--verboseinput.inioutput.iniINFO:iniherit.cli:"source.ini"changed;updatingoutput...INFO:iniherit.cli:"inherited-file.ini"changed;updatingoutput...^CInstallationInstall iniherit via your favorite PyPI package manager works as
follows:# if using python 3+, upgrade your `distribute` package *first*$pipinstall"distribute>=0.7.3"# then istall with pip:$pipinstalliniheritInheritance MechanismINI file inheritance with theiniheritpackage:To add inheritance to an INI file, add an%inheritoption to the
“DEFAULT” section of the INI file to inherit all sections of the
specified files. Example:[DEFAULT]%inherit=base.inidef_var=Overrides the "def_var" setting, if present,in the "DEFAULT" section of "base.ini".[my-app]sect_var=Overrides the "sect_var" setting, if present,in the "my-app" section of "base.ini". Other sections in"base.ini" will also be inherited, even if not specifiedhere.The%inheritoption points to a space-separated, URL-encoded,
list of files to inherit values from, whose values are loaded
depth-first, left-to-right. For example:[DEFAULT]%inherit=base.ini file-with%20space.iniTo inherit only a specific section, add the%inheritoption
directly to the applicable section. By default, the section by the
same name will be loaded from the other files, unless the filename
is suffixed with square-bracket enclosed (“[” … “]”), URL-encoded,
section name. Example:[section]%inherit=base.ini override.ini[other%20section]In this case, the “section” section in “base.ini” will be inherited,
followed by the “other section” from “override.ini”.Note that if the inherited section includes interpolation references
to the DEFAULT section, these willNOTbe carried over! In other
words, inheritance currently ONLY inherits the actual values, not
the interpreted values. Be warned, as this can lead to surprising
results!If a filename has “[” in the actual name, it can be URL-encoded.Filenames, if specified relatively, are taken to be relative to the
current INI file.If a filename is prefixed with “?”, then it will be loaded
optionally: i.e. if the file does not exist, it will be silently
ignored. If the file does NOT have a “?” prefixed and cannot be
found, then anIOErrorwill be raised. Note that this is unlike
standard ConfigParser.read() behavior, which silently ignores any
files that cannot be found.If a filename has “?” as its first character, it can be URL-encoded.Note that the actual name of the inherit option can be changed by
changing eitheriniherit.parser.DEFAULT_INHERITTAGfor a global
effect, orConfigParser.IM_INHERITTAGfor a per-instance effect.SubstitutionsTheiniheritpackage adds the following additional substitution
options:%(SUPER[:-DEFAULT])sEvaluates to the inherited value of the current section/key value.
If the inherited INI does not specify a value and no default is
provided, then anInterpolationMissingSuperErroris raised. The
“inherited value” is evaluated depth-first. Note that “SUPER” must
be all upper case.For example, given the following INI files:# base.ini[loggers]keys=root, app# config.ini[DEFAULT]%inherit=base.ini[loggers]keys=%(SUPER)s, authwdef=%(SUPER:-more)s or lessnada=%(SUPER)s boom!Then the following Python will result:importiniheritiniherit.mixin.install_globally()importConfigParsercfg=ConfigParser.SafeConfigParser()cfg.read('config.ini')cfg.get('loggers','keys')# ==> 'root, app, auth'cfg.get('loggers','wdef')# ==> 'more or less'cfg.get('loggers','nada')# ==> raises InterpolationMissingSuperErrorAs with standard interpolation errors, the
InterpolationMissingSuperError exception is only raised if/when the
value is requested from the config (withrawset to falsy).%(ENV:VARNAME[:-DEFAULT])sEvaluates to the value of the environment variable name “VARNAME”.
If the environment variable is not defined and no default is
provided, then anInterpolationMissingEnvErroris raised. Note
that environment variable names are always case sensitive.For example, given the following INI file:# config.ini[section]home=%(ENV:HOME)srdir=%(ENV:RDIR:-/var/run)snada=%(ENV:RDIR)sThen the following Python will result:importiniheritiniherit.mixin.install_globally()importConfigParsercfg=ConfigParser.SafeConfigParser()cfg.read('config.ini')importosos.environ['home']='/home/user'# ensure "HOME" envvar existsos.environ.pop('RDIR',None)# ensure "RDIR" envvar does NOT existcfg.get('section','home')# ==> '/home/user'cfg.get('section','rdir')# ==> '/var/run'cfg.get('section','nada')# ==> raises InterpolationMissingEnvErrorAs with standard interpolation errors, the
InterpolationMissingEnvError exception is only raised if/when the
value is requested from the config (withrawset to falsy).GotchasAfter an inherit-enabled INI file is loaded, the ConfigParser no
longer has knowledge of where a particular option was loaded from or
how it was derived. For this reason, when thewritemethod is
called, the ConfigParser generates an INI file without inheritance.
In other words, it flattens the inheritance tree.