[Python-modules-commits] [raven] 02/08: New upstream release.
Vincent Bernat
bernat at moszumanska.debian.org
Sun Nov 12 19:55:32 UTC 2017
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to annotated tag debian/6.3.0-1
in repository raven.
commit 2de408d771d1e9a528916980be5ec038dedf9cd0
Author: Vincent Bernat <bernat at debian.org>
Date: Sun Nov 12 20:35:39 2017 +0100
New upstream release.
---
MANIFEST.in | 2 +-
PKG-INFO | 5 +-
README.rst | 124 ++++++++++++++--
conftest.py | 74 +++++++++
raven.egg-info/PKG-INFO | 5 +-
raven.egg-info/SOURCES.txt | 8 +
raven.egg-info/requires.txt | 15 +-
raven/__init__.py | 2 +-
raven/base.py | 52 +++++--
raven/breadcrumbs.py | 9 +-
raven/conf/defaults.py | 2 +-
raven/contrib/async.py | 1 +
raven/contrib/awslambda/__init__.py | 173 ++++++++++++++++++++++
raven/contrib/bottle/__init__.py | 1 +
raven/contrib/celery/__init__.py | 4 +-
raven/contrib/django/__init__.py | 1 -
raven/contrib/django/apps.py | 5 +
raven/contrib/django/client.py | 45 +++---
raven/contrib/django/management/commands/raven.py | 9 +-
raven/contrib/django/middleware/__init__.py | 21 +++
raven/contrib/django/middleware/wsgi.py | 1 +
raven/contrib/django/models.py | 96 +++++++-----
raven/contrib/django/resolver.py | 2 +-
raven/contrib/flask.py | 46 ++++--
raven/contrib/tornado/__init__.py | 4 +-
raven/contrib/webpy/__init__.py | 1 +
raven/contrib/zconfig/__init__.py | 39 +++++
raven/contrib/zerorpc/__init__.py | 11 +-
raven/contrib/zope/__init__.py | 4 +-
raven/events.py | 6 +
raven/handlers/logging.py | 4 +-
raven/middleware.py | 2 +
raven/processors.py | 11 +-
raven/utils/conf.py | 1 +
raven/utils/encoding.py | 2 +-
raven/utils/stacks.py | 5 +-
raven/utils/wsgi.py | 14 ++
setup.cfg | 9 +-
setup.py | 26 ++--
tests/base/tests.py | 53 +++++++
tests/contrib/awslambda/conftest.py | 106 +++++++++++++
tests/contrib/awslambda/test_lambda.py | 69 +++++++++
tests/contrib/django/settings.py | 64 ++++++++
tests/contrib/django/test_resolver.py | 13 +-
tests/contrib/django/test_tastypie.py | 5 +-
tests/contrib/django/tests.py | 130 ++++++++++++----
tests/contrib/django/urls.py | 5 +-
tests/contrib/django/views.py | 9 ++
tests/contrib/flask/tests.py | 64 ++++++--
tests/contrib/zconfig/__init__.py | 0
tests/contrib/zconfig/tests.py | 76 ++++++++++
tests/handlers/logging/tests.py | 11 ++
tests/transport/gevent/tests.py | 5 +
tests/utils/wsgi/tests.py | 12 +-
tests/versioning/tests.py | 8 +-
55 files changed, 1277 insertions(+), 195 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 76ddd09..887e552 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include setup.py README.rst MANIFEST.in LICENSE *.txt
+include setup.py conftest.py README.rst MANIFEST.in LICENSE *.txt
recursive-include raven/contrib/zope *.xml
recursive-include raven/data *
graft tests
diff --git a/PKG-INFO b/PKG-INFO
index d14e2db..a9105da 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: raven
-Version: 6.0.0
+Version: 6.3.0
Summary: Raven is a client for Sentry (https://getsentry.com)
Home-page: https://github.com/getsentry/raven-python
Author: Sentry
@@ -21,10 +21,13 @@ Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development
diff --git a/README.rst b/README.rst
index 8d5d2bf..481a239 100644
--- a/README.rst
+++ b/README.rst
@@ -1,24 +1,128 @@
-Raven
-======
+.. raw:: html
+
+ <p align="center">
+
+.. image:: docs/_static/logo.png
+ :target: https://sentry.io
+ :align: center
+ :width: 116
+ :alt: Sentry website
+
+.. raw:: html
+
+ </p>
+
+Raven - Sentry for Python
+=========================
+
+.. image:: https://img.shields.io/pypi/v/raven.svg
+ :target: https://pypi.python.org/pypi/raven
+ :alt: PyPi page link -- version
.. image:: https://travis-ci.org/getsentry/raven-python.svg?branch=master
:target: https://travis-ci.org/getsentry/raven-python
-Raven is a Python client for `Sentry <http://getsentry.com/>`_. It provides
-full out-of-the-box support for many of the popular frameworks, including
+.. image:: https://img.shields.io/pypi/l/raven.svg
+ :target: https://pypi.python.org/pypi/raven
+ :alt: PyPi page link -- MIT licence
+
+.. image:: https://img.shields.io/pypi/pyversions/raven.svg
+ :target: https://pypi.python.org/pypi/raven
+ :alt: PyPi page link -- Python versions
+
+.. image:: https://codeclimate.com/github/getsentry/raven-python/badges/gpa.svg
+ :target: https://codeclimate.com/github/codeclimate/codeclimate
+ :alt: Code Climate
+
+
+Raven is the official Python client for `Sentry`_, officially supports
+Python 2.6–2.7 & 3.3–3.7, and runs on PyPy and Google App Engine.
+
+It tracks errors and exceptions that happen during the
+execution of your application and provides instant notification with detailed
+information needed to prioritize, identify, reproduce and fix each issue.
+
+It provides full out-of-the-box support for many of the popular python frameworks, including
Django, and Flask. Raven also includes drop-in support for any WSGI-compatible
web application.
Your application doesn't live on the web? No problem! Raven is easy to use in
any Python application.
+For more information, see our `Python Documentation`_ for framework integrations and other goodies.
+
+
+Features
+--------
+
+- Automatically report (un)handled exceptions and errors
+- Send customized diagnostic data
+- Process and sanitize data before sending it over the network
+
+
+Quickstart
+----------
+
+It's really easy to get started with Raven. After you complete setting up a project in Sentry,
+you’ll be given a value which we call a DSN, or Data Source Name. You will need it to configure the client.
+
+
+Install the latest package with *pip* and configure the client::
+
+ pip install raven --upgrade
+
+Create a client and capture an example exception:
+
+.. sourcecode:: python
+
+ from raven import Client
+
+ client = Client('___DSN___')
+
+ try:
+ 1 / 0
+ except ZeroDivisionError:
+ client.captureException()
+
+
+Raven Python is more than that however. Checkout our `Python Documentation`_.
+
+
+Contributing
+------------
+
+Raven is under active development and contributions are more than welcome!
+There are many ways to contribute:
+
+* Join in on discussions on our `Mailing List`_ or in our `IRC Channel`_.
+
+* Report bugs on our `Issue Tracker`_.
+
+* Submit a pull request!
+
+
Resources
---------
-* `Documentation <https://docs.getsentry.com/hosted/clients/python/>`_
-* `Bug Tracker <http://github.com/getsentry/raven-python/issues>`_
-* `Code <http://github.com/getsentry/raven-python>`_
-* `Mailing List <https://groups.google.com/group/getsentry>`_
-* `IRC <irc://irc.freenode.net/sentry>`_ (irc.freenode.net, #sentry)
-* `Travis CI <http://travis-ci.org/getsentry/raven-python>`_
+* `Sentry`_
+* `Python Documentation`_
+* `Issue Tracker`_
+* `Code`_ on Github
+* `Mailing List`_
+* `IRC Channel`_ (irc.freenode.net, #sentry)
+* `Travis CI`_
+
+.. _Sentry: https://getsentry.com/
+.. _Python Documentation: https://docs.getsentry.com/hosted/clients/python/
+.. _SDKs for other platforms: https://docs.sentry.io/#platforms
+.. _Issue Tracker: https://github.com/getsentry/raven-python/issues
+.. _Code: https://github.com/getsentry/raven-python
+.. _Mailing List: https://groups.google.com/group/getsentry
+.. _IRC Channel: irc://irc.freenode.net/sentry
+.. _Travis CI: http://travis-ci.org/getsentry/raven-python
+
+
+
+
+Not using Python? Check out our `SDKs for other platforms`_.
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..70d582d
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,74 @@
+from __future__ import absolute_import
+
+import os.path
+import pytest
+import sys
+
+collect_ignore = [
+ 'tests/contrib/awslambda'
+]
+
+if sys.version_info[0] > 2:
+ if sys.version_info[1] < 3:
+ collect_ignore.append('tests/contrib/flask')
+ if sys.version_info[1] == 2:
+ collect_ignore.append('tests/handlers/logbook')
+
+try:
+ import gevent # NOQA
+except ImportError:
+ collect_ignore.append('tests/transport/gevent')
+
+try:
+ import web # NOQA
+except ImportError:
+ collect_ignore.append('tests/contrib/webpy')
+
+try:
+ import django # NOQA
+except ImportError:
+ django = None
+ collect_ignore.append('tests/contrib/django')
+
+try:
+ import tastypie # NOQA
+except ImportError:
+ collect_ignore.append('tests/contrib/django/test_tastypie.py')
+
+
+use_djcelery = True
+try:
+ import djcelery # NOQA
+ #INSTALLED_APPS.append('djcelery')
+except ImportError:
+ use_djcelery = False
+
+
+def pytest_runtest_teardown(item):
+ if django:
+ from raven.contrib.django.models import client
+ client.events = []
+
+
+ at pytest.fixture
+def project_root():
+ return os.path.dirname(os.path.abspath(__file__))
+
+
+ at pytest.fixture
+def mytest_model():
+
+ from tests.contrib.django.models import MyTestModel
+ return MyTestModel
+
+
+ at pytest.fixture(scope='function', autouse=False)
+def user_instance(request, admin_user):
+ request.cls.user = admin_user
+
+
+ at pytest.fixture(autouse=True)
+def has_git_requirements(request, project_root):
+ if request.node.get_marker('has_git_requirements'):
+ if not os.path.exists(os.path.join(project_root, '.git', 'refs', 'heads', 'master')):
+ pytest.skip('skipped test as project is not a git repo')
diff --git a/raven.egg-info/PKG-INFO b/raven.egg-info/PKG-INFO
index d14e2db..a9105da 100644
--- a/raven.egg-info/PKG-INFO
+++ b/raven.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: raven
-Version: 6.0.0
+Version: 6.3.0
Summary: Raven is a client for Sentry (https://getsentry.com)
Home-page: https://github.com/getsentry/raven-python
Author: Sentry
@@ -21,10 +21,13 @@ Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development
diff --git a/raven.egg-info/SOURCES.txt b/raven.egg-info/SOURCES.txt
index 2b43023..75d4d4d 100644
--- a/raven.egg-info/SOURCES.txt
+++ b/raven.egg-info/SOURCES.txt
@@ -1,6 +1,7 @@
LICENSE
MANIFEST.in
README.rst
+conftest.py
setup.cfg
setup.py
raven/__init__.py
@@ -26,6 +27,7 @@ raven/contrib/__init__.py
raven/contrib/async.py
raven/contrib/flask.py
raven/contrib/paste.py
+raven/contrib/awslambda/__init__.py
raven/contrib/bottle/__init__.py
raven/contrib/bottle/utils.py
raven/contrib/celery/__init__.py
@@ -64,6 +66,7 @@ raven/contrib/pylons/__init__.py
raven/contrib/tornado/__init__.py
raven/contrib/webpy/__init__.py
raven/contrib/webpy/utils.py
+raven/contrib/zconfig/__init__.py
raven/contrib/zerorpc/__init__.py
raven/contrib/zope/__init__.py
raven/contrib/zope/component.xml
@@ -114,12 +117,15 @@ tests/contrib/__init__.py
tests/contrib/test_celery.py
tests/contrib/async/__init__.py
tests/contrib/async/tests.py
+tests/contrib/awslambda/conftest.py
+tests/contrib/awslambda/test_lambda.py
tests/contrib/bottle/__init__.py
tests/contrib/bottle/tests.py
tests/contrib/django/__init__.py
tests/contrib/django/api.py
tests/contrib/django/middleware.py
tests/contrib/django/models.py
+tests/contrib/django/settings.py
tests/contrib/django/test_resolver.py
tests/contrib/django/test_tastypie.py
tests/contrib/django/tests.py
@@ -139,6 +145,8 @@ tests/contrib/tornado/__init__.py
tests/contrib/tornado/tests.py
tests/contrib/webpy/__init__.py
tests/contrib/webpy/tests.py
+tests/contrib/zconfig/__init__.py
+tests/contrib/zconfig/tests.py
tests/contrib/zerorpc/__init__.py
tests/contrib/zerorpc/tests.py
tests/events/__init__.py
diff --git a/raven.egg-info/requires.txt b/raven.egg-info/requires.txt
index 2df2934..b15bd57 100644
--- a/raven.egg-info/requires.txt
+++ b/raven.egg-info/requires.txt
@@ -1,3 +1,5 @@
+
+[:python_version<"3.2"]
contextlib2
[flask]
@@ -8,19 +10,26 @@ blinker>=1.1
bottle
celery>=2.5
exam>=0.5.2
-flake8>=2.6,<2.7
+flake8==3.5.0
logbook
mock
nose
pycodestyle
pytz
-pytest>=3.0.0,<3.1.0
-pytest-timeout==0.4
+pytest<3.3.0,>=3.2.0
+pytest-timeout==1.2.0
+pytest-xdist==1.18.2
+pytest-pythonpath==0.7.1
+pytest-sugar==0.8
+pytest-assume
+pytest-cov
+pytest-flake8==0.9
requests
tornado>=4.1
webob
webtest
anyjson
+ZConfig
Flask>=0.8
blinker>=1.1
Flask-Login>=0.2.0
diff --git a/raven/__init__.py b/raven/__init__.py
index 550b8a8..1e8b754 100644
--- a/raven/__init__.py
+++ b/raven/__init__.py
@@ -12,7 +12,7 @@ import os.path
__all__ = ('VERSION', 'Client', 'get_version')
-VERSION = '6.0.0'
+VERSION = '6.3.0'
def _get_git_revision(path):
diff --git a/raven/base.py b/raven/base.py
index 6fadbce..652e5e4 100644
--- a/raven/base.py
+++ b/raven/base.py
@@ -17,6 +17,8 @@ import uuid
import warnings
from datetime import datetime
+from inspect import isclass
+from random import Random
from types import FunctionType
from threading import local
@@ -60,6 +62,9 @@ SDK_VALUE = {
# singleton for the client
Raven = None
+if sys.version_info >= (3, 2):
+ basestring = str
+
def get_excepthook_client():
hook = sys.excepthook
@@ -139,6 +144,7 @@ class Client(object):
>>> ident = client.get_ident(client.captureException())
>>> print "Exception caught; reference is %s" % ident
"""
+
logger = logging.getLogger('raven')
protocol_version = '6'
@@ -146,7 +152,8 @@ class Client(object):
def __init__(self, dsn=None, raise_send_errors=False, transport=None,
install_sys_hook=True, install_logging_hook=True,
- hook_libraries=None, enable_breadcrumbs=True, **options):
+ hook_libraries=None, enable_breadcrumbs=True,
+ _random_seed=None, **options):
global Raven
o = options
@@ -191,12 +198,18 @@ class Client(object):
self.environment = o.get('environment') or None
self.release = o.get('release') or os.environ.get('HEROKU_SLUG_COMMIT')
self.repos = self._format_repos(o.get('repos'))
+ self.sample_rate = (
+ o.get('sample_rate')
+ if o.get('sample_rate') is not None
+ else 1
+ )
self.transaction = TransactionStack()
-
self.ignore_exceptions = set(o.get('ignore_exceptions') or ())
self.module_cache = ModuleProxyCache()
+ self._random = Random(_random_seed)
+
if not self.is_enabled():
self.logger.info(
'Raven is not configured (logging is disabled). Please see the'
@@ -362,7 +375,6 @@ class Client(object):
The result of ``build_msg`` should be a standardized dict, with
all default values available.
"""
-
# create ID client-side so that it can be passed to application
event_id = uuid.uuid4().hex
@@ -548,13 +560,15 @@ class Client(object):
Update the tags context for future events.
>>> client.tags_context({'version': '1.0'})
+
"""
return self.context.merge({
'tags': data,
})
def capture(self, event_type, data=None, date=None, time_spent=None,
- extra=None, stack=None, tags=None, **kwargs):
+ extra=None, stack=None, tags=None, sample_rate=None,
+ **kwargs):
"""
Captures and processes an event and pipes it off to SentryClient.send.
@@ -602,9 +616,9 @@ class Client(object):
:param extra: a dictionary of additional standard metadata
:param stack: a stacktrace for the event
:param tags: dict of extra tags
+ :param sample_rate: a float in the range [0, 1] to sample this message
:return: a tuple with a 32-length string identifying this event
"""
-
if not self.is_enabled():
return
@@ -623,7 +637,12 @@ class Client(object):
event_type, data, date, time_spent, extra, stack, tags=tags,
**kwargs)
- self.send(**data)
+ # should this event be sampled?
+ if sample_rate is None:
+ sample_rate = self.sample_rate
+
+ if self._random.random() < sample_rate:
+ self.send(**data)
self._local_state.last_event_id = data['event_id']
@@ -641,7 +660,7 @@ class Client(object):
for frame in data['stacktrace']['frames']:
yield frame
if 'exception' in data:
- for frame in data['exception']['values'][-1]['stacktrace']['frames']:
+ for frame in data['exception']['values'][-1]['stacktrace'].get('frames', []):
yield frame
def _successful_send(self):
@@ -675,7 +694,7 @@ class Client(object):
output = [message]
if 'exception' in data and 'stacktrace' in data['exception']['values'][-1]:
# try to reconstruct a reasonable version of the exception
- for frame in data['exception']['values'][-1]['stacktrace']['frames']:
+ for frame in data['exception']['values'][-1]['stacktrace'].get('frames', []):
output.append(' File "%(fn)s", line %(lineno)s, in %(func)s' % {
'fn': frame.get('filename', 'unknown_filename'),
'lineno': frame.get('lineno', -1),
@@ -802,12 +821,19 @@ class Client(object):
exc_type = exc_info[0]
exc_name = '%s.%s' % (exc_type.__module__, exc_type.__name__)
exclusions = self.ignore_exceptions
+ string_exclusions = (e for e in exclusions if isinstance(e, basestring))
+ wildcard_exclusions = (e for e in string_exclusions if e.endswith('*'))
+ class_exclusions = (e for e in exclusions if isclass(e))
- if exc_type.__name__ in exclusions:
+ if exc_type in exclusions:
+ return False
+ elif exc_type.__name__ in exclusions:
return False
elif exc_name in exclusions:
return False
- elif any(exc_name.startswith(e[:-1]) for e in exclusions if e.endswith('*')):
+ elif any(issubclass(exc_type, e) for e in class_exclusions):
+ return False
+ elif any(exc_name.startswith(e[:-1]) for e in wildcard_exclusions):
return False
return True
@@ -876,7 +902,8 @@ class Client(object):
return self.context(**kwargs)
def captureBreadcrumb(self, *args, **kwargs):
- """Records a breadcrumb with the current context. They will be
+ """
+ Records a breadcrumb with the current context. They will be
sent with the next event.
"""
# Note: framework integration should not call this method but
@@ -896,6 +923,7 @@ class Client(object):
class DummyClient(Client):
- "Sends messages into an empty void"
+ """Sends messages into an empty void."""
+
def send(self, **kwargs):
return None
diff --git a/raven/breadcrumbs.py b/raven/breadcrumbs.py
index 812e96f..3980b88 100644
--- a/raven/breadcrumbs.py
+++ b/raven/breadcrumbs.py
@@ -1,5 +1,6 @@
from __future__ import absolute_import
+import os
import time
import logging
from types import FunctionType
@@ -154,6 +155,12 @@ def _wrap_logging_method(meth, level=None):
code = get_code(func)
+ logging_srcfile = logging._srcfile
+ if logging_srcfile is None:
+ logging_srcfile = os.path.normpath(
+ logging.currentframe.__code__.co_filename
+ )
+
# This requires a bit of explanation why we're doing this. Due to how
# logging itself works we need to pretend that the method actually was
# created within the logging module. There are a few ways to detect
@@ -183,7 +190,7 @@ def _wrap_logging_method(meth, level=None):
'args': ', '.join(args),
'fwd': fwd,
'level': level,
- }, logging._srcfile, 'exec'), logging.__dict__, ns)
+ }, logging_srcfile, 'exec'), logging.__dict__, ns)
new_func = ns['factory'](meth, _record_log_breadcrumb)
new_func.__doc__ = func.__doc__
diff --git a/raven/conf/defaults.py b/raven/conf/defaults.py
index 073e01a..3505a09 100644
--- a/raven/conf/defaults.py
+++ b/raven/conf/defaults.py
@@ -15,7 +15,7 @@ import socket
ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), os.pardir))
-TIMEOUT = 1
+TIMEOUT = 5
# TODO: this is specific to Django
CLIENT = 'raven.contrib.django.DjangoClient'
diff --git a/raven/contrib/async.py b/raven/contrib/async.py
index 4f94abb..251e4f8 100644
--- a/raven/contrib/async.py
+++ b/raven/contrib/async.py
@@ -17,6 +17,7 @@ class AsyncClient(Client):
"""
This client uses a single background thread to dispatch errors.
"""
+
def __init__(self, worker=None, *args, **kwargs):
warnings.warn('AsyncClient is deprecated. Use the threaded+http transport instead.', DeprecationWarning)
self.worker = worker or AsyncWorker()
diff --git a/raven/contrib/awslambda/__init__.py b/raven/contrib/awslambda/__init__.py
new file mode 100644
index 0000000..7d6f2b8
--- /dev/null
+++ b/raven/contrib/awslambda/__init__.py
@@ -0,0 +1,173 @@
+"""
+raven.contrib.awslambda
+~~~~~~~~~~~~~~~~~~~~
+
+Raven wrapper for AWS Lambda handlers.
+
+:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:license: BSD, see LICENSE for more details.
+"""
+# flake8: noqa
+
+from __future__ import absolute_import
+
+import os
+import logging
+import functools
+from types import FunctionType
+
+from raven.base import Client
+from raven.transport.http import HTTPTransport
+
+logger = logging.getLogger('sentry.errors.client')
+
+
+def get_default_tags():
+ return {
+ 'lambda': 'AWS_LAMBDA_FUNCTION_NAME',
+ 'version': 'AWS_LAMBDA_FUNCTION_VERSION',
+ 'memory_size': 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE',
+ 'log_group': 'AWS_LAMBDA_LOG_GROUP_NAME',
+ 'log_stream': 'AWS_LAMBDA_LOG_STREAM_NAME',
+ 'region': 'AWS_REGION'
+ }
+
+
+class LambdaClient(Client):
+ """
+ Raven decorator for AWS Lambda.
+
+ By default, the lambda integration will capture unhandled exceptions and instrument logging.
+
+ Usage:
+
+ >>> from raven.contrib.awslambda import LambdaClient
+ >>>
+ >>>
+ >>> client = LambdaClient()
+ >>>
+ >>> @client.capture_exceptions
+ >>> def handler(event, context):
+ >>> ...
+ >>> raise Exception('I will be sent to sentry!')
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ transport = kwargs.get('transport', HTTPTransport)
+ super(LambdaClient, self).__init__(*args, transport=transport, **kwargs)
+
+ def capture(self, *args, **kwargs):
+ if 'data' not in kwargs:
+ kwargs['data'] = data = {}
+ else:
+ data = kwargs['data']
+ event = kwargs.get('event', None)
+ context = kwargs.get('context', None)
+ user_info = self._get_user_interface(event)
+ if user_info:
+ data.update(user_info)
+ if event:
+ http_info = self._get_http_interface(event)
+ if http_info:
+ data.update(http_info)
+ data['extra'] = self._get_extra_data(event, context)
+ return super(LambdaClient, self).capture(*args, **kwargs)
+
+ def build_msg(self, *args, **kwargs):
+
+ data = super(LambdaClient, self).build_msg(*args, **kwargs)
+ for option, default in get_default_tags().items():
+ data['tags'].setdefault(option, os.environ.get(default))
+ data.setdefault('release', os.environ.get('SENTRY_RELEASE'))
+ data.setdefault('environment', os.environ.get('SENTRY_ENVIRONMENT'))
+ return data
+
+ def capture_exceptions(self, f=None, exceptions=None): # TODO: Ash fix kwargs in base
+ """
+ Wrap a function or code block in try/except and automatically call
+ ``.captureException`` if it raises an exception, then the exception
+ is reraised.
+
+ By default, it will capture ``Exception``
+
+ >>> @client.capture_exceptions
+ >>> def foo():
+ >>> raise Exception()
+
+ >>> with client.capture_exceptions():
+ >>> raise Exception()
+
+ You can also specify exceptions to be caught specifically
+
+ >>> @client.capture_exceptions((IOError, LookupError))
+ >>> def bar():
+ >>> ...
+
+ ``kwargs`` are passed through to ``.captureException``.
+ """
+ if not isinstance(f, FunctionType):
+ # when the decorator has args which is not a function we except
+ # f to be the exceptions tuple
+ return functools.partial(self.capture_exceptions, exceptions=f)
+
+ exceptions = exceptions or (Exception,)
+
+ @functools.wraps(f)
+ def wrapped(event, context, *args, **kwargs):
+ try:
+ return f(event, context, *args, **kwargs)
+ except exceptions:
+ self.captureException(event=event, context=context, **kwargs)
+ self.context.clear()
+ raise
+ return wrapped
+
+ @staticmethod
+ def _get_user_interface(event):
+ if event.get('requestContext'):
+ identity = event['requestContext']['identity']
+ if identity:
+ user = {
+ 'id': identity.get('cognitoIdentityId', None) or identity.get('user', None),
+ 'username': identity.get('user', None),
+ 'ip_address': identity.get('sourceIp', None),
+ 'cognito_identity_pool_id': identity.get('cognitoIdentityPoolId', None),
+ 'cognito_authentication_type': identity.get('cognitoAuthenticationType', None),
+ 'user_agent': identity.get('userAgent')
+ }
+ return {'user': user}
+
+ @staticmethod
+ def _get_http_interface(event):
+ if event.get('path') and event.get('httpMethod'):
+ request = {
+ "url": event.get('path'),
+ "method": event.get('httpMethod'),
+ "query_string": event.get('queryStringParameters', None),
+ "headers": event.get('headers', None) or [],
+ }
+ return {'request': request}
+
+ @staticmethod
+ def _get_extra_data(event, context):
+ extra_context = {
+ 'event': event,
+ 'aws_request_id': context.aws_request_id,
+ 'context': vars(context),
+ }
+
+ if context.client_context:
+ extra_context['client_context'] = {
+ 'client.installation_id': context.client_context.client.installation_id,
+ 'client.app_title': context.client_context.client.app_title,
+ 'client.app_version_name': context.client_context.client.app_version_name,
+ 'client.app_version_code': context.client_context.client.app_version_code,
+ 'client.app_package_name': context.client_context.client.app_package_name,
+ 'custom': context.client_context.custom,
+ 'env': context.client_context.env,
+ }
+ return extra_context
+
+
+
diff --git a/raven/contrib/bottle/__init__.py b/raven/contrib/bottle/__init__.py
index d9f845b..de82c85 100644
--- a/raven/contrib/bottle/__init__.py
+++ b/raven/contrib/bottle/__init__.py
@@ -37,6 +37,7 @@ class Sentry(object):
>>> sentry.captureMessage('hello, world!')
"""
+
def __init__(self, app, client, logging=False):
self.app = app
self.client = client
diff --git a/raven/contrib/celery/__init__.py b/raven/contrib/celery/__init__.py
index edf2c42..d4fd759 100644
--- a/raven/contrib/celery/__init__.py
+++ b/raven/contrib/celery/__init__.py
@@ -73,7 +73,7 @@ class SentryCeleryHandler(object):
# This signal is fired inside the stack so let raven do its magic
if isinstance(einfo.exception, SoftTimeLimitExceeded):
- fingerprint = ['celery', 'SoftTimeLimitExceeded', sender]
+ fingerprint = ['celery', 'SoftTimeLimitExceeded', getattr(sender, 'name', sender)]
else:
fingerprint = None
@@ -88,7 +88,9 @@ class SentryCeleryHandler(object):
)
def handle_task_prerun(self, sender, task_id, task, **kw):
+ self.client.context.activate()
self.client.transaction.push(task.name)
def handle_task_postrun(self, sender, task_id, task, **kw):
self.client.transaction.pop(task.name)
+ self.client.context.clear()
diff --git a/raven/contrib/django/__init__.py b/raven/contrib/django/__init__.py
index 23036a6..4ce0b95 100644
--- a/raven/contrib/django/__init__.py
+++ b/raven/contrib/django/__init__.py
@@ -9,5 +9,4 @@ from __future__ import absolute_import
default_app_config = 'raven.contrib.django.apps.RavenConfig'
-
from .client import DjangoClient # NOQA
diff --git a/raven/contrib/django/apps.py b/raven/contrib/django/apps.py
index 7aaf4ff..db33b38 100644
--- a/raven/contrib/django/apps.py
+++ b/raven/contrib/django/apps.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
+
from django.apps import AppConfig
@@ -7,3 +8,7 @@ class RavenConfig(AppConfig):
name = 'raven.contrib.django'
label = 'raven_contrib_django'
verbose_name = 'Raven'
+
+ def ready(self):
+ from .models import initialize
+ initialize()
diff --git a/raven/contrib/django/client.py b/raven/contrib/django/client.py
index 6f81a62..a161342 100644
--- a/raven/contrib/django/client.py
+++ b/raven/contrib/django/client.py
@@ -12,6 +12,7 @@ from __future__ import absolute_import
import time
import logging
+from django import VERSION as DJANGO_VERSION
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
from django.http import HttpRequest
@@ -30,13 +31,21 @@ from raven.contrib.django.utils import get_data_from_template, get_host
from raven.contrib.django.middleware import SentryMiddleware
from raven.utils.compat import string_types, binary_type, iterlists
from raven.contrib.django.resolver import RouteResolver
-from raven.utils.wsgi import get_headers, get_environ
+from raven.utils.wsgi import get_headers, get_environ, get_client_ip
from raven.utils import once
from raven import breadcrumbs
__all__ = ('DjangoClient',)
+if DJANGO_VERSION < (1, 10):
+ def is_authenticated(request_user):
+ return request_user.is_authenticated()
+else:
+ def is_authenticated(request_user):
+ return request_user.is_authenticated
+
+
class _FormatConverter(object):
def __init__(self, param_mapping):
@@ -142,19 +151,19 @@ class DjangoClient(Client):
def install_sql_hook(self):
install_sql_hook()
- def get_user_info(self, user):
- try:
- if hasattr(user, 'is_authenticated'):
- # is_authenticated was a method in Django < 1.10
- if callable(user.is_authenticated):
- authenticated = user.is_authenticated()
- else:
- authenticated = user.is_authenticated
- if not authenticated:
- return None
+ def get_user_info(self, request):
- user_info = {}
+ user_info = {
+ 'ip_address': get_client_ip(request.META),
+ }
+ user = getattr(request, 'user', None)
+ if user is None:
+ return user_info
+ try:
+ authenticated = is_authenticated(user)
+ if not authenticated:
+ return user_info
user_info['id'] = user.pk
if hasattr(user, 'email'):
@@ -164,22 +173,18 @@ class DjangoClient(Client):
user_info['username'] = user.get_username()
elif hasattr(user, 'username'):
user_info['username'] = user.username
-
- return user_info
except Exception:
# We expect that user objects can be somewhat broken at times
# and try to just handle as much as possible and ignore errors
... 1803 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/raven.git
More information about the Python-modules-commits
mailing list