|
|
f499a8 |
From 7fa846eea1f9f1a541ec061f272d4f82d4aee428 Mon Sep 17 00:00:00 2001
|
|
|
f499a8 |
From: Jakub Filak <jfilak@redhat.com>
|
|
|
f499a8 |
Date: Wed, 25 Jun 2014 17:28:00 +0200
|
|
|
f499a8 |
Subject: [SATYR PATCH 3/6] Fix parsing of invalid syntax Python exceptions
|
|
|
f499a8 |
|
|
|
f499a8 |
Closes #168
|
|
|
f499a8 |
|
|
|
f499a8 |
Signed-off-by: Jakub Filak <jfilak@redhat.com>
|
|
|
f499a8 |
|
|
|
f499a8 |
mmilata: commit msg typo
|
|
|
f499a8 |
---
|
|
|
f499a8 |
lib/python_frame.c | 56 +++++++++++++++++++------------
|
|
|
f499a8 |
lib/python_stacktrace.c | 69 ++++++++++++++++++++++++++++++++++----
|
|
|
f499a8 |
tests/python/python.py | 38 +++++++++++++++++++++
|
|
|
f499a8 |
tests/python_stacktraces/python-04 | 6 ++++
|
|
|
f499a8 |
tests/python_stacktraces/python-05 | 16 +++++++++
|
|
|
f499a8 |
5 files changed, 157 insertions(+), 28 deletions(-)
|
|
|
f499a8 |
create mode 100644 tests/python_stacktraces/python-04
|
|
|
f499a8 |
create mode 100644 tests/python_stacktraces/python-05
|
|
|
f499a8 |
|
|
|
f499a8 |
diff --git a/lib/python_frame.c b/lib/python_frame.c
|
|
|
f499a8 |
index b62628f..b0540fe 100644
|
|
|
f499a8 |
--- a/lib/python_frame.c
|
|
|
f499a8 |
+++ b/lib/python_frame.c
|
|
|
f499a8 |
@@ -260,33 +260,45 @@ sr_python_frame_parse(const char **input,
|
|
|
f499a8 |
|
|
|
f499a8 |
if (0 == sr_skip_string(&local_input, ", in "))
|
|
|
f499a8 |
{
|
|
|
f499a8 |
- location->message = sr_asprintf("Function name separator not found.");
|
|
|
f499a8 |
- return NULL;
|
|
|
f499a8 |
- }
|
|
|
f499a8 |
-
|
|
|
f499a8 |
- location->column += strlen(", in ");
|
|
|
f499a8 |
+ if (local_input[0] != '\n')
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ location->message = sr_asprintf("Function name separator not found.");
|
|
|
f499a8 |
+ return NULL;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
|
|
|
f499a8 |
- /* Parse function name */
|
|
|
f499a8 |
- if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name))
|
|
|
f499a8 |
+ /* The last frame of SyntaxError stack trace does not have
|
|
|
f499a8 |
+ * function name on its line. For the sake of simplicity, we will
|
|
|
f499a8 |
+ * believe that we are dealing with such a frame now.
|
|
|
f499a8 |
+ */
|
|
|
f499a8 |
+ frame->function_name = sr_strdup("syntax");
|
|
|
f499a8 |
+ frame->special_function = true;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+ else
|
|
|
f499a8 |
{
|
|
|
f499a8 |
- sr_python_frame_free(frame);
|
|
|
f499a8 |
- location->message = sr_asprintf("Unable to find the newline character "
|
|
|
f499a8 |
- "identifying the end of function name.");
|
|
|
f499a8 |
+ location->column += strlen(", in ");
|
|
|
f499a8 |
|
|
|
f499a8 |
- return NULL;
|
|
|
f499a8 |
- }
|
|
|
f499a8 |
+ /* Parse function name */
|
|
|
f499a8 |
+ if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name))
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ sr_python_frame_free(frame);
|
|
|
f499a8 |
+ location->message = sr_asprintf("Unable to find the newline character "
|
|
|
f499a8 |
+ "identifying the end of function name.");
|
|
|
f499a8 |
|
|
|
f499a8 |
- location->column += strlen(frame->function_name);
|
|
|
f499a8 |
+ return NULL;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
|
|
|
f499a8 |
- if (strlen(frame->function_name) > 0 &&
|
|
|
f499a8 |
- frame->function_name[0] == '<' &&
|
|
|
f499a8 |
- frame->function_name[strlen(frame->function_name)-1] == '>')
|
|
|
f499a8 |
- {
|
|
|
f499a8 |
- frame->special_function = true;
|
|
|
f499a8 |
- frame->function_name[strlen(frame->function_name)-1] = '\0';
|
|
|
f499a8 |
- char *inside = sr_strdup(frame->function_name + 1);
|
|
|
f499a8 |
- free(frame->function_name);
|
|
|
f499a8 |
- frame->function_name = inside;
|
|
|
f499a8 |
+ location->column += strlen(frame->function_name);
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ if (strlen(frame->function_name) > 0 &&
|
|
|
f499a8 |
+ frame->function_name[0] == '<' &&
|
|
|
f499a8 |
+ frame->function_name[strlen(frame->function_name)-1] == '>')
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ frame->special_function = true;
|
|
|
f499a8 |
+ frame->function_name[strlen(frame->function_name)-1] = '\0';
|
|
|
f499a8 |
+ char *inside = sr_strdup(frame->function_name + 1);
|
|
|
f499a8 |
+ free(frame->function_name);
|
|
|
f499a8 |
+ frame->function_name = inside;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
}
|
|
|
f499a8 |
|
|
|
f499a8 |
sr_skip_char(&local_input, '\n');
|
|
|
f499a8 |
diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c
|
|
|
f499a8 |
index 3dc0a75..99aa52c 100644
|
|
|
f499a8 |
--- a/lib/python_stacktrace.c
|
|
|
f499a8 |
+++ b/lib/python_stacktrace.c
|
|
|
f499a8 |
@@ -29,6 +29,7 @@
|
|
|
f499a8 |
#include "generic_thread.h"
|
|
|
f499a8 |
#include "internal_utils.h"
|
|
|
f499a8 |
#include <stdio.h>
|
|
|
f499a8 |
+#include <stdlib.h>
|
|
|
f499a8 |
#include <string.h>
|
|
|
f499a8 |
#include <inttypes.h>
|
|
|
f499a8 |
|
|
|
f499a8 |
@@ -137,13 +138,48 @@ sr_python_stacktrace_parse(const char **input,
|
|
|
f499a8 |
|
|
|
f499a8 |
if (!local_input)
|
|
|
f499a8 |
{
|
|
|
f499a8 |
- location->message = "Traceback header not found.";
|
|
|
f499a8 |
- return NULL;
|
|
|
f499a8 |
- }
|
|
|
f499a8 |
+ /* SyntaxError stack trace of an exception thrown in the executed file
|
|
|
f499a8 |
+ * conforms to the following template:
|
|
|
f499a8 |
+ * invalid syntax ($file, line $number)
|
|
|
f499a8 |
+ *
|
|
|
f499a8 |
+ * File "$file", line $number
|
|
|
f499a8 |
+ * $code
|
|
|
f499a8 |
+ * ^
|
|
|
f499a8 |
+ * SyntaxError: invalid syntax
|
|
|
f499a8 |
+ *
|
|
|
f499a8 |
+ * for exceptions thrown from imported files, the stack trace has the
|
|
|
f499a8 |
+ * regular form, except the last frame has no function name and is
|
|
|
f499a8 |
+ * followed by the pointer line (^).
|
|
|
f499a8 |
+ */
|
|
|
f499a8 |
+ HEADER = "invalid syntax (",
|
|
|
f499a8 |
+ local_input = sr_strstr_location(*input,
|
|
|
f499a8 |
+ HEADER,
|
|
|
f499a8 |
+ &location->line,
|
|
|
f499a8 |
+ &location->column);
|
|
|
f499a8 |
|
|
|
f499a8 |
- local_input += strlen(HEADER);
|
|
|
f499a8 |
- location->line += 2;
|
|
|
f499a8 |
- location->column = 0;
|
|
|
f499a8 |
+ if (!local_input)
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ location->message = "Traceback header not found.";
|
|
|
f499a8 |
+ return NULL;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ local_input = sr_strstr_location(local_input,
|
|
|
f499a8 |
+ " File \"",
|
|
|
f499a8 |
+ &location->line,
|
|
|
f499a8 |
+ &location->column);
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ if (!local_input)
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ location->message = "Frame with invalid line not found.";
|
|
|
f499a8 |
+ return NULL;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+ else
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ local_input += strlen(HEADER);
|
|
|
f499a8 |
+ location->line += 2;
|
|
|
f499a8 |
+ location->column = 0;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
|
|
|
f499a8 |
struct sr_python_stacktrace *stacktrace = sr_python_stacktrace_new();
|
|
|
f499a8 |
|
|
|
f499a8 |
@@ -173,6 +209,27 @@ sr_python_stacktrace_parse(const char **input,
|
|
|
f499a8 |
return NULL;
|
|
|
f499a8 |
}
|
|
|
f499a8 |
|
|
|
f499a8 |
+ bool invalid_syntax_pointer = true;
|
|
|
f499a8 |
+ const char *tmp_input = local_input;
|
|
|
f499a8 |
+ while (*tmp_input != '\n' && *tmp_input != '\0')
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ if (*tmp_input != ' ' && *tmp_input != '^')
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ invalid_syntax_pointer = false;
|
|
|
f499a8 |
+ break;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+ ++tmp_input;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ if (invalid_syntax_pointer)
|
|
|
f499a8 |
+ {
|
|
|
f499a8 |
+ /* Skip line " ^" pointing to the invalid code */
|
|
|
f499a8 |
+ sr_skip_char_cspan(&local_input, "\n");
|
|
|
f499a8 |
+ ++local_input;
|
|
|
f499a8 |
+ ++location->line;
|
|
|
f499a8 |
+ location->column = 1;
|
|
|
f499a8 |
+ }
|
|
|
f499a8 |
+
|
|
|
f499a8 |
/* Parse exception name. */
|
|
|
f499a8 |
sr_parse_char_cspan(&local_input,
|
|
|
f499a8 |
":\n",
|
|
|
f499a8 |
diff --git a/tests/python/python.py b/tests/python/python.py
|
|
|
f499a8 |
index 146ca01..9044200 100755
|
|
|
f499a8 |
--- a/tests/python/python.py
|
|
|
f499a8 |
+++ b/tests/python/python.py
|
|
|
f499a8 |
@@ -143,6 +143,44 @@ class TestPythonStacktrace(BindingsTestCase):
|
|
|
f499a8 |
def test_hash(self):
|
|
|
f499a8 |
self.assertHashable(self.trace)
|
|
|
f499a8 |
|
|
|
f499a8 |
+ def test_invalid_syntax_current_file(self):
|
|
|
f499a8 |
+ trace = load_input_contents('../python_stacktraces/python-04')
|
|
|
f499a8 |
+ trace = satyr.PythonStacktrace(trace)
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ self.assertEqual(len(trace.frames), 1)
|
|
|
f499a8 |
+ self.assertEqual(trace.exception_name, 'SyntaxError')
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ f = trace.frames[0]
|
|
|
f499a8 |
+ self.assertEqual(f.file_name, '/usr/bin/python3-mako-render')
|
|
|
f499a8 |
+ self.assertEqual(f.function_name, "syntax")
|
|
|
f499a8 |
+ self.assertEqual(f.file_line, 43)
|
|
|
f499a8 |
+ self.assertEqual(f.line_contents, 'print render(data, kw)')
|
|
|
f499a8 |
+ self.assertTrue(f.special_function)
|
|
|
f499a8 |
+ self.assertFalse(f.special_file)
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ def test_invalid_syntax_imported_file(self):
|
|
|
f499a8 |
+ trace = load_input_contents('../python_stacktraces/python-05')
|
|
|
f499a8 |
+ trace = satyr.PythonStacktrace(trace)
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ self.assertEqual(len(trace.frames), 2)
|
|
|
f499a8 |
+ self.assertEqual(trace.exception_name, 'SyntaxError')
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ f = trace.frames[1]
|
|
|
f499a8 |
+ self.assertEqual(f.file_name, '/usr/bin/will_python_raise')
|
|
|
f499a8 |
+ self.assertEqual(f.function_name, "module")
|
|
|
f499a8 |
+ self.assertEqual(f.file_line, 2)
|
|
|
f499a8 |
+ self.assertEqual(f.line_contents, 'import report')
|
|
|
f499a8 |
+ self.assertTrue(f.special_function)
|
|
|
f499a8 |
+ self.assertFalse(f.special_file)
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ f = trace.frames[0]
|
|
|
f499a8 |
+ self.assertEqual(f.file_name, '/usr/lib64/python2.7/site-packages/report/__init__.py')
|
|
|
f499a8 |
+ self.assertEqual(f.function_name, "syntax")
|
|
|
f499a8 |
+ self.assertEqual(f.file_line, 15)
|
|
|
f499a8 |
+ self.assertEqual(f.line_contents, 'def foo(:')
|
|
|
f499a8 |
+ self.assertTrue(f.special_function)
|
|
|
f499a8 |
+ self.assertFalse(f.special_file)
|
|
|
f499a8 |
+
|
|
|
f499a8 |
class TestPythonFrame(BindingsTestCase):
|
|
|
f499a8 |
def setUp(self):
|
|
|
f499a8 |
self.frame = satyr.PythonStacktrace(contents).frames[-1]
|
|
|
f499a8 |
diff --git a/tests/python_stacktraces/python-04 b/tests/python_stacktraces/python-04
|
|
|
f499a8 |
new file mode 100644
|
|
|
f499a8 |
index 0000000..91c3a3d
|
|
|
f499a8 |
--- /dev/null
|
|
|
f499a8 |
+++ b/tests/python_stacktraces/python-04
|
|
|
f499a8 |
@@ -0,0 +1,6 @@
|
|
|
f499a8 |
+invalid syntax (python3-mako-render, line 43)
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+ File "/usr/bin/python3-mako-render", line 43
|
|
|
f499a8 |
+ print render(data, kw)
|
|
|
f499a8 |
+ ^
|
|
|
f499a8 |
+SyntaxError: invalid syntax
|
|
|
f499a8 |
diff --git a/tests/python_stacktraces/python-05 b/tests/python_stacktraces/python-05
|
|
|
f499a8 |
new file mode 100644
|
|
|
f499a8 |
index 0000000..79d1b8d
|
|
|
f499a8 |
--- /dev/null
|
|
|
f499a8 |
+++ b/tests/python_stacktraces/python-05
|
|
|
f499a8 |
@@ -0,0 +1,16 @@
|
|
|
f499a8 |
+will_python_raise:2:<module>: File "/usr/lib64/python2.7/site-packages/report/__init__.py", line 15
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+Traceback (most recent call last):
|
|
|
f499a8 |
+ File "/usr/bin/will_python_raise", line 2, in <module>
|
|
|
f499a8 |
+ import report
|
|
|
f499a8 |
+ File "/usr/lib64/python2.7/site-packages/report/__init__.py", line 15
|
|
|
f499a8 |
+ def foo(:
|
|
|
f499a8 |
+ ^
|
|
|
f499a8 |
+SyntaxError: invalid syntax
|
|
|
f499a8 |
+
|
|
|
f499a8 |
+Local variables in innermost frame:
|
|
|
f499a8 |
+__builtins__: <module '__builtin__' (built-in)>
|
|
|
f499a8 |
+__name__: '__main__'
|
|
|
f499a8 |
+__file__: '/usr/bin/will_python_raise'
|
|
|
f499a8 |
+__doc__: None
|
|
|
f499a8 |
+__package__: None
|
|
|
f499a8 |
--
|
|
|
f499a8 |
1.9.3
|
|
|
f499a8 |
|