Skip to content

Commit

Permalink
Merge branch 'master' into explicit-export
Browse files Browse the repository at this point in the history
  • Loading branch information
sentrivana authored Aug 13, 2024
2 parents fc39e45 + 17a6cf0 commit 743eeb6
Show file tree
Hide file tree
Showing 34 changed files with 1,144 additions and 87 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-ai.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test AI
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-aws-lambda.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test AWS Lambda
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-cloud-computing.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Cloud Computing
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-common.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Common
on:
push:
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/test-integrations-data-processing.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Data Processing
on:
push:
Expand Down Expand Up @@ -57,10 +59,18 @@ jobs:
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-celery-latest"
- name: Test dramatiq latest
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-dramatiq-latest"
- name: Test huey latest
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-huey-latest"
- name: Test ray latest
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-ray-latest"
- name: Test rq latest
run: |
set -x # print commands that are executed
Expand Down Expand Up @@ -125,10 +135,18 @@ jobs:
run: |
set -x # print commands that are executed
./scripts/runtox.sh --exclude-latest "py${{ matrix.python-version }}-celery"
- name: Test dramatiq pinned
run: |
set -x # print commands that are executed
./scripts/runtox.sh --exclude-latest "py${{ matrix.python-version }}-dramatiq"
- name: Test huey pinned
run: |
set -x # print commands that are executed
./scripts/runtox.sh --exclude-latest "py${{ matrix.python-version }}-huey"
- name: Test ray pinned
run: |
set -x # print commands that are executed
./scripts/runtox.sh --exclude-latest "py${{ matrix.python-version }}-ray"
- name: Test rq pinned
run: |
set -x # print commands that are executed
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-databases.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Databases
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-graphql.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test GraphQL
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-miscellaneous.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Miscellaneous
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-networking.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Networking
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-web-frameworks-1.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Web Frameworks 1
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-web-frameworks-2.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py
name: Test Web Frameworks 2
on:
push:
Expand Down
2 changes: 2 additions & 0 deletions scripts/split-tox-gh-actions/split-tox-gh-actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@
"arq",
"beam",
"celery",
"dramatiq",
"huey",
"ray",
"rq",
"spark",
],
Expand Down
3 changes: 3 additions & 0 deletions scripts/split-tox-gh-actions/templates/base.jinja
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Do not edit this file. This file is generated automatically by executing
# python scripts/split-tox-gh-actions/split-tox-gh-actions.py

{% with lowercase_group=group | replace(" ", "_") | lower %}
name: Test {{ group }}

Expand Down
17 changes: 9 additions & 8 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
from collections.abc import Mapping
from datetime import datetime, timezone
from importlib import import_module
from typing import cast

from sentry_sdk._compat import PY37, check_uwsgi_thread_support
from sentry_sdk.utils import (
capture_internal_exceptions,
current_stacktrace,
disable_capture_event,
format_timestamp,
get_sdk_name,
get_type_name,
Expand Down Expand Up @@ -525,10 +525,14 @@ def _prepare_event(
# Postprocess the event here so that annotated types do
# generally not surface in before_send
if event is not None:
event = serialize(
event,
max_request_body_size=self.options.get("max_request_body_size"),
max_value_length=self.options.get("max_value_length"),
event = cast(
"Event",
serialize(
cast("Dict[str, Any]", event),
max_request_body_size=self.options.get("max_request_body_size"),
max_value_length=self.options.get("max_value_length"),
custom_repr=self.options.get("custom_repr"),
),
)

before_send = self.options["before_send"]
Expand Down Expand Up @@ -726,9 +730,6 @@ def capture_event(
:returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help.
"""
if disable_capture_event.get(False):
return None

if hint is None:
hint = {}
event_id = event.get("event_id")
Expand Down
3 changes: 3 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ class OP:
QUEUE_TASK_RQ = "queue.task.rq"
QUEUE_SUBMIT_HUEY = "queue.submit.huey"
QUEUE_TASK_HUEY = "queue.task.huey"
QUEUE_SUBMIT_RAY = "queue.submit.ray"
QUEUE_TASK_RAY = "queue.task.ray"
SUBPROCESS = "subprocess"
SUBPROCESS_WAIT = "subprocess.wait"
SUBPROCESS_COMMUNICATE = "subprocess.communicate"
Expand Down Expand Up @@ -539,6 +541,7 @@ def __init__(
spotlight=None, # type: Optional[Union[bool, str]]
cert_file=None, # type: Optional[str]
key_file=None, # type: Optional[str]
custom_repr=None, # type: Optional[Callable[..., Optional[str]]]
):
# type: (...) -> None
pass
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.sessions import auto_session_tracking_scope
from sentry_sdk.sessions import track_session
from sentry_sdk.integrations._wsgi_common import (
_filter_headers,
request_body_within_bounds,
Expand Down Expand Up @@ -105,7 +105,7 @@ async def sentry_app_handle(self, request, *args, **kwargs):
weak_request = weakref.ref(request)

with sentry_sdk.isolation_scope() as scope:
with auto_session_tracking_scope(scope, session_mode="request"):
with track_session(scope, session_mode="request"):
# Scope data will not leak between requests because aiohttp
# create a task to wrap each request.
scope.generate_propagation_context()
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
_get_request_data,
_get_url,
)
from sentry_sdk.sessions import auto_session_tracking_scope
from sentry_sdk.sessions import track_session
from sentry_sdk.tracing import (
SOURCE_FOR_STYLE,
TRANSACTION_SOURCE_ROUTE,
Expand Down Expand Up @@ -169,7 +169,7 @@ async def _run_app(self, scope, receive, send, asgi_version):
_asgi_middleware_applied.set(True)
try:
with sentry_sdk.isolation_scope() as sentry_scope:
with auto_session_tracking_scope(sentry_scope, session_mode="request"):
with track_session(sentry_scope, session_mode="request"):
sentry_scope.clear_breadcrumbs()
sentry_scope._name = "asgi"
processor = partial(self.event_processor, asgi_scope=scope)
Expand Down
167 changes: 167 additions & 0 deletions sentry_sdk/integrations/dramatiq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import json

import sentry_sdk
from sentry_sdk.integrations import Integration
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.integrations._wsgi_common import request_body_within_bounds
from sentry_sdk.utils import (
AnnotatedValue,
capture_internal_exceptions,
event_from_exception,
)

from dramatiq.broker import Broker # type: ignore
from dramatiq.message import Message # type: ignore
from dramatiq.middleware import Middleware, default_middleware # type: ignore
from dramatiq.errors import Retry # type: ignore

if TYPE_CHECKING:
from typing import Any, Callable, Dict, Optional, Union
from sentry_sdk._types import Event, Hint


class DramatiqIntegration(Integration):
"""
Dramatiq integration for Sentry
Please make sure that you call `sentry_sdk.init` *before* initializing
your broker, as it monkey patches `Broker.__init__`.
This integration was originally developed and maintained
by https://github.com/jacobsvante and later donated to the Sentry
project.
"""

identifier = "dramatiq"

@staticmethod
def setup_once():
# type: () -> None
_patch_dramatiq_broker()


def _patch_dramatiq_broker():
# type: () -> None
original_broker__init__ = Broker.__init__

def sentry_patched_broker__init__(self, *args, **kw):
# type: (Broker, *Any, **Any) -> None
integration = sentry_sdk.get_client().get_integration(DramatiqIntegration)

try:
middleware = kw.pop("middleware")
except KeyError:
# Unfortunately Broker and StubBroker allows middleware to be
# passed in as positional arguments, whilst RabbitmqBroker and
# RedisBroker does not.
if len(args) == 1:
middleware = args[0]
args = [] # type: ignore
else:
middleware = None

if middleware is None:
middleware = list(m() for m in default_middleware)
else:
middleware = list(middleware)

if integration is not None:
middleware = [m for m in middleware if not isinstance(m, SentryMiddleware)]
middleware.insert(0, SentryMiddleware())

kw["middleware"] = middleware
original_broker__init__(self, *args, **kw)

Broker.__init__ = sentry_patched_broker__init__


class SentryMiddleware(Middleware): # type: ignore[misc]
"""
A Dramatiq middleware that automatically captures and sends
exceptions to Sentry.
This is automatically added to every instantiated broker via the
DramatiqIntegration.
"""

def before_process_message(self, broker, message):
# type: (Broker, Message) -> None
integration = sentry_sdk.get_client().get_integration(DramatiqIntegration)
if integration is None:
return

message._scope_manager = sentry_sdk.new_scope()
message._scope_manager.__enter__()

scope = sentry_sdk.get_current_scope()
scope.transaction = message.actor_name
scope.set_extra("dramatiq_message_id", message.message_id)
scope.add_event_processor(_make_message_event_processor(message, integration))

def after_process_message(self, broker, message, *, result=None, exception=None):
# type: (Broker, Message, Any, Optional[Any], Optional[Exception]) -> None
integration = sentry_sdk.get_client().get_integration(DramatiqIntegration)
if integration is None:
return

actor = broker.get_actor(message.actor_name)
throws = message.options.get("throws") or actor.options.get("throws")

try:
if (
exception is not None
and not (throws and isinstance(exception, throws))
and not isinstance(exception, Retry)
):
event, hint = event_from_exception(
exception,
client_options=sentry_sdk.get_client().options,
mechanism={
"type": DramatiqIntegration.identifier,
"handled": False,
},
)
sentry_sdk.capture_event(event, hint=hint)
finally:
message._scope_manager.__exit__(None, None, None)


def _make_message_event_processor(message, integration):
# type: (Message, DramatiqIntegration) -> Callable[[Event, Hint], Optional[Event]]

def inner(event, hint):
# type: (Event, Hint) -> Optional[Event]
with capture_internal_exceptions():
DramatiqMessageExtractor(message).extract_into_event(event)

return event

return inner


class DramatiqMessageExtractor(object):
def __init__(self, message):
# type: (Message) -> None
self.message_data = dict(message.asdict())

def content_length(self):
# type: () -> int
return len(json.dumps(self.message_data))

def extract_into_event(self, event):
# type: (Event) -> None
client = sentry_sdk.get_client()
if not client.is_active():
return

contexts = event.setdefault("contexts", {})
request_info = contexts.setdefault("dramatiq", {})
request_info["type"] = "dramatiq"

data = None # type: Optional[Union[AnnotatedValue, Dict[str, Any]]]
if not request_body_within_bounds(client, self.content_length()):
data = AnnotatedValue.removed_because_over_size_limit()
else:
data = self.message_data

request_info["data"] = data
3 changes: 2 additions & 1 deletion sentry_sdk/integrations/pure_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ def start(n):
atok = source.asttokens()

expressions.sort(key=closeness, reverse=True)
return {
vars = {
atok.get_text(nodes[0]): value
for nodes, value in expressions[: serializer.MAX_DATABAG_BREADTH]
}
return serializer.serialize(vars, is_vars=True)
Loading

0 comments on commit 743eeb6

Please sign in to comment.