@@ -146,6 +146,51 @@ def __exit__(self, exception_type, exception_value, traceback):
146
146
self ._config .__exit__ (exception_type ,exception_value ,traceback )
147
147
148
148
149
+ class _OMD (OrderedDict ):
150
+ """Ordered multi-dict."""
151
+
152
+ def __setitem__ (self ,key ,value ):
153
+ super (_OMD ,self ).__setitem__ (key , [value ])
154
+
155
+ def add (self ,key ,value ):
156
+ if key not in self :
157
+ super (_OMD ,self ).__setitem__ (key , [value ])
158
+ return
159
+
160
+ super (_OMD ,self ).__getitem__ (key ).append (value )
161
+
162
+ def setall (self ,key ,values ):
163
+ super (_OMD ,self ).__setitem__ (key ,values )
164
+
165
+ def __getitem__ (self ,key ):
166
+ return super (_OMD ,self ).__getitem__ (key )[- 1 ]
167
+
168
+ def getlast (self ,key ):
169
+ return super (_OMD ,self ).__getitem__ (key )[- 1 ]
170
+
171
+ def setlast (self ,key ,value ):
172
+ if key not in self :
173
+ super (_OMD ,self ).__setitem__ (key , [value ])
174
+ return
175
+
176
+ prior = super (_OMD ,self ).__getitem__ (key )
177
+ prior [- 1 ]= value
178
+
179
+ def get (self ,key ,default = None ):
180
+ return super (_OMD ,self ).get (key , [default ])[- 1 ]
181
+
182
+ def getall (self ,key ):
183
+ return super (_OMD ,self ).__getitem__ (key )
184
+
185
+ def items (self ):
186
+ """List of (key, last value for key)."""
187
+ return [(k ,self [k ])for k in self ]
188
+
189
+ def items_all (self ):
190
+ """List of (key, list of values for key)."""
191
+ return [(k ,self .getall (k ))for k in self ]
192
+
193
+
149
194
class GitConfigParser (with_metaclass (MetaParserBuilder ,cp .RawConfigParser ,object )):
150
195
151
196
"""Implements specifics required to read git style configuration files.
@@ -200,7 +245,7 @@ def __init__(self, file_or_files, read_only=True, merge_includes=True):
200
245
contents into ours. This makes it impossible to write back an individual configuration file.
201
246
Thus, if you want to modify a single configuration file, turn this off to leave the original
202
247
dataset unaltered when reading it."""
203
- cp .RawConfigParser .__init__ (self ,dict_type = OrderedDict )
248
+ cp .RawConfigParser .__init__ (self ,dict_type = _OMD )
204
249
205
250
# Used in python 3, needs to stay in sync with sections for underlying implementation to work
206
251
if not hasattr (self ,'_proxies' ):
@@ -348,7 +393,8 @@ def string_decode(v):
348
393
is_multi_line = True
349
394
optval = string_decode (optval [1 :])
350
395
# end handle multi-line
351
- cursect [optname ]= optval
396
+ # preserves multiple values for duplicate optnames
397
+ cursect .add (optname ,optval )
352
398
else :
353
399
# check if it's an option with no value - it's just ignored by git
354
400
if not self .OPTVALUEONLY .match (line ):
@@ -362,7 +408,8 @@ def string_decode(v):
362
408
is_multi_line = False
363
409
line = line [:- 1 ]
364
410
# end handle quotations
365
- cursect [optname ]+= string_decode (line )
411
+ optval = cursect .getlast (optname )
412
+ cursect .setlast (optname ,optval + string_decode (line ))
366
413
# END parse section or option
367
414
# END while reading
368
415
@@ -442,9 +489,12 @@ def _write(self, fp):
442
489
git compatible format"""
443
490
def write_section (name ,section_dict ):
444
491
fp .write (("[%s]\n " % name ).encode (defenc ))
445
- for (key ,value )in section_dict .items ():
446
- if key != "__name__" :
447
- fp .write (("\t %s = %s\n " % (key ,self ._value_to_string (value ).replace ('\n ' ,'\n \t ' ))).encode (defenc ))
492
+ for (key ,values )in section_dict .items_all ():
493
+ if key == "__name__" :
494
+ continue
495
+
496
+ for v in values :
497
+ fp .write (("\t %s = %s\n " % (key ,self ._value_to_string (v ).replace ('\n ' ,'\n \t ' ))).encode (defenc ))
448
498
# END if key is not __name__
449
499
# END section writing
450
500
@@ -457,6 +507,22 @@ def items(self, section_name):
457
507
""":return: list((option, value), ...) pairs of all items in the given section"""
458
508
return [(k ,v )for k ,v in super (GitConfigParser ,self ).items (section_name )if k != '__name__' ]
459
509
510
+ def items_all (self ,section_name ):
511
+ """:return: list((option, [values...]), ...) pairs of all items in the given section"""
512
+ rv = _OMD (self ._defaults )
513
+
514
+ for k ,vs in self ._sections [section_name ].items_all ():
515
+ if k == '__name__' :
516
+ continue
517
+
518
+ if k in rv and rv .getall (k )== vs :
519
+ continue
520
+
521
+ for v in vs :
522
+ rv .add (k ,v )
523
+
524
+ return rv .items_all ()
525
+
460
526
@needs_values
461
527
def write (self ):
462
528
"""Write changes to our file, if there are changes at all
@@ -508,7 +574,11 @@ def read_only(self):
508
574
return self ._read_only
509
575
510
576
def get_value (self ,section ,option ,default = None ):
511
- """
577
+ """Get an option's value.
578
+
579
+ If multiple values are specified for this option in the section, the
580
+ last one specified is returned.
581
+
512
582
:param default:
513
583
If not None, the given default value will be returned in case
514
584
the option did not exist
@@ -523,6 +593,31 @@ def get_value(self, section, option, default=None):
523
593
return default
524
594
raise
525
595
596
+ return self ._string_to_value (valuestr )
597
+
598
+ def get_values (self ,section ,option ,default = None ):
599
+ """Get an option's values.
600
+
601
+ If multiple values are specified for this option in the section, all are
602
+ returned.
603
+
604
+ :param default:
605
+ If not None, a list containing the given default value will be
606
+ returned in case the option did not exist
607
+ :return: a list of properly typed values, either int, float or string
608
+
609
+ :raise TypeError: in case the value could not be understood
610
+ Otherwise the exceptions known to the ConfigParser will be raised."""
611
+ try :
612
+ lst = self ._sections [section ].getall (option )
613
+ except Exception :
614
+ if default is not None :
615
+ return [default ]
616
+ raise
617
+
618
+ return [self ._string_to_value (valuestr )for valuestr in lst ]
619
+
620
+ def _string_to_value (self ,valuestr ):
526
621
types = (int ,float )
527
622
for numtype in types :
528
623
try :
@@ -545,7 +640,9 @@ def get_value(self, section, option, default=None):
545
640
return True
546
641
547
642
if not isinstance (valuestr ,string_types ):
548
- raise TypeError ("Invalid value type: only int, long, float and str are allowed" ,valuestr )
643
+ raise TypeError (
644
+ "Invalid value type: only int, long, float and str are allowed" ,
645
+ valuestr )
549
646
550
647
return valuestr
551
648
@@ -572,6 +669,25 @@ def set_value(self, section, option, value):
572
669
self .set (section ,option ,self ._value_to_string (value ))
573
670
return self
574
671
672
+ @needs_values
673
+ @set_dirty_and_flush_changes
674
+ def add_value (self ,section ,option ,value ):
675
+ """Adds a value for the given option in section.
676
+ It will create the section if required, and will not throw as opposed to the default
677
+ ConfigParser 'set' method. The value becomes the new value of the option as returned
678
+ by 'get_value', and appends to the list of values returned by 'get_values`'.
679
+
680
+ :param section: Name of the section in which the option resides or should reside
681
+ :param option: Name of the option
682
+
683
+ :param value: Value to add to option. It must be a string or convertible
684
+ to a string
685
+ :return: this instance"""
686
+ if not self .has_section (section ):
687
+ self .add_section (section )
688
+ self ._sections [section ].add (option ,self ._value_to_string (value ))
689
+ return self
690
+
575
691
def rename_section (self ,section ,new_name ):
576
692
"""rename the given section to new_name
577
693
:raise ValueError: if section doesn't exit
@@ -584,8 +700,9 @@ def rename_section(self, section, new_name):
584
700
raise ValueError ("Destination section '%s' already exists" % new_name )
585
701
586
702
super (GitConfigParser ,self ).add_section (new_name )
587
- for k ,v in self .items (section ):
588
- self .set (new_name ,k ,self ._value_to_string (v ))
703
+ new_section = self ._sections [new_name ]
704
+ for k ,vs in self .items_all (section ):
705
+ new_section .setall (k ,vs )
589
706
# end for each value to copy
590
707
591
708
# This call writes back the changes, which is why we don't have the respective decorator