diff --git a/.buildinfo b/.buildinfo index 04afd0f718..c7cca1b551 100644 --- a/.buildinfo +++ b/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 2ed008e28db0c89418795e4f88735099 +config: b35fde85f9e4bd191fe6e3c6be1b209a tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.doctrees/apidocs.doctree b/.doctrees/apidocs.doctree index 6f74f5e0d3..aa96eea6f2 100644 Binary files a/.doctrees/apidocs.doctree and b/.doctrees/apidocs.doctree differ diff --git a/.doctrees/environment.pickle b/.doctrees/environment.pickle index 54fe779d30..21d83dff13 100644 Binary files a/.doctrees/environment.pickle and b/.doctrees/environment.pickle differ diff --git a/_modules/index.html b/_modules/index.html index 0283eb3109..1ac54c1063 100644 --- a/_modules/index.html +++ b/_modules/index.html @@ -2,7 +2,7 @@ - Overview: module code - sentry-python 2.12.0 documentation @@ -141,7 +141,7 @@

All modules for which code is available

- + diff --git a/_modules/sentry_sdk/api.html b/_modules/sentry_sdk/api.html index 41bf6c4164..cafa79b30d 100644 --- a/_modules/sentry_sdk/api.html +++ b/_modules/sentry_sdk/api.html @@ -2,7 +2,7 @@ - sentry_sdk.api - sentry-python 2.12.0 documentation @@ -623,7 +623,7 @@

Source code for sentry_sdk.api

     
- + diff --git a/_modules/sentry_sdk/attachments.html b/_modules/sentry_sdk/attachments.html index b4fd8cd81c..a90e75d220 100644 --- a/_modules/sentry_sdk/attachments.html +++ b/_modules/sentry_sdk/attachments.html @@ -2,7 +2,7 @@ - sentry_sdk.attachments - sentry-python 2.12.0 documentation @@ -212,7 +212,7 @@

Source code for sentry_sdk.attachments

     
- + diff --git a/_modules/sentry_sdk/client.html b/_modules/sentry_sdk/client.html index 92fd6c95e4..aafe99ca66 100644 --- a/_modules/sentry_sdk/client.html +++ b/_modules/sentry_sdk/client.html @@ -2,7 +2,7 @@ - sentry_sdk.client - sentry-python 2.12.0 documentation @@ -111,12 +111,12 @@

Source code for sentry_sdk.client

 from collections.abc import Mapping
 from datetime import datetime, timezone
 from importlib import import_module
-
-from sentry_sdk._compat import PY37, check_uwsgi_thread_support
-from sentry_sdk.utils import (
-    capture_internal_exceptions,
-    current_stacktrace,
-    disable_capture_event,
+from typing import cast
+
+from sentry_sdk._compat import PY37, check_uwsgi_thread_support
+from sentry_sdk.utils import (
+    capture_internal_exceptions,
+    current_stacktrace,
     format_timestamp,
     get_sdk_name,
     get_type_name,
@@ -160,7 +160,7 @@ 

Source code for sentry_sdk.client

     from typing import Type
     from typing import Union
 
-    from sentry_sdk._types import Event, Hint
+    from sentry_sdk._types import Event, Hint, SDKInfo
     from sentry_sdk.integrations import Integration
     from sentry_sdk.metrics import MetricsAggregator
     from sentry_sdk.scope import Scope
@@ -175,7 +175,7 @@ 

Source code for sentry_sdk.client

     "name": "sentry.python",  # SDK name will be overridden after integrations have been loaded with sentry_sdk.integrations.setup_integrations()
     "version": VERSION,
     "packages": [{"name": "pypi:sentry-sdk", "version": VERSION}],
-}
+}  # type: SDKInfo
 
 
 def _get_options(*args, **kwargs):
@@ -508,532 +508,534 @@ 

Source code for sentry_sdk.client

                 try:
                     setup_continuous_profiler(
                         self.options,
-                        capture_func=_capture_envelope,
-                    )
-                except Exception as e:
-                    logger.debug("Can not set up continuous profiler. (%s)", e)
-
-        finally:
-            _client_init_debug.set(old_debug)
-
-        self._setup_instrumentation(self.options.get("functions_to_trace", []))
-
-        if (
-            self.monitor
-            or self.metrics_aggregator
-            or has_profiling_enabled(self.options)
-            or isinstance(self.transport, HttpTransport)
-        ):
-            # If we have anything on that could spawn a background thread, we
-            # need to check if it's safe to use them.
-            check_uwsgi_thread_support()
-
+                        sdk_info=SDK_INFO,
+                        capture_func=_capture_envelope,
+                    )
+                except Exception as e:
+                    logger.debug("Can not set up continuous profiler. (%s)", e)
+
+        finally:
+            _client_init_debug.set(old_debug)
+
+        self._setup_instrumentation(self.options.get("functions_to_trace", []))
+
+        if (
+            self.monitor
+            or self.metrics_aggregator
+            or has_profiling_enabled(self.options)
+            or isinstance(self.transport, HttpTransport)
+        ):
+            # If we have anything on that could spawn a background thread, we
+            # need to check if it's safe to use them.
+            check_uwsgi_thread_support()
+
 
[docs] - def is_active(self): - # type: () -> bool - """ - .. versionadded:: 2.0.0 - - Returns whether the client is active (able to send data to Sentry) - """ - return True
+
def is_active(self): + # type: () -> bool + """ + .. versionadded:: 2.0.0 + + Returns whether the client is active (able to send data to Sentry) + """ + return True
-
+
[docs] - def should_send_default_pii(self): - # type: () -> bool - """ - .. versionadded:: 2.0.0 - - Returns whether the client should send default PII (Personally Identifiable Information) data to Sentry. - """ - return self.options.get("send_default_pii", False)
+
def should_send_default_pii(self): + # type: () -> bool + """ + .. versionadded:: 2.0.0 + + Returns whether the client should send default PII (Personally Identifiable Information) data to Sentry. + """ + return self.options.get("send_default_pii", False)
-
- @property - def dsn(self): - # type: () -> Optional[str] - """Returns the configured DSN as string.""" - return self.options["dsn"] - - def _prepare_event( - self, - event, # type: Event - hint, # type: Hint - scope, # type: Optional[Scope] - ): - # type: (...) -> Optional[Event] - - if event.get("timestamp") is None: - event["timestamp"] = datetime.now(timezone.utc) - - if scope is not None: - is_transaction = event.get("type") == "transaction" - spans_before = len(event.get("spans", [])) - event_ = scope.apply_to_event(event, hint, self.options) - - # one of the event/error processors returned None - if event_ is None: - if self.transport: - self.transport.record_lost_event( - "event_processor", - data_category=("transaction" if is_transaction else "error"), - ) - if is_transaction: - self.transport.record_lost_event( - "event_processor", - data_category="span", - quantity=spans_before + 1, # +1 for the transaction itself - ) - return None - - event = event_ - - spans_delta = spans_before - len(event.get("spans", [])) - if is_transaction and spans_delta > 0 and self.transport is not None: - self.transport.record_lost_event( - "event_processor", data_category="span", quantity=spans_delta - ) - - if ( - self.options["attach_stacktrace"] - and "exception" not in event - and "stacktrace" not in event - and "threads" not in event - ): - with capture_internal_exceptions(): - event["threads"] = { - "values": [ - { - "stacktrace": current_stacktrace( - include_local_variables=self.options.get( - "include_local_variables", True - ), - max_value_length=self.options.get( - "max_value_length", DEFAULT_MAX_VALUE_LENGTH - ), - ), - "crashed": False, - "current": True, - } - ] - } - - for key in "release", "environment", "server_name", "dist": - if event.get(key) is None and self.options[key] is not None: - event[key] = str(self.options[key]).strip() # type: ignore[literal-required] - if event.get("sdk") is None: - sdk_info = dict(SDK_INFO) - sdk_info["integrations"] = sorted(self.integrations.keys()) - event["sdk"] = sdk_info - - if event.get("platform") is None: - event["platform"] = "python" - - event = handle_in_app( - event, - self.options["in_app_exclude"], - self.options["in_app_include"], - self.options["project_root"], - ) - - if event is not None: - event_scrubber = self.options["event_scrubber"] - if event_scrubber and not self.options["send_default_pii"]: - event_scrubber.scrub_event(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"), - ) - - before_send = self.options["before_send"] - if ( - before_send is not None - and event is not None - and event.get("type") != "transaction" - ): - new_event = None - with capture_internal_exceptions(): - new_event = before_send(event, hint or {}) - if new_event is None: - logger.info("before send dropped event") - if self.transport: - self.transport.record_lost_event( - "before_send", data_category="error" - ) - event = new_event # type: ignore - - before_send_transaction = self.options["before_send_transaction"] - if ( - before_send_transaction is not None - and event is not None - and event.get("type") == "transaction" - ): - new_event = None - spans_before = len(event.get("spans", [])) - with capture_internal_exceptions(): - new_event = before_send_transaction(event, hint or {}) - if new_event is None: - logger.info("before send transaction dropped event") - if self.transport: - self.transport.record_lost_event( - reason="before_send", data_category="transaction" - ) - self.transport.record_lost_event( - reason="before_send", - data_category="span", - quantity=spans_before + 1, # +1 for the transaction itself + + @property + def dsn(self): + # type: () -> Optional[str] + """Returns the configured DSN as string.""" + return self.options["dsn"] + + def _prepare_event( + self, + event, # type: Event + hint, # type: Hint + scope, # type: Optional[Scope] + ): + # type: (...) -> Optional[Event] + + if event.get("timestamp") is None: + event["timestamp"] = datetime.now(timezone.utc) + + if scope is not None: + is_transaction = event.get("type") == "transaction" + spans_before = len(event.get("spans", [])) + event_ = scope.apply_to_event(event, hint, self.options) + + # one of the event/error processors returned None + if event_ is None: + if self.transport: + self.transport.record_lost_event( + "event_processor", + data_category=("transaction" if is_transaction else "error"), + ) + if is_transaction: + self.transport.record_lost_event( + "event_processor", + data_category="span", + quantity=spans_before + 1, # +1 for the transaction itself + ) + return None + + event = event_ + + spans_delta = spans_before - len(event.get("spans", [])) + if is_transaction and spans_delta > 0 and self.transport is not None: + self.transport.record_lost_event( + "event_processor", data_category="span", quantity=spans_delta + ) + + if ( + self.options["attach_stacktrace"] + and "exception" not in event + and "stacktrace" not in event + and "threads" not in event + ): + with capture_internal_exceptions(): + event["threads"] = { + "values": [ + { + "stacktrace": current_stacktrace( + include_local_variables=self.options.get( + "include_local_variables", True + ), + max_value_length=self.options.get( + "max_value_length", DEFAULT_MAX_VALUE_LENGTH + ), + ), + "crashed": False, + "current": True, + } + ] + } + + for key in "release", "environment", "server_name", "dist": + if event.get(key) is None and self.options[key] is not None: + event[key] = str(self.options[key]).strip() # type: ignore[literal-required] + if event.get("sdk") is None: + sdk_info = dict(SDK_INFO) + sdk_info["integrations"] = sorted(self.integrations.keys()) + event["sdk"] = sdk_info + + if event.get("platform") is None: + event["platform"] = "python" + + event = handle_in_app( + event, + self.options["in_app_exclude"], + self.options["in_app_include"], + self.options["project_root"], + ) + + if event is not None: + event_scrubber = self.options["event_scrubber"] + if event_scrubber and not self.options["send_default_pii"]: + event_scrubber.scrub_event(event) + + # Postprocess the event here so that annotated types do + # generally not surface in before_send + if event is not None: + 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"] + if ( + before_send is not None + and event is not None + and event.get("type") != "transaction" + ): + new_event = None + with capture_internal_exceptions(): + new_event = before_send(event, hint or {}) + if new_event is None: + logger.info("before send dropped event") + if self.transport: + self.transport.record_lost_event( + "before_send", data_category="error" + ) + event = new_event # type: ignore + + before_send_transaction = self.options["before_send_transaction"] + if ( + before_send_transaction is not None + and event is not None + and event.get("type") == "transaction" + ): + new_event = None + spans_before = len(event.get("spans", [])) + with capture_internal_exceptions(): + new_event = before_send_transaction(event, hint or {}) + if new_event is None: + logger.info("before send transaction dropped event") + if self.transport: + self.transport.record_lost_event( + reason="before_send", data_category="transaction" ) - else: - spans_delta = spans_before - len(new_event.get("spans", [])) - if spans_delta > 0 and self.transport is not None: - self.transport.record_lost_event( - reason="before_send", data_category="span", quantity=spans_delta - ) - - event = new_event # type: ignore - - return event - - def _is_ignored_error(self, event, hint): - # type: (Event, Hint) -> bool - exc_info = hint.get("exc_info") - if exc_info is None: - return False - - error = exc_info[0] - error_type_name = get_type_name(exc_info[0]) - error_full_name = "%s.%s" % (exc_info[0].__module__, error_type_name) - - for ignored_error in self.options["ignore_errors"]: - # String types are matched against the type name in the - # exception only - if isinstance(ignored_error, str): - if ignored_error == error_full_name or ignored_error == error_type_name: - return True - else: - if issubclass(error, ignored_error): - return True - - return False - - def _should_capture( - self, - event, # type: Event - hint, # type: Hint - scope=None, # type: Optional[Scope] - ): - # type: (...) -> bool - # Transactions are sampled independent of error events. - is_transaction = event.get("type") == "transaction" - if is_transaction: - return True - - ignoring_prevents_recursion = scope is not None and not scope._should_capture - if ignoring_prevents_recursion: - return False - - ignored_by_config_option = self._is_ignored_error(event, hint) - if ignored_by_config_option: - return False - - return True - - def _should_sample_error( - self, - event, # type: Event - hint, # type: Hint - ): - # type: (...) -> bool - error_sampler = self.options.get("error_sampler", None) - - if callable(error_sampler): - with capture_internal_exceptions(): - sample_rate = error_sampler(event, hint) - else: - sample_rate = self.options["sample_rate"] - - try: - not_in_sample_rate = sample_rate < 1.0 and random.random() >= sample_rate - except NameError: - logger.warning( - "The provided error_sampler raised an error. Defaulting to sampling the event." - ) - - # If the error_sampler raised an error, we should sample the event, since the default behavior - # (when no sample_rate or error_sampler is provided) is to sample all events. - not_in_sample_rate = False - except TypeError: - parameter, verb = ( - ("error_sampler", "returned") - if callable(error_sampler) - else ("sample_rate", "contains") - ) - logger.warning( - "The provided %s %s an invalid value of %s. The value should be a float or a bool. Defaulting to sampling the event." - % (parameter, verb, repr(sample_rate)) - ) - - # If the sample_rate has an invalid value, we should sample the event, since the default behavior - # (when no sample_rate or error_sampler is provided) is to sample all events. - not_in_sample_rate = False - - if not_in_sample_rate: - # because we will not sample this event, record a "lost event". - if self.transport: - self.transport.record_lost_event("sample_rate", data_category="error") + self.transport.record_lost_event( + reason="before_send", + data_category="span", + quantity=spans_before + 1, # +1 for the transaction itself + ) + else: + spans_delta = spans_before - len(new_event.get("spans", [])) + if spans_delta > 0 and self.transport is not None: + self.transport.record_lost_event( + reason="before_send", data_category="span", quantity=spans_delta + ) + + event = new_event # type: ignore + + return event + + def _is_ignored_error(self, event, hint): + # type: (Event, Hint) -> bool + exc_info = hint.get("exc_info") + if exc_info is None: + return False + + error = exc_info[0] + error_type_name = get_type_name(exc_info[0]) + error_full_name = "%s.%s" % (exc_info[0].__module__, error_type_name) + + for ignored_error in self.options["ignore_errors"]: + # String types are matched against the type name in the + # exception only + if isinstance(ignored_error, str): + if ignored_error == error_full_name or ignored_error == error_type_name: + return True + else: + if issubclass(error, ignored_error): + return True + + return False + + def _should_capture( + self, + event, # type: Event + hint, # type: Hint + scope=None, # type: Optional[Scope] + ): + # type: (...) -> bool + # Transactions are sampled independent of error events. + is_transaction = event.get("type") == "transaction" + if is_transaction: + return True + + ignoring_prevents_recursion = scope is not None and not scope._should_capture + if ignoring_prevents_recursion: + return False + + ignored_by_config_option = self._is_ignored_error(event, hint) + if ignored_by_config_option: + return False + + return True + + def _should_sample_error( + self, + event, # type: Event + hint, # type: Hint + ): + # type: (...) -> bool + error_sampler = self.options.get("error_sampler", None) + + if callable(error_sampler): + with capture_internal_exceptions(): + sample_rate = error_sampler(event, hint) + else: + sample_rate = self.options["sample_rate"] + + try: + not_in_sample_rate = sample_rate < 1.0 and random.random() >= sample_rate + except NameError: + logger.warning( + "The provided error_sampler raised an error. Defaulting to sampling the event." + ) + + # If the error_sampler raised an error, we should sample the event, since the default behavior + # (when no sample_rate or error_sampler is provided) is to sample all events. + not_in_sample_rate = False + except TypeError: + parameter, verb = ( + ("error_sampler", "returned") + if callable(error_sampler) + else ("sample_rate", "contains") + ) + logger.warning( + "The provided %s %s an invalid value of %s. The value should be a float or a bool. Defaulting to sampling the event." + % (parameter, verb, repr(sample_rate)) + ) + + # If the sample_rate has an invalid value, we should sample the event, since the default behavior + # (when no sample_rate or error_sampler is provided) is to sample all events. + not_in_sample_rate = False - return False - - return True - - def _update_session_from_event( - self, - session, # type: Session - event, # type: Event - ): - # type: (...) -> None - - crashed = False - errored = False - user_agent = None - - exceptions = (event.get("exception") or {}).get("values") - if exceptions: - errored = True - for error in exceptions: - mechanism = error.get("mechanism") - if isinstance(mechanism, Mapping) and mechanism.get("handled") is False: - crashed = True - break - - user = event.get("user") - - if session.user_agent is None: - headers = (event.get("request") or {}).get("headers") - headers_dict = headers if isinstance(headers, dict) else {} - for k, v in headers_dict.items(): - if k.lower() == "user-agent": - user_agent = v - break - - session.update( - status="crashed" if crashed else None, - user=user, - user_agent=user_agent, - errors=session.errors + (errored or crashed), - ) - + if not_in_sample_rate: + # because we will not sample this event, record a "lost event". + if self.transport: + self.transport.record_lost_event("sample_rate", data_category="error") + + return False + + return True + + def _update_session_from_event( + self, + session, # type: Session + event, # type: Event + ): + # type: (...) -> None + + crashed = False + errored = False + user_agent = None + + exceptions = (event.get("exception") or {}).get("values") + if exceptions: + errored = True + for error in exceptions: + mechanism = error.get("mechanism") + if isinstance(mechanism, Mapping) and mechanism.get("handled") is False: + crashed = True + break + + user = event.get("user") + + if session.user_agent is None: + headers = (event.get("request") or {}).get("headers") + headers_dict = headers if isinstance(headers, dict) else {} + for k, v in headers_dict.items(): + if k.lower() == "user-agent": + user_agent = v + break + + session.update( + status="crashed" if crashed else None, + user=user, + user_agent=user_agent, + errors=session.errors + (errored or crashed), + ) +
[docs] - def capture_event( - self, - event, # type: Event - hint=None, # type: Optional[Hint] - scope=None, # type: Optional[Scope] - ): - # type: (...) -> Optional[str] - """Captures an event. - - :param event: A ready-made event that can be directly sent to Sentry. - - :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. - - :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. - - :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 + def capture_event( + self, + event, # type: Event + hint=None, # type: Optional[Hint] + scope=None, # type: Optional[Scope] + ): + # type: (...) -> Optional[str] + """Captures an event. + + :param event: A ready-made event that can be directly sent to Sentry. + + :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. - if hint is None: - hint = {} - event_id = event.get("event_id") - hint = dict(hint or ()) # type: Hint - - if event_id is None: - event["event_id"] = event_id = uuid.uuid4().hex - if not self._should_capture(event, hint, scope): - return None - - profile = event.pop("profile", None) + :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 hint is None: + hint = {} + event_id = event.get("event_id") + hint = dict(hint or ()) # type: Hint + + if event_id is None: + event["event_id"] = event_id = uuid.uuid4().hex + if not self._should_capture(event, hint, scope): + return None - event_opt = self._prepare_event(event, hint, scope) - if event_opt is None: - return None - - # whenever we capture an event we also check if the session needs - # to be updated based on that information. - session = scope._session if scope else None - if session: - self._update_session_from_event(session, event) - - is_transaction = event_opt.get("type") == "transaction" - is_checkin = event_opt.get("type") == "check_in" - - if ( - not is_transaction - and not is_checkin - and not self._should_sample_error(event, hint) - ): - return None - - attachments = hint.get("attachments") + profile = event.pop("profile", None) + + event_opt = self._prepare_event(event, hint, scope) + if event_opt is None: + return None + + # whenever we capture an event we also check if the session needs + # to be updated based on that information. + session = scope._session if scope else None + if session: + self._update_session_from_event(session, event) + + is_transaction = event_opt.get("type") == "transaction" + is_checkin = event_opt.get("type") == "check_in" + + if ( + not is_transaction + and not is_checkin + and not self._should_sample_error(event, hint) + ): + return None - trace_context = event_opt.get("contexts", {}).get("trace") or {} - dynamic_sampling_context = trace_context.pop("dynamic_sampling_context", {}) - - headers = { - "event_id": event_opt["event_id"], - "sent_at": format_timestamp(datetime.now(timezone.utc)), - } # type: dict[str, object] - - if dynamic_sampling_context: - headers["trace"] = dynamic_sampling_context - - envelope = Envelope(headers=headers) + attachments = hint.get("attachments") + + trace_context = event_opt.get("contexts", {}).get("trace") or {} + dynamic_sampling_context = trace_context.pop("dynamic_sampling_context", {}) + + headers = { + "event_id": event_opt["event_id"], + "sent_at": format_timestamp(datetime.now(timezone.utc)), + } # type: dict[str, object] + + if dynamic_sampling_context: + headers["trace"] = dynamic_sampling_context - if is_transaction: - if isinstance(profile, Profile): - envelope.add_profile(profile.to_json(event_opt, self.options)) - envelope.add_transaction(event_opt) - elif is_checkin: - envelope.add_checkin(event_opt) - else: - envelope.add_event(event_opt) - - for attachment in attachments or (): - envelope.add_item(attachment.to_envelope_item()) - - if self.spotlight: - self.spotlight.capture_envelope(envelope) - - if self.transport is None: - return None - - self.transport.capture_envelope(envelope) + envelope = Envelope(headers=headers) + + if is_transaction: + if isinstance(profile, Profile): + envelope.add_profile(profile.to_json(event_opt, self.options)) + envelope.add_transaction(event_opt) + elif is_checkin: + envelope.add_checkin(event_opt) + else: + envelope.add_event(event_opt) + + for attachment in attachments or (): + envelope.add_item(attachment.to_envelope_item()) + + if self.spotlight: + self.spotlight.capture_envelope(envelope) + + if self.transport is None: + return None - return event_id
- +
self.transport.capture_envelope(envelope) - def capture_session( - self, session # type: Session - ): - # type: (...) -> None - if not session.release: - logger.info("Discarded session update because of missing release") - else: - self.session_flusher.add_session(session) - + return event_id
+ +
+ def capture_session( + self, session # type: Session + ): + # type: (...) -> None + if not session.release: + logger.info("Discarded session update because of missing release") + else: + self.session_flusher.add_session(session) +
[docs] - def get_integration( - self, name_or_class # type: Union[str, Type[Integration]] - ): - # type: (...) -> Any - """Returns the integration for this client by name or class. - If the client does not have that integration then `None` is returned. - """ - if isinstance(name_or_class, str): - integration_name = name_or_class - elif name_or_class.identifier is not None: - integration_name = name_or_class.identifier - else: - raise ValueError("Integration has no name") - - return self.integrations.get(integration_name)
- +
def get_integration( + self, name_or_class # type: Union[str, Type[Integration]] + ): + # type: (...) -> Any + """Returns the integration for this client by name or class. + If the client does not have that integration then `None` is returned. + """ + if isinstance(name_or_class, str): + integration_name = name_or_class + elif name_or_class.identifier is not None: + integration_name = name_or_class.identifier + else: + raise ValueError("Integration has no name") + return self.integrations.get(integration_name)
+ +
[docs] - def close( - self, - timeout=None, # type: Optional[float] - callback=None, # type: Optional[Callable[[int, float], None]] - ): - # type: (...) -> None - """ - Close the client and shut down the transport. Arguments have the same - semantics as :py:meth:`Client.flush`. - """ - if self.transport is not None: - self.flush(timeout=timeout, callback=callback) - self.session_flusher.kill() - if self.metrics_aggregator is not None: - self.metrics_aggregator.kill() - if self.monitor: - self.monitor.kill() - self.transport.kill() - self.transport = None
+
def close( + self, + timeout=None, # type: Optional[float] + callback=None, # type: Optional[Callable[[int, float], None]] + ): + # type: (...) -> None + """ + Close the client and shut down the transport. Arguments have the same + semantics as :py:meth:`Client.flush`. + """ + if self.transport is not None: + self.flush(timeout=timeout, callback=callback) + self.session_flusher.kill() + if self.metrics_aggregator is not None: + self.metrics_aggregator.kill() + if self.monitor: + self.monitor.kill() + self.transport.kill() + self.transport = None - +
[docs] - def flush( - self, - timeout=None, # type: Optional[float] - callback=None, # type: Optional[Callable[[int, float], None]] - ): - # type: (...) -> None - """ - Wait for the current events to be sent. - - :param timeout: Wait for at most `timeout` seconds. If no `timeout` is provided, the `shutdown_timeout` option value is used. + def flush( + self, + timeout=None, # type: Optional[float] + callback=None, # type: Optional[Callable[[int, float], None]] + ): + # type: (...) -> None + """ + Wait for the current events to be sent. - :param callback: Is invoked with the number of pending events and the configured timeout. - """ - if self.transport is not None: - if timeout is None: - timeout = self.options["shutdown_timeout"] - self.session_flusher.flush() - if self.metrics_aggregator is not None: - self.metrics_aggregator.flush() - self.transport.flush(timeout=timeout, callback=callback)
+
:param timeout: Wait for at most `timeout` seconds. If no `timeout` is provided, the `shutdown_timeout` option value is used. + + :param callback: Is invoked with the number of pending events and the configured timeout. + """ + if self.transport is not None: + if timeout is None: + timeout = self.options["shutdown_timeout"] + self.session_flusher.flush() + if self.metrics_aggregator is not None: + self.metrics_aggregator.flush() + self.transport.flush(timeout=timeout, callback=callback) - - def __enter__(self): - # type: () -> _Client - return self - - def __exit__(self, exc_type, exc_value, tb): - # type: (Any, Any, Any) -> None - self.close() + + def __enter__(self): + # type: () -> _Client + return self + + def __exit__(self, exc_type, exc_value, tb): + # type: (Any, Any, Any) -> None + self.close() - - -from sentry_sdk._types import TYPE_CHECKING + -if TYPE_CHECKING: - # Make mypy, PyCharm and other static analyzers think `get_options` is a - # type to have nicer autocompletion for params. - # - # Use `ClientConstructor` to define the argument types of `init` and - # `Dict[str, Any]` to tell static analyzers about the return type. - - class get_options(ClientConstructor, Dict[str, Any]): # noqa: N801 - pass - +from sentry_sdk._types import TYPE_CHECKING + +if TYPE_CHECKING: + # Make mypy, PyCharm and other static analyzers think `get_options` is a + # type to have nicer autocompletion for params. + # + # Use `ClientConstructor` to define the argument types of `init` and + # `Dict[str, Any]` to tell static analyzers about the return type. + + class get_options(ClientConstructor, Dict[str, Any]): # noqa: N801 + pass +
[docs] - class Client(ClientConstructor, _Client): - pass
+
class Client(ClientConstructor, _Client): + pass - -else: - # Alias `get_options` for actual usage. Go through the lambda indirection - # to throw PyCharm off of the weakly typed signature (it would otherwise - # discover both the weakly typed signature of `_init` and our faked `init` - # type). - - get_options = (lambda: _get_options)() - Client = (lambda: _Client)() + +else: + # Alias `get_options` for actual usage. Go through the lambda indirection + # to throw PyCharm off of the weakly typed signature (it would otherwise + # discover both the weakly typed signature of `_init` and our faked `init` + # type). + + get_options = (lambda: _get_options)() + Client = (lambda: _Client)()