Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 8 additions & 63 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,70 +1,15 @@
language: python
sudo: false

matrix:
include:
- python: "2.7"
env: DJANGO_VERSION=1.4.*
- python: "2.7"
env: DJANGO_VERSION=1.5.*
- python: "2.7"
env: DJANGO_VERSION=1.6.*
- python: "2.7"
env: DJANGO_VERSION=1.7.*
- python: "2.7"
env: DJANGO_VERSION=1.8.*
- python: "2.7"
env: DJANGO_VERSION=1.9.*

- python: "3.4"
env: DJANGO_VERSION=1.5.*
- python: "3.4"
env: DJANGO_VERSION=1.6.*
- python: "3.4"
env: DJANGO_VERSION=1.7.*
- python: "3.4"
env: DJANGO_VERSION=1.8.*
- python: "3.4"
env: DJANGO_VERSION=1.9.*
- python: "3.4"
env: DJANGO_VERSION=2.0.*

- python: "3.5"
env: DJANGO_VERSION=1.8.*
- python: "3.5"
env: DJANGO_VERSION=1.9.*
- python: "3.5"
env: DJANGO_VERSION=2.0.*

- python: "3.6"
env: DJANGO_VERSION=1.8.*
- python: "3.6"
env: DJANGO_VERSION=1.9.*
- python: "3.6"
env: DJANGO_VERSION=2.0.*

- python: "3.6"
env: DJANGO_VERSION=1.8.*
- python: "3.6"
env: DJANGO_VERSION=1.9.*
- python: "3.6"
env: DJANGO_VERSION=2.0.*

- python: "3.7-dev"
env: DJANGO_VERSION=1.8.*
- python: "3.7-dev"
env: DJANGO_VERSION=1.9.*
- python: "3.7-dev"
env: DJANGO_VERSION=2.0.*
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
- "3.7-dev"

install:
- pip install -e .
- pip install -Ur dev-requirements.txt
- pip install -U "django==$DJANGO_VERSION"
- |
if [ "$DJANGO_VERSION" = 1.4.* ] || [ "$DJANGO_VERSION" = 1.5.* ] || [ "$DJANGO_VERSION" = 1.6.* ] || [ "$DJANGO_VERSION" = 1.7.* ] || [ "$DJANGO_VERSION" = 1.8.* ]; then
pip install 'pytest-django<3.0';
fi
- pip install tox

script:
- pytest # or py.test for Python versions 3.5 and below
- tox -e $(tox -l | grep $(echo $TRAVIS_PYTHON_VERSION | sed -e 's/[^0-9]//g') | tr '\n' ',')
2 changes: 2 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pytest
pytest-django
django
flask
flask-login
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[pytest]
DJANGO_SETTINGS_MODULE = tests.django.myapp.settings
DJANGO_SETTINGS_MODULE = tests.integrations.django.myapp.settings
2 changes: 1 addition & 1 deletion sentry_minimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class Hub(object):
current = main = None

class Scope(object):
fingerprint = transaction = user = None
fingerprint = transaction = user = request = None

def set_tag(self, key, value):
pass
Expand Down
7 changes: 7 additions & 0 deletions sentry_sdk/integrations/_wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def get_environ(environ):
"""
Returns our whitelisted environment variables.
"""
for key in ('REMOTE_ADDR', 'SERVER_NAME', 'SERVER_PORT'):
if key in environ:
yield key, environ[key]
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
MiddlewareMixin = object

def _get_transaction_from_request(request):
return resolve(request.path).func
return resolve(request.path).func.__name__

_request_scope = local()

Expand Down
111 changes: 111 additions & 0 deletions sentry_sdk/integrations/flask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from __future__ import absolute_import

from sentry_sdk import capture_exception, configure_scope, get_current_hub
from ._wsgi import get_environ

try:
from flask_login import current_user
except ImportError:
current_user = None

from flask import current_app, request, _app_ctx_stack
from flask.signals import appcontext_pushed, appcontext_popped, got_request_exception

class FlaskSentry(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)

def init_app(self, app):
if not hasattr(app, 'extensions'):
app.extensions = {}
elif app.extensions.get(__name__, None):
raise RuntimeError('Sentry registration is already registered')
app.extensions[__name__] = True

appcontext_pushed.connect(_push_appctx, sender=app)
app.teardown_appcontext(_pop_appctx)
got_request_exception.connect(_capture_exception, sender=app)
app.before_request(_before_request)


def _push_appctx(*args, **kwargs):
assert getattr(
_app_ctx_stack.top,
'_sentry_app_scope_manager',
None
) is None, 'race condition'
_app_ctx_stack.top._sentry_app_scope_manager = \
get_current_hub().push_scope().__enter__()


def _pop_appctx(exception):
assert getattr(
_app_ctx_stack.top,
'_sentry_app_scope_manager',
None
) is not None, 'race condition'
_app_ctx_stack.top._sentry_app_scope_manager.__exit__(None, None, None)
_app_ctx_stack.top._sentry_app_scope_manager = None


def _capture_exception(sender, exception, **kwargs):
capture_exception(exception)


def _before_request(*args, **kwargs):
assert getattr(
_app_ctx_stack.top,
'_sentry_app_scope_manager',
None
) is not None, 'scope push failed'

with configure_scope() as scope:
if request.url_rule:
scope.transaction = request.url_rule.endpoint

try:
scope.request = _get_request_info()
except Exception:
get_current_hub().capture_internal_exception()

try:
scope.user = _get_user_info()
except Exception:
raise
get_current_hub().capture_internal_exception()

def _get_request_info():
{
'url': '%s://%s%s' % (request.scheme, urlparts.host, request.path),
'query_string': urlparts.query,
'method': request.method,
'data': request.get_data(cache=True, as_text=True, parse_form_data=True),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure about this. The old integration didn't have any protection against too large requests either.

'headers': dict(request.headers),
'env': get_environ(request.environ)
}


def _get_user_info():
try:
ip_address = request.access_route[0]
except IndexError:
ip_address = request.remote_addr

user_info = {
'id': None,
'ip_address': ip_address
}

try:
user_info['id'] = current_user.get_id()
# TODO: more configurable user attrs here
except AttributeError:
# might happen if:
# - flask_login could not be imported
# - flask_login is not configured
# - no user is logged in
pass

return user_info
5 changes: 5 additions & 0 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ def _set_user(self, value):
user = property(fset=_set_user)
del _set_user

def _set_request(self, request):
self._data['request'] = request
request = property(fset=_set_request)
del _set_request

def set_tag(self, key, value):
self._data.setdefault('tags', {})[key] = value

Expand Down
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@
packages=find_packages(exclude=("tests", "tests.*",)),
zip_safe=False,
license='BSD',
install_requires=['urllib3', 'certifi']
install_requires=['urllib3', 'certifi'],
extras_require={
'flask': ['flask>=0.8', 'blinker>=1.1']
}
)
21 changes: 0 additions & 21 deletions tests/conftest.py

This file was deleted.

File renamed without changes.
50 changes: 50 additions & 0 deletions tests/integrations/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pytest
import sentry_sdk
from sentry_sdk.client import Client, Transport

class TestClient(Client):
def __init__(self):
pass

def capture_internal_exception(self, error=None):
if not error:
raise
raise error

dsn = 'LOL'
options = {'with_locals': False, 'release': 'fake_release', 'environment': 'fake_environment', 'server_name': 'fake_servername'}
_transport = None


class TestTransport(Transport):
def __init__(self):
pass

def start(self):
pass

def close(self):
pass

test_client = TestClient()

sentry_sdk.init()
sentry_sdk.get_current_hub().bind_client(test_client)


@pytest.fixture
def capture_exceptions(monkeypatch):
errors = []
def capture_exception(error=None):
errors.append(error or sys.exc_info()[1])

monkeypatch.setattr(sentry_sdk.Hub.current, 'capture_exception', capture_exception)
return errors


@pytest.fixture
def capture_events(monkeypatch):
events = []
monkeypatch.setattr(test_client, '_transport', TestTransport())
monkeypatch.setattr(test_client._transport, 'capture_event', events.append)
return events
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ def process_response(self, request, response):
return response

MIDDLEWARE_CLASSES = [
'tests.django.myapp.settings.TestMiddleware'
'tests.integrations.django.myapp.settings.TestMiddleware'
]

if MiddlewareMixin is not object:
MIDDLEWARE = MIDDLEWARE_CLASSES


ROOT_URLCONF = 'tests.django.myapp.urls'
ROOT_URLCONF = 'tests.integrations.django.myapp.urls'

TEMPLATES = [
{
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

def self_check(request):
with sentry_sdk.configure_scope() as scope:
assert scope._data['transaction'] == self_check
assert scope._data['transaction'] == 'self_check'
return HttpResponse("ok")


Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import sys

import pytest

django = pytest.importorskip('django')


from django.test import Client
from django.test.utils import setup_test_environment
try:
Expand Down Expand Up @@ -46,16 +48,5 @@ def test_middleware_exceptions(client, capture_exceptions):


def test_get_dsn(request, client):
class Client(SentryClient):
def __init__(self):
pass

dsn = 'LOL'
options = {'with_locals': False}
_transport = None

Hub.current.bind_client(Client())
request.addfinalizer(lambda: Hub.current.bind_client(None))

response = client.get(reverse('get_dsn'))
assert response.content == b'LOL!'
Loading