Last active
September 27, 2019 20:57
-
-
Save bradmontgomery/655a96e7c934aad6ab6730faf0e0caf5 to your computer and use it in GitHub Desktop.
Python config parsing / access experiment.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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