How to validate structure (or schema) of dictionary in Python?
You may use schema
(PyPi Link)
schema is a library for validating Python data structures, such as those obtained from config-files, forms, external services or command-line parsing, converted from JSON/YAML (or something else) to Python data-types.
from schema import Schema, And, Use, Optional, SchemaErrordef check(conf_schema, conf): try: conf_schema.validate(conf) return True except SchemaError: return Falseconf_schema = Schema({ 'version': And(Use(int)), 'info': { 'conf_one': And(Use(float)), 'conf_two': And(Use(str)), 'conf_three': And(Use(bool)), Optional('optional_conf'): And(Use(str)) }})conf = { 'version': 1, 'info': { 'conf_one': 2.5, 'conf_two': 'foo', 'conf_three': False, 'optional_conf': 'bar' }}print(check(conf_schema, conf))
Without using libraries, you could also define a simple recursive function like this:
def check_structure(struct, conf): if isinstance(struct, dict) and isinstance(conf, dict): # struct is a dict of types or other dicts return all(k in conf and check_structure(struct[k], conf[k]) for k in struct) if isinstance(struct, list) and isinstance(conf, list): # struct is list in the form [type or dict] return all(check_structure(struct[0], c) for c in conf) elif isinstance(struct, type): # struct is the type of conf return isinstance(conf, struct) else: # struct is neither a dict, nor list, not type return False
This assumes that the config can have keys that are not in your structure, as in your example.
Update: New version also supports lists, e.g. like 'foo': [{'bar': int}]
You can build structure using recursion:
def get_type(value): if isinstance(value, dict): return {key: get_type(value[key]) for key in value} else: return str(type(value))
And then compare required structure with your dictionary:
get_type(current_conf) == get_type(required_conf)
Example:
required_conf = { 'version': 1, 'info': { 'conf_one': 2.5, 'conf_two': 'foo', 'conf_three': False, 'optional_conf': 'bar' }}get_type(required_conf){'info': {'conf_two': "<type 'str'>", 'conf_one': "<type 'float'>", 'optional_conf': "<type 'str'>", 'conf_three': "<type 'bool'>"}, 'version': "<type 'int'>"}