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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ pip-log.txt
.eggs
venv
.vscode/tags
.pytest_cache
2 changes: 2 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest
pytest-django
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
DJANGO_SETTINGS_MODULE = tests.django.myapp.settings
27 changes: 26 additions & 1 deletion sentry_minimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,32 @@ def get_current_hub():


try:
from sentry_sdk import Hub, Scope
from sentry_sdk.hub import Hub
from sentry_sdk.scope import Scope
except ImportError:
class Hub(object):
current = main = None

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

def set_tag(self, key, value):
pass

def remove_tag(self, key):
pass

def set_context(self, key, value):
pass

def remove_context(self, key):
pass

def set_extra(self, key, value):
pass

def remove_extra(self, key):
pass

def clear(self):
pass
21 changes: 11 additions & 10 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
from contextlib import contextmanager

import sentry_minimal

from .hub import Hub
from .scope import Scope
from .client import Client


__all__ = ['Hub', 'Client', 'init'] + sentry_minimal.__all__


for _key in sentry_minimal.__all__:
globals()[_key] = getattr(sentry_minimal, _key)
globals()[_key].__module__ = __name__
del _key


class _InitGuard(object):

Expand All @@ -35,3 +25,14 @@ def init(*args, **kwargs):
if client.dsn is not None:
Hub.main.bind_client(client)
return _InitGuard(client)


import sentry_minimal

__all__ = ['Hub', 'Scope', 'Client', 'init'] + sentry_minimal.__all__


for _key in sentry_minimal.__all__:
globals()[_key] = getattr(sentry_minimal, _key)
globals()[_key].__module__ = __name__
del _key
5 changes: 4 additions & 1 deletion sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ class _ScopeManager(object):
def __init__(self, hub):
self._hub = hub

def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, tb):
self._hub.stack.pop()
self._hub._stack.pop()


class Hub(with_metaclass(HubMeta)):
Expand Down
Empty file.
103 changes: 103 additions & 0 deletions sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from threading import Lock, local

from django.apps import AppConfig
from django.conf import settings
from django.core import signals
from django.urls import resolve

from sentry_sdk import get_current_hub, configure_scope, capture_exception


try:
# Django >= 1.10
from django.utils.deprecation import MiddlewareMixin
except ImportError:
# Not required for Django <= 1.9, see:
# https://docs.djangoproject.com/en/1.10/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware
MiddlewareMixin = object

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

_request_scope = local()


# request_started (or any other signal) cannot be used because the request is
# not yet available
class SentryMiddleware(MiddlewareMixin):
def process_view(self, request, func, args, kwargs):
try:
with configure_scope() as scope:
scope.transaction = _get_transaction_from_request(request)
except Exception:
capture_exception()


def _request_started(*args, **kwargs):
assert getattr(_request_scope, 'manager', None) is None, 'race condition'
_request_scope.manager = get_current_hub().push_scope().__enter__()


def _request_finished(*args, **kwargs):
assert getattr(_request_scope, 'manager', None) is not None, 'race condition'
_request_scope.manager.__exit__(None, None, None)
_request_scope.manager = None


def _got_request_exception(request=None, **kwargs):
capture_exception()


MIDDLEWARE_NAME = 'sentry_sdk.integrations.django.SentryMiddleware'

CONFLICTING_MIDDLEWARE = (
'raven.contrib.django.middleware.SentryMiddleware',
'raven.contrib.django.middleware.SentryLogMiddleware'
) + (MIDDLEWARE_NAME,)

_installer_lock = Lock()
_installed = False


def initialize():
global _installed
with _installer_lock:
if _installed:
return
_initialize_impl()
_installed = True


def _initialize_impl():
# default settings.MIDDLEWARE is None
if getattr(settings, 'MIDDLEWARE', None):
middleware_attr = 'MIDDLEWARE'
else:
middleware_attr = 'MIDDLEWARE_CLASSES'

# make sure to get an empty tuple when attr is None
middleware = getattr(settings, middleware_attr, ()) or ()
conflicts = set(CONFLICTING_MIDDLEWARE).intersection(set(middleware))
if conflicts:
raise RuntimeError('Other sentry-middleware already registered: %s' %
conflicts)

setattr(settings,
middleware_attr,
[MIDDLEWARE_NAME] + list(middleware))

signals.request_started.connect(_request_started)
signals.request_finished.connect(_request_finished)
signals.got_request_exception.connect(_got_request_exception)


default_app_config = 'sentry_sdk.integrations.django.SentryConfig'


class SentryConfig(AppConfig):
name = 'sentry_sdk.integrations.django'
label = 'sentry_sdk_integrations_django'
verbose_name = 'Sentry'

def ready(self):
initialize()
Empty file.
11 changes: 11 additions & 0 deletions sentry_sdk/integrations/django/templatetags/sentry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.template import Library

from sentry_sdk import get_current_hub

register = Library()

@register.simple_tag
def sentry_dsn():
if get_current_hub().client is not None:
return get_current_hub().client.dsn or ''
return ''
17 changes: 17 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python

from setuptools import setup, find_packages

setup(
name='sentry-sdk',
version='0.1.0',
author='Sentry',
author_email='[email protected]',
url='https://github.com/getsentry/sentry-sdk',
description='Python client for Sentry (https://getsentry.com)',
long_description=__doc__,
packages=find_packages(exclude=("tests", "tests.*",)),
zip_safe=False,
license='BSD',
install_requires=['urllib3', 'certifi']
)
Empty file added tests/__init__.py
Empty file.
Empty file added tests/django/__init__.py
Empty file.
Empty file.
130 changes: 130 additions & 0 deletions tests/django/myapp/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""
Django settings for myapp project.

Generated by 'django-admin startproject' using Django 2.0.7.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'u95e#xr$t3!vdux)fj11!*q*^w^^r#kiyrvt3kjui-t_k%m3op'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'sentry_sdk.integrations.django'
]

class TestMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
if 'middleware-exc' in request.path:
1/0
return get_response(request)


MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware'
]

ROOT_URLCONF = 'tests.django.myapp.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

WSGI_APPLICATION = 'tests.django.myapp.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
26 changes: 26 additions & 0 deletions tests/django/myapp/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""myapp URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path

from . import views

urlpatterns = [
path('self-check', views.self_check, name='self_check'),
path('view-exc', views.view_exc, name='view_exc'),
path('middleware-exc', views.view_exc, name='middleware_exc'),
path('get-dsn', views.get_dsn, name='get_dsn')
]
Loading