66
77import six
88import yaml
9- from six import add_metaclass , string_types as basestring , iteritems
9+ from six import add_metaclass , string_types , iteritems
10+
1011
1112# this was only introduced in six 1.13.0, but it's too old in RHEL,
1213# and we don't want to rebuild its package
1617else :
1718 import collections .abc as collections_abc
1819
19- SPLIT_REGEX = r"(?<!\\)(\.)"
20+ SPLIT_REGEX = re . compile ( r"(?<!\\)(\.)" )
2021
2122
2223def is_dotted_key (key ):
@@ -25,20 +26,20 @@ def is_dotted_key(key):
2526
2627
2728def split_key (key , max_keys = 0 ):
28- """Splits a key but allows dots in the key name if they're scaped properly.
29+ """Splits a key but allows dots in the key name if they're escaped properly.
2930
3031 Splitting this complex key:
3132
32- complex_key = r" .dont\.splitme.d\.o\. origen.splitme\.dontsplit.splitme."
33+ complex_key = r' .dont\\ \\ .splitme.d\\ \\ .o\\ \\ . origen.splitme\\ \\ .dontsplit.splitme.'
3334 split_key(complex_key)
3435
3536 results in:
3637
37- ['', r'dont\.splitme', r'd\.o\. origen', r'splitme\.dontsplit', 'splitme', '']
38+ ['', r'dont\\ \\ .splitme', r'd\\ \\ .o\\ \\ . origen', r'splitme\\ \ \ .dontsplit', 'splitme', '']
3839
3940
4041 Args:
41- key (basestring ): The key to be splitted .
42+ key (str ): The key to be split .
4243 max_keys (int): The maximum number of keys to be extracted. 0 means no
4344 limits.
4445
@@ -48,7 +49,7 @@ def split_key(key, max_keys=0):
4849 parts = [x for x in re .split (SPLIT_REGEX , key ) if x != "." ]
4950 result = []
5051 while len (parts ) > 0 :
51- if max_keys > 0 and len (result ) == max_keys :
52+ if 0 < max_keys == len (result ):
5253 break
5354 result .append (parts .pop (0 ))
5455
@@ -61,7 +62,6 @@ def split_key(key, max_keys=0):
6162class DottedCollection (object ):
6263 """Abstract Base Class for DottedDict and DottedDict"""
6364
64-
6565 @classmethod
6666 def factory (cls , initial = None ):
6767 """Returns a DottedDict or a DottedList based on the type of the
@@ -74,21 +74,19 @@ def factory(cls, initial=None):
7474 return DottedDict (initial )
7575 return initial
7676
77-
7877 @classmethod
7978 def load_json (cls , json_value ):
8079 """Returns a DottedCollection from a JSON string"""
8180 return cls .factory (json .loads (json_value ))
8281
83-
8482 @classmethod
8583 def _factory_by_index (cls , dotted_key ):
8684 """Returns the proper DottedCollection that best suits the next key in
8785 the dotted_key string. First guesses the next key and then analyzes it.
8886 If the next key is numeric then returns a DottedList. In other case a
8987 DottedDict is returned.
9088 """
91- if not isinstance (dotted_key , basestring ):
89+ if not isinstance (dotted_key , string_types ):
9290 next_key = str (dotted_key )
9391 elif not is_dotted_key (dotted_key ):
9492 next_key = dotted_key
@@ -97,7 +95,6 @@ def _factory_by_index(cls, dotted_key):
9795
9896 return DottedCollection .factory ([] if next_key .isdigit () else {})
9997
100-
10198 def __init__ (self , initial ):
10299 """Base constructor. If there are nested dicts or lists they are
103100 transformed into DottedCollection instances.
@@ -120,7 +117,6 @@ def __init__(self, initial):
120117 except ValueError :
121118 pass
122119
123-
124120 def _validate_initial (self , initial ):
125121 """Validates data so no unescaped dotted key is present."""
126122 if isinstance (initial , list ):
@@ -133,42 +129,33 @@ def _validate_initial(self, initial):
133129 "DottedCollection!" .format (key ))
134130 self ._validate_initial (item )
135131
136-
137132 def __len__ (self ):
138133 return len (self .store )
139134
140-
141135 def __iter__ (self ):
142136 return iter (self .store )
143137
144-
145138 def __repr__ (self ):
146139 return repr (self .store )
147140
148-
149141 def to_json (self ):
150142 return json .dumps (self , cls = DottedJSONEncoder , indent = 4 )
151143
152-
153144 def to_yaml (self ):
154145 return yaml .dump (self , Dumper = DottedYAMLDumper )
155146
156-
157147 @abstractmethod
158148 def __getitem__ (self , name ):
159149 raise NotImplementedError
160150
161-
162151 @abstractmethod
163152 def __setitem__ (self , name , value ):
164153 raise NotImplementedError
165154
166-
167155 @abstractmethod
168156 def __delitem__ (self , name ):
169157 raise NotImplementedError
170158
171-
172159 @abstractmethod
173160 def to_python (self ):
174161 raise NotImplementedError
@@ -177,23 +164,21 @@ def to_python(self):
177164class DottedList (DottedCollection , collections_abc .MutableSequence ):
178165 """A list with support for the dotted path syntax"""
179166
180-
181167 def __init__ (self , initial = None ):
182168 DottedCollection .__init__ (
183169 self ,
184170 [] if initial is None else list (initial )
185171 )
186172
187-
188173 def __getitem__ (self , index ):
189174 if isinstance (index , slice ):
190175 return self .store [index ]
191176
192177 if isinstance (index , int ) \
193- or (isinstance (index , basestring ) and index .isdigit ()):
178+ or (isinstance (index , string_types ) and index .isdigit ()):
194179 return self .store [int (index )]
195180
196- if isinstance (index , basestring ) and is_dotted_key (index ):
181+ if isinstance (index , string_types ) and is_dotted_key (index ):
197182 my_index , alt_index = split_key (index , 1 )
198183 target = self .store [int (my_index )]
199184
@@ -209,19 +194,18 @@ def __getitem__(self, index):
209194
210195 raise IndexError ('cannot get %s in %s' % (index , repr (self .store )))
211196
212-
213197 def __setitem__ (self , index , value ):
214198 if isinstance (index , int ) \
215- or (isinstance (index , basestring ) and index .isdigit ()):
216- # If the index does not exist in the list but it's the same index
199+ or (isinstance (index , string_types ) and index .isdigit ()):
200+ # If the index does not exist in the list, but it's the same index
217201 # we would obtain by appending the value to the list we actually
218202 # append the value. (***)
219203 if int (index ) not in self .store and int (index ) == len (self .store ):
220204 self .store .append (DottedCollection .factory (value ))
221205 else :
222206 self .store [int (index )] = DottedCollection .factory (value )
223207
224- elif isinstance (index , basestring ) and is_dotted_key (index ):
208+ elif isinstance (index , string_types ) and is_dotted_key (index ):
225209 my_index , alt_index = split_key (index , 1 )
226210
227211 # (***)
@@ -240,13 +224,12 @@ def __setitem__(self, index, value):
240224 raise IndexError ('cannot use %s as index in %s' % (
241225 index , repr (self .store )))
242226
243-
244227 def __delitem__ (self , index ):
245228 if isinstance (index , int ) \
246- or (isinstance (index , basestring ) and index .isdigit ()):
229+ or (isinstance (index , string_types ) and index .isdigit ()):
247230 del self .store [int (index )]
248231
249- elif isinstance (index , basestring ) and is_dotted_key (index ):
232+ elif isinstance (index , string_types ) and is_dotted_key (index ):
250233 my_index , alt_index = split_key (index , 1 )
251234 target = self .store [int (my_index )]
252235
@@ -261,7 +244,6 @@ def __delitem__(self, index):
261244 raise IndexError ('cannot delete %s in %s' % (
262245 index , repr (self .store )))
263246
264-
265247 def to_python (self ):
266248 """Returns a plain python list and converts to plain python objects all
267249 this object's descendants.
@@ -274,26 +256,23 @@ def to_python(self):
274256
275257 return result
276258
277-
278259 def insert (self , index , value ):
279260 self .store .insert (index , value )
280261
281262
282263class DottedDict (DottedCollection , collections_abc .MutableMapping ):
283264 """A dict with support for the dotted path syntax"""
284265
285-
286266 def __init__ (self , initial = None ):
287267 DottedCollection .__init__ (
288268 self ,
289269 {} if initial is None else dict (initial )
290270 )
291271
292-
293272 def __getitem__ (self , k ):
294273 key = self .__keytransform__ (k )
295274
296- if not isinstance (k , basestring ) or not is_dotted_key (key ):
275+ if not isinstance (k , string_types ) or not is_dotted_key (key ):
297276 return self .store [key ]
298277
299278 my_key , alt_key = split_key (key , 1 )
@@ -309,11 +288,10 @@ def __getitem__(self, k):
309288
310289 return target [alt_key ]
311290
312-
313291 def __setitem__ (self , k , value ):
314292 key = self .__keytransform__ (k )
315293
316- if not isinstance (k , basestring ):
294+ if not isinstance (k , string_types ):
317295 raise KeyError ('DottedDict keys must be str or unicode' )
318296 if not is_dotted_key (key ):
319297 self .store [key ] = DottedCollection .factory (value )
@@ -325,11 +303,10 @@ def __setitem__(self, k, value):
325303
326304 self .store [my_key ][alt_key ] = value
327305
328-
329306 def __delitem__ (self , k ):
330307 key = self .__keytransform__ (k )
331308
332- if not isinstance (k , basestring ) or not is_dotted_key (key ):
309+ if not isinstance (k , string_types ) or not is_dotted_key (key ):
333310 del self .store [key ]
334311
335312 else :
@@ -345,7 +322,6 @@ def __delitem__(self, k):
345322
346323 del target [alt_key ]
347324
348-
349325 def to_python (self ):
350326 """Returns a plain python dict and converts to plain python objects all
351327 this object's descendants.
@@ -358,10 +334,8 @@ def to_python(self):
358334
359335 return result
360336
361-
362337 __getattr__ = __getitem__
363338
364-
365339 # self.store does not exist before __init__() initializes it
366340
367341 def __setattr__ (self , key , value ):
@@ -370,18 +344,16 @@ def __setattr__(self, key, value):
370344 else :
371345 self .__setitem__ (key , value )
372346
373-
374347 def __delattr__ (self , key ):
375348 if key in self .__dict__ or key == 'store' :
376349 object .__delattr__ (self , key )
377350 else :
378351 self .__delitem__ (key )
379352
380-
381353 def __contains__ (self , k ):
382354 key = self .__keytransform__ (k )
383355
384- if not isinstance (k , basestring ) or not is_dotted_key (key ):
356+ if not isinstance (k , string_types ) or not is_dotted_key (key ):
385357 return self .store .__contains__ (key )
386358
387359 my_key , alt_key = split_key (key , 1 )
@@ -392,8 +364,8 @@ def __contains__(self, k):
392364
393365 return alt_key in target
394366
395-
396- def __keytransform__ (self , key ):
367+ @ staticmethod
368+ def __keytransform__ (key ):
397369 return key
398370
399371
@@ -420,9 +392,9 @@ class DottedYAMLDumper(yaml.Dumper):
420392
421393 dumper.add_representer(DottedDict, lambda dumper, data: data.store)
422394
423- but we'd have to do it for each type.
395+ But we'd have to do it for each type.
424396
425- This suggests making a custom dumper for a hiearchy of types:
397+ This suggests making a custom dumper for a hierarchy of types:
426398 https://github.com/yaml/pyyaml/issues/51
427399 """
428400
0 commit comments