Created
March 20, 2016 13:48
-
-
Save tsuriga/5bc5bbfaf21c6a51cda7 to your computer and use it in GitHub Desktop.
Simple config file validation in python
This file contains 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] | |
; Operation mode | |
; This is a global value for all sections | |
mode = master | |
[server] | |
; Connection lifetime | |
timeout = 3600 | |
; Garbage collection mode | |
; Accepted values: none, aggressive, smart, auto | |
gc_mode = smart | |
; Notice there is no mode set under this section - it will be read from defaults | |
[client] | |
; Fallback procedure for clients | |
; Accepted values: none, polling, auto | |
; Invalid value as an example here | |
fallback = socket | |
; Overriding global value here | |
mode = slave |
This file contains 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
from configparser import ConfigParser | |
class MyException(Exception): | |
pass | |
class MyConfig(ConfigParser): | |
def __init__(self, config_file): | |
super(MyConfig, self).__init__() | |
self.read(config_file) | |
self.validate_config() | |
def validate_config(self): | |
required_values = { | |
'server': { | |
'timeout': None, | |
'gc_mode': ('none', 'aggressive', 'smart', 'auto'), | |
'mode': ('master') | |
}, | |
'client': { | |
'fallback': ('none', 'polling', 'auto'), | |
'mode': ('master', 'slave') | |
} | |
} | |
""" | |
Notice the different mode validations for global mode setting: we can | |
enforce different value sets for different sections | |
""" | |
for section, keys in required_values.items(): | |
if section not in self: | |
raise MyException( | |
'Missing section %s in the config file' % section) | |
for key, values in keys.items(): | |
if key not in self[section] or self[section][key] == '': | |
raise MyException(( | |
'Missing value for %s under section %s in ' + | |
'the config file') % (key, section)) | |
if values: | |
if self[section][key] not in values: | |
raise MyException(( | |
'Invalid value for %s under section %s in ' + | |
'the config file') % (key, section)) | |
cfg = {} | |
try: | |
# The example config file has an invalid value so cfg will stay empty first | |
cfg = MyConfig('config.ini') | |
except MyException as e: | |
# Initially you'll see this due to the invalid value | |
print(e) | |
else: | |
# Once you fix the config file you'll see this | |
print(cfg['client']['fallback']) |
Cheers, good to know it's of use to someone 👍
I've just seen your sample.
I'm new to python.
Is there an elegant way to make the allow values more declarative.
For example: range (1,100)
to allow numbers 1 to 100 instead of noting down every single number
In my case None
is not an option for the numeric values
update:
I've found a solution, but stopped using ConfigParser because INI do not fit my needs
def valueRange(min, max):
return [str(x) for x in [*range(min, max + 1)]]
required_values = {
'root': {
'i2c_port': ('26'),
"hysteresis": '%s' % (valueRange(1,10))
},
'thresholds': [
{
'threshold': '%s' % (valueRange(35,100)),
'speed': '%s' % (valueRange(10,100))
}
]
}
Thanks for posting the solution. Yeah these days I'd probably recommend using a different config file format, something like YAML or JSON.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Exactly what I've been looking for! Danke :)