Skip to content

Commit 6badb9f

Browse files
BrunoSilvaAndradeBrunoSilvaAndrade
authored andcommitted
Turning configparser.py able to handle json and yaml/yml files
1 parent 5ab8e1d commit 6badb9f

4 files changed

Lines changed: 143 additions & 81 deletions

File tree

configparser.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import os
2+
import re
3+
import yaml
4+
import json
5+
from os import path
6+
from typing import Any
7+
from schema import Schema, SchemaError
8+
9+
10+
ENTITY_NAME_PATTERN = '^[\w\d_]+$'
11+
12+
13+
def json_parser(file_buff):
14+
try:
15+
return json.loads(file_buff)
16+
except json.JSONDecodeError as e:
17+
raise ConfigFileDecodeError(e)
18+
19+
20+
def yaml_parser(file_buff):
21+
try:
22+
return yaml.load(file_buff, yaml.FullLoader)
23+
except yaml.YAMLError as e:
24+
raise ConfigFileDecodeError(e)
25+
26+
27+
DEFAULT_CONFIG_FILES = ('config.json', 'config.yaml', 'config.yml')
28+
29+
SUPPORTED_EXTENSIONS = {
30+
'json': json_parser,
31+
'yaml': yaml_parser,
32+
'yml': yaml_parser
33+
}
34+
35+
36+
class Config(object):
37+
__instance = None
38+
39+
def __new__(cls, schema: dict = None, config_dir: str = 'config', file_name: Any = DEFAULT_CONFIG_FILES):
40+
41+
if cls.__instance is None or schema is not None:
42+
cls.__create_new_instance(schema, config_dir, file_name)
43+
return cls.__instance
44+
45+
@classmethod
46+
def __create_new_instance(cls, schema, config_dir, file_name):
47+
cls.__check_schema(schema)
48+
file_path = cls.__get_file_path(config_dir, file_name)
49+
parser = cls.__get_file_parser(file_path)
50+
file_buff = cls.__get_file_buff(file_path)
51+
52+
try:
53+
config = Schema(schema).validate(parser(file_buff))
54+
cls.__instance = cls.__dict_2_obj(config)
55+
except SchemaError as e:
56+
raise ConfigFileModelError(str(e))
57+
58+
@classmethod
59+
def __get_file_parser(cls, file_path):
60+
try:
61+
extension = file_path.split('.')[-1]
62+
return SUPPORTED_EXTENSIONS[extension]
63+
except KeyError:
64+
raise ConfigFileExtensionNotSupportedError(f'Supported extensions: {list(SUPPORTED_EXTENSIONS.keys())}')
65+
66+
@classmethod
67+
def __get_file_path(cls, config_dir, file_name):
68+
file_path = f'{os.getcwd()}/{config_dir}/'
69+
if type(file_name) is str:
70+
file_name = [file_name]
71+
72+
for f_name in file_name:
73+
if path.isfile(file_path + f_name):
74+
return file_path + f_name
75+
76+
raise ConfigFileNotFoundError(f'Config file {file_path}{file_name} was not found')
77+
78+
@classmethod
79+
def __check_schema(cls, schema):
80+
if schema is None:
81+
raise ConfigError('The schema config can not be None')
82+
if type(schema) is not dict:
83+
raise ConfigError('The first config\'s schema element should be a Map')
84+
85+
@classmethod
86+
def __get_file_buff(cls, path_file: str):
87+
try:
88+
with open(path_file, 'r') as f:
89+
return f.read()
90+
except Exception as e:
91+
raise ConfigFileOpenReadError(str(e))
92+
93+
@classmethod
94+
def __dict_2_obj(cls, data: Any):
95+
_type = type(data)
96+
97+
if _type is dict:
98+
obj = object.__new__(cls)
99+
for key, value in data.items():
100+
if re.search(ENTITY_NAME_PATTERN, key) is None:
101+
raise ConfigEntitiesWithWrongNameError(
102+
'The entity keys only may have words, number and underscores')
103+
setattr(obj, key, cls.__dict_2_obj(value))
104+
return obj
105+
if _type in (list, set, tuple):
106+
return list(map(lambda v: cls.__dict_2_obj(v), data))
107+
else:
108+
return data
109+
110+
111+
class ConfigError(Exception):
112+
pass
113+
114+
115+
class ConfigFileModelError(ConfigError):
116+
pass
117+
118+
119+
class ConfigFileDecodeError(ConfigError):
120+
pass
121+
122+
123+
class ConfigSchemaModelError(ConfigError):
124+
pass
125+
126+
127+
class ConfigFileOpenReadError(ConfigError):
128+
pass
129+
130+
131+
class ConfigFileNotFoundError(ConfigError):
132+
pass
133+
134+
135+
class ConfigFileExtensionNotSupportedError(ConfigError):
136+
pass
137+
138+
139+
class ConfigEntitiesWithWrongNameError(ConfigError):
140+
pass

jsonconfigparser.py

Lines changed: 0 additions & 79 deletions
This file was deleted.

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
schema>=0.7.1
1+
schema>=0.7.1
2+
PyYAML~=6.0

test_jsonconfigparser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import unittest
2-
from jsonconfigparser import *
2+
from configparser import *
33
from schema_config_test import SIMPLE_SCHEMA_CONFIG
44

55

0 commit comments

Comments
 (0)