
\section{\module{plasTeX.ConfigManager} --- \plasTeX\ Configuration}
\label{sec:configuration-api}

\declaremodule{standard}{plasTeX.ConfigManager}
\modulesynopsis{\plasTeX's configuration management system}

A \class{ConfigManager} manages the configuration options for plastex. From the
end user's point of view, this behaves like a 2-layered nested \class{dict}, so
that configuration options can be retrieved as
\code{config["general"]["theme"]}, for example. However, \class{ConfigManager} is
much more powerful than a nested dict. It supports reading in configuration
options from \code{.ini} files and command line arguments.

The former is performed via the \method{read} function, and the latter is via
the \method{registerArgparse} and \method{updateFromDict} functions. The first
function adds the options to an \class{ArgumentParser} from \method{argparse},
and the latter reads in the values obtained from \method{argparse}. The
interaction looks as follows:

\begin{verbatim}

config = ConfigManager()

# Set up the config manager here

# Construct an ArgumentParser
parser = ArgumentParser("plasTeX")

# This function adds a command line option to `parser` for each
# configuration item in `config`
config.registerArgparse(parser)

# We now let the parser parse the arguments in `sys.argv`
data = vars(parser.parse_args())

# Finally, we let `config` read the values back in
config.updateFromDict(data)

\end{verbatim}

The following sample code shows how one would set up a `ConfigManager` object:

\begin{verbatim}
from plasTeX.ConfigManager import *
c = ConfigManager()

# Create a new section in the config file.  This corresponds to the
# [ sectionname ] sections in an INI file.  The returned value is 
# a reference to the new section
d = c.add_section('debugging')

# Add an option to the 'debugging' section called 'verbose'.
# This corresponds to the config file setting:
#
# [debugging]
# verbose = no
#
d['verbose'] = BooleanOption(
    """ Increase level of debugging information """,
    options = '-v --verbose !-q !--quiet',
    default = False,
)

# Read config files
c.loadFromFiles(['/etc/myconfig.ini', '~/myconfig.ini')
\end{verbatim}

The configuration system supports interpolation. That is, in options whose
value is a string (or a list of strings), we replace all instances of
\code{\%(foo)s} with the value of the option \code{foo}. The formatting is done
with pythons \%-formatting and all \%-formatting features are supported. Note
that we only specify the name of the option, and not the section it belongs to.
For example,
\begin{verbatim}
plastex --renderer=HTML5 --theme=%(renderer)stheme
\end{verbatim}
sets the theme to \code{HTML5theme}.

Interpolation is performed when you access the value of an option, not when the
value is set. For example, if we modify the \code{renderer} variable in runtime
and then access \code{theme}, it changes accordingly. Specifically, this is
performed in \method{ConfigSection.__getitem__}.

\subsection{ConfigManager Objects}

\begin{classdesc}{ConfigManager}{}
A configuration manager.
\end{classdesc}

\begin{methoddesc}[ConfigManager]{addSection}{name, description=None}
  Creates a section with the given name and description and adds it to the
  \class{ConfigManager}.

  \var{name} is the key for accessing the section, which is also the section
        header used in the INI file.

  \var{description} is the header of the section for the cli `--help` function.
  Defaults to `name.capitalize() + " Options"`

  Returns the new \class{ConfigSection} object produced.
\end{methoddesc}

\begin{methoddesc}[ConfigManager]{read}{filenames}
loads config from the INI files in \var{filenames}. The function ignores files
that do not exist. The argument may also be a single filename.
\end{methoddesc}

\begin{methoddesc}[ConfigManager]{registerArgparse}{parser}
  adds command line options to \var{parser} for each configuration item. This
  function merely delegates the job to the identically-named function of each
  of the sections.
\end{methoddesc}

\begin{methoddesc}[ConfigManager]{updateFromDict}{data}
  reads in configuration options from the dict returned by
  `vars(ArgumentParser.parse_args)`. This function merely delegates the job to
  the identically-named function of each of the sections.
\end{methoddesc}

\subsection{ConfigSection Objects}

\begin{classdesc}{ConfigSection}{name, parent}
For the most part, a \class{ConfigSection} is a dict of \class{ConfigOption}s.
However, when we access \code{section["key"]}, it doesn't return the
\class{ConfigOption} itself, but the value of the option.

The rationale for this setup is that we want to think of a \class{ConfigOption}
as the value of the option together with some metadata. When trying to read or
write the config option, what we want to do is the read or write to the value
and ignoring the metadata.

The only exception is when we want to add a new config option to the section,
where we would write
\begin{verbatim}
section["new_key"] = ConfigOption(...)
\end{verbatim}

\var{name} is the name of the section.

\var{parent} is the \class{ConfigManager} that contains this section.
\end{classdesc}

\begin{memberdesc}[ConfigSection]{data}
dictionary that contains the option instances. This is only accessed if you
want to retrieve the real option instances. Normally, you would use standard
dictionary key access syntax on the section itself to retrieve the option
values.
\end{memberdesc}

\begin{memberdesc}[ConfigSection]{name}
the name given to the section.
\end{memberdesc}

\begin{methoddesc}[ConfigManager]{get}{option}
retrieve the value of \var{option}, returning \keyword{None} if \var{option} doesn't exist.
\end{methoddesc}

\begin{methoddesc}[ConfigSection]{__getitem__}{key}
  retrieve the value of an option, raising a \exception{KeyError} if the option
  does not exist. This method allows you to use Python's dictionary syntax on a
  section as shown below.
\begin{verbatim}
# Print the value of the 'optionname' option
print(mysection['optionname'])
\end{verbatim}
\end{methoddesc}

\begin{memberdesc}[ConfigSection]{parent}
a reference to the parent \class{ConfigManager} object.
\end{memberdesc}

\begin{methoddesc}[ConfigSection]{__setitem__}{key, value}
create a new option or set an existing option with the name \var{key} and
the value of \var{value}.  This method allows you to use Python's 
dictionary syntax to set options as shown below.
\begin{verbatim}
# Create a new option called 'optionname'
mysection['optionname'] = IntegerOption("Test", --option="--test", default=0)
mysection['optionname'] = 10
\end{verbatim}
\end{methoddesc}

\subsection{Configuration Option Types}

\begin{classdesc}{ConfigOption[T]}{description, options, default}
  This class represents an option whose value has a ``simple'' type.
  Specifically, we require \code{T(s)} to correctly converts a string \var{s}
  to an object of type \class{T}. The type is determined at runtime based on
  the type of the default value. More complex options should inherit this class.

  From the point of view of the external API, every option item is expected to
  inherit \class{ConfigOption}, and is expected to implement the methods described
  below correctly. Moreover, the instance attribute \member{value} is expected
  to hold the value of the option.

  On the other hand, custom \method{__init__} implementations are not required
  to set the other instance attributes (i.e.\ \member{description},
  \member{options} and \member{name}) described below.
\end{classdesc}

\begin{memberdesc}[ConfigOption]{description}
  description of this option to be used in the \code{plastex -{}-help} output.
\end{memberdesc}

\begin{memberdesc}[ConfigOption]{options}
  space separated list of command line argument that correspond to this option.
  The behaviour of this variable may be customized by implementations of
  \class{ConfigOption}.
\end{memberdesc}

\begin{memberdesc}[ConfigOption]{name}
  the key used by \var{argparse} to store the value read from the command line.
  Note that \var{argparse} does not group the argument values by section, so
  one has to be careful to avoid collision.

  This is set to the first entry in \member{self.option} with leading \code{-}s
  stripped.
\end{memberdesc}

\begin{memberdesc}[ConfigOption]{value}
  the actual value of the option
\end{memberdesc}

\begin{methoddesc}[ConfigOption]{registerArgparse}{group}
  this function should add the command line arguments for this configuration to
  the \var{argparse._ArgumentGroup} object \var{group}.
\end{methoddesc}

\begin{methoddesc}[ConfigOption]{updateFromDict}{data}
  sets the value of the option based on the data in \var{data}. The format of
  \var{data} is defined to be what is returned by
  \code{vars(parser.parse_args())}, where \var{parser} is populated by
  \method{registerArgparse}. This is intended to be used in conjuction with
  \method{registerArgparse}.
\end{methoddesc}

\begin{classdesc}{BooleanConfigOption}{\optional{\class{ConfigOption} arguments}}
Boolean options are simply options that allow you to specify an `on' or 
`off' state. In a config file, the value is converted to a boolean via the
builtin \code{bool} function. Boolean options on the command-line do not take
an argument; simply specifying the option sets the state to true.

One interesting feature of boolean options is in specifying the command-line
options.  Since you cannot specify a value on the command-line (the existence
of the option indicates the state), there must be a way to set the state to
false.  This is done using the `not' operator (!).  When specifying the 
\var{options} argument of the constructor, if you prefix an command-line 
option with an exclamation point, the existence of that option indicates
a false state rather than a true state.  Below is an example of an \var{options}
value that has a way to turn debugging information on (\longprogramopt{debug}) 
or off (\longprogramopt{no-debug}).
\begin{verbatim}
BooleanOption("", options = '--debug !--no-debug', default=True)
\end{verbatim}
\end{classdesc}

\begin{classdesc}{FloatOption}{\optional{\class{ConfigOption} arguments}}
A \class{FloatOption} is an option that accepts a floating point number.
\end{classdesc}

\begin{classdesc}{IntegerOption}{\optional{\class{ConfigOption} arguments}}
An \class{IntegerOption} is an option that accepts an integer value.
\end{classdesc}

\begin{classdesc}{StringOption}{\optional{\class{ConfigOption} arguments}}
A \class{StringOption} is an option that accepts an arbitrary string.
\end{classdesc}

\begin{classdesc}{MultiStringOption}{\optional{\class{ConfigOption} arguments}}
A \class{MultiStringOption} is an option that is intended to be used multiple
times on the command-line, or take a list of values. Other options when
specified more than once simply overwrite the previous value.
\class{MultiStringOption}s will append the new values to a list.

In the configuration file, this is a space separated list, interpreted in the
same ways as shell arguments (with the same escaping/quoting rules for strings
that contains whitespaces). In fact, it is processed by \code{shlex.split}.

The individual values of \class{MultiStringOption} are strings. This can, of
course, be made generic, but such a use case has not arisen.
\end{classdesc}

\begin{classdesc}{DictOption[T]}{\optional{\class{ConfigOption} arguments}}
  This is an abstract base class for options whose value is a
  \code{Dict[str, T]}. This receives special treatment by
  \method{ConfigManager.read} --- in a config file, any line whose key is
  unrecognized (i.e.\ not the key of an existing option) is added to the first
  \class{DictOption} in the section. Usually, such sections only contain a
  single option which is a \class{DictOption}.

  The value can also be set directly, in the form
\begin{verbatim}
data=a=b,c=d,e=f
\end{verbatim}

  There is no implementor in \code{ConfigManager.py}, but is used by
  \var{CountersOption}, \var{LinksOption}, \var{ImageScaleOption} and \var{LogOption} in
  \code{Config.py}.

  Note that all implementors of \class{DictOption} must also implement
  \method{registerArgparse} (and can optionally implement other methods of
  \class{ConfigOption}).
\end{classdesc}

\begin{methoddesc}[DictOption]{entryFromString}{entry}
  a class method to convert a string to the desired value entry. This is used
  by the in the default implementation of \method{set}.
\end{methoddesc}

\begin{methoddesc}[DictOption]{set}{key, value}
  sets the value of the dict. The \var{value} is assumed to be a string.
\end{methoddesc}
