Repository: getsentry/sentry-python

Issue:
Stop Using `Hub` in `HttpTransport`
We are still using the deprecated `Hub` in `HttpTransport`:

https://github.com/getsentry/sentry-python/blob/407f651f66fa811a20241579aa7881de624b3e20/sentry_sdk/transport.py#L221-L223

https://github.com/getsentry/sentry-python/blob/407f651f66fa811a20241579aa7881de624b3e20/sentry_sdk/transport.py#L551-L555

We should stop using the `Hub` here, as it is deprecated

Discussion:
szokeasaurusrex: > Nice, let's drop this in 3.0.

@sentrivana You mean we should drop the `self.hub_cls` compatibility code with 3.0, correct? Can we still merge this PR now and release in a 2.x minor?
sentrivana: > @sentrivana You mean we should drop the `self.hub_cls` compatibility code with 3.0, correct? Can we still merge this PR now and release in a 2.x minor?

Yes, we can merge and release! Sorry for the confusion.

Patch:
diff --git a/sentry_sdk/transport.py b/sentry_sdk/transport.py
index a9414ae7..2cbba041 100644
--- a/sentry_sdk/transport.py
+++ b/sentry_sdk/transport.py
@@ -12,6 +12,7 @@ from urllib.request import getproxies
 import urllib3
 import certifi
 
+import sentry_sdk
 from sentry_sdk.consts import EndpointType
 from sentry_sdk.utils import Dsn, logger, capture_internal_exceptions
 from sentry_sdk.worker import BackgroundWorker
@@ -37,7 +38,6 @@ if TYPE_CHECKING:
 
     DataCategory = Optional[str]
 
-
 KEEP_ALIVE_SOCKET_OPTIONS = []
 for option in [
     (socket.SOL_SOCKET, lambda: getattr(socket, "SO_KEEPALIVE"), 1),  # noqa: B009
@@ -218,9 +218,8 @@ class HttpTransport(Transport):
             proxy_headers=options["proxy_headers"],
         )
 
-        from sentry_sdk import Hub
-
-        self.hub_cls = Hub
+        # Backwards compatibility for deprecated `self.hub_class` attribute
+        self._hub_cls = sentry_sdk.Hub
 
     def record_lost_event(
         self,
@@ -548,14 +547,11 @@ class HttpTransport(Transport):
         self, envelope  # type: Envelope
     ):
         # type: (...) -> None
-        hub = self.hub_cls.current
-
         def send_envelope_wrapper():
             # type: () -> None
-            with hub:
-                with capture_internal_exceptions():
-                    self._send_envelope(envelope)
-                    self._flush_client_reports()
+            with capture_internal_exceptions():
+                self._send_envelope(envelope)
+                self._flush_client_reports()
 
         if not self._worker.submit(send_envelope_wrapper):
             self.on_dropped_event("full_queue")
@@ -579,6 +575,30 @@ class HttpTransport(Transport):
         logger.debug("Killing HTTP transport")
         self._worker.kill()
 
+    @staticmethod
+    def _warn_hub_cls():
+        # type: () -> None
+        """Convenience method to warn users about the deprecation of the `hub_cls` attribute."""
+        warnings.warn(
+            "The `hub_cls` attribute is deprecated and will be removed in a future release.",
+            DeprecationWarning,
+            stacklevel=3,
+        )
+
+    @property
+    def hub_cls(self):
+        # type: () -> type[sentry_sdk.Hub]
+        """DEPRECATED: This attribute is deprecated and will be removed in a future release."""
+        HttpTransport._warn_hub_cls()
+        return self._hub_cls
+
+    @hub_cls.setter
+    def hub_cls(self, value):
+        # type: (type[sentry_sdk.Hub]) -> None
+        """DEPRECATED: This attribute is deprecated and will be removed in a future release."""
+        HttpTransport._warn_hub_cls()
+        self._hub_cls = value
+
 
 class _FunctionTransport(Transport):
     """


Test patch:
diff --git a/tests/test_transport.py b/tests/test_transport.py
index 6cace6f4..b831d7f8 100644
--- a/tests/test_transport.py
+++ b/tests/test_transport.py
@@ -3,7 +3,7 @@ import pickle
 import gzip
 import io
 import socket
-from collections import namedtuple
+from collections import defaultdict, namedtuple
 from datetime import datetime, timedelta, timezone
 from unittest import mock
 
@@ -17,7 +17,6 @@ from sentry_sdk.envelope import Envelope, Item, parse_json
 from sentry_sdk.transport import KEEP_ALIVE_SOCKET_OPTIONS, _parse_rate_limits
 from sentry_sdk.integrations.logging import LoggingIntegration, ignore_logger
 
-
 CapturedData = namedtuple("CapturedData", ["path", "event", "envelope", "compressed"])
 
 
@@ -585,3 +584,21 @@ def test_metric_bucket_limits_with_all_namespaces(
     assert report["discarded_events"] == [
         {"category": "metric_bucket", "reason": "ratelimit_backoff", "quantity": 1},
     ]
+
+
+def test_hub_cls_backwards_compat():
+    class TestCustomHubClass(sentry_sdk.Hub):
+        pass
+
+    transport = sentry_sdk.transport.HttpTransport(
+        defaultdict(lambda: None, {"dsn": "https://123abc@example.com/123"})
+    )
+
+    with pytest.deprecated_call():
+        assert transport.hub_cls is sentry_sdk.Hub
+
+    with pytest.deprecated_call():
+        transport.hub_cls = TestCustomHubClass
+
+    with pytest.deprecated_call():
+        assert transport.hub_cls is TestCustomHubClass