@@ -131,19 +131,20 @@ class GitConfigParser(cp.RawConfigParser, object):
131131
132132 OPTCRE = re .compile (
133133 r'\s*(?P<option>[^:=\s][^:=]*)' # very permissive, incuding leading whitespace
134- r'\s*(?P<vi>[:=])\s*' # any number of space/tab,
135- # followed by separator
136- # (either : or =), followed
137- # by any # space/tab
138- r'(?P<value>.*)$' # everything up to eol
139- )
134+ r'\s*(?:' # any number of space/tab,
135+ r'(?P<vi>[:=])\s*' # optionally followed by
136+ # separator (either : or
137+ # =), followed by any #
138+ # space/tab
139+ r'(?P<value>.*))?$' # everything up to eol
140+ )
140141
141142 # list of RawConfigParser methods able to change the instance
142143 _mutating_methods_ = ("add_section" , "remove_section" , "remove_option" , "set" )
143144 __slots__ = ("_sections" , "_defaults" , "_file_or_files" , "_read_only" , "_is_initialized" , '_lock' )
144145
145146 def __init__ (self , file_or_files , read_only = True ):
146- """Initialize a configuration reader to read the given file_or_files and to
147+ """Initialize a configuration reader to read the given file_or_files and to
147148 possibly allow changes to it by setting read_only False
148149
149150 :param file_or_files:
@@ -198,10 +199,10 @@ def optionxform(self, optionstr):
198199 return optionstr
199200
200201 def _read (self , fp , fpname ):
201- """A direct copy of the py2.4 version of the super class's _read method
202+ """A direct copy of the py2.7 version of the super class's _read method
202203 to assure it uses ordered dicts. Had to change one line to make it work.
203204
204- Future versions have this fixed, but in fact its quite embarassing for the
205+ Future versions have this fixed, but in fact its quite embarassing for the
205206 guys not to have done it right in the first place !
206207
207208 Removed big comments to make it more compact.
@@ -222,6 +223,7 @@ def _read(self, fp, fpname):
222223 if line .split (None , 1 )[0 ].lower () == 'rem' and line [0 ] in "rR" :
223224 # no leading whitespace
224225 continue
226+ # a section header or option header?
225227 else :
226228 # is it a section header?
227229 mo = self .SECTCRE .match (line .strip ())
@@ -245,43 +247,48 @@ def _read(self, fp, fpname):
245247 mo = self .OPTCRE .match (line )
246248 if mo :
247249 optname , vi , optval = mo .group ('option' , 'vi' , 'value' )
248- if vi in ('=' , ':' ) and ';' in optval :
249- pos = optval .find (';' )
250- if pos != - 1 and optval [pos - 1 ].isspace ():
251- optval = optval [:pos ]
252- optval = optval .strip ()
253-
254- # Remove paired unescaped-quotes
255- unquoted_optval = ''
256- escaped = False
257- in_quote = False
258- for c in optval :
259- if not escaped and c == '"' :
260- in_quote = not in_quote
261- else :
262- escaped = (c == '\\ ' ) and not escaped
263- unquoted_optval += c
264-
265- if in_quote :
266- if not e :
267- e = cp .ParsingError (fpname )
268- e .append (lineno , repr (line ))
269-
270- optval = unquoted_optval
271-
272- optval = optval .replace ('\\ \\ ' , '\\ ' ) # Unescape backslashes
273- optval = optval .replace (r'\"' , '"' ) # Unescape quotes
274-
275250 optname = self .optionxform (optname .rstrip ())
276- cursect [optname ] = optval
251+ if optval is not None :
252+ if vi in ('=' , ':' ) and ';' in optval :
253+ # ';' is a comment delimiter only if it follows
254+ # a spacing character
255+ pos = optval .find (';' )
256+ if pos != - 1 and optval [pos - 1 ].isspace ():
257+ optval = optval [:pos ]
258+ optval = optval .strip ()
259+ # allow empty values
260+ if optval == '""' :
261+ optval = ''
262+ # Remove paired unescaped-quotes
263+ unquoted_optval = ''
264+ escaped = False
265+ in_quote = False
266+ for c in optval :
267+ if not escaped and c == '"' :
268+ in_quote = not in_quote
269+ else :
270+ escaped = (c == '\\ ' ) and not escaped
271+ unquoted_optval += c
272+ if in_quote :
273+ if not e :
274+ e = cp .ParsingError (fpname )
275+ e .append (lineno , repr (line ))
276+
277+ optval = unquoted_optval
278+ optval = optval .replace ('\\ \\ ' , '\\ ' ) # Unescape backslashes
279+ optval = optval .replace (r'\"' , '"' ) # Unescape quotes
280+ cursect [optname ] = optval
281+ else :
282+ # valueless option handling
283+ cursect [optname ] = optval
277284 else :
285+ # a non-fatal parsing error occurred. set up the
286+ # exception but keep going. the exception will be
287+ # raised at the end of the file and will contain a
288+ # list of all bogus lines
278289 if not e :
279290 e = cp .ParsingError (fpname )
280291 e .append (lineno , repr (line ))
281- # END
282- # END ?
283- # END ?
284- # END while reading
285292 # if any parsing errors occurred, raise an exception
286293 if e :
287294 raise e
@@ -398,7 +405,7 @@ def get_value(self, section, option, default=None):
398405 :param default:
399406 If not None, the given default value will be returned in case
400407 the option did not exist
401- :return: a properly typed value, either int, float or string
408+ :return: a properly typed value, either int, bool, float, string or None
402409
403410 :raise TypeError: in case the value could not be understood
404411 Otherwise the exceptions known to the ConfigParser will be raised."""
@@ -409,6 +416,9 @@ def get_value(self, section, option, default=None):
409416 return default
410417 raise
411418
419+ if valuestr is None :
420+ return valuestr
421+
412422 types = (long , float )
413423 for numtype in types :
414424 try :
0 commit comments