Proper repr of all variables

This commit is contained in:
David Cramer 2013-03-31 02:12:50 -07:00
parent 539922ed2a
commit 7a917c1058
17 changed files with 166 additions and 219 deletions

View File

@ -23,7 +23,7 @@ import raven
from raven.conf import defaults
from raven.context import Context
from raven.utils import json, get_versions, get_auth_header
from raven.utils.encoding import to_string
from raven.utils.encoding import to_string, to_unicode
from raven.utils.serializer import transform
from raven.utils.stacks import get_stack_info, iter_stack_frames, get_culprit
from raven.utils.urlparse import urlparse
@ -288,8 +288,7 @@ class Client(object):
data.update({
'sentry.interfaces.Stacktrace': {
'frames': get_stack_info(frames,
list_max_length=self.list_max_length,
string_max_length=self.string_max_length)
transformer=self.transform)
},
})
@ -367,8 +366,10 @@ class Client(object):
if site:
data['tags'].setdefault('site', site)
# Make sure all data is coerced
data = self.transform(data)
# Make sure custom data is coerced
data['extra'] = self.transform(data['extra'])
for key, value in six.iteritems(data['tags']):
data['tags'][key] = to_unicode(value)
# It's important date is added **after** we serialize
data.update({
@ -457,7 +458,7 @@ class Client(object):
self.send(**data)
return (data['event_id'], data['checksum'])
return (data.get('event_id'), data.get('checksum'))
def _get_log_message(self, data):
# decode message so we can show the actual event

View File

@ -48,7 +48,8 @@ class HttpRequestSerializer(Serializer):
types = (HttpRequest,)
def serialize(self, value, **kwargs):
return u'<%s at 0x%s>' % (type(value).__name__, id(value))
return six.text_type(
'<%s at 0x%s>' % (type(value).__name__, id(value)))
register(HttpRequestSerializer)
@ -62,7 +63,8 @@ if getattr(settings, 'DATABASES', None):
def serialize(self, value, **kwargs):
qs_name = type(value).__name__
if value.model:
return u'<%s: model=%s>' % (qs_name, value.model.__name__)
return u'<%s: (Unbound)>' % (qs_name,)
return six.text_type(
'<%s: model=%s>' % (qs_name, value.model.__name__))
return six.text_type('<%s: (Unbound)>' % (qs_name,))
register(QuerySetSerializer)

View File

@ -9,8 +9,7 @@ raven.events
import logging
import sys
from raven.utils import varmap
from raven.utils.encoding import shorten, to_unicode
from raven.utils.encoding import to_unicode
from raven.utils.stacks import get_stack_info, iter_traceback_frames
__all__ = ('BaseEvent', 'Exception', 'Message', 'Query')
@ -28,6 +27,9 @@ class BaseEvent(object):
return {
}
def transform(self, value):
return self.client.transform(value)
class Exception(BaseEvent):
"""
@ -65,11 +67,8 @@ class Exception(BaseEvent):
try:
exc_type, exc_value, exc_traceback = exc_info
frames = varmap(lambda k, v: shorten(v,
string_length=self.client.string_max_length, list_length=self.client.list_max_length),
get_stack_info(iter_traceback_frames(exc_traceback),
list_max_length=self.client.list_max_length,
string_max_length=self.client.string_max_length))
frames = get_stack_info(iter_traceback_frames(exc_traceback),
transformer=self.transform)
exc_module = getattr(exc_type, '__module__', None)
if exc_module:
@ -88,7 +87,7 @@ class Exception(BaseEvent):
'sentry.interfaces.Exception': {
'value': to_unicode(exc_value),
'type': str(exc_type),
'module': exc_module,
'module': to_unicode(exc_module),
},
'sentry.interfaces.Stacktrace': {
'frames': frames
@ -103,24 +102,20 @@ class Message(BaseEvent):
- message: 'My message from %s about %s'
- params: ('foo', 'bar')
"""
def to_string(self, data):
msg = data['sentry.interfaces.Message']
if msg.get('params'):
return msg['message'] % msg['params']
return msg['message']
def get_hash(self, data):
msg = data['sentry.interfaces.Message']
return [msg['message']]
def capture(self, message, params=(), **kwargs):
def capture(self, message, params=(), formatted=None, **kwargs):
message = to_unicode(message)
data = {
'sentry.interfaces.Message': {
'message': message,
'params': params,
}
'params': self.transform(params),
},
}
if 'message' not in data:
data['message'] = formatted or message
return data
@ -142,7 +137,7 @@ class Query(BaseEvent):
def capture(self, query, engine, **kwargs):
return {
'sentry.interfaces.Query': {
'query': query,
'engine': engine,
'query': to_unicode(query),
'engine': str(engine),
}
}

View File

@ -60,11 +60,14 @@ class SentryHandler(logbook.Handler):
data = {
'level': logbook.get_level_name(record.level).lower(),
'logger': record.channel,
'message': self.format(record),
}
event_type = 'raven.events.Message'
handler_kwargs = {'message': record.msg, 'params': record.args}
handler_kwargs = {
'message': record.msg,
'params': record.args,
'formatted': self.format(record),
}
# If there's no exception being processed, exc_info may be a 3-tuple of None
# http://docs.python.org/library/sys.html#sys.exc_info

View File

@ -65,47 +65,40 @@ class SentryHandler(logging.Handler, object):
except Exception:
pass
def _get_targetted_stack(self, stack):
# we might need to traverse this multiple times, so coerce it to a list
stack = list(stack)
frames = []
started = False
last_mod = ''
for item in stack:
if isinstance(item, (list, tuple)):
frame, lineno = item
else:
frame, lineno = item, item.f_lineno
if not started:
f_globals = getattr(frame, 'f_globals', {})
module_name = f_globals.get('__name__', '')
if ((last_mod and last_mod.startswith('logging'))
and not module_name.startswith('logging')):
started = True
else:
last_mod = module_name
continue
frames.append((frame, lineno))
# We failed to find a starting point
if not frames:
return stack
return frames
def _emit(self, record, **kwargs):
data = {}
for k, v in six.iteritems(record.__dict__):
if '.' not in k and k not in ('culprit',):
continue
data[k] = v
stack = getattr(record, 'stack', None)
if stack is True:
stack = iter_stack_frames()
if stack:
# we might need to traverse this multiple times, so coerce it to a list
stack = list(stack)
frames = []
started = False
last_mod = ''
for item in stack:
if isinstance(item, (list, tuple)):
frame, lineno = item
else:
frame, lineno = item, item.f_lineno
if not started:
f_globals = getattr(frame, 'f_globals', {})
module_name = f_globals.get('__name__', '')
if (last_mod and last_mod.startswith('logging')) \
and not module_name.startswith('logging'):
started = True
else:
last_mod = module_name
continue
frames.append((frame, lineno))
# We must've not found a starting point
if not frames:
frames = stack
stack = frames
extra = getattr(record, 'data', None)
if not isinstance(extra, dict):
if extra:
@ -113,17 +106,30 @@ class SentryHandler(logging.Handler, object):
else:
extra = {}
# Add in all of the data from the record that we aren't already capturing
for k in record.__dict__.keys():
for k, v in six.iteritems(vars(record)):
if k in RESERVED:
continue
if k.startswith('_'):
continue
extra[k] = record.__dict__[k]
if '.' not in k and k not in ('culprit',):
extra[k] = v
else:
data[k] = v
stack = getattr(record, 'stack', None)
if stack is True:
stack = iter_stack_frames()
if stack:
stack = self._get_targetted_stack(stack)
date = datetime.datetime.utcfromtimestamp(record.created)
event_type = 'raven.events.Message'
handler_kwargs = {'message': record.msg, 'params': record.args}
handler_kwargs = {
'message': record.msg,
'params': record.args,
'formatted': record.message,
}
# If there's no exception being processed, exc_info may be a 3-tuple of None
# http://docs.python.org/library/sys.html#sys.exc_info
@ -133,8 +139,6 @@ class SentryHandler(logging.Handler, object):
# message interface attached
handler = self.client.get_handler(event_type)
data.update(handler.capture(**handler_kwargs))
# ensure message is propagated, otherwise the exception will overwrite it
data['message'] = record.message
event_type = 'raven.events.Exception'
handler_kwargs = {'exc_info': record.exc_info}
@ -153,5 +157,5 @@ class SentryHandler(logging.Handler, object):
kwargs.update(handler_kwargs)
return self.client.capture(event_type, stack=stack, data=data, extra=extra,
date=date, **kwargs)
return self.client.capture(event_type, stack=stack, data=data,
extra=extra, date=date, **kwargs)

View File

@ -78,17 +78,3 @@ def to_string(value):
return str(value.decode('utf-8').encode('utf-8'))
except:
return to_unicode(value).encode('utf-8')
def shorten(var, list_length=50, string_length=200):
from raven.utils.serializer import transform
var = transform(var)
if isinstance(var, basestring) and len(var) > string_length:
var = var[:string_length] + '...'
elif isinstance(var, (list, tuple, set, frozenset)) and len(var) > list_length:
# TODO: we should write a real API for storing some metadata with vars when
# we get around to doing ref storage
# TODO: when we finish the above, we should also implement this for dicts
var = list(var)[:list_length] + ['...', '(%d more elements)' % (len(var) - list_length,)]
return var

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
"""
raven.utils.serializer.base
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -7,9 +9,9 @@ raven.utils.serializer.base
"""
import itertools
from raven.utils.encoding import to_string, to_unicode
from raven.utils.serializer.manager import register
from raven.utils import six
from raven.utils.encoding import to_unicode
from raven.utils.serializer.manager import register
from uuid import UUID
__all__ = ('Serializer',)
@ -51,8 +53,10 @@ class Serializer(object):
try:
value = repr(value)
except Exception as e:
import traceback
traceback.print_exc()
self.manager.logger.exception(e)
return six.text_type(type(value))
return type(value)
return self.manager.transform(value, max_depth=max_depth, _depth=_depth, **kwargs)
@ -61,7 +65,11 @@ class IterableSerializer(Serializer):
def serialize(self, value, **kwargs):
list_max_length = kwargs.get('list_max_length') or float('inf')
return tuple(self.recurse(o, **kwargs) for n, o in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(value)))
return tuple(
self.recurse(o, **kwargs)
for n, o
in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(value))
)
class UUIDSerializer(Serializer):
@ -74,11 +82,17 @@ class UUIDSerializer(Serializer):
class DictSerializer(Serializer):
types = (dict,)
def make_key(self, key):
if not isinstance(key, basestring):
return to_unicode(key)
return key
def serialize(self, value, **kwargs):
list_max_length = kwargs.get('list_max_length') or float('inf')
return dict(
(to_string(k), self.recurse(v, **kwargs))
for n, (k, v) in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(value.iteritems()))
(self.make_key(self.recurse(k, **kwargs)), self.recurse(v, **kwargs))
for n, (k, v)
in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(six.iteritems(value)))
)
@ -86,8 +100,12 @@ class UnicodeSerializer(Serializer):
types = (six.text_type,)
def serialize(self, value, **kwargs):
# try to return a reasonable string that can be decoded
# correctly by the server so it doesnt show up as \uXXX for each
# unicode character
# e.g. we want the output to be like: "u'רונית מגן'"
string_max_length = kwargs.get('string_max_length', None)
return to_unicode(value)[:string_max_length]
return six.text_type("u'{}'").format(value[:string_max_length])
class StringSerializer(Serializer):
@ -95,7 +113,8 @@ class StringSerializer(Serializer):
def serialize(self, value, **kwargs):
string_max_length = kwargs.get('string_max_length', None)
return to_string(value)[:string_max_length]
return six.binary_type("'{}'").format(
value.decode('utf-8').encode('utf-8')[:string_max_length])
class TypeSerializer(Serializer):

View File

@ -71,7 +71,7 @@ class Serializer(object):
# if all else fails, lets use the repr of the object
try:
return self.transform(repr(value), **kwargs)
return six.text_type(repr(value))
except Exception as e:
logger.exception(e)
# It's common case that a model's __unicode__ definition may try to query the database

View File

@ -173,7 +173,7 @@ def iter_stack_frames(frames=None):
yield frame, lineno
def get_stack_info(frames, list_max_length=None, string_max_length=None):
def get_stack_info(frames, transformer=transform):
"""
Given a list of frames, returns a list of stack information
dictionary objects that are JSON-ready.
@ -245,8 +245,7 @@ def get_stack_info(frames, list_max_length=None, string_max_length=None):
'module': module_name or None,
'function': function or '<unknown>',
'lineno': lineno + 1,
'vars': transform(f_locals, list_max_length=list_max_length,
string_max_length=string_max_length),
'vars': transformer(f_locals),
}
if context_line is not None:
frame_result.update({

View File

@ -7,7 +7,7 @@ import time
from socket import socket, AF_INET, SOCK_DGRAM
from raven.utils.compat import TestCase
from raven.base import Client, ClientState
from raven.transport import AsyncTransport, HTTPTransport
from raven.transport import AsyncTransport
from raven.utils.stacks import iter_stack_frames
@ -69,9 +69,10 @@ class ClientTest(TestCase):
base.Raven = None
client = Client()
client2 = Client() # NOQA
client2 = Client()
assert base.Raven is client
assert client is not client2
@mock.patch('raven.transport.base.HTTPTransport.send')
@mock.patch('raven.base.ClientState.should_try')
@ -146,9 +147,11 @@ class ClientTest(TestCase):
headers={
'User-Agent': 'raven-python/%s' % (raven.VERSION,),
'Content-Type': 'application/octet-stream',
'X-Sentry-Auth': 'Sentry sentry_timestamp=1328055286.51, '
'sentry_client=raven-python/%s, sentry_version=2.0, sentry_key=public, '
'sentry_secret=secret' % (raven.VERSION,)
'X-Sentry-Auth': (
'Sentry sentry_timestamp=1328055286.51, '
'sentry_client=raven-python/%s, sentry_version=2.0, '
'sentry_key=public, '
'sentry_secret=secret' % (raven.VERSION,))
},
)
@ -361,7 +364,7 @@ class ClientTest(TestCase):
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['extra'], {'logger': 'test', 'foo': 'bar'})
self.assertEquals(event['extra'], {"'logger'": "'test'", "'foo'": "'bar'"})
class ClientUDPTest(TestCase):

View File

@ -669,7 +669,8 @@ class ReportViewTest(TestCase):
@mock.patch('raven.contrib.django.views.is_valid_origin', mock.Mock(return_value=True))
def test_sends_x_sentry_auth_header(self):
resp = self.client.post(self.path, HTTP_ORIGIN='http://example.com',
HTTP_X_SENTRY_AUTH='Sentry foo/bar', data='{}', content_type='application/octet-stream')
HTTP_X_SENTRY_AUTH='Sentry foo/bar', data='{}',
content_type='application/octet-stream')
self.assertEquals(resp.status_code, 200)
event = client.events.pop(0)
self.assertEquals(event, {'auth_header': 'Sentry foo/bar'})
@ -681,7 +682,7 @@ class PromiseSerializerTestCase(TestCase):
obj = lazy(lambda: 'bar', str)()
res = transform(obj)
self.assertEquals(res, 'bar')
self.assertEquals(res, "'bar'")
def test_handles_gettext_lazy(self):
from django.utils.functional import lazy
@ -693,24 +694,26 @@ class PromiseSerializerTestCase(TestCase):
result = transform(fake_gettext_lazy("something"))
self.assertTrue(isinstance(result, basestring))
self.assertEquals(result, u'Igpay Atinlay')
self.assertEquals(result, "u'Igpay Atinlay'")
class QuerySetSerializerTestCase(TestCase):
def test_model_instance(self):
class ModelInstanceSerializerTestCase(TestCase):
def test_basic(self):
instance = TestModel()
result = transform(instance)
self.assertTrue(isinstance(result, basestring))
self.assertEquals(result, u'<TestModel: TestModel object>')
self.assertEquals(result, '<TestModel: TestModel object>')
class QuerySetSerializerTestCase(TestCase):
def test_basic(self):
from django.db.models.query import QuerySet
obj = QuerySet(model=TestModel)
result = transform(obj)
self.assertTrue(isinstance(result, basestring))
self.assertEquals(result, u'<QuerySet: model=TestModel>')
self.assertEquals(result, '<QuerySet: model=TestModel>')
class SentryExceptionHandlerTest(TestCase):

View File

@ -121,8 +121,8 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase):
user_data = kwargs['sentry.interfaces.User']
self.assertEqual(user_data['is_authenticated'], False)
self.assertTrue('extra_data' in kwargs['extra'])
self.assertEqual(kwargs['extra']['extra_data'], 'extra custom non-dict data')
assert "'extra_data'" in kwargs['extra']
assert kwargs['extra']["'extra_data'"] == "'extra custom non-dict data'"
@patch('raven.contrib.tornado.AsyncSentryClient.send')
def test_error_with_custom_dict_data_handler(self, send):
@ -146,8 +146,8 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase):
user_data = kwargs['sentry.interfaces.User']
self.assertEqual(user_data['is_authenticated'], False)
self.assertTrue('extra_data' in kwargs['extra'])
self.assertEqual(kwargs['extra']['extra_data'], 'extra custom dict data')
assert "'extra_data'" in kwargs['extra']
assert kwargs['extra']["'extra_data'"] == "'extra custom dict data'"
@patch(
'raven.contrib.tornado.AsyncSentryClient.send',

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
from mock import Mock
from raven.utils.compat import TestCase
from raven.events import Message
class MessageTest(TestCase):
def test_to_string(self):
unformatted_message = 'My message from %s about %s'
client = Mock()
message = Message(client)
message.logger = Mock()
data = {
'sentry.interfaces.Message': {
'message': unformatted_message,
}
}
self.assertEqual(message.to_string(data), unformatted_message)
data['sentry.interfaces.Message']['params'] = (1, 2)
self.assertEqual(message.to_string(data),
unformatted_message % (1, 2))

View File

@ -59,7 +59,7 @@ class LogbookHandlerTest(TestCase):
))
self.assertEquals(len(client.events), 1)
event = client.events.pop(0)
self.assertEquals(event['extra']['url'], 'http://example.com')
self.assertEquals(event['extra']["'url'"], "'http://example.com'")
self.assertFalse('sentry.interfaces.Stacktrace' in event)
self.assertFalse('sentry.interfaces.Exception' in event)
self.assertTrue('sentry.interfaces.Message' in event)
@ -96,7 +96,7 @@ class LogbookHandlerTest(TestCase):
self.assertTrue('sentry.interfaces.Message' in event)
msg = event['sentry.interfaces.Message']
self.assertEquals(msg['message'], 'This is a test of {0}')
self.assertEquals(msg['params'], ('args',))
self.assertEquals(msg['params'], ("'args'",))
def test_client_arg(self):
client = TempStoreClient(include_paths=['tests'])

View File

@ -55,7 +55,7 @@ class LoggingIntegrationTest(TestCase):
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['extra']['url'], 'http://example.com')
self.assertEquals(event['extra']["'url'"], "'http://example.com'")
def test_logger_exc_info(self):
try:
@ -90,7 +90,7 @@ class LoggingIntegrationTest(TestCase):
self.assertEquals(event['message'], 'This is a test of args')
msg = event['sentry.interfaces.Message']
self.assertEquals(msg['message'], 'This is a test of %s')
self.assertEquals(msg['params'], ('args',))
self.assertEquals(msg['params'], ("'args'",))
def test_record_stack(self):
record = self.make_record('This is a test of stacks', extra={'stack': True})
@ -148,7 +148,7 @@ class LoggingIntegrationTest(TestCase):
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['extra']['data'], 'foo')
self.assertEquals(event['extra']["'data'"], "'foo'")
def test_tags(self):
record = self.make_record('Message', extra={'tags': {'foo': 'bar'}})
@ -170,7 +170,6 @@ class LoggingIntegrationTest(TestCase):
assert event['tags'] == {'foo': 'bar'}
class LoggingHandlerTest(TestCase):
def test_client_arg(self):
client = TempStoreClient(include_paths=['tests'])

View File

@ -2,9 +2,8 @@
import uuid
from raven.utils import six
from raven.utils.compat import TestCase
from raven.utils.encoding import shorten
from raven.utils.serializer import transform
@ -13,22 +12,20 @@ class TransformTest(TestCase):
x = 'רונית מגן'
result = transform(x)
self.assertEquals(type(result), str)
self.assertEquals(result, 'רונית מגן')
assert result == "'רונית מגן'"
def test_correct_unicode(self):
x = 'רונית מגן'.decode('utf-8')
# 'רונית מגן'
x = six.text_type('\u05e8\u05d5\u05e0\u05d9\u05ea \u05de\u05d2\u05df')
result = transform(x)
self.assertEquals(type(result), unicode)
self.assertEquals(result, x)
assert result == six.text_type("u'\u05e8\u05d5\u05e0\u05d9\u05ea \u05de\u05d2\u05df'")
def test_bad_string(self):
x = 'The following character causes problems: \xd4'
result = transform(x)
self.assertEquals(type(result), str)
self.assertEquals(result, '<type \'str\'>')
assert result == "<type 'str'>"
def test_float(self):
result = transform(13.0)
@ -55,14 +52,14 @@ class TransformTest(TestCase):
# self.assertEquals(result, '(Error decoding value)')
def test_dict_keys(self):
x = {u'foo': 'bar'}
x = {'foo': 'bar'}
result = transform(x)
self.assertEquals(type(result), dict)
keys = result.keys()
self.assertEquals(len(keys), 1)
self.assertTrue(type(keys[0]), str)
self.assertEquals(keys[0], 'foo')
self.assertEquals(keys[0], "'foo'")
def test_dict_keys_utf8_as_str(self):
x = {'רונית מגן': 'bar'}
@ -71,23 +68,23 @@ class TransformTest(TestCase):
self.assertEquals(type(result), dict)
keys = result.keys()
self.assertEquals(len(keys), 1)
self.assertTrue(type(keys[0]), str)
self.assertEquals(keys[0], 'רונית מגן')
assert keys[0] == "'רונית מגן'"
def test_dict_keys_utf8_as_unicode(self):
x = {u'רונית מגן': 'bar'}
x = {
six.text_type('\u05e8\u05d5\u05e0\u05d9\u05ea \u05de\u05d2\u05df'): 'bar'
}
result = transform(x)
assert type(result) is dict
keys = result.keys()
self.assertEquals(len(keys), 1)
self.assertTrue(type(keys[0]), str)
self.assertEquals(keys[0], 'רונית מגן')
assert len(keys) == 1
assert keys[0] == six.text_type("u'\u05e8\u05d5\u05e0\u05d9\u05ea \u05de\u05d2\u05df'")
def test_uuid(self):
x = uuid.uuid4()
result = transform(x)
self.assertEquals(result, repr(x))
self.assertTrue(type(result), str)
assert result == repr(x)
def test_recursive(self):
x = []
@ -104,7 +101,7 @@ class TransformTest(TestCase):
x = Foo()
result = transform(x)
self.assertEquals(result, 'example')
self.assertEquals(result, "'example'")
def test_broken_repr(self):
class Foo(object):
@ -114,12 +111,12 @@ class TransformTest(TestCase):
x = Foo()
result = transform(x)
self.assertEquals(result, u"<class 'tests.utils.encoding.tests.Foo'>")
self.assertEquals(result, "<class 'tests.utils.encoding.tests.Foo'>")
def test_recursion_max_depth(self):
x = [[[[1]]]]
result = transform(x, max_depth=3)
self.assertEquals(result, ((('[1]',),),))
self.assertEquals(result, ((("'[1]'",),),))
def test_list_max_length(self):
x = range(10)
@ -135,41 +132,4 @@ class TransformTest(TestCase):
def test_string_max_length(self):
x = '1234'
result = transform(x, string_max_length=3)
self.assertEquals(result, '123')
class ShortenTest(TestCase):
def test_shorten_string(self):
result = shorten('hello world!', string_length=5)
self.assertEquals(len(result), 8)
self.assertEquals(result, 'hello...')
def test_shorten_lists(self):
result = shorten(range(500), list_length=50)
self.assertEquals(len(result), 52)
self.assertEquals(result[-2], '...')
self.assertEquals(result[-1], '(450 more elements)')
def test_shorten_sets(self):
result = shorten(set(range(500)), list_length=50)
self.assertEquals(len(result), 52)
self.assertEquals(result[-2], '...')
self.assertEquals(result[-1], '(450 more elements)')
def test_shorten_frozenset(self):
result = shorten(frozenset(range(500)), list_length=50)
self.assertEquals(len(result), 52)
self.assertEquals(result[-2], '...')
self.assertEquals(result[-1], '(450 more elements)')
def test_shorten_tuple(self):
result = shorten(tuple(range(500)), list_length=50)
self.assertEquals(len(result), 52)
self.assertEquals(result[-2], '...')
self.assertEquals(result[-1], '(450 more elements)')
# def test_shorten_generator(self):
# result = shorten(xrange(500))
# self.assertEquals(len(result), 52)
# self.assertEquals(result[-2], '...')
# self.assertEquals(result[-1], '(450 more elements)')
self.assertEquals(result, "'123'")

View File

@ -33,7 +33,7 @@ class GetCulpritTest(TestCase):
def test_no_module_or_function(self):
culprit = get_culprit([{}])
assert culprit == None
assert culprit is None
def test_all_params(self):
culprit = get_culprit([{
@ -59,10 +59,8 @@ class GetStackInfoTest(TestCase):
results = get_stack_info(frames)
self.assertEquals(len(results), 1)
result = results[0]
self.assertTrue('vars' in result)
vars = result['vars']
self.assertTrue(isinstance(vars, dict))
self.assertTrue('foo' in vars)
self.assertEquals(vars['foo'], 'bar')
self.assertTrue('biz' in vars)
self.assertEquals(vars['biz'], 'baz')
assert 'vars' in result
assert result['vars'] == {
"'foo'": "'bar'",
"'biz'": "'baz'",
}