Skip to content

Instantly share code, notes, and snippets.

@bradmontgomery
Last active September 27, 2019 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradmontgomery/655a96e7c934aad6ab6730faf0e0caf5 to your computer and use it in GitHub Desktop.
Save bradmontgomery/655a96e7c934aad6ab6730faf0e0caf5 to your computer and use it in GitHub Desktop.
Python config parsing / access experiment.
[DEFAULT]
num = 42
pi = 3.141312301928312039810
debug = no
on = yes
sleep = 2
[mqtt]
host = localhost
port = 1883
[database]
host = localhost
port = 5432
username = FOO
password = BAR
"""
This is a very simple config reader that includes support for the following
features:
* Parsing settings from a INI-style config file (using ConfigParser)
* It re-reads the config file on every attribute access (so you don't need to
reload your software after a config update)
* It converts config entries to a reasonable datatype without you knowing
in advance what those are (i.e. you don't have to write extra code for this)
* It uses dot notation to access entries within a section; i.e. conf.database.host
or conf.database.username
Example usage:
>>> config = Config("/path/to/config.ini")
>>> config.debug
True
>>> config.database.host
'localhost'
>>> config.database.port
5432
"""
import configparser
import re
from contextlib import contextmanager
class ConfigSection:
"""This class is a small wrapper for a ConfigParser section. It will
attempt to convert data found in a config file to an appropriate data type;
Supported data types include:
- boolean
- int
- float
- string
"""
def __init__(self, section):
self.section = section
def _to_datatype(self, result):
# Try converting the requested config entry to it's appropriate data type.
if result in ["yes", "on", "true"]:
return True
elif result in ["no", "off", "false"]:
return False
elif result.isdigit():
return int(result)
elif re.match("^\d+?\.\d+?$", result):
return float(result)
else:
return result
def __getattr__(self, name):
return self._to_datatype(self.section[name])
class Config:
"""This class is a ConfigParser wrapper that allows you to access configuration
settings using a dot notation; for example:
>>> conf = Config("your_config.ini")
>>> conf.DEBUG # specified as "on" or "yes"
True
>>> conf.database.host
'localhost'
>>> conf.database.port
5432
"""
def __init__(self, filename):
self.filename = filename
self.config = configparser.ConfigParser()
self._read()
def _read(self):
self.config.read(self.filename)
def __getattr__(self, key):
return self._read_from_section(key)
def _read_from_section(self, key):
self._read()
try:
section = self.config[key]
return ConfigSection(section)
except KeyError:
# key is not a section, it's likely a default value
section = ConfigSection(self.config["DEFAULT"])
return getattr(section, key)
@contextmanager
def section(self, key):
yield self._read_from_section(key)
if __name__ == "__main__":
#
# This is a essentially a simple test that reads from the included
# config.ini file & continues to print config data forever.
#
import time
conf = Config("config.ini")
print("-" * 40)
print("Edit the included config.ini file & watch\nyour results update below!")
print("-" * 40)
time.sleep(2)
while True:
sleep = conf.sleep
print(f"Is this thing on!? {conf.on}")
print(f"conf.debug -> {conf.debug} /// {type(conf.debug)}")
print(f"conf.num -> {conf.num} /// {type(conf.num)}")
print(f"conf.pi -> {conf.pi} /// {type(conf.pi)}")
host = conf.database.host
port = conf.database.port
user = conf.database.username
password = conf.database.password
print(f"DB -> {user}:{password}@{host}:{port}")
print("\nUsing contxt manager to read the MQTT section... ")
with conf.section("mqtt") as section:
print(f"MQTT: {section.host}:{section.port}")
print("-" * 40)
time.sleep(sleep)
clog==0.2.3
ipython==7.8.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment