Blame SOURCES/satyr-0.13-python-exceptions.patch

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