@@ -561,36 +561,32 @@ def __init__(self):
561561 self .errors = list ()
562562
563563
564- class _Line ( str ) :
564+ class _LineParser :
565565
566- def __new__ (cls , val , * args , ** kwargs ):
567- return super ().__new__ (cls , val )
568-
569- def __init__ (self , val , prefixes ):
570- self .prefixes = prefixes
571-
572- @functools .cached_property
573- def clean (self ):
574- return self ._strip_full () and self ._strip_inline ()
566+ def __init__ (self , comments ):
567+ self .comments = comments
575568
576569 @property
577- def has_comments (self ):
578- return self .strip () != self . clean
570+ def value (self ):
571+ return self ._value
579572
580- def _strip_inline (self ):
581- """
582- Search for the earliest prefix at the beginning of the line or following a space.
583- """
584- matcher = re .compile (
585- '|' .join (fr'(^|\s)({ re .escape (prefix )} )' for prefix in self .prefixes .inline )
586- # match nothing if no prefixes
587- or '(?!)'
588- )
589- match = matcher .search (self )
590- return self [:match .start () if match else None ].strip ()
573+ @value .setter
574+ def value (self , string ):
575+ self ._value = string
576+ string = string .strip ()
577+ self .clean = self ._strip_full (string ) and self ._strip_inline (string )
578+ self .has_comments = string != self .clean
579+
580+ def _strip_full (self , string ):
581+ return '' if any (map (string .startswith , self .comments .full )) else True
591582
592- def _strip_full (self ):
593- return '' if any (map (self .strip ().startswith , self .prefixes .full )) else True
583+ def _strip_inline (self , string ):
584+ match = None
585+ if self .comments .inline :
586+ match = self .comments .inline .search (string )
587+ if match :
588+ return string [:match .start ()].rstrip ()
589+ return string
594590
595591
596592class RawConfigParser (MutableMapping ):
@@ -659,9 +655,17 @@ def __init__(self, defaults=None, dict_type=_default_dict,
659655 else :
660656 self ._optcre = re .compile (self ._OPT_TMPL .format (delim = d ),
661657 re .VERBOSE )
662- self ._prefixes = types .SimpleNamespace (
663- full = tuple (comment_prefixes or ()),
664- inline = tuple (inline_comment_prefixes or ()),
658+ comment_prefixes = tuple (comment_prefixes or ())
659+ if inline_comment_prefixes :
660+ # prefix at the beginning of the line or following a space
661+ inline_comment_cre = re .compile (
662+ '|' .join (fr'(^|\s)({ re .escape (prefix )} )'
663+ for prefix in inline_comment_prefixes ))
664+ else :
665+ inline_comment_cre = None
666+ self ._comments = types .SimpleNamespace (
667+ full = comment_prefixes ,
668+ inline = inline_comment_cre ,
665669 )
666670 self ._strict = strict
667671 self ._allow_no_value = allow_no_value
@@ -1057,17 +1061,16 @@ def _read(self, fp, fpname):
10571061 in an otherwise empty line or may be entered in lines holding values or
10581062 section names. Please note that comments get stripped off when reading configuration files.
10591063 """
1060-
10611064 try :
10621065 ParsingError ._raise_all (self ._read_inner (fp , fpname ))
10631066 finally :
10641067 self ._join_multiline_values ()
10651068
10661069 def _read_inner (self , fp , fpname ):
10671070 st = _ReadState ()
1071+ line = _LineParser (self ._comments )
10681072
1069- Line = functools .partial (_Line , prefixes = self ._prefixes )
1070- for st .lineno , line in enumerate (map (Line , fp ), start = 1 ):
1073+ for st .lineno , line .value in enumerate (fp , start = 1 ):
10711074 if not line .clean :
10721075 if self ._empty_lines_in_values :
10731076 # add empty line to the value, but only if there was no
@@ -1082,7 +1085,7 @@ def _read_inner(self, fp, fpname):
10821085 st .indent_level = sys .maxsize
10831086 continue
10841087
1085- first_nonspace = self .NONSPACECRE .search (line )
1088+ first_nonspace = self .NONSPACECRE .search (line . value )
10861089 st .cur_indent_level = first_nonspace .start () if first_nonspace else 0
10871090
10881091 if self ._handle_continuation_line (st , line , fpname ):
@@ -1098,7 +1101,7 @@ def _handle_continuation_line(self, st, line, fpname):
10981101 st .cur_indent_level > st .indent_level )
10991102 if is_continue :
11001103 if st .cursect [st .optname ] is None :
1101- raise MultilineContinuationError (fpname , st .lineno , line )
1104+ raise MultilineContinuationError (fpname , st .lineno , line . value )
11021105 st .cursect [st .optname ].append (line .clean )
11031106 return is_continue
11041107
@@ -1112,7 +1115,7 @@ def _handle_rest(self, st, line, fpname):
11121115 mo = self .SECTCRE .match (line .clean )
11131116
11141117 if not mo and st .cursect is None :
1115- raise MissingSectionHeaderError (fpname , st .lineno , line )
1118+ raise MissingSectionHeaderError (fpname , st .lineno , line . value )
11161119
11171120 self ._handle_header (st , mo .group ('header' ), fpname ) if mo else self ._handle_option (st , line , fpname )
11181121
@@ -1144,12 +1147,12 @@ def _handle_option(self, st, line, fpname):
11441147 # exception but keep going. the exception will be
11451148 # raised at the end of the file and will contain a
11461149 # list of all bogus lines
1147- st .errors .append (ParsingError (fpname , st .lineno , line ))
1150+ st .errors .append (ParsingError (fpname , st .lineno , line . value ))
11481151 return
11491152
11501153 st .optname , vi , optval = mo .group ('option' , 'vi' , 'value' )
11511154 if not st .optname :
1152- st .errors .append (ParsingError (fpname , st .lineno , line ))
1155+ st .errors .append (ParsingError (fpname , st .lineno , line . value ))
11531156 st .optname = self .optionxform (st .optname .rstrip ())
11541157 if (self ._strict and
11551158 (st .sectname , st .optname ) in st .elements_added ):
0 commit comments