diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 73f44cc..8faa465 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,8 @@ +3.0.2 + +- Fix UnicodeDecodeError being thrown when sending payload due to jsonpickle not pickling large binary values correctly (such as local variable bytestrings or high-codepoint unicode characters) +- Guard added for tracebacks with null method names (such as exceptions from C-wrapper libraries) + 3.0.1 - Updated bdist egg to include middleware package diff --git a/python2/raygun4py/raygunmsgs.py b/python2/raygun4py/raygunmsgs.py index f8c7cbb..9828b6f 100644 --- a/python2/raygun4py/raygunmsgs.py +++ b/python2/raygun4py/raygunmsgs.py @@ -1,6 +1,7 @@ import traceback import inspect import os +import sys try: import multiprocessing @@ -114,6 +115,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, options): if 'transmitGlobalVariables' in options and options['transmitGlobalVariables'] is True: self.globalVariables = globals() + frames = None try: frames = inspect.getinnerframes(exc_traceback) @@ -123,7 +125,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, options): 'lineNumber': frame[2], 'className': frame[3], 'fileName': frame[1], - 'methodName': frame[4][0], + 'methodName': frame[4][0] if frame[4] is not None else None, 'localVariables': self._get_locals(frame[0]) if 'transmitLocalVariables' in options and options['transmitLocalVariables'] is True else None }) finally: diff --git a/python2/tests/test_functional.py b/python2/tests/test_functional.py index d8d7484..550bc7a 100644 --- a/python2/tests/test_functional.py +++ b/python2/tests/test_functional.py @@ -1,3 +1,5 @@ +# coding=utf-8 + import unittest2 as unittest import sys, logging, socket, os from raygun4py import raygunprovider @@ -138,7 +140,7 @@ def test_filter_keys_202(self): raise Exception("Raygun4py functional test - Py2 filter_keys") except Exception as e: httpResult = client.send_exception(e, exc_info = sys.exc_info()) - + self.assertEqual(httpResult[0], 202) @unittest.skip('Requires a proxy, skipping for Travis') @@ -150,7 +152,7 @@ def test_proxy(self): raise Exception("Raygun4py functional test - Py2 set_proxy") except Exception as e: httpResult = client.send_exception(e, exc_info = sys.exc_info()) - + self.assertEqual(httpResult[0], 202) @@ -162,7 +164,7 @@ def test_before_send_callback(self): raise Exception("Raygun4py functional test - on_before_send") except Exception as e: httpResult = client.send_exception(e, exc_info = sys.exc_info()) - + self.assertEqual(httpResult[0], 202) def test_before_send_callback_sets_none_cancels_send(self): @@ -173,7 +175,7 @@ def test_before_send_callback_sets_none_cancels_send(self): raise Exception("Raygun4py functional test - on_before_send") except Exception as e: result = client.send_exception(e, exc_info = sys.exc_info()) - + self.assertIsNone(result) def test_request(self): @@ -218,6 +220,40 @@ def test_localvariables_multilevels(self): self.assertEqual(result[0], 202) + def test_utf8_message(self): + client = raygunprovider.RaygunSender(self.apiKey) + + try: + raise Exception("ΔΔΔΔ") + except Exception as e: + result = client.send_exception(httpRequest={}) + + self.assertEqual(result[0], 202) + + def test_utf8_localvariable(self): + client = raygunprovider.RaygunSender(self.apiKey) + + theVariable = 'ᵫ' + + try: + raise Exception("Raygun4py2: utf8 local variable") + except Exception as e: + result = client.send_exception(httpRequest={}) + + self.assertEqual(result[0], 202) + + def test_bytestring_localvariable(self): + client = raygunprovider.RaygunSender(self.apiKey) + + byteString = b'\x8d\x80\x92uK!M\xed' + + try: + raise Exception("Raygun4py2: bytestring local variable") + except Exception as e: + result = client.send_exception(httpRequest={}) + + self.assertEqual(result[0], 202) + class CustomException(Exception): def __init__(self, value): self.value = value diff --git a/python2/tests/test_raygunmsgs.py b/python2/tests/test_raygunmsgs.py index 19aa778..451e587 100644 --- a/python2/tests/test_raygunmsgs.py +++ b/python2/tests/test_raygunmsgs.py @@ -1,6 +1,8 @@ import sys import unittest2 as unittest import socket +import inspect + from raygun4py import raygunmsgs class TestRaygunMessageBuilder(unittest.TestCase): @@ -97,6 +99,25 @@ def test_local_variable(self): self.assertTrue('i_must_be_included' in localVars) + def test_methodname_none(self): + originalGetinnerframes = inspect.getinnerframes + inspect.getinnerframes = getinnerframes_mock_methodname_none + + errorMessage = raygunmsgs.RaygunErrorMessage(int, 1, None, { "transmitLocalVariables": False }) + + self.assertEqual(errorMessage.__dict__['stackTrace'][0]['methodName'], None) + + inspect.getinnerframes = originalGetinnerframes + +def getinnerframes_mock_methodname_none(exception): + return [( + 'localVar', + 'fileName', + 'lineNumber', + 'className', + None + )] + def main(): unittest.main() diff --git a/python3/raygun4py/raygunmsgs.py b/python3/raygun4py/raygunmsgs.py index 87d52a6..964ca12 100644 --- a/python3/raygun4py/raygunmsgs.py +++ b/python3/raygun4py/raygunmsgs.py @@ -127,7 +127,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, options): 'lineNumber': frame[2], 'className': frame[3], 'fileName': frame[1], - 'methodName': frame[4][0], + 'methodName': frame[4][0] if frame[4] is not None else None, 'localVariables': self._get_locals(frame[0]) if 'transmitLocalVariables' in options and options['transmitLocalVariables'] is True else None }) finally: diff --git a/python3/tests/test_functional.py b/python3/tests/test_functional.py index 800b439..19886e8 100644 --- a/python3/tests/test_functional.py +++ b/python3/tests/test_functional.py @@ -189,6 +189,16 @@ def test_http_request(self): self.assertEqual(result[0], 202) + def test_utf8_message(self): + client = raygunprovider.RaygunSender(self.apiKey) + + try: + raise Exception("ΔΔΔΔ") + except Exception as e: + result = client.send_exception(httpRequest={}) + + self.assertEqual(result[0], 202) + def before_send_mutate_payload(message): message['newKey'] = 'newValue' diff --git a/python3/tests/test_raygunmsgs.py b/python3/tests/test_raygunmsgs.py index 6b78926..69ca4d8 100644 --- a/python3/tests/test_raygunmsgs.py +++ b/python3/tests/test_raygunmsgs.py @@ -1,4 +1,5 @@ import sys, unittest, socket +import inspect from raygun4py import raygunmsgs class TestRaygunMessageBuilder(unittest.TestCase): @@ -129,6 +130,25 @@ def test_chained_exception_cause_is_child(self): def test_chained_exception_childs_cause_is_grandchild(self): self.assertIsInstance(self.theException.__cause__.__context__, TestRaygunErrorMessage.GrandchildError) + def test_methodname_none(self): + originalGetinnerframes = inspect.getinnerframes + inspect.getinnerframes = getinnerframes_mock_methodname_none + + errorMessage = raygunmsgs.RaygunErrorMessage(int, 1, None, { "transmitLocalVariables": False }) + + self.assertEqual(errorMessage.__dict__['stackTrace'][0]['methodName'], None) + + inspect.getinnerframes = originalGetinnerframes + +def getinnerframes_mock_methodname_none(exception): + return [( + 'localVar', + 'fileName', + 'lineNumber', + 'className', + None + )] + def main(): unittest.main() diff --git a/setup.py b/setup.py index 481765d..da5e12a 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='raygun4py', - version='3.0.1', + version='3.0.2', packages=packages, package_dir= { "raygun4py": base_dir + "/raygun4py" @@ -23,7 +23,7 @@ description='Official Raygun provider for Python 2.6/2.7 and Python 3+', long_description=open('README.rst').read(), install_requires=[ - 'jsonpickle == 0.7.0', + 'jsonpickle == 0.9.2', 'blinker == 1.3.0' ], entry_points={