Skip to content

Commit

Permalink
Selected backports from 3.0.0 work
Browse files Browse the repository at this point in the history
  • Loading branch information
ptmcg committed Mar 7, 2020
1 parent 847af59 commit 7daecd9
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 20 deletions.
9 changes: 9 additions & 0 deletions CHANGES
Expand Up @@ -2,6 +2,15 @@
Change Log
==========

Version 2.4.7 - March, 2020
---------------------------
- Backport of selected fixes from 3.0.0 work:
. Each bug with Regex expressions
. And expressions not properly constructing with generator
. Traceback abbreviation
. Bug in delta_time example


Version 2.4.6 - December, 2019
------------------------------
- Fixed typos in White mapping of whitespace characters, to use
Expand Down
19 changes: 16 additions & 3 deletions examples/delta_time.py
Expand Up @@ -52,6 +52,9 @@ def make_integer_word_expr(int_name, int_value):
def plural(s):
return CK(s) | CK(s + 's').addParseAction(pp.replaceWith(s))
week, day, hour, minute, second = map(plural, "week day hour minute second".split())
time_units = hour | minute | second
any_time_units = week | day | time_units

am = CL("am")
pm = CL("pm")
COLON = pp.Suppress(':')
Expand All @@ -69,7 +72,7 @@ def plural(s):
couple = (pp.Optional(CK("a")) + CK("couple") + pp.Optional(CK("of"))).setParseAction(pp.replaceWith(2))
a_qty = (CK("a") | CK("an")).setParseAction(pp.replaceWith(1))
the_qty = CK("the").setParseAction(pp.replaceWith(1))
qty = pp.ungroup(integer | couple | a_qty | the_qty)
qty = pp.ungroup(integer | couple | a_qty | the_qty).setName("qty")
time_ref_present = pp.Empty().addParseAction(pp.replaceWith(True))('time_ref_present')

def fill_24hr_time_fields(t):
Expand All @@ -86,8 +89,10 @@ def fill_default_time_fields(t):
weekday_name_list = list(calendar.day_name)
weekday_name = pp.oneOf(weekday_name_list)

_24hour_time = pp.Word(pp.nums, exact=4).addParseAction(lambda t: [int(t[0][:2]),int(t[0][2:])],
fill_24hr_time_fields)
_24hour_time = (~(integer + any_time_units)
+ pp.Word(pp.nums, exact=4).addParseAction(lambda t: [int(t[0][:2]),int(t[0][2:])],
fill_24hr_time_fields)
)
_24hour_time.setName("0000 time")
ampm = am | pm
timespec = (integer("HH")
Expand Down Expand Up @@ -302,13 +307,21 @@ def remove_temp_keys(t):
2pm next Sunday
next Sunday at 2pm
last Sunday at 2pm
10 seconds ago
100 seconds ago
1000 seconds ago
10000 seconds ago
"""

time_of_day = timedelta(hours=current_time.hour,
minutes=current_time.minute,
seconds=current_time.second)
expected = {
'now' : timedelta(0),
"10 seconds ago": timedelta(seconds=-10),
"100 seconds ago": timedelta(seconds=-100),
"1000 seconds ago": timedelta(seconds=-1000),
"10000 seconds ago": timedelta(seconds=-10000),
'10 minutes ago': timedelta(minutes=-10),
'10 minutes from now': timedelta(minutes=10),
'in 10 minutes': timedelta(minutes=10),
Expand Down
30 changes: 21 additions & 9 deletions pyparsing.py
Expand Up @@ -95,8 +95,8 @@
namespace class
"""

__version__ = "2.4.6"
__versionTime__ = "24 Dec 2019 04:27 UTC"
__version__ = "2.4.7"
__versionTime__ = "04 Mar 2020 02:48 UTC"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"

import string
Expand Down Expand Up @@ -1391,6 +1391,12 @@ def inlineLiteralsUsing(cls):
"""
ParserElement._literalStringClass = cls

@classmethod
def _trim_traceback(cls, tb):
while tb.tb_next:
tb = tb.tb_next
return tb

def __init__(self, savelist=False):
self.parseAction = list()
self.failAction = None
Expand Down Expand Up @@ -1943,7 +1949,8 @@ def parseString(self, instring, parseAll=False):
if ParserElement.verbose_stacktrace:
raise
else:
# catch and re-raise exception from here, clears out pyparsing internal stack trace
# catch and re-raise exception from here, clearing out pyparsing internal stack trace
exc.__traceback__ = self._trim_traceback(exc.__traceback__)
raise exc
else:
return tokens
Expand Down Expand Up @@ -2017,7 +2024,8 @@ def scanString(self, instring, maxMatches=_MAX_INT, overlap=False):
if ParserElement.verbose_stacktrace:
raise
else:
# catch and re-raise exception from here, clears out pyparsing internal stack trace
# catch and re-raise exception from here, clearing out pyparsing internal stack trace
exc.__traceback__ = self._trim_traceback(exc.__traceback__)
raise exc

def transformString(self, instring):
Expand Down Expand Up @@ -2063,7 +2071,8 @@ def transformString(self, instring):
if ParserElement.verbose_stacktrace:
raise
else:
# catch and re-raise exception from here, clears out pyparsing internal stack trace
# catch and re-raise exception from here, clearing out pyparsing internal stack trace
exc.__traceback__ = self._trim_traceback(exc.__traceback__)
raise exc

def searchString(self, instring, maxMatches=_MAX_INT):
Expand Down Expand Up @@ -2093,7 +2102,8 @@ def searchString(self, instring, maxMatches=_MAX_INT):
if ParserElement.verbose_stacktrace:
raise
else:
# catch and re-raise exception from here, clears out pyparsing internal stack trace
# catch and re-raise exception from here, clearing out pyparsing internal stack trace
exc.__traceback__ = self._trim_traceback(exc.__traceback__)
raise exc

def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
Expand Down Expand Up @@ -2565,7 +2575,8 @@ def parseFile(self, file_or_filename, parseAll=False):
if ParserElement.verbose_stacktrace:
raise
else:
# catch and re-raise exception from here, clears out pyparsing internal stack trace
# catch and re-raise exception from here, clearing out pyparsing internal stack trace
exc.__traceback__ = self._trim_traceback(exc.__traceback__)
raise exc

def __eq__(self, other):
Expand Down Expand Up @@ -3312,7 +3323,7 @@ def __init__(self, pattern, flags=0, asGroupList=False, asMatch=False):
self.name = _ustr(self)
self.errmsg = "Expected " + self.name
self.mayIndexError = False
self.mayReturnEmpty = True
self.mayReturnEmpty = self.re_match("") is not None
self.asGroupList = asGroupList
self.asMatch = asMatch
if self.asGroupList:
Expand Down Expand Up @@ -3993,6 +4004,7 @@ def __init__(self, *args, **kwargs):
self.leaveWhitespace()

def __init__(self, exprs, savelist=True):
exprs = list(exprs)
if exprs and Ellipsis in exprs:
tmp = []
for i, expr in enumerate(exprs):
Expand Down Expand Up @@ -4358,7 +4370,7 @@ def parseImpl(self, instring, loc, doActions=True):
if self.initExprGroups:
self.opt1map = dict((id(e.expr), e) for e in self.exprs if isinstance(e, Optional))
opt1 = [e.expr for e in self.exprs if isinstance(e, Optional)]
opt2 = [e for e in self.exprs if e.mayReturnEmpty and not isinstance(e, Optional)]
opt2 = [e for e in self.exprs if e.mayReturnEmpty and not isinstance(e, (Optional, Regex))]
self.optionals = opt1 + opt2
self.multioptionals = [e.expr for e in self.exprs if isinstance(e, ZeroOrMore)]
self.multirequired = [e.expr for e in self.exprs if isinstance(e, OneOrMore)]
Expand Down
52 changes: 44 additions & 8 deletions unitTests.py
Expand Up @@ -2845,14 +2845,28 @@ class OptionalEachTest(ParseTestCase):
def runTest1(self):
from pyparsing import Optional, Keyword

the_input = "Major Tal Weiss"
parser1 = (Optional('Tal') + Optional('Weiss')) & Keyword('Major')
parser2 = Optional(Optional('Tal') + Optional('Weiss')) & Keyword('Major')
p1res = parser1.parseString( the_input)
p2res = parser2.parseString( the_input)
self.assertEqual(p1res.asList(), p2res.asList(),
"Each failed to match with nested Optionals, "
+ str(p1res.asList()) + " should match " + str(p2res.asList()))
for the_input in [
"Tal Weiss Major",
"Tal Major",
"Weiss Major",
"Major",
"Major Tal",
"Major Weiss",
"Major Tal Weiss",
]:
print_(the_input)
parser1 = (Optional("Tal") + Optional("Weiss")) & Keyword("Major")
parser2 = Optional(Optional("Tal") + Optional("Weiss")) & Keyword("Major")
p1res = parser1.parseString(the_input)
p2res = parser2.parseString(the_input)
self.assertEqual(
p1res.asList(),
p2res.asList(),
"Each failed to match with nested Optionals, "
+ str(p1res.asList())
+ " should match "
+ str(p2res.asList()),
)

def runTest2(self):
from pyparsing import Word, alphanums, OneOrMore, Group, Regex, Optional
Expand Down Expand Up @@ -2906,12 +2920,34 @@ def runTest4(self):
42
""")

def testParseExpressionsWithRegex(self):
from itertools import product
match_empty_regex = pp.Regex(r"[a-z]*")
match_nonempty_regex = pp.Regex(r"[a-z]+")

parser_classes = pp.ParseExpression.__subclasses__()
test_string = "abc def"
expected = ["abc"]
for expr, cls in product((match_nonempty_regex, match_empty_regex), parser_classes):
print_(expr, cls)
parser = cls([expr])
parsed_result = parser.parseString(test_string)
print_(parsed_result.dump())
self.assertParseResultsEquals(parsed_result, expected)

for expr, cls in product((match_nonempty_regex, match_empty_regex), (pp.MatchFirst, pp.Or)):
parser = cls([expr, expr])
print_(parser)
parsed_result = parser.parseString(test_string)
print_(parsed_result.dump())
self.assertParseResultsEquals(parsed_result, expected)

def runTest(self):
self.runTest1()
self.runTest2()
self.runTest3()
self.runTest4()
self.testParseExpressionsWithRegex()

class SumParseResultsTest(ParseTestCase):
def runTest(self):
Expand Down

0 comments on commit 7daecd9

Please sign in to comment.