From 2b48ff26f8e3108d3a6859c3702cd3fd38fcede5 Mon Sep 17 00:00:00 2001 From: ptmcg Date: Fri, 12 Aug 2016 14:48:18 +0000 Subject: Add limit=n args to extract_stack and extract_tb calls, to minimize scanning through full call stack - trying to address UnicodeDecodeError exceptions from loading in more code than we need. git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@415 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b --- src/CHANGES | 5 +++++ src/pyparsing.py | 14 +++++++------- src/unitTests.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/CHANGES b/src/CHANGES index 6971d81..b73b7bb 100644 --- a/src/CHANGES +++ b/src/CHANGES @@ -4,6 +4,11 @@ Change Log Version 2.1.8 - ------------------------------ +- Fixed issue in the optimization to _trim_arity, when the full + stacktrace is retrieved to determine if a TypeError is raised in + pyparsing or in the caller's parse action. Code was traversing + the full stacktrace, and potentially encountering UnicodeDecodeError. + - Fixed bug in ParserElement.inlineLiteralsUsing, causing infinite loop with Suppress. diff --git a/src/pyparsing.py b/src/pyparsing.py index e101ab4..9191426 100644 --- a/src/pyparsing.py +++ b/src/pyparsing.py @@ -58,7 +58,7 @@ The pyparsing module handles some of the problems that are typically vexing when """ __version__ = "2.1.8" -__versionTime__ = "11 Aug 2016 20:00 UTC" +__versionTime__ = "12 Aug 2016 14:46 UTC" __author__ = "Paul McGuire " import string @@ -1017,13 +1017,13 @@ def _trim_arity(func, maxargs=2): # traceback return data structure changed in Py3.5 - normalize back to plain tuples if system_version[:2] >= (3,5): - def extract_stack(): + def extract_stack(limit=0): # special handling for Python 3.5.0 - extra deep call stack by 1 offset = -3 if system_version == (3,5,0) else -2 - frame_summary = traceback.extract_stack()[offset] + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] return [(frame_summary.filename, frame_summary.lineno)] - def extract_tb(tb): - frames = traceback.extract_tb(tb) + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) frame_summary = frames[-1] return [(frame_summary.filename, frame_summary.lineno)] else: @@ -1036,7 +1036,7 @@ def _trim_arity(func, maxargs=2): LINE_DIFF = 6 # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! - this_line = extract_stack()[-1] + this_line = extract_stack(limit=2)[-1] pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) def wrapper(*args): @@ -1052,7 +1052,7 @@ def _trim_arity(func, maxargs=2): else: try: tb = sys.exc_info()[-1] - if not extract_tb(tb)[-1][:2] == pa_call_line_synth: + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: raise finally: del tb diff --git a/src/unitTests.py b/src/unitTests.py index 87b4beb..1f7964e 100644 --- a/src/unitTests.py +++ b/src/unitTests.py @@ -2529,6 +2529,58 @@ class TrimArityExceptionMaskingTest(ParseTestCase): exc_msg = str(e) assert exc_msg != invalid_message, "failed to catch TypeError thrown in _trim_arity" +class TrimArityExceptionMaskingTest2(ParseTestCase): + def runTest(self): + + + # construct deep call tree + def A(): + import traceback + + traceback.print_stack(limit=2) + + from pyparsing import Word + + invalid_message = [ + "() takes exactly 1 argument (0 given)", + "() missing 1 required positional argument: 't'" + ][PY_3] + try: + Word('a').setParseAction(lambda t: t[0]+1).parseString('aaa') + except Exception as e: + exc_msg = str(e) + assert exc_msg != invalid_message, "failed to catch TypeError thrown in _trim_arity" + + + def B(): + A() + + def C(): + B() + + def D(): + C() + + def E(): + D() + + def F(): + E() + + def G(): + F() + + def H(): + G() + + def J(): + H() + + def K(): + J() + + K() + class OneOrMoreStopTest(ParseTestCase): def runTest(self): from pyparsing import (Word, OneOrMore, alphas, Keyword, CaselessKeyword, -- cgit v1.2.1