Skip to content

Commit d213146

Browse files
committed
finish version 0.0.1
1 parent 5468544 commit d213146

File tree

9 files changed

+132
-63
lines changed

9 files changed

+132
-63
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# loadit
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.
1+
# Importify
2+
Import and export your configuration like a boss !!
53

64
## Usage

examples/__init__.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
# temporary for test use
22
import sys
3-
sys.path.insert(0, "/Users/litcoderr/project/loadit")
3+
sys.path.insert(0, "/Users/litcoderr/project/importify")
44
#####
55
"""
66
This demonstrates how you can construct a serializable configuration object
77
"""
8-
from loadit import Serializable
8+
from importify import Serializable
99

1010

1111
class MasterConfig(Serializable):
1212
def __init__(self):
13-
self.loadit_is_awesome = True
14-
self.loadit_sub_config = NestedConfig()
13+
super().__init__()
14+
self.is_awesome = True
15+
self.nested = NestedConfig()
1516

1617

1718
class NestedConfig(Serializable):
1819
def __init__(self):
19-
self.loadit_is_legit = True
20-
self.loadit_sub_sub_config = NestedNestedConfig()
20+
self.is_legit = True
21+
self.double_nested = DoubleNestedConfig()
2122

2223

23-
class NestedNestedConfig(Serializable):
24+
class DoubleNestedConfig(Serializable):
2425
def __init__(self):
25-
self.loadit_can_go_deep = True
26+
self.is_intuitive = True

examples/export_dict.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
2-
This demo demonstrates export_dict() method that can extract dictionary from any Serializable object recursively.
3-
4-
Usage:
2+
This demo demonstrates export_dict() method
3+
that can extract dictionary from any Serializable object recursively.
54
"""
65
from . import MasterConfig
76

@@ -10,5 +9,3 @@
109
master_config = MasterConfig()
1110

1211
extracted_dict = master_config.export_dict()
13-
14-
print(extracted_dict)

examples/export_json.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
2-
This demo demonstrates export_json() method that saves Serializable object to json file.
3-
'
4-
Usage:
2+
This demo demonstrates export_json() method
3+
that saves Serializable object to json file.
54
"""
65
from . import MasterConfig
76

@@ -19,11 +18,8 @@
1918

2019
# Export to json
2120
succeded = master_config.export_json(path=file_path, ignore_error=True)
22-
2321
print('Save status: {succeded}'.format(succeded=succeded))
2422

2523
# Now lets see if json file has been succesfully saved.
2624
with open(file_path, 'r') as file:
2725
data = json.load(file)
28-
print(data)
29-

examples/exported.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"loadit_is_awesome": true, "loadit_sub_config": {"loadit_is_legit": true, "loadit_sub_sub_config": {"loadit_can_go_deep": true}}}
1+
{"is_awesome": true, "nested": {"is_legit": true, "double_nested": {"is_intuitive": true}}}

examples/import_json.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,16 @@
11
"""
2-
This demo demonstrates import_json() method that imports data from json file and loads recursively.
3-
4-
Usage:
2+
This demo demonstrates import_json() method
3+
that imports data from json file and loads recursively.
54
"""
65
from . import MasterConfig
76

87

98
if __name__ == "__main__":
109
import os
11-
import json
1210

1311
# Set json file path
1412
current_file_path = os.path.dirname(os.path.abspath(__file__))
1513
file_path = os.path.join(current_file_path, "exported.json")
1614

1715
# 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-
16+
succeded, master_config = MasterConfig.import_json(path=file_path, ignore_error=True)

examples/parse.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
"""
22
This demo demonstrates parse() method that implements argument parsing fucntionality based on object elements.
3-
4-
Usage:
53
"""
64

75
from . import MasterConfig
86

97

108
if __name__ == "__main__":
9+
# Initialize Serializable Instance
1110
master_config = MasterConfig()
12-
print('before parsing:')
13-
print(master_config.export_dict())
14-
15-
# parse
11+
12+
# Parse. Done !!
1613
master_config.parse()
17-
18-
print('after parseing:')
14+
15+
# Checkout result
1916
print(master_config.export_dict())
2017

importify/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
# Copyright @Litcoderr. github: https://github.com/litcoderr
22

33
from .interface import Serializable
4-

importify/interface.py

Lines changed: 108 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,46 @@
1+
2+
13
from typing import Tuple, Dict, Any, Optional, List
24
import sys
35
import json
46
import argparse
57

6-
DELIM = "__"
8+
DELIM = "."
9+
10+
11+
def json_serializable(x):
12+
try:
13+
json.dumps(x)
14+
return True
15+
except:
16+
return False
717

818

919
class Serializable:
1020
def __init__(self):
11-
pass
21+
self.parser = SerializableParser(instance=self)
1222

1323
# Export Methods
1424
def export_dict(self) -> Dict[str, Any]:
1525
"""export dictionary recursively
16-
26+
1727
Returns:
1828
Dictionary that consists child arguments recursively.
1929
"""
2030
# Get current item in dictionary
2131
parent_dict = self.__dict__.copy()
2232

2333
# Build child dictionary recursively
34+
delete_queue = []
2435
for key, obj in parent_dict.items():
2536
if isinstance(obj, Serializable):
2637
parent_dict[key] = obj.export_dict()
38+
elif not json_serializable(obj):
39+
delete_queue.append(key)
40+
41+
# delete non json serializables
42+
for key in delete_queue:
43+
del parent_dict[key]
2744

2845
return parent_dict
2946

@@ -45,16 +62,16 @@ def export_json(self, path: str, ignore_error=True) -> bool:
4562
with open(path, 'w') as file:
4663
file.write(json.dumps(extracted_dict, ensure_ascii=False))
4764
except Exception as e:
48-
succeed= False
49-
print(e)
65+
succeed = False
66+
print(e)
5067
if not ignore_error:
5168
sys.exit()
5269
return succeed
53-
70+
5471
# Import Methods
5572
def import_dict(self, data: Dict[str, Any]):
5673
"""Import arguments from dictionary
57-
74+
5875
Args:
5976
data: dictionary that consists child argument recursively.
6077
"""
@@ -65,9 +82,9 @@ def import_dict(self, data: Dict[str, Any]):
6582
else:
6683
setattr(self, key, value)
6784
return self
68-
85+
6986
@classmethod
70-
def import_json(cls, path: str, ignore_error: bool = True)\
87+
def import_json(cls, path: str, ignore_error: bool = True) \
7188
-> Tuple[bool, Optional['Serializable']]:
7289
try:
7390
with open(path, 'r') as file:
@@ -78,12 +95,15 @@ def import_json(cls, path: str, ignore_error: bool = True)\
7895
if not ignore_error:
7996
sys.exit()
8097
return False, None
81-
82-
# Parsing related method
98+
99+
# Parsing Method
83100
def parse(self):
101+
self.parser.parse()
102+
103+
def parse_(self):
84104
"""Implement argument parsing functionality based on object elements
85105
"""
86-
parser = argparse.ArgumentParser()
106+
parser = argparse.ArgumentParser()
87107
for key, value in self.strip_dict().items():
88108
parser.add_argument('--{}'.format(key), type=type(value))
89109
args = parser.parse_args()
@@ -103,24 +123,56 @@ def strip_dict(self, prefix: str = "") -> Dict[str, Any]:
103123
stripped_dict = {}
104124
for key, value in self.__dict__.items():
105125
if isinstance(value, Serializable):
106-
for k, v in value.strip_dict(prefix=prefix+key+DELIM).items():
126+
for k, v in value.strip_dict(prefix=prefix + key + DELIM).items():
107127
stripped_dict[k] = v
108128
else:
109-
stripped_dict[prefix+key] = value
129+
stripped_dict[prefix + key] = value
110130
return stripped_dict
111-
112-
def unstrip_dict(self, data: Dict[str, Any]):
131+
132+
# Utility
133+
@staticmethod
134+
def is_base(key):
135+
BASE = ['parser']
136+
return key in BASE
137+
138+
def strip(self, prefix: str = "") -> Dict[str, Any]:
139+
""" Strip arguments.
140+
141+
Args:
142+
instance: Parent Serializable instance
143+
prefix: Prefix string.
144+
145+
Returns:
146+
Stripped dictionary.
147+
"""
148+
stripped_dicts = {}
149+
for key, value in self.__dict__.items():
150+
if isinstance(value, Serializable):
151+
for child_key, child_value in value.strip(prefix=prefix+key+DELIM).items():
152+
stripped_dicts[child_key] = child_value
153+
elif Serializable.is_base(key):
154+
continue
155+
else:
156+
stripped_dicts[prefix+key] = value
157+
return stripped_dicts
158+
159+
def unstrip(self, data: Dict[str, Any]) -> Dict[str, Dict[str, Any]]:
113160
"""Unstrip parsed dictionary and save.
114161
115162
Args:
116163
data: stripped dictionary
164+
165+
Returns:
166+
Dictionary consisting reference and key to value
117167
"""
168+
result = {}
118169
for key, value in data.items():
119170
key_path = key.split(DELIM)
120171
ref, k = self.get_attribute(key_path)
121-
setattr(ref, k, value)
172+
result[key] = {"reference": ref, "key": k, "value": value}
173+
return result
122174

123-
def get_attribute(self, key_path: List[str]):
175+
def get_attribute(self, key_path: List[str]) -> Tuple[Any, str]:
124176
"""Return key path corresponding instance
125177
126178
Args:
@@ -134,3 +186,41 @@ def get_attribute(self, key_path: List[str]):
134186
return getattr(self, child_attr_name).get_attribute(key_path)
135187
else:
136188
return self, key_path[0]
189+
190+
191+
class SerializableParser:
192+
def __init__(self, instance):
193+
self.instance = instance
194+
195+
def parse(self):
196+
"""Parse method.
197+
"""
198+
# Strip dicts for parser
199+
dicts = self.instance.strip()
200+
201+
# Parse
202+
boolean_keys = []
203+
parser = argparse.ArgumentParser()
204+
for key, value in dicts.items():
205+
if type(value) == bool:
206+
boolean_keys.append(key)
207+
if value:
208+
default = "True"
209+
else:
210+
default = "False"
211+
parser.add_argument('--{key}'.format(key=key), type=str, choices=["True", "False"], default=default,
212+
help="boolean option.")
213+
else:
214+
parser.add_argument('--{key}'.format(key=key), type=type(value), default=value)
215+
args = vars(parser.parse_args())
216+
217+
# Deal with stringed boolean attributes
218+
for key in boolean_keys:
219+
if args[key] == "True":
220+
args[key] = True
221+
else:
222+
args[key] = False
223+
224+
# Unstrip and Update
225+
for stripped_key, res in self.instance.unstrip(args).items():
226+
setattr(res["reference"], res["key"], res["value"])

0 commit comments

Comments
 (0)