Skip to content

Commit a3a696b

Browse files
committed
perf: replace _Line with _LineParser
1 parent 7eaef74 commit a3a696b

File tree

1 file changed

+39
-36
lines changed

1 file changed

+39
-36
lines changed

Lib/configparser.py

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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

596592
class 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

Comments
 (0)