1
+
2
+
1
3
from typing import Tuple , Dict , Any , Optional , List
2
4
import sys
3
5
import json
4
6
import argparse
5
7
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
7
17
8
18
9
19
class Serializable :
10
20
def __init__ (self ):
11
- pass
21
+ self . parser = SerializableParser ( instance = self )
12
22
13
23
# Export Methods
14
24
def export_dict (self ) -> Dict [str , Any ]:
15
25
"""export dictionary recursively
16
-
26
+
17
27
Returns:
18
28
Dictionary that consists child arguments recursively.
19
29
"""
20
30
# Get current item in dictionary
21
31
parent_dict = self .__dict__ .copy ()
22
32
23
33
# Build child dictionary recursively
34
+ delete_queue = []
24
35
for key , obj in parent_dict .items ():
25
36
if isinstance (obj , Serializable ):
26
37
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 ]
27
44
28
45
return parent_dict
29
46
@@ -45,16 +62,16 @@ def export_json(self, path: str, ignore_error=True) -> bool:
45
62
with open (path , 'w' ) as file :
46
63
file .write (json .dumps (extracted_dict , ensure_ascii = False ))
47
64
except Exception as e :
48
- succeed = False
49
- print (e )
65
+ succeed = False
66
+ print (e )
50
67
if not ignore_error :
51
68
sys .exit ()
52
69
return succeed
53
-
70
+
54
71
# Import Methods
55
72
def import_dict (self , data : Dict [str , Any ]):
56
73
"""Import arguments from dictionary
57
-
74
+
58
75
Args:
59
76
data: dictionary that consists child argument recursively.
60
77
"""
@@ -65,9 +82,9 @@ def import_dict(self, data: Dict[str, Any]):
65
82
else :
66
83
setattr (self , key , value )
67
84
return self
68
-
85
+
69
86
@classmethod
70
- def import_json (cls , path : str , ignore_error : bool = True )\
87
+ def import_json (cls , path : str , ignore_error : bool = True ) \
71
88
-> Tuple [bool , Optional ['Serializable' ]]:
72
89
try :
73
90
with open (path , 'r' ) as file :
@@ -78,12 +95,15 @@ def import_json(cls, path: str, ignore_error: bool = True)\
78
95
if not ignore_error :
79
96
sys .exit ()
80
97
return False , None
81
-
82
- # Parsing related method
98
+
99
+ # Parsing Method
83
100
def parse (self ):
101
+ self .parser .parse ()
102
+
103
+ def parse_ (self ):
84
104
"""Implement argument parsing functionality based on object elements
85
105
"""
86
- parser = argparse .ArgumentParser ()
106
+ parser = argparse .ArgumentParser ()
87
107
for key , value in self .strip_dict ().items ():
88
108
parser .add_argument ('--{}' .format (key ), type = type (value ))
89
109
args = parser .parse_args ()
@@ -103,24 +123,56 @@ def strip_dict(self, prefix: str = "") -> Dict[str, Any]:
103
123
stripped_dict = {}
104
124
for key , value in self .__dict__ .items ():
105
125
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 ():
107
127
stripped_dict [k ] = v
108
128
else :
109
- stripped_dict [prefix + key ] = value
129
+ stripped_dict [prefix + key ] = value
110
130
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 ]]:
113
160
"""Unstrip parsed dictionary and save.
114
161
115
162
Args:
116
163
data: stripped dictionary
164
+
165
+ Returns:
166
+ Dictionary consisting reference and key to value
117
167
"""
168
+ result = {}
118
169
for key , value in data .items ():
119
170
key_path = key .split (DELIM )
120
171
ref , k = self .get_attribute (key_path )
121
- setattr (ref , k , value )
172
+ result [key ] = {"reference" : ref , "key" : k , "value" : value }
173
+ return result
122
174
123
- def get_attribute (self , key_path : List [str ]):
175
+ def get_attribute (self , key_path : List [str ]) -> Tuple [ Any , str ] :
124
176
"""Return key path corresponding instance
125
177
126
178
Args:
@@ -134,3 +186,41 @@ def get_attribute(self, key_path: List[str]):
134
186
return getattr (self , child_attr_name ).get_attribute (key_path )
135
187
else :
136
188
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