8000 Convert mayReturnEmpty to computed property, for proper handling of R… · pyparsing/pyparsing@626cca7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 626cca7

Browse files
committed
Convert mayReturnEmpty to computed property, for proper handling of Regex expr by other classes during parser building
1 parent 4bb24ba commit 626cca7

File tree

4 files changed

+85
-41
lines changed

4 files changed

+85
-41
lines changed

pyparsing/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def __repr__(self):
121121

122122

123123
__version_info__ = version_info(3, 2, 2, "final", 1)
124-
__version_time__ = "16 Mar 2025 21:18 UTC"
124+
__version_time__ = "17 Mar 2025 00:04 UTC"
125125
__version__ = __version_info__.__version__
126126
__versionTime__ = __version_time__
127127
__author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>"

pyparsing/core.py

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ def __init__(self, savelist: bool = False) -> None:
465465
self.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS)
466466
self.copyDefaultWhiteChars = True
467467
# used when checking for left-recursion
468-
self.mayReturnEmpty = False
468+
self._may_return_empty = False
469469
self.keepTabs = False
470470
self.ignoreExprs: list[ParserElement] = list()
471471
self.debug = False
@@ -483,6 +483,14 @@ def __init__(self, savelist: bool = False) -> None:
483483
self.suppress_warnings_: list[Diagnostics] = []
484484
self.show_in_diagram = True
485485

486+
@property
487+
def mayReturnEmpty(self):
488+
return self._may_return_empty
489+
490+
@mayReturnEmpty.setter
491+
def mayReturnEmpty(self, value):
492+
self._may_return_empty = value
493+
486494
def suppress_warning(self, warning_type: Diagnostics) -> ParserElement:
487495
"""
488496
Suppress warnings emitted for a particular diagnostic on this expression.
@@ -2412,7 +2420,7 @@ class NoMatch(Token):
24122420

24132421
def __init__(self) -> None:
24142422
super().__init__()
2415-
self.mayReturnEmpty = True
2423+
self._may_return_empty = True
24162424
self.mayIndexError = False
24172425
self.errmsg = "Unmatchable token"
24182426

@@ -2459,7 +2467,7 @@ def __init__(self, match_string: str = "", *, matchString: str = "") -> None:
24592467
self.matchLen = len(match_string)
24602468
self.firstMatchChar = match_string[:1]
24612469
self.errmsg = f"Expected {self.name}"
2462-
self.mayReturnEmpty = False
2470+
self._may_return_empty = False
24632471
self.mayIndexError = False
24642472

24652473
def _generateDefaultName(self) -> str:
@@ -2480,7 +2488,7 @@ class Empty(Literal):
24802488

24812489
def __init__(self, match_string="", *, matchString="") -> None:
24822490
super().__init__("")
2483-
self.mayReturnEmpty = True
2491+
self._may_return_empty = True
24842492
self.mayIndexError = False
24852493

24862494
def _generateDefaultName(self) -> str:
@@ -2549,7 +2557,7 @@ def __init__(
25492557
if not self.firstMatchChar:
25502558
raise ValueError("null string passed to Keyword; use Empty() instead")
25512559
self.errmsg = f"Expected {type(self).__name__} {self.name}"
2552-
self.mayReturnEmpty = False
2560+
self._may_return_empty = False
25532561
self.mayIndexError = False
25542562
self.caseless = caseless
25552563
if caseless:
@@ -2719,7 +2727,7 @@ def __init__(
27192727
self.errmsg = f"Expected {self.match_string!r} (with up to {self.maxMismatches} mismatches)"
27202728
self.caseless = caseless
27212729
self.mayIndexError = False
2722-
self.mayReturnEmpty = False
2730+
self._may_return_empty = False
27232731

27242732
def _generateDefaultName(self) -> str:
27252733
return f"{type(self).__name__}:{self.match_string!r}"
@@ -3078,15 +3086,18 @@ def __init__(
30783086
raise ValueError("null string passed to Regex; use Empty() instead")
30793087

30803088
self._re = None
3089+
self._may_return_empty = None # type: ignore [assignment]
30813090
self.reString = self.pattern = pattern
30823091

30833092
elif hasattr(pattern, "pattern") and hasattr(pattern, "match"):
30843093
self._re = pattern
3094+
self._may_return_empty = None # type: ignore [assignment]
30853095
self.pattern = self.reString = pattern.pattern
30863096

30873097
elif callable(pattern):
30883098
# defer creating this pattern until we really need it
30893099
self.pattern = pattern
3100+
self._may_return_empty = None # type: ignore [assignment]
30903101
self._re = None
30913102

30923103
else:
@@ -3126,13 +3137,24 @@ def re(self) -> re.Pattern:
31263137
except re.error:
31273138
raise ValueError(f"invalid pattern ({self.pattern!r}) passed to Regex")
31283139
else:
3129-
self.mayReturnEmpty = self.re.match("", pos=0) is not None
3140+
self._may_return_empty = self.re.match("", pos=0) is not None
31303141
return self._re
31313142

31323143
@cached_property
31333144
def re_match(self) -> Callable[[str, int], Any]:
31343145
return self.re.match
31353146

3147+
@property
3148+
def mayReturnEmpty(self):
3149+
if self._may_return_empty is None:
3150+
# force compile of regex pattern, to set may_return_empty flag
3151+
self.re # noqa
3152+
return self._may_return_empty
3153+
3154+
@mayReturnEmpty.setter
3155+
def mayReturnEmpty(self, value):
3156+
self._may_return_empty = value
3157+
31363158
def _generateDefaultName(self) -> str:
31373159
unescaped = repr(self.pattern).replace("\\\\", "\\")
31383160
return f"Re:({unescaped})"
@@ -3375,7 +3397,7 @@ def __init__(
33753397

33763398
self.errmsg = f"Expected {self.name}"
33773399
self.mayIndexError = False
3378-
self.mayReturnEmpty = True
3400+
self._may_return_empty = True
33793401

33803402
def _generateDefaultName(self) -> str:
33813403
if self.quote_char == self.end_quote_char and isinstance(
@@ -3502,7 +3524,7 @@ def __init__(
35023524
self.minLen = exact
35033525

35043526
self.errmsg = f"Expected {self.name}"
3505-
self.mayReturnEmpty = self.minLen == 0
3527+
self._may_return_empty = self.minLen == 0
35063528
self.mayIndexError = False
35073529

35083530
def _generateDefaultName(self) -> str:
@@ -3575,7 +3597,7 @@ def __init__(
35753597
copy_defaults=True,
35763598
)
35773599
# self.leave_whitespace()
3578-
self.mayReturnEmpty = True
3600+
self._may_return_empty = True
35793601
self.errmsg = f"Expected {self.name}"
35803602

35813603
self.minLen = min
@@ -3611,7 +3633,7 @@ def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
36113633
class PositionToken(Token):
36123634
def __init__(self) -> None:
36133635
supe F42D r().__init__()
3614-
self.mayReturnEmpty = True
3636+
self._may_return_empty = True
36153637
self.mayIndexError = False
36163638

36173639

@@ -3843,7 +3865,7 @@ class Tag(Token):
38433865

38443866
def __init__(self, tag_name: str, value: Any = True) -> None:
38453867
super().__init__()
3846-
self.mayReturnEmpty = True
3868+
self._may_return_empty = True
38473869
self.mayIndexError = False
38483870
self.leave_whitespace()
38493871
self.tag_name = tag_name
@@ -3961,7 +3983,7 @@ def streamline(self) -> ParserElement:
39613983
):
39623984
self.exprs = other.exprs[:] + [self.exprs[1]]
39633985
self._defaultName = None
3964-
self.mayReturnEmpty |= other.mayReturnEmpty
3986+
self._may_return_empty |= other.mayReturnEmpty
39653987
self.mayIndexError |= other.mayIndexError
39663988

39673989
other = self.exprs[-1]
@@ -3973,7 +3995,7 @@ def streamline(self) -> ParserElement:
39733995
):
39743996
self.exprs = self.exprs[:-1] + other.exprs[:]
39753997
self._defaultName = None
3976-
self.mayReturnEmpty |= other.mayReturnEmpty
3998+
self._may_return_empty |= other.mayReturnEmpty
39773999
self.mayIndexError |= other.mayIndexError
39784000

39794001
self.errmsg = f"Expected {self}"
@@ -4085,7 +4107,7 @@ def __init__(
40854107

40864108
super().__init__(exprs, savelist)
40874109
if self.exprs:
4088-
self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
4110+
self._may_return_empty = all(e.mayReturnEmpty for e in self.exprs)
40894111
if not isinstance(self.exprs[0], White):
40904112
self.set_whitespace_chars(
40914113
self.exprs[0].whiteChars,
@@ -4095,7 +4117,7 @@ def __init__(
40954117
else:
40964118
self.skipWhitespace = False
40974119
else:
4098-
self.mayReturnEmpty = True
4120+
self._may_return_empty = True
40994121
self.callPreparse = True
41004122

41014123
def streamline(self) -> ParserElement:
@@ -4145,7 +4167,7 @@ def streamline(self) -> ParserElement:
41454167
break
41464168
cur = typing.cast(ParserElement, next_first)
41474169

4148-
self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
4170+
self._may_return_empty = all(e.mayReturnEmpty for e in self.exprs)
41494171
return self
41504172

41514173
def parseImpl(self, instring, loc, do_actions=True):
@@ -4222,15 +4244,15 @@ def __init__(
42224244
) -> None:
42234245
super().__init__(exprs, savelist)
42244246
if self.exprs:
4225-
self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
4247+
self._may_return_empty = any(e.mayReturnEmpty for e in self.exprs)
42264248
self.skipWhitespace = all(e.skipWhitespace for e in self.exprs)
42274249
else:
4228-
self.mayReturnEmpty = True
4250+
self._may_return_empty = True
42294251

42304252
def streamline(self) -> ParserElement:
42314253
super().streamline()
42324254
if self.exprs:
4233-
self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
4255+
self._may_return_empty = any(e.mayReturnEmpty for e in self.exprs)
42344256
self.saveAsList = any(e.saveAsList for e in self.exprs)
42354257
self.skipWhitespace = all(
42364258
e.skipWhitespace and not isinstance(e, White) for e in self.exprs
@@ -4380,10 +4402,10 @@ def __init__(
43804402
) -> None:
43814403
super().__init__(exprs, savelist)
43824404
if self.exprs:
4383-
self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
4405+
self._may_return_empty = any(e.mayReturnEmpty for e in self.exprs)
43844406
self.skipWhitespace = all(e.skipWhitespace for e in self.exprs)
43854407
else:
4386-
self.mayReturnEmpty = True
4408+
self._may_return_empty = True
43874409

43884410
def streamline(self) -> ParserElement:
43894411
if self.streamlined:
@@ -4392,13 +4414,13 @@ def streamline(self) -> ParserElement:
43924414
super().streamline()
43934415
if self.exprs:
43944416
self.saveAsList = any(e.saveAsList for e in self.exprs)
4395-
self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
4417+
self._may_return_empty = any(e.mayReturnEmpty for e in self.exprs)
43964418
self.skipWhitespace = all(
43974419
e.skipWhitespace and not isinstance(e, White) for e in self.exprs
43984420
)
43994421
else:
44004422
self.saveAsList = False
4401-
self.mayReturnEmpty = True
4423+
self._may_return_empty = True
44024424
return self
44034425

44044426
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
@@ -4530,9 +4552,9 @@ def __init__(
45304552
) -> None:
45314553
super().__init__(exprs, savelist)
45324554
if self.exprs:
4533-
self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
4555+
self._may_return_empty = all(e.mayReturnEmpty for e in self.exprs)
45344556
else:
4535-
self.mayReturnEmpty = True
4557+
self._may_return_empty = True
45364558
self.skipWhitespace = True
45374559
self.initExprGroups = True
45384560
self.saveAsList = True
@@ -4547,9 +4569,9 @@ def __iand__(self, other):
45474569
def streamline(self) -> ParserElement:
45484570
super().streamline()
45494571
if self.exprs:
4550-
self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
4572+
self._may_return_empty = all(e.mayReturnEmpty for e in self.exprs)
45514573
else:
4552-
self.mayReturnEmpty = True
4574+
self._may_return_empty = True
< 10000 /code>
45534575
return self
45544576

45554577
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
@@ -4662,7 +4684,7 @@ def __init__(self, expr: Union[ParserElement, str], savelist: bool = False) -> N
46624684
self.expr = expr
46634685
if expr is not None:
46644686
self.mayIndexError = expr.mayIndexError
4665-
self.mayReturnEmpty = expr.mayReturnEmpty
4687+
self._may_return_empty = expr.mayReturnEmpty
46664688
self.set_whitespace_chars(
46674689
expr.whiteChars, copy_defaults=expr.copyDefaultWhiteChars
46684690
)
@@ -4896,7 +4918,7 @@ class FollowedBy(ParseElementEnhance):
48964918

48974919
def __init__(self, expr: Union[ParserElement, str]) -> None:
48984920
super().__init__(expr)
4899-
self.mayReturnEmpty = True
4921+
self._may_return_empty = True
49004922

49014923
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
49024924
# by using self._expr.parse and deleting the contents of the returned ParseResults list
@@ -4940,7 +4962,7 @@ class PrecededBy(ParseElementEnhance):
49404962
def __init__(self, expr: Union[ParserElement, str], retreat: int = 0) -> None:
49414963
super().__init__(expr)
49424964
self.expr = self.expr().leave_whitespace()
4943-
self.mayReturnEmpty = True
4965+
self._may_return_empty = True
49444966
self.mayIndexError = False
49454967
self.exact = False
49464968
if isinstance(expr, str_type):
@@ -5061,7 +5083,7 @@ def __init__(self, expr: Union[ParserElement, str]) -> None:
50615083
# self.leave_whitespace()
50625084
self.skipWhitespace = False
50635085

5064-
self.mayReturnEmpty = True
5086+
self._may_return_empty = True
50655087
self.errmsg = f"Found unwanted token, {self.expr}"
50665088

50675089
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
@@ -5204,7 +5226,7 @@ def __init__(
52045226
stopOn: typing.Optional[Union[ParserElement, str]] = None,
52055227
) -> None:
52065228
super().__init__(expr, stopOn=stopOn or stop_on)
5207-
self.mayReturnEmpty = True
5229+
self._may_return_empty = True
52085230

52095231
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
52105232
try:
@@ -5337,7 +5359,7 @@ def __init__(
53375359
super().__init__(expr, savelist=False)
53385360
self.saveAsList = self.expr.saveAsList
53395361
self.defaultValue = default
5340-
self.mayReturnEmpty = True
5362+
self._may_return_empty = True
53415363

53425364
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
53435365
self_expr = self.expr
@@ -5442,7 +5464,7 @@ def __init__(
54425464
super().__init__(other)
54435465
failOn = failOn or fail_on
54445466
self.ignoreExpr = ignore
5445-
self.mayReturnEmpty = True
5467+
self._may_return_empty = True
54465468
self.mayIndexError = False
54475469
self.includeMatch = include
54485470
self.saveAsList = False
@@ -5568,7 +5590,7 @@ def __lshift__(self, other) -> Forward:
55685590
self.expr = other
55695591
self.streamlined = other.streamlined
55705592
self.mayIndexError = self.expr.mayIndexError
5571-
self.mayReturnEmpty = self.expr.mayReturnEmpty
5593+
self._may_return_empty = self.expr.mayReturnEmpty
55725594
self.set_whitespace_chars(
55735595
self.expr.whiteChars, copy_defaults=self.expr.copyDefaultWhiteChars
55745596
)

0 commit comments

Comments
 (0)
0