Skip to content

Instantly share code, notes, and snippets.

@tsuriga
Created March 20, 2016 13:48
Show Gist options
  • Save tsuriga/5bc5bbfaf21c6a51cda7 to your computer and use it in GitHub Desktop.
Save tsuriga/5bc5bbfaf21c6a51cda7 to your computer and use it in GitHub Desktop.
Simple config file validation in python
[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
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'])
@tsuriga
Copy link
Author

tsuriga commented Dec 17, 2020

Cheers, good to know it's of use to someone 👍

@frudolph77
Copy link

frudolph77 commented Jan 28, 2022

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))
                }
            ]
        }

@tsuriga
Copy link
Author

tsuriga commented Feb 1, 2022

Thanks for posting the solution. Yeah these days I'd probably recommend using a different config file format, something like YAML or JSON.

@sangimed
Copy link

sangimed commented Sep 18, 2022

you can also use Cerberus to avoid the extra boilerplate code. There is a cool tutorial here and there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment