@@ -70,6 +70,162 @@ def split_at_given_level(
70
70
NoneType = type (None )
71
71
72
72
73
+ def list_parser (
74
+ cls : Type , type_ : TypeAlias , parsers : Optional [Dict [type , Callable [[str ], Any ]]] = None
75
+ ) -> partial :
76
+ """
77
+ Returns a function that parses a stringified list into a `List` of the correct type.
78
+
79
+ Args:
80
+ cls: the type of the class object this is being parsed for (used to get default val for
81
+ parsers)
82
+ type_: the type of the attribute to be parsed
83
+ parsers: an optional mapping from type to the function to use for parsing that type (allows
84
+ for parsing of more complex types)
85
+ """
86
+ subtypes = typing .get_args (type_ )
87
+ assert len (subtypes ) == 1 , "Lists are allowed only one subtype per PEP specification!"
88
+ subtype_parser = _get_parser (
89
+ cls ,
90
+ subtypes [0 ],
91
+ parsers ,
92
+ )
93
+ return functools .partial (
94
+ lambda s : list (
95
+ []
96
+ if s == ""
97
+ else [subtype_parser (item ) for item in list (split_at_given_level (s , split_delim = "," ))]
98
+ )
99
+ )
100
+
101
+
102
+ def set_parser (
103
+ cls : Type , type_ : TypeAlias , parsers : Optional [Dict [type , Callable [[str ], Any ]]] = None
104
+ ) -> partial :
105
+ """
106
+ Returns a function that parses a stringified set into a `Set` of the correct type.
107
+
108
+ Args:
109
+ cls: the type of the class object this is being parsed for (used to get default val for
110
+ parsers)
111
+ type_: the type of the attribute to be parsed
112
+ parsers: an optional mapping from type to the function to use for parsing that type (allows
113
+ for parsing of more complex types)
114
+ """
115
+ subtypes = typing .get_args (type_ )
116
+ assert len (subtypes ) == 1 , "Sets are allowed only one subtype per PEP specification!"
117
+ subtype_parser = _get_parser (
118
+ cls ,
119
+ subtypes [0 ],
120
+ parsers ,
121
+ )
122
+ return functools .partial (
123
+ lambda s : set (
124
+ set ({})
125
+ if s == "{}"
126
+ else [
127
+ subtype_parser (item )
128
+ for item in set (split_at_given_level (s [1 :- 1 ], split_delim = "," ))
129
+ ]
130
+ )
131
+ )
132
+
133
+
134
+ def tuple_parser (
135
+ cls : Type , type_ : TypeAlias , parsers : Optional [Dict [type , Callable [[str ], Any ]]] = None
136
+ ) -> partial :
137
+ """
138
+ Returns a function that parses a stringified tuple into a `Tuple` of the correct type.
139
+
140
+ Args:
141
+ cls: the type of the class object this is being parsed for (used to get default val for
142
+ parsers)
143
+ type_: the type of the attribute to be parsed
144
+ parsers: an optional mapping from type to the function to use for parsing that type (allows
145
+ for parsing of more complex types)
146
+ """
147
+ subtype_parsers = [
148
+ _get_parser (
149
+ cls ,
150
+ subtype ,
151
+ parsers ,
152
+ )
153
+ for subtype in typing .get_args (type_ )
154
+ ]
155
+
156
+ def tuple_parse (tuple_string : str ) -> Tuple [Any , ...]:
157
+ """
158
+ Parses a dictionary value (can do so recursively)
159
+ Note that this tool will fail on tuples containing strings containing
160
+ unpaired '{', or '}' characters
161
+ """
162
+ assert tuple_string [0 ] == "(" , "Tuple val improperly formatted"
163
+ assert tuple_string [- 1 ] == ")" , "Tuple val improperly formatted"
164
+ tuple_string = tuple_string [1 :- 1 ]
165
+ if len (tuple_string ) == 0 :
166
+ return ()
167
+ else :
168
+ val_strings = split_at_given_level (tuple_string , split_delim = "," )
169
+ return tuple (parser (val_str ) for parser , val_str in zip (subtype_parsers , val_strings ))
170
+
171
+ return functools .partial (tuple_parse )
172
+
173
+
174
+ def dict_parser (
175
+ cls : Type , type_ : TypeAlias , parsers : Optional [Dict [type , Callable [[str ], Any ]]] = None
176
+ ) -> partial :
177
+ """
178
+ Returns a function that parses a stringified dict into a `Dict` of the correct type.
179
+
180
+ Args:
181
+ cls: the type of the class object this is being parsed for (used to get default val for
182
+ parsers)
183
+ type_: the type of the attribute to be parsed
184
+ parsers: an optional mapping from type to the function to use for parsing that type (allows
185
+ for parsing of more complex types)
186
+ """
187
+ subtypes = typing .get_args (type_ )
188
+ assert len (subtypes ) == 2 , "Dict object must have exactly 2 subtypes per PEP specification!"
189
+ (key_parser , val_parser ) = (
190
+ _get_parser (
191
+ cls ,
192
+ subtypes [0 ],
193
+ parsers ,
194
+ ),
195
+ _get_parser (
196
+ cls ,
197
+ subtypes [1 ],
198
+ parsers ,
199
+ ),
200
+ )
201
+
202
+ def dict_parse (dict_string : str ) -> Dict [Any , Any ]:
203
+ """
204
+ Parses a dictionary value (can do so recursively)
205
+ """
206
+ assert dict_string [0 ] == "{" , "Dict val improperly formatted"
207
+ assert dict_string [- 1 ] == "}" , "Dict val improprly formatted"
208
+ dict_string = dict_string [1 :- 1 ]
209
+ if len (dict_string ) == 0 :
210
+ return {}
211
+ else :
212
+ outer_splits = split_at_given_level (dict_string , split_delim = "," )
213
+ out_dict = {}
214
+ for outer_split in outer_splits :
215
+ inner_splits = split_at_given_level (outer_split , split_delim = ";" )
216
+ assert (
217
+ len (inner_splits ) % 2 == 0
218
+ ), "Inner splits of dict didn't have matched key val pairs"
219
+ for i in range (0 , len (inner_splits ), 2 ):
220
+ key = key_parser (inner_splits [i ])
221
+ if key in out_dict :
222
+ raise ValueError ("Duplicate key found in dict: {}" .format (key ))
223
+ out_dict [key ] = val_parser (inner_splits [i + 1 ])
224
+ return out_dict
225
+
226
+ return functools .partial (dict_parse )
227
+
228
+
73
229
def _get_parser (
74
230
cls : Type , type_ : TypeAlias , parsers : Optional [Dict [type , Callable [[str ], Any ]]] = None
75
231
) -> partial :
@@ -110,118 +266,13 @@ def get_parser() -> partial:
110
266
elif type_ == dict :
111
267
raise ValueError ("Unable to parse dict (try typing.Mapping[type])" )
112
268
elif typing .get_origin (type_ ) == list :
113
- subtypes = typing .get_args (type_ )
114
-
115
- assert (
116
- len (subtypes ) == 1
117
- ), "Lists are allowed only one subtype per PEP specification!"
118
- subtype_parser = _get_parser (
119
- cls ,
120
- subtypes [0 ],
121
- parsers ,
122
- )
123
- return functools .partial (
124
- lambda s : list (
125
- []
126
- if s == ""
127
- else [
128
- subtype_parser (item )
129
- for item in list (split_at_given_level (s , split_delim = "," ))
130
- ]
131
- )
132
- )
269
+ return list_parser (cls , type_ , parsers )
133
270
elif typing .get_origin (type_ ) == set :
134
- subtypes = typing .get_args (type_ )
135
- assert (
136
- len (subtypes ) == 1
137
- ), "Sets are allowed only one subtype per PEP specification!"
138
- subtype_parser = _get_parser (
139
- cls ,
140
- subtypes [0 ],
141
- parsers ,
142
- )
143
- return functools .partial (
144
- lambda s : set (
145
- set ({})
146
- if s == "{}"
147
- else [
148
- subtype_parser (item )
149
- for item in set (split_at_given_level (s [1 :- 1 ], split_delim = "," ))
150
- ]
151
- )
152
- )
271
+ return set_parser (cls , type_ , parsers )
153
272
elif typing .get_origin (type_ ) == tuple :
154
- subtype_parsers = [
155
- _get_parser (
156
- cls ,
157
- subtype ,
158
- parsers ,
159
- )
160
- for subtype in typing .get_args (type_ )
161
- ]
162
-
163
- def tuple_parse (tuple_string : str ) -> Tuple [Any , ...]:
164
- """
165
- Parses a dictionary value (can do so recursively)
166
- Note that this tool will fail on tuples containing strings containing
167
- unpaired '{', or '}' characters
168
- """
169
- assert tuple_string [0 ] == "(" , "Tuple val improperly formatted"
170
- assert tuple_string [- 1 ] == ")" , "Tuple val improperly formatted"
171
- tuple_string = tuple_string [1 :- 1 ]
172
- if len (tuple_string ) == 0 :
173
- return ()
174
- else :
175
- val_strings = split_at_given_level (tuple_string , split_delim = "," )
176
- return tuple (
177
- parser (val_str )
178
- for parser , val_str in zip (subtype_parsers , val_strings )
179
- )
180
-
181
- return functools .partial (tuple_parse )
182
-
273
+ return tuple_parser (cls , type_ , parsers )
183
274
elif typing .get_origin (type_ ) == dict :
184
- subtypes = typing .get_args (type_ )
185
- assert (
186
- len (subtypes ) == 2
187
- ), "Dict object must have exactly 2 subtypes per PEP specification!"
188
- (key_parser , val_parser ) = (
189
- _get_parser (
190
- cls ,
191
- subtypes [0 ],
192
- parsers ,
193
- ),
194
- _get_parser (
195
- cls ,
196
- subtypes [1 ],
197
- parsers ,
198
- ),
199
- )
200
-
201
- def dict_parse (dict_string : str ) -> Dict [Any , Any ]:
202
- """
203
- Parses a dictionary value (can do so recursively)
204
- """
205
- assert dict_string [0 ] == "{" , "Dict val improperly formatted"
206
- assert dict_string [- 1 ] == "}" , "Dict val improprly formatted"
207
- dict_string = dict_string [1 :- 1 ]
208
- if len (dict_string ) == 0 :
209
- return {}
210
- else :
211
- outer_splits = split_at_given_level (dict_string , split_delim = "," )
212
- out_dict = {}
213
- for outer_split in outer_splits :
214
- inner_splits = split_at_given_level (outer_split , split_delim = ";" )
215
- assert (
216
- len (inner_splits ) % 2 == 0
217
- ), "Inner splits of dict didn't have matched key val pairs"
218
- for i in range (0 , len (inner_splits ), 2 ):
219
- out_dict [key_parser (inner_splits [i ])] = val_parser (
220
- inner_splits [i + 1 ]
221
- )
222
- return out_dict
223
-
224
- return functools .partial (dict_parse )
275
+ return dict_parser (cls , type_ , parsers )
225
276
elif isinstance (type_ , type ) and issubclass (type_ , Enum ):
226
277
return types .make_enum_parser (type_ )
227
278
elif types .is_constructible_from_str (type_ ):
0 commit comments