Configuration File Handling
***************************

Handlers for text configuration files. Configurations are simple
string to string mappings, with the configuration files using the
following rules…

* the key/value is separated by a space

* anything after a ‘#’ is ignored as a comment

* excess whitespace is trimmed

* empty lines are ignored

* multi-line values can be defined by following the key with lines
  starting with a ‘|’

For instance…

   # This is my sample config
   user.name Galen
   user.password yabba1234 # here's an inline comment
   user.notes takes a fancy to pepperjack cheese
   blankEntry.example

   msg.greeting
   |Multi-line message exclaiming of the
   |wonder and awe that is pepperjack!

… would be loaded as…

   config = {
     'user.name': 'Galen',
     'user.password': 'yabba1234',
     'user.notes': 'takes a fancy to pepperjack cheese',
     'blankEntry.example': '',
     'msg.greeting': 'Multi-line message exclaiming of the\nwonder and awe that is pepperjack!',
   }

Configurations are managed via the "Config" class. The "Config" can be
be used directly with its "get()" and "set()" methods, but usually
modules will want a local dictionary with just the configurations that
it cares about.

To do this use the "config_dict()" function. For example…

   import getpass
   from stem.util import conf, connection

   def config_validator(key, value):
     if key == 'timeout':
       # require at least a one second timeout
       return max(1, value)
     elif key == 'endpoint':
       if not connection.is_valid_ipv4_address(value):
         raise ValueError("'%s' isn't a valid IPv4 address" % value)
     elif key == 'port':
       if not connection.is_valid_port(value):
         raise ValueError("'%s' isn't a valid port" % value)
     elif key == 'retries':
       # negative retries really don't make sense
       return max(0, value)

   CONFIG = conf.config_dict('ssh_login', {
     'username': getpass.getuser(),
     'password': '',
     'timeout': 10,
     'endpoint': '263.12.8.0',
     'port': 22,
     'reconnect': False,
     'retries': 3,
   }, config_validator)

There’s several things going on here so lets take it step by step…

* The "config_dict()" provides a dictionary that’s bound to a given
  configuration. If the “ssh_proxy_config” configuration changes then
  so will the contents of CONFIG.

* The dictionary we’re passing to "config_dict()" provides two
  important pieces of information: default values and their types. See
  the Config’s "get()" method for how these type inferences work.

* The config_validator is a hook we’re adding to make sure CONFIG only
  gets values we think are valid. In this case it ensures that our
  timeout value is at least one second, and rejects endpoints or ports
  that are invalid.

Now lets say our user has the following configuration file…

   username waddle_doo
   password jabberwocky
   timeout -15
   port 9000000
   retries lots
   reconnect true
   logging debug

… and we load it as follows…

   >>> from stem.util import conf
   >>> our_config = conf.get_config('ssh_login')
   >>> our_config.load('/home/atagar/user_config')
   >>> print CONFIG
   {
     "username": "waddle_doo",
     "password": "jabberwocky",
     "timeout": 1,
     "endpoint": "263.12.8.0",
     "port": 22,
     "reconnect": True,
     "retries": 3,
   }

Here’s an expanation of what happened…

* the username, password, and reconnect attributes took the values in
  the configuration file

* the ‘config_validator’ we added earlier allows for a minimum timeout
  of one and rejected the invalid port (with a log message)

* we weren’t able to convert the retries’ “lots” value to an integer
  so it kept its default value and logged a warning

* the user didn’t supply an endpoint so that remained unchanged

* our CONFIG didn’t have a ‘logging’ attribute so it was ignored

**Module Overview:**

   config_dict - provides a dictionary that's kept in sync with our config
   get_config - singleton for getting configurations
   uses_settings - provides an annotation for functions that use configurations
   parse_enum_csv - helper funcion for parsing confguration entries for enums

   Config - Custom configuration
     |- load - reads a configuration file
     |- save - writes the current configuration to a file
     |- clear - empties our loaded configuration contents
     |- add_listener - notifies the given listener when an update occurs
     |- clear_listeners - removes any attached listeners
     |- keys - provides keys in the loaded configuration
     |- set - sets the given key/value pair
     |- unused_keys - provides keys that have never been requested
     |- get - provides the value for a given key, with type inference
     +- get_value - provides the value for a given key as a string
