summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2016-10-04 00:23:22 +0000
committerptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2016-10-04 00:23:22 +0000
commitff26efcec65724844461f54a0eabd04487f6291f (patch)
treefb790b11283850d4deb2da5c7ad075cec2f690b8
parent80b2dcbf152a6c5dd539db3fba8f99a19ddd1a56 (diff)
downloadpyparsing-ff26efcec65724844461f54a0eabd04487f6291f.tar.gz
Fixed behavior of LineStart; added AutoReset to other unit tests that modify global state in pyparsing; also, disable output buffering in unit tests when specific test classes are given to build the test suite
git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@446 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b
-rw-r--r--src/CHANGES5
-rw-r--r--src/pyparsing.py18
-rw-r--r--src/unitTests.py105
3 files changed, 95 insertions, 33 deletions
diff --git a/src/CHANGES b/src/CHANGES
index 7a9d3f2..bf964aa 100644
--- a/src/CHANGES
+++ b/src/CHANGES
@@ -7,6 +7,11 @@ Version 2.1.10 -
- Fixed bug in reporting named parse results for ZeroOrMore
expressions, thanks Ethan Nash for reporting this!
+- Fixed behavior of LineStart to be much more predictable.
+ LineStart can now be used to detect if the next parse position
+ is col 1, factoring in potential leading whitespace (which would
+ cause LineStart to fail).
+
- Added support for multiline test strings in runTests.
- Fixed bug in ParseResults.dump when keys were not strings.
diff --git a/src/pyparsing.py b/src/pyparsing.py
index 516e804..a12e1b6 100644
--- a/src/pyparsing.py
+++ b/src/pyparsing.py
@@ -61,7 +61,7 @@ The pyparsing module handles some of the problems that are typically vexing when
"""
__version__ = "2.1.10"
-__versionTime__ = "02 Oct 2016 23:15 UTC"
+__versionTime__ = "04 Oct 2016 00:19 UTC"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
import string
@@ -3085,27 +3085,25 @@ class GoToColumn(_PositionToken):
ret = instring[ loc: newloc ]
return newloc, ret
+
class LineStart(_PositionToken):
"""
Matches if current position is at the beginning of a line within the parse string
"""
def __init__( self ):
super(LineStart,self).__init__()
- self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+ self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n", ""))
self.errmsg = "Expected start of line"
def preParse( self, instring, loc ):
- preloc = super(LineStart,self).preParse(instring,loc)
- if instring[preloc] == "\n":
- loc += 1
+ # don't do any pre-parsing for this class
return loc
def parseImpl( self, instring, loc, doActions=True ):
- if not( loc==0 or
- (loc == self.preParse( instring, 0 )) or
- (instring[loc-1] == "\n") ): #col(loc, instring) != 1:
- raise ParseException(instring, loc, self.errmsg, self)
- return loc, []
+ loc, _ = Optional(White(' \t').leaveWhitespace() + Empty()).parseImpl(instring, loc)
+ if loc == 0 or col(loc, instring) == 1:
+ return loc, []
+ raise ParseException(instring, loc, self.errmsg, self)
class LineEnd(_PositionToken):
"""
diff --git a/src/unitTests.py b/src/unitTests.py
index 04aa5d8..d97c27a 100644
--- a/src/unitTests.py
+++ b/src/unitTests.py
@@ -76,6 +76,8 @@ class AutoReset(object):
for attr, value in zip(self.save_attrs, self.save_values):
setattr(self.ob, attr, value)
+BUFFER_OUTPUT = True
+
class ParseTestCase(TestCase):
def __init__(self):
super(ParseTestCase, self).__init__(methodName='_runTest')
@@ -87,8 +89,9 @@ class ParseTestCase(TestCase):
try:
with AutoReset(sys, 'stdout', 'stderr'):
try:
- sys.stdout = buffered_stdout
- sys.stderr = buffered_stdout
+ if BUFFER_OUTPUT:
+ sys.stdout = buffered_stdout
+ sys.stderr = buffered_stdout
print_(">>>> Starting test",str(self))
self.runTest()
@@ -97,8 +100,9 @@ class ParseTestCase(TestCase):
print_()
except Exception as exc:
- print_()
- print_(buffered_stdout.getvalue())
+ if BUFFER_OUTPUT:
+ print_()
+ print_(buffered_stdout.getvalue())
raise
@@ -1674,6 +1678,63 @@ class CountedArrayTest3(ParseTestCase):
assert r.asList() == [[5,7],[0,1,2,3,4,5],[],[5,4,3]], \
"Failed matching countedArray, got " + str(r.asList())
+class LineStartTest(ParseTestCase):
+ def runTest(self):
+ import pyparsing as pp
+
+ pass_tests = [
+ """\
+ AAA
+ BBB
+ """,
+ """\
+ AAA...
+ BBB
+ """,
+ ]
+ fail_tests = [
+ """\
+ AAA...
+ ...BBB
+ """,
+ """\
+ AAA BBB
+ """,
+ ]
+
+ # cleanup test strings
+ pass_tests = ['\n'.join(s.lstrip() for s in t.splitlines()).replace('.', ' ') for t in pass_tests]
+ fail_tests = ['\n'.join(s.lstrip() for s in t.splitlines()).replace('.', ' ') for t in fail_tests]
+
+ test_patt = pp.Word('A') - pp.LineStart() + pp.Word('B')
+ print(test_patt.streamline())
+ success = test_patt.runTests(pass_tests)[0]
+ assert success, "failed LineStart passing tests (1)"
+
+ success = test_patt.runTests(fail_tests, failureTests=True)[0]
+ assert success, "failed LineStart failure mode tests (1)"
+
+ with AutoReset(pp.ParserElement, "DEFAULT_WHITE_CHARS"):
+ print(r'no \n in default whitespace chars')
+ pp.ParserElement.setDefaultWhitespaceChars(' ')
+
+ test_patt = pp.Word('A') - pp.LineStart() + pp.Word('B')
+ print(test_patt.streamline())
+ # should fail the pass tests too, since \n is no longer valid whitespace and we aren't parsing for it
+ success = test_patt.runTests(pass_tests, failureTests=True)[0]
+ assert success, "failed LineStart passing tests (2)"
+
+ success = test_patt.runTests(fail_tests, failureTests=True)[0]
+ assert success, "failed LineStart failure mode tests (2)"
+
+ test_patt = pp.Word('A') - pp.LineEnd().suppress() + pp.LineStart() + pp.Word('B') + pp.LineEnd().suppress()
+ print(test_patt.streamline())
+ success = test_patt.runTests(pass_tests)[0]
+ assert success, "failed LineStart passing tests (3)"
+
+ success = test_patt.runTests(fail_tests, failureTests=True)[0]
+ assert success, "failed LineStart failure mode tests (3)"
+
class LineAndStringEndTest(ParseTestCase):
def runTest(self):
from pyparsing import OneOrMore,lineEnd,alphanums,Word,stringEnd,delimitedList,SkipTo
@@ -3197,16 +3258,14 @@ class DefaultKeywordCharsTest(ParseTestCase):
else:
pass
- save_kwd_chars = pp.Keyword.DEFAULT_KEYWORD_CHARS
- pp.Keyword.setDefaultKeywordChars(pp.alphas)
- try:
- pp.Keyword("start").parseString("start1000")
- except pp.ParseException:
- assert False, "failed to match keyword using updated keyword chars"
- else:
- pass
-
- pp.Keyword.setDefaultKeywordChars(save_kwd_chars)
+ with AutoReset(pp.Keyword, "DEFAULT_KEYWORD_CHARS"):
+ pp.Keyword.setDefaultKeywordChars(pp.alphas)
+ try:
+ pp.Keyword("start").parseString("start1000")
+ except pp.ParseException:
+ assert False, "failed to match keyword using updated keyword chars"
+ else:
+ pass
try:
pp.CaselessKeyword("START").parseString("start1000")
@@ -3222,15 +3281,14 @@ class DefaultKeywordCharsTest(ParseTestCase):
else:
pass
- pp.Keyword.setDefaultKeywordChars(pp.alphas)
- try:
- pp.CaselessKeyword("START").parseString("start1000")
- except pp.ParseException:
- assert False, "failed to match keyword using updated keyword chars"
- else:
- pass
-
- pp.Keyword.setDefaultKeywordChars(save_kwd_chars)
+ with AutoReset(pp.Keyword, "DEFAULT_KEYWORD_CHARS"):
+ pp.Keyword.setDefaultKeywordChars(pp.alphas)
+ try:
+ pp.CaselessKeyword("START").parseString("start1000")
+ except pp.ParseException:
+ assert False, "failed to match keyword using updated keyword chars"
+ else:
+ pass
class MiscellaneousParserTests(ParseTestCase):
@@ -3493,6 +3551,7 @@ if console:
if not testclasses:
testRunner.run( makeTestSuite() )
else:
+ BUFFER_OUTPUT = False
if lp is None:
testRunner.run( makeTestSuiteTemp(testclasses) )
else: