forked from UTK-ML-Dream-Team/accident-severity-prediction
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathconfiguration.py
177 lines (145 loc) · 6.04 KB
/
configuration.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import os
from typing import Dict, List, Tuple, Union
import json
import _io
from io import StringIO, TextIOWrapper
import re
import yaml
from jsonschema import validate as validate_json_schema
from project_libs import ColorizedLogger
logger = ColorizedLogger('Config', 'white')
class Configuration:
__slots__ = ('config', 'config_path', 'config_keys', 'tag')
config: Dict
config_path: str
tag: str
config_keys: List
env_variable_tag: str = '!ENV'
env_variable_pattern: str = r'.*?\${(\w+)}.*?' # ${var}
def __init__(self, config_src: Union[TextIOWrapper, StringIO, str],
config_schema_path: str = 'yml_schema.json'):
"""
The basic constructor. Creates a new instance of the Configuration class.
Args:
config_src: The path, file or StringIO object of the configuration to load
config_schema_path: The path, file or StringIO object of the configuration validation file
"""
# Load the predefined schema of the configuration
configuration_schema = self.load_configuration_schema(config_schema_path=config_schema_path)
# Load the configuration
self.config, self.config_path = self.load_yml(config_src=config_src,
env_tag=self.env_variable_tag,
env_pattern=self.env_variable_pattern)
# Validate the config
validate_json_schema(self.config, configuration_schema)
logger.debug("Schema Validation was Successful.")
# Set the config properties as instance attributes
self.tag = self.config['tag']
self.config_keys = [key for key in self.config.keys() if key != 'tag']
logger.info(f"Configuration file loaded successfully from path: {self.config_path}")
logger.info(f"Configuration Tag: {self.tag}")
@staticmethod
def load_configuration_schema(config_schema_path: str) -> Dict:
"""
Loads the configuration schema file
Args:
config_schema_path: The path of the config schema
Returns:
configuration_schema: The loaded config schema
"""
if config_schema_path[0] != os.sep:
config_schema_path = '/'.join(
[os.path.dirname(os.path.realpath(__file__)), config_schema_path])
with open(config_schema_path) as f:
configuration_schema = json.load(f)
return configuration_schema
@staticmethod
def load_yml(config_src: Union[TextIOWrapper, StringIO, str], env_tag: str, env_pattern: str) -> \
Tuple[Dict, str]:
"""
Loads the configuration file
Args:
config_src: The path of the configuration
env_tag: The tag that distinguishes the env variables
env_pattern: The regex for finding the env variables
Returns:
config, config_path
"""
pattern = re.compile(env_pattern)
loader = yaml.SafeLoader
loader.add_implicit_resolver(env_tag, pattern, None)
def constructor_env_variables(loader, node):
"""
Extracts the environment variable from the node's value
:param yaml.Loader loader: the yaml loader
:param node: the current node in the yaml
:return: the parsed string that contains the value of the environment
variable
"""
value = loader.construct_scalar(node)
match = pattern.findall(value) # to find all env variables in line
if match:
full_value = value
for g in match:
full_value = full_value.replace(
f'${{{g}}}', os.environ.get(g, g)
)
return full_value
return value
loader.add_constructor(env_tag, constructor_env_variables)
if isinstance(config_src, TextIOWrapper):
logger.debug("Loading yaml from TextIOWrapper")
config = yaml.load(config_src, Loader=loader)
config_path = os.path.abspath(config_src.name)
elif isinstance(config_src, StringIO):
logger.debug("Loading yaml from StringIO")
config = yaml.load(config_src, Loader=loader)
config_path = "StringIO"
elif isinstance(config_src, str):
config_path = os.path.abspath(config_src)
logger.debug("Loading yaml from path")
with open(config_path) as f:
config = yaml.load(f, Loader=loader)
else:
raise TypeError('Config file must be TextIOWrapper or path to a file')
return config, config_path
def get_config(self, config_name) -> Dict:
"""
Returns the subconfig requested
Args:
config_name: The name of the subconfig
Returns:
sub_config: The sub_configs List
"""
if config_name in self.config.keys():
return self.config[config_name]
else:
raise ConfigurationError('Config property %s not set!' % config_name)
def to_yml(self, fn: Union[str, _io.TextIOWrapper]) -> None:
"""
Writes the configuration to a stream. For example a file.
Args:
fn:
Returns:
"""
self.config['tag'] = self.tag
if isinstance(fn, str):
with open(fn, 'w') as f:
yaml.dump(self.config, f, default_flow_style=False)
elif isinstance(fn, _io.TextIOWrapper):
yaml.dump(self.config, fn, default_flow_style=False)
else:
raise TypeError('Expected str or _io.TextIOWrapper not %s' % (type(fn)))
to_yaml = to_yml
def to_json(self) -> Dict:
"""
Returns the whole config file
Returns:
"""
return self.config
# def __getitem__(self, item):
# return self.get_config(item)
class ConfigurationError(Exception):
def __init__(self, message):
# Call the base class constructor with the parameters it needs
super().__init__(message)