ActiveConfigParser Class Reference
ActiveConfigParser provides extended functionality for the ConfigParser module. This class attempts to satisfy the following goals:
Provide an extended “parsing” capability in
.inistyle files by allowing the construction of handlers that can be triggered by specially formatted options to provide enhanced parsing capabilities for.inifiles.Enable chaining of
[SECTIONS]within a single.inifile using the parsing capability noted in (1).Provide an extensible capability. We intend
ActiveConfigParserto be used as a base class for other tools so that subclasses can add additional handlers for new ‘operations’ which can be used by the parser.
Note
ConfigParser SECTION entries are stored as a dict.
Because ConfigParser treats OPTIONS as dictionary objects
storing { key: value } entries, the KEY fields must be
unique within a SECTION or ConfigParser will raise a KeyError
exception.
This is why the default behavior of the parser is to partition
keys into three parts: <operation> <parameter> <everything else>:
where <everything else> can be used to ‘uniqueify’ the overall
OPTION field.
Normalization of Fields
Internally, the parser will apply a normalization transform to the operation and the parameter fields.
The following operations are applied to the operation field:
Replace occurrences of
-with_.
Extending through Inheritance
One example of how we might extend activeconfigparser.ActiveConfigParser
to add an operation that might prepend to an environment variable could be
the following. Say we wish to prepend to the PATH environment variable
by adding a new operation called envvar-prepend that might be invoked
in this .ini snippet:
1[SET_PATH_VARS]
2envvar-prepend PATH A: /some/new/path
3envvar-prepend PATH B: /another/path/to/prepend/to/PATH
ActiveConfigParser will identify an operation envvar-prepend
with a parameter PATH. It will then look for a method called
_handler_envvar_prepend() to handle the operation:
1def _handler_envvar_prepend(self, section_name, handler_parameters) -> int:
2 op1,op2 = handler_parameters.op_params
3 envvar_new = op2 # I think this was missing (if the envvar doesn't exist yet).
4 if op1 in os.environ.keys():
5 envvar_new = ":".join([
6 op2,
7 os.environ[op1]
8 ])
9 os.environ[op1] = envvar_new
10 return 0
The strings “A” and “B” on lines 2 and 3 of the .ini file are solely
used to uniqueify the entries within the section SET_PATH_VARS.
Dealing with DEFAULT sections
The underlying ConfigParser instance inside activeconfigparser.ActiveConfigParser
has a notion of a special default section that it can process. The name of this section can be
changed but it defaults to “DEFAULT”.
Normally, the contents of DEFAULT are prepended to every section that is read by ConfigParser.
This is a problem for ActiveConfigParser because the contents of this section will be re-applied
to a sections content every time a use operation is encountered because use triggers
the loading of a new section. When this happens, any values from the default section that had been
changed will be re-set to their original values. This is not something we want, but it is useful to
keep the concept of a “DEFAULT” section.
We implement this by changing the default section name for the internal ConfigParser object to
a new value: CONFIGPARSERENHANCED_COMMON. Generally speaking, one should not include this
in their .ini files for the reasons already presented. The name of this is controlled by the
new property _internal_default_section_name.
To include the original concept of a default section we have another property called
default_section_name which defaults to “DEFAULT”. This section is processed once
at the start of parsing a new section and provides a behaviour that should be familiar
to users of ConfigParser.
API Documentation
ActiveConfigParser
The ActiveConfigParser provides extended
functionality for the ConfigParser
module. This class enables configurable parsing of .ini files by splitting
the key portion of an option in a configuration section into an operation
and a parameter. For example, given this .ini file:
1[SECTION_NAME]
2key: value
3operation parameter: optional value
4operation parameter uniquestr: optional value
The built-in parser will process each OPTION (key: value pairs)
in-order within a SECTION. During the parsing process we attempt to
split the key field into two pieces consisting of an operation
and a parameter.
If there is a handler method associated with the operation
field that is extracted, then the parser will call that handler.
Handlers are added as methods named like _handler_<operation>()
(with or without the leading underscore, which denotes a method not intended
to be overriden by subclasses)
and will be called if they exist. For example, if the operation
field resolves to use, then the parser looks for a method called
_handler_use().
If the handler exists, then it is invoked. Otherwise the parser treats
the OPTION field as a generic key: value pair as normal.
In this way, we can customize our processing by subclassing
ActiveConfigParser and defining our own
handler methods.
- Authors:
William McLendon <Semantik-Codeworks@outlook.com>
Public Interface
- class activeconfigparser.ActiveConfigParser(filename=None)[source]
Bases:
LightweightDebugMessages,ExceptionControlAn enhanced
.inifile parser built usingConfigParser.Provides an enhanced version of the
ConfigParsermodule, which enables some extended processing of the information provided in a.inifile.See also
ConfigParser reference <https://docs.python.org/3/library/configparser.html>
- __init__(filename=None)[source]
Constructor
- Parameters:
filename (str,Path,list) – The
.inifile or files to be loaded. If astrorPathis provided then we load only the one file. Alistof strings orPaths can also be provided, which will be loaded byConfigParser’s read() method.
- property activeconfigparserdata
Enhanced
configparserdata.inifile information data.This property returns a parsed representation of the
configparserdatathat would be loaded from our.inifile. The data in this will return the contents of a section plus its parsed results. For example, if we have this in our.inifile:[SEC A] key A1: value A1 [SEC B] use 'SEC A': key B1: value B1
Extracting the data from ‘SEC B’ would result the contents of ‘SEC B’ + ‘SEC A’:
>>> ActiveConfigParserObj.activeconfigparserdata["SEC B"] { 'key A1': 'value A1', 'key B1': 'value B1' }
Options that resolve to a format matching an operation (
operation parameter : value) what also have a handler defined (i.e, the operation is “handled”) will not be included in the data of a section. This is shown in the example above where theuse 'SEC A':option in[SEC B]is not included inactiveconfigparserdata["SEC B"]after processing.- Returns:
Note
Subclasses should not override this.
- assert_file_all_sections_handled() int[source]
Checks that ALL the options within a file are fully handled. This calls
assert_section_all_options_handledon all the sections of a .ini file.- Returns:
- 0 indicates a successful parse with no errors. Otherwise if problems
are detected then the return code will be nonzero. Note the return value only occurs if
exception_control_levelis 2 or lower (See Raises notes on this method for more details).
- Return type:
int
- Raises:
ValueError – A
ValueErroris raised if there are any unhandled options detected in the file(s) loaded in any section. The error can be suppressed by settingexception_control_levelto suppressSERIOUSerrors (2 or lower). If the exception is suppressed then this method will return a nonzero integer.
- assert_section_all_options_handled(section_name: str, do_raise: bool = True) int[source]
This method provides a validation capability against a section in a .ini file to validate that all of the entries are handled. The use case of this is fairly specific will flag any ‘unhandled’ options within a section under an expectation that the intent or design of the .ini file is that _all_ options are handled by some handler and _any_ that are not indicate an error or typo of some sort.
- Parameters:
section_name (str) – The _name_ of the section that will be searched. Note: in
ActiveConfigParserthis includes the fully parsed section which includes the transitive closure of the dependency graph imposed byuseoperations.do_raise (bool) – If True (default) then this assert will trigger an
exception_control_eventthat may raise aValueErrorif there are unhandled entries detected. If False then the return value is set to nonzero if unhandled entries are found but the ece is not triggered.
- Returns:
- 0 indicates a successful parse with no errors, otherwise
any nonzero return value indicates there is at least one entry in the section that was not properly handled. IF a bad entry is found then the output will be a string containing a message that indicates what the bad entries are.
- Return type:
int
- Raises:
ValueError – A
ValueErroris raised if there are any unhandled options detected in the file(s) loaded in any section. The error can be suppressed by settingexception_control_levelto suppressSERIOUSerrors (2 or lower). If the exception is suppressed then this method will return a nonzero integer.
- property configparser_delimiters: tuple
Delimiters for the configparser
Changing the value of this will trigger a reset of the cached data in the class since this fundamentally changes how ConfigParser will parse the .ini file.
This defaults to
('=', ':')which is the default in ConfigParser (https://docs.python.org/3/library/configparser.html)- Returns:
Returns a tuple containing the delimiters.
- Return type:
tuple
- Raises:
TypeError – If assignment of something other than a
tuple, alist, or astris attempted.
- property configparserdata: ConfigParser
The raw results to a vanilla
ConfigParser-processed.inifile.This property is lazy-evaluated and will be processed the first time it is accessed.
- Returns:
The object containing the contents of the loaded
.inifile.- Return type:
ConfigParser
- Raises:
ValueError – If the length of
self.inifilepathis zero.IOError – If any of the files in
self.inifilepathdon’t exist or are not files.
Note
Subclasses should not override this.
See also
Python library: configparser reference and user-guide.
- property default_section_name
- enter_handler(handler_parameters)[source]
General tasks to do when entering a handler
This executes some general operations that should be executed when entering a handler. Generally, these are just logging and console messages for debugging.
- Parameters:
handler_parameters (HandlerParameters) – The parameters passed to the handler.
- exit_handler(handler_parameters)[source]
General tasks to do when entering a handler
This executes some general operations that should be executed when entering a handler. Generally, these are just logging and console messages for debugging.
- Parameters:
handler_parameters (HandlerParameters) – The parameters passed to the handler.
- get_known_operations()[source]
Generate a list of known operation keywords based on the existing handlers defined in this class.
There is potentially some data loss here since there is nothing in ActiveConfigParser that would prevent someone from defining an operation of the form
foo_barrather thanfoo-barbut since the parser will always convert-to_, in terms of processing an operation there would be no distinction betweenfoo-barandfoo_bar.- Returns:
- A list of strings is returned containing the list of
known operations based on existing handlers.
- Return type:
list
- get_known_operations_message()[source]
Generate a string that lists valid operations.
- Returns:
A string containing the message listing the valid operations.
- Return type:
str
- property inifilepath: list
Provides access to the path to the
.inifile (or files).inifilepathcan be set to one of these things:A
strcontining a path to a.inifile.A
pathlib.Pathobject pointing to a.inifile.A
listof one or more of (1) or (2).
Entries in the list will be converted to
pathlib.Pathobjects.- Returns:
A
listcontaining the.inifiles that will be processed.- Return type:
list
Note
Subclasses should not override this.
- operation_handler()[source]
Implements the
operation_handlerdecorator.This decorator is used on handlers and implements entry and exit calls and exit value checks.
1@operation_handler 2def handler_my_operation(self, section_name, handler_parameters) -> int: 3 # do stuff 4 return 0
- Parameters:
func_handler (Callable) – Reference to the handler function. This gets managed through Python’s decorator syntax.
- parse_all_sections()[source]
Parse ALL sections in the .ini file.
This can be useful if the user wishes to pre-parse all the sections of an
inifile in one go.
- parse_section(section, initialize=True, finalize=True)[source]
Execute parser operations for the provided section.
- Parameters:
section (str) – The section name that will be parsed and retrieved.
initialize (bool) – If True then
handler_initialize()will be executed at the start of the search.finalize (bool) – If True then
handler_finalize()will be executed at the end of the search.
- Returns:
data_sharedproperty fromHandlerParameters. UnlessHandlerParametersis changed, this wil be adicttype.
- property parse_section_last_result
- unroll_to_str(section=None, space_around_delimiters=True, use_base_class_parser=True) str[source]
Unroll a section or whole .ini file to a string
This method generates a string that is the equivalent to the original
.inifile that is loaded with all theuseoperations processed so that the output will look like a regular.inifile.If
use_base_class_parseris set then we instantiate a base-classActiveConfigParserthat will load and (re)parse theinifilepathfile(s) to generate a “clean” copy. Because all options that have handled operations in them will be stripped from the generated .ini file, the most common use case is that we just want theuseoperations processed and stripped, but this paramter gives the option to disable that capability.- Parameters:
section (
str) – If a section name is provided we only generate the specified section (if it exists), otherwise we generate output for all sections.space_around_delimiters (bool) – If
Truethe delimiter is given an extra space of padding.use_base_class_parser (bool) – If
Truthen we instantiate aActiveConfigParserobject to parse the .ini file. IfFalsethen we use the current class (possibly a sub-class) ofActiveConfigParser, which will strip out all options that have handled operations.
- Raises:
TypeError – If
sectionis not astrorNoneobject.
- write(file_object, space_around_delimiters=True, section=None, use_base_class_parser=True) int[source]
File writer utility for ActiveConfigParser objects.
Writes the output of
unroll_to_str()to a file.- Parameters:
file_object (
File Ptr) – Pointer to the file that should be written.space_around_delimiters (bool) – If
Truethe delimiter is given an extra space of padding.section (
str) – If a section name is provided we only generate the specified section (if it exists), otherwise we generate output for all sections.
- Raises:
TypeError – If
file_objectis not a file pointer (instance or derivitive ofio.IOBase).
Operation Handlers (Public)
These handlers are defined by ActiveConfigParser and may be overridden by
subclasses to specialize parser behaviour. If you wish to do so, we recommend
cutting and pasting the default handlers into your code and customizing from
there.
Operation Handlers (Private)
These handlers perform tasks internal to ActiveConfigParser and should
not be overridden.
Parser Helpers (Private)
- ActiveConfigParser._parse_section_r(section_name, handler_parameters=None, initialize=True, finalize=True)[source]
Recursive driver of the parser.
This is the main heavy lifter of the parser.
- Parameters:
section_name (str) – The name of the section being processed.
handler_parameters (HandlerParameters) – The parameters passed to the handler.
initialize (bool) – If enabled and this level of recursion is the root level then we will call
handler_initialize()to do some preprocessing activities.finalize (bool) – If enabled and this level of recursion is the root level then we will call
handler_finalize()to wrap up the recursion.
- Returns:
data_shared
- ActiveConfigParser._apply_transformation_to_operation(operation) str[source]
Apply transformations to the operator parameters which are necessary for the parser and down-stream handlers.
Current transformations:
Repalce all occurrences of - with _
- Returns:
A string containing the transformed operator parameter.
- Return type:
str
- ActiveConfigParser._apply_transformation_to_parameter(parameter) str[source]
Applies transformations to the parameter field if necessary.
Current transformations:
none at this time
- Returns:
A string containing the transformed operator parameter.
- Return type:
str
- ActiveConfigParser._new_handler_parameters(handler_parameters=None) HandlerParameters[source]
Generate a new
HandlerParametersobject.This is called inside the parser to generate
HandlerParameters. If subclasses extend theHandlerParametersclass, this can be overridden.- Parameters:
handler_parameters – an existing
HandlerParametersobject to copy the persistent state over from (i.e.,data_shared,data_internal) for continuity in the new object.- Returns:
HandlerParametersobject.
- ActiveConfigParser._validate_handlerparameters(handler_parameters)[source]
Check
HandlerParameters.Check the
handler_parametersobject that’s being passed around to the handlers to verify that items in it have the proper types.- Raises:
TypeError – Raises a
TypeErrorifhandler_parameters.data_internal['processed_sections']is not asettype.
- ActiveConfigParser._check_handler_rval(handler_name, handler_rval) 0[source]
Check the returned value from a handler.
- Parameters:
handler_name (string) – The name of the handler.
handler_rval (int) – The return value from a handler being processed.
- Returns:
Returns a 0 if everything is ok or a 1 if a warning occurred.
- Return type:
int
- Raises:
RuntimeError – If
handler_rval > 0and theexception_control_levelis high enough, depending on the value ofhandler_rval.
- ActiveConfigParser._launch_generic_option_handler(section_name, handler_parameters, sec_k, sec_v) int[source]
Launcher for
_generic_option_handler()_generic_option_handler()is called in two places inside the recursive parser.It is called when the
key:valuepair in an option does not parse out tooperationandparameterfields.It is called when the
key:valuepair in an option does parse to anoperationand aparameterfield, but there are no handlers defined for theoperation.
This helper just manages the call in both places so thay’re done the same way.
- Returns:
Returns the output value from
_generic_option_handler()- Return type:
int
- ActiveConfigParser._locate_class_method(method_name) tuple[source]
Helper that locates a class method (if it exists)
- Parameters:
method_name (str) – The name of the class method we’re searching for.
- Returns:
- A tuple containing the name and a reference to the method if it
is found or None if not found.
(method_name, method_ref or None)
- Return type:
tuple
- ActiveConfigParser._locate_handler_method(operation) tuple[source]
Convert
operationto a handler name and get the reference to the handler.This method converts the operation parameter (op) to a handler name and searches for the existence of a matching method.
Handlers may be of the format:
_handler_<operation>for internal/private handlers (that should not be overridden) orhandler_<operation>for handlers that are considered fair game for subclasses to override and customize.In the case where there are both public and private handlers for the same operation defined, we will optionally raise a
AmbiguousHandlerErorrif theexception_control_levelis set high enough to trigger “SERIOUS” events. Otherwise, we will choose the PUBLIC handler definition to execute.- Parameters:
operation (str) – The operation parameter that is converted to a proper handler name of the form
_handler_{operation}()orhandler_{operation}().- Returns:
- A tuple containing the
(handler_name, handler_method)where: handler_nameis a string.handler_methodis either a reference to the handler method if it exists, or None if it does not exist.
- A tuple containing the
- Return type:
tuple
- Raises:
AmbiguousHandlerError – An
AmbiguousHandlerErrormight be raised if both a public and a private handler of the same name are defined and theexception_control_levelis set to raise exceptions for “SERIOUS” events.
Private Methods
- ActiveConfigParser._loginfo_add(typeinfo, entry) None[source]
If in debug mode, we can use this to log operations by appending to
_loginfo.- Parameters:
typeinfo (str) – The kind of operation this is. This generates the ‘type’ entry in the
_loginfodict. (Required)entry (dict) – A dictionary containing log information that we’re appending. At minimum it should have:
{ type: typestring }.
Inner Classes
ActiveConfigParserData
ActiveConfigParserData is
an inner class of ActiveConfigParser that
behaves somewhat like a partial implementation of a ConfigParser
object that is customized to work in concert with
ActiveConfigParser.
This class operates using lazy-evaluation of the parser method in
ActiveConfigParser to process sections when requested.
The following table shows the main API and implementation differences
between a ActiveConfigParserData
object and a ConfigParser object, illustrating what subset of features
a ActiveConfigParserData
object provides:
Property |
activeconfigparserdata |
configparser |
|---|---|---|
|
X |
|
|
X |
X |
|
X |
X |
|
X |
X |
|
X |
X |
|
X |
X |
|
X |
|
|
X |
|
|
X |
|
|
X |
|
|
X |
X |
|
X |
|
|
X |
|
|
X |
|
|
X |
X |
|
X |
X |
|
X |
X |
|
X |
|
|
X |
|
|
X |
|
|
X |
|
|
X |
|
|
X |
X |
|
X |
X |
|
X |
X |
Public Interface
- class ActiveConfigParser.ActiveConfigParserData(owner=None)[source]
Bases:
LightweightDebugMessages,ExceptionControlThis class is intended to serve as a lite analog to
ConfigParserto provide a similar result but with theActiveConfigParserclass’s ability to parse.inifiles and follow entries that implement ause <section name>rule. In this case, when a section parses through, we will return sections with all options that were not handled by some handler.For example, if we have this
.inifile:[SEC A] opt1: value1-A opt2: value2-A [SEC B] opt1: value1-B use 'SEC A' [SEC C] use 'SEC A' opt1: value1-C
and we pull the
SEC Bdata from it using, say,options("SEC B"), the result we should expect is:[SEC B] opt1: value1-A opt2: value2-A
but if we loaded
options("SEC C")then we should get:[SEC C] opt1: value1-C opt2: value2-A
Since the recursion of the
useoperations is a depth-first search, when there are entries with the same keys, then the ‘last one visited’ will win.When we parse a particular section, the result for a given section name is the union of all options in the transitive closure of the directed acyclic graph generated by the
useoperations. For example:[SEC A] use 'SEC B' opt1: value1-A opt2: value2-A [SEC B] use 'SEC A' opt1: value1-B
The results of
options('SEC A')andoptions('SEC B)will be different:>>> options("SEC A") [SEC A] opt1: value1-A opt2: value2-A
>>> options("SEC B") [SEC B] opt1: value1-B opt2: value1-A
Note
This MUST be an inner class of
ActiveConfigParserbecause it contains a ‘hook’ back to the instance ofActiveConfigParserin in which this entry exists. This allows us to access the owner’s state so we can implement our lazy-evaluation and caching schemes. When an intance ofActiveConfigParseraccesses a section via theactiveconfigparserdataproperty, the parser will be invoked on this section to generate the result.- add_section(section, force=False)[source]
Add a new empty section.
Adds a new empty section if an existing one does not already exist. We can override this and force the new section if we enable the
forceoption.- Parameters:
section (str) – The new section name.
force (boolean) – If True then we will FORCE the new section to be added regardless of whether or not this will overwrite an existing section.
- Returns:
A dictionary containing the new section added.
- Return type:
dict
- property data: dict
- get(section, option=None)[source]
Get a section/option pair, if it exists. If we have not parsed the section yet, we should run the parser to fully get the key data.
- has_section(section) bool[source]
Checks if the section exists in the data.
- Returns:
True if the section exists, False if otherwise.
- Return type:
boolean
- has_section_no_parse(section)[source]
Check for existence of the section without parsing
This will return True if the section exists or False if it does not. In this check, if the section does not exist the owner parser will NOT be invoked.
- Returns:
- True if the section exists in the data and
False if it does not.
- Return type:
boolean
- keys()[source]
Returns an iterable of sections in the
.inifile.- Returns:
containing the sections in the
.inifile.- Return type:
iterable object
- sections(parse=False)[source]
Returns an iterable of sections in the
.inifile.- Parameters:
parse (str,bool) –
Determines whether or not we will parse the sections as well. This can be a string or a boolean type.
False: (default): Only returns the section names.True: Return the section names but also execute a parse of the sections.- ”force”Same as
Truebut we force a (re)parse of all sections even if they’ve already been parsed before.
- ”force”Same as
- Returns:
containing the sections in the
.inifile.- Return type:
iterable object
ActiveConfigParser and ConfigParser Differences
There are some differences between ConfigParser and ActiveConfigParser that are worthwhile to note:
ConfigParser.keys()andActiveConfigParserData.keys()work slightly differently:ConfigParser.keys()will return aKeyViewobject that when iterated will include theDEFAULTsection even if it’s not present in the .ini file.ActiveConfigParserData.keys()does not include theDEFAULTsection if it does not exist in the file.