154
154
import os
155
155
import re
156
156
import sys
157
- import types
158
157
159
158
__all__ = ("NoSectionError" , "DuplicateOptionError" , "DuplicateSectionError" ,
160
159
"NoOptionError" , "InterpolationError" , "InterpolationDepthError" ,
@@ -570,35 +569,36 @@ def __init__(self):
570
569
571
570
572
571
class _Line (str ):
572
+ __slots__ = 'clean' , 'has_comments'
573
573
574
574
def __new__ (cls , val , * args , ** kwargs ):
575
575
return super ().__new__ (cls , val )
576
576
577
- def __init__ (self , val , prefixes ):
578
- self .prefixes = prefixes
577
+ def __init__ (self , val , comments ):
578
+ trimmed = val .strip ()
579
+ self .clean = comments .strip (trimmed )
580
+ self .has_comments = trimmed != self .clean
579
581
580
- @functools .cached_property
581
- def clean (self ):
582
- return self ._strip_full () and self ._strip_inline ()
583
582
584
- @property
585
- def has_comments (self ):
586
- return self .strip () != self .clean
587
-
588
- def _strip_inline (self ):
589
- """
590
- Search for the earliest prefix at the beginning of the line or following a space.
591
- """
592
- matcher = re .compile (
593
- '|' .join (fr'(^|\s)({ re .escape (prefix )} )' for prefix in self .prefixes .inline )
594
- # match nothing if no prefixes
595
- or '(?!)'
583
+ class _CommentSpec :
584
+ def __init__ (self , full_prefixes , inline_prefixes ):
585
+ full_patterns = (
586
+ # prefix at the beginning of a line
587
+ fr'^({ re .escape (prefix )} ).*'
588
+ for prefix in full_prefixes
596
589
)
597
- match = matcher .search (self )
598
- return self [:match .start () if match else None ].strip ()
590
+ inline_patterns = (
591
+ # prefix at the beginning of the line or following a space
592
+ fr'(^|\s)({ re .escape (prefix )} .*)'
593
+ for prefix in inline_prefixes
594
+ )
595
+ self .pattern = re .compile ('|' .join (itertools .chain (full_patterns , inline_patterns )))
596
+
597
+ def strip (self , text ):
598
+ return self .pattern .sub ('' , text ).rstrip ()
599
599
600
- def _strip_full (self ):
601
- return '' if any ( map ( self . strip (). startswith , self . prefixes . full )) else True
600
+ def wrap (self , text ):
601
+ return _Line ( text , self )
602
602
603
603
604
604
class RawConfigParser (MutableMapping ):
@@ -667,10 +667,7 @@ def __init__(self, defaults=None, dict_type=_default_dict,
667
667
else :
668
668
self ._optcre = re .compile (self ._OPT_TMPL .format (delim = d ),
669
669
re .VERBOSE )
670
- self ._prefixes = types .SimpleNamespace (
671
- full = tuple (comment_prefixes or ()),
672
- inline = tuple (inline_comment_prefixes or ()),
673
- )
670
+ self ._comments = _CommentSpec (comment_prefixes or (), inline_comment_prefixes or ())
674
671
self ._strict = strict
675
672
self ._allow_no_value = allow_no_value
676
673
self ._empty_lines_in_values = empty_lines_in_values
@@ -1066,7 +1063,6 @@ def _read(self, fp, fpname):
1066
1063
in an otherwise empty line or may be entered in lines holding values or
1067
1064
section names. Please note that comments get stripped off when reading configuration files.
1068
1065
"""
1069
-
1070
1066
try :
1071
1067
ParsingError ._raise_all (self ._read_inner (fp , fpname ))
1072
1068
finally :
@@ -1075,8 +1071,7 @@ def _read(self, fp, fpname):
1075
1071
def _read_inner (self , fp , fpname ):
1076
1072
st = _ReadState ()
1077
1073
1078
- Line = functools .partial (_Line , prefixes = self ._prefixes )
1079
- for st .lineno , line in enumerate (map (Line , fp ), start = 1 ):
1074
+ for st .lineno , line in enumerate (map (self ._comments .wrap , fp ), start = 1 ):
1080
1075
if not line .clean :
1081
1076
if self ._empty_lines_in_values :
1082
1077
# add empty line to the value, but only if there was no
0 commit comments