Strip six module and rename to raven._compat

We're seeing some odd issues with our six module interacting
badly with the six module from other libraries for some users.
The suspicion is that our six module in some cases gets unvendored.
We thus rename it to `raven._compat` (which is also what some
other Python modules do).  We still use six normally for the
tests.
This commit is contained in:
Armin Ronacher 2016-01-12 21:40:51 +01:00
parent f2eedb742c
commit 97e93ea7ba
33 changed files with 350 additions and 535 deletions

View File

@ -20,13 +20,17 @@ def main():
from flake8.main import DEFAULT_CONFIG from flake8.main import DEFAULT_CONFIG
from flake8.engine import get_style_guide from flake8.engine import get_style_guide
from flake8.hooks import run from flake8.hooks import run
from raven.utils import six
gitcmd = "git diff-index --cached --name-only HEAD" gitcmd = "git diff-index --cached --name-only HEAD"
_, files_modified, _ = run(gitcmd) _, files_modified, _ = run(gitcmd)
files_modified = [six.text_type(x) for x in files_modified] try:
text_type = unicode
except NameError:
text_type = str
files_modified = [text_type(x) for x in files_modified]
# remove non-py files and files which no longer exist # remove non-py files and files which no longer exist
files_modified = filter( files_modified = filter(

177
raven/_compat.py Normal file
View File

@ -0,0 +1,177 @@
"""Utilities for writing code that runs on Python 2 and 3"""
# flake8: noqa
# Copyright (c) 2010-2013 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import absolute_import
import operator
import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.3.0"
PY2 = sys.version_info[0] == 2
if not PY2:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
if sys.platform.startswith("java"):
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
def _import_module(name):
"""Import module, returning the module after the last dot."""
__import__(name)
return sys.modules[name]
if not PY2:
_iterkeys = "keys"
_itervalues = "values"
_iteritems = "items"
_iterlists = "lists"
else:
_iterkeys = "iterkeys"
_itervalues = "itervalues"
_iteritems = "iteritems"
_iterlists = "iterlists"
try:
advance_iterator = next
except NameError:
def advance_iterator(it):
return it.next()
next = advance_iterator
try:
callable = callable
except NameError:
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
def iterkeys(d, **kw):
"""Return an iterator over the keys of a dictionary."""
return iter(getattr(d, _iterkeys)(**kw))
def itervalues(d, **kw):
"""Return an iterator over the values of a dictionary."""
return iter(getattr(d, _itervalues)(**kw))
def iteritems(d, **kw):
"""Return an iterator over the (key, value) pairs of a dictionary."""
return iter(getattr(d, _iteritems)(**kw))
def iterlists(d, **kw):
"""Return an iterator over the (key, [values]) pairs of a dictionary."""
return iter(getattr(d, _iterlists)(**kw))
if not PY2:
def b(s):
return s.encode("latin-1")
def u(s):
return s
if sys.version_info[1] <= 1:
def int2byte(i):
return bytes((i,))
else:
# This is about 2x faster than the implementation above on 3.2+
int2byte = operator.methodcaller("to_bytes", 1, "big")
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
else:
def b(s): # NOQA
return s
def u(s): # NOQA
return unicode(s, "unicode_escape")
int2byte = chr
import StringIO
StringIO = BytesIO = StringIO.StringIO
if not PY2:
import builtins
exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None):
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
del builtins
else:
def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
if _globs_ is None:
frame = sys._getframe(1)
_globs_ = frame.f_globals
if _locs_ is None:
_locs_ = frame.f_locals
del frame
elif _locs_ is None:
_locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""")
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
def with_metaclass(meta, base=object):
"""Create a base class with a metaclass."""
return meta("NewBase", (base,), {})

View File

@ -30,7 +30,8 @@ from raven.conf import defaults
from raven.conf.remote import RemoteConfig from raven.conf.remote import RemoteConfig
from raven.context import Context from raven.context import Context
from raven.exceptions import APIError, RateLimited from raven.exceptions import APIError, RateLimited
from raven.utils import six, json, get_versions, get_auth_header, merge_dicts from raven.utils import json, get_versions, get_auth_header, merge_dicts
from raven._compat import text_type, iteritems
from raven.utils.encoding import to_unicode from raven.utils.encoding import to_unicode
from raven.utils.serializer import transform from raven.utils.serializer import transform
from raven.utils.stacks import get_stack_info, iter_stack_frames, get_culprit from raven.utils.stacks import get_stack_info, iter_stack_frames, get_culprit
@ -145,7 +146,7 @@ class Client(object):
self.include_paths = set(o.get('include_paths') or []) self.include_paths = set(o.get('include_paths') or [])
self.exclude_paths = set(o.get('exclude_paths') or []) self.exclude_paths = set(o.get('exclude_paths') or [])
self.name = six.text_type(o.get('name') or o.get('machine') or defaults.NAME) self.name = text_type(o.get('name') or o.get('machine') or defaults.NAME)
self.auto_log_stacks = bool( self.auto_log_stacks = bool(
o.get('auto_log_stacks') or defaults.AUTO_LOG_STACKS) o.get('auto_log_stacks') or defaults.AUTO_LOG_STACKS)
self.capture_locals = bool( self.capture_locals = bool(
@ -252,7 +253,8 @@ class Client(object):
>>> result = client.capture(**kwargs) >>> result = client.capture(**kwargs)
>>> ident = client.get_ident(result) >>> ident = client.get_ident(result)
""" """
warnings.warn('Client.get_ident is deprecated. The event ID is now returned as the result of capture.', warnings.warn('Client.get_ident is deprecated. The event ID is now '
'returned as the result of capture.',
DeprecationWarning) DeprecationWarning)
return result return result
@ -306,7 +308,7 @@ class Client(object):
if data.get('culprit'): if data.get('culprit'):
culprit = data['culprit'] culprit = data['culprit']
for k, v in six.iteritems(result): for k, v in iteritems(result):
if k not in data: if k not in data:
data[k] = v data[k] = v
@ -394,11 +396,11 @@ class Client(object):
data['message'] = kwargs.get('message', handler.to_string(data)) data['message'] = kwargs.get('message', handler.to_string(data))
# tags should only be key=>u'value' # tags should only be key=>u'value'
for key, value in six.iteritems(data['tags']): for key, value in iteritems(data['tags']):
data['tags'][key] = to_unicode(value) data['tags'][key] = to_unicode(value)
# extra data can be any arbitrary value # extra data can be any arbitrary value
for k, v in six.iteritems(data['extra']): for k, v in iteritems(data['extra']):
data['extra'][k] = self.transform(v) data['extra'][k] = self.transform(v)
# It's important date is added **after** we serialize # It's important date is added **after** we serialize
@ -556,7 +558,8 @@ class Client(object):
if isinstance(exc, RateLimited): if isinstance(exc, RateLimited):
retry_after = exc.retry_after retry_after = exc.retry_after
self.error_logger.error( self.error_logger.error(
'Sentry responded with an API error: %s(%s)', type(exc).__name__, exc.message) 'Sentry responded with an API error: %s(%s)',
type(exc).__name__, exc.message)
else: else:
self.error_logger.error( self.error_logger.error(
'Sentry responded with an error: %s (url: %s)\n%s', 'Sentry responded with an error: %s (url: %s)\n%s',
@ -577,10 +580,10 @@ class Client(object):
if 'exception' in data and 'stacktrace' in data['exception']['values'][0]: if 'exception' in data and 'stacktrace' in data['exception']['values'][0]:
# try to reconstruct a reasonable version of the exception # try to reconstruct a reasonable version of the exception
for frame in data['exception']['values'][0]['stacktrace']['frames']: for frame in data['exception']['values'][0]['stacktrace']['frames']:
output.append(' File "%(filename)s", line %(lineno)s, in %(function)s' % { output.append(' File "%(fn)s", line %(lineno)s, in %(func)s' % {
'filename': frame['filename'], 'fn': frame['filename'],
'lineno': frame['lineno'], 'lineno': frame['lineno'],
'function': frame['function'], 'func': frame['function'],
}) })
self.uncaught_logger.error(output) self.uncaught_logger.error(output)

View File

@ -2,9 +2,9 @@ from __future__ import absolute_import
import warnings import warnings
from raven._compat import PY2, text_type
from raven.exceptions import InvalidDsn from raven.exceptions import InvalidDsn
from raven.transport.threaded import ThreadedHTTPTransport from raven.transport.threaded import ThreadedHTTPTransport
from raven.utils import six
from raven.utils.encoding import to_string from raven.utils.encoding import to_string
from raven.utils.urlparse import parse_qsl, urlparse from raven.utils.urlparse import parse_qsl, urlparse
@ -32,7 +32,7 @@ class RemoteConfig(object):
self._transport_cls = transport or DEFAULT_TRANSPORT self._transport_cls = transport or DEFAULT_TRANSPORT
def __unicode__(self): def __unicode__(self):
return six.text_type(self.base_url) return text_type(self.base_url)
def is_active(self): def is_active(self):
return all([self.base_url, self.project, self.public_key, self.secret_key]) return all([self.base_url, self.project, self.public_key, self.secret_key])
@ -58,7 +58,7 @@ class RemoteConfig(object):
def from_string(cls, value, transport=None, transport_registry=None): def from_string(cls, value, transport=None, transport_registry=None):
# in Python 2.x sending the DSN as a unicode value will eventually # in Python 2.x sending the DSN as a unicode value will eventually
# cause issues in httplib # cause issues in httplib
if not six.PY3: if PY2:
value = to_string(value) value = to_string(value)
url = urlparse(value) url = urlparse(value)

View File

@ -10,7 +10,7 @@ from __future__ import absolute_import
from collections import Mapping, Iterable from collections import Mapping, Iterable
from threading import local from threading import local
from raven.utils import six from raven._compat import iteritems
class Context(local, Mapping, Iterable): class Context(local, Mapping, Iterable):
@ -42,10 +42,10 @@ class Context(local, Mapping, Iterable):
def merge(self, data): def merge(self, data):
d = self.data d = self.data
for key, value in six.iteritems(data): for key, value in iteritems(data):
if key in ('tags', 'extra'): if key in ('tags', 'extra'):
d.setdefault(key, {}) d.setdefault(key, {})
for t_key, t_value in six.iteritems(value): for t_key, t_value in iteritems(value):
d[key][t_key] = t_value d[key][t_key] = t_value
else: else:
d[key] = value d[key] = value

View File

@ -19,7 +19,7 @@ import warnings
from django.conf import settings from django.conf import settings
from hashlib import md5 from hashlib import md5
from raven.utils import six from raven._compat import PY2, binary_type, text_type
from raven.utils.imports import import_string from raven.utils.imports import import_string
from raven.contrib.django.management import patch_cli_runner from raven.contrib.django.management import patch_cli_runner
@ -61,7 +61,7 @@ class ProxyClient(object):
__ne__ = lambda x, o: get_client() != o __ne__ = lambda x, o: get_client() != o
__gt__ = lambda x, o: get_client() > o __gt__ = lambda x, o: get_client() > o
__ge__ = lambda x, o: get_client() >= o __ge__ = lambda x, o: get_client() >= o
if not six.PY3: if PY2:
__cmp__ = lambda x, o: cmp(get_client(), o) # NOQA __cmp__ = lambda x, o: cmp(get_client(), o) # NOQA
__hash__ = lambda x: hash(get_client()) __hash__ = lambda x: hash(get_client())
# attributes are currently not callable # attributes are currently not callable
@ -92,11 +92,11 @@ class ProxyClient(object):
__invert__ = lambda x: ~(get_client()) __invert__ = lambda x: ~(get_client())
__complex__ = lambda x: complex(get_client()) __complex__ = lambda x: complex(get_client())
__int__ = lambda x: int(get_client()) __int__ = lambda x: int(get_client())
if not six.PY3: if PY2:
__long__ = lambda x: long(get_client()) # NOQA __long__ = lambda x: long(get_client()) # NOQA
__float__ = lambda x: float(get_client()) __float__ = lambda x: float(get_client())
__str__ = lambda x: six.binary_type(get_client()) __str__ = lambda x: binary_type(get_client())
__unicode__ = lambda x: six.text_type(get_client()) __unicode__ = lambda x: text_type(get_client())
__oct__ = lambda x: oct(get_client()) __oct__ = lambda x: oct(get_client())
__hex__ = lambda x: hex(get_client()) __hex__ = lambda x: hex(get_client())
__index__ = lambda x: get_client().__index__() __index__ = lambda x: get_client().__index__()
@ -139,7 +139,7 @@ def get_client(client=None, reset=False):
options.setdefault('release', ga('RELEASE')) options.setdefault('release', ga('RELEASE'))
transport = ga('TRANSPORT') or options.get('transport') transport = ga('TRANSPORT') or options.get('transport')
if isinstance(transport, six.string_types): if isinstance(transport, string_types):
transport = import_string(transport) transport = import_string(transport)
options['transport'] = transport options['transport'] = transport

View File

@ -12,7 +12,7 @@ from django.conf import settings
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.functional import Promise from django.utils.functional import Promise
from raven.utils.serializer import Serializer, register from raven.utils.serializer import Serializer, register
from raven.utils import six from raven._compat import text_type
__all__ = ('PromiseSerializer',) __all__ = ('PromiseSerializer',)
@ -34,12 +34,15 @@ class PromiseSerializer(Serializer):
def serialize(self, value, **kwargs): def serialize(self, value, **kwargs):
# EPIC HACK # EPIC HACK
# handles lazy model instances (which are proxy values that don't easily give you the actual function) # handles lazy model instances (which are proxy values that don't
# easily give you the actual function)
pre = value.__class__.__name__[1:] pre = value.__class__.__name__[1:]
if hasattr(value, '%s__func' % pre): if hasattr(value, '%s__func' % pre):
value = getattr(value, '%s__func' % pre)(*getattr(value, '%s__args' % pre), **getattr(value, '%s__kw' % pre)) value = getattr(value, '%s__func' % pre)(
*getattr(value, '%s__args' % pre),
**getattr(value, '%s__kw' % pre))
else: else:
return self.recurse(six.text_type(value)) return self.recurse(text_type(value))
return self.recurse(value, **kwargs) return self.recurse(value, **kwargs)
register(PromiseSerializer) register(PromiseSerializer)

View File

@ -15,9 +15,9 @@ from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from raven._compat import string_types
from raven.contrib.django.models import client from raven.contrib.django.models import client
from raven.utils import json from raven.utils import json
from raven.utils import six
def is_valid_origin(origin): def is_valid_origin(origin):
@ -32,7 +32,7 @@ def is_valid_origin(origin):
origin = origin.lower() origin = origin.lower()
for value in settings.SENTRY_ALLOW_ORIGIN: for value in settings.SENTRY_ALLOW_ORIGIN:
if isinstance(value, six.string_types): if isinstance(value, string_types):
if value.lower() == origin: if value.lower() == origin:
return True return True
else: else:

View File

@ -23,11 +23,11 @@ from flask import request, current_app, g
from flask.signals import got_request_exception, request_finished from flask.signals import got_request_exception, request_finished
from werkzeug.exceptions import ClientDisconnected from werkzeug.exceptions import ClientDisconnected
from raven._compat import string_types
from raven.conf import setup_logging from raven.conf import setup_logging
from raven.base import Client from raven.base import Client
from raven.middleware import Sentry as SentryMiddleware from raven.middleware import Sentry as SentryMiddleware
from raven.handlers.logging import SentryHandler from raven.handlers.logging import SentryHandler
from raven.utils import six
from raven.utils.compat import _urlparse from raven.utils.compat import _urlparse
from raven.utils.encoding import to_unicode from raven.utils.encoding import to_unicode
from raven.utils.imports import import_string from raven.utils.imports import import_string
@ -38,13 +38,14 @@ def make_client(client_cls, app, dsn=None):
# TODO(dcramer): django and Flask share very similar concepts here, and # TODO(dcramer): django and Flask share very similar concepts here, and
# should be refactored # should be refactored
transport = app.config.get('SENTRY_TRANSPORT') transport = app.config.get('SENTRY_TRANSPORT')
if isinstance(transport, six.string_types): if isinstance(transport, string_types):
transport = import_string(transport) transport = import_string(transport)
return client_cls( return client_cls(
dsn=dsn or app.config.get('SENTRY_DSN') or os.environ.get('SENTRY_DSN'), dsn=dsn or app.config.get('SENTRY_DSN') or os.environ.get('SENTRY_DSN'),
transport=transport, transport=transport,
include_paths=set(app.config.get('SENTRY_INCLUDE_PATHS', [])) | set([app.import_name]), include_paths=set(app.config.get(
'SENTRY_INCLUDE_PATHS', [])) | set([app.import_name]),
exclude_paths=app.config.get('SENTRY_EXCLUDE_PATHS'), exclude_paths=app.config.get('SENTRY_EXCLUDE_PATHS'),
name=app.config.get('SENTRY_NAME'), name=app.config.get('SENTRY_NAME'),
site=app.config.get('SENTRY_SITE_NAME'), site=app.config.get('SENTRY_SITE_NAME'),
@ -131,17 +132,20 @@ class Sentry(object):
if not self.client: if not self.client:
return return
ignored_exc_type_list = current_app.config.get('RAVEN_IGNORE_EXCEPTIONS', []) ignored_exc_type_list = current_app.config.get(
'RAVEN_IGNORE_EXCEPTIONS', [])
exc = sys.exc_info()[1] exc = sys.exc_info()[1]
if any((isinstance(exc, ignored_exc_type) for ignored_exc_type in ignored_exc_type_list)): if any((isinstance(exc, ignored_exc_type)
for ignored_exc_type in ignored_exc_type_list)):
return return
self.captureException(exc_info=kwargs.get('exc_info')) self.captureException(exc_info=kwargs.get('exc_info'))
def get_user_info(self, request): def get_user_info(self, request):
""" """
Requires Flask-Login (https://pypi.python.org/pypi/Flask-Login/) to be installed Requires Flask-Login (https://pypi.python.org/pypi/Flask-Login/)
to be installed
and setup and setup
""" """
if not has_flask_login: if not has_flask_login:

View File

@ -1,6 +1,6 @@
from __future__ import absolute_import from __future__ import absolute_import
from raven.utils import six from raven._compat import text_type
class APIError(Exception): class APIError(Exception):
@ -9,7 +9,7 @@ class APIError(Exception):
self.message = message self.message = message
def __unicode__(self): def __unicode__(self):
return six.text_type("%s: %s" % (self.message, self.code)) return text_type("%s: %s" % (self.message, self.code))
class RateLimited(APIError): class RateLimited(APIError):

View File

@ -13,16 +13,16 @@ import logbook
import sys import sys
import traceback import traceback
from raven._compat import string_types
from raven.base import Client from raven.base import Client
from raven.utils.encoding import to_string from raven.utils.encoding import to_string
from raven.utils import six
class SentryHandler(logbook.Handler): class SentryHandler(logbook.Handler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if len(args) == 1: if len(args) == 1:
arg = args[0] arg = args[0]
if isinstance(arg, six.string_types): if isinstance(arg, string_types):
self.client = kwargs.pop('client_cls', Client)(dsn=arg, **kwargs) self.client = kwargs.pop('client_cls', Client)(dsn=arg, **kwargs)
elif isinstance(arg, Client): elif isinstance(arg, Client):
self.client = arg self.client = arg

View File

@ -14,8 +14,8 @@ import logging
import sys import sys
import traceback import traceback
from raven._compat import string_types, iteritems, text_type
from raven.base import Client from raven.base import Client
from raven.utils import six
from raven.utils.encoding import to_string from raven.utils.encoding import to_string
from raven.utils.stacks import iter_stack_frames, label_from_frame from raven.utils.stacks import iter_stack_frames, label_from_frame
@ -31,15 +31,14 @@ class SentryHandler(logging.Handler, object):
client = kwargs.get('client_cls', Client) client = kwargs.get('client_cls', Client)
if len(args) == 1: if len(args) == 1:
arg = args[0] arg = args[0]
if isinstance(arg, six.string_types): if isinstance(arg, string_types):
self.client = client(dsn=arg, **kwargs) self.client = client(dsn=arg, **kwargs)
elif isinstance(arg, Client): elif isinstance(arg, Client):
self.client = arg self.client = arg
else: else:
raise ValueError('The first argument to %s must be either a Client instance or a DSN, got %r instead.' % ( raise ValueError('The first argument to %s must be either a '
self.__class__.__name__, 'Client instance or a DSN, got %r instead.' %
arg, (self.__class__.__name__, arg,))
))
elif 'client' in kwargs: elif 'client' in kwargs:
self.client = kwargs['client'] self.client = kwargs['client']
else: else:
@ -68,7 +67,8 @@ class SentryHandler(logging.Handler, object):
except Exception: except Exception:
if self.client.raise_send_errors: if self.client.raise_send_errors:
raise raise
print("Top level Sentry exception caught - failed creating log record", file=sys.stderr) print("Top level Sentry exception caught - failed "
"creating log record", file=sys.stderr)
print(to_string(record.msg), file=sys.stderr) print(to_string(record.msg), file=sys.stderr)
print(to_string(traceback.format_exc()), file=sys.stderr) print(to_string(traceback.format_exc()), file=sys.stderr)
@ -113,7 +113,7 @@ class SentryHandler(logging.Handler, object):
else: else:
extra = {} extra = {}
for k, v in six.iteritems(vars(record)): for k, v in iteritems(vars(record)):
if k in RESERVED: if k in RESERVED:
continue continue
if k.startswith('_'): if k.startswith('_'):
@ -136,13 +136,13 @@ class SentryHandler(logging.Handler, object):
'params': record.args, 'params': record.args,
} }
try: try:
handler_kwargs['message'] = six.text_type(record.msg) handler_kwargs['message'] = text_type(record.msg)
except UnicodeDecodeError: except UnicodeDecodeError:
# Handle binary strings where it should be unicode... # Handle binary strings where it should be unicode...
handler_kwargs['message'] = repr(record.msg)[1:-1] handler_kwargs['message'] = repr(record.msg)[1:-1]
try: try:
handler_kwargs['formatted'] = six.text_type(record.message) handler_kwargs['formatted'] = text_type(record.message)
except UnicodeDecodeError: except UnicodeDecodeError:
# Handle binary strings where it should be unicode... # Handle binary strings where it should be unicode...
handler_kwargs['formatted'] = repr(record.message)[1:-1] handler_kwargs['formatted'] = repr(record.message)[1:-1]
@ -160,8 +160,12 @@ class SentryHandler(logging.Handler, object):
handler_kwargs = {'exc_info': record.exc_info} handler_kwargs = {'exc_info': record.exc_info}
# HACK: discover a culprit when we normally couldn't # HACK: discover a culprit when we normally couldn't
elif not (data.get('stacktrace') or data.get('culprit')) and (record.name or record.funcName): elif not (data.get('stacktrace') or data.get('culprit')) \
culprit = label_from_frame({'module': record.name, 'function': record.funcName}) and (record.name or record.funcName):
culprit = label_from_frame({
'module': record.name,
'function': record.funcName
})
if culprit: if culprit:
data['culprit'] = culprit data['culprit'] = culprit

View File

@ -9,8 +9,8 @@ from __future__ import absolute_import
import re import re
from raven._compat import string_types, text_type
from raven.utils import varmap from raven.utils import varmap
from raven.utils import six
class Processor(object): class Processor(object):
@ -88,13 +88,13 @@ class SanitizePasswordsProcessor(Processor):
if value is None: if value is None:
return return
if isinstance(value, six.string_types) and self.VALUES_RE.match(value): if isinstance(value, string_types) and self.VALUES_RE.match(value):
return self.MASK return self.MASK
if not key: # key can be a NoneType if not key: # key can be a NoneType
return value return value
key = six.text_type(key).lower() key = text_type(key).lower()
for field in self.FIELDS: for field in self.FIELDS:
if field in key: if field in key:
# store mask as a fixed length for security # store mask as a fixed length for security
@ -112,7 +112,7 @@ class SanitizePasswordsProcessor(Processor):
if n not in data: if n not in data:
continue continue
if isinstance(data[n], six.string_types) and '=' in data[n]: if isinstance(data[n], string_types) and '=' in data[n]:
# at this point we've assumed it's a standard HTTP query # at this point we've assumed it's a standard HTTP query
# or cookie # or cookie
if n == 'cookies': if n == 'cookies':

View File

@ -7,10 +7,10 @@ raven.transport.http
""" """
from __future__ import absolute_import from __future__ import absolute_import
from raven._compat import string_types
from raven.conf import defaults from raven.conf import defaults
from raven.exceptions import APIError, RateLimited from raven.exceptions import APIError, RateLimited
from raven.transport.base import Transport from raven.transport.base import Transport
from raven.utils import six
from raven.utils.http import urlopen from raven.utils.http import urlopen
from raven.utils.compat import urllib2 from raven.utils.compat import urllib2
@ -23,9 +23,9 @@ class HTTPTransport(Transport):
self._parsed_url = parsed_url self._parsed_url = parsed_url
self._url = parsed_url.geturl().rsplit('+', 1)[-1] self._url = parsed_url.geturl().rsplit('+', 1)[-1]
if isinstance(timeout, six.string_types): if isinstance(timeout, string_types):
timeout = int(timeout) timeout = int(timeout)
if isinstance(verify_ssl, six.string_types): if isinstance(verify_ssl, string_types):
verify_ssl = bool(int(verify_ssl)) verify_ssl = bool(int(verify_ssl))
self.timeout = timeout self.timeout = timeout

View File

@ -7,7 +7,7 @@ raven.utils
""" """
from __future__ import absolute_import from __future__ import absolute_import
from raven.utils import six from raven._compat import iteritems, string_types
import logging import logging
try: try:
import pkg_resources import pkg_resources
@ -24,7 +24,7 @@ def merge_dicts(*dicts):
if not d: if not d:
continue continue
for k, v in six.iteritems(d): for k, v in iteritems(d):
out[k] = v out[k] = v
return out return out
@ -42,7 +42,8 @@ def varmap(func, var, context=None, name=None):
return func(name, '<...>') return func(name, '<...>')
context[objid] = 1 context[objid] = 1
if isinstance(var, dict): if isinstance(var, dict):
ret = dict((k, varmap(func, v, context, k)) for k, v in six.iteritems(var)) ret = dict((k, varmap(func, v, context, k))
for k, v in iteritems(var))
elif isinstance(var, (list, tuple)): elif isinstance(var, (list, tuple)):
ret = [varmap(func, f, context, name) for f in var] ret = [varmap(func, f, context, name) for f in var]
else: else:
@ -79,7 +80,7 @@ def get_version_from_app(module_name, app):
if callable(version): if callable(version):
version = version() version = version()
if not isinstance(version, (six.string_types, list, tuple)): if not isinstance(version, (string_types, list, tuple)):
version = None version = None
if version is None: if version is None:
@ -98,7 +99,8 @@ def get_versions(module_list=None):
ext_module_list = set() ext_module_list = set()
for m in module_list: for m in module_list:
parts = m.split('.') parts = m.split('.')
ext_module_list.update('.'.join(parts[:idx]) for idx in range(1, len(parts) + 1)) ext_module_list.update('.'.join(parts[:idx])
for idx in range(1, len(parts) + 1))
versions = {} versions = {}
for module_name in ext_module_list: for module_name in ext_module_list:
@ -128,7 +130,8 @@ def get_versions(module_list=None):
return versions return versions
def get_auth_header(protocol, timestamp, client, api_key, api_secret=None, **kwargs): def get_auth_header(protocol, timestamp, client, api_key,
api_secret=None, **kwargs):
header = [ header = [
('sentry_timestamp', timestamp), ('sentry_timestamp', timestamp),
('sentry_client', client), ('sentry_client', client),

View File

@ -8,7 +8,9 @@ raven.utils.encoding
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import warnings import warnings
from raven.utils import six
from raven._compat import integer_types, text_type, binary_type, \
string_types, PY2
def is_protected_type(obj): def is_protected_type(obj):
@ -19,7 +21,7 @@ def is_protected_type(obj):
""" """
import Decimal import Decimal
import datetime import datetime
return isinstance(obj, six.integer_types + (type(None), float, Decimal, return isinstance(obj, integer_types + (type(None), float, Decimal,
datetime.datetime, datetime.date, datetime.time)) datetime.datetime, datetime.date, datetime.time))
@ -31,25 +33,25 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'):
If strings_only is True, don't convert (some) non-string-like objects. If strings_only is True, don't convert (some) non-string-like objects.
""" """
# Handle the common case first, saves 30-40% when s is an instance of # Handle the common case first, saves 30-40% when s is an instance of
# six.text_type. This function gets called often in that setting. # text_type. This function gets called often in that setting.
if isinstance(s, six.text_type): if isinstance(s, text_type):
return s return s
if strings_only and is_protected_type(s): if strings_only and is_protected_type(s):
return s return s
try: try:
if not isinstance(s, six.string_types): if not isinstance(s, string_types):
if hasattr(s, '__unicode__'): if hasattr(s, '__unicode__'):
s = s.__unicode__() s = s.__unicode__()
else: else:
if six.PY3: if not PY2:
if isinstance(s, bytes): if isinstance(s, bytes):
s = six.text_type(s, encoding, errors) s = text_type(s, encoding, errors)
else: else:
s = six.text_type(s) s = text_type(s)
else: else:
s = six.text_type(bytes(s), encoding, errors) s = text_type(bytes(s), encoding, errors)
else: else:
# Note: We use .decode() here, instead of six.text_type(s, encoding, # Note: We use .decode() here, instead of text_type(s, encoding,
# errors), so that if s is a SafeBytes, it ends up being a # errors), so that if s is a SafeBytes, it ends up being a
# SafeText at the end. # SafeText at the end.
s = s.decode(encoding, errors) s = s.decode(encoding, errors)
@ -69,19 +71,20 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'):
def transform(value): def transform(value):
from raven.utils.serializer import transform from raven.utils.serializer import transform
warnings.warn('You should switch to raven.utils.serializer.transform', DeprecationWarning) warnings.warn('You should switch to raven.utils.serializer.'
'transform', DeprecationWarning)
return transform(value) return transform(value)
def to_unicode(value): def to_unicode(value):
try: try:
value = six.text_type(force_text(value)) value = text_type(force_text(value))
except (UnicodeEncodeError, UnicodeDecodeError): except (UnicodeEncodeError, UnicodeDecodeError):
value = '(Error decoding value)' value = '(Error decoding value)'
except Exception: # in some cases we get a different exception except Exception: # in some cases we get a different exception
try: try:
value = six.binary_type(repr(type(value))) value = binary_type(repr(type(value)))
except Exception: except Exception:
value = '(Error decoding value)' value = '(Error decoding value)'
return value return value
@ -89,6 +92,6 @@ def to_unicode(value):
def to_string(value): def to_string(value):
try: try:
return six.binary_type(value.decode('utf-8').encode('utf-8')) return binary_type(value.decode('utf-8').encode('utf-8'))
except: except:
return to_unicode(value).encode('utf-8') return to_unicode(value).encode('utf-8')

View File

@ -1,11 +1,11 @@
from __future__ import absolute_import from __future__ import absolute_import
from . import six from raven._compat import PY2
def import_string(key): def import_string(key):
# HACK(dcramer): Ensure a unicode key is still importable # HACK(dcramer): Ensure a unicode key is still importable
if not six.PY3: if PY2:
key = str(key) key = str(key)
if '.' not in key: if '.' not in key:

View File

@ -11,7 +11,8 @@ from __future__ import absolute_import
import itertools import itertools
import types import types
from raven.utils import six from raven._compat import text_type, binary_type, string_types, iteritems, \
class_types, PY2
from raven.utils.encoding import to_unicode from raven.utils.encoding import to_unicode
from .manager import manager as serialization_manager from .manager import manager as serialization_manager
@ -54,13 +55,14 @@ class Serializer(object):
_depth += 1 _depth += 1
if _depth >= max_depth: if _depth >= max_depth:
try: try:
value = six.text_type(repr(value))[:string_max_length] value = text_type(repr(value))[:string_max_length]
except Exception as e: except Exception as e:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
self.manager.logger.exception(e) self.manager.logger.exception(e)
return six.text_type(type(value)) return text_type(type(value))
return self.manager.transform(value, max_depth=max_depth, _depth=_depth, **kwargs) return self.manager.transform(value, max_depth=max_depth,
_depth=_depth, **kwargs)
class IterableSerializer(Serializer): class IterableSerializer(Serializer):
@ -71,7 +73,8 @@ class IterableSerializer(Serializer):
return tuple( return tuple(
self.recurse(o, **kwargs) self.recurse(o, **kwargs)
for n, o for n, o
in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(value)) in itertools.takewhile(lambda x: x[0] < list_max_length,
enumerate(value))
) )
@ -79,7 +82,7 @@ class DictSerializer(Serializer):
types = (dict,) types = (dict,)
def make_key(self, key): def make_key(self, key):
if not isinstance(key, six.string_types): if not isinstance(key, string_types):
return to_unicode(key) return to_unicode(key)
return key return key
@ -88,12 +91,13 @@ class DictSerializer(Serializer):
return dict( return dict(
(self.make_key(self.recurse(k, **kwargs)), self.recurse(v, **kwargs)) (self.make_key(self.recurse(k, **kwargs)), self.recurse(v, **kwargs))
for n, (k, v) for n, (k, v)
in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(six.iteritems(value))) in itertools.takewhile(lambda x: x[0] < list_max_length, enumerate(
iteritems(value)))
) )
class UnicodeSerializer(Serializer): class UnicodeSerializer(Serializer):
types = (six.text_type,) types = (text_type,)
def serialize(self, value, **kwargs): def serialize(self, value, **kwargs):
# try to return a reasonable string that can be decoded # try to return a reasonable string that can be decoded
@ -101,21 +105,22 @@ class UnicodeSerializer(Serializer):
# unicode character # unicode character
# e.g. we want the output to be like: "u'רונית מגן'" # e.g. we want the output to be like: "u'רונית מגן'"
string_max_length = kwargs.get('string_max_length', None) string_max_length = kwargs.get('string_max_length', None)
return repr(six.text_type('%s')) % (value[:string_max_length],) return repr(text_type('%s')) % (value[:string_max_length],)
class StringSerializer(Serializer): class StringSerializer(Serializer):
types = (six.binary_type,) types = (binary_type,)
def serialize(self, value, **kwargs): def serialize(self, value, **kwargs):
string_max_length = kwargs.get('string_max_length', None) string_max_length = kwargs.get('string_max_length', None)
if six.PY3: if not PY2:
return repr(value[:string_max_length]) return repr(value[:string_max_length])
try: try:
# Python2 madness: let's try to recover from developer's issues # Python2 madness: let's try to recover from developer's issues
# Try to process the string as if it was a unicode. # Try to process the string as if it was a unicode.
return "'" + value.decode('utf8')[:string_max_length].encode('utf8') + "'" return "'" + value.decode('utf8')[:string_max_length] \
.encode('utf8') + "'"
except UnicodeDecodeError: except UnicodeDecodeError:
pass pass
@ -123,10 +128,11 @@ class StringSerializer(Serializer):
class TypeSerializer(Serializer): class TypeSerializer(Serializer):
types = six.class_types types = class_types
def can(self, value): def can(self, value):
return not super(TypeSerializer, self).can(value) and has_sentry_metadata(value) return not super(TypeSerializer, self).can(value) \
and has_sentry_metadata(value)
def serialize(self, value, **kwargs): def serialize(self, value, **kwargs):
return self.recurse(value.__sentry__(), **kwargs) return self.recurse(value.__sentry__(), **kwargs)
@ -157,10 +163,11 @@ class FunctionSerializer(Serializer):
types = (types.FunctionType,) types = (types.FunctionType,)
def serialize(self, value, **kwargs): def serialize(self, value, **kwargs):
return '<function %s from %s at 0x%x>' % (value.__name__, value.__module__, id(value)) return '<function %s from %s at 0x%x>' % (
value.__name__, value.__module__, id(value))
if not six.PY3: if PY2:
class LongSerializer(Serializer): class LongSerializer(Serializer):
types = (long,) # noqa types = (long,) # noqa
@ -178,5 +185,5 @@ serialization_manager.register(BooleanSerializer)
serialization_manager.register(FloatSerializer) serialization_manager.register(FloatSerializer)
serialization_manager.register(IntegerSerializer) serialization_manager.register(IntegerSerializer)
serialization_manager.register(FunctionSerializer) serialization_manager.register(FunctionSerializer)
if not six.PY3: if PY2:
serialization_manager.register(LongSerializer) serialization_manager.register(LongSerializer)

View File

@ -9,7 +9,7 @@ from __future__ import absolute_import
import logging import logging
from contextlib import closing from contextlib import closing
from raven.utils import six from raven._compat import text_type
__all__ = ('register', 'transform') __all__ = ('register', 'transform')
@ -69,16 +69,18 @@ class Serializer(object):
return serializer.serialize(value, **kwargs) return serializer.serialize(value, **kwargs)
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
return six.text_type(type(value)) return text_type(type(value))
# if all else fails, lets use the repr of the object # if all else fails, lets use the repr of the object
try: try:
return repr(value) return repr(value)
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
# It's common case that a model's __unicode__ definition may try to query the database # It's common case that a model's __unicode__ definition
# which if it was not cleaned up correctly, would hit a transaction aborted exception # may try to query the database which if it was not
return six.text_type(type(value)) # cleaned up correctly, would hit a transaction aborted
# exception
return text_type(type(value))
finally: finally:
self.context.remove(objid) self.context.remove(objid)

View File

@ -1,406 +0,0 @@
"""Utilities for writing code that runs on Python 2 and 3"""
# flake8: noqa
# Copyright (c) 2010-2013 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import absolute_import
import operator
import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.3.0"
# True if we are running on Python 3.
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
if sys.platform.startswith("java"):
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
def _add_doc(func, doc):
"""Add documentation to a function."""
func.__doc__ = doc
def _import_module(name):
"""Import module, returning the module after the last dot."""
__import__(name)
return sys.modules[name]
class _LazyDescr(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, tp):
result = self._resolve()
setattr(obj, self.name, result)
# This is a bit ugly, but it avoids running this again.
delattr(tp, self.name)
return result
class MovedModule(_LazyDescr):
def __init__(self, name, old, new=None):
super(MovedModule, self).__init__(name)
if PY3:
if new is None:
new = name
self.mod = new
else:
self.mod = old
def _resolve(self):
return _import_module(self.mod)
class MovedAttribute(_LazyDescr):
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
super(MovedAttribute, self).__init__(name)
if PY3:
if new_mod is None:
new_mod = name
self.mod = new_mod
if new_attr is None:
if old_attr is None:
new_attr = name
else:
new_attr = old_attr
self.attr = new_attr
else:
self.mod = old_mod
if old_attr is None:
old_attr = name
self.attr = old_attr
def _resolve(self):
module = _import_module(self.mod)
return getattr(module, self.attr)
class _MovedItems(types.ModuleType):
"""Lazy loading of moved objects"""
_moved_attributes = [
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
MovedAttribute("reduce", "__builtin__", "functools"),
MovedAttribute("StringIO", "StringIO", "io"),
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
MovedModule("builtins", "__builtin__"),
MovedModule("configparser", "ConfigParser"),
MovedModule("copyreg", "copy_reg"),
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
MovedModule("http_cookies", "Cookie", "http.cookies"),
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
MovedModule("html_parser", "HTMLParser", "html.parser"),
MovedModule("http_client", "httplib", "http.client"),
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
MovedModule("cPickle", "cPickle", "pickle"),
MovedModule("queue", "Queue"),
MovedModule("reprlib", "repr"),
MovedModule("socketserver", "SocketServer"),
MovedModule("tkinter", "Tkinter"),
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
MovedModule("tkinter_colorchooser", "tkColorChooser",
"tkinter.colorchooser"),
MovedModule("tkinter_commondialog", "tkCommonDialog",
"tkinter.commondialog"),
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
"tkinter.simpledialog"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("winreg", "_winreg"),
]
for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr)
del attr
moves = sys.modules[__name__ + ".moves"] = _MovedItems("moves")
def add_move(move):
"""Add an item to six.moves."""
setattr(_MovedItems, move.name, move)
def remove_move(name):
"""Remove item from six.moves."""
try:
delattr(_MovedItems, name)
except AttributeError:
try:
del moves.__dict__[name]
except KeyError:
raise AttributeError("no such move, %r" % (name,))
if PY3:
_meth_func = "__func__"
_meth_self = "__self__"
_func_closure = "__closure__"
_func_code = "__code__"
_func_defaults = "__defaults__"
_func_globals = "__globals__"
_iterkeys = "keys"
_itervalues = "values"
_iteritems = "items"
_iterlists = "lists"
else:
_meth_func = "im_func"
_meth_self = "im_self"
_func_closure = "func_closure"
_func_code = "func_code"
_func_defaults = "func_defaults"
_func_globals = "func_globals"
_iterkeys = "iterkeys"
_itervalues = "itervalues"
_iteritems = "iteritems"
_iterlists = "iterlists"
try:
advance_iterator = next
except NameError:
def advance_iterator(it):
return it.next()
next = advance_iterator
try:
callable = callable
except NameError:
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
if PY3:
def get_unbound_function(unbound):
return unbound
Iterator = object
else:
def get_unbound_function(unbound): # NOQA
return unbound.im_func
class Iterator(object):
def next(self):
return type(self).__next__(self)
callable = callable
_add_doc(get_unbound_function,
"""Get the function out of a possibly unbound function""")
get_method_function = operator.attrgetter(_meth_func)
get_method_self = operator.attrgetter(_meth_self)
get_function_closure = operator.attrgetter(_func_closure)
get_function_code = operator.attrgetter(_func_code)
get_function_defaults = operator.attrgetter(_func_defaults)
get_function_globals = operator.attrgetter(_func_globals)
def iterkeys(d, **kw):
"""Return an iterator over the keys of a dictionary."""
return iter(getattr(d, _iterkeys)(**kw))
def itervalues(d, **kw):
"""Return an iterator over the values of a dictionary."""
return iter(getattr(d, _itervalues)(**kw))
def iteritems(d, **kw):
"""Return an iterator over the (key, value) pairs of a dictionary."""
return iter(getattr(d, _iteritems)(**kw))
def iterlists(d, **kw):
"""Return an iterator over the (key, [values]) pairs of a dictionary."""
return iter(getattr(d, _iterlists)(**kw))
if PY3:
def b(s):
return s.encode("latin-1")
def u(s):
return s
if sys.version_info[1] <= 1:
def int2byte(i):
return bytes((i,))
else:
# This is about 2x faster than the implementation above on 3.2+
int2byte = operator.methodcaller("to_bytes", 1, "big")
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
else:
def b(s): # NOQA
return s
def u(s): # NOQA
return unicode(s, "unicode_escape")
int2byte = chr
import StringIO
StringIO = BytesIO = StringIO.StringIO
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")
if PY3:
import builtins
exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None):
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
print_ = getattr(builtins, "print")
del builtins
else:
def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
if _globs_ is None:
frame = sys._getframe(1)
_globs_ = frame.f_globals
if _locs_ is None:
_locs_ = frame.f_locals
del frame
elif _locs_ is None:
_locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""")
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
def print_(*args, **kwargs):
"""The new-style print function."""
fp = kwargs.pop("file", sys.stdout)
if fp is None:
return
def write(data):
if not isinstance(data, basestring):
data = str(data)
fp.write(data)
want_unicode = False
sep = kwargs.pop("sep", None)
if sep is not None:
if isinstance(sep, unicode):
want_unicode = True
elif not isinstance(sep, str):
raise TypeError("sep must be None or a string")
end = kwargs.pop("end", None)
if end is not None:
if isinstance(end, unicode):
want_unicode = True
elif not isinstance(end, str):
raise TypeError("end must be None or a string")
if kwargs:
raise TypeError("invalid keyword arguments to print()")
if not want_unicode:
for arg in args:
if isinstance(arg, unicode):
want_unicode = True
break
if want_unicode:
newline = unicode("\n")
space = unicode(" ")
else:
newline = "\n"
space = " "
if sep is None:
sep = space
if end is None:
end = newline
for i, arg in enumerate(args):
if i:
write(sep)
write(arg)
write(end)
_add_doc(reraise, """Reraise an exception.""")
def with_metaclass(meta, base=object):
"""Create a base class with a metaclass."""
return meta("NewBase", (base,), {})

View File

@ -14,13 +14,14 @@ import sys
import warnings import warnings
from raven.utils.serializer import transform from raven.utils.serializer import transform
from raven.utils import six from raven._compat import iteritems
_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') _coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
def get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None): def get_lines_from_file(filename, lineno, context_lines,
loader=None, module_name=None):
""" """
Returns context_lines before and after lineno from file. Returns context_lines before and after lineno from file.
Returns (pre_context_lineno, pre_context, context_line, post_context). Returns (pre_context_lineno, pre_context, context_line, post_context).
@ -63,7 +64,8 @@ def get_lines_from_file(filename, lineno, context_lines, loader=None, module_nam
try: try:
pre_context = [line.strip('\r\n') for line in source[lower_bound:lineno]] pre_context = [line.strip('\r\n') for line in source[lower_bound:lineno]]
context_line = source[lineno].strip('\r\n') context_line = source[lineno].strip('\r\n')
post_context = [line.strip('\r\n') for line in source[(lineno + 1):upper_bound]] post_context = [line.strip('\r\n') for line in
source[(lineno + 1):upper_bound]]
except IndexError: except IndexError:
# the file may have changed since it was loaded into memory # the file may have changed since it was loaded into memory
return None, None, None return None, None, None
@ -178,7 +180,7 @@ def get_frame_locals(frame, transformer=transform, max_var_size=4096):
f_vars = {} f_vars = {}
f_size = 0 f_size = 0
for k, v in six.iteritems(f_locals): for k, v in iteritems(f_locals):
v = transformer(v) v = transformer(v)
v_size = len(repr(v)) v_size = len(repr(v))
if v_size + f_size < 4096: if v_size + f_size < 4096:
@ -253,7 +255,8 @@ def get_stack_info(frames, transformer=transform, capture_locals=True,
lineno -= 1 lineno -= 1
if lineno is not None and abs_path: if lineno is not None and abs_path:
pre_context, context_line, post_context = get_lines_from_file(abs_path, lineno, 5, loader, module_name) pre_context, context_line, post_context = \
get_lines_from_file(abs_path, lineno, 5, loader, module_name)
else: else:
pre_context, context_line, post_context = None, None, None pre_context, context_line, post_context = None, None, None
@ -261,7 +264,8 @@ def get_stack_info(frames, transformer=transform, capture_locals=True,
# This changes /foo/site-packages/baz/bar.py into baz/bar.py # This changes /foo/site-packages/baz/bar.py into baz/bar.py
try: try:
base_filename = sys.modules[module_name.split('.', 1)[0]].__file__ base_filename = sys.modules[module_name.split('.', 1)[0]].__file__
filename = abs_path.split(base_filename.rsplit('/', 2)[0], 1)[-1].lstrip("/") filename = abs_path.split(
base_filename.rsplit('/', 2)[0], 1)[-1].lstrip("/")
except: except:
filename = abs_path filename = abs_path

View File

@ -6,7 +6,7 @@ This module implements WSGI related helpers adapted from ``werkzeug.wsgi``
""" """
from __future__ import absolute_import from __future__ import absolute_import
from raven.utils import six from raven._compat import iteritems
from raven.utils.compat import urllib_quote from raven.utils.compat import urllib_quote
@ -15,7 +15,7 @@ def get_headers(environ):
""" """
Returns only proper HTTP headers. Returns only proper HTTP headers.
""" """
for key, value in six.iteritems(environ): for key, value in iteritems(environ):
key = str(key) key = str(key)
if key.startswith('HTTP_') and key not in \ if key.startswith('HTTP_') and key not in \
('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):

View File

@ -8,8 +8,7 @@ except ImportError:
# pkg_resource is not available on Google App Engine # pkg_resource is not available on Google App Engine
pkg_resources = None pkg_resources = None
from raven.utils import six from raven._compat import text_type
from .exceptions import InvalidGitRepository from .exceptions import InvalidGitRepository
__all__ = ('fetch_git_sha', 'fetch_package_version') __all__ = ('fetch_git_sha', 'fetch_package_version')
@ -22,10 +21,11 @@ def fetch_git_sha(path, head=None):
if not head: if not head:
head_path = os.path.join(path, '.git', 'HEAD') head_path = os.path.join(path, '.git', 'HEAD')
if not os.path.exists(head_path): if not os.path.exists(head_path):
raise InvalidGitRepository('Cannot identify HEAD for git repository at %s' % (path,)) raise InvalidGitRepository(
'Cannot identify HEAD for git repository at %s' % (path,))
with open(head_path, 'r') as fp: with open(head_path, 'r') as fp:
head = six.text_type(fp.read()).strip() head = text_type(fp.read()).strip()
if head.startswith('ref: '): if head.startswith('ref: '):
revision_file = os.path.join( revision_file = os.path.join(
@ -38,12 +38,14 @@ def fetch_git_sha(path, head=None):
if not os.path.exists(revision_file): if not os.path.exists(revision_file):
if not os.path.exists(os.path.join(path, '.git')): if not os.path.exists(os.path.join(path, '.git')):
raise InvalidGitRepository('%s does not seem to be the root of a git repository' % (path,)) raise InvalidGitRepository(
raise InvalidGitRepository('Unable to find ref to head "%s" in repository' % (head,)) '%s does not seem to be the root of a git repository' % (path,))
raise InvalidGitRepository(
'Unable to find ref to head "%s" in repository' % (head,))
fh = open(revision_file, 'r') fh = open(revision_file, 'r')
try: try:
return six.text_type(fh.read()).strip() return text_type(fh.read()).strip()
finally: finally:
fh.close() fh.close()
@ -53,6 +55,7 @@ def fetch_package_version(dist_name):
>>> fetch_package_version('sentry') >>> fetch_package_version('sentry')
""" """
if pkg_resources is None: if pkg_resources is None:
raise NotImplementedError('pkg_resources is not available on this Python install') raise NotImplementedError('pkg_resources is not available '
'on this Python install')
dist = pkg_resources.get_distribution(dist_name) dist = pkg_resources.get_distribution(dist_name)
return dist.version return dist.version

View File

@ -53,6 +53,7 @@ if sys.version_info[0] == 3:
install_requires.remove('contextlib2') install_requires.remove('contextlib2')
tests_require = [ tests_require = [
'six',
'bottle', 'bottle',
'celery>=2.5', 'celery>=2.5',
'Django>=1.4', 'Django>=1.4',

View File

@ -5,13 +5,13 @@ import inspect
import mock import mock
import raven import raven
import time import time
import six
from raven.base import Client, ClientState from raven.base import Client, ClientState
from raven.exceptions import RateLimited from raven.exceptions import RateLimited
from raven.transport import AsyncTransport from raven.transport import AsyncTransport
from raven.transport.http import HTTPTransport from raven.transport.http import HTTPTransport
from raven.utils.stacks import iter_stack_frames from raven.utils.stacks import iter_stack_frames
from raven.utils import six
from raven.utils.testutils import TestCase from raven.utils.testutils import TestCase

View File

@ -9,8 +9,10 @@ import logging
import mock import mock
import pytest import pytest
import re import re
import six
import sys # NOQA import sys # NOQA
from exam import fixture from exam import fixture
from six import StringIO
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -33,8 +35,6 @@ from raven.contrib.django.templatetags.raven import sentry_public_dsn
from raven.contrib.django.views import is_valid_origin from raven.contrib.django.views import is_valid_origin
from raven.transport import HTTPTransport from raven.transport import HTTPTransport
from raven.utils.serializer import transform from raven.utils.serializer import transform
from raven.utils import six
from raven.utils.six import StringIO
from django.test.client import Client as TestClient, ClientHandler as TestClientHandler from django.test.client import Client as TestClient, ClientHandler as TestClientHandler
from .models import TestModel from .models import TestModel

View File

@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import six
from mock import patch from mock import patch
from tornado import web, gen, testing from tornado import web, gen, testing
from tornado.concurrent import Future from tornado.concurrent import Future
from tornado.httpclient import HTTPError from tornado.httpclient import HTTPError
from raven.contrib.tornado import SentryMixin, AsyncSentryClient from raven.contrib.tornado import SentryMixin, AsyncSentryClient
from raven.utils import six
class AnErrorProneHandler(SentryMixin, web.RequestHandler): class AnErrorProneHandler(SentryMixin, web.RequestHandler):

View File

@ -1,9 +1,9 @@
from __future__ import with_statement from __future__ import with_statement
from __future__ import unicode_literals from __future__ import unicode_literals
import six
import logbook import logbook
from raven.utils.testutils import TestCase from raven.utils.testutils import TestCase
from raven.utils import six
from raven.base import Client from raven.base import Client
from raven.handlers.logbook import SentryHandler from raven.handlers.logbook import SentryHandler

View File

@ -3,10 +3,10 @@ from __future__ import unicode_literals
import logging import logging
import sys import sys
import mock import mock
import six
from raven.base import Client from raven.base import Client
from raven.handlers.logging import SentryHandler from raven.handlers.logging import SentryHandler
from raven.utils import six
from raven.utils.stacks import iter_stack_frames from raven.utils.stacks import iter_stack_frames
from raven.utils.testutils import TestCase from raven.utils.testutils import TestCase

View File

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
import uuid import uuid
import six
from raven.utils import six, json from raven.utils import json
from raven.utils.testutils import TestCase from raven.utils.testutils import TestCase
from raven.utils.serializer import transform from raven.utils.serializer import transform

View File

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import six
from mock import Mock from mock import Mock
from raven.utils.testutils import TestCase from raven.utils.testutils import TestCase
from raven.utils import six
from raven.utils.stacks import get_culprit, get_stack_info, get_lines_from_file from raven.utils.stacks import get_culprit, get_stack_info, get_lines_from_file

View File

@ -1,8 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import
import six
import raven import raven
from raven.utils import six
from raven.utils.imports import import_string from raven.utils.imports import import_string

View File

@ -3,11 +3,11 @@ from __future__ import absolute_import
import os.path import os.path
import pytest import pytest
import subprocess import subprocess
import six
from django.conf import settings from django.conf import settings
from raven.versioning import fetch_git_sha, fetch_package_version from raven.versioning import fetch_git_sha, fetch_package_version
from raven.utils import six
def has_git_requirements(): def has_git_requirements():