diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 94871b4a77..13d2231f9a 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -24,6 +24,7 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh - The UnraisableHookIntegration is now enabled by default. - We now don't suppress chained exceptions in the ASGI and asyncio integrations by default. The related `suppress_asgi_chained_exceptions` experimental option was removed. - In the AWS Lambda and GCP integrations, the message of the warning the SDK optionally emits if a function is about to time out has changed. +- `sentry_sdk.init()` can no longer be used as a context manager. ## Removed @@ -82,9 +83,19 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh - The deprecated `propagate_traces` option has been removed. Use `trace_propagation_targets` instead, which gives you more power over trace propagation. Note that only the top-level `init` option was removed; the `propagate_traces` option of the Celery integration remains available. - Removed Spotlight integration for Django. See [Spotlight 2.0](https://github.com/getsentry/spotlight/issues/891) for more context. - The deprecated parameter `propagate_hub` in `ThreadingIntegration()` was removed. +- `configure_debug_hub` was removed. +- The `max_spans` option of the `LangchainIntegration` was removed. +- `Baggage.from_options` was removed. +- `Transport.capture_event` was removed. Use `Transport.capture_envelope` instead. +- Function transports were removed. +- The `Scope.trace_propagation_meta` function no longer accepts a `span` as argument. +- Direct assignment to `Scope.level` was removed. Use `Scope.set_level` instead. +- Direct assignment to `Scope.user` was removed. Use `Scope.set_user` instead. +- `Scope.iter_headers` was removed. - The SDK won't set any tags on its own anymore. - The `update_current_span` API was removed. + ## Deprecated diff --git a/sentry_sdk/_init_implementation.py b/sentry_sdk/_init_implementation.py index 3051ae010b..e89a4e6489 100644 --- a/sentry_sdk/_init_implementation.py +++ b/sentry_sdk/_init_implementation.py @@ -1,48 +1,15 @@ import re -import warnings from typing import TYPE_CHECKING import sentry_sdk from sentry_sdk.utils import logger, parse_version if TYPE_CHECKING: - from typing import Any, ContextManager, Optional + from typing import Any, Optional import sentry_sdk.consts -class _InitGuard: - _CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE = ( - "Using the return value of sentry_sdk.init as a context manager " - "and manually calling the __enter__ and __exit__ methods on the " - "return value are deprecated. We are no longer maintaining this " - "functionality, and we will remove it in the next major release." - ) - - def __init__(self, client: "sentry_sdk.Client") -> None: - self._client = client - - def __enter__(self) -> "_InitGuard": - warnings.warn( - self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE, - stacklevel=2, - category=DeprecationWarning, - ) - - return self - - def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None: - warnings.warn( - self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE, - stacklevel=2, - category=DeprecationWarning, - ) - - c = self._client - if c is not None: - c.close() - - def _check_version_deprecations() -> None: try: import gevent @@ -71,7 +38,7 @@ def _check_version_deprecations() -> None: pass -def _init(*args: "Optional[str]", **kwargs: "Any") -> "ContextManager[Any]": +def _init(*args: "Optional[str]", **kwargs: "Any") -> None: """Initializes the SDK and optionally integrations. This takes the same arguments as the client constructor. @@ -79,18 +46,15 @@ def _init(*args: "Optional[str]", **kwargs: "Any") -> "ContextManager[Any]": client = sentry_sdk.Client(*args, **kwargs) sentry_sdk.get_global_scope().set_client(client) _check_version_deprecations() - rv = _InitGuard(client) - return rv if TYPE_CHECKING: # Make mypy, PyCharm and other static analyzers think `init` is a type to # have nicer autocompletion for params. # - # Use `ClientConstructor` to define the argument types of `init` and - # `ContextManager[Any]` to tell static analyzers about the return type. + # Use `ClientConstructor` to define the argument types of `init`. - class init(sentry_sdk.consts.ClientConstructor, _InitGuard): # noqa: N801 + class init(sentry_sdk.consts.ClientConstructor): # noqa: N801 pass else: diff --git a/sentry_sdk/debug.py b/sentry_sdk/debug.py index 795882e9ef..466f680528 100644 --- a/sentry_sdk/debug.py +++ b/sentry_sdk/debug.py @@ -1,6 +1,5 @@ import logging import sys -import warnings from logging import LogRecord from sentry_sdk import get_client @@ -27,11 +26,3 @@ def configure_logger() -> None: logger.addHandler(_handler) logger.setLevel(logging.DEBUG) logger.addFilter(_DebugFilter()) - - -def configure_debug_hub() -> None: - warnings.warn( - "configure_debug_hub is deprecated. Please remove calls to it, as it is a no-op.", - DeprecationWarning, - stacklevel=2, - ) diff --git a/sentry_sdk/integrations/langchain.py b/sentry_sdk/integrations/langchain.py index 25a6ae2467..0009c764a8 100644 --- a/sentry_sdk/integrations/langchain.py +++ b/sentry_sdk/integrations/langchain.py @@ -1,7 +1,6 @@ import itertools import json import sys -import warnings from collections import OrderedDict from functools import wraps from typing import TYPE_CHECKING, NamedTuple @@ -236,18 +235,8 @@ class LangchainIntegration(Integration): def __init__( self: "LangchainIntegration", include_prompts: bool = True, - max_spans: "Optional[int]" = None, ) -> None: self.include_prompts = include_prompts - self.max_spans = max_spans - - if max_spans is not None: - warnings.warn( - "The `max_spans` parameter of `LangchainIntegration` is " - "deprecated and will be removed in version 3.0 of sentry-sdk.", - DeprecationWarning, - stacklevel=2, - ) @staticmethod def setup_once() -> None: @@ -274,19 +263,10 @@ def setup_once() -> None: class SentryLangchainCallback(BaseCallbackHandler): """Callback handler that creates Sentry spans.""" - def __init__( - self, max_span_map_size: "Optional[int]", include_prompts: bool - ) -> None: + def __init__(self, include_prompts: bool) -> None: self.span_map: "OrderedDict[UUID, Union[sentry_sdk.tracing.Span, StreamedSpan]]" = OrderedDict() - self.max_span_map_size = max_span_map_size self.include_prompts = include_prompts - def gc_span_map(self) -> None: - if self.max_span_map_size is not None: - while len(self.span_map) > self.max_span_map_size: - run_id, span = self.span_map.popitem(last=False) - self._exit_span(span, run_id) - def _handle_error(self, run_id: "UUID", error: "Any") -> None: is_ignored = isinstance(error, tuple(LangchainIntegration._ignored_exceptions)) @@ -356,7 +336,6 @@ def _create_span( span.__enter__() self.span_map[run_id] = span - self.gc_span_map() return span def _exit_span( @@ -1135,7 +1114,6 @@ def new_configure( for cb in itertools.chain(callbacks_list, inheritable_callbacks_list) ): sentry_handler = SentryLangchainCallback( - integration.max_spans, integration.include_prompts, ) if isinstance(local_callbacks, BaseCallbackManager): diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 5ad644b2d8..ab00ac5948 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -67,7 +67,6 @@ Deque, Dict, Generator, - Iterator, List, Optional, ParamSpec, @@ -572,18 +571,6 @@ def generate_propagation_context( if self._propagation_context is None: self.set_new_propagation_context() - def get_dynamic_sampling_context(self) -> "Optional[Dict[str, str]]": - """ - Returns the Dynamic Sampling Context from the Propagation Context. - If not existing, creates a new one. - - Deprecated: Logic moved to PropagationContext, don't use directly. - """ - if self._propagation_context is None: - return None - - return self._propagation_context.dynamic_sampling_context - def get_traceparent(self, *args: "Any", **kwargs: "Any") -> "Optional[str]": """ Returns the Sentry "sentry-trace" header (aka the traceparent) from the @@ -655,12 +642,6 @@ def trace_propagation_meta(self, *args: "Any", **kwargs: "Any") -> str: Return meta tags which should be injected into HTML templates to allow propagation of trace information. """ - span = kwargs.pop("span", None) - if span is not None: - logger.warning( - "The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future." - ) - meta = "" for name, content in self.iter_trace_propagation_headers(): @@ -668,14 +649,6 @@ def trace_propagation_meta(self, *args: "Any", **kwargs: "Any") -> str: return meta - def iter_headers(self) -> "Iterator[Tuple[str, str]]": - """ - Creates a generator which returns the `sentry-trace` and `baggage` headers from the Propagation Context. - Deprecated: use PropagationContext.iter_headers instead. - """ - if self._propagation_context is not None: - yield from self._propagation_context.iter_headers() - def iter_trace_propagation_headers( self, *args: "Any", **kwargs: "Any" ) -> "Generator[Tuple[str, str], None, None]": @@ -759,22 +732,6 @@ def clear(self) -> None: self._gen_ai_conversation_id: "Optional[str]" = None - @_attr_setter - def level(self, value: "LogLevelStr") -> None: - """ - When set this overrides the level. - - .. deprecated:: 1.0.0 - Use :func:`set_level` instead. - - :param value: The level to set. - """ - logger.warning( - "Deprecated: use .set_level() instead. This will be removed in the future." - ) - - self._level = value - def set_level(self, value: "LogLevelStr") -> None: """ Sets the level for the scope. @@ -865,16 +822,6 @@ def set_transaction_name(self, name: str, source: "Optional[str]" = None) -> Non if source: self._transaction_info["source"] = source - @_attr_setter - def user(self, value: "Optional[Dict[str, Any]]") -> None: - """When set a specific user is bound to the scope. Deprecated in favor of set_user.""" - warnings.warn( - "The `Scope.user` setter is deprecated in favor of `Scope.set_user()`.", - DeprecationWarning, - stacklevel=2, - ) - self.set_user(value) - def set_user(self, value: "Optional[Dict[str, Any]]") -> None: """Sets a user for the scope.""" self._user = value diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index c7ed24ba56..a931124538 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -749,16 +749,6 @@ def from_incoming_header( return Baggage(sentry_items, third_party_items, mutable) - @classmethod - def from_options(cls, scope: "sentry_sdk.scope.Scope") -> "Optional[Baggage]": - """ - Deprecated: use populate_from_propagation_context - """ - if scope._propagation_context is None: - return Baggage({}) - - return Baggage.populate_from_propagation_context(scope._propagation_context) - @classmethod def populate_from_propagation_context( cls, propagation_context: "PropagationContext" diff --git a/sentry_sdk/transport.py b/sentry_sdk/transport.py index d98b8597fa..72a3a5e81d 100644 --- a/sentry_sdk/transport.py +++ b/sentry_sdk/transport.py @@ -7,7 +7,6 @@ import socket import ssl import time -import warnings from abc import ABC, abstractmethod from collections import defaultdict from datetime import datetime, timedelta, timezone @@ -68,7 +67,7 @@ from urllib3.poolmanager import PoolManager, ProxyManager - from sentry_sdk._types import Event, EventDataCategory + from sentry_sdk._types import EventDataCategory KEEP_ALIVE_SOCKET_OPTIONS = [] for option in [ @@ -113,24 +112,6 @@ def __init__(self: "Self", options: "Optional[Dict[str, Any]]" = None) -> None: else: self.parsed_dsn = None - def capture_event(self: "Self", event: "Event") -> None: - """ - DEPRECATED: Please use capture_envelope instead. - - This gets invoked with the event dictionary when an event should - be sent to sentry. - """ - - warnings.warn( - "capture_event is deprecated, please use capture_envelope instead!", - DeprecationWarning, - stacklevel=2, - ) - - envelope = Envelope() - envelope.add_event(event) - self.capture_envelope(envelope) - @abstractmethod def capture_envelope(self: "Self", envelope: "Envelope") -> None: """ @@ -1136,35 +1117,6 @@ def __getattr__(self, name: str) -> "Any": return getattr(self._inner, name) -class _FunctionTransport(Transport): - """ - DEPRECATED: Users wishing to provide a custom transport should subclass - the Transport class, rather than providing a function. - """ - - def __init__( - self, - func: "Callable[[Event], None]", - ) -> None: - Transport.__init__(self) - self._func = func - - def capture_event( - self, - event: "Event", - ) -> None: - self._func(event) - return None - - def capture_envelope(self, envelope: "Envelope") -> None: - # Since function transports expect to be called with an event, we need - # to iterate over the envelope and call the function for each event, via - # the deprecated capture_event method. - event = envelope.get_event() - if event is not None: - self.capture_event(event) - - def make_transport(options: "Dict[str, Any]") -> "Optional[Transport]": ref_transport = options["transport"] @@ -1208,14 +1160,6 @@ def make_transport(options: "Dict[str, Any]") -> "Optional[Transport]": transport = ref_transport elif isinstance(ref_transport, type) and issubclass(ref_transport, Transport): transport_cls = ref_transport - elif callable(ref_transport): - warnings.warn( - "Function transports are deprecated and will be removed in a future release." - "Please provide a Transport instance or subclass, instead.", - DeprecationWarning, - stacklevel=2, - ) - transport = _FunctionTransport(ref_transport) # if a transport class is given only instantiate it if the dsn is not # empty or None diff --git a/tests/integrations/langchain/test_langchain.py b/tests/integrations/langchain/test_langchain.py index 92968e1915..a6e6c99e98 100644 --- a/tests/integrations/langchain/test_langchain.py +++ b/tests/integrations/langchain/test_langchain.py @@ -3821,9 +3821,7 @@ def _identifying_params(self): ) # Create a manual SentryLangchainCallback - manual_callback = SentryLangchainCallback( - max_span_map_size=100, include_prompts=False - ) + manual_callback = SentryLangchainCallback(include_prompts=False) # Create RunnableConfig with the manual callback config = RunnableConfig(callbacks=[manual_callback]) @@ -3846,8 +3844,8 @@ def _identifying_params(self): def test_span_map_is_instance_variable(): """Test that each SentryLangchainCallback instance has its own span_map.""" # Create two separate callback instances - callback1 = SentryLangchainCallback(max_span_map_size=100, include_prompts=True) - callback2 = SentryLangchainCallback(max_span_map_size=100, include_prompts=True) + callback1 = SentryLangchainCallback(include_prompts=True) + callback2 = SentryLangchainCallback(include_prompts=True) # Verify they have different span_map instances assert callback1.span_map is not callback2.span_map, ( @@ -3894,7 +3892,7 @@ def test_langchain_callback_manager_with_sentry_callback(sentry_init): disabled_integrations=[StdlibIntegration], traces_sample_rate=1.0, ) - sentry_callback = SentryLangchainCallback(0, False) + sentry_callback = SentryLangchainCallback(False) local_manager = BaseCallbackManager(handlers=[sentry_callback]) with mock.patch("sentry_sdk.integrations.langchain.manager") as mock_manager_module: @@ -3960,7 +3958,7 @@ def test_langchain_callback_list_existing_callback(sentry_init): disabled_integrations=[StdlibIntegration], traces_sample_rate=1.0, ) - sentry_callback = SentryLangchainCallback(0, False) + sentry_callback = SentryLangchainCallback(False) local_callbacks = [sentry_callback] with mock.patch("sentry_sdk.integrations.langchain.manager") as mock_manager_module: @@ -4220,7 +4218,7 @@ def test_langchain_message_truncation(sentry_init, capture_events): ) events = capture_events() - callback = SentryLangchainCallback(max_span_map_size=100, include_prompts=True) + callback = SentryLangchainCallback(include_prompts=True) run_id = "12345678-1234-1234-1234-123456789012" serialized = {"_type": "openai-chat", "model_name": "gpt-3.5-turbo"} @@ -5891,7 +5889,7 @@ def test_langchain_ai_system_detection( trace_lifecycle="stream" if span_streaming else "static", ) - callback = SentryLangchainCallback(max_span_map_size=100, include_prompts=True) + callback = SentryLangchainCallback(include_prompts=True) run_id = "test-ai-system-uuid" serialized = {"_type": ai_type} if ai_type is not None else {} @@ -6730,7 +6728,7 @@ def test_langchain_data_collection_request_tool_call_params( sentry_init(**sentry_init_kwargs) - callback = SentryLangchainCallback(max_span_map_size=100, include_prompts=False) + callback = SentryLangchainCallback(include_prompts=False) streamed = span_streaming or stream_gen_ai_spans captured = capture_items("span") if streamed else capture_events() diff --git a/tests/test_api.py b/tests/test_api.py index 4c83f03cb6..a1a8d7b285 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,8 +1,6 @@ import re from unittest import mock -import pytest - import sentry_sdk from sentry_sdk import ( capture_exception, @@ -310,19 +308,3 @@ def test_set_tags(sentry_init, capture_events): "tag2": "updated", "tag3": "new", }, "Updating tags with empty dict changed tags" - - -def test_init_context_manager_deprecation(): - with pytest.warns(DeprecationWarning): - with sentry_sdk.init(): - ... - - -def test_init_enter_deprecation(): - with pytest.warns(DeprecationWarning): - sentry_sdk.init().__enter__() - - -def test_init_exit_deprecation(): - with pytest.warns(DeprecationWarning): - sentry_sdk.init().__exit__(None, None, None) diff --git a/tests/test_client.py b/tests/test_client.py index 47a76764cb..387cab3e8f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -393,13 +393,6 @@ def test_socks_proxy(testcase, http2): ) -def test_simple_transport(sentry_init): - events = [] - sentry_init(transport=events.append) - capture_message("Hello World!") - assert events[0]["message"] == "Hello World!" - - def test_ignore_errors(sentry_init, capture_events): sentry_init(ignore_errors=[ZeroDivisionError]) events = capture_events()