Skip to content

Commit c6259c1

Browse files
committed
master: initial update
1 parent e6eb2b9 commit c6259c1

File tree

10 files changed

+261
-1
lines changed

10 files changed

+261
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,5 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
.idea
131+

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# loadit
2-
Load and save your configuration like a BOSS
2+
Load and save your configuration like a BOSS !!!
3+
4+
Have you ever experienced parsing code for your configuration being verbose? LoadIt neatly organizes your configuration in one python object and can be saved or imported from json file. Also if you ever need cli argument parsing feature, you are good to go with one line of code. Folks are always welcome to contribute.
5+
6+
## Usage

examples/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# temporary for test use
2+
import sys
3+
sys.path.insert(0, "/Users/litcoderr/project/loadit")
4+
#####
5+
"""
6+
This demonstrates how you can construct a serializable configuration object
7+
"""
8+
from loadit import Serializable
9+
10+
11+
class MasterConfig(Serializable):
12+
def __init__(self):
13+
self.loadit_is_awesome = True
14+
self.loadit_sub_config = NestedConfig()
15+
16+
17+
class NestedConfig(Serializable):
18+
def __init__(self):
19+
self.loadit_is_legit = True
20+
self.loadit_sub_sub_config = NestedNestedConfig()
21+
22+
23+
class NestedNestedConfig(Serializable):
24+
def __init__(self):
25+
self.loadit_can_go_deep = True

examples/export_dict.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
This demo demonstrates export_dict() method that can extract dictionary from any Serializable object recursively.
3+
4+
Usage:
5+
"""
6+
from . import MasterConfig
7+
8+
9+
if __name__ == "__main__":
10+
master_config = MasterConfig()
11+
12+
extracted_dict = master_config.export_dict()
13+
14+
print(extracted_dict)

examples/export_json.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
This demo demonstrates export_json() method that saves Serializable object to json file.
3+
'
4+
Usage:
5+
"""
6+
from . import MasterConfig
7+
8+
9+
if __name__ == "__main__":
10+
import os
11+
import json
12+
13+
# Initialize configuration object
14+
master_config = MasterConfig()
15+
16+
# Set json file path
17+
current_file_path = os.path.dirname(os.path.abspath(__file__))
18+
file_path = os.path.join(current_file_path, "exported.json")
19+
20+
# Export to json
21+
succeded = master_config.export_json(path=file_path, ignore_error=True)
22+
23+
print('Save status: {succeded}'.format(succeded=succeded))
24+
25+
# Now lets see if json file has been succesfully saved.
26+
with open(file_path, 'r') as file:
27+
data = json.load(file)
28+
print(data)
29+

examples/exported.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"loadit_is_awesome": true, "loadit_sub_config": {"loadit_is_legit": true, "loadit_sub_sub_config": {"loadit_can_go_deep": true}}}

examples/import_json.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
This demo demonstrates import_json() method that imports data from json file and loads recursively.
3+
4+
Usage:
5+
"""
6+
from . import MasterConfig
7+
8+
9+
if __name__ == "__main__":
10+
import os
11+
import json
12+
13+
# Set json file path
14+
current_file_path = os.path.dirname(os.path.abspath(__file__))
15+
file_path = os.path.join(current_file_path, "exported.json")
16+
17+
# Import from json
18+
succeded, master_config= MasterConfig.import_json(path=file_path, ignore_error=True)
19+
20+
if succeded:
21+
print('succeded...')
22+
print(master_config.export_dict())
23+
else:
24+
print('failed to import json')
25+

examples/parse.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
This demo demonstrates parse() method that implements argument parsing fucntionality based on object elements.
3+
4+
Usage:
5+
"""
6+
7+
from . import MasterConfig
8+
9+
10+
if __name__ == "__main__":
11+
master_config = MasterConfig()
12+
print('before parsing:')
13+
print(master_config.export_dict())
14+
15+
# parse
16+
master_config.parse()
17+
18+
print('after parseing:')
19+
print(master_config.export_dict())
20+

loadit/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright @Litcoderr. github: https://github.com/litcoderr
2+
3+
from .interface import Serializable
4+

loadit/interface.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
from typing import Tuple, Dict, Any, Optional, List
2+
import sys
3+
import json
4+
import argparse
5+
6+
DELIM = "__"
7+
8+
9+
class Serializable:
10+
def __init__(self):
11+
pass
12+
13+
# Export Methods
14+
def export_dict(self) -> Dict[str, Any]:
15+
"""export dictionary recursively
16+
17+
Returns:
18+
Dictionary that consists child arguments recursively.
19+
"""
20+
# Get current item in dictionary
21+
parent_dict = self.__dict__.copy()
22+
23+
# Build child dictionary recursively
24+
for key, obj in parent_dict.items():
25+
if isinstance(obj, Serializable):
26+
parent_dict[key] = obj.export_dict()
27+
28+
return parent_dict
29+
30+
def export_json(self, path: str, ignore_error=True) -> bool:
31+
"""
32+
33+
Args:
34+
path: path of json file to be saved.
35+
36+
Returns:
37+
succeed saving json file or not.
38+
"""
39+
succeed = True
40+
try:
41+
# extract dictionary
42+
extracted_dict = self.export_dict()
43+
44+
# save as json
45+
with open(path, 'w') as file:
46+
file.write(json.dumps(extracted_dict, ensure_ascii=False))
47+
except Exception as e:
48+
succeed= False
49+
print(e)
50+
if not ignore_error:
51+
sys.exit()
52+
return succeed
53+
54+
# Import Methods
55+
def import_dict(self, data: Dict[str, Any]):
56+
"""Import arguments from dictionary
57+
58+
Args:
59+
data: dictionary that consists child argument recursively.
60+
"""
61+
for key, value in data.items():
62+
if hasattr(self, key):
63+
if isinstance(getattr(self, key), Serializable):
64+
setattr(self, key, getattr(self, key).import_dict(value))
65+
else:
66+
setattr(self, key, value)
67+
return self
68+
69+
@classmethod
70+
def import_json(cls, path: str, ignore_error: bool = True)\
71+
-> Tuple[bool, Optional['Serializable']]:
72+
try:
73+
with open(path, 'r') as file:
74+
data = json.load(file)
75+
return True, cls().import_dict(data)
76+
except Exception as e:
77+
print(e)
78+
if not ignore_error:
79+
sys.exit()
80+
return False, None
81+
82+
# Parsing related method
83+
def parse(self):
84+
"""Implement argument parsing functionality based on object elements
85+
"""
86+
parser = argparse.ArgumentParser()
87+
for key, value in self.strip_dict().items():
88+
parser.add_argument('--{}'.format(key), type=type(value))
89+
args = parser.parse_args()
90+
print(vars(args))
91+
self.unstrip_dict(vars(args))
92+
93+
# Utility
94+
def strip_dict(self, prefix: str = "") -> Dict[str, Any]:
95+
"""Strips dictionary recursively.
96+
97+
Args:
98+
prefix: prefix string.
99+
100+
Returns:
101+
dictionary with stripped keys.
102+
"""
103+
stripped_dict = {}
104+
for key, value in self.__dict__.items():
105+
if isinstance(value, Serializable):
106+
for k, v in value.strip_dict(prefix=prefix+key+DELIM).items():
107+
stripped_dict[k] = v
108+
else:
109+
stripped_dict[prefix+key] = value
110+
return stripped_dict
111+
112+
def unstrip_dict(self, data: Dict[str, Any]):
113+
"""Unstrip parsed dictionary and save.
114+
115+
Args:
116+
data: stripped dictionary
117+
"""
118+
for key, value in data.items():
119+
key_path = key.split(DELIM)
120+
ref, k = self.get_attribute(key_path)
121+
setattr(ref, k, value)
122+
123+
def get_attribute(self, key_path: List[str]):
124+
"""Return key path corresponding instance
125+
126+
Args:
127+
key_path: heirechical key path.
128+
129+
Returns:
130+
key path corresponding instance
131+
"""
132+
if len(key_path) > 1:
133+
child_attr_name = key_path.pop(0)
134+
return getattr(self, child_attr_name).get_attribute(key_path)
135+
else:
136+
return self, key_path[0]

0 commit comments

Comments
 (0)