Skip to content

Commit e199fe2

Browse files
Merge pull request #1 from BrunoSilvaAndrade/release/2.0.0
Release/2.0.0
2 parents 5ab8e1d + a8c439b commit e199fe2

13 files changed

Lines changed: 227 additions & 163 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea/
2+
python_json_config_parser.egg-info/

README.md

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
# python-json-config-parser
2-
This project was created to give you the possibility of creating json config files dynamicaly using OOP
1+
# python-config-parser
2+
This project was created to give you the possibility of creating json and yaml/yml config files dynamically using OOP
33

44
HOW TO INSTALL
55
---------------------------
66
Use pip to install it.
77

88
```
9-
pip install python-json-config-parser
9+
pip install python-config-parser
1010
```
1111

1212

1313
HOW TO USE
1414
---------------------------
15-
The Class Config takes two arguments, the first is a Schema(https://github.com/keleshev/schema) to ensure the model of config, the second is the str path of json config file.
16-
17-
If you don't specify the path, the default value('config.json') will be used if one exists in the same dir where the script is running.
18-
19-
For example:
2015

2116
The model file.
2217
```
@@ -39,35 +34,26 @@ SCHEMA_CONFIG = {
3934
4035
```
4136

42-
The config.json file
37+
The config.yml file
4338
```
44-
{
45-
"core":{
46-
"logging":{
47-
"format":"[%(asctime)s][%(levelname)s]: %(message)s",
48-
"datefmt": "%d-%b-%y %H:%M:%S"
49-
},
50-
"allowed_clients":[
51-
{
52-
"ip":"192.168.0.10",
53-
"timeout":60
54-
},
55-
{
56-
"ip":"192.168.0.11",
57-
"timeout":100
58-
}
59-
]
60-
}
61-
}
39+
core:
40+
logging:
41+
format: "[%(asctime)s][%(levelname)s]: %(message)s"
42+
datefmt: "%d-%b-%y %H:%M:%S"
43+
allowed_clients:
44+
- ip: 192.168.0.10
45+
timeout: 60
46+
- ip: 192.168.0.11
47+
timeout: 100
6248
```
6349

64-
The istance of Config Class:
50+
The instance of Config Class:
6551
```
66-
from jsonconfigparser import Config, ConfigException
52+
from pyconfigparser import Config, ConfigException
6753
import logging
6854
6955
try:
70-
config = Config(SCHEMA_CONFIG) # <- Here I'm using that SCHEMA_CONFIG we had declared, and the dir file default value is being used
56+
config = Config.get_config(SCHEMA_CONFIG) # <- Here I'm using that SCHEMA_CONFIG we had declared, and the dir file default value is being used
7157
except ConfigException as e:
7258
print(e)
7359
exit()
@@ -83,7 +69,7 @@ logging.getLogger(__name__)
8369
logging.basicConfig(
8470
format=fmt,
8571
datefmt=dtfmt
86-
level=20
72+
level=logging.INFO
8773
)
8874
8975
#the list of object example:
@@ -98,13 +84,13 @@ Instanced the first obj, you can instance Config in other files of your project
9884
just calling the Config without args like that:
9985

10086
```
101-
from jsonconfigparser import Config, ConfigException
87+
from pyconfigparser import Config, ConfigException
10288
103-
config = Config() #At this point you already have the configuration properties in your config object
89+
config = Config.get_config() #At this point you already have the configuration properties in your config object
10490
```
10591

10692

10793
CONTRIBUTE
10894
----------
10995

110-
Fork https://github.com/BrunoSilvaAndrade/python-json-config-parser/ , create commit and pull request to ``develop``.
96+
Fork https://github.com/BrunoSilvaAndrade/python-config-parser/ , create commit and pull request to ``develop``.

config/config.bad_extension

Whitespace-only changes.
File renamed without changes.

config/schema_config_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
SIMPLE_SCHEMA_CONFIG = {
2+
'core': {
3+
'logging': {
4+
'format': str,
5+
'datefmt': str
6+
},
7+
'obj_list': [{'name': str, 'age': int}]
8+
}
9+
}

jsonconfigparser.py

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

pyconfigparser.py

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

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

schema_config_test.py

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

0 commit comments

Comments
 (0)