diff --git a/sentry_sdk/_compat.py b/sentry_sdk/_compat.py index a811cf2120..dcb590fcfa 100644 --- a/sentry_sdk/_compat.py +++ b/sentry_sdk/_compat.py @@ -15,18 +15,15 @@ PY311 = sys.version_info[0] == 3 and sys.version_info[1] >= 11 -def with_metaclass(meta, *bases): - # type: (Any, *Any) -> Any +def with_metaclass(meta: "Any", *bases: "Any") -> "Any": class MetaClass(type): - def __new__(metacls, name, this_bases, d): - # type: (Any, Any, Any, Any) -> Any + def __new__(metacls: "Any", name: "Any", this_bases: "Any", d: "Any") -> "Any": return meta(name, bases, d) return type.__new__(MetaClass, "temporary_class", (), {}) -def check_uwsgi_thread_support(): - # type: () -> bool +def check_uwsgi_thread_support() -> bool: # We check two things here: # # 1. uWSGI doesn't run in threaded mode by default -- issue a warning if @@ -46,8 +43,7 @@ def check_uwsgi_thread_support(): from sentry_sdk.consts import FALSE_VALUES - def enabled(option): - # type: (str) -> bool + def enabled(option: str) -> bool: value = opt.get(option, False) if isinstance(value, bool): return value diff --git a/sentry_sdk/_init_implementation.py b/sentry_sdk/_init_implementation.py index eb02b3d11e..c2d77809c7 100644 --- a/sentry_sdk/_init_implementation.py +++ b/sentry_sdk/_init_implementation.py @@ -18,12 +18,10 @@ class _InitGuard: "functionality, and we will remove it in the next major release." ) - def __init__(self, client): - # type: (sentry_sdk.Client) -> None + def __init__(self, client: "sentry_sdk.Client") -> None: self._client = client - def __enter__(self): - # type: () -> _InitGuard + def __enter__(self) -> "_InitGuard": warnings.warn( self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE, stacklevel=2, @@ -32,8 +30,7 @@ def __enter__(self): return self - def __exit__(self, exc_type, exc_value, tb): - # type: (Any, Any, Any) -> None + def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None: warnings.warn( self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE, stacklevel=2, @@ -45,16 +42,14 @@ def __exit__(self, exc_type, exc_value, tb): c.close() -def _check_python_deprecations(): - # type: () -> None +def _check_python_deprecations() -> None: # Since we're likely to deprecate Python versions in the future, I'm keeping # this handy function around. Use this to detect the Python version used and # to output logger.warning()s if it's deprecated. pass -def _init(*args, **kwargs): - # type: (*Optional[str], **Any) -> ContextManager[Any] +def _init(*args: "Optional[str]", **kwargs: "Any") -> "ContextManager[Any]": """Initializes the SDK and optionally integrations. This takes the same arguments as the client constructor. diff --git a/sentry_sdk/_log_batcher.py b/sentry_sdk/_log_batcher.py index 6d793aceb7..f5c8f101a9 100644 --- a/sentry_sdk/_log_batcher.py +++ b/sentry_sdk/_log_batcher.py @@ -18,23 +18,21 @@ class LogBatcher: def __init__( self, - capture_func, # type: Callable[[Envelope], None] - record_lost_func, # type: Callable[..., None] - ): - # type: (...) -> None - self._log_buffer = [] # type: List[Log] + capture_func: "Callable[[Envelope], None]", + record_lost_func: "Callable[..., None]", + ) -> None: + self._log_buffer: List[Log] = [] self._capture_func = capture_func self._record_lost_func = record_lost_func self._running = True self._lock = threading.Lock() - self._flush_event = threading.Event() # type: threading.Event + self._flush_event: "threading.Event" = threading.Event() - self._flusher = None # type: Optional[threading.Thread] - self._flusher_pid = None # type: Optional[int] + self._flusher: "Optional[threading.Thread]" = None + self._flusher_pid: "Optional[int]" = None - def _ensure_thread(self): - # type: (...) -> bool + def _ensure_thread(self) -> bool: """For forking processes we might need to restart this thread. This ensures that our process actually has that thread running. """ @@ -66,8 +64,7 @@ def _ensure_thread(self): return True - def _flush_loop(self): - # type: (...) -> None + def _flush_loop(self) -> None: while self._running: self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random()) self._flush_event.clear() @@ -75,9 +72,8 @@ def _flush_loop(self): def add( self, - log, # type: Log - ): - # type: (...) -> None + log: "Log", + ) -> None: if not self._ensure_thread() or self._flusher is None: return None @@ -106,8 +102,7 @@ def add( if len(self._log_buffer) >= self.MAX_LOGS_BEFORE_FLUSH: self._flush_event.set() - def kill(self): - # type: (...) -> None + def kill(self) -> None: if self._flusher is None: return @@ -115,15 +110,12 @@ def kill(self): self._flush_event.set() self._flusher = None - def flush(self): - # type: (...) -> None + def flush(self) -> None: self._flush() @staticmethod - def _log_to_transport_format(log): - # type: (Log) -> Any - def format_attribute(val): - # type: (int | float | str | bool) -> Any + def _log_to_transport_format(log: "Log") -> "Any": + def format_attribute(val: int | float | str | bool) -> Any: if isinstance(val, bool): return {"value": val, "type": "boolean"} if isinstance(val, int): @@ -151,9 +143,7 @@ def format_attribute(val): return res - def _flush(self): - # type: (...) -> Optional[Envelope] - + def _flush(self) -> "Optional[Envelope]": envelope = Envelope( headers={"sent_at": format_timestamp(datetime.now(timezone.utc))} ) diff --git a/sentry_sdk/_lru_cache.py b/sentry_sdk/_lru_cache.py index cbadd9723b..16c238bcab 100644 --- a/sentry_sdk/_lru_cache.py +++ b/sentry_sdk/_lru_cache.py @@ -8,17 +8,15 @@ class LRUCache: - def __init__(self, max_size): - # type: (int) -> None + def __init__(self, max_size: int) -> None: if max_size <= 0: raise AssertionError(f"invalid max_size: {max_size}") self.max_size = max_size - self._data = {} # type: dict[Any, Any] + self._data: "dict[Any, Any]" = {} self.hits = self.misses = 0 self.full = False - def set(self, key, value): - # type: (Any, Any) -> None + def set(self, key: "Any", value: "Any") -> None: current = self._data.pop(key, _SENTINEL) if current is not _SENTINEL: self._data[key] = value @@ -29,8 +27,7 @@ def set(self, key, value): self._data[key] = value self.full = len(self._data) >= self.max_size - def get(self, key, default=None): - # type: (Any, Any) -> Any + def get(self, key: "Any", default: "Any" = None) -> "Any": try: ret = self._data.pop(key) except KeyError: @@ -42,6 +39,5 @@ def get(self, key, default=None): return ret - def get_all(self): - # type: () -> list[tuple[Any, Any]] + def get_all(self) -> "list[tuple[Any, Any]]": return list(self._data.items()) diff --git a/sentry_sdk/_metrics_batcher.py b/sentry_sdk/_metrics_batcher.py index 0db424cfcb..06df4d0fa1 100644 --- a/sentry_sdk/_metrics_batcher.py +++ b/sentry_sdk/_metrics_batcher.py @@ -18,23 +18,21 @@ class MetricsBatcher: def __init__( self, - capture_func, # type: Callable[[Envelope], None] - record_lost_func, # type: Callable[..., None] - ): - # type: (...) -> None - self._metric_buffer = [] # type: List[Metric] + capture_func: "Callable[[Envelope], None]", + record_lost_func: "Callable[..., None]", + ) -> None: + self._metric_buffer: List[Metric] = [] self._capture_func = capture_func self._record_lost_func = record_lost_func self._running = True self._lock = threading.Lock() - self._flush_event = threading.Event() # type: threading.Event + self._flush_event: "threading.Event" = threading.Event() - self._flusher = None # type: Optional[threading.Thread] - self._flusher_pid = None # type: Optional[int] + self._flusher: "Optional[threading.Thread]" = None + self._flusher_pid: "Optional[int]" = None - def _ensure_thread(self): - # type: (...) -> bool + def _ensure_thread(self) -> bool: if not self._running: return False @@ -59,8 +57,7 @@ def _ensure_thread(self): return True - def _flush_loop(self): - # type: (...) -> None + def _flush_loop(self) -> None: while self._running: self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random()) self._flush_event.clear() @@ -68,9 +65,8 @@ def _flush_loop(self): def add( self, - metric, # type: Metric - ): - # type: (...) -> None + metric: "Metric", + ) -> None: if not self._ensure_thread() or self._flusher is None: return None @@ -87,8 +83,7 @@ def add( if len(self._metric_buffer) >= self.MAX_METRICS_BEFORE_FLUSH: self._flush_event.set() - def kill(self): - # type: (...) -> None + def kill(self) -> None: if self._flusher is None: return @@ -96,15 +91,12 @@ def kill(self): self._flush_event.set() self._flusher = None - def flush(self): - # type: (...) -> None + def flush(self) -> None: self._flush() @staticmethod - def _metric_to_transport_format(metric): - # type: (Metric) -> Any - def format_attribute(val): - # type: (Union[int, float, str, bool]) -> Any + def _metric_to_transport_format(metric: "Metric") -> "Any": + def format_attribute(val: Union[int, float, str, bool]) -> Any: if isinstance(val, bool): return {"value": val, "type": "boolean"} if isinstance(val, int): @@ -134,9 +126,7 @@ def format_attribute(val): return res - def _flush(self): - # type: (...) -> Optional[Envelope] - + def _flush(self) -> "Optional[Envelope]": envelope = Envelope( headers={"sent_at": format_timestamp(datetime.now(timezone.utc))} ) diff --git a/sentry_sdk/_queue.py b/sentry_sdk/_queue.py index a21c86ec0a..9bdb76dddb 100644 --- a/sentry_sdk/_queue.py +++ b/sentry_sdk/_queue.py @@ -275,7 +275,7 @@ def get_nowait(self): # Initialize the queue representation def _init(self, maxsize): - self.queue = deque() # type: Any + self.queue: "Any" = deque() def _qsize(self): return len(self.queue) diff --git a/sentry_sdk/_types.py b/sentry_sdk/_types.py index 0426bf7a93..87332de1d3 100644 --- a/sentry_sdk/_types.py +++ b/sentry_sdk/_types.py @@ -18,32 +18,27 @@ class AnnotatedValue: __slots__ = ("value", "metadata") - def __init__(self, value, metadata): - # type: (Optional[Any], Dict[str, Any]) -> None + def __init__(self, value: "Optional[Any]", metadata: "Dict[str, Any]") -> None: self.value = value self.metadata = metadata - def __eq__(self, other): - # type: (Any) -> bool + def __eq__(self, other: "Any") -> bool: if not isinstance(other, AnnotatedValue): return False return self.value == other.value and self.metadata == other.metadata - def __str__(self): - # type: (AnnotatedValue) -> str + def __str__(self: "AnnotatedValue") -> str: return str({"value": str(self.value), "metadata": str(self.metadata)}) - def __len__(self): - # type: (AnnotatedValue) -> int + def __len__(self: "AnnotatedValue") -> int: if self.value is not None: return len(self.value) else: return 0 @classmethod - def removed_because_raw_data(cls): - # type: () -> AnnotatedValue + def removed_because_raw_data(cls) -> "AnnotatedValue": """The value was removed because it could not be parsed. This is done for request body values that are not json nor a form.""" return AnnotatedValue( value="", @@ -58,8 +53,7 @@ def removed_because_raw_data(cls): ) @classmethod - def removed_because_over_size_limit(cls, value=""): - # type: (Any) -> AnnotatedValue + def removed_because_over_size_limit(cls, value: "Any" = "") -> "AnnotatedValue": """ The actual value was removed because the size of the field exceeded the configured maximum size, for example specified with the max_request_body_size sdk option. @@ -77,8 +71,7 @@ def removed_because_over_size_limit(cls, value=""): ) @classmethod - def substituted_because_contains_sensitive_data(cls): - # type: () -> AnnotatedValue + def substituted_because_contains_sensitive_data(cls) -> "AnnotatedValue": """The actual value was removed because it contained sensitive information.""" return AnnotatedValue( value=SENSITIVE_DATA_SUBSTITUTE, diff --git a/sentry_sdk/_werkzeug.py b/sentry_sdk/_werkzeug.py index 0fa3d611f1..cdc3026c08 100644 --- a/sentry_sdk/_werkzeug.py +++ b/sentry_sdk/_werkzeug.py @@ -47,8 +47,7 @@ # We need this function because Django does not give us a "pure" http header # dict. So we might as well use it for all WSGI integrations. # -def _get_headers(environ): - # type: (Dict[str, str]) -> Iterator[Tuple[str, str]] +def _get_headers(environ: "Dict[str, str]") -> "Iterator[Tuple[str, str]]": """ Returns only proper HTTP headers. """ @@ -67,8 +66,7 @@ def _get_headers(environ): # `get_host` comes from `werkzeug.wsgi.get_host` # https://github.com/pallets/werkzeug/blob/1.0.1/src/werkzeug/wsgi.py#L145 # -def get_host(environ, use_x_forwarded_for=False): - # type: (Dict[str, str], bool) -> str +def get_host(environ: "Dict[str, str]", use_x_forwarded_for: bool = False) -> str: """ Return the host for the given WSGI environment. """ diff --git a/sentry_sdk/ai/monitoring.py b/sentry_sdk/ai/monitoring.py index 9dd1aa132c..44b844194c 100644 --- a/sentry_sdk/ai/monitoring.py +++ b/sentry_sdk/ai/monitoring.py @@ -17,22 +17,17 @@ _ai_pipeline_name = ContextVar("ai_pipeline_name", default=None) -def set_ai_pipeline_name(name): - # type: (Optional[str]) -> None +def set_ai_pipeline_name(name: "Optional[str]") -> None: _ai_pipeline_name.set(name) -def get_ai_pipeline_name(): - # type: () -> Optional[str] +def get_ai_pipeline_name() -> "Optional[str]": return _ai_pipeline_name.get() -def ai_track(description, **span_kwargs): - # type: (str, Any) -> Callable[[F], F] - def decorator(f): - # type: (F) -> F - def sync_wrapped(*args, **kwargs): - # type: (Any, Any) -> Any +def ai_track(description: str, **span_kwargs: "Any") -> "Callable[[F], F]": + def decorator(f: F) -> F: + def sync_wrapped(*args: Any, **kwargs: Any) -> Any: curr_pipeline = _ai_pipeline_name.get() op = span_kwargs.pop("op", "ai.run" if curr_pipeline else "ai.pipeline") @@ -60,8 +55,7 @@ def sync_wrapped(*args, **kwargs): _ai_pipeline_name.set(None) return res - async def async_wrapped(*args, **kwargs): - # type: (Any, Any) -> Any + async def async_wrapped(*args: "Any", **kwargs: "Any") -> "Any": curr_pipeline = _ai_pipeline_name.get() op = span_kwargs.pop("op", "ai.run" if curr_pipeline else "ai.pipeline") @@ -98,15 +92,13 @@ async def async_wrapped(*args, **kwargs): def record_token_usage( - span, - input_tokens=None, - input_tokens_cached=None, - output_tokens=None, - output_tokens_reasoning=None, - total_tokens=None, -): - # type: (Span, Optional[int], Optional[int], Optional[int], Optional[int], Optional[int]) -> None - + span: "Span", + input_tokens: "Optional[int]" = None, + input_tokens_cached: "Optional[int]" = None, + output_tokens: "Optional[int]" = None, + output_tokens_reasoning: "Optional[int]" = None, + total_tokens: "Optional[int]" = None, +) -> None: # TODO: move pipeline name elsewhere ai_pipeline_name = get_ai_pipeline_name() if ai_pipeline_name: diff --git a/sentry_sdk/ai/utils.py b/sentry_sdk/ai/utils.py index 9a1853110f..922bac4c32 100644 --- a/sentry_sdk/ai/utils.py +++ b/sentry_sdk/ai/utils.py @@ -35,8 +35,7 @@ class GEN_AI_ALLOWED_MESSAGE_ROLES: GEN_AI_MESSAGE_ROLE_MAPPING[source_role] = target_role -def _normalize_data(data, unpack=True): - # type: (Any, bool) -> Any +def _normalize_data(data: "Any", unpack: bool = True) -> "Any": # convert pydantic data (e.g. OpenAI v1+) to json compatible format if hasattr(data, "model_dump"): # Check if it's a class (type) rather than an instance @@ -61,8 +60,9 @@ def _normalize_data(data, unpack=True): return data if isinstance(data, (int, float, bool, str)) else str(data) -def set_data_normalized(span, key, value, unpack=True): - # type: (Span, str, Any, bool) -> None +def set_data_normalized( + span: "Span", key: str, value: "Any", unpack: bool = True +) -> None: normalized = _normalize_data(value, unpack=unpack) if isinstance(normalized, (int, float, bool, str)): span.set_data(key, normalized) @@ -70,8 +70,7 @@ def set_data_normalized(span, key, value, unpack=True): span.set_data(key, json.dumps(normalized)) -def normalize_message_role(role): - # type: (str) -> str +def normalize_message_role(role: str) -> str: """ Normalize a message role to one of the 4 allowed gen_ai role values. Maps "ai" -> "assistant" and keeps other standard roles unchanged. @@ -79,8 +78,7 @@ def normalize_message_role(role): return GEN_AI_MESSAGE_ROLE_MAPPING.get(role, role) -def normalize_message_roles(messages): - # type: (list[dict[str, Any]]) -> list[dict[str, Any]] +def normalize_message_roles(messages: "list[dict[str, Any]]") -> "list[dict[str, Any]]": """ Normalize roles in a list of messages to use standard gen_ai role values. Creates a deep copy to avoid modifying the original messages. @@ -98,8 +96,7 @@ def normalize_message_roles(messages): return normalized_messages -def get_start_span_function(): - # type: () -> Callable[..., Any] +def get_start_span_function() -> "Callable[..., Any]": current_span = sentry_sdk.get_current_span() transaction_exists = ( current_span is not None and current_span.containing_transaction is not None @@ -107,8 +104,7 @@ def get_start_span_function(): return sentry_sdk.start_span if transaction_exists else sentry_sdk.start_transaction -def _find_truncation_index(messages, max_bytes): - # type: (List[Dict[str, Any]], int) -> int +def _find_truncation_index(messages: "List[Dict[str, Any]]", max_bytes: int) -> int: """ Find the index of the first message that would exceed the max bytes limit. Compute the individual message sizes, and return the index of the first message from the back @@ -124,8 +120,9 @@ def _find_truncation_index(messages, max_bytes): return 0 -def truncate_messages_by_size(messages, max_bytes=MAX_GEN_AI_MESSAGE_BYTES): - # type: (List[Dict[str, Any]], int) -> Tuple[List[Dict[str, Any]], int] +def truncate_messages_by_size( + messages: "List[Dict[str, Any]]", max_bytes: int = MAX_GEN_AI_MESSAGE_BYTES +) -> "Tuple[List[Dict[str, Any]], int]": serialized_json = json.dumps(messages, separators=(",", ":")) current_size = len(serialized_json.encode("utf-8")) @@ -137,9 +134,11 @@ def truncate_messages_by_size(messages, max_bytes=MAX_GEN_AI_MESSAGE_BYTES): def truncate_and_annotate_messages( - messages, span, scope, max_bytes=MAX_GEN_AI_MESSAGE_BYTES -): - # type: (Optional[List[Dict[str, Any]]], Any, Any, int) -> Optional[List[Dict[str, Any]]] + messages: "Optional[List[Dict[str, Any]]]", + span: "Any", + scope: "Any", + max_bytes: int = MAX_GEN_AI_MESSAGE_BYTES, +) -> "Optional[List[Dict[str, Any]]]": if not messages: return None diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 43758b4d78..c4e2229938 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -43,8 +43,7 @@ F = TypeVar("F", bound=Callable[..., Any]) else: - def overload(x): - # type: (T) -> T + def overload(x: "T") -> "T": return x @@ -89,8 +88,7 @@ def overload(x): ] -def scopemethod(f): - # type: (F) -> F +def scopemethod(f: "F") -> "F": f.__doc__ = "%s\n\n%s" % ( "Alias for :py:meth:`sentry_sdk.Scope.%s`" % f.__name__, inspect.getdoc(getattr(Scope, f.__name__)), @@ -98,8 +96,7 @@ def scopemethod(f): return f -def clientmethod(f): - # type: (F) -> F +def clientmethod(f: "F") -> "F": f.__doc__ = "%s\n\n%s" % ( "Alias for :py:meth:`sentry_sdk.Client.%s`" % f.__name__, inspect.getdoc(getattr(Client, f.__name__)), @@ -108,13 +105,11 @@ def clientmethod(f): @scopemethod -def get_client(): - # type: () -> BaseClient +def get_client() -> "BaseClient": return Scope.get_client() -def is_initialized(): - # type: () -> bool +def is_initialized() -> bool: """ .. versionadded:: 2.0.0 @@ -128,26 +123,22 @@ def is_initialized(): @scopemethod -def get_global_scope(): - # type: () -> Scope +def get_global_scope() -> "Scope": return Scope.get_global_scope() @scopemethod -def get_isolation_scope(): - # type: () -> Scope +def get_isolation_scope() -> "Scope": return Scope.get_isolation_scope() @scopemethod -def get_current_scope(): - # type: () -> Scope +def get_current_scope() -> "Scope": return Scope.get_current_scope() @scopemethod -def last_event_id(): - # type: () -> Optional[str] +def last_event_id() -> "Optional[str]": """ See :py:meth:`sentry_sdk.Scope.last_event_id` documentation regarding this method's limitations. @@ -157,23 +148,21 @@ def last_event_id(): @scopemethod def capture_event( - event, # type: Event - hint=None, # type: Optional[Hint] - scope=None, # type: Optional[Any] - **scope_kwargs, # type: Any -): - # type: (...) -> Optional[str] + event: "Event", + hint: "Optional[Hint]" = None, + scope: "Optional[Any]" = None, + **scope_kwargs: "Any", +) -> "Optional[str]": return get_current_scope().capture_event(event, hint, scope=scope, **scope_kwargs) @scopemethod def capture_message( - message, # type: str - level=None, # type: Optional[LogLevelStr] - scope=None, # type: Optional[Any] - **scope_kwargs, # type: Any -): - # type: (...) -> Optional[str] + message: str, + level: "Optional[LogLevelStr]" = None, + scope: "Optional[Any]" = None, + **scope_kwargs: "Any", +) -> "Optional[str]": return get_current_scope().capture_message( message, level, scope=scope, **scope_kwargs ) @@ -181,23 +170,21 @@ def capture_message( @scopemethod def capture_exception( - error=None, # type: Optional[Union[BaseException, ExcInfo]] - scope=None, # type: Optional[Any] - **scope_kwargs, # type: Any -): - # type: (...) -> Optional[str] + error: "Optional[Union[BaseException, ExcInfo]]" = None, + scope: "Optional[Any]" = None, + **scope_kwargs: "Any", +) -> "Optional[str]": return get_current_scope().capture_exception(error, scope=scope, **scope_kwargs) @scopemethod def add_attachment( - bytes=None, # type: Union[None, bytes, Callable[[], bytes]] - filename=None, # type: Optional[str] - path=None, # type: Optional[str] - content_type=None, # type: Optional[str] - add_to_transactions=False, # type: bool -): - # type: (...) -> None + bytes: "Union[None, bytes, Callable[[], bytes]]" = None, + filename: "Optional[str]" = None, + path: "Optional[str]" = None, + content_type: "Optional[str]" = None, + add_to_transactions: bool = False, +) -> None: return get_isolation_scope().add_attachment( bytes, filename, path, content_type, add_to_transactions ) @@ -205,32 +192,28 @@ def add_attachment( @scopemethod def add_breadcrumb( - crumb=None, # type: Optional[Breadcrumb] - hint=None, # type: Optional[BreadcrumbHint] - **kwargs, # type: Any -): - # type: (...) -> None + crumb: "Optional[Breadcrumb]" = None, + hint: "Optional[BreadcrumbHint]" = None, + **kwargs: "Any", +) -> None: return get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs) @overload -def configure_scope(): - # type: () -> ContextManager[Scope] +def configure_scope() -> "ContextManager[Scope]": pass @overload def configure_scope( # noqa: F811 - callback, # type: Callable[[Scope], None] -): - # type: (...) -> None + callback: "Callable[[Scope], None]", +) -> None: pass def configure_scope( # noqa: F811 - callback=None, # type: Optional[Callable[[Scope], None]] -): - # type: (...) -> Optional[ContextManager[Scope]] + callback: "Optional[Callable[[Scope], None]]" = None, +) -> "Optional[ContextManager[Scope]]": """ Reconfigures the scope. @@ -256,31 +239,27 @@ def configure_scope( # noqa: F811 return None @contextmanager - def inner(): - # type: () -> Generator[Scope, None, None] + def inner() -> "Generator[Scope, None, None]": yield scope return inner() @overload -def push_scope(): - # type: () -> ContextManager[Scope] +def push_scope() -> "ContextManager[Scope]": pass @overload def push_scope( # noqa: F811 - callback, # type: Callable[[Scope], None] -): - # type: (...) -> None + callback: "Callable[[Scope], None]", +) -> None: pass def push_scope( # noqa: F811 - callback=None, # type: Optional[Callable[[Scope], None]] -): - # type: (...) -> Optional[ContextManager[Scope]] + callback: "Optional[Callable[[Scope], None]]" = None, +) -> "Optional[ContextManager[Scope]]": """ Pushes a new layer on the scope stack. @@ -309,66 +288,57 @@ def push_scope( # noqa: F811 @scopemethod -def set_tag(key, value): - # type: (str, Any) -> None +def set_tag(key: str, value: "Any") -> None: return get_isolation_scope().set_tag(key, value) @scopemethod -def set_tags(tags): - # type: (Mapping[str, object]) -> None +def set_tags(tags: "Mapping[str, object]") -> None: return get_isolation_scope().set_tags(tags) @scopemethod -def set_context(key, value): - # type: (str, Dict[str, Any]) -> None +def set_context(key: str, value: "Dict[str, Any]") -> None: return get_isolation_scope().set_context(key, value) @scopemethod -def set_extra(key, value): - # type: (str, Any) -> None +def set_extra(key: str, value: "Any") -> None: return get_isolation_scope().set_extra(key, value) @scopemethod -def set_user(value): - # type: (Optional[Dict[str, Any]]) -> None +def set_user(value: "Optional[Dict[str, Any]]") -> None: return get_isolation_scope().set_user(value) @scopemethod -def set_level(value): - # type: (LogLevelStr) -> None +def set_level(value: "LogLevelStr") -> None: return get_isolation_scope().set_level(value) @clientmethod def flush( - timeout=None, # type: Optional[float] - callback=None, # type: Optional[Callable[[int, float], None]] -): - # type: (...) -> None + timeout: "Optional[float]" = None, + callback: "Optional[Callable[[int, float], None]]" = None, +) -> None: return get_client().flush(timeout=timeout, callback=callback) @scopemethod def start_span( - **kwargs, # type: Any -): - # type: (...) -> Span + **kwargs: "Any", +) -> "Span": return get_current_scope().start_span(**kwargs) @scopemethod def start_transaction( - transaction=None, # type: Optional[Transaction] - instrumenter=INSTRUMENTER.SENTRY, # type: str - custom_sampling_context=None, # type: Optional[SamplingContext] - **kwargs, # type: Unpack[TransactionKwargs] -): - # type: (...) -> Union[Transaction, NoOpSpan] + transaction: "Optional[Transaction]" = None, + instrumenter: str = INSTRUMENTER.SENTRY, + custom_sampling_context: "Optional[SamplingContext]" = None, + **kwargs: "Unpack[TransactionKwargs]", +) -> "Union[Transaction, NoOpSpan]": """ Start and return a transaction on the current scope. @@ -405,8 +375,7 @@ def start_transaction( ) -def set_measurement(name, value, unit=""): - # type: (str, float, MeasurementUnit) -> None +def set_measurement(name: str, value: float, unit: "MeasurementUnit" = "") -> None: """ .. deprecated:: 2.28.0 This function is deprecated and will be removed in the next major release. @@ -416,24 +385,21 @@ def set_measurement(name, value, unit=""): transaction.set_measurement(name, value, unit) -def get_current_span(scope=None): - # type: (Optional[Scope]) -> Optional[Span] +def get_current_span(scope: "Optional[Scope]" = None) -> "Optional[Span]": """ Returns the currently active span if there is one running, otherwise `None` """ return tracing_utils.get_current_span(scope) -def get_traceparent(): - # type: () -> Optional[str] +def get_traceparent() -> "Optional[str]": """ Returns the traceparent either from the active span or from the scope. """ return get_current_scope().get_traceparent() -def get_baggage(): - # type: () -> Optional[str] +def get_baggage() -> "Optional[str]": """ Returns Baggage either from the active span or from the scope. """ @@ -445,9 +411,12 @@ def get_baggage(): def continue_trace( - environ_or_headers, op=None, name=None, source=None, origin="manual" -): - # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], str) -> Transaction + environ_or_headers: "Dict[str, Any]", + op: "Optional[str]" = None, + name: "Optional[str]" = None, + source: "Optional[str]" = None, + origin: str = "manual", +) -> "Transaction": """ Sets the propagation context from environment or headers and returns a transaction. """ @@ -458,26 +427,27 @@ def continue_trace( @scopemethod def start_session( - session_mode="application", # type: str -): - # type: (...) -> None + session_mode: str = "application", +) -> None: return get_isolation_scope().start_session(session_mode=session_mode) @scopemethod -def end_session(): - # type: () -> None +def end_session() -> None: return get_isolation_scope().end_session() @scopemethod -def set_transaction_name(name, source=None): - # type: (str, Optional[str]) -> None +def set_transaction_name(name: str, source: "Optional[str]" = None) -> None: return get_current_scope().set_transaction_name(name, source) -def update_current_span(op=None, name=None, attributes=None, data=None): - # type: (Optional[str], Optional[str], Optional[dict[str, Union[str, int, float, bool]]], Optional[dict[str, Any]]) -> None +def update_current_span( + op: "Optional[str]" = None, + name: "Optional[str]" = None, + attributes: "Optional[dict[str, Union[str, int, float, bool]]]" = None, + data: "Optional[dict[str, Any]]" = None, +) -> None: """ Update the current active span with the provided parameters. diff --git a/sentry_sdk/attachments.py b/sentry_sdk/attachments.py index e5404f8658..8ad85f4335 100644 --- a/sentry_sdk/attachments.py +++ b/sentry_sdk/attachments.py @@ -31,13 +31,12 @@ class Attachment: def __init__( self, - bytes=None, # type: Union[None, bytes, Callable[[], bytes]] - filename=None, # type: Optional[str] - path=None, # type: Optional[str] - content_type=None, # type: Optional[str] - add_to_transactions=False, # type: bool - ): - # type: (...) -> None + bytes: "Union[None, bytes, Callable[[], bytes]]" = None, + filename: "Optional[str]" = None, + path: "Optional[str]" = None, + content_type: "Optional[str]" = None, + add_to_transactions: bool = False, + ) -> None: if bytes is None and path is None: raise TypeError("path or raw bytes required for attachment") if filename is None and path is not None: @@ -52,10 +51,9 @@ def __init__( self.content_type = content_type self.add_to_transactions = add_to_transactions - def to_envelope_item(self): - # type: () -> Item + def to_envelope_item(self) -> "Item": """Returns an envelope item for this attachment.""" - payload = None # type: Union[None, PayloadRef, bytes] + payload: "Union[None, PayloadRef, bytes]" = None if self.bytes is not None: if callable(self.bytes): payload = self.bytes() @@ -70,6 +68,5 @@ def to_envelope_item(self): filename=self.filename, ) - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "" % (self.filename,) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index ad682b1979..3648f17c12 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -77,17 +77,16 @@ _client_init_debug = ContextVar("client_init_debug") -SDK_INFO = { +SDK_INFO: "SDKInfo" = { "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): - # type: (*Optional[str], **Any) -> Dict[str, Any] +def _get_options(*args: "Optional[str]", **kwargs: "Any") -> "Dict[str, Any]": if args and (isinstance(args[0], (bytes, str)) or args[0] is None): - dsn = args[0] # type: Optional[str] + dsn: "Optional[str]" = args[0] args = args[1:] else: dsn = None @@ -178,41 +177,36 @@ class BaseClient: The basic definition of a client that is used for sending data to Sentry. """ - spotlight = None # type: Optional[SpotlightClient] + spotlight: "Optional[SpotlightClient]" = None - def __init__(self, options=None): - # type: (Optional[Dict[str, Any]]) -> None - self.options = options if options is not None else DEFAULT_OPTIONS # type: Dict[str, Any] + def __init__(self, options: "Optional[Dict[str, Any]]" = None) -> None: + self.options: Dict[str, Any] = ( + options if options is not None else DEFAULT_OPTIONS + ) - self.transport = None # type: Optional[Transport] - self.monitor = None # type: Optional[Monitor] - self.log_batcher = None # type: Optional[LogBatcher] - self.metrics_batcher = None # type: Optional[MetricsBatcher] + self.transport: "Optional[Transport]" = None + self.monitor: "Optional[Monitor]" = None + self.log_batcher: "Optional[LogBatcher]" = None + self.metrics_batcher: "Optional[MetricsBatcher]" = None - def __getstate__(self, *args, **kwargs): - # type: (*Any, **Any) -> Any + def __getstate__(self, *args: "Any", **kwargs: "Any") -> "Any": return {"options": {}} - def __setstate__(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def __setstate__(self, *args: "Any", **kwargs: "Any") -> None: pass @property - def dsn(self): - # type: () -> Optional[str] + def dsn(self) -> "Optional[str]": return None @property - def parsed_dsn(self): - # type: () -> Optional[Dsn] + def parsed_dsn(self) -> "Optional[Dsn]": return None - def should_send_default_pii(self): - # type: () -> bool + def should_send_default_pii(self) -> bool: return False - def is_active(self): - # type: () -> bool + def is_active(self) -> bool: """ .. versionadded:: 2.0.0 @@ -220,52 +214,41 @@ def is_active(self): """ return False - def capture_event(self, *args, **kwargs): - # type: (*Any, **Any) -> Optional[str] + def capture_event(self, *args: "Any", **kwargs: "Any") -> "Optional[str]": return None - def _capture_log(self, log): - # type: (Log) -> None + def _capture_log(self, log: "Log") -> None: pass - def _capture_metric(self, metric): - # type: (Metric) -> None + def _capture_metric(self, metric: "Metric") -> None: pass - def capture_session(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def capture_session(self, *args: "Any", **kwargs: "Any") -> None: return None if TYPE_CHECKING: @overload - def get_integration(self, name_or_class): - # type: (str) -> Optional[Integration] - ... + def get_integration(self, name_or_class: str) -> "Optional[Integration]": ... @overload - def get_integration(self, name_or_class): - # type: (type[I]) -> Optional[I] - ... + def get_integration(self, name_or_class: "type[I]") -> "Optional[I]": ... - def get_integration(self, name_or_class): - # type: (Union[str, type[Integration]]) -> Optional[Integration] + def get_integration( + self, name_or_class: "Union[str, type[Integration]]" + ) -> "Optional[Integration]": return None - def close(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def close(self, *args: "Any", **kwargs: "Any") -> None: return None - def flush(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def flush(self, *args: "Any", **kwargs: "Any") -> None: return None - def __enter__(self): - # type: () -> BaseClient + def __enter__(self) -> "BaseClient": return self - def __exit__(self, exc_type, exc_value, tb): - # type: (Any, Any, Any) -> None + def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None: return None @@ -289,22 +272,20 @@ class _Client(BaseClient): Alias of :py:class:`sentry_sdk.Client`. (Was created for better intelisense support) """ - def __init__(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def __init__(self, *args: "Any", **kwargs: "Any") -> None: super(_Client, self).__init__(options=get_options(*args, **kwargs)) self._init_impl() - def __getstate__(self): - # type: () -> Any + def __getstate__(self) -> "Any": return {"options": self.options} - def __setstate__(self, state): - # type: (Any) -> None + def __setstate__(self, state: "Any") -> None: self.options = state["options"] self._init_impl() - def _setup_instrumentation(self, functions_to_trace): - # type: (Sequence[Dict[str, str]]) -> None + def _setup_instrumentation( + self, functions_to_trace: "Sequence[Dict[str, str]]" + ) -> None: """ Instruments the functions given in the list `functions_to_trace` with the `@sentry_sdk.tracing.trace` decorator. """ @@ -354,24 +335,21 @@ def _setup_instrumentation(self, functions_to_trace): e, ) - def _init_impl(self): - # type: () -> None + def _init_impl(self) -> None: old_debug = _client_init_debug.get(False) - def _capture_envelope(envelope): - # type: (Envelope) -> None + def _capture_envelope(envelope: "Envelope") -> None: if self.spotlight is not None: self.spotlight.capture_envelope(envelope) if self.transport is not None: self.transport.capture_envelope(envelope) def _record_lost_event( - reason, # type: str - data_category, # type: EventDataCategory - item=None, # type: Optional[Item] - quantity=1, # type: int - ): - # type: (...) -> None + reason: str, + data_category: "EventDataCategory", + item: "Optional[Item]" = None, + quantity: int = 1, + ) -> None: if self.transport is not None: self.transport.record_lost_event( reason=reason, @@ -485,8 +463,7 @@ def _record_lost_event( # need to check if it's safe to use them. check_uwsgi_thread_support() - def is_active(self): - # type: () -> bool + def is_active(self) -> bool: """ .. versionadded:: 2.0.0 @@ -494,8 +471,7 @@ def is_active(self): """ return True - def should_send_default_pii(self): - # type: () -> bool + def should_send_default_pii(self) -> bool: """ .. versionadded:: 2.0.0 @@ -504,27 +480,23 @@ def should_send_default_pii(self): return self.options.get("send_default_pii") or False @property - def dsn(self): - # type: () -> Optional[str] + def dsn(self) -> "Optional[str]": """Returns the configured DSN as string.""" return self.options["dsn"] @property - def parsed_dsn(self): - # type: () -> Optional[Dsn] + def parsed_dsn(self) -> "Optional[Dsn]": """Returns the configured parsed DSN object.""" return self.transport.parsed_dsn if self.transport else None def _prepare_event( self, - event, # type: Event - hint, # type: Hint - scope, # type: Optional[Scope] - ): - # type: (...) -> Optional[Event] - - previous_total_spans = None # type: Optional[int] - previous_total_breadcrumbs = None # type: Optional[int] + event: "Event", + hint: "Hint", + scope: "Optional[Scope]", + ) -> "Optional[Event]": + previous_total_spans: Optional[int] = None + previous_total_breadcrumbs: "Optional[int]" = None if event.get("timestamp") is None: event["timestamp"] = datetime.now(timezone.utc) @@ -559,7 +531,7 @@ def _prepare_event( "event_processor", data_category="span", quantity=spans_delta ) - dropped_spans = event.pop("_dropped_spans", 0) + spans_delta # type: int + dropped_spans: int = event.pop("_dropped_spans", 0) + spans_delta if dropped_spans > 0: previous_total_spans = spans_before + dropped_spans if scope._n_breadcrumbs_truncated > 0: @@ -622,7 +594,7 @@ def _prepare_event( event_scrubber.scrub_event(event) if scope is not None and scope._gen_ai_original_message_count: - spans = event.get("spans", []) # type: List[Dict[str, Any]] | AnnotatedValue + spans: "List[Dict[str, Any]] | AnnotatedValue" = event.get("spans", []) if isinstance(spans, list): for span in spans: span_id = span.get("span_id", None) @@ -716,8 +688,7 @@ def _prepare_event( return event - def _is_ignored_error(self, event, hint): - # type: (Event, Hint) -> bool + def _is_ignored_error(self, event: "Event", hint: "Hint") -> bool: exc_info = hint.get("exc_info") if exc_info is None: return False @@ -740,11 +711,10 @@ def _is_ignored_error(self, event, hint): def _should_capture( self, - event, # type: Event - hint, # type: Hint - scope=None, # type: Optional[Scope] - ): - # type: (...) -> bool + event: "Event", + hint: "Hint", + scope: "Optional[Scope]" = None, + ) -> bool: # Transactions are sampled independent of error events. is_transaction = event.get("type") == "transaction" if is_transaction: @@ -762,10 +732,9 @@ def _should_capture( def _should_sample_error( self, - event, # type: Event - hint, # type: Hint - ): - # type: (...) -> bool + event: "Event", + hint: "Hint", + ) -> bool: error_sampler = self.options.get("error_sampler", None) if callable(error_sampler): @@ -810,11 +779,9 @@ def _should_sample_error( def _update_session_from_event( self, - session, # type: Session - event, # type: Event - ): - # type: (...) -> None - + session: "Session", + event: "Event", + ) -> None: crashed = False errored = False user_agent = None @@ -849,11 +816,10 @@ def _update_session_from_event( def capture_event( self, - event, # type: Event - hint=None, # type: Optional[Hint] - scope=None, # type: Optional[Scope] - ): - # type: (...) -> Optional[str] + event: "Event", + hint: "Optional[Hint]" = None, + scope: "Optional[Scope]" = None, + ) -> "Optional[str]": """Captures an event. :param event: A ready-made event that can be directly sent to Sentry. @@ -864,7 +830,7 @@ 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. """ - hint = dict(hint or ()) # type: Hint + hint: "Hint" = dict(hint or ()) if not self._should_capture(event, hint, scope): return None @@ -899,10 +865,10 @@ def capture_event( trace_context = event_opt.get("contexts", {}).get("trace") or {} dynamic_sampling_context = trace_context.pop("dynamic_sampling_context", {}) - headers = { + headers: "dict[str, object]" = { "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 @@ -932,8 +898,7 @@ def capture_event( return return_value - def _capture_log(self, log): - # type: (Optional[Log]) -> None + def _capture_log(self, log: "Optional[Log]") -> None: if not has_logs_enabled(self.options) or log is None: return @@ -1000,8 +965,7 @@ def _capture_log(self, log): if self.log_batcher: self.log_batcher.add(log) - def _capture_metric(self, metric): - # type: (Optional[Metric]) -> None + def _capture_metric(self, metric: "Optional[Metric]") -> None: if not has_metrics_enabled(self.options) or metric is None: return @@ -1066,9 +1030,8 @@ def _capture_metric(self, metric): def capture_session( self, - session, # type: Session - ): - # type: (...) -> None + session: "Session", + ) -> None: if not session.release: logger.info("Discarded session update because of missing release") else: @@ -1077,20 +1040,15 @@ def capture_session( if TYPE_CHECKING: @overload - def get_integration(self, name_or_class): - # type: (str) -> Optional[Integration] - ... + def get_integration(self, name_or_class: str) -> "Optional[Integration]": ... @overload - def get_integration(self, name_or_class): - # type: (type[I]) -> Optional[I] - ... + def get_integration(self, name_or_class: "type[I]") -> "Optional[I]": ... def get_integration( self, - name_or_class, # type: Union[str, Type[Integration]] - ): - # type: (...) -> Optional[Integration] + name_or_class: "Union[str, Type[Integration]]", + ) -> "Optional[Integration]": """Returns the integration for this client by name or class. If the client does not have that integration then `None` is returned. """ @@ -1105,10 +1063,9 @@ def get_integration( def close( self, - timeout=None, # type: Optional[float] - callback=None, # type: Optional[Callable[[int, float], None]] - ): - # type: (...) -> None + timeout: "Optional[float]" = None, + callback: "Optional[Callable[[int, float], None]]" = None, + ) -> None: """ Close the client and shut down the transport. Arguments have the same semantics as :py:meth:`Client.flush`. @@ -1127,10 +1084,9 @@ def close( def flush( self, - timeout=None, # type: Optional[float] - callback=None, # type: Optional[Callable[[int, float], None]] - ): - # type: (...) -> None + timeout: "Optional[float]" = None, + callback: "Optional[Callable[[int, float], None]]" = None, + ) -> None: """ Wait for the current events to be sent. @@ -1148,12 +1104,10 @@ def flush( self.metrics_batcher.flush() self.transport.flush(timeout=timeout, callback=callback) - def __enter__(self): - # type: () -> _Client + def __enter__(self) -> "_Client": return self - def __exit__(self, exc_type, exc_value, tb): - # type: (Any, Any, Any) -> None + def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None: self.close() diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index ae6bc10f99..4fb69da071 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -30,21 +30,22 @@ class CompressionAlgo(Enum): if TYPE_CHECKING: - import sentry_sdk + from typing import ( + AbstractSet, + Any, + Callable, + Dict, + List, + Optional, + Sequence, + Tuple, + Type, + Union, + ) - from typing import Optional - from typing import Callable - from typing import Union - from typing import List - from typing import Type - from typing import Dict - from typing import Any - from typing import Sequence - from typing import Tuple - from typing import AbstractSet - from typing_extensions import Literal - from typing_extensions import TypedDict + from typing_extensions import Literal, TypedDict + import sentry_sdk from sentry_sdk._types import ( BreadcrumbProcessor, ContinuousProfilerMode, @@ -104,8 +105,7 @@ class SPANTEMPLATE(str, Enum): AI_TOOL = "ai_tool" AI_CHAT = "ai_chat" - def __str__(self): - # type: () -> str + def __str__(self) -> str: return self.value @@ -953,80 +953,79 @@ class OP: class ClientConstructor: def __init__( self, - dsn=None, # type: Optional[str] + dsn: "Optional[str]" = None, *, - max_breadcrumbs=DEFAULT_MAX_BREADCRUMBS, # type: int - release=None, # type: Optional[str] - environment=None, # type: Optional[str] - server_name=None, # type: Optional[str] - shutdown_timeout=2, # type: float - integrations=[], # type: Sequence[sentry_sdk.integrations.Integration] # noqa: B006 - in_app_include=[], # type: List[str] # noqa: B006 - in_app_exclude=[], # type: List[str] # noqa: B006 - default_integrations=True, # type: bool - dist=None, # type: Optional[str] - transport=None, # type: Optional[Union[sentry_sdk.transport.Transport, Type[sentry_sdk.transport.Transport], Callable[[Event], None]]] - transport_queue_size=DEFAULT_QUEUE_SIZE, # type: int - sample_rate=1.0, # type: float - send_default_pii=None, # type: Optional[bool] - http_proxy=None, # type: Optional[str] - https_proxy=None, # type: Optional[str] - ignore_errors=[], # type: Sequence[Union[type, str]] # noqa: B006 - max_request_body_size="medium", # type: str - socket_options=None, # type: Optional[List[Tuple[int, int, int | bytes]]] - keep_alive=None, # type: Optional[bool] - before_send=None, # type: Optional[EventProcessor] - before_breadcrumb=None, # type: Optional[BreadcrumbProcessor] - debug=None, # type: Optional[bool] - attach_stacktrace=False, # type: bool - ca_certs=None, # type: Optional[str] - propagate_traces=True, # type: bool - traces_sample_rate=None, # type: Optional[float] - traces_sampler=None, # type: Optional[TracesSampler] - profiles_sample_rate=None, # type: Optional[float] - profiles_sampler=None, # type: Optional[TracesSampler] - profiler_mode=None, # type: Optional[ProfilerMode] - profile_lifecycle="manual", # type: Literal["manual", "trace"] - profile_session_sample_rate=None, # type: Optional[float] - auto_enabling_integrations=True, # type: bool - disabled_integrations=None, # type: Optional[Sequence[sentry_sdk.integrations.Integration]] - auto_session_tracking=True, # type: bool - send_client_reports=True, # type: bool - _experiments={}, # type: Experiments # noqa: B006 - proxy_headers=None, # type: Optional[Dict[str, str]] - instrumenter=INSTRUMENTER.SENTRY, # type: Optional[str] - before_send_transaction=None, # type: Optional[TransactionProcessor] - project_root=None, # type: Optional[str] - enable_tracing=None, # type: Optional[bool] - include_local_variables=True, # type: Optional[bool] - include_source_context=True, # type: Optional[bool] - trace_propagation_targets=[ # noqa: B006 + max_breadcrumbs: int = DEFAULT_MAX_BREADCRUMBS, + release: "Optional[str]" = None, + environment: "Optional[str]" = None, + server_name: "Optional[str]" = None, + shutdown_timeout: float = 2, + integrations: "Sequence[sentry_sdk.integrations.Integration]" = [], # noqa: B006 + in_app_include: "List[str]" = [], # noqa: B006 + in_app_exclude: "List[str]" = [], # noqa: B006 + default_integrations: bool = True, + dist: "Optional[str]" = None, + transport: "Optional[Union[sentry_sdk.transport.Transport, Type[sentry_sdk.transport.Transport], Callable[[Event], None]]]" = None, + transport_queue_size: int = DEFAULT_QUEUE_SIZE, + sample_rate: float = 1.0, + send_default_pii: "Optional[bool]" = None, + http_proxy: "Optional[str]" = None, + https_proxy: "Optional[str]" = None, + ignore_errors: "Sequence[Union[type, str]]" = [], # noqa: B006 + max_request_body_size: str = "medium", + socket_options: "Optional[List[Tuple[int, int, int | bytes]]]" = None, + keep_alive: "Optional[bool]" = None, + before_send: "Optional[EventProcessor]" = None, + before_breadcrumb: "Optional[BreadcrumbProcessor]" = None, + debug: "Optional[bool]" = None, + attach_stacktrace: bool = False, + ca_certs: "Optional[str]" = None, + propagate_traces: bool = True, + traces_sample_rate: "Optional[float]" = None, + traces_sampler: "Optional[TracesSampler]" = None, + profiles_sample_rate: "Optional[float]" = None, + profiles_sampler: "Optional[TracesSampler]" = None, + profiler_mode: "Optional[ProfilerMode]" = None, + profile_lifecycle: 'Literal["manual", "trace"]' = "manual", + profile_session_sample_rate: "Optional[float]" = None, + auto_enabling_integrations: bool = True, + disabled_integrations: "Optional[Sequence[sentry_sdk.integrations.Integration]]" = None, + auto_session_tracking: bool = True, + send_client_reports: bool = True, + _experiments: "Experiments" = {}, # noqa: B006 + proxy_headers: "Optional[Dict[str, str]]" = None, + instrumenter: "Optional[str]" = INSTRUMENTER.SENTRY, + before_send_transaction: "Optional[TransactionProcessor]" = None, + project_root: "Optional[str]" = None, + enable_tracing: "Optional[bool]" = None, + include_local_variables: "Optional[bool]" = True, + include_source_context: "Optional[bool]" = True, + trace_propagation_targets: "Optional[Sequence[str]]" = [ # noqa: B006 MATCH_ALL - ], # type: Optional[Sequence[str]] - functions_to_trace=[], # type: Sequence[Dict[str, str]] # noqa: B006 - event_scrubber=None, # type: Optional[sentry_sdk.scrubber.EventScrubber] - max_value_length=DEFAULT_MAX_VALUE_LENGTH, # type: int - enable_backpressure_handling=True, # type: bool - error_sampler=None, # type: Optional[Callable[[Event, Hint], Union[float, bool]]] - enable_db_query_source=True, # type: bool - db_query_source_threshold_ms=100, # type: int - enable_http_request_source=True, # type: bool - http_request_source_threshold_ms=100, # type: int - 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]]] - add_full_stack=DEFAULT_ADD_FULL_STACK, # type: bool - max_stack_frames=DEFAULT_MAX_STACK_FRAMES, # type: Optional[int] - enable_logs=False, # type: bool - before_send_log=None, # type: Optional[Callable[[Log, Hint], Optional[Log]]] - trace_ignore_status_codes=frozenset(), # type: AbstractSet[int] - enable_metrics=True, # type: bool - before_send_metric=None, # type: Optional[Callable[[Metric, Hint], Optional[Metric]]] - org_id=None, # type: Optional[str] - strict_trace_continuation=False, # type: bool - ): - # type: (...) -> None + ], + functions_to_trace: "Sequence[Dict[str, str]]" = [], # noqa: B006 + event_scrubber: "Optional[sentry_sdk.scrubber.EventScrubber]" = None, + max_value_length: int = DEFAULT_MAX_VALUE_LENGTH, + enable_backpressure_handling: bool = True, + error_sampler: "Optional[Callable[[Event, Hint], Union[float, bool]]]" = None, + enable_db_query_source: bool = True, + db_query_source_threshold_ms: int = 100, + enable_http_request_source: bool = True, + http_request_source_threshold_ms: int = 100, + spotlight: "Optional[Union[bool, str]]" = None, + cert_file: "Optional[str]" = None, + key_file: "Optional[str]" = None, + custom_repr: "Optional[Callable[..., Optional[str]]]" = None, + add_full_stack: bool = DEFAULT_ADD_FULL_STACK, + max_stack_frames: "Optional[int]" = DEFAULT_MAX_STACK_FRAMES, + enable_logs: bool = False, + before_send_log: "Optional[Callable[[Log, Hint], Optional[Log]]]" = None, + trace_ignore_status_codes: "AbstractSet[int]" = frozenset(), + enable_metrics: bool = True, + before_send_metric: "Optional[Callable[[Metric, Hint], Optional[Metric]]]" = None, + org_id: "Optional[str]" = None, + strict_trace_continuation: bool = False, + ) -> None: """Initialize the Sentry SDK with the given parameters. All parameters described here can be used in a call to `sentry_sdk.init()`. :param dsn: The DSN tells the SDK where to send the events. @@ -1446,8 +1445,7 @@ def __init__( pass -def _get_default_options(): - # type: () -> dict[str, Any] +def _get_default_options() -> "dict[str, Any]": import inspect a = inspect.getfullargspec(ClientConstructor.__init__) diff --git a/sentry_sdk/crons/api.py b/sentry_sdk/crons/api.py index b67e5961c8..5b7bdc2480 100644 --- a/sentry_sdk/crons/api.py +++ b/sentry_sdk/crons/api.py @@ -11,17 +11,16 @@ def _create_check_in_event( - monitor_slug=None, # type: Optional[str] - check_in_id=None, # type: Optional[str] - status=None, # type: Optional[str] - duration_s=None, # type: Optional[float] - monitor_config=None, # type: Optional[MonitorConfig] -): - # type: (...) -> Event + monitor_slug: "Optional[str]" = None, + check_in_id: "Optional[str]" = None, + status: "Optional[str]" = None, + duration_s: "Optional[float]" = None, + monitor_config: "Optional[MonitorConfig]" = None, +) -> "Event": options = sentry_sdk.get_client().options - check_in_id = check_in_id or uuid.uuid4().hex # type: str + check_in_id: str = check_in_id or uuid.uuid4().hex - check_in = { + check_in: "Event" = { "type": "check_in", "monitor_slug": monitor_slug, "check_in_id": check_in_id, @@ -29,7 +28,7 @@ def _create_check_in_event( "duration": duration_s, "environment": options.get("environment", None), "release": options.get("release", None), - } # type: Event + } if monitor_config: check_in["monitor_config"] = monitor_config @@ -38,13 +37,12 @@ def _create_check_in_event( def capture_checkin( - monitor_slug=None, # type: Optional[str] - check_in_id=None, # type: Optional[str] - status=None, # type: Optional[str] - duration=None, # type: Optional[float] - monitor_config=None, # type: Optional[MonitorConfig] -): - # type: (...) -> str + monitor_slug: "Optional[str]" = None, + check_in_id: "Optional[str]" = None, + status: "Optional[str]" = None, + duration: "Optional[float]" = None, + monitor_config: "Optional[MonitorConfig]" = None, +) -> str: check_in_event = _create_check_in_event( monitor_slug=monitor_slug, check_in_id=check_in_id, diff --git a/sentry_sdk/crons/decorator.py b/sentry_sdk/crons/decorator.py index 9af00e61c0..7f67b14aac 100644 --- a/sentry_sdk/crons/decorator.py +++ b/sentry_sdk/crons/decorator.py @@ -55,13 +55,15 @@ def test(arg): ``` """ - def __init__(self, monitor_slug=None, monitor_config=None): - # type: (Optional[str], Optional[MonitorConfig]) -> None + def __init__( + self, + monitor_slug: "Optional[str]" = None, + monitor_config: "Optional[MonitorConfig]" = None, + ) -> None: self.monitor_slug = monitor_slug self.monitor_config = monitor_config - def __enter__(self): - # type: () -> None + def __enter__(self) -> None: self.start_timestamp = now() self.check_in_id = capture_checkin( monitor_slug=self.monitor_slug, @@ -69,8 +71,12 @@ def __enter__(self): monitor_config=self.monitor_config, ) - def __exit__(self, exc_type, exc_value, traceback): - # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> None + def __exit__( + self, + exc_type: "Optional[Type[BaseException]]", + exc_value: "Optional[BaseException]", + traceback: "Optional[TracebackType]", + ) -> None: duration_s = now() - self.start_timestamp if exc_type is None and exc_value is None and traceback is None: @@ -89,23 +95,21 @@ def __exit__(self, exc_type, exc_value, traceback): if TYPE_CHECKING: @overload - def __call__(self, fn): - # type: (Callable[P, Awaitable[Any]]) -> Callable[P, Awaitable[Any]] + def __call__( + self, fn: "Callable[P, Awaitable[Any]]" + ) -> "Callable[P, Awaitable[Any]]": # Unfortunately, mypy does not give us any reliable way to type check the # return value of an Awaitable (i.e. async function) for this overload, # since calling iscouroutinefunction narrows the type to Callable[P, Awaitable[Any]]. ... @overload - def __call__(self, fn): - # type: (Callable[P, R]) -> Callable[P, R] - ... + def __call__(self, fn: "Callable[P, R]") -> "Callable[P, R]": ... def __call__( self, - fn, # type: Union[Callable[P, R], Callable[P, Awaitable[Any]]] - ): - # type: (...) -> Union[Callable[P, R], Callable[P, Awaitable[Any]]] + fn: "Union[Callable[P, R], Callable[P, Awaitable[Any]]]", + ) -> "Union[Callable[P, R], Callable[P, Awaitable[Any]]]": if iscoroutinefunction(fn): return self._async_wrapper(fn) @@ -114,21 +118,19 @@ def __call__( fn = cast("Callable[P, R]", fn) return self._sync_wrapper(fn) - def _async_wrapper(self, fn): - # type: (Callable[P, Awaitable[Any]]) -> Callable[P, Awaitable[Any]] + def _async_wrapper( + self, fn: "Callable[P, Awaitable[Any]]" + ) -> "Callable[P, Awaitable[Any]]": @wraps(fn) - async def inner(*args: "P.args", **kwargs: "P.kwargs"): - # type: (...) -> R + async def inner(*args: "P.args", **kwargs: "P.kwargs") -> R: with self: return await fn(*args, **kwargs) return inner - def _sync_wrapper(self, fn): - # type: (Callable[P, R]) -> Callable[P, R] + def _sync_wrapper(self, fn: "Callable[P, R]") -> "Callable[P, R]": @wraps(fn) - def inner(*args: "P.args", **kwargs: "P.kwargs"): - # type: (...) -> R + def inner(*args: "P.args", **kwargs: "P.kwargs") -> R: with self: return fn(*args, **kwargs) diff --git a/sentry_sdk/debug.py b/sentry_sdk/debug.py index e4c686a3e8..513ba8813f 100644 --- a/sentry_sdk/debug.py +++ b/sentry_sdk/debug.py @@ -9,22 +9,19 @@ class _DebugFilter(logging.Filter): - def filter(self, record): - # type: (LogRecord) -> bool + def filter(self, record: "LogRecord") -> bool: if _client_init_debug.get(False): return True return get_client().options["debug"] -def init_debug_support(): - # type: () -> None +def init_debug_support() -> None: if not logger.handlers: configure_logger() -def configure_logger(): - # type: () -> None +def configure_logger() -> None: _handler = logging.StreamHandler(sys.stderr) _handler.setFormatter(logging.Formatter(" [sentry] %(levelname)s: %(message)s")) logger.addHandler(_handler) @@ -32,8 +29,7 @@ def configure_logger(): logger.addFilter(_DebugFilter()) -def configure_debug_hub(): - # type: () -> None +def configure_debug_hub() -> None: warnings.warn( "configure_debug_hub is deprecated. Please remove calls to it, as it is a no-op.", DeprecationWarning, diff --git a/sentry_sdk/envelope.py b/sentry_sdk/envelope.py index 56bb5fde73..307fb26fd6 100644 --- a/sentry_sdk/envelope.py +++ b/sentry_sdk/envelope.py @@ -18,8 +18,7 @@ from sentry_sdk._types import Event, EventDataCategory -def parse_json(data): - # type: (Union[bytes, str]) -> Any +def parse_json(data: "Union[bytes, str]") -> "Any": # on some python 3 versions this needs to be bytes if isinstance(data, bytes): data = data.decode("utf-8", "replace") @@ -35,10 +34,9 @@ class Envelope: def __init__( self, - headers=None, # type: Optional[Dict[str, Any]] - items=None, # type: Optional[List[Item]] - ): - # type: (...) -> None + headers: "Optional[Dict[str, Any]]" = None, + items: "Optional[List[Item]]" = None, + ) -> None: if headers is not None: headers = dict(headers) self.headers = headers or {} @@ -49,8 +47,7 @@ def __init__( self.items = items @property - def description(self): - # type: (...) -> str + def description(self) -> str: return "envelope with %s items (%s)" % ( len(self.items), ", ".join(x.data_category for x in self.items), @@ -58,30 +55,26 @@ def description(self): def add_event( self, - event, # type: Event - ): - # type: (...) -> None + event: "Event", + ) -> None: self.add_item(Item(payload=PayloadRef(json=event), type="event")) def add_transaction( self, - transaction, # type: Event - ): - # type: (...) -> None + transaction: "Event", + ) -> None: self.add_item(Item(payload=PayloadRef(json=transaction), type="transaction")) def add_profile( self, - profile, # type: Any - ): - # type: (...) -> None + profile: "Any", + ) -> None: self.add_item(Item(payload=PayloadRef(json=profile), type="profile")) def add_profile_chunk( self, - profile_chunk, # type: Any - ): - # type: (...) -> None + profile_chunk: "Any", + ) -> None: self.add_item( Item( payload=PayloadRef(json=profile_chunk), @@ -92,66 +85,57 @@ def add_profile_chunk( def add_checkin( self, - checkin, # type: Any - ): - # type: (...) -> None + checkin: "Any", + ) -> None: self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in")) def add_session( self, - session, # type: Union[Session, Any] - ): - # type: (...) -> None + session: "Union[Session, Any]", + ) -> None: if isinstance(session, Session): session = session.to_json() self.add_item(Item(payload=PayloadRef(json=session), type="session")) def add_sessions( self, - sessions, # type: Any - ): - # type: (...) -> None + sessions: "Any", + ) -> None: self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions")) def add_item( self, - item, # type: Item - ): - # type: (...) -> None + item: "Item", + ) -> None: self.items.append(item) - def get_event(self): - # type: (...) -> Optional[Event] + def get_event(self) -> "Optional[Event]": for items in self.items: event = items.get_event() if event is not None: return event return None - def get_transaction_event(self): - # type: (...) -> Optional[Event] + def get_transaction_event(self) -> "Optional[Event]": for item in self.items: event = item.get_transaction_event() if event is not None: return event return None - def __iter__(self): - # type: (...) -> Iterator[Item] + def __iter__(self) -> "Iterator[Item]": return iter(self.items) def serialize_into( self, - f, # type: Any - ): - # type: (...) -> None + f: "Any", + ) -> None: f.write(json_dumps(self.headers)) f.write(b"\n") for item in self.items: item.serialize_into(f) - def serialize(self): - # type: (...) -> bytes + def serialize(self) -> bytes: out = io.BytesIO() self.serialize_into(out) return out.getvalue() @@ -159,9 +143,8 @@ def serialize(self): @classmethod def deserialize_from( cls, - f, # type: Any - ): - # type: (...) -> Envelope + f: "Any", + ) -> "Envelope": headers = parse_json(f.readline()) items = [] while 1: @@ -174,30 +157,26 @@ def deserialize_from( @classmethod def deserialize( cls, - bytes, # type: bytes - ): - # type: (...) -> Envelope + bytes: bytes, + ) -> "Envelope": return cls.deserialize_from(io.BytesIO(bytes)) - def __repr__(self): - # type: (...) -> str + def __repr__(self) -> str: return "" % (self.headers, self.items) class PayloadRef: def __init__( self, - bytes=None, # type: Optional[bytes] - path=None, # type: Optional[Union[bytes, str]] - json=None, # type: Optional[Any] - ): - # type: (...) -> None + bytes: "Optional[bytes]" = None, + path: "Optional[Union[bytes, str]]" = None, + json: "Optional[Any]" = None, + ) -> None: self.json = json self.bytes = bytes self.path = path - def get_bytes(self): - # type: (...) -> bytes + def get_bytes(self) -> bytes: if self.bytes is None: if self.path is not None: with capture_internal_exceptions(): @@ -208,8 +187,7 @@ def get_bytes(self): return self.bytes or b"" @property - def inferred_content_type(self): - # type: (...) -> str + def inferred_content_type(self) -> str: if self.json is not None: return "application/json" elif self.path is not None: @@ -221,19 +199,18 @@ def inferred_content_type(self): return ty return "application/octet-stream" - def __repr__(self): - # type: (...) -> str + def __repr__(self) -> str: return "" % (self.inferred_content_type,) class Item: def __init__( self, - payload, # type: Union[bytes, str, PayloadRef] - headers=None, # type: Optional[Dict[str, Any]] - type=None, # type: Optional[str] - content_type=None, # type: Optional[str] - filename=None, # type: Optional[str] + payload: "Union[bytes, str, PayloadRef]", + headers: "Optional[Dict[str, Any]]" = None, + type: "Optional[str]" = None, + content_type: "Optional[str]" = None, + filename: "Optional[str]" = None, ): if headers is not None: headers = dict(headers) @@ -258,8 +235,7 @@ def __init__( self.payload = payload - def __repr__(self): - # type: (...) -> str + def __repr__(self) -> str: return "" % ( self.headers, self.payload, @@ -267,13 +243,11 @@ def __repr__(self): ) @property - def type(self): - # type: (...) -> Optional[str] + def type(self) -> "Optional[str]": return self.headers.get("type") @property - def data_category(self): - # type: (...) -> EventDataCategory + def data_category(self) -> "EventDataCategory": ty = self.headers.get("type") if ty == "session" or ty == "sessions": return "session" @@ -298,12 +272,10 @@ def data_category(self): else: return "default" - def get_bytes(self): - # type: (...) -> bytes + def get_bytes(self) -> bytes: return self.payload.get_bytes() - def get_event(self): - # type: (...) -> Optional[Event] + def get_event(self) -> "Optional[Event]": """ Returns an error event if there is one. """ @@ -311,17 +283,15 @@ def get_event(self): return self.payload.json return None - def get_transaction_event(self): - # type: (...) -> Optional[Event] + def get_transaction_event(self) -> "Optional[Event]": if self.type == "transaction" and self.payload.json is not None: return self.payload.json return None def serialize_into( self, - f, # type: Any - ): - # type: (...) -> None + f: "Any", + ) -> None: headers = dict(self.headers) bytes = self.get_bytes() headers["length"] = len(bytes) @@ -330,8 +300,7 @@ def serialize_into( f.write(bytes) f.write(b"\n") - def serialize(self): - # type: (...) -> bytes + def serialize(self) -> bytes: out = io.BytesIO() self.serialize_into(out) return out.getvalue() @@ -339,9 +308,8 @@ def serialize(self): @classmethod def deserialize_from( cls, - f, # type: Any - ): - # type: (...) -> Optional[Item] + f: "Any", + ) -> "Optional[Item]": line = f.readline().rstrip() if not line: return None @@ -363,7 +331,6 @@ def deserialize_from( @classmethod def deserialize( cls, - bytes, # type: bytes - ): - # type: (...) -> Optional[Item] + bytes: bytes, + ) -> "Optional[Item]": return cls.deserialize_from(io.BytesIO(bytes)) diff --git a/sentry_sdk/feature_flags.py b/sentry_sdk/feature_flags.py index 03fba9c53c..c9f3f303f9 100644 --- a/sentry_sdk/feature_flags.py +++ b/sentry_sdk/feature_flags.py @@ -15,8 +15,7 @@ class FlagBuffer: - def __init__(self, capacity): - # type: (int) -> None + def __init__(self, capacity: int) -> None: self.capacity = capacity self.lock = Lock() @@ -24,26 +23,22 @@ def __init__(self, capacity): # directly you're on your own! self.__buffer = LRUCache(capacity) - def clear(self): - # type: () -> None + def clear(self) -> None: self.__buffer = LRUCache(self.capacity) - def __deepcopy__(self, memo): - # type: (dict[int, Any]) -> FlagBuffer + def __deepcopy__(self, memo: "dict[int, Any]") -> "FlagBuffer": with self.lock: buffer = FlagBuffer(self.capacity) buffer.__buffer = copy.deepcopy(self.__buffer, memo) return buffer - def get(self): - # type: () -> list[FlagData] + def get(self) -> "list[FlagData]": with self.lock: return [ {"flag": key, "result": value} for key, value in self.__buffer.get_all() ] - def set(self, flag, result): - # type: (str, bool) -> None + def set(self, flag: str, result: bool) -> None: if isinstance(result, FlagBuffer): # If someone were to insert `self` into `self` this would create a circular dependency # on the lock. This is of course a deadlock. However, this is far outside the expected @@ -57,8 +52,7 @@ def set(self, flag, result): self.__buffer.set(flag, result) -def add_feature_flag(flag, result): - # type: (str, bool) -> None +def add_feature_flag(flag: str, result: bool) -> None: """ Records a flag and its value to be sent on subsequent error events. We recommend you do this on flag evaluations. Flags are buffered per Sentry scope. diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 6f2d1bbf13..0e5d7df9f9 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -1,65 +1,64 @@ import warnings from contextlib import contextmanager +from typing import TYPE_CHECKING from sentry_sdk import ( get_client, + get_current_scope, get_global_scope, get_isolation_scope, - get_current_scope, ) from sentry_sdk._compat import with_metaclass +from sentry_sdk.client import Client from sentry_sdk.consts import INSTRUMENTER from sentry_sdk.scope import _ScopeManager -from sentry_sdk.client import Client from sentry_sdk.tracing import ( NoOpSpan, Span, Transaction, ) - from sentry_sdk.utils import ( - logger, ContextVar, + logger, ) -from typing import TYPE_CHECKING - if TYPE_CHECKING: - from typing import Any - from typing import Callable - from typing import ContextManager - from typing import Dict - from typing import Generator - from typing import List - from typing import Optional - from typing import overload - from typing import Tuple - from typing import Type - from typing import TypeVar - from typing import Union + from typing import ( + Any, + Callable, + ContextManager, + Dict, + Generator, + List, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, + ) from typing_extensions import Unpack - from sentry_sdk.scope import Scope - from sentry_sdk.client import BaseClient - from sentry_sdk.integrations import Integration from sentry_sdk._types import ( - Event, - Hint, Breadcrumb, BreadcrumbHint, + Event, ExcInfo, + Hint, LogLevelStr, SamplingContext, ) + from sentry_sdk.client import BaseClient + from sentry_sdk.integrations import Integration + from sentry_sdk.scope import Scope from sentry_sdk.tracing import TransactionKwargs T = TypeVar("T") else: - def overload(x): - # type: (T) -> T + def overload(x: "T") -> "T": return x @@ -75,14 +74,12 @@ class SentryHubDeprecationWarning(DeprecationWarning): "https://docs.sentry.io/platforms/python/migration/1.x-to-2.x" ) - def __init__(self, *_): - # type: (*object) -> None + def __init__(self, *_: object) -> None: super().__init__(self._MESSAGE) @contextmanager -def _suppress_hub_deprecation_warning(): - # type: () -> Generator[None, None, None] +def _suppress_hub_deprecation_warning() -> "Generator[None, None, None]": """Utility function to suppress deprecation warnings for the Hub.""" with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=SentryHubDeprecationWarning) @@ -94,8 +91,7 @@ def _suppress_hub_deprecation_warning(): class HubMeta(type): @property - def current(cls): - # type: () -> Hub + def current(cls) -> "Hub": """Returns the current instance of the hub.""" warnings.warn(SentryHubDeprecationWarning(), stacklevel=2) rv = _local.get(None) @@ -107,8 +103,7 @@ def current(cls): return rv @property - def main(cls): - # type: () -> Hub + def main(cls) -> "Hub": """Returns the main instance of the hub.""" warnings.warn(SentryHubDeprecationWarning(), stacklevel=2) return GLOBAL_HUB @@ -126,21 +121,20 @@ class Hub(with_metaclass(HubMeta)): # type: ignore If the hub is used with a with statement it's temporarily activated. """ - _stack = None # type: List[Tuple[Optional[Client], Scope]] - _scope = None # type: Optional[Scope] + _stack: "List[Tuple[Optional[Client], Scope]]" = None # type: ignore[assignment] + _scope: "Optional[Scope]" = None # Mypy doesn't pick up on the metaclass. if TYPE_CHECKING: - current = None # type: Hub - main = None # type: Hub + current: "Hub" = None # type: ignore[assignment] + main: "Optional[Hub]" = None def __init__( self, - client_or_hub=None, # type: Optional[Union[Hub, Client]] - scope=None, # type: Optional[Any] - ): - # type: (...) -> None + client_or_hub: "Optional[Union[Hub, Client]]" = None, + scope: "Optional[Any]" = None, + ) -> None: warnings.warn(SentryHubDeprecationWarning(), stacklevel=2) current_scope = None @@ -165,16 +159,15 @@ def __init__( current_scope = get_current_scope() self._stack = [(client, scope)] # type: ignore - self._last_event_id = None # type: Optional[str] - self._old_hubs = [] # type: List[Hub] + self._last_event_id: "Optional[str]" = None + self._old_hubs: "List[Hub]" = [] - self._old_current_scopes = [] # type: List[Scope] - self._old_isolation_scopes = [] # type: List[Scope] - self._current_scope = current_scope # type: Scope - self._scope = scope # type: Scope + self._old_current_scopes: "List[Scope]" = [] + self._old_isolation_scopes: "List[Scope]" = [] + self._current_scope: "Scope" = current_scope + self._scope: "Scope" = scope - def __enter__(self): - # type: () -> Hub + def __enter__(self) -> "Hub": self._old_hubs.append(Hub.current) _local.set(self) @@ -190,11 +183,10 @@ def __enter__(self): def __exit__( self, - exc_type, # type: Optional[type] - exc_value, # type: Optional[BaseException] - tb, # type: Optional[Any] - ): - # type: (...) -> None + exc_type: "Optional[type]", + exc_value: "Optional[BaseException]", + tb: "Optional[Any]", + ) -> None: old = self._old_hubs.pop() _local.set(old) @@ -206,9 +198,8 @@ def __exit__( def run( self, - callback, # type: Callable[[], T] - ): - # type: (...) -> T + callback: "Callable[[], T]", + ) -> "T": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -221,9 +212,8 @@ def run( def get_integration( self, - name_or_class, # type: Union[str, Type[Integration]] - ): - # type: (...) -> Any + name_or_class: "Union[str, Type[Integration]]", + ) -> "Any": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -239,8 +229,7 @@ def get_integration( return get_client().get_integration(name_or_class) @property - def client(self): - # type: () -> Optional[BaseClient] + def client(self) -> "Optional[BaseClient]": """ .. deprecated:: 2.0.0 This property is deprecated and will be removed in a future release. @@ -256,8 +245,7 @@ def client(self): return client @property - def scope(self): - # type: () -> Scope + def scope(self) -> "Scope": """ .. deprecated:: 2.0.0 This property is deprecated and will be removed in a future release. @@ -265,8 +253,7 @@ def scope(self): """ return get_isolation_scope() - def last_event_id(self): - # type: () -> Optional[str] + def last_event_id(self) -> "Optional[str]": """ Returns the last event ID. @@ -280,9 +267,8 @@ def last_event_id(self): def bind_client( self, - new, # type: Optional[BaseClient] - ): - # type: (...) -> None + new: "Optional[BaseClient]", + ) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -292,8 +278,13 @@ def bind_client( """ get_global_scope().set_client(new) - def capture_event(self, event, hint=None, scope=None, **scope_kwargs): - # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] + def capture_event( + self, + event: "Event", + hint: "Optional[Hint]" = None, + scope: "Optional[Scope]" = None, + **scope_kwargs: "Any", + ) -> "Optional[str]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -324,8 +315,13 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): return last_event_id - def capture_message(self, message, level=None, scope=None, **scope_kwargs): - # type: (str, Optional[LogLevelStr], Optional[Scope], Any) -> Optional[str] + def capture_message( + self, + message: str, + level: "Optional[LogLevelStr]" = None, + scope: "Optional[Scope]" = None, + **scope_kwargs: "Any", + ) -> "Optional[str]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -357,8 +353,12 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): return last_event_id - def capture_exception(self, error=None, scope=None, **scope_kwargs): - # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + def capture_exception( + self, + error: "Optional[Union[BaseException, ExcInfo]]" = None, + scope: "Optional[Scope]" = None, + **scope_kwargs: "Any", + ) -> "Optional[str]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -388,8 +388,12 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): return last_event_id - def add_breadcrumb(self, crumb=None, hint=None, **kwargs): - # type: (Optional[Breadcrumb], Optional[BreadcrumbHint], Any) -> None + def add_breadcrumb( + self, + crumb: "Optional[Breadcrumb]" = None, + hint: "Optional[BreadcrumbHint]" = None, + **kwargs: "Any", + ) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -404,8 +408,9 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): """ get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs) - def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): - # type: (str, Any) -> Span + def start_span( + self, instrumenter: str = INSTRUMENTER.SENTRY, **kwargs: "Any" + ) -> "Span": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -430,12 +435,11 @@ def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): def start_transaction( self, - transaction=None, - instrumenter=INSTRUMENTER.SENTRY, - custom_sampling_context=None, - **kwargs, - ): - # type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan] + transaction: "Optional[Transaction]" = None, + instrumenter: str = INSTRUMENTER.SENTRY, + custom_sampling_context: "Optional[SamplingContext]" = None, + **kwargs: "Unpack[TransactionKwargs]", + ) -> "Union[Transaction, NoOpSpan]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -475,8 +479,13 @@ def start_transaction( transaction, instrumenter, custom_sampling_context, **kwargs ) - def continue_trace(self, environ_or_headers, op=None, name=None, source=None): - # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str]) -> Transaction + def continue_trace( + self, + environ_or_headers: "Dict[str, Any]", + op: "Optional[str]" = None, + name: "Optional[str]" = None, + source: "Optional[str]" = None, + ) -> "Transaction": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -491,25 +500,22 @@ def continue_trace(self, environ_or_headers, op=None, name=None, source=None): @overload def push_scope( self, - callback=None, # type: Optional[None] - ): - # type: (...) -> ContextManager[Scope] + callback: "Optional[None]" = None, + ) -> "ContextManager[Scope]": pass @overload def push_scope( # noqa: F811 self, - callback, # type: Callable[[Scope], None] - ): - # type: (...) -> None + callback: "Callable[[Scope], None]", + ) -> None: pass def push_scope( # noqa self, - callback=None, # type: Optional[Callable[[Scope], None]] - continue_trace=True, # type: bool - ): - # type: (...) -> Optional[ContextManager[Scope]] + callback: "Optional[Callable[[Scope], None]]" = None, + continue_trace: bool = True, + ) -> "Optional[ContextManager[Scope]]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -529,8 +535,7 @@ def push_scope( # noqa return _ScopeManager(self) - def pop_scope_unsafe(self): - # type: () -> Tuple[Optional[Client], Scope] + def pop_scope_unsafe(self) -> "Tuple[Optional[Client], Scope]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -546,25 +551,22 @@ def pop_scope_unsafe(self): @overload def configure_scope( self, - callback=None, # type: Optional[None] - ): - # type: (...) -> ContextManager[Scope] + callback: "Optional[None]" = None, + ) -> "ContextManager[Scope]": pass @overload def configure_scope( # noqa: F811 self, - callback, # type: Callable[[Scope], None] - ): - # type: (...) -> None + callback: "Callable[[Scope], None]", + ) -> None: pass def configure_scope( # noqa self, - callback=None, # type: Optional[Callable[[Scope], None]] - continue_trace=True, # type: bool - ): - # type: (...) -> Optional[ContextManager[Scope]] + callback: "Optional[Callable[[Scope], None]]" = None, + continue_trace: bool = True, + ) -> "Optional[ContextManager[Scope]]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -587,17 +589,15 @@ def configure_scope( # noqa return None @contextmanager - def inner(): - # type: () -> Generator[Scope, None, None] + def inner() -> "Generator[Scope, None, None]": yield scope return inner() def start_session( self, - session_mode="application", # type: str - ): - # type: (...) -> None + session_mode: str = "application", + ) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -609,8 +609,7 @@ def start_session( session_mode=session_mode, ) - def end_session(self): - # type: (...) -> None + def end_session(self) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -620,8 +619,7 @@ def end_session(self): """ get_isolation_scope().end_session() - def stop_auto_session_tracking(self): - # type: (...) -> None + def stop_auto_session_tracking(self) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -634,8 +632,7 @@ def stop_auto_session_tracking(self): """ get_isolation_scope().stop_auto_session_tracking() - def resume_auto_session_tracking(self): - # type: (...) -> None + def resume_auto_session_tracking(self) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -649,10 +646,9 @@ def resume_auto_session_tracking(self): def flush( self, - timeout=None, # type: Optional[float] - callback=None, # type: Optional[Callable[[int, float], None]] - ): - # type: (...) -> None + timeout: "Optional[float]" = None, + callback: "Optional[Callable[[int, float], None]]" = None, + ) -> None: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -662,8 +658,7 @@ def flush( """ return get_client().flush(timeout=timeout, callback=callback) - def get_traceparent(self): - # type: () -> Optional[str] + def get_traceparent(self) -> "Optional[str]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -680,8 +675,7 @@ def get_traceparent(self): return traceparent - def get_baggage(self): - # type: () -> Optional[str] + def get_baggage(self) -> "Optional[str]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -701,8 +695,9 @@ def get_baggage(self): return None - def iter_trace_propagation_headers(self, span=None): - # type: (Optional[Span]) -> Generator[Tuple[str, str], None, None] + def iter_trace_propagation_headers( + self, span: "Optional[Span]" = None + ) -> "Generator[Tuple[str, str], None, None]": """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. @@ -716,8 +711,7 @@ def iter_trace_propagation_headers(self, span=None): span=span, ) - def trace_propagation_meta(self, span=None): - # type: (Optional[Span]) -> str + def trace_propagation_meta(self, span: "Optional[Span]" = None) -> str: """ .. deprecated:: 2.0.0 This function is deprecated and will be removed in a future release. diff --git a/sentry_sdk/integrations/__init__.py b/sentry_sdk/integrations/__init__.py index b340dec36e..1ce937217f 100644 --- a/sentry_sdk/integrations/__init__.py +++ b/sentry_sdk/integrations/__init__.py @@ -1,21 +1,12 @@ from abc import ABC, abstractmethod from threading import Lock +from typing import TYPE_CHECKING from sentry_sdk.utils import logger -from typing import TYPE_CHECKING - if TYPE_CHECKING: from collections.abc import Sequence - from typing import Callable - from typing import Dict - from typing import Iterator - from typing import List - from typing import Optional - from typing import Set - from typing import Type - from typing import Union - from typing import Any + from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Type, Union _DEFAULT_FAILED_REQUEST_STATUS_CODES = frozenset(range(500, 600)) @@ -24,20 +15,19 @@ _installer_lock = Lock() # Set of all integration identifiers we have attempted to install -_processed_integrations = set() # type: Set[str] +_processed_integrations: "Set[str]" = set() # Set of all integration identifiers we have actually installed -_installed_integrations = set() # type: Set[str] +_installed_integrations: "Set[str]" = set() def _generate_default_integrations_iterator( - integrations, # type: List[str] - auto_enabling_integrations, # type: List[str] -): - # type: (...) -> Callable[[bool], Iterator[Type[Integration]]] - - def iter_default_integrations(with_auto_enabling_integrations): - # type: (bool) -> Iterator[Type[Integration]] + integrations: "List[str]", + auto_enabling_integrations: "List[str]", +) -> "Callable[[bool], Iterator[Type[Integration]]]": + def iter_default_integrations( + with_auto_enabling_integrations: bool, + ) -> Iterator[Type[Integration]]: """Returns an iterator of the default integration classes:""" from importlib import import_module @@ -182,13 +172,12 @@ def iter_default_integrations(with_auto_enabling_integrations): def setup_integrations( - integrations, # type: Sequence[Integration] - with_defaults=True, # type: bool - with_auto_enabling_integrations=False, # type: bool - disabled_integrations=None, # type: Optional[Sequence[Union[type[Integration], Integration]]] - options=None, # type: Optional[Dict[str, Any]] -): - # type: (...) -> Dict[str, Integration] + integrations: "Sequence[Integration]", + with_defaults: bool = True, + with_auto_enabling_integrations: bool = False, + disabled_integrations: "Optional[Sequence[Union[type[Integration], Integration]]]" = None, + options: "Optional[Dict[str, Any]]" = None, +) -> "Dict[str, Integration]": """ Given a list of integration instances, this installs them all. @@ -290,8 +279,11 @@ def setup_integrations( return integrations -def _check_minimum_version(integration, version, package=None): - # type: (type[Integration], Optional[tuple[int, ...]], Optional[str]) -> None +def _check_minimum_version( + integration: "type[Integration]", + version: "Optional[tuple[int, ...]]", + package: "Optional[str]" = None, +) -> None: package = package or integration.identifier if version is None: @@ -327,13 +319,12 @@ class Integration(ABC): install = None """Legacy method, do not implement.""" - identifier = None # type: str + identifier: "str" = None # type: ignore[assignment] """String unique ID of integration type""" @staticmethod @abstractmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: """ Initialize the integration. @@ -346,8 +337,9 @@ def setup_once(): """ pass - def setup_once_with_options(self, options=None): - # type: (Optional[Dict[str, Any]]) -> None + def setup_once_with_options( + self, options: "Optional[Dict[str, Any]]" = None + ) -> None: """ Called after setup_once in rare cases on the instance and with options since we don't have those available above. """ diff --git a/sentry_sdk/integrations/_asgi_common.py b/sentry_sdk/integrations/_asgi_common.py index c16bbbcfe8..a8022c6bb1 100644 --- a/sentry_sdk/integrations/_asgi_common.py +++ b/sentry_sdk/integrations/_asgi_common.py @@ -15,12 +15,11 @@ from sentry_sdk.utils import AnnotatedValue -def _get_headers(asgi_scope): - # type: (Any) -> Dict[str, str] +def _get_headers(asgi_scope: "Any") -> "Dict[str, str]": """ Extract headers from the ASGI scope, in the format that the Sentry protocol expects. """ - headers = {} # type: Dict[str, str] + headers: "Dict[str, str]" = {} for raw_key, raw_value in asgi_scope["headers"]: key = raw_key.decode("latin-1") value = raw_value.decode("latin-1") @@ -32,8 +31,11 @@ def _get_headers(asgi_scope): return headers -def _get_url(asgi_scope, default_scheme, host): - # type: (Dict[str, Any], Literal["ws", "http"], Optional[Union[AnnotatedValue, str]]) -> str +def _get_url( + asgi_scope: "Dict[str, Any]", + default_scheme: "Literal['ws', 'http']", + host: "Optional[Union[AnnotatedValue, str]]", +) -> str: """ Extract URL from the ASGI scope, without also including the querystring. """ @@ -54,8 +56,7 @@ def _get_url(asgi_scope, default_scheme, host): return path -def _get_query(asgi_scope): - # type: (Any) -> Any +def _get_query(asgi_scope: "Any") -> "Any": """ Extract querystring from the ASGI scope, in the format that the Sentry protocol expects. """ @@ -65,8 +66,7 @@ def _get_query(asgi_scope): return urllib.parse.unquote(qs.decode("latin-1")) -def _get_ip(asgi_scope): - # type: (Any) -> str +def _get_ip(asgi_scope: "Any") -> str: """ Extract IP Address from the ASGI scope based on request headers with fallback to scope client. """ @@ -84,12 +84,11 @@ def _get_ip(asgi_scope): return asgi_scope.get("client")[0] -def _get_request_data(asgi_scope): - # type: (Any) -> Dict[str, Any] +def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]": """ Returns data related to the HTTP request from the ASGI scope. """ - request_data = {} # type: Dict[str, Any] + request_data: "Dict[str, Any]" = {} ty = asgi_scope["type"] if ty in ("http", "websocket"): request_data["method"] = asgi_scope.get("method") diff --git a/sentry_sdk/integrations/_wsgi_common.py b/sentry_sdk/integrations/_wsgi_common.py index 48bc432887..688e965be4 100644 --- a/sentry_sdk/integrations/_wsgi_common.py +++ b/sentry_sdk/integrations/_wsgi_common.py @@ -54,13 +54,13 @@ # This noop context manager can be replaced with "from contextlib import nullcontext" when we drop Python 3.6 support @contextmanager -def nullcontext(): - # type: () -> Iterator[None] +def nullcontext() -> "Iterator[None]": yield -def request_body_within_bounds(client, content_length): - # type: (Optional[sentry_sdk.client.BaseClient], int) -> bool +def request_body_within_bounds( + client: "Optional[sentry_sdk.client.BaseClient]", content_length: int +) -> bool: if client is None: return False @@ -82,17 +82,15 @@ class RequestExtractor: # it. Only some child classes implement all methods that raise # NotImplementedError in this class. - def __init__(self, request): - # type: (Any) -> None + def __init__(self, request: "Any") -> None: self.request = request - def extract_into_event(self, event): - # type: (Event) -> None + def extract_into_event(self, event: "Event") -> None: client = sentry_sdk.get_client() if not client.is_active(): return - data = None # type: Optional[Union[AnnotatedValue, Dict[str, Any]]] + data: "Optional[Union[AnnotatedValue, Dict[str, Any]]]" = None content_length = self.content_length() request_info = event.get("request", {}) @@ -128,27 +126,22 @@ def extract_into_event(self, event): event["request"] = deepcopy(request_info) - def content_length(self): - # type: () -> int + def content_length(self) -> int: try: return int(self.env().get("CONTENT_LENGTH", 0)) except ValueError: return 0 - def cookies(self): - # type: () -> MutableMapping[str, Any] + def cookies(self) -> "MutableMapping[str, Any]": raise NotImplementedError() - def raw_data(self): - # type: () -> Optional[Union[str, bytes]] + def raw_data(self) -> "Optional[Union[str, bytes]]": raise NotImplementedError() - def form(self): - # type: () -> Optional[Dict[str, Any]] + def form(self) -> "Optional[Dict[str, Any]]": raise NotImplementedError() - def parsed_body(self): - # type: () -> Optional[Dict[str, Any]] + def parsed_body(self) -> "Optional[Dict[str, Any]]": try: form = self.form() except Exception: @@ -170,12 +163,10 @@ def parsed_body(self): return self.json() - def is_json(self): - # type: () -> bool + def is_json(self) -> bool: return _is_json_content_type(self.env().get("CONTENT_TYPE")) - def json(self): - # type: () -> Optional[Any] + def json(self) -> "Optional[Any]": try: if not self.is_json(): return None @@ -199,21 +190,17 @@ def json(self): return None - def files(self): - # type: () -> Optional[Dict[str, Any]] + def files(self) -> "Optional[Dict[str, Any]]": raise NotImplementedError() - def size_of_file(self, file): - # type: (Any) -> int + def size_of_file(self, file: "Any") -> int: raise NotImplementedError() - def env(self): - # type: () -> Dict[str, Any] + def env(self) -> "Dict[str, Any]": raise NotImplementedError() -def _is_json_content_type(ct): - # type: (Optional[str]) -> bool +def _is_json_content_type(ct: "Optional[str]") -> bool: mt = (ct or "").split(";", 1)[0] return ( mt == "application/json" @@ -222,8 +209,9 @@ def _is_json_content_type(ct): ) -def _filter_headers(headers): - # type: (Mapping[str, str]) -> Mapping[str, Union[AnnotatedValue, str]] +def _filter_headers( + headers: "Mapping[str, str]", +) -> "Mapping[str, Union[AnnotatedValue, str]]": if should_send_default_pii(): return headers @@ -237,8 +225,9 @@ def _filter_headers(headers): } -def _in_http_status_code_range(code, code_ranges): - # type: (object, list[HttpStatusCodeRange]) -> bool +def _in_http_status_code_range( + code: object, code_ranges: "list[HttpStatusCodeRange]" +) -> bool: for target in code_ranges: if isinstance(target, int): if code == target: @@ -262,10 +251,8 @@ class HttpCodeRangeContainer: Used for backwards compatibility with the old `failed_request_status_codes` option. """ - def __init__(self, code_ranges): - # type: (list[HttpStatusCodeRange]) -> None + def __init__(self, code_ranges: "list[HttpStatusCodeRange]") -> None: self._code_ranges = code_ranges - def __contains__(self, item): - # type: (object) -> bool + def __contains__(self, item: object) -> bool: return _in_http_status_code_range(item, self._code_ranges) diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 0a417f8dc4..521c639518 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -74,11 +74,10 @@ class AioHttpIntegration(Integration): def __init__( self, - transaction_style="handler_name", # type: str + transaction_style: str = "handler_name", *, - failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int] - ): - # type: (...) -> None + failed_request_status_codes: "Set[int]" = _DEFAULT_FAILED_REQUEST_STATUS_CODES, + ) -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -88,9 +87,7 @@ def __init__( self._failed_request_status_codes = failed_request_status_codes @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: version = parse_version(AIOHTTP_VERSION) _check_minimum_version(AioHttpIntegration, version) @@ -106,8 +103,9 @@ def setup_once(): old_handle = Application._handle - async def sentry_app_handle(self, request, *args, **kwargs): - # type: (Any, Request, *Any, **Any) -> Any + async def sentry_app_handle( + self: "Any", request: "Request", *args: "Any", **kwargs: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(AioHttpIntegration) if integration is None: return await old_handle(self, request, *args, **kwargs) @@ -174,8 +172,9 @@ async def sentry_app_handle(self, request, *args, **kwargs): old_urldispatcher_resolve = UrlDispatcher.resolve @wraps(old_urldispatcher_resolve) - async def sentry_urldispatcher_resolve(self, request): - # type: (UrlDispatcher, Request) -> UrlMappingMatchInfo + async def sentry_urldispatcher_resolve( + self: "UrlDispatcher", request: "Request" + ) -> "UrlMappingMatchInfo": rv = await old_urldispatcher_resolve(self, request) integration = sentry_sdk.get_client().get_integration(AioHttpIntegration) @@ -207,8 +206,7 @@ async def sentry_urldispatcher_resolve(self, request): old_client_session_init = ClientSession.__init__ @ensure_integration_enabled(AioHttpIntegration, old_client_session_init) - def init(*args, **kwargs): - # type: (Any, Any) -> None + def init(*args: "Any", **kwargs: "Any") -> None: client_trace_configs = list(kwargs.get("trace_configs") or ()) trace_config = create_trace_config() client_trace_configs.append(trace_config) @@ -219,11 +217,12 @@ def init(*args, **kwargs): ClientSession.__init__ = init -def create_trace_config(): - # type: () -> TraceConfig - - async def on_request_start(session, trace_config_ctx, params): - # type: (ClientSession, SimpleNamespace, TraceRequestStartParams) -> None +def create_trace_config() -> "TraceConfig": + async def on_request_start( + session: ClientSession, + trace_config_ctx: SimpleNamespace, + params: TraceRequestStartParams, + ) -> None: if sentry_sdk.get_client().get_integration(AioHttpIntegration) is None: return @@ -269,8 +268,11 @@ async def on_request_start(session, trace_config_ctx, params): trace_config_ctx.span = span - async def on_request_end(session, trace_config_ctx, params): - # type: (ClientSession, SimpleNamespace, TraceRequestEndParams) -> None + async def on_request_end( + session: "ClientSession", + trace_config_ctx: "SimpleNamespace", + params: "TraceRequestEndParams", + ) -> None: if trace_config_ctx.span is None: return @@ -290,13 +292,13 @@ async def on_request_end(session, trace_config_ctx, params): return trace_config -def _make_request_processor(weak_request): - # type: (weakref.ReferenceType[Request]) -> EventProcessor +def _make_request_processor( + weak_request: "weakref.ReferenceType[Request]", +) -> "EventProcessor": def aiohttp_processor( - event, # type: Event - hint, # type: dict[str, Tuple[type, BaseException, Any]] - ): - # type: (...) -> Event + event: Event, + hint: dict[str, Tuple[type, BaseException, Any]], + ) -> Event: request = weak_request() if request is None: return event @@ -325,8 +327,7 @@ def aiohttp_processor( return aiohttp_processor -def _capture_exception(): - # type: () -> ExcInfo +def _capture_exception() -> "ExcInfo": exc_info = sys.exc_info() event, hint = event_from_exception( exc_info, @@ -340,8 +341,9 @@ def _capture_exception(): BODY_NOT_READ_MESSAGE = "[Can't show request body due to implementation details.]" -def get_aiohttp_request_data(request): - # type: (Request) -> Union[Optional[str], AnnotatedValue] +def get_aiohttp_request_data( + request: "Request", +) -> "Union[Optional[str], AnnotatedValue]": bytes_body = request._read_bytes if bytes_body is not None: diff --git a/sentry_sdk/integrations/anthropic.py b/sentry_sdk/integrations/anthropic.py index 208a4706f5..4ec652c0d4 100644 --- a/sentry_sdk/integrations/anthropic.py +++ b/sentry_sdk/integrations/anthropic.py @@ -49,13 +49,11 @@ class AnthropicIntegration(Integration): identifier = "anthropic" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True): - # type: (AnthropicIntegration, bool) -> None + def __init__(self: "AnthropicIntegration", include_prompts: bool = True) -> None: self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = package_version("anthropic") _check_minimum_version(AnthropicIntegration, version) @@ -63,8 +61,7 @@ def setup_once(): AsyncMessages.create = _wrap_message_create_async(AsyncMessages.create) -def _capture_exception(exc): - # type: (Any) -> None +def _capture_exception(exc: "Any") -> None: set_span_errored() event, hint = event_from_exception( @@ -75,8 +72,7 @@ def _capture_exception(exc): sentry_sdk.capture_event(event, hint=hint) -def _get_token_usage(result): - # type: (Messages) -> tuple[int, int] +def _get_token_usage(result: "Messages") -> "tuple[int, int]": """ Get token usage from the Anthropic response. """ @@ -92,8 +88,13 @@ def _get_token_usage(result): return input_tokens, output_tokens -def _collect_ai_data(event, model, input_tokens, output_tokens, content_blocks): - # type: (MessageStreamEvent, str | None, int, int, list[str]) -> tuple[str | None, int, int, list[str]] +def _collect_ai_data( + event: "MessageStreamEvent", + model: "str | None", + input_tokens: int, + output_tokens: int, + content_blocks: "list[str]", +) -> "tuple[str | None, int, int, list[str]]": """ Collect model information, token usage, and collect content blocks from the AI streaming response. """ @@ -119,8 +120,9 @@ def _collect_ai_data(event, model, input_tokens, output_tokens, content_blocks): return model, input_tokens, output_tokens, content_blocks -def _set_input_data(span, kwargs, integration): - # type: (Span, dict[str, Any], AnthropicIntegration) -> None +def _set_input_data( + span: "Span", kwargs: "dict[str, Any]", integration: "AnthropicIntegration" +) -> None: """ Set input data for the span based on the provided keyword arguments for the anthropic message creation. """ @@ -135,7 +137,7 @@ def _set_input_data(span, kwargs, integration): ): normalized_messages = [] if system_prompt: - system_prompt_content = None # type: Optional[Union[str, List[dict[str, Any]]]] + system_prompt_content: "Optional[Union[str, List[dict[str, Any]]]]" = None if isinstance(system_prompt, str): system_prompt_content = system_prompt elif isinstance(system_prompt, Iterable): @@ -212,23 +214,22 @@ def _set_input_data(span, kwargs, integration): def _set_output_data( - span, - integration, - model, - input_tokens, - output_tokens, - content_blocks, - finish_span=False, -): - # type: (Span, AnthropicIntegration, str | None, int | None, int | None, list[Any], bool) -> None + span: "Span", + integration: "AnthropicIntegration", + model: "str | None", + input_tokens: "int | None", + output_tokens: "int | None", + content_blocks: "list[Any]", + finish_span: bool = False, +) -> None: """ Set output data for the span based on the AI response.""" span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, model) if should_send_default_pii() and integration.include_prompts: - output_messages = { + output_messages: "dict[str, list[Any]]" = { "response": [], "tool": [], - } # type: (dict[str, list[Any]]) + } for output in content_blocks: if output["type"] == "text": @@ -259,8 +260,7 @@ def _set_output_data( span.__exit__(None, None, None) -def _sentry_patched_create_common(f, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any +def _sentry_patched_create_common(f: "Any", *args: "Any", **kwargs: "Any") -> "Any": integration = kwargs.pop("integration") if integration is None: return f(*args, **kwargs) @@ -313,12 +313,11 @@ def _sentry_patched_create_common(f, *args, **kwargs): elif hasattr(result, "_iterator"): old_iterator = result._iterator - def new_iterator(): - # type: () -> Iterator[MessageStreamEvent] + def new_iterator() -> "Iterator[MessageStreamEvent]": model = None input_tokens = 0 output_tokens = 0 - content_blocks = [] # type: list[str] + content_blocks: "list[str]" = [] for event in old_iterator: model, input_tokens, output_tokens, content_blocks = ( @@ -338,12 +337,11 @@ def new_iterator(): finish_span=True, ) - async def new_iterator_async(): - # type: () -> AsyncIterator[MessageStreamEvent] + async def new_iterator_async() -> "AsyncIterator[MessageStreamEvent]": model = None input_tokens = 0 output_tokens = 0 - content_blocks = [] # type: list[str] + content_blocks: "list[str]" = [] async for event in old_iterator: model, input_tokens, output_tokens, content_blocks = ( @@ -375,10 +373,8 @@ async def new_iterator_async(): return result -def _wrap_message_create(f): - # type: (Any) -> Any - def _execute_sync(f, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any +def _wrap_message_create(f: "Any") -> "Any": + def _execute_sync(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _sentry_patched_create_common(f, *args, **kwargs) try: @@ -398,8 +394,7 @@ def _execute_sync(f, *args, **kwargs): return e.value @wraps(f) - def _sentry_patched_create_sync(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_patched_create_sync(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(AnthropicIntegration) kwargs["integration"] = integration @@ -414,10 +409,8 @@ def _sentry_patched_create_sync(*args, **kwargs): return _sentry_patched_create_sync -def _wrap_message_create_async(f): - # type: (Any) -> Any - async def _execute_async(f, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any +def _wrap_message_create_async(f: "Any") -> "Any": + async def _execute_async(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _sentry_patched_create_common(f, *args, **kwargs) try: @@ -437,8 +430,7 @@ async def _execute_async(f, *args, **kwargs): return e.value @wraps(f) - async def _sentry_patched_create_async(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def _sentry_patched_create_async(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(AnthropicIntegration) kwargs["integration"] = integration @@ -453,8 +445,7 @@ async def _sentry_patched_create_async(*args, **kwargs): return _sentry_patched_create_async -def _is_given(obj): - # type: (Any) -> bool +def _is_given(obj: "Any") -> bool: """ Check for givenness safely across different anthropic versions. """ diff --git a/sentry_sdk/integrations/argv.py b/sentry_sdk/integrations/argv.py index 315feefb4a..6bc5e0c390 100644 --- a/sentry_sdk/integrations/argv.py +++ b/sentry_sdk/integrations/argv.py @@ -16,11 +16,9 @@ class ArgvIntegration(Integration): identifier = "argv" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: @add_global_event_processor - def processor(event, hint): - # type: (Event, Optional[Hint]) -> Optional[Event] + def processor(event: Event, hint: Optional[Hint]) -> Optional[Event]: if sentry_sdk.get_client().get_integration(ArgvIntegration) is not None: extra = event.setdefault("extra", {}) # If some event processor decided to set extra to e.g. an diff --git a/sentry_sdk/integrations/ariadne.py b/sentry_sdk/integrations/ariadne.py index 1a95bc0145..d353b62bea 100644 --- a/sentry_sdk/integrations/ariadne.py +++ b/sentry_sdk/integrations/ariadne.py @@ -33,8 +33,7 @@ class AriadneIntegration(Integration): identifier = "ariadne" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = package_version("ariadne") _check_minimum_version(AriadneIntegration, version) @@ -43,15 +42,17 @@ def setup_once(): _patch_graphql() -def _patch_graphql(): - # type: () -> None +def _patch_graphql() -> None: old_parse_query = ariadne_graphql.parse_query old_handle_errors = ariadne_graphql.handle_graphql_errors old_handle_query_result = ariadne_graphql.handle_query_result @ensure_integration_enabled(AriadneIntegration, old_parse_query) - def _sentry_patched_parse_query(context_value, query_parser, data): - # type: (Optional[Any], Optional[QueryParser], Any) -> DocumentNode + def _sentry_patched_parse_query( + context_value: "Optional[Any]", + query_parser: "Optional[QueryParser]", + data: "Any", + ) -> "DocumentNode": event_processor = _make_request_event_processor(data) sentry_sdk.get_isolation_scope().add_event_processor(event_processor) @@ -59,8 +60,9 @@ def _sentry_patched_parse_query(context_value, query_parser, data): return result @ensure_integration_enabled(AriadneIntegration, old_handle_errors) - def _sentry_patched_handle_graphql_errors(errors, *args, **kwargs): - # type: (List[GraphQLError], Any, Any) -> GraphQLResult + def _sentry_patched_handle_graphql_errors( + errors: "List[GraphQLError]", *args: "Any", **kwargs: "Any" + ) -> "GraphQLResult": result = old_handle_errors(errors, *args, **kwargs) event_processor = _make_response_event_processor(result[1]) @@ -83,8 +85,9 @@ def _sentry_patched_handle_graphql_errors(errors, *args, **kwargs): return result @ensure_integration_enabled(AriadneIntegration, old_handle_query_result) - def _sentry_patched_handle_query_result(result, *args, **kwargs): - # type: (Any, Any, Any) -> GraphQLResult + def _sentry_patched_handle_query_result( + result: "Any", *args: "Any", **kwargs: "Any" + ) -> "GraphQLResult": query_result = old_handle_query_result(result, *args, **kwargs) event_processor = _make_response_event_processor(query_result[1]) @@ -111,12 +114,10 @@ def _sentry_patched_handle_query_result(result, *args, **kwargs): ariadne_graphql.handle_query_result = _sentry_patched_handle_query_result # type: ignore -def _make_request_event_processor(data): - # type: (GraphQLSchema) -> EventProcessor +def _make_request_event_processor(data: "GraphQLSchema") -> "EventProcessor": """Add request data and api_target to events.""" - def inner(event, hint): - # type: (Event, dict[str, Any]) -> Event + def inner(event: "Event", hint: "dict[str, Any]") -> "Event": if not isinstance(data, dict): return event @@ -143,12 +144,10 @@ def inner(event, hint): return inner -def _make_response_event_processor(response): - # type: (Dict[str, Any]) -> EventProcessor +def _make_response_event_processor(response: "Dict[str, Any]") -> "EventProcessor": """Add response data to the event's response context.""" - def inner(event, hint): - # type: (Event, dict[str, Any]) -> Event + def inner(event: "Event", hint: "dict[str, Any]") -> "Event": with capture_internal_exceptions(): if should_send_default_pii() and response.get("errors"): contexts = event.setdefault("contexts", {}) diff --git a/sentry_sdk/integrations/arq.py b/sentry_sdk/integrations/arq.py index b0b3d3f03e..43782126af 100644 --- a/sentry_sdk/integrations/arq.py +++ b/sentry_sdk/integrations/arq.py @@ -43,9 +43,7 @@ class ArqIntegration(Integration): origin = f"auto.queue.{identifier}" @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: try: if isinstance(ARQ_VERSION, str): version = parse_version(ARQ_VERSION) @@ -64,13 +62,13 @@ def setup_once(): ignore_logger("arq.worker") -def patch_enqueue_job(): - # type: () -> None +def patch_enqueue_job() -> None: old_enqueue_job = ArqRedis.enqueue_job original_kwdefaults = old_enqueue_job.__kwdefaults__ - async def _sentry_enqueue_job(self, function, *args, **kwargs): - # type: (ArqRedis, str, *Any, **Any) -> Optional[Job] + async def _sentry_enqueue_job( + self: "ArqRedis", function: str, *args: "Any", **kwargs: "Any" + ) -> "Optional[Job]": integration = sentry_sdk.get_client().get_integration(ArqIntegration) if integration is None: return await old_enqueue_job(self, function, *args, **kwargs) @@ -84,12 +82,10 @@ async def _sentry_enqueue_job(self, function, *args, **kwargs): ArqRedis.enqueue_job = _sentry_enqueue_job -def patch_run_job(): - # type: () -> None +def patch_run_job() -> None: old_run_job = Worker.run_job - async def _sentry_run_job(self, job_id, score): - # type: (Worker, str, int) -> None + async def _sentry_run_job(self: "Worker", job_id: str, score: int) -> None: integration = sentry_sdk.get_client().get_integration(ArqIntegration) if integration is None: return await old_run_job(self, job_id, score) @@ -112,8 +108,7 @@ async def _sentry_run_job(self, job_id, score): Worker.run_job = _sentry_run_job -def _capture_exception(exc_info): - # type: (ExcInfo) -> None +def _capture_exception(exc_info: "ExcInfo") -> None: scope = sentry_sdk.get_current_scope() if scope.transaction is not None: @@ -131,11 +126,10 @@ def _capture_exception(exc_info): sentry_sdk.capture_event(event, hint=hint) -def _make_event_processor(ctx, *args, **kwargs): - # type: (Dict[Any, Any], *Any, **Any) -> EventProcessor - def event_processor(event, hint): - # type: (Event, Hint) -> Optional[Event] - +def _make_event_processor( + ctx: "Dict[Any, Any]", *args: "Any", **kwargs: "Any" +) -> "EventProcessor": + def event_processor(event: Event, hint: Hint) -> Optional[Event]: with capture_internal_exceptions(): scope = sentry_sdk.get_current_scope() if scope.transaction is not None: @@ -162,11 +156,8 @@ def event_processor(event, hint): return event_processor -def _wrap_coroutine(name, coroutine): - # type: (str, WorkerCoroutine) -> WorkerCoroutine - - async def _sentry_coroutine(ctx, *args, **kwargs): - # type: (Dict[Any, Any], *Any, **Any) -> Any +def _wrap_coroutine(name: str, coroutine: "WorkerCoroutine") -> "WorkerCoroutine": + async def _sentry_coroutine(ctx: Dict[Any, Any], *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(ArqIntegration) if integration is None: return await coroutine(ctx, *args, **kwargs) @@ -187,13 +178,11 @@ async def _sentry_coroutine(ctx, *args, **kwargs): return _sentry_coroutine -def patch_create_worker(): - # type: () -> None +def patch_create_worker() -> None: old_create_worker = arq.worker.create_worker @ensure_integration_enabled(ArqIntegration, old_create_worker) - def _sentry_create_worker(*args, **kwargs): - # type: (*Any, **Any) -> Worker + def _sentry_create_worker(*args: "Any", **kwargs: "Any") -> "Worker": settings_cls = args[0] if isinstance(settings_cls, dict): @@ -232,16 +221,14 @@ def _sentry_create_worker(*args, **kwargs): arq.worker.create_worker = _sentry_create_worker -def _get_arq_function(func): - # type: (Union[str, Function, WorkerCoroutine]) -> Function +def _get_arq_function(func: "Union[str, Function, WorkerCoroutine]") -> "Function": arq_func = arq.worker.func(func) arq_func.coroutine = _wrap_coroutine(arq_func.name, arq_func.coroutine) return arq_func -def _get_arq_cron_job(cron_job): - # type: (CronJob) -> CronJob +def _get_arq_cron_job(cron_job: "CronJob") -> "CronJob": cron_job.coroutine = _wrap_coroutine(cron_job.name, cron_job.coroutine) return cron_job diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 28b44cc7ab..def461d709 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -55,9 +55,7 @@ TRANSACTION_STYLE_VALUES = ("endpoint", "url") -def _capture_exception(exc, mechanism_type="asgi"): - # type: (Any, str) -> None - +def _capture_exception(exc: "Any", mechanism_type: str = "asgi") -> None: event, hint = event_from_exception( exc, client_options=sentry_sdk.get_client().options, @@ -66,8 +64,7 @@ def _capture_exception(exc, mechanism_type="asgi"): sentry_sdk.capture_event(event, hint=hint) -def _looks_like_asgi3(app): - # type: (Any) -> bool +def _looks_like_asgi3(app: "Any") -> bool: """ Try to figure out if an application object supports ASGI3. @@ -94,15 +91,14 @@ class SentryAsgiMiddleware: def __init__( self, - app, # type: Any - unsafe_context_data=False, # type: bool - transaction_style="endpoint", # type: str - mechanism_type="asgi", # type: str - span_origin="manual", # type: str - http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: Tuple[str, ...] - asgi_version=None, # type: Optional[int] - ): - # type: (...) -> None + app: "Any", + unsafe_context_data: bool = False, + transaction_style: str = "endpoint", + mechanism_type: str = "asgi", + span_origin: str = "manual", + http_methods_to_capture: "Tuple[str, ...]" = DEFAULT_HTTP_METHODS_TO_CAPTURE, + asgi_version: "Optional[int]" = None, + ) -> None: """ Instrument an ASGI application with Sentry. Provides HTTP/websocket data to sent events and basic handling for exceptions bubbling up @@ -150,36 +146,32 @@ def __init__( elif asgi_version == 2: self.__call__ = self._run_asgi2 # type: ignore - def _capture_lifespan_exception(self, exc): - # type: (Exception) -> None + def _capture_lifespan_exception(self, exc: Exception) -> None: """Capture exceptions raise in application lifespan handlers. The separate function is needed to support overriding in derived integrations that use different catching mechanisms. """ return _capture_exception(exc=exc, mechanism_type=self.mechanism_type) - def _capture_request_exception(self, exc): - # type: (Exception) -> None + def _capture_request_exception(self, exc: Exception) -> None: """Capture exceptions raised in incoming request handlers. The separate function is needed to support overriding in derived integrations that use different catching mechanisms. """ return _capture_exception(exc=exc, mechanism_type=self.mechanism_type) - def _run_asgi2(self, scope): - # type: (Any) -> Any - async def inner(receive, send): - # type: (Any, Any) -> Any + def _run_asgi2(self, scope: "Any") -> "Any": + async def inner(receive: Any, send: Any) -> Any: return await self._run_app(scope, receive, send, asgi_version=2) return inner - async def _run_asgi3(self, scope, receive, send): - # type: (Any, Any, Any) -> Any + async def _run_asgi3(self, scope: "Any", receive: "Any", send: "Any") -> "Any": return await self._run_app(scope, receive, send, asgi_version=3) - async def _run_app(self, scope, receive, send, asgi_version): - # type: (Any, Any, Any, int) -> Any + async def _run_app( + self, scope: "Any", receive: "Any", send: "Any", asgi_version: int + ) -> "Any": is_recursive_asgi_middleware = _asgi_middleware_applied.get(False) is_lifespan = scope["type"] == "lifespan" if is_recursive_asgi_middleware or is_lifespan: @@ -244,8 +236,9 @@ async def _run_app(self, scope, receive, send, asgi_version): with transaction_context: try: - async def _sentry_wrapped_send(event): - # type: (Dict[str, Any]) -> Any + async def _sentry_wrapped_send( + event: "Dict[str, Any]", + ) -> "Any": if transaction is not None: is_http_response = ( event.get("type") == "http.response.start" @@ -270,8 +263,9 @@ async def _sentry_wrapped_send(event): finally: _asgi_middleware_applied.set(False) - def event_processor(self, event, hint, asgi_scope): - # type: (Event, Hint, Any) -> Optional[Event] + def event_processor( + self, event: "Event", hint: "Hint", asgi_scope: "Any" + ) -> "Optional[Event]": request_data = event.get("request", {}) request_data.update(_get_request_data(asgi_scope)) event["request"] = deepcopy(request_data) @@ -304,8 +298,9 @@ def event_processor(self, event, hint, asgi_scope): # data to your liking it's recommended to use the `before_send` callback # for that. - def _get_transaction_name_and_source(self, transaction_style, asgi_scope): - # type: (SentryAsgiMiddleware, str, Any) -> Tuple[str, str] + def _get_transaction_name_and_source( + self: "SentryAsgiMiddleware", transaction_style: str, asgi_scope: "Any" + ) -> "Tuple[str, str]": name = None source = SOURCE_FOR_STYLE[transaction_style] ty = asgi_scope.get("type") diff --git a/sentry_sdk/integrations/asyncio.py b/sentry_sdk/integrations/asyncio.py index a652edc280..f263c131cf 100644 --- a/sentry_sdk/integrations/asyncio.py +++ b/sentry_sdk/integrations/asyncio.py @@ -23,8 +23,7 @@ T = TypeVar("T", bound=Callable[..., Any]) -def get_name(coro): - # type: (Any) -> str +def get_name(coro: "Any") -> str: return ( getattr(coro, "__qualname__", None) or getattr(coro, "__name__", None) @@ -32,8 +31,7 @@ def get_name(coro): ) -def _wrap_coroutine(wrapped): - # type: (Coroutine[Any, Any, Any]) -> Callable[[T], T] +def _wrap_coroutine(wrapped: "Coroutine[Any, Any, Any]") -> "Callable[[T], T]": # Only __name__ and __qualname__ are copied from function to coroutine in CPython return functools.partial( functools.update_wrapper, @@ -43,19 +41,19 @@ def _wrap_coroutine(wrapped): ) -def patch_asyncio(): - # type: () -> None +def patch_asyncio() -> None: orig_task_factory = None try: loop = asyncio.get_running_loop() orig_task_factory = loop.get_task_factory() - def _sentry_task_factory(loop, coro, **kwargs): - # type: (asyncio.AbstractEventLoop, Coroutine[Any, Any, Any], Any) -> asyncio.Future[Any] - + def _sentry_task_factory( + loop: "asyncio.AbstractEventLoop", + coro: "Coroutine[Any, Any, Any]", + **kwargs: "Any", + ) -> "asyncio.Future[Any]": @_wrap_coroutine(coro) - async def _task_with_sentry_span_creation(): - # type: () -> Any + async def _task_with_sentry_span_creation() -> Any: result = None with sentry_sdk.isolation_scope(): @@ -116,8 +114,7 @@ async def _task_with_sentry_span_creation(): ) -def _capture_exception(): - # type: () -> ExcInfo +def _capture_exception() -> "ExcInfo": exc_info = sys.exc_info() client = sentry_sdk.get_client() @@ -139,6 +136,5 @@ class AsyncioIntegration(Integration): origin = f"auto.function.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: patch_asyncio() diff --git a/sentry_sdk/integrations/atexit.py b/sentry_sdk/integrations/atexit.py index dfc6d08e1a..efa4c74af0 100644 --- a/sentry_sdk/integrations/atexit.py +++ b/sentry_sdk/integrations/atexit.py @@ -12,15 +12,13 @@ from typing import Optional -def default_callback(pending, timeout): - # type: (int, int) -> None +def default_callback(pending: int, timeout: int) -> None: """This is the default shutdown callback that is set on the options. It prints out a message to stderr that informs the user that some events are still pending and the process is waiting for them to flush out. """ - def echo(msg): - # type: (str) -> None + def echo(msg: str) -> None: sys.stderr.write(msg + "\n") echo("Sentry is attempting to send %i pending events" % pending) @@ -32,18 +30,15 @@ def echo(msg): class AtexitIntegration(Integration): identifier = "atexit" - def __init__(self, callback=None): - # type: (Optional[Any]) -> None + def __init__(self, callback: "Optional[Any]" = None) -> None: if callback is None: callback = default_callback self.callback = callback @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: @atexit.register - def _shutdown(): - # type: () -> None + def _shutdown() -> None: client = sentry_sdk.get_client() integration = client.get_integration(AtexitIntegration) diff --git a/sentry_sdk/integrations/aws_lambda.py b/sentry_sdk/integrations/aws_lambda.py index 85d1a6c28c..cf743f8c35 100644 --- a/sentry_sdk/integrations/aws_lambda.py +++ b/sentry_sdk/integrations/aws_lambda.py @@ -40,11 +40,9 @@ MILLIS_TO_SECONDS = 1000.0 -def _wrap_init_error(init_error): - # type: (F) -> F +def _wrap_init_error(init_error: "F") -> "F": @ensure_integration_enabled(AwsLambdaIntegration, init_error) - def sentry_init_error(*args, **kwargs): - # type: (*Any, **Any) -> Any + def sentry_init_error(*args: Any, **kwargs: Any) -> Any: client = sentry_sdk.get_client() with capture_internal_exceptions(): @@ -72,12 +70,11 @@ def sentry_init_error(*args, **kwargs): return sentry_init_error # type: ignore -def _wrap_handler(handler): - # type: (F) -> F +def _wrap_handler(handler: "F") -> "F": @functools.wraps(handler) - def sentry_handler(aws_event, aws_context, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> Any - + def sentry_handler( + aws_event: Any, aws_context: Any, *args: Any, **kwargs: Any + ) -> Any: # Per https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html, # `event` here is *likely* a dictionary, but also might be a number of # other types (str, int, float, None). @@ -183,8 +180,7 @@ def sentry_handler(aws_event, aws_context, *args, **kwargs): return sentry_handler # type: ignore -def _drain_queue(): - # type: () -> None +def _drain_queue() -> None: with capture_internal_exceptions(): client = sentry_sdk.get_client() integration = client.get_integration(AwsLambdaIntegration) @@ -198,14 +194,11 @@ class AwsLambdaIntegration(Integration): identifier = "aws_lambda" origin = f"auto.function.{identifier}" - def __init__(self, timeout_warning=False): - # type: (bool) -> None + def __init__(self, timeout_warning: bool = False) -> None: self.timeout_warning = timeout_warning @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: lambda_bootstrap = get_lambda_bootstrap() if not lambda_bootstrap: logger.warning( @@ -226,8 +219,9 @@ def setup_once(): if pre_37: old_handle_event_request = lambda_bootstrap.handle_event_request - def sentry_handle_event_request(request_handler, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_handle_event_request( + request_handler: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": request_handler = _wrap_handler(request_handler) return old_handle_event_request(request_handler, *args, **kwargs) @@ -235,8 +229,9 @@ def sentry_handle_event_request(request_handler, *args, **kwargs): old_handle_http_request = lambda_bootstrap.handle_http_request - def sentry_handle_http_request(request_handler, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_handle_http_request( + request_handler: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": request_handler = _wrap_handler(request_handler) return old_handle_http_request(request_handler, *args, **kwargs) @@ -247,8 +242,7 @@ def sentry_handle_http_request(request_handler, *args, **kwargs): old_to_json = lambda_bootstrap.to_json - def sentry_to_json(*args, **kwargs): - # type: (*Any, **Any) -> Any + def sentry_to_json(*args: "Any", **kwargs: "Any") -> "Any": _drain_queue() return old_to_json(*args, **kwargs) @@ -273,10 +267,8 @@ def sentry_handle_event_request( # type: ignore # Patch the runtime client to drain the queue. This should work # even when the SDK is initialized inside of the handler - def _wrap_post_function(f): - # type: (F) -> F - def inner(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _wrap_post_function(f: "F") -> "F": + def inner(*args: Any, **kwargs: Any) -> Any: _drain_queue() return f(*args, **kwargs) @@ -294,9 +286,7 @@ def inner(*args, **kwargs): ) -def get_lambda_bootstrap(): - # type: () -> Optional[Any] - +def get_lambda_bootstrap() -> "Optional[Any]": # Python 3.7: If the bootstrap module is *already imported*, it is the # one we actually want to use (no idea what's in __main__) # @@ -331,12 +321,14 @@ def get_lambda_bootstrap(): return None -def _make_request_event_processor(aws_event, aws_context, configured_timeout): - # type: (Any, Any, Any) -> EventProcessor +def _make_request_event_processor( + aws_event: "Any", aws_context: "Any", configured_timeout: "Any" +) -> "EventProcessor": start_time = datetime.now(timezone.utc) - def event_processor(sentry_event, hint, start_time=start_time): - # type: (Event, Hint, datetime) -> Optional[Event] + def event_processor( + sentry_event: "Event", hint: "Hint", start_time: "datetime" = start_time + ) -> "Optional[Event]": remaining_time_in_milis = aws_context.get_remaining_time_in_millis() exec_duration = configured_timeout - remaining_time_in_milis @@ -399,8 +391,7 @@ def event_processor(sentry_event, hint, start_time=start_time): return event_processor -def _get_url(aws_event, aws_context): - # type: (Any, Any) -> str +def _get_url(aws_event: "Any", aws_context: "Any") -> str: path = aws_event.get("path", None) headers = aws_event.get("headers") @@ -414,8 +405,7 @@ def _get_url(aws_event, aws_context): return "awslambda:///{}".format(aws_context.function_name) -def _get_cloudwatch_logs_url(aws_context, start_time): - # type: (Any, datetime) -> str +def _get_cloudwatch_logs_url(aws_context: "Any", start_time: "datetime") -> str: """ Generates a CloudWatchLogs console URL based on the context object @@ -446,8 +436,7 @@ def _get_cloudwatch_logs_url(aws_context, start_time): return url -def _parse_formatted_traceback(formatted_tb): - # type: (list[str]) -> list[dict[str, Any]] +def _parse_formatted_traceback(formatted_tb: "list[str]") -> "list[dict[str, Any]]": frames = [] for frame in formatted_tb: match = re.match(r'File "(.+)", line (\d+), in (.+)', frame.strip()) @@ -468,8 +457,7 @@ def _parse_formatted_traceback(formatted_tb): return frames -def _event_from_error_json(error_json): - # type: (dict[str, Any]) -> Event +def _event_from_error_json(error_json: "dict[str, Any]") -> "Event": """ Converts the error JSON from AWS Lambda into a Sentry error event. This is not a full fletched event, but better than nothing. @@ -477,7 +465,7 @@ def _event_from_error_json(error_json): This is an example of where AWS creates the error JSON: https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/2.2.1/awslambdaric/bootstrap.py#L479 """ - event = { + event: "Event" = { "level": "error", "exception": { "values": [ @@ -496,6 +484,6 @@ def _event_from_error_json(error_json): } ], }, - } # type: Event + } return event diff --git a/sentry_sdk/integrations/beam.py b/sentry_sdk/integrations/beam.py index a2e4553f5a..6496e6293d 100644 --- a/sentry_sdk/integrations/beam.py +++ b/sentry_sdk/integrations/beam.py @@ -35,8 +35,7 @@ class BeamIntegration(Integration): identifier = "beam" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: from apache_beam.transforms.core import DoFn, ParDo # type: ignore ignore_logger("root") @@ -52,8 +51,9 @@ def setup_once(): old_init = ParDo.__init__ - def sentry_init_pardo(self, fn, *args, **kwargs): - # type: (ParDo, Any, *Any, **Any) -> Any + def sentry_init_pardo( + self: "ParDo", fn: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": # Do not monkey patch init twice if not getattr(self, "_sentry_is_patched", False): for func_name in function_patches: @@ -79,14 +79,11 @@ def sentry_init_pardo(self, fn, *args, **kwargs): ParDo.__init__ = sentry_init_pardo -def _wrap_inspect_call(cls, func_name): - # type: (Any, Any) -> Any - +def _wrap_inspect_call(cls: "Any", func_name: "Any") -> "Any": if not hasattr(cls, func_name): return None - def _inspect(self): - # type: (Any) -> Any + def _inspect(self: "Any") -> "Any": """ Inspect function overrides the way Beam gets argspec. """ @@ -113,15 +110,13 @@ def _inspect(self): return _inspect -def _wrap_task_call(func): - # type: (F) -> F +def _wrap_task_call(func: "F") -> "F": """ Wrap task call with a try catch to get exceptions. """ @wraps(func) - def _inner(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _inner(*args: "Any", **kwargs: "Any") -> "Any": try: gen = func(*args, **kwargs) except Exception: @@ -136,8 +131,7 @@ def _inner(*args, **kwargs): @ensure_integration_enabled(BeamIntegration) -def _capture_exception(exc_info): - # type: (ExcInfo) -> None +def _capture_exception(exc_info: "ExcInfo") -> None: """ Send Beam exception to Sentry. """ @@ -151,8 +145,7 @@ def _capture_exception(exc_info): sentry_sdk.capture_event(event, hint=hint) -def raise_exception(): - # type: () -> None +def raise_exception() -> None: """ Raise an exception. """ @@ -162,8 +155,7 @@ def raise_exception(): reraise(*exc_info) -def _wrap_generator_call(gen): - # type: (Iterator[T]) -> Iterator[T] +def _wrap_generator_call(gen: "Iterator[T]") -> "Iterator[T]": """ Wrap the generator to handle any failures. """ diff --git a/sentry_sdk/integrations/boto3.py b/sentry_sdk/integrations/boto3.py index 0207341f1b..979f6493ca 100644 --- a/sentry_sdk/integrations/boto3.py +++ b/sentry_sdk/integrations/boto3.py @@ -33,15 +33,15 @@ class Boto3Integration(Integration): origin = f"auto.http.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = parse_version(BOTOCORE_VERSION) _check_minimum_version(Boto3Integration, version, "botocore") orig_init = BaseClient.__init__ - def sentry_patched_init(self, *args, **kwargs): - # type: (Type[BaseClient], *Any, **Any) -> None + def sentry_patched_init( + self: "Type[BaseClient]", *args: "Any", **kwargs: "Any" + ) -> None: orig_init(self, *args, **kwargs) meta = self.meta service_id = meta.service_model.service_id.hyphenize() @@ -56,8 +56,9 @@ def sentry_patched_init(self, *args, **kwargs): @ensure_integration_enabled(Boto3Integration) -def _sentry_request_created(service_id, request, operation_name, **kwargs): - # type: (str, AWSRequest, str, **Any) -> None +def _sentry_request_created( + service_id: str, request: "AWSRequest", operation_name: str, **kwargs: "Any" +) -> None: description = "aws.%s.%s" % (service_id, operation_name) span = sentry_sdk.start_span( op=OP.HTTP_CLIENT, @@ -84,9 +85,10 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs): request.context["_sentrysdk_span"] = span -def _sentry_after_call(context, parsed, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], **Any) -> None - span = context.pop("_sentrysdk_span", None) # type: Optional[Span] +def _sentry_after_call( + context: "Dict[str, Any]", parsed: "Dict[str, Any]", **kwargs: "Any" +) -> None: + span: Optional[Span] = context.pop("_sentrysdk_span", None) # Span could be absent if the integration is disabled. if span is None: @@ -106,8 +108,7 @@ def _sentry_after_call(context, parsed, **kwargs): orig_read = body.read orig_close = body.close - def sentry_streaming_body_read(*args, **kwargs): - # type: (*Any, **Any) -> bytes + def sentry_streaming_body_read(*args: "Any", **kwargs: "Any") -> bytes: try: ret = orig_read(*args, **kwargs) if not ret: @@ -119,17 +120,17 @@ def sentry_streaming_body_read(*args, **kwargs): body.read = sentry_streaming_body_read - def sentry_streaming_body_close(*args, **kwargs): - # type: (*Any, **Any) -> None + def sentry_streaming_body_close(*args: "Any", **kwargs: "Any") -> None: streaming_span.finish() orig_close(*args, **kwargs) body.close = sentry_streaming_body_close -def _sentry_after_call_error(context, exception, **kwargs): - # type: (Dict[str, Any], Type[BaseException], **Any) -> None - span = context.pop("_sentrysdk_span", None) # type: Optional[Span] +def _sentry_after_call_error( + context: "Dict[str, Any]", exception: "Type[BaseException]", **kwargs: "Any" +) -> None: + span: Optional[Span] = context.pop("_sentrysdk_span", None) # Span could be absent if the integration is disabled. if span is None: diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index 8a9fc41208..91805ea0cd 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -55,12 +55,10 @@ class BottleIntegration(Integration): def __init__( self, - transaction_style="endpoint", # type: str + transaction_style: str = "endpoint", *, - failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int] - ): - # type: (...) -> None - + failed_request_status_codes: "Set[int]" = _DEFAULT_FAILED_REQUEST_STATUS_CODES, + ) -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -70,16 +68,16 @@ def __init__( self.failed_request_status_codes = failed_request_status_codes @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = parse_version(BOTTLE_VERSION) _check_minimum_version(BottleIntegration, version) old_app = Bottle.__call__ @ensure_integration_enabled(BottleIntegration, old_app) - def sentry_patched_wsgi_app(self, environ, start_response): - # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse + def sentry_patched_wsgi_app( + self: "Any", environ: "Dict[str, str]", start_response: "Callable[..., Any]" + ) -> "_ScopedResponse": middleware = SentryWsgiMiddleware( lambda *a, **kw: old_app(self, *a, **kw), span_origin=BottleIntegration.origin, @@ -92,8 +90,7 @@ def sentry_patched_wsgi_app(self, environ, start_response): old_handle = Bottle._handle @functools.wraps(old_handle) - def _patched_handle(self, environ): - # type: (Bottle, Dict[str, Any]) -> Any + def _patched_handle(self: "Bottle", environ: "Dict[str, Any]") -> "Any": integration = sentry_sdk.get_client().get_integration(BottleIntegration) if integration is None: return old_handle(self, environ) @@ -112,16 +109,16 @@ def _patched_handle(self, environ): old_make_callback = Route._make_callback @functools.wraps(old_make_callback) - def patched_make_callback(self, *args, **kwargs): - # type: (Route, *object, **object) -> Any + def patched_make_callback( + self: "Route", *args: object, **kwargs: object + ) -> "Any": prepared_callback = old_make_callback(self, *args, **kwargs) integration = sentry_sdk.get_client().get_integration(BottleIntegration) if integration is None: return prepared_callback - def wrapped_callback(*args, **kwargs): - # type: (*object, **object) -> Any + def wrapped_callback(*args: object, **kwargs: object) -> "Any": try: res = prepared_callback(*args, **kwargs) except Exception as exception: @@ -142,38 +139,33 @@ def wrapped_callback(*args, **kwargs): class BottleRequestExtractor(RequestExtractor): - def env(self): - # type: () -> Dict[str, str] + def env(self) -> "Dict[str, str]": return self.request.environ - def cookies(self): - # type: () -> Dict[str, str] + def cookies(self) -> "Dict[str, str]": return self.request.cookies - def raw_data(self): - # type: () -> bytes + def raw_data(self) -> bytes: return self.request.body.read() - def form(self): - # type: () -> FormsDict + def form(self) -> "FormsDict": if self.is_json(): return None return self.request.forms.decode() - def files(self): - # type: () -> Optional[Dict[str, str]] + def files(self) -> "Optional[Dict[str, str]]": if self.is_json(): return None return self.request.files - def size_of_file(self, file): - # type: (FileUpload) -> int + def size_of_file(self, file: "FileUpload") -> int: return file.content_length -def _set_transaction_name_and_source(event, transaction_style, request): - # type: (Event, str, Any) -> None +def _set_transaction_name_and_source( + event: "Event", transaction_style: str, request: "Any" +) -> None: name = "" if transaction_style == "url": @@ -196,11 +188,10 @@ def _set_transaction_name_and_source(event, transaction_style, request): event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} -def _make_request_event_processor(app, request, integration): - # type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor - - def event_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_request_event_processor( + app: "Bottle", request: "LocalRequest", integration: "BottleIntegration" +) -> "EventProcessor": + def event_processor(event: Event, hint: dict[str, Any]) -> Event: _set_transaction_name_and_source(event, integration.transaction_style, request) with capture_internal_exceptions(): @@ -211,8 +202,7 @@ def event_processor(event, hint): return event_processor -def _capture_exception(exception, handled): - # type: (BaseException, bool) -> None +def _capture_exception(exception: BaseException, handled: bool) -> None: event, hint = event_from_exception( exception, client_options=sentry_sdk.get_client().options, diff --git a/sentry_sdk/integrations/celery/__init__.py b/sentry_sdk/integrations/celery/__init__.py index b5601fc0f9..0ea588a967 100644 --- a/sentry_sdk/integrations/celery/__init__.py +++ b/sentry_sdk/integrations/celery/__init__.py @@ -63,11 +63,10 @@ class CeleryIntegration(Integration): def __init__( self, - propagate_traces=True, - monitor_beat_tasks=False, - exclude_beat_tasks=None, - ): - # type: (bool, bool, Optional[List[str]]) -> None + propagate_traces: bool = True, + monitor_beat_tasks: bool = False, + exclude_beat_tasks: "Optional[List[str]]" = None, + ) -> None: self.propagate_traces = propagate_traces self.monitor_beat_tasks = monitor_beat_tasks self.exclude_beat_tasks = exclude_beat_tasks @@ -77,8 +76,7 @@ def __init__( _setup_celery_beat_signals(monitor_beat_tasks) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: _check_minimum_version(CeleryIntegration, CELERY_VERSION) _patch_build_tracer() @@ -98,16 +96,14 @@ def setup_once(): ignore_logger("celery.redirected") -def _set_status(status): - # type: (str) -> None +def _set_status(status: str) -> None: with capture_internal_exceptions(): scope = sentry_sdk.get_current_scope() if scope.span is not None: scope.span.set_status(status) -def _capture_exception(task, exc_info): - # type: (Any, ExcInfo) -> None +def _capture_exception(task: "Any", exc_info: "ExcInfo") -> None: client = sentry_sdk.get_client() if client.get_integration(CeleryIntegration) is None: return @@ -131,11 +127,14 @@ def _capture_exception(task, exc_info): sentry_sdk.capture_event(event, hint=hint) -def _make_event_processor(task, uuid, args, kwargs, request=None): - # type: (Any, Any, Any, Any, Optional[Any]) -> EventProcessor - def event_processor(event, hint): - # type: (Event, Hint) -> Optional[Event] - +def _make_event_processor( + task: "Any", + uuid: "Any", + args: "Any", + kwargs: "Any", + request: "Optional[Any]" = None, +) -> "EventProcessor": + def event_processor(event: Event, hint: Hint) -> Optional[Event]: with capture_internal_exceptions(): tags = event.setdefault("tags", {}) tags["celery_task_id"] = uuid @@ -160,8 +159,9 @@ def event_processor(event, hint): return event_processor -def _update_celery_task_headers(original_headers, span, monitor_beat_tasks): - # type: (dict[str, Any], Optional[Span], bool) -> dict[str, Any] +def _update_celery_task_headers( + original_headers: "dict[str, Any]", span: "Optional[Span]", monitor_beat_tasks: bool +) -> "dict[str, Any]": """ Updates the headers of the Celery task with the tracing information and eventually Sentry Crons monitoring information for beat tasks. @@ -235,20 +235,16 @@ def _update_celery_task_headers(original_headers, span, monitor_beat_tasks): class NoOpMgr: - def __enter__(self): - # type: () -> None + def __enter__(self) -> None: return None - def __exit__(self, exc_type, exc_value, traceback): - # type: (Any, Any, Any) -> None + def __exit__(self, exc_type: "Any", exc_value: "Any", traceback: "Any") -> None: return None -def _wrap_task_run(f): - # type: (F) -> F +def _wrap_task_run(f: "F") -> "F": @wraps(f) - def apply_async(*args, **kwargs): - # type: (*Any, **Any) -> Any + def apply_async(*args: Any, **kwargs: Any) -> Any: # Note: kwargs can contain headers=None, so no setdefault! # Unsure which backend though. integration = sentry_sdk.get_client().get_integration(CeleryIntegration) @@ -264,7 +260,7 @@ def apply_async(*args, **kwargs): return f(*args, **kwargs) if isinstance(args[0], Task): - task_name = args[0].name # type: str + task_name: str = args[0].name elif len(args) > 1 and isinstance(args[1], str): task_name = args[1] else: @@ -272,7 +268,7 @@ def apply_async(*args, **kwargs): task_started_from_beat = sentry_sdk.get_isolation_scope()._name == "celery-beat" - span_mgr = ( + span_mgr: "Union[Span, NoOpMgr]" = ( sentry_sdk.start_span( op=OP.QUEUE_SUBMIT_CELERY, name=task_name, @@ -280,7 +276,7 @@ def apply_async(*args, **kwargs): ) if not task_started_from_beat else NoOpMgr() - ) # type: Union[Span, NoOpMgr] + ) with span_mgr as span: kwargs["headers"] = _update_celery_task_headers( @@ -291,9 +287,7 @@ def apply_async(*args, **kwargs): return apply_async # type: ignore -def _wrap_tracer(task, f): - # type: (Any, F) -> F - +def _wrap_tracer(task: "Any", f: "F") -> "F": # Need to wrap tracer for pushing the scope before prerun is sent, and # popping it after postrun is sent. # @@ -302,8 +296,7 @@ def _wrap_tracer(task, f): # crashes. @wraps(f) @ensure_integration_enabled(CeleryIntegration, f) - def _inner(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _inner(*args: Any, **kwargs: Any) -> Any: with isolation_scope() as scope: scope._name = "celery" scope.clear_breadcrumbs() @@ -345,8 +338,7 @@ def _inner(*args, **kwargs): return _inner # type: ignore -def _set_messaging_destination_name(task, span): - # type: (Any, Span) -> None +def _set_messaging_destination_name(task: "Any", span: "Span") -> None: """Set "messaging.destination.name" tag for span""" with capture_internal_exceptions(): delivery_info = task.request.delivery_info @@ -358,9 +350,7 @@ def _set_messaging_destination_name(task, span): span.set_data(SPANDATA.MESSAGING_DESTINATION_NAME, routing_key) -def _wrap_task_call(task, f): - # type: (Any, F) -> F - +def _wrap_task_call(task: "Any", f: "F") -> "F": # Need to wrap task call because the exception is caught before we get to # see it. Also celery's reported stacktrace is untrustworthy. @@ -370,8 +360,7 @@ def _wrap_task_call(task, f): # to add @functools.wraps(f) here. # https://github.com/getsentry/sentry-python/issues/421 @ensure_integration_enabled(CeleryIntegration, f) - def _inner(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _inner(*args: Any, **kwargs: Any) -> Any: try: with sentry_sdk.start_span( op=OP.QUEUE_PROCESS, @@ -418,14 +407,14 @@ def _inner(*args, **kwargs): return _inner # type: ignore -def _patch_build_tracer(): - # type: () -> None +def _patch_build_tracer() -> None: import celery.app.trace as trace # type: ignore original_build_tracer = trace.build_tracer - def sentry_build_tracer(name, task, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> Any + def sentry_build_tracer( + name: "Any", task: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": if not getattr(task, "_sentry_is_patched", False): # determine whether Celery will use __call__ or run and patch # accordingly @@ -444,29 +433,24 @@ def sentry_build_tracer(name, task, *args, **kwargs): trace.build_tracer = sentry_build_tracer -def _patch_task_apply_async(): - # type: () -> None +def _patch_task_apply_async() -> None: Task.apply_async = _wrap_task_run(Task.apply_async) -def _patch_celery_send_task(): - # type: () -> None +def _patch_celery_send_task() -> None: from celery import Celery Celery.send_task = _wrap_task_run(Celery.send_task) -def _patch_worker_exit(): - # type: () -> None - +def _patch_worker_exit() -> None: # Need to flush queue before worker shutdown because a crashing worker will # call os._exit from billiard.pool import Worker # type: ignore original_workloop = Worker.workloop - def sentry_workloop(*args, **kwargs): - # type: (*Any, **Any) -> Any + def sentry_workloop(*args: "Any", **kwargs: "Any") -> "Any": try: return original_workloop(*args, **kwargs) finally: @@ -480,13 +464,11 @@ def sentry_workloop(*args, **kwargs): Worker.workloop = sentry_workloop -def _patch_producer_publish(): - # type: () -> None +def _patch_producer_publish() -> None: original_publish = Producer.publish @ensure_integration_enabled(CeleryIntegration, original_publish) - def sentry_publish(self, *args, **kwargs): - # type: (Producer, *Any, **Any) -> Any + def sentry_publish(self: "Producer", *args: "Any", **kwargs: "Any") -> "Any": kwargs_headers = kwargs.get("headers", {}) if not isinstance(kwargs_headers, Mapping): # Ensure kwargs_headers is a Mapping, so we can safely call get(). diff --git a/sentry_sdk/integrations/celery/beat.py b/sentry_sdk/integrations/celery/beat.py index 4b7e45e6f0..3202f991fe 100644 --- a/sentry_sdk/integrations/celery/beat.py +++ b/sentry_sdk/integrations/celery/beat.py @@ -42,8 +42,7 @@ RedBeatScheduler = None -def _get_headers(task): - # type: (Task) -> dict[str, Any] +def _get_headers(task: "Task") -> "dict[str, Any]": headers = task.request.get("headers") or {} # flatten nested headers @@ -56,12 +55,13 @@ def _get_headers(task): return headers -def _get_monitor_config(celery_schedule, app, monitor_name): - # type: (Any, Celery, str) -> MonitorConfig - monitor_config = {} # type: MonitorConfig - schedule_type = None # type: Optional[MonitorConfigScheduleType] - schedule_value = None # type: Optional[Union[str, int]] - schedule_unit = None # type: Optional[MonitorConfigScheduleUnit] +def _get_monitor_config( + celery_schedule: "Any", app: "Celery", monitor_name: str +) -> "MonitorConfig": + monitor_config: MonitorConfig = {} + schedule_type: "Optional[MonitorConfigScheduleType]" = None + schedule_value: "Optional[Union[str, int]]" = None + schedule_unit: "Optional[MonitorConfigScheduleUnit]" = None if isinstance(celery_schedule, crontab): schedule_type = "crontab" @@ -113,8 +113,11 @@ def _get_monitor_config(celery_schedule, app, monitor_name): return monitor_config -def _apply_crons_data_to_schedule_entry(scheduler, schedule_entry, integration): - # type: (Any, Any, sentry_sdk.integrations.celery.CeleryIntegration) -> None +def _apply_crons_data_to_schedule_entry( + scheduler: "Any", + schedule_entry: "Any", + integration: "sentry_sdk.integrations.celery.CeleryIntegration", +) -> None: """ Add Sentry Crons information to the schedule_entry headers. """ @@ -158,8 +161,9 @@ def _apply_crons_data_to_schedule_entry(scheduler, schedule_entry, integration): schedule_entry.options["headers"] = headers -def _wrap_beat_scheduler(original_function): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_beat_scheduler( + original_function: "Callable[..., Any]", +) -> "Callable[..., Any]": """ Makes sure that: - a new Sentry trace is started for each task started by Celery Beat and @@ -178,8 +182,7 @@ def _wrap_beat_scheduler(original_function): from sentry_sdk.integrations.celery import CeleryIntegration - def sentry_patched_scheduler(*args, **kwargs): - # type: (*Any, **Any) -> None + def sentry_patched_scheduler(*args: "Any", **kwargs: "Any") -> None: integration = sentry_sdk.get_client().get_integration(CeleryIntegration) if integration is None: return original_function(*args, **kwargs) @@ -197,29 +200,25 @@ def sentry_patched_scheduler(*args, **kwargs): return sentry_patched_scheduler -def _patch_beat_apply_entry(): - # type: () -> None +def _patch_beat_apply_entry() -> None: Scheduler.apply_entry = _wrap_beat_scheduler(Scheduler.apply_entry) -def _patch_redbeat_apply_async(): - # type: () -> None +def _patch_redbeat_apply_async() -> None: if RedBeatScheduler is None: return RedBeatScheduler.apply_async = _wrap_beat_scheduler(RedBeatScheduler.apply_async) -def _setup_celery_beat_signals(monitor_beat_tasks): - # type: (bool) -> None +def _setup_celery_beat_signals(monitor_beat_tasks: bool) -> None: if monitor_beat_tasks: task_success.connect(crons_task_success) task_failure.connect(crons_task_failure) task_retry.connect(crons_task_retry) -def crons_task_success(sender, **kwargs): - # type: (Task, dict[Any, Any]) -> None +def crons_task_success(sender: "Task", **kwargs: "dict[Any, Any]") -> None: logger.debug("celery_task_success %s", sender) headers = _get_headers(sender) @@ -243,8 +242,7 @@ def crons_task_success(sender, **kwargs): ) -def crons_task_failure(sender, **kwargs): - # type: (Task, dict[Any, Any]) -> None +def crons_task_failure(sender: "Task", **kwargs: "dict[Any, Any]") -> None: logger.debug("celery_task_failure %s", sender) headers = _get_headers(sender) @@ -268,8 +266,7 @@ def crons_task_failure(sender, **kwargs): ) -def crons_task_retry(sender, **kwargs): - # type: (Task, dict[Any, Any]) -> None +def crons_task_retry(sender: "Task", **kwargs: "dict[Any, Any]") -> None: logger.debug("celery_task_retry %s", sender) headers = _get_headers(sender) diff --git a/sentry_sdk/integrations/celery/utils.py b/sentry_sdk/integrations/celery/utils.py index a1961b15bc..f9378558c1 100644 --- a/sentry_sdk/integrations/celery/utils.py +++ b/sentry_sdk/integrations/celery/utils.py @@ -6,8 +6,7 @@ from sentry_sdk._types import MonitorConfigScheduleUnit -def _now_seconds_since_epoch(): - # type: () -> float +def _now_seconds_since_epoch() -> float: # We cannot use `time.perf_counter()` when dealing with the duration # of a Celery task, because the start of a Celery task and # the end are recorded in different processes. @@ -16,8 +15,7 @@ def _now_seconds_since_epoch(): return time.time() -def _get_humanized_interval(seconds): - # type: (float) -> Tuple[int, MonitorConfigScheduleUnit] +def _get_humanized_interval(seconds: float) -> "Tuple[int, MonitorConfigScheduleUnit]": TIME_UNITS = ( # noqa: N806 ("day", 60 * 60 * 24.0), ("hour", 60 * 60.0), @@ -34,10 +32,8 @@ def _get_humanized_interval(seconds): class NoOpMgr: - def __enter__(self): - # type: () -> None + def __enter__(self) -> None: return None - def __exit__(self, exc_type, exc_value, traceback): - # type: (Any, Any, Any) -> None + def __exit__(self, exc_type: "Any", exc_value: "Any", traceback: "Any") -> None: return None diff --git a/sentry_sdk/integrations/chalice.py b/sentry_sdk/integrations/chalice.py index 947e41ebf7..e196f5daed 100644 --- a/sentry_sdk/integrations/chalice.py +++ b/sentry_sdk/integrations/chalice.py @@ -32,8 +32,7 @@ class EventSourceHandler(ChaliceEventSourceHandler): # type: ignore - def __call__(self, event, context): - # type: (Any, Any) -> Any + def __call__(self, event: "Any", context: "Any") -> "Any": client = sentry_sdk.get_client() with sentry_sdk.isolation_scope() as scope: @@ -56,11 +55,11 @@ def __call__(self, event, context): reraise(*exc_info) -def _get_view_function_response(app, view_function, function_args): - # type: (Any, F, Any) -> F +def _get_view_function_response( + app: "Any", view_function: "F", function_args: "Any" +) -> "F": @wraps(view_function) - def wrapped_view_function(**function_args): - # type: (**Any) -> Any + def wrapped_view_function(**function_args: Any) -> Any: client = sentry_sdk.get_client() with sentry_sdk.isolation_scope() as scope: with capture_internal_exceptions(): @@ -99,9 +98,7 @@ class ChaliceIntegration(Integration): identifier = "chalice" @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: version = parse_version(CHALICE_VERSION) if version is None: @@ -116,8 +113,9 @@ def setup_once(): RestAPIEventHandler._get_view_function_response ) - def sentry_event_response(app, view_function, function_args): - # type: (Any, F, Dict[str, Any]) -> Any + def sentry_event_response( + app: "Any", view_function: "F", function_args: "Dict[str, Any]" + ) -> "Any": wrapped_view_function = _get_view_function_response( app, view_function, function_args ) diff --git a/sentry_sdk/integrations/cloud_resource_context.py b/sentry_sdk/integrations/cloud_resource_context.py index ca5ae47e6b..09d55ac119 100644 --- a/sentry_sdk/integrations/cloud_resource_context.py +++ b/sentry_sdk/integrations/cloud_resource_context.py @@ -65,13 +65,11 @@ class CloudResourceContextIntegration(Integration): gcp_metadata = None - def __init__(self, cloud_provider=""): - # type: (str) -> None + def __init__(self, cloud_provider: str = "") -> None: CloudResourceContextIntegration.cloud_provider = cloud_provider @classmethod - def _is_aws(cls): - # type: () -> bool + def _is_aws(cls) -> bool: try: r = cls.http.request( "PUT", @@ -95,8 +93,7 @@ def _is_aws(cls): return False @classmethod - def _get_aws_context(cls): - # type: () -> Dict[str, str] + def _get_aws_context(cls) -> "Dict[str, str]": ctx = { "cloud.provider": CLOUD_PROVIDER.AWS, "cloud.platform": CLOUD_PLATFORM.AWS_EC2, @@ -149,8 +146,7 @@ def _get_aws_context(cls): return ctx @classmethod - def _is_gcp(cls): - # type: () -> bool + def _is_gcp(cls) -> bool: try: r = cls.http.request( "GET", @@ -174,8 +170,7 @@ def _is_gcp(cls): return False @classmethod - def _get_gcp_context(cls): - # type: () -> Dict[str, str] + def _get_gcp_context(cls) -> "Dict[str, str]": ctx = { "cloud.provider": CLOUD_PROVIDER.GCP, "cloud.platform": CLOUD_PLATFORM.GCP_COMPUTE_ENGINE, @@ -229,8 +224,7 @@ def _get_gcp_context(cls): return ctx @classmethod - def _get_cloud_provider(cls): - # type: () -> str + def _get_cloud_provider(cls) -> str: if cls._is_aws(): return CLOUD_PROVIDER.AWS @@ -240,8 +234,7 @@ def _get_cloud_provider(cls): return "" @classmethod - def _get_cloud_resource_context(cls): - # type: () -> Dict[str, str] + def _get_cloud_resource_context(cls) -> "Dict[str, str]": cloud_provider = ( cls.cloud_provider if cls.cloud_provider != "" @@ -253,8 +246,7 @@ def _get_cloud_resource_context(cls): return {} @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: cloud_provider = CloudResourceContextIntegration.cloud_provider unsupported_cloud_provider = ( cloud_provider != "" and cloud_provider not in context_getters.keys() diff --git a/sentry_sdk/integrations/cohere.py b/sentry_sdk/integrations/cohere.py index 3445900c80..053d7ffe53 100644 --- a/sentry_sdk/integrations/cohere.py +++ b/sentry_sdk/integrations/cohere.py @@ -72,20 +72,17 @@ class CohereIntegration(Integration): identifier = "cohere" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True): - # type: (CohereIntegration, bool) -> None + def __init__(self: "CohereIntegration", include_prompts: bool = True) -> None: self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: BaseCohere.chat = _wrap_chat(BaseCohere.chat, streaming=False) Client.embed = _wrap_embed(Client.embed) BaseCohere.chat_stream = _wrap_chat(BaseCohere.chat_stream, streaming=True) -def _capture_exception(exc): - # type: (Any) -> None +def _capture_exception(exc: "Any") -> None: set_span_errored() event, hint = event_from_exception( @@ -96,11 +93,10 @@ def _capture_exception(exc): sentry_sdk.capture_event(event, hint=hint) -def _wrap_chat(f, streaming): - # type: (Callable[..., Any], bool) -> Callable[..., Any] - - def collect_chat_response_fields(span, res, include_pii): - # type: (Span, NonStreamedChatResponse, bool) -> None +def _wrap_chat(f: "Callable[..., Any]", streaming: bool) -> "Callable[..., Any]": + def collect_chat_response_fields( + span: Span, res: NonStreamedChatResponse, include_pii: bool + ) -> None: if include_pii: if hasattr(res, "text"): set_data_normalized( @@ -134,8 +130,7 @@ def collect_chat_response_fields(span, res, include_pii): set_data_normalized(span, SPANDATA.AI_WARNINGS, res.meta.warnings) @wraps(f) - def new_chat(*args, **kwargs): - # type: (*Any, **Any) -> Any + def new_chat(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(CohereIntegration) if ( @@ -188,9 +183,7 @@ def new_chat(*args, **kwargs): if streaming: old_iterator = res - def new_iterator(): - # type: () -> Iterator[StreamedChatResponse] - + def new_iterator() -> "Iterator[StreamedChatResponse]": with capture_internal_exceptions(): for x in old_iterator: if isinstance(x, ChatStreamEndEvent) or isinstance( @@ -223,12 +216,9 @@ def new_iterator(): return new_chat -def _wrap_embed(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - +def _wrap_embed(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_embed(*args, **kwargs): - # type: (*Any, **Any) -> Any + def new_embed(*args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(CohereIntegration) if integration is None: return f(*args, **kwargs) diff --git a/sentry_sdk/integrations/dedupe.py b/sentry_sdk/integrations/dedupe.py index 99ac6ce164..f2163e5578 100644 --- a/sentry_sdk/integrations/dedupe.py +++ b/sentry_sdk/integrations/dedupe.py @@ -16,16 +16,13 @@ class DedupeIntegration(Integration): identifier = "dedupe" - def __init__(self): - # type: () -> None + def __init__(self) -> None: self._last_seen = ContextVar("last-seen") @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: @add_global_event_processor - def processor(event, hint): - # type: (Event, Optional[Hint]) -> Optional[Event] + def processor(event: Event, hint: Optional[Hint]) -> Optional[Event]: if hint is None: return event @@ -58,8 +55,7 @@ def processor(event, hint): return event @staticmethod - def reset_last_seen(): - # type: () -> None + def reset_last_seen() -> None: integration = sentry_sdk.get_client().get_integration(DedupeIntegration) if integration is None: return diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index c18a03a38c..0062abb825 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -91,14 +91,12 @@ if DJANGO_VERSION < (1, 10): - def is_authenticated(request_user): - # type: (Any) -> bool + def is_authenticated(request_user: "Any") -> bool: return request_user.is_authenticated() else: - def is_authenticated(request_user): - # type: (Any) -> bool + def is_authenticated(request_user: "Any") -> bool: return request_user.is_authenticated @@ -124,19 +122,18 @@ class DjangoIntegration(Integration): middleware_spans = None signals_spans = None cache_spans = None - signals_denylist = [] # type: list[signals.Signal] + signals_denylist: "list[signals.Signal]" = [] def __init__( self, - transaction_style="url", # type: str - middleware_spans=True, # type: bool - signals_spans=True, # type: bool - cache_spans=False, # type: bool - db_transaction_spans=False, # type: bool - signals_denylist=None, # type: Optional[list[signals.Signal]] - http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...] - ): - # type: (...) -> None + transaction_style: str = "url", + middleware_spans: bool = True, + signals_spans: bool = True, + cache_spans: bool = False, + db_transaction_spans: bool = False, + signals_denylist: "Optional[list[signals.Signal]]" = None, + http_methods_to_capture: "tuple[str, ...]" = DEFAULT_HTTP_METHODS_TO_CAPTURE, + ) -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -154,8 +151,7 @@ def __init__( self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture)) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: _check_minimum_version(DjangoIntegration, DJANGO_VERSION) install_sql_hook() @@ -170,8 +166,9 @@ def setup_once(): old_app = WSGIHandler.__call__ @ensure_integration_enabled(DjangoIntegration, old_app) - def sentry_patched_wsgi_handler(self, environ, start_response): - # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse + def sentry_patched_wsgi_handler( + self: "Any", environ: "Dict[str, str]", start_response: "Callable[..., Any]" + ) -> "_ScopedResponse": bound_old_app = old_app.__get__(self, WSGIHandler) from django.conf import settings @@ -201,8 +198,9 @@ def sentry_patched_wsgi_handler(self, environ, start_response): signals.got_request_exception.connect(_got_request_exception) @add_global_event_processor - def process_django_templates(event, hint): - # type: (Event, Optional[Hint]) -> Optional[Event] + def process_django_templates( + event: "Event", hint: "Optional[Hint]" + ) -> "Optional[Event]": if hint is None: return event @@ -244,8 +242,9 @@ def process_django_templates(event, hint): return event @add_global_repr_processor - def _django_queryset_repr(value, hint): - # type: (Any, Dict[str, Any]) -> Union[NotImplementedType, str] + def _django_queryset_repr( + value: "Any", hint: "Dict[str, Any]" + ) -> "Union[NotImplementedType, str]": try: # Django 1.6 can fail to import `QuerySet` when Django settings # have not yet been initialized. @@ -281,8 +280,7 @@ def _django_queryset_repr(value, hint): _DRF_PATCH_LOCK = threading.Lock() -def _patch_drf(): - # type: () -> None +def _patch_drf() -> None: """ Patch Django Rest Framework for more/better request data. DRF's request type is a wrapper around Django's request type. The attribute we're @@ -324,8 +322,9 @@ def _patch_drf(): else: old_drf_initial = APIView.initial - def sentry_patched_drf_initial(self, request, *args, **kwargs): - # type: (APIView, Any, *Any, **Any) -> Any + def sentry_patched_drf_initial( + self: "APIView", request: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": with capture_internal_exceptions(): request._request._sentry_drf_request_backref = weakref.ref( request @@ -336,8 +335,7 @@ def sentry_patched_drf_initial(self, request, *args, **kwargs): APIView.initial = sentry_patched_drf_initial -def _patch_channels(): - # type: () -> None +def _patch_channels() -> None: try: from channels.http import AsgiHandler # type: ignore except ImportError: @@ -361,8 +359,7 @@ def _patch_channels(): patch_channels_asgi_handler_impl(AsgiHandler) -def _patch_django_asgi_handler(): - # type: () -> None +def _patch_django_asgi_handler() -> None: try: from django.core.handlers.asgi import ASGIHandler except ImportError: @@ -383,8 +380,9 @@ def _patch_django_asgi_handler(): patch_django_asgi_handler_impl(ASGIHandler) -def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (sentry_sdk.Scope, str, WSGIRequest) -> None +def _set_transaction_name_and_source( + scope: "sentry_sdk.Scope", transaction_style: str, request: "WSGIRequest" +) -> None: try: transaction_name = None if transaction_style == "function_name": @@ -425,8 +423,7 @@ def _set_transaction_name_and_source(scope, transaction_style, request): pass -def _before_get_response(request): - # type: (WSGIRequest) -> None +def _before_get_response(request: "WSGIRequest") -> None: integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None: return @@ -442,8 +439,9 @@ def _before_get_response(request): ) -def _attempt_resolve_again(request, scope, transaction_style): - # type: (WSGIRequest, sentry_sdk.Scope, str) -> None +def _attempt_resolve_again( + request: "WSGIRequest", scope: "sentry_sdk.Scope", transaction_style: str +) -> None: """ Some django middlewares overwrite request.urlconf so we need to respect that contract, @@ -455,8 +453,7 @@ def _attempt_resolve_again(request, scope, transaction_style): _set_transaction_name_and_source(scope, transaction_style, request) -def _after_get_response(request): - # type: (WSGIRequest) -> None +def _after_get_response(request: "WSGIRequest") -> None: integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None or integration.transaction_style != "url": return @@ -465,8 +462,7 @@ def _after_get_response(request): _attempt_resolve_again(request, scope, integration.transaction_style) -def _patch_get_response(): - # type: () -> None +def _patch_get_response() -> None: """ patch get_response, because at that point we have the Django request object """ @@ -474,8 +470,9 @@ def _patch_get_response(): old_get_response = BaseHandler.get_response - def sentry_patched_get_response(self, request): - # type: (Any, WSGIRequest) -> Union[HttpResponse, BaseException] + def sentry_patched_get_response( + self: "Any", request: "WSGIRequest" + ) -> "Union[HttpResponse, BaseException]": _before_get_response(request) rv = old_get_response(self, request) _after_get_response(request) @@ -489,10 +486,10 @@ def sentry_patched_get_response(self, request): patch_get_response_async(BaseHandler, _before_get_response) -def _make_wsgi_request_event_processor(weak_request, integration): - # type: (Callable[[], WSGIRequest], DjangoIntegration) -> EventProcessor - def wsgi_request_event_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_wsgi_request_event_processor( + weak_request: "Callable[[], WSGIRequest]", integration: "DjangoIntegration" +) -> "EventProcessor": + def wsgi_request_event_processor(event: Event, hint: dict[str, Any]) -> Event: # if the request is gone we are fine not logging the data from # it. This might happen if the processor is pushed away to # another thread. @@ -517,8 +514,7 @@ def wsgi_request_event_processor(event, hint): return wsgi_request_event_processor -def _got_request_exception(request=None, **kwargs): - # type: (WSGIRequest, **Any) -> None +def _got_request_exception(request: "WSGIRequest" = None, **kwargs: "Any") -> None: client = sentry_sdk.get_client() integration = client.get_integration(DjangoIntegration) if integration is None: @@ -537,8 +533,7 @@ def _got_request_exception(request=None, **kwargs): class DjangoRequestExtractor(RequestExtractor): - def __init__(self, request): - # type: (Union[WSGIRequest, ASGIRequest]) -> None + def __init__(self, request: "Union[WSGIRequest, ASGIRequest]") -> None: try: drf_request = request._sentry_drf_request_backref() if drf_request is not None: @@ -547,18 +542,16 @@ def __init__(self, request): pass self.request = request - def env(self): - # type: () -> Dict[str, str] + def env(self) -> "Dict[str, str]": return self.request.META - def cookies(self): - # type: () -> Dict[str, Union[str, AnnotatedValue]] + def cookies(self) -> "Dict[str, Union[str, AnnotatedValue]]": privacy_cookies = [ django_settings.CSRF_COOKIE_NAME, django_settings.SESSION_COOKIE_NAME, ] - clean_cookies = {} # type: Dict[str, Union[str, AnnotatedValue]] + clean_cookies: "Dict[str, Union[str, AnnotatedValue]]" = {} for key, val in self.request.COOKIES.items(): if key in privacy_cookies: clean_cookies[key] = SENSITIVE_DATA_SUBSTITUTE @@ -567,32 +560,26 @@ def cookies(self): return clean_cookies - def raw_data(self): - # type: () -> bytes + def raw_data(self) -> bytes: return self.request.body - def form(self): - # type: () -> QueryDict + def form(self) -> "QueryDict": return self.request.POST - def files(self): - # type: () -> MultiValueDict + def files(self) -> "MultiValueDict": return self.request.FILES - def size_of_file(self, file): - # type: (Any) -> int + def size_of_file(self, file: "Any") -> int: return file.size - def parsed_body(self): - # type: () -> Optional[Dict[str, Any]] + def parsed_body(self) -> "Optional[Dict[str, Any]]": try: return self.request.data except Exception: return RequestExtractor.parsed_body(self) -def _set_user_info(request, event): - # type: (WSGIRequest, Event) -> None +def _set_user_info(request: "WSGIRequest", event: "Event") -> None: user_info = event.setdefault("user", {}) user = getattr(request, "user", None) @@ -616,8 +603,7 @@ def _set_user_info(request, event): pass -def install_sql_hook(): - # type: () -> None +def install_sql_hook() -> None: """If installed this causes Django's queries to be captured.""" try: from django.db.backends.utils import CursorWrapper @@ -642,8 +628,9 @@ def install_sql_hook(): return @ensure_integration_enabled(DjangoIntegration, real_execute) - def execute(self, sql, params=None): - # type: (CursorWrapper, Any, Optional[Any]) -> Any + def execute( + self: "CursorWrapper", sql: "Any", params: "Optional[Any]" = None + ) -> "Any": with record_sql_queries( cursor=self.cursor, query=sql, @@ -661,8 +648,9 @@ def execute(self, sql, params=None): return result @ensure_integration_enabled(DjangoIntegration, real_executemany) - def executemany(self, sql, param_list): - # type: (CursorWrapper, Any, List[Any]) -> Any + def executemany( + self: "CursorWrapper", sql: "Any", param_list: "List[Any]" + ) -> "Any": with record_sql_queries( cursor=self.cursor, query=sql, @@ -681,8 +669,7 @@ def executemany(self, sql, param_list): return result @ensure_integration_enabled(DjangoIntegration, real_connect) - def connect(self): - # type: (BaseDatabaseWrapper) -> None + def connect(self: "BaseDatabaseWrapper") -> None: with capture_internal_exceptions(): sentry_sdk.add_breadcrumb(message="connect", category="query") @@ -694,8 +681,7 @@ def connect(self): _set_db_data(span, self) return real_connect(self) - def _commit(self): - # type: (BaseDatabaseWrapper) -> None + def _commit(self: "BaseDatabaseWrapper") -> None: integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None or not integration.db_transaction_spans: @@ -709,8 +695,7 @@ def _commit(self): _set_db_data(span, self, SPANNAME.DB_COMMIT) return real_commit(self) - def _rollback(self): - # type: (BaseDatabaseWrapper) -> None + def _rollback(self: "BaseDatabaseWrapper") -> None: integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None or not integration.db_transaction_spans: @@ -732,8 +717,9 @@ def _rollback(self): ignore_logger("django.db.backends") -def _set_db_data(span, cursor_or_db, db_operation=None): - # type: (Span, Any, Optional[str]) -> None +def _set_db_data( + span: "Span", cursor_or_db: "Any", db_operation: "Optional[str]" = None +) -> None: db = cursor_or_db.db if hasattr(cursor_or_db, "db") else cursor_or_db vendor = db.vendor span.set_data(SPANDATA.DB_SYSTEM, vendor) @@ -787,8 +773,7 @@ def _set_db_data(span, cursor_or_db, db_operation=None): span.set_data(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address) -def add_template_context_repr_sequence(): - # type: () -> None +def add_template_context_repr_sequence() -> None: try: from django.template.context import BaseContext diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index 773c538045..ef1132fc9e 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -51,10 +51,8 @@ def markcoroutinefunction(func: "_F") -> "_F": return func -def _make_asgi_request_event_processor(request): - # type: (ASGIRequest) -> EventProcessor - def asgi_request_event_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_asgi_request_event_processor(request: "ASGIRequest") -> "EventProcessor": + def asgi_request_event_processor(event: Event, hint: dict[str, Any]) -> Event: # if the request is gone we are fine not logging the data from # it. This might happen if the processor is pushed away to # another thread. @@ -81,15 +79,14 @@ def asgi_request_event_processor(event, hint): return asgi_request_event_processor -def patch_django_asgi_handler_impl(cls): - # type: (Any) -> None - +def patch_django_asgi_handler_impl(cls: "Any") -> None: from sentry_sdk.integrations.django import DjangoIntegration old_app = cls.__call__ - async def sentry_patched_asgi_handler(self, scope, receive, send): - # type: (Any, Any, Any, Any) -> Any + async def sentry_patched_asgi_handler( + self: "Any", scope: "Any", receive: "Any", send: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None: return await old_app(self, scope, receive, send) @@ -110,8 +107,9 @@ async def sentry_patched_asgi_handler(self, scope, receive, send): old_create_request = cls.create_request @ensure_integration_enabled(DjangoIntegration, old_create_request) - def sentry_patched_create_request(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_patched_create_request( + self: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": request, error_response = old_create_request(self, *args, **kwargs) scope = sentry_sdk.get_isolation_scope() scope.add_event_processor(_make_asgi_request_event_processor(request)) @@ -121,20 +119,19 @@ def sentry_patched_create_request(self, *args, **kwargs): cls.create_request = sentry_patched_create_request -def patch_get_response_async(cls, _before_get_response): - # type: (Any, Any) -> None +def patch_get_response_async(cls: "Any", _before_get_response: "Any") -> None: old_get_response_async = cls.get_response_async - async def sentry_patched_get_response_async(self, request): - # type: (Any, Any) -> Union[HttpResponse, BaseException] + async def sentry_patched_get_response_async( + self: "Any", request: "Any" + ) -> "Union[HttpResponse, BaseException]": _before_get_response(request) return await old_get_response_async(self, request) cls.get_response_async = sentry_patched_get_response_async -def patch_channels_asgi_handler_impl(cls): - # type: (Any) -> None +def patch_channels_asgi_handler_impl(cls: "Any") -> None: import channels # type: ignore from sentry_sdk.integrations.django import DjangoIntegration @@ -142,8 +139,9 @@ def patch_channels_asgi_handler_impl(cls): if channels.__version__ < "3.0.0": old_app = cls.__call__ - async def sentry_patched_asgi_handler(self, receive, send): - # type: (Any, Any, Any) -> Any + async def sentry_patched_asgi_handler( + self: "Any", receive: "Any", send: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None: return await old_app(self, receive, send) @@ -165,13 +163,13 @@ async def sentry_patched_asgi_handler(self, receive, send): patch_django_asgi_handler_impl(cls) -def wrap_async_view(callback): - # type: (Any) -> Any +def wrap_async_view(callback: "Any") -> "Any": from sentry_sdk.integrations.django import DjangoIntegration @functools.wraps(callback) - async def sentry_wrapped_callback(request, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + async def sentry_wrapped_callback( + request: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() @@ -190,8 +188,9 @@ async def sentry_wrapped_callback(request, *args, **kwargs): return sentry_wrapped_callback -def _asgi_middleware_mixin_factory(_check_middleware_span): - # type: (Callable[..., Any]) -> Any +def _asgi_middleware_mixin_factory( + _check_middleware_span: "Callable[..., Any]", +) -> "Any": """ Mixin class factory that generates a middleware mixin for handling requests in async mode. @@ -201,14 +200,12 @@ class SentryASGIMixin: if TYPE_CHECKING: _inner = None - def __init__(self, get_response): - # type: (Callable[..., Any]) -> None + def __init__(self, get_response: "Callable[..., Any]") -> None: self.get_response = get_response self._acall_method = None self._async_check() - def _async_check(self): - # type: () -> None + def _async_check(self) -> None: """ If get_response is a coroutine function, turns us into async mode so a thread is not consumed during a whole request. @@ -217,16 +214,14 @@ def _async_check(self): if iscoroutinefunction(self.get_response): markcoroutinefunction(self) - def async_route_check(self): - # type: () -> bool + def async_route_check(self) -> bool: """ Function that checks if we are in async mode, and if we are forwards the handling of requests to __acall__ """ return iscoroutinefunction(self.get_response) - async def __acall__(self, *args, **kwargs): - # type: (*Any, **Any) -> Any + async def __acall__(self, *args: "Any", **kwargs: "Any") -> "Any": f = self._acall_method if f is None: if hasattr(self._inner, "__acall__"): diff --git a/sentry_sdk/integrations/django/caching.py b/sentry_sdk/integrations/django/caching.py index 82b602f9b5..2ea49a2fa1 100644 --- a/sentry_sdk/integrations/django/caching.py +++ b/sentry_sdk/integrations/django/caching.py @@ -28,22 +28,32 @@ ] -def _get_span_description(method_name, args, kwargs): - # type: (str, tuple[Any], dict[str, Any]) -> str +def _get_span_description( + method_name: str, args: "tuple[Any]", kwargs: "dict[str, Any]" +) -> str: return _key_as_string(_get_safe_key(method_name, args, kwargs)) -def _patch_cache_method(cache, method_name, address, port): - # type: (CacheHandler, str, Optional[str], Optional[int]) -> None +def _patch_cache_method( + cache: "CacheHandler", + method_name: str, + address: "Optional[str]", + port: "Optional[int]", +) -> None: from sentry_sdk.integrations.django import DjangoIntegration original_method = getattr(cache, method_name) @ensure_integration_enabled(DjangoIntegration, original_method) def _instrument_call( - cache, method_name, original_method, args, kwargs, address, port - ): - # type: (CacheHandler, str, Callable[..., Any], tuple[Any, ...], dict[str, Any], Optional[str], Optional[int]) -> Any + cache: "CacheHandler", + method_name: str, + original_method: "Callable[..., Any]", + args: "tuple[Any, ...]", + kwargs: "dict[str, Any]", + address: "Optional[str]", + port: "Optional[int]", + ) -> "Any": is_set_operation = method_name.startswith("set") is_get_method = method_name == "get" is_get_many_method = method_name == "get_many" @@ -103,8 +113,7 @@ def _instrument_call( return value @functools.wraps(original_method) - def sentry_method(*args, **kwargs): - # type: (*Any, **Any) -> Any + def sentry_method(*args: "Any", **kwargs: "Any") -> "Any": return _instrument_call( cache, method_name, original_method, args, kwargs, address, port ) @@ -112,16 +121,18 @@ def sentry_method(*args, **kwargs): setattr(cache, method_name, sentry_method) -def _patch_cache(cache, address=None, port=None): - # type: (CacheHandler, Optional[str], Optional[int]) -> None +def _patch_cache( + cache: "CacheHandler", address: "Optional[str]" = None, port: "Optional[int]" = None +) -> None: if not hasattr(cache, "_sentry_patched"): for method_name in METHODS_TO_INSTRUMENT: _patch_cache_method(cache, method_name, address, port) cache._sentry_patched = True -def _get_address_port(settings): - # type: (dict[str, Any]) -> tuple[Optional[str], Optional[int]] +def _get_address_port( + settings: "dict[str, Any]", +) -> "tuple[Optional[str], Optional[int]]": location = settings.get("LOCATION") # TODO: location can also be an array of locations @@ -146,8 +157,7 @@ def _get_address_port(settings): return address, int(port) if port is not None else None -def should_enable_cache_spans(): - # type: () -> bool +def should_enable_cache_spans() -> bool: from sentry_sdk.integrations.django import DjangoIntegration client = sentry_sdk.get_client() @@ -160,15 +170,13 @@ def should_enable_cache_spans(): ) -def patch_caching(): - # type: () -> None +def patch_caching() -> None: if not hasattr(CacheHandler, "_sentry_patched"): if DJANGO_VERSION < (3, 2): original_get_item = CacheHandler.__getitem__ @functools.wraps(original_get_item) - def sentry_get_item(self, alias): - # type: (CacheHandler, str) -> Any + def sentry_get_item(self: "CacheHandler", alias: str) -> "Any": cache = original_get_item(self, alias) if should_enable_cache_spans(): @@ -189,8 +197,7 @@ def sentry_get_item(self, alias): original_create_connection = CacheHandler.create_connection @functools.wraps(original_create_connection) - def sentry_create_connection(self, alias): - # type: (CacheHandler, str) -> Any + def sentry_create_connection(self: "CacheHandler", alias: str) -> "Any": cache = original_create_connection(self, alias) if should_enable_cache_spans(): diff --git a/sentry_sdk/integrations/django/middleware.py b/sentry_sdk/integrations/django/middleware.py index 245276566e..94c0decf87 100644 --- a/sentry_sdk/integrations/django/middleware.py +++ b/sentry_sdk/integrations/django/middleware.py @@ -38,14 +38,12 @@ from .asgi import _asgi_middleware_mixin_factory -def patch_django_middlewares(): - # type: () -> None +def patch_django_middlewares() -> None: from django.core.handlers import base old_import_string = base.import_string - def sentry_patched_import_string(dotted_path): - # type: (str) -> Any + def sentry_patched_import_string(dotted_path: str) -> "Any": rv = old_import_string(dotted_path) if _import_string_should_wrap_middleware.get(None): @@ -57,8 +55,7 @@ def sentry_patched_import_string(dotted_path): old_load_middleware = base.BaseHandler.load_middleware - def sentry_patched_load_middleware(*args, **kwargs): - # type: (Any, Any) -> Any + def sentry_patched_load_middleware(*args: "Any", **kwargs: "Any") -> "Any": _import_string_should_wrap_middleware.set(True) try: return old_load_middleware(*args, **kwargs) @@ -68,12 +65,10 @@ def sentry_patched_load_middleware(*args, **kwargs): base.BaseHandler.load_middleware = sentry_patched_load_middleware -def _wrap_middleware(middleware, middleware_name): - # type: (Any, str) -> Any +def _wrap_middleware(middleware: "Any", middleware_name: str) -> "Any": from sentry_sdk.integrations.django import DjangoIntegration - def _check_middleware_span(old_method): - # type: (Callable[..., Any]) -> Optional[Span] + def _check_middleware_span(old_method: "Callable[..., Any]") -> "Optional[Span]": integration = sentry_sdk.get_client().get_integration(DjangoIntegration) if integration is None or not integration.middleware_spans: return None @@ -95,12 +90,10 @@ def _check_middleware_span(old_method): return middleware_span - def _get_wrapped_method(old_method): - # type: (F) -> F + def _get_wrapped_method(old_method: "F") -> "F": with capture_internal_exceptions(): - def sentry_wrapped_method(*args, **kwargs): - # type: (*Any, **Any) -> Any + def sentry_wrapped_method(*args: "Any", **kwargs: "Any") -> "Any": middleware_span = _check_middleware_span(old_method) if middleware_span is None: @@ -130,8 +123,12 @@ class SentryWrappingMiddleware( middleware, "async_capable", False ) - def __init__(self, get_response=None, *args, **kwargs): - # type: (Optional[Callable[..., Any]], *Any, **Any) -> None + def __init__( + self, + get_response: "Optional[Callable[..., Any]]" = None, + *args: "Any", + **kwargs: "Any", + ) -> None: if get_response: self._inner = middleware(get_response, *args, **kwargs) else: @@ -143,8 +140,7 @@ def __init__(self, get_response=None, *args, **kwargs): # We need correct behavior for `hasattr()`, which we can only determine # when we have an instance of the middleware we're wrapping. - def __getattr__(self, method_name): - # type: (str) -> Any + def __getattr__(self, method_name: str) -> "Any": if method_name not in ( "process_request", "process_view", @@ -159,8 +155,7 @@ def __getattr__(self, method_name): self.__dict__[method_name] = rv return rv - def __call__(self, *args, **kwargs): - # type: (*Any, **Any) -> Any + def __call__(self, *args: "Any", **kwargs: "Any") -> "Any": if hasattr(self, "async_route_check") and self.async_route_check(): return self.__acall__(*args, **kwargs) diff --git a/sentry_sdk/integrations/django/signals_handlers.py b/sentry_sdk/integrations/django/signals_handlers.py index cb0f8b9d2e..c54741a6df 100644 --- a/sentry_sdk/integrations/django/signals_handlers.py +++ b/sentry_sdk/integrations/django/signals_handlers.py @@ -13,8 +13,7 @@ from typing import Any, Union -def _get_receiver_name(receiver): - # type: (Callable[..., Any]) -> str +def _get_receiver_name(receiver: "Callable[..., Any]") -> str: name = "" if hasattr(receiver, "__qualname__"): @@ -38,8 +37,7 @@ def _get_receiver_name(receiver): return name -def patch_signals(): - # type: () -> None +def patch_signals() -> None: """ Patch django signal receivers to create a span. @@ -50,19 +48,20 @@ def patch_signals(): old_live_receivers = Signal._live_receivers - def _sentry_live_receivers(self, sender): - # type: (Signal, Any) -> Union[tuple[list[Callable[..., Any]], list[Callable[..., Any]]], list[Callable[..., Any]]] + def _sentry_live_receivers( + self: "Signal", sender: "Any" + ) -> "Union[tuple[list[Callable[..., Any]], list[Callable[..., Any]]], list[Callable[..., Any]]]": if DJANGO_VERSION >= (5, 0): sync_receivers, async_receivers = old_live_receivers(self, sender) else: sync_receivers = old_live_receivers(self, sender) async_receivers = [] - def sentry_sync_receiver_wrapper(receiver): - # type: (Callable[..., Any]) -> Callable[..., Any] + def sentry_sync_receiver_wrapper( + receiver: "Callable[..., Any]", + ) -> "Callable[..., Any]": @wraps(receiver) - def wrapper(*args, **kwargs): - # type: (Any, Any) -> Any + def wrapper(*args: Any, **kwargs: Any) -> Any: signal_name = _get_receiver_name(receiver) with sentry_sdk.start_span( op=OP.EVENT_DJANGO, diff --git a/sentry_sdk/integrations/django/templates.py b/sentry_sdk/integrations/django/templates.py index 10e8a924b7..c8ca6682fe 100644 --- a/sentry_sdk/integrations/django/templates.py +++ b/sentry_sdk/integrations/django/templates.py @@ -25,9 +25,9 @@ from django.template.loader import LoaderOrigin as Origin -def get_template_frame_from_exception(exc_value): - # type: (Optional[BaseException]) -> Optional[Dict[str, Any]] - +def get_template_frame_from_exception( + exc_value: "Optional[BaseException]", +) -> "Optional[Dict[str, Any]]": # As of Django 1.9 or so the new template debug thing showed up. if hasattr(exc_value, "template_debug"): return _get_template_frame_from_debug(exc_value.template_debug) # type: ignore @@ -48,8 +48,7 @@ def get_template_frame_from_exception(exc_value): return None -def _get_template_name_description(template_name): - # type: (str) -> str +def _get_template_name_description(template_name: str) -> str: if isinstance(template_name, (list, tuple)): if template_name: return "[{}, ...]".format(template_name[0]) @@ -57,8 +56,7 @@ def _get_template_name_description(template_name): return template_name -def patch_templates(): - # type: () -> None +def patch_templates() -> None: from django.template.response import SimpleTemplateResponse from sentry_sdk.integrations.django import DjangoIntegration @@ -66,8 +64,7 @@ def patch_templates(): @property # type: ignore @ensure_integration_enabled(DjangoIntegration, real_rendered_content.fget) - def rendered_content(self): - # type: (SimpleTemplateResponse) -> str + def rendered_content(self: "SimpleTemplateResponse") -> str: with sentry_sdk.start_span( op=OP.TEMPLATE_RENDER, name=_get_template_name_description(self.template_name), @@ -86,9 +83,13 @@ def rendered_content(self): @functools.wraps(real_render) @ensure_integration_enabled(DjangoIntegration, real_render) - def render(request, template_name, context=None, *args, **kwargs): - # type: (django.http.HttpRequest, str, Optional[Dict[str, Any]], *Any, **Any) -> django.http.HttpResponse - + def render( + request: "django.http.HttpRequest", + template_name: str, + context: "Optional[Dict[str, Any]]" = None, + *args: "Any", + **kwargs: "Any", + ) -> "django.http.HttpResponse": # Inject trace meta tags into template context context = context or {} if "sentry_trace_meta" not in context: @@ -107,8 +108,7 @@ def render(request, template_name, context=None, *args, **kwargs): django.shortcuts.render = render -def _get_template_frame_from_debug(debug): - # type: (Dict[str, Any]) -> Dict[str, Any] +def _get_template_frame_from_debug(debug: "Dict[str, Any]") -> "Dict[str, Any]": if debug is None: return None @@ -139,8 +139,7 @@ def _get_template_frame_from_debug(debug): } -def _linebreak_iter(template_source): - # type: (str) -> Iterator[int] +def _linebreak_iter(template_source: str) -> "Iterator[int]": yield 0 p = template_source.find("\n") while p >= 0: @@ -148,8 +147,9 @@ def _linebreak_iter(template_source): p = template_source.find("\n", p + 1) -def _get_template_frame_from_source(source): - # type: (Tuple[Origin, Tuple[int, int]]) -> Optional[Dict[str, Any]] +def _get_template_frame_from_source( + source: "Tuple[Origin, Tuple[int, int]]", +) -> "Optional[Dict[str, Any]]": if not source: return None diff --git a/sentry_sdk/integrations/django/transactions.py b/sentry_sdk/integrations/django/transactions.py index 5a7d69f3c9..0017aa437c 100644 --- a/sentry_sdk/integrations/django/transactions.py +++ b/sentry_sdk/integrations/django/transactions.py @@ -32,8 +32,7 @@ from django.core.urlresolvers import get_resolver -def get_regex(resolver_or_pattern): - # type: (Union[URLPattern, URLResolver]) -> Pattern[str] +def get_regex(resolver_or_pattern: "Union[URLPattern, URLResolver]") -> "Pattern[str]": """Utility method for django's deprecated resolver.regex""" try: regex = resolver_or_pattern.regex @@ -53,10 +52,9 @@ class RavenResolver: _either_option_matcher = re.compile(r"\[([^\]]+)\|([^\]]+)\]") _camel_re = re.compile(r"([A-Z]+)([a-z])") - _cache = {} # type: Dict[URLPattern, str] + _cache: "Dict[URLPattern, str]" = {} - def _simplify(self, pattern): - # type: (Union[URLPattern, URLResolver]) -> str + def _simplify(self, pattern: "Union[URLPattern, URLResolver]") -> str: r""" Clean up urlpattern regexes into something readable by humans: @@ -107,9 +105,12 @@ def _simplify(self, pattern): return result - def _resolve(self, resolver, path, parents=None): - # type: (URLResolver, str, Optional[List[URLResolver]]) -> Optional[str] - + def _resolve( + self, + resolver: "URLResolver", + path: str, + parents: "Optional[List[URLResolver]]" = None, + ) -> "Optional[str]": match = get_regex(resolver).search(path) # Django < 2.0 if not match: @@ -147,10 +148,9 @@ def _resolve(self, resolver, path, parents=None): def resolve( self, - path, # type: str - urlconf=None, # type: Union[None, Tuple[URLPattern, URLPattern, URLResolver], Tuple[URLPattern]] - ): - # type: (...) -> Optional[str] + path: str, + urlconf: "Union[None, Tuple[URLPattern, URLPattern, URLResolver], Tuple[URLPattern]]" = None, + ) -> "Optional[str]": resolver = get_resolver(urlconf) match = self._resolve(resolver, path) return match diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index 0a9861a6a6..ddd74d5677 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -21,9 +21,7 @@ wrap_async_view = None # type: ignore -def patch_views(): - # type: () -> None - +def patch_views() -> None: from django.core.handlers.base import BaseHandler from django.template.response import SimpleTemplateResponse from sentry_sdk.integrations.django import DjangoIntegration @@ -31,8 +29,7 @@ def patch_views(): old_make_view_atomic = BaseHandler.make_view_atomic old_render = SimpleTemplateResponse.render - def sentry_patched_render(self): - # type: (SimpleTemplateResponse) -> Any + def sentry_patched_render(self: "SimpleTemplateResponse") -> "Any": with sentry_sdk.start_span( op=OP.VIEW_RESPONSE_RENDER, name="serialize response", @@ -41,8 +38,9 @@ def sentry_patched_render(self): return old_render(self) @functools.wraps(old_make_view_atomic) - def sentry_patched_make_view_atomic(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_patched_make_view_atomic( + self: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": callback = old_make_view_atomic(self, *args, **kwargs) # XXX: The wrapper function is created for every request. Find more @@ -69,13 +67,11 @@ def sentry_patched_make_view_atomic(self, *args, **kwargs): BaseHandler.make_view_atomic = sentry_patched_make_view_atomic -def _wrap_sync_view(callback): - # type: (Any) -> Any +def _wrap_sync_view(callback: "Any") -> "Any": from sentry_sdk.integrations.django import DjangoIntegration @functools.wraps(callback) - def sentry_wrapped_callback(request, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_wrapped_callback(request: "Any", *args: "Any", **kwargs: "Any") -> "Any": current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() diff --git a/sentry_sdk/integrations/dramatiq.py b/sentry_sdk/integrations/dramatiq.py index 8b85831cf4..4e9e648f18 100644 --- a/sentry_sdk/integrations/dramatiq.py +++ b/sentry_sdk/integrations/dramatiq.py @@ -50,18 +50,16 @@ class DramatiqIntegration(Integration): origin = f"auto.queue.{identifier}" @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: _patch_dramatiq_broker() -def _patch_dramatiq_broker(): - # type: () -> None +def _patch_dramatiq_broker() -> None: original_broker__init__ = Broker.__init__ - def sentry_patched_broker__init__(self, *args, **kw): - # type: (Broker, *Any, **Any) -> None + def sentry_patched_broker__init__( + self: "Broker", *args: "Any", **kw: "Any" + ) -> None: integration = sentry_sdk.get_client().get_integration(DramatiqIntegration) try: @@ -102,8 +100,9 @@ class SentryMiddleware(Middleware): # type: ignore[misc] SENTRY_HEADERS_NAME = "_sentry_headers" - def before_enqueue(self, broker, message, delay): - # type: (Broker, Message[R], int) -> None + def before_enqueue( + self, broker: "Broker", message: "Message[R]", delay: int + ) -> None: integration = sentry_sdk.get_client().get_integration(DramatiqIntegration) if integration is None: return @@ -113,8 +112,7 @@ def before_enqueue(self, broker, message, delay): SENTRY_TRACE_HEADER_NAME: get_traceparent(), } - def before_process_message(self, broker, message): - # type: (Broker, Message[R]) -> None + def before_process_message(self, broker: "Broker", message: "Message[R]") -> None: integration = sentry_sdk.get_client().get_integration(DramatiqIntegration) if integration is None: return @@ -146,8 +144,14 @@ def before_process_message(self, broker, message): ) transaction.__enter__() - def after_process_message(self, broker, message, *, result=None, exception=None): - # type: (Broker, Message[R], Optional[Any], Optional[Exception]) -> None + def after_process_message( + self, + broker: "Broker", + message: "Message[R]", + *, + result: "Optional[Any]" = None, + exception: "Optional[Exception]" = None, + ) -> None: integration = sentry_sdk.get_client().get_integration(DramatiqIntegration) if integration is None: return @@ -185,11 +189,10 @@ def after_process_message(self, broker, message, *, result=None, exception=None) scope_manager.__exit__(type(exception), exception, None) -def _make_message_event_processor(message, integration): - # type: (Message[R], DramatiqIntegration) -> Callable[[Event, Hint], Optional[Event]] - - def inner(event, hint): - # type: (Event, Hint) -> Optional[Event] +def _make_message_event_processor( + message: "Message[R]", integration: "DramatiqIntegration" +) -> "Callable[[Event, Hint], Optional[Event]]": + def inner(event: Event, hint: Hint) -> Optional[Event]: with capture_internal_exceptions(): DramatiqMessageExtractor(message).extract_into_event(event) @@ -199,16 +202,13 @@ def inner(event, hint): class DramatiqMessageExtractor: - def __init__(self, message): - # type: (Message[R]) -> None + def __init__(self, message: "Message[R]") -> None: self.message_data = dict(message.asdict()) - def content_length(self): - # type: () -> int + def content_length(self) -> int: return len(json.dumps(self.message_data)) - def extract_into_event(self, event): - # type: (Event) -> None + def extract_into_event(self, event: "Event") -> None: client = sentry_sdk.get_client() if not client.is_active(): return @@ -217,7 +217,7 @@ def extract_into_event(self, event): request_info = contexts.setdefault("dramatiq", {}) request_info["type"] = "dramatiq" - data = None # type: Optional[Union[AnnotatedValue, Dict[str, Any]]] + data: "Optional[Union[AnnotatedValue, Dict[str, Any]]]" = None if not request_body_within_bounds(client, self.content_length()): data = AnnotatedValue.removed_because_over_size_limit() else: diff --git a/sentry_sdk/integrations/excepthook.py b/sentry_sdk/integrations/excepthook.py index 61c7e460bf..4a9c9299d4 100644 --- a/sentry_sdk/integrations/excepthook.py +++ b/sentry_sdk/integrations/excepthook.py @@ -28,9 +28,7 @@ class ExcepthookIntegration(Integration): always_run = False - def __init__(self, always_run=False): - # type: (bool) -> None - + def __init__(self, always_run: bool = False) -> None: if not isinstance(always_run, bool): raise ValueError( "Invalid value for always_run: %s (must be type boolean)" @@ -39,15 +37,16 @@ def __init__(self, always_run=False): self.always_run = always_run @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: sys.excepthook = _make_excepthook(sys.excepthook) -def _make_excepthook(old_excepthook): - # type: (Excepthook) -> Excepthook - def sentry_sdk_excepthook(type_, value, traceback): - # type: (Type[BaseException], BaseException, Optional[TracebackType]) -> None +def _make_excepthook(old_excepthook: "Excepthook") -> "Excepthook": + def sentry_sdk_excepthook( + type_: Type[BaseException], + value: BaseException, + traceback: Optional[TracebackType], + ) -> None: integration = sentry_sdk.get_client().get_integration(ExcepthookIntegration) # Note: If we replace this with ensure_integration_enabled then @@ -70,8 +69,7 @@ def sentry_sdk_excepthook(type_, value, traceback): return sentry_sdk_excepthook -def _should_send(always_run=False): - # type: (bool) -> bool +def _should_send(always_run: bool = False) -> bool: if always_run: return True diff --git a/sentry_sdk/integrations/executing.py b/sentry_sdk/integrations/executing.py index 6e68b8c0c7..5718aaa89e 100644 --- a/sentry_sdk/integrations/executing.py +++ b/sentry_sdk/integrations/executing.py @@ -20,12 +20,9 @@ class ExecutingIntegration(Integration): identifier = "executing" @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: @add_global_event_processor - def add_executing_info(event, hint): - # type: (Event, Optional[Hint]) -> Optional[Event] + def add_executing_info(event: Event, hint: Optional[Hint]) -> Optional[Event]: if sentry_sdk.get_client().get_integration(ExecutingIntegration) is None: return event diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index ddedcb10de..21baf714e7 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -43,32 +43,26 @@ FALCON3 = False -_FALCON_UNSET = None # type: Optional[object] +_FALCON_UNSET: "Optional[object]" = None if FALCON3: # falcon.request._UNSET is only available in Falcon 3.0+ with capture_internal_exceptions(): from falcon.request import _UNSET as _FALCON_UNSET # type: ignore[import-not-found, no-redef] class FalconRequestExtractor(RequestExtractor): - def env(self): - # type: () -> Dict[str, Any] + def env(self) -> "Dict[str, Any]": return self.request.env - def cookies(self): - # type: () -> Dict[str, Any] + def cookies(self) -> "Dict[str, Any]": return self.request.cookies - def form(self): - # type: () -> None + def form(self) -> None: return None # No such concept in Falcon - def files(self): - # type: () -> None + def files(self) -> None: return None # No such concept in Falcon - def raw_data(self): - # type: () -> Optional[str] - + def raw_data(self) -> "Optional[str]": # As request data can only be read once we won't make this available # to Sentry. Just send back a dummy string in case there was a # content length. @@ -79,8 +73,7 @@ def raw_data(self): else: return None - def json(self): - # type: () -> Optional[Dict[str, Any]] + def json(self) -> "Optional[Dict[str, Any]]": # fallback to cached_media = None if self.request._media is not available cached_media = None with capture_internal_exceptions(): @@ -101,8 +94,9 @@ def json(self): class SentryFalconMiddleware: """Captures exceptions in Falcon requests and send to Sentry""" - def process_request(self, req, resp, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> None + def process_request( + self, req: "Any", resp: "Any", *args: "Any", **kwargs: "Any" + ) -> None: integration = sentry_sdk.get_client().get_integration(FalconIntegration) if integration is None: return @@ -121,8 +115,7 @@ class FalconIntegration(Integration): transaction_style = "" - def __init__(self, transaction_style="uri_template"): - # type: (str) -> None + def __init__(self, transaction_style: str = "uri_template") -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -131,9 +124,7 @@ def __init__(self, transaction_style="uri_template"): self.transaction_style = transaction_style @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: version = parse_version(FALCON_VERSION) _check_minimum_version(FalconIntegration, version) @@ -142,12 +133,12 @@ def setup_once(): _patch_prepare_middleware() -def _patch_wsgi_app(): - # type: () -> None +def _patch_wsgi_app() -> None: original_wsgi_app = falcon_app_class.__call__ - def sentry_patched_wsgi_app(self, env, start_response): - # type: (falcon.API, Any, Any) -> Any + def sentry_patched_wsgi_app( + self: "falcon.API", env: "Any", start_response: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(FalconIntegration) if integration is None: return original_wsgi_app(self, env, start_response) @@ -162,13 +153,11 @@ def sentry_patched_wsgi_app(self, env, start_response): falcon_app_class.__call__ = sentry_patched_wsgi_app -def _patch_handle_exception(): - # type: () -> None +def _patch_handle_exception() -> None: original_handle_exception = falcon_app_class._handle_exception @ensure_integration_enabled(FalconIntegration, original_handle_exception) - def sentry_patched_handle_exception(self, *args): - # type: (falcon.API, *Any) -> Any + def sentry_patched_handle_exception(self: "falcon.API", *args: "Any") -> "Any": # NOTE(jmagnusson): falcon 2.0 changed falcon.API._handle_exception # method signature from `(ex, req, resp, params)` to # `(req, resp, ex, params)` @@ -200,14 +189,14 @@ def sentry_patched_handle_exception(self, *args): falcon_app_class._handle_exception = sentry_patched_handle_exception -def _patch_prepare_middleware(): - # type: () -> None +def _patch_prepare_middleware() -> None: original_prepare_middleware = falcon_helpers.prepare_middleware def sentry_patched_prepare_middleware( - middleware=None, independent_middleware=False, asgi=False - ): - # type: (Any, Any, bool) -> Any + middleware: "Any" = None, + independent_middleware: "Any" = False, + asgi: bool = False, + ) -> "Any": if asgi: # We don't support ASGI Falcon apps, so we don't patch anything here return original_prepare_middleware(middleware, independent_middleware, asgi) @@ -223,8 +212,7 @@ def sentry_patched_prepare_middleware( falcon_helpers.prepare_middleware = sentry_patched_prepare_middleware -def _exception_leads_to_http_5xx(ex, response): - # type: (Exception, falcon.Response) -> bool +def _exception_leads_to_http_5xx(ex: Exception, response: "falcon.Response") -> bool: is_server_error = isinstance(ex, falcon.HTTPError) and (ex.status or "").startswith( "5" ) @@ -242,13 +230,13 @@ def _exception_leads_to_http_5xx(ex, response): ) -def _has_http_5xx_status(response): - # type: (falcon.Response) -> bool +def _has_http_5xx_status(response: "falcon.Response") -> bool: return response.status.startswith("5") -def _set_transaction_name_and_source(event, transaction_style, request): - # type: (Event, str, falcon.Request) -> None +def _set_transaction_name_and_source( + event: "Event", transaction_style: str, request: "falcon.Request" +) -> None: name_for_style = { "uri_template": request.uri_template, "path": request.path, @@ -257,11 +245,10 @@ def _set_transaction_name_and_source(event, transaction_style, request): event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} -def _make_request_event_processor(req, integration): - # type: (falcon.Request, FalconIntegration) -> EventProcessor - - def event_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_request_event_processor( + req: "falcon.Request", integration: "FalconIntegration" +) -> "EventProcessor": + def event_processor(event: Event, hint: dict[str, Any]) -> Event: _set_transaction_name_and_source(event, integration.transaction_style, req) with capture_internal_exceptions(): diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index 1473cbcab7..2b1ae0bbca 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -35,13 +35,13 @@ class FastApiIntegration(StarletteIntegration): identifier = "fastapi" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: patch_get_request_handler() -def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (sentry_sdk.Scope, str, Any) -> None +def _set_transaction_name_and_source( + scope: "sentry_sdk.Scope", transaction_style: str, request: "Any" +) -> None: name = "" if transaction_style == "endpoint": @@ -65,12 +65,10 @@ def _set_transaction_name_and_source(scope, transaction_style, request): scope.set_transaction_name(name, source=source) -def patch_get_request_handler(): - # type: () -> None +def patch_get_request_handler() -> None: old_get_request_handler = fastapi.routing.get_request_handler - def _sentry_get_request_handler(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_get_request_handler(*args: "Any", **kwargs: "Any") -> "Any": dependant = kwargs.get("dependant") if ( dependant @@ -80,8 +78,7 @@ def _sentry_get_request_handler(*args, **kwargs): old_call = dependant.call @wraps(old_call) - def _sentry_call(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_call(*args: "Any", **kwargs: "Any") -> "Any": current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() @@ -96,8 +93,7 @@ def _sentry_call(*args, **kwargs): old_app = old_get_request_handler(*args, **kwargs) - async def _sentry_app(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def _sentry_app(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(FastApiIntegration) if integration is None: return await old_app(*args, **kwargs) @@ -111,11 +107,10 @@ async def _sentry_app(*args, **kwargs): extractor = StarletteRequestExtractor(request) info = await extractor.extract_request_info() - def _make_request_event_processor(req, integration): - # type: (Any, Any) -> Callable[[Event, Dict[str, Any]], Event] - def event_processor(event, hint): - # type: (Event, Dict[str, Any]) -> Event - + def _make_request_event_processor( + req: "Any", integration: "Any" + ) -> "Callable[[Event, Dict[str, Any]], Event]": + def event_processor(event: Event, hint: Dict[str, Any]) -> Event: # Extract information from request request_info = event.get("request", {}) if info: diff --git a/sentry_sdk/integrations/flask.py b/sentry_sdk/integrations/flask.py index f45ec6db20..2200b43311 100644 --- a/sentry_sdk/integrations/flask.py +++ b/sentry_sdk/integrations/flask.py @@ -57,10 +57,9 @@ class FlaskIntegration(Integration): def __init__( self, - transaction_style="endpoint", # type: str - http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...] - ): - # type: (...) -> None + transaction_style: str = "endpoint", + http_methods_to_capture: "tuple[str, ...]" = DEFAULT_HTTP_METHODS_TO_CAPTURE, + ) -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -70,8 +69,7 @@ def __init__( self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture)) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: try: from quart import Quart # type: ignore @@ -93,8 +91,9 @@ def setup_once(): old_app = Flask.__call__ - def sentry_patched_wsgi_app(self, environ, start_response): - # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse + def sentry_patched_wsgi_app( + self: "Any", environ: "Dict[str, str]", start_response: "Callable[..., Any]" + ) -> "_ScopedResponse": if sentry_sdk.get_client().get_integration(FlaskIntegration) is None: return old_app(self, environ, start_response) @@ -114,8 +113,9 @@ def sentry_patched_wsgi_app(self, environ, start_response): Flask.__call__ = sentry_patched_wsgi_app -def _add_sentry_trace(sender, template, context, **extra): - # type: (Flask, Any, Dict[str, Any], **Any) -> None +def _add_sentry_trace( + sender: "Flask", template: "Any", context: "Dict[str, Any]", **extra: "Any" +) -> None: if "sentry_trace" in context: return @@ -125,8 +125,9 @@ def _add_sentry_trace(sender, template, context, **extra): context["sentry_trace_meta"] = trace_meta -def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (sentry_sdk.Scope, str, Request) -> None +def _set_transaction_name_and_source( + scope: "sentry_sdk.Scope", transaction_style: str, request: "Request" +) -> None: try: name_for_style = { "url": request.url_rule.rule, @@ -140,8 +141,7 @@ def _set_transaction_name_and_source(scope, transaction_style, request): pass -def _request_started(app, **kwargs): - # type: (Flask, **Any) -> None +def _request_started(app: "Flask", **kwargs: "Any") -> None: integration = sentry_sdk.get_client().get_integration(FlaskIntegration) if integration is None: return @@ -160,48 +160,38 @@ def _request_started(app, **kwargs): class FlaskRequestExtractor(RequestExtractor): - def env(self): - # type: () -> Dict[str, str] + def env(self) -> "Dict[str, str]": return self.request.environ - def cookies(self): - # type: () -> Dict[Any, Any] + def cookies(self) -> "Dict[Any, Any]": return { k: v[0] if isinstance(v, list) and len(v) == 1 else v for k, v in self.request.cookies.items() } - def raw_data(self): - # type: () -> bytes + def raw_data(self) -> bytes: return self.request.get_data() - def form(self): - # type: () -> ImmutableMultiDict[str, Any] + def form(self) -> "ImmutableMultiDict[str, Any]": return self.request.form - def files(self): - # type: () -> ImmutableMultiDict[str, Any] + def files(self) -> "ImmutableMultiDict[str, Any]": return self.request.files - def is_json(self): - # type: () -> bool + def is_json(self) -> bool: return self.request.is_json - def json(self): - # type: () -> Any + def json(self) -> "Any": return self.request.get_json(silent=True) - def size_of_file(self, file): - # type: (FileStorage) -> int + def size_of_file(self, file: "FileStorage") -> int: return file.content_length -def _make_request_event_processor(app, request, integration): - # type: (Flask, Callable[[], Request], FlaskIntegration) -> EventProcessor - - def inner(event, hint): - # type: (Event, dict[str, Any]) -> Event - +def _make_request_event_processor( + app: "Flask", request: "Callable[[], Request]", integration: "FlaskIntegration" +) -> "EventProcessor": + def inner(event: Event, hint: dict[str, Any]) -> Event: # if the request is gone we are fine not logging the data from # it. This might happen if the processor is pushed away to # another thread. @@ -221,8 +211,9 @@ def inner(event, hint): @ensure_integration_enabled(FlaskIntegration) -def _capture_exception(sender, exception, **kwargs): - # type: (Flask, Union[ValueError, BaseException], **Any) -> None +def _capture_exception( + sender: "Flask", exception: "Union[ValueError, BaseException]", **kwargs: "Any" +) -> None: event, hint = event_from_exception( exception, client_options=sentry_sdk.get_client().options, @@ -232,8 +223,7 @@ def _capture_exception(sender, exception, **kwargs): sentry_sdk.capture_event(event, hint=hint) -def _add_user_to_event(event): - # type: (Event) -> None +def _add_user_to_event(event: "Event") -> None: if flask_login is None: return diff --git a/sentry_sdk/integrations/gcp.py b/sentry_sdk/integrations/gcp.py index 2b0441f95d..084f3c7755 100644 --- a/sentry_sdk/integrations/gcp.py +++ b/sentry_sdk/integrations/gcp.py @@ -37,11 +37,11 @@ F = TypeVar("F", bound=Callable[..., Any]) -def _wrap_func(func): - # type: (F) -> F +def _wrap_func(func: "F") -> "F": @functools.wraps(func) - def sentry_func(functionhandler, gcp_event, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> Any + def sentry_func( + functionhandler: Any, gcp_event: Any, *args: Any, **kwargs: Any + ) -> Any: client = sentry_sdk.get_client() integration = client.get_integration(GcpIntegration) @@ -133,13 +133,11 @@ class GcpIntegration(Integration): identifier = "gcp" origin = f"auto.function.{identifier}" - def __init__(self, timeout_warning=False): - # type: (bool) -> None + def __init__(self, timeout_warning: bool = False) -> None: self.timeout_warning = timeout_warning @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: import __main__ as gcp_functions if not hasattr(gcp_functions, "worker_v1"): @@ -155,12 +153,10 @@ def setup_once(): ) -def _make_request_event_processor(gcp_event, configured_timeout, initial_time): - # type: (Any, Any, Any) -> EventProcessor - - def event_processor(event, hint): - # type: (Event, Hint) -> Optional[Event] - +def _make_request_event_processor( + gcp_event: "Any", configured_timeout: "Any", initial_time: "Any" +) -> "EventProcessor": + def event_processor(event: Event, hint: Hint) -> Optional[Event]: final_time = datetime.now(timezone.utc) time_diff = final_time - initial_time @@ -210,8 +206,7 @@ def event_processor(event, hint): return event_processor -def _get_google_cloud_logs_url(final_time): - # type: (datetime) -> str +def _get_google_cloud_logs_url(final_time: "datetime") -> str: """ Generates a Google Cloud Logs console URL based on the environment variables Arguments: diff --git a/sentry_sdk/integrations/gnu_backtrace.py b/sentry_sdk/integrations/gnu_backtrace.py index 8241e27f13..918baec9d3 100644 --- a/sentry_sdk/integrations/gnu_backtrace.py +++ b/sentry_sdk/integrations/gnu_backtrace.py @@ -30,17 +30,14 @@ class GnuBacktraceIntegration(Integration): identifier = "gnu_backtrace" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: @add_global_event_processor - def process_gnu_backtrace(event, hint): - # type: (Event, dict[str, Any]) -> Event + def process_gnu_backtrace(event: Event, hint: dict[str, Any]) -> Event: with capture_internal_exceptions(): return _process_gnu_backtrace(event, hint) -def _process_gnu_backtrace(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _process_gnu_backtrace(event: "Event", hint: "dict[str, Any]") -> "Event": if sentry_sdk.get_client().get_integration(GnuBacktraceIntegration) is None: return event diff --git a/sentry_sdk/integrations/google_genai/__init__.py b/sentry_sdk/integrations/google_genai/__init__.py index ee71895b9b..264e24f904 100644 --- a/sentry_sdk/integrations/google_genai/__init__.py +++ b/sentry_sdk/integrations/google_genai/__init__.py @@ -40,13 +40,11 @@ class GoogleGenAIIntegration(Integration): identifier = IDENTIFIER origin = ORIGIN - def __init__(self, include_prompts=True): - # type: (GoogleGenAIIntegration, bool) -> None + def __init__(self: "GoogleGenAIIntegration", include_prompts: bool = True) -> None: self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: # Patch sync methods Models.generate_content = _wrap_generate_content(Models.generate_content) Models.generate_content_stream = _wrap_generate_content_stream( @@ -64,11 +62,9 @@ def setup_once(): AsyncModels.embed_content = _wrap_async_embed_content(AsyncModels.embed_content) -def _wrap_generate_content_stream(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_generate_content_stream(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_generate_content_stream(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_generate_content_stream(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(GoogleGenAIIntegration) if integration is None: return f(self, *args, **kwargs) @@ -103,9 +99,8 @@ def new_generate_content_stream(self, *args, **kwargs): stream = f(self, *args, **kwargs) # Create wrapper iterator to accumulate responses - def new_iterator(): - # type: () -> Iterator[Any] - chunks = [] # type: List[Any] + def new_iterator() -> "Iterator[Any]": + chunks: List[Any] = [] try: for chunk in stream: chunks.append(chunk) @@ -138,11 +133,13 @@ def new_iterator(): return new_generate_content_stream -def _wrap_async_generate_content_stream(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_async_generate_content_stream( + f: "Callable[..., Any]", +) -> "Callable[..., Any]": @wraps(f) - async def new_async_generate_content_stream(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + async def new_async_generate_content_stream( + self: Any, *args: Any, **kwargs: Any + ) -> Any: integration = sentry_sdk.get_client().get_integration(GoogleGenAIIntegration) if integration is None: return await f(self, *args, **kwargs) @@ -177,9 +174,8 @@ async def new_async_generate_content_stream(self, *args, **kwargs): stream = await f(self, *args, **kwargs) # Create wrapper async iterator to accumulate responses - async def new_async_iterator(): - # type: () -> AsyncIterator[Any] - chunks = [] # type: List[Any] + async def new_async_iterator() -> "AsyncIterator[Any]": + chunks: List[Any] = [] try: async for chunk in stream: chunks.append(chunk) @@ -212,11 +208,9 @@ async def new_async_iterator(): return new_async_generate_content_stream -def _wrap_generate_content(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_generate_content(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_generate_content(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_generate_content(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(GoogleGenAIIntegration) if integration is None: return f(self, *args, **kwargs) @@ -260,11 +254,9 @@ def new_generate_content(self, *args, **kwargs): return new_generate_content -def _wrap_async_generate_content(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_async_generate_content(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - async def new_async_generate_content(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + async def new_async_generate_content(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(GoogleGenAIIntegration) if integration is None: return await f(self, *args, **kwargs) @@ -306,11 +298,9 @@ async def new_async_generate_content(self, *args, **kwargs): return new_async_generate_content -def _wrap_embed_content(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_embed_content(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_embed_content(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_embed_content(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(GoogleGenAIIntegration) if integration is None: return f(self, *args, **kwargs) @@ -341,11 +331,9 @@ def new_embed_content(self, *args, **kwargs): return new_embed_content -def _wrap_async_embed_content(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_async_embed_content(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - async def new_async_embed_content(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + async def new_async_embed_content(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(GoogleGenAIIntegration) if integration is None: return await f(self, *args, **kwargs) diff --git a/sentry_sdk/integrations/google_genai/streaming.py b/sentry_sdk/integrations/google_genai/streaming.py index 03d09aadf6..0582cac4eb 100644 --- a/sentry_sdk/integrations/google_genai/streaming.py +++ b/sentry_sdk/integrations/google_genai/streaming.py @@ -34,8 +34,9 @@ class AccumulatedResponse(TypedDict): usage_metadata: UsageData -def accumulate_streaming_response(chunks): - # type: (List[GenerateContentResponse]) -> AccumulatedResponse +def accumulate_streaming_response( + chunks: "List[GenerateContentResponse]", +) -> "AccumulatedResponse": """Accumulate streaming chunks into a single response-like object.""" accumulated_text = [] finish_reasons = [] @@ -93,8 +94,9 @@ def accumulate_streaming_response(chunks): return accumulated_response -def set_span_data_for_streaming_response(span, integration, accumulated_response): - # type: (Span, Any, AccumulatedResponse) -> None +def set_span_data_for_streaming_response( + span: "Span", integration: "Any", accumulated_response: "AccumulatedResponse" +) -> None: """Set span data for accumulated streaming response.""" if ( should_send_default_pii() diff --git a/sentry_sdk/integrations/google_genai/utils.py b/sentry_sdk/integrations/google_genai/utils.py index bda5822bb4..03423c385a 100644 --- a/sentry_sdk/integrations/google_genai/utils.py +++ b/sentry_sdk/integrations/google_genai/utils.py @@ -50,8 +50,9 @@ class UsageData(TypedDict): total_tokens: int -def extract_usage_data(response): - # type: (Union[GenerateContentResponse, dict[str, Any]]) -> UsageData +def extract_usage_data( + response: "Union[GenerateContentResponse, dict[str, Any]]", +) -> "UsageData": """Extract usage data from response into a structured format. Args: @@ -124,8 +125,7 @@ def extract_usage_data(response): return usage_data -def _capture_exception(exc): - # type: (Any) -> None +def _capture_exception(exc: "Any") -> None: """Capture exception with Google GenAI mechanism.""" event, hint = event_from_exception( exc, @@ -135,8 +135,7 @@ def _capture_exception(exc): sentry_sdk.capture_event(event, hint=hint) -def get_model_name(model): - # type: (Union[str, Model]) -> str +def get_model_name(model: "Union[str, Model]") -> str: """Extract model name from model parameter.""" if isinstance(model, str): return model @@ -146,8 +145,7 @@ def get_model_name(model): return str(model) -def extract_contents_text(contents): - # type: (ContentListUnion) -> Optional[str] +def extract_contents_text(contents: "ContentListUnion") -> "Optional[str]": """Extract text from contents parameter which can have various formats.""" if contents is None: return None @@ -185,8 +183,9 @@ def extract_contents_text(contents): return None -def _format_tools_for_span(tools): - # type: (Iterable[Tool | Callable[..., Any]]) -> Optional[List[dict[str, Any]]] +def _format_tools_for_span( + tools: "Iterable[Tool | Callable[..., Any]]", +) -> "Optional[List[dict[str, Any]]]": """Format tools parameter for span data.""" formatted_tools = [] for tool in tools: @@ -226,8 +225,9 @@ def _format_tools_for_span(tools): return formatted_tools if formatted_tools else None -def extract_tool_calls(response): - # type: (GenerateContentResponse) -> Optional[List[dict[str, Any]]] +def extract_tool_calls( + response: "GenerateContentResponse", +) -> "Optional[List[dict[str, Any]]]": """Extract tool/function calls from response candidates and automatic function calling history.""" tool_calls = [] @@ -278,8 +278,9 @@ def extract_tool_calls(response): return tool_calls if tool_calls else None -def _capture_tool_input(args, kwargs, tool): - # type: (tuple[Any, ...], dict[str, Any], Tool) -> dict[str, Any] +def _capture_tool_input( + args: "tuple[Any, ...]", kwargs: "dict[str, Any]", tool: "Tool" +) -> "dict[str, Any]": """Capture tool input from args and kwargs.""" tool_input = kwargs.copy() if kwargs else {} @@ -298,8 +299,7 @@ def _capture_tool_input(args, kwargs, tool): return tool_input -def _create_tool_span(tool_name, tool_doc): - # type: (str, Optional[str]) -> Span +def _create_tool_span(tool_name: str, tool_doc: "Optional[str]") -> "Span": """Create a span for tool execution.""" span = sentry_sdk.start_span( op=OP.GEN_AI_EXECUTE_TOOL, @@ -313,8 +313,7 @@ def _create_tool_span(tool_name, tool_doc): return span -def wrapped_tool(tool): - # type: (Tool | Callable[..., Any]) -> Tool | Callable[..., Any] +def wrapped_tool(tool: "Tool | Callable[..., Any]") -> "Tool | Callable[..., Any]": """Wrap a tool to emit execute_tool spans when called.""" if not callable(tool): # Not a callable function, return as-is (predefined tools) @@ -326,8 +325,7 @@ def wrapped_tool(tool): if inspect.iscoroutinefunction(tool): # Async function @wraps(tool) - async def async_wrapped(*args, **kwargs): - # type: (Any, Any) -> Any + async def async_wrapped(*args: "Any", **kwargs: "Any") -> "Any": with _create_tool_span(tool_name, tool_doc) as span: # Capture tool input tool_input = _capture_tool_input(args, kwargs, tool) @@ -354,8 +352,7 @@ async def async_wrapped(*args, **kwargs): else: # Sync function @wraps(tool) - def sync_wrapped(*args, **kwargs): - # type: (Any, Any) -> Any + def sync_wrapped(*args: "Any", **kwargs: "Any") -> "Any": with _create_tool_span(tool_name, tool_doc) as span: # Capture tool input tool_input = _capture_tool_input(args, kwargs, tool) @@ -381,8 +378,9 @@ def sync_wrapped(*args, **kwargs): return sync_wrapped -def wrapped_config_with_tools(config): - # type: (GenerateContentConfig) -> GenerateContentConfig +def wrapped_config_with_tools( + config: "GenerateContentConfig", +) -> "GenerateContentConfig": """Wrap tools in config to emit execute_tool spans. Tools are sometimes passed directly as callable functions as a part of the config object.""" @@ -395,8 +393,9 @@ def wrapped_config_with_tools(config): return result -def _extract_response_text(response): - # type: (GenerateContentResponse) -> Optional[List[str]] +def _extract_response_text( + response: "GenerateContentResponse", +) -> "Optional[List[str]]": """Extract text from response candidates.""" if not response or not getattr(response, "candidates", []): @@ -414,8 +413,9 @@ def _extract_response_text(response): return texts if texts else None -def extract_finish_reasons(response): - # type: (GenerateContentResponse) -> Optional[List[str]] +def extract_finish_reasons( + response: "GenerateContentResponse", +) -> "Optional[List[str]]": """Extract finish reasons from response candidates.""" if not response or not getattr(response, "candidates", []): return None @@ -433,8 +433,13 @@ def extract_finish_reasons(response): return finish_reasons if finish_reasons else None -def set_span_data_for_request(span, integration, model, contents, kwargs): - # type: (Span, Any, str, ContentListUnion, dict[str, Any]) -> None +def set_span_data_for_request( + span: "Span", + integration: "Any", + model: str, + contents: "ContentListUnion", + kwargs: "dict[str, Any]", +) -> None: """Set span data for the request.""" span.set_data(SPANDATA.GEN_AI_SYSTEM, GEN_AI_SYSTEM) span.set_data(SPANDATA.GEN_AI_REQUEST_MODEL, model) @@ -442,7 +447,7 @@ def set_span_data_for_request(span, integration, model, contents, kwargs): if kwargs.get("stream", False): span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, True) - config = kwargs.get("config") # type: Optional[GenerateContentConfig] + config: "Optional[GenerateContentConfig]" = kwargs.get("config") # Set input messages/prompts if PII is allowed if should_send_default_pii() and integration.include_prompts: @@ -504,8 +509,9 @@ def set_span_data_for_request(span, integration, model, contents, kwargs): ) -def set_span_data_for_response(span, integration, response): - # type: (Span, Any, GenerateContentResponse) -> None +def set_span_data_for_response( + span: "Span", integration: "Any", response: "GenerateContentResponse" +) -> None: """Set span data for the response.""" if not response: return @@ -557,8 +563,9 @@ def set_span_data_for_response(span, integration, response): span.set_data(SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS, usage_data["total_tokens"]) -def prepare_generate_content_args(args, kwargs): - # type: (tuple[Any, ...], dict[str, Any]) -> tuple[Any, Any, str] +def prepare_generate_content_args( + args: "tuple[Any, ...]", kwargs: "dict[str, Any]" +) -> "tuple[Any, Any, str]": """Extract and prepare common arguments for generate_content methods.""" model = args[0] if args else kwargs.get("model", "unknown") contents = args[1] if len(args) > 1 else kwargs.get("contents") @@ -572,8 +579,9 @@ def prepare_generate_content_args(args, kwargs): return model, contents, model_name -def prepare_embed_content_args(args, kwargs): - # type: (tuple[Any, ...], dict[str, Any]) -> tuple[str, Any] +def prepare_embed_content_args( + args: "tuple[Any, ...]", kwargs: "dict[str, Any]" +) -> "tuple[str, Any]": """Extract and prepare common arguments for embed_content methods. Returns: @@ -586,8 +594,9 @@ def prepare_embed_content_args(args, kwargs): return model_name, contents -def set_span_data_for_embed_request(span, integration, contents, kwargs): - # type: (Span, Any, Any, dict[str, Any]) -> None +def set_span_data_for_embed_request( + span: "Span", integration: "Any", contents: "Any", kwargs: "dict[str, Any]" +) -> None: """Set span data for embedding request.""" # Include input contents if PII is allowed if should_send_default_pii() and integration.include_prompts: @@ -617,8 +626,9 @@ def set_span_data_for_embed_request(span, integration, contents, kwargs): ) -def set_span_data_for_embed_response(span, integration, response): - # type: (Span, Any, EmbedContentResponse) -> None +def set_span_data_for_embed_response( + span: "Span", integration: "Any", response: "EmbedContentResponse" +) -> None: """Set span data for embedding response.""" if not response: return diff --git a/sentry_sdk/integrations/gql.py b/sentry_sdk/integrations/gql.py index 8c378060b7..64643bb095 100644 --- a/sentry_sdk/integrations/gql.py +++ b/sentry_sdk/integrations/gql.py @@ -41,19 +41,17 @@ class GQLIntegration(Integration): identifier = "gql" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: gql_version = parse_version(gql.__version__) _check_minimum_version(GQLIntegration, gql_version) _patch_execute() -def _data_from_document(document): - # type: (DocumentNode) -> EventDataType +def _data_from_document(document: "DocumentNode") -> "EventDataType": try: operation_ast = get_operation_ast(document) - data = {"query": print_ast(document)} # type: EventDataType + data: "EventDataType" = {"query": print_ast(document)} if operation_ast is not None: data["variables"] = operation_ast.variable_definitions @@ -65,8 +63,7 @@ def _data_from_document(document): return dict() -def _transport_method(transport): - # type: (Union[Transport, AsyncTransport]) -> str +def _transport_method(transport: "Union[Transport, AsyncTransport]") -> str: """ The RequestsHTTPTransport allows defining the HTTP method; all other transports use POST. @@ -77,8 +74,9 @@ def _transport_method(transport): return "POST" -def _request_info_from_transport(transport): - # type: (Union[Transport, AsyncTransport, None]) -> Dict[str, str] +def _request_info_from_transport( + transport: "Union[Transport, AsyncTransport, None]", +) -> "Dict[str, str]": if transport is None: return {} @@ -94,13 +92,16 @@ def _request_info_from_transport(transport): return request_info -def _patch_execute(): - # type: () -> None +def _patch_execute() -> None: real_execute = gql.Client.execute @ensure_integration_enabled(GQLIntegration, real_execute) - def sentry_patched_execute(self, document_or_request, *args, **kwargs): - # type: (gql.Client, DocumentNode, Any, Any) -> Any + def sentry_patched_execute( + self: "gql.Client", + document_or_request: "DocumentNode", + *args: "Any", + **kwargs: "Any", + ) -> "Any": scope = sentry_sdk.get_isolation_scope() scope.add_event_processor(_make_gql_event_processor(self, document_or_request)) @@ -119,10 +120,10 @@ def sentry_patched_execute(self, document_or_request, *args, **kwargs): gql.Client.execute = sentry_patched_execute -def _make_gql_event_processor(client, document_or_request): - # type: (gql.Client, Union[DocumentNode, gql.GraphQLRequest]) -> EventProcessor - def processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_gql_event_processor( + client: "gql.Client", document_or_request: "Union[DocumentNode, gql.GraphQLRequest]" +) -> "EventProcessor": + def processor(event: Event, hint: dict[str, Any]) -> Event: try: errors = hint["exc_info"][1].errors except (AttributeError, KeyError): diff --git a/sentry_sdk/integrations/graphene.py b/sentry_sdk/integrations/graphene.py index 00a8d155d4..5a61ca5c78 100644 --- a/sentry_sdk/integrations/graphene.py +++ b/sentry_sdk/integrations/graphene.py @@ -31,22 +31,24 @@ class GrapheneIntegration(Integration): identifier = "graphene" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = package_version("graphene") _check_minimum_version(GrapheneIntegration, version) _patch_graphql() -def _patch_graphql(): - # type: () -> None +def _patch_graphql() -> None: old_graphql_sync = graphene_schema.graphql_sync old_graphql_async = graphene_schema.graphql @ensure_integration_enabled(GrapheneIntegration, old_graphql_sync) - def _sentry_patched_graphql_sync(schema, source, *args, **kwargs): - # type: (GraphQLSchema, Union[str, Source], Any, Any) -> ExecutionResult + def _sentry_patched_graphql_sync( + schema: "GraphQLSchema", + source: "Union[str, Source]", + *args: "Any", + **kwargs: "Any", + ) -> "ExecutionResult": scope = sentry_sdk.get_isolation_scope() scope.add_event_processor(_event_processor) @@ -68,8 +70,12 @@ def _sentry_patched_graphql_sync(schema, source, *args, **kwargs): return result - async def _sentry_patched_graphql_async(schema, source, *args, **kwargs): - # type: (GraphQLSchema, Union[str, Source], Any, Any) -> ExecutionResult + async def _sentry_patched_graphql_async( + schema: "GraphQLSchema", + source: "Union[str, Source]", + *args: "Any", + **kwargs: "Any", + ) -> "ExecutionResult": integration = sentry_sdk.get_client().get_integration(GrapheneIntegration) if integration is None: return await old_graphql_async(schema, source, *args, **kwargs) @@ -99,8 +105,7 @@ async def _sentry_patched_graphql_async(schema, source, *args, **kwargs): graphene_schema.graphql = _sentry_patched_graphql_async -def _event_processor(event, hint): - # type: (Event, Dict[str, Any]) -> Event +def _event_processor(event: "Event", hint: "Dict[str, Any]") -> "Event": if should_send_default_pii(): request_info = event.setdefault("request", {}) request_info["api_target"] = "graphql" @@ -112,8 +117,9 @@ def _event_processor(event, hint): @contextmanager -def graphql_span(schema, source, kwargs): - # type: (GraphQLSchema, Union[str, Source], Dict[str, Any]) -> Generator[None, None, None] +def graphql_span( + schema: "GraphQLSchema", source: "Union[str, Source]", kwargs: "Dict[str, Any]" +) -> "Generator[None, None, None]": operation_name = kwargs.get("operation_name") operation_type = "query" diff --git a/sentry_sdk/integrations/grpc/__init__.py b/sentry_sdk/integrations/grpc/__init__.py index 4e15f95ae5..3faa480b45 100644 --- a/sentry_sdk/integrations/grpc/__init__.py +++ b/sentry_sdk/integrations/grpc/__init__.py @@ -130,10 +130,10 @@ def patched_aio_server( # type: ignore **kwargs: P.kwargs, ) -> Server: server_interceptor = AsyncServerInterceptor() - interceptors = [ + interceptors: "Sequence[grpc.ServerInterceptor]" = [ server_interceptor, *(interceptors or []), - ] # type: Sequence[grpc.ServerInterceptor] + ] try: # We prefer interceptors as a list because of compatibility with diff --git a/sentry_sdk/integrations/grpc/aio/server.py b/sentry_sdk/integrations/grpc/aio/server.py index 7a9eca7671..3ed15c2de6 100644 --- a/sentry_sdk/integrations/grpc/aio/server.py +++ b/sentry_sdk/integrations/grpc/aio/server.py @@ -21,14 +21,19 @@ class ServerInterceptor(grpc.aio.ServerInterceptor): # type: ignore - def __init__(self, find_name=None): - # type: (ServerInterceptor, Callable[[ServicerContext], str] | None) -> None + def __init__( + self: "ServerInterceptor", + find_name: "Callable[[ServicerContext], str] | None" = None, + ) -> None: self._find_method_name = find_name or self._find_name super().__init__() - async def intercept_service(self, continuation, handler_call_details): - # type: (ServerInterceptor, Callable[[HandlerCallDetails], Awaitable[RpcMethodHandler]], HandlerCallDetails) -> Optional[Awaitable[RpcMethodHandler]] + async def intercept_service( + self: "ServerInterceptor", + continuation: "Callable[[HandlerCallDetails], Awaitable[RpcMethodHandler]]", + handler_call_details: "HandlerCallDetails", + ) -> "Optional[Awaitable[RpcMethodHandler]]": self._handler_call_details = handler_call_details handler = await continuation(handler_call_details) if handler is None: @@ -37,8 +42,7 @@ async def intercept_service(self, continuation, handler_call_details): if not handler.request_streaming and not handler.response_streaming: handler_factory = grpc.unary_unary_rpc_method_handler - async def wrapped(request, context): - # type: (Any, ServicerContext) -> Any + async def wrapped(request: "Any", context: "ServicerContext") -> "Any": name = self._find_method_name(context) if not name: return await handler(request, context) @@ -68,24 +72,21 @@ async def wrapped(request, context): elif not handler.request_streaming and handler.response_streaming: handler_factory = grpc.unary_stream_rpc_method_handler - async def wrapped(request, context): # type: ignore - # type: (Any, ServicerContext) -> Any + async def wrapped(request: "Any", context: "ServicerContext") -> "Any": # type: ignore async for r in handler.unary_stream(request, context): yield r elif handler.request_streaming and not handler.response_streaming: handler_factory = grpc.stream_unary_rpc_method_handler - async def wrapped(request, context): - # type: (Any, ServicerContext) -> Any + async def wrapped(request: "Any", context: "ServicerContext") -> "Any": response = handler.stream_unary(request, context) return await response elif handler.request_streaming and handler.response_streaming: handler_factory = grpc.stream_stream_rpc_method_handler - async def wrapped(request, context): # type: ignore - # type: (Any, ServicerContext) -> Any + async def wrapped(request: "Any", context: "ServicerContext") -> "Any": # type: ignore async for r in handler.stream_stream(request, context): yield r @@ -95,6 +96,5 @@ async def wrapped(request, context): # type: ignore response_serializer=handler.response_serializer, ) - def _find_name(self, context): - # type: (ServicerContext) -> str + def _find_name(self, context: "ServicerContext") -> str: return self._handler_call_details.method diff --git a/sentry_sdk/integrations/grpc/client.py b/sentry_sdk/integrations/grpc/client.py index ef24821ed2..69b3f3d318 100644 --- a/sentry_sdk/integrations/grpc/client.py +++ b/sentry_sdk/integrations/grpc/client.py @@ -24,8 +24,12 @@ class ClientInterceptor( ): _is_intercepted = False - def intercept_unary_unary(self, continuation, client_call_details, request): - # type: (ClientInterceptor, Callable[[ClientCallDetails, Message], _UnaryOutcome], ClientCallDetails, Message) -> _UnaryOutcome + def intercept_unary_unary( + self: "ClientInterceptor", + continuation: "Callable[[ClientCallDetails, Message], _UnaryOutcome]", + client_call_details: "ClientCallDetails", + request: "Message", + ) -> "_UnaryOutcome": method = client_call_details.method with sentry_sdk.start_span( @@ -45,8 +49,12 @@ def intercept_unary_unary(self, continuation, client_call_details, request): return response - def intercept_unary_stream(self, continuation, client_call_details, request): - # type: (ClientInterceptor, Callable[[ClientCallDetails, Message], Union[Iterable[Any], UnaryStreamCall]], ClientCallDetails, Message) -> Union[Iterator[Message], Call] + def intercept_unary_stream( + self: "ClientInterceptor", + continuation: "Callable[[ClientCallDetails, Message], Union[Iterable[Any], UnaryStreamCall]]", + client_call_details: "ClientCallDetails", + request: "Message", + ) -> "Union[Iterator[Message], Call]": method = client_call_details.method with sentry_sdk.start_span( @@ -61,15 +69,16 @@ def intercept_unary_stream(self, continuation, client_call_details, request): client_call_details ) - response = continuation(client_call_details, request) # type: UnaryStreamCall + response: "UnaryStreamCall" = continuation(client_call_details, request) # Setting code on unary-stream leads to execution getting stuck # span.set_data("code", response.code().name) return response @staticmethod - def _update_client_call_details_metadata_from_scope(client_call_details): - # type: (ClientCallDetails) -> ClientCallDetails + def _update_client_call_details_metadata_from_scope( + client_call_details: "ClientCallDetails", + ) -> "ClientCallDetails": metadata = ( list(client_call_details.metadata) if client_call_details.metadata else [] ) diff --git a/sentry_sdk/integrations/grpc/server.py b/sentry_sdk/integrations/grpc/server.py index cce7e99d27..9edf9ea29e 100644 --- a/sentry_sdk/integrations/grpc/server.py +++ b/sentry_sdk/integrations/grpc/server.py @@ -18,20 +18,24 @@ class ServerInterceptor(grpc.ServerInterceptor): # type: ignore - def __init__(self, find_name=None): - # type: (ServerInterceptor, Optional[Callable[[ServicerContext], str]]) -> None + def __init__( + self: "ServerInterceptor", + find_name: "Optional[Callable[[ServicerContext], str]]" = None, + ) -> None: self._find_method_name = find_name or ServerInterceptor._find_name super().__init__() - def intercept_service(self, continuation, handler_call_details): - # type: (ServerInterceptor, Callable[[HandlerCallDetails], RpcMethodHandler], HandlerCallDetails) -> RpcMethodHandler + def intercept_service( + self: "ServerInterceptor", + continuation: "Callable[[HandlerCallDetails], RpcMethodHandler]", + handler_call_details: "HandlerCallDetails", + ) -> "RpcMethodHandler": handler = continuation(handler_call_details) if not handler or not handler.unary_unary: return handler - def behavior(request, context): - # type: (Message, ServicerContext) -> Message + def behavior(request: "Message", context: "ServicerContext") -> "Message": with sentry_sdk.isolation_scope(): name = self._find_method_name(context) @@ -61,6 +65,5 @@ def behavior(request, context): ) @staticmethod - def _find_name(context): - # type: (ServicerContext) -> str + def _find_name(context: "ServicerContext") -> str: return context._rpc_event.call_details.method.decode() diff --git a/sentry_sdk/integrations/httpx.py b/sentry_sdk/integrations/httpx.py index 2ada95aad0..38e9c1a3d7 100644 --- a/sentry_sdk/integrations/httpx.py +++ b/sentry_sdk/integrations/httpx.py @@ -36,8 +36,7 @@ class HttpxIntegration(Integration): origin = f"auto.http.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: """ httpx has its own transport layer and can be customized when needed, so patch Client.send and AsyncClient.send to support both synchronous and async interfaces. @@ -46,13 +45,11 @@ def setup_once(): _install_httpx_async_client() -def _install_httpx_client(): - # type: () -> None +def _install_httpx_client() -> None: real_send = Client.send @ensure_integration_enabled(HttpxIntegration, real_send) - def send(self, request, **kwargs): - # type: (Client, Request, **Any) -> Response + def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response": parsed_url = None with capture_internal_exceptions(): parsed_url = parse_url(str(request.url), sanitize=False) @@ -101,12 +98,12 @@ def send(self, request, **kwargs): Client.send = send -def _install_httpx_async_client(): - # type: () -> None +def _install_httpx_async_client() -> None: real_send = AsyncClient.send - async def send(self, request, **kwargs): - # type: (AsyncClient, Request, **Any) -> Response + async def send( + self: "AsyncClient", request: "Request", **kwargs: "Any" + ) -> "Response": if sentry_sdk.get_client().get_integration(HttpxIntegration) is None: return await real_send(self, request, **kwargs) @@ -160,8 +157,9 @@ async def send(self, request, **kwargs): AsyncClient.send = send -def _add_sentry_baggage_to_headers(headers, sentry_baggage): - # type: (MutableMapping[str, str], str) -> None +def _add_sentry_baggage_to_headers( + headers: "MutableMapping[str, str]", sentry_baggage: str +) -> None: """Add the Sentry baggage to the headers. This function directly mutates the provided headers. The provided sentry_baggage diff --git a/sentry_sdk/integrations/huey.py b/sentry_sdk/integrations/huey.py index f0aff4c0dd..7a9eb518e2 100644 --- a/sentry_sdk/integrations/huey.py +++ b/sentry_sdk/integrations/huey.py @@ -44,19 +44,18 @@ class HueyIntegration(Integration): origin = f"auto.queue.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: patch_enqueue() patch_execute() -def patch_enqueue(): - # type: () -> None +def patch_enqueue() -> None: old_enqueue = Huey.enqueue @ensure_integration_enabled(HueyIntegration, old_enqueue) - def _sentry_enqueue(self, task): - # type: (Huey, Task) -> Optional[Union[Result, ResultGroup]] + def _sentry_enqueue( + self: "Huey", task: "Task" + ) -> "Optional[Union[Result, ResultGroup]]": with sentry_sdk.start_span( op=OP.QUEUE_SUBMIT_HUEY, name=task.name, @@ -75,11 +74,8 @@ def _sentry_enqueue(self, task): Huey.enqueue = _sentry_enqueue -def _make_event_processor(task): - # type: (Any) -> EventProcessor - def event_processor(event, hint): - # type: (Event, Hint) -> Optional[Event] - +def _make_event_processor(task: "Any") -> "EventProcessor": + def event_processor(event: Event, hint: Hint) -> Optional[Event]: with capture_internal_exceptions(): tags = event.setdefault("tags", {}) tags["huey_task_id"] = task.id @@ -105,8 +101,7 @@ def event_processor(event, hint): return event_processor -def _capture_exception(exc_info): - # type: (ExcInfo) -> None +def _capture_exception(exc_info: "ExcInfo") -> None: scope = sentry_sdk.get_current_scope() if exc_info[0] in HUEY_CONTROL_FLOW_EXCEPTIONS: @@ -122,12 +117,9 @@ def _capture_exception(exc_info): scope.capture_event(event, hint=hint) -def _wrap_task_execute(func): - # type: (F) -> F - +def _wrap_task_execute(func: "F") -> "F": @ensure_integration_enabled(HueyIntegration, func) - def _sentry_execute(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_execute(*args: Any, **kwargs: Any) -> Any: try: result = func(*args, **kwargs) except Exception: @@ -140,13 +132,13 @@ def _sentry_execute(*args, **kwargs): return _sentry_execute # type: ignore -def patch_execute(): - # type: () -> None +def patch_execute() -> None: old_execute = Huey._execute @ensure_integration_enabled(HueyIntegration, old_execute) - def _sentry_execute(self, task, timestamp=None): - # type: (Huey, Task, Optional[datetime]) -> Any + def _sentry_execute( + self: "Huey", task: "Task", timestamp: "Optional[datetime]" = None + ) -> "Any": with sentry_sdk.isolation_scope() as scope: with capture_internal_exceptions(): scope._name = "huey" diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index 2e2b382abd..0de771866b 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -28,14 +28,13 @@ class HuggingfaceHubIntegration(Integration): identifier = "huggingface_hub" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True): - # type: (HuggingfaceHubIntegration, bool) -> None + def __init__( + self: "HuggingfaceHubIntegration", include_prompts: bool = True + ) -> None: self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: # Other tasks that can be called: https://huggingface.co/docs/huggingface_hub/guides/inference#supported-providers-and-tasks huggingface_hub.inference._client.InferenceClient.text_generation = ( _wrap_huggingface_task( @@ -51,8 +50,7 @@ def setup_once(): ) -def _capture_exception(exc): - # type: (Any) -> None +def _capture_exception(exc: "Any") -> None: set_span_errored() event, hint = event_from_exception( @@ -63,11 +61,9 @@ def _capture_exception(exc): sentry_sdk.capture_event(event, hint=hint) -def _wrap_huggingface_task(f, op): - # type: (Callable[..., Any], str) -> Callable[..., Any] +def _wrap_huggingface_task(f: "Callable[..., Any]", op: str) -> "Callable[..., Any]": @wraps(f) - def new_huggingface_task(*args, **kwargs): - # type: (*Any, **Any) -> Any + def new_huggingface_task(*args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(HuggingfaceHubIntegration) if integration is None: return f(*args, **kwargs) @@ -229,8 +225,7 @@ def new_huggingface_task(*args, **kwargs): if kwargs.get("details", False): # text-generation stream output - def new_details_iterator(): - # type: () -> Iterable[Any] + def new_details_iterator() -> "Iterable[Any]": finish_reason = None response_text_buffer: list[str] = [] tokens_used = 0 @@ -287,8 +282,7 @@ def new_details_iterator(): else: # chat-completion stream output - def new_iterator(): - # type: () -> Iterable[str] + def new_iterator() -> "Iterable[str]": finish_reason = None response_model = None response_text_buffer: list[str] = [] diff --git a/sentry_sdk/integrations/langchain.py b/sentry_sdk/integrations/langchain.py index dca470b749..8400718e1a 100644 --- a/sentry_sdk/integrations/langchain.py +++ b/sentry_sdk/integrations/langchain.py @@ -1,26 +1,25 @@ import contextvars import itertools +import sys import warnings from collections import OrderedDict from functools import wraps -import sys +from typing import TYPE_CHECKING import sentry_sdk from sentry_sdk.ai.monitoring import set_ai_pipeline_name from sentry_sdk.ai.utils import ( GEN_AI_ALLOWED_MESSAGE_ROLES, + get_start_span_function, normalize_message_roles, set_data_normalized, - get_start_span_function, truncate_and_annotate_messages, ) from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration from sentry_sdk.scope import should_send_default_pii from sentry_sdk.tracing_utils import _get_value, set_span_errored -from sentry_sdk.utils import logger, capture_internal_exceptions - -from typing import TYPE_CHECKING +from sentry_sdk.utils import capture_internal_exceptions, logger if TYPE_CHECKING: from typing import ( @@ -34,6 +33,7 @@ Union, ) from uuid import UUID + from sentry_sdk.tracing import Span @@ -118,11 +118,12 @@ # Contextvar to track agent names in a stack for re-entrant agent support -_agent_stack = contextvars.ContextVar("langchain_agent_stack", default=None) # type: contextvars.ContextVar[Optional[List[Optional[str]]]] +_agent_stack: "contextvars.ContextVar[Optional[List[Optional[str]]]]" = ( + contextvars.ContextVar("langchain_agent_stack", default=None) +) -def _push_agent(agent_name): - # type: (Optional[str]) -> None +def _push_agent(agent_name: "Optional[str]") -> None: """Push an agent name onto the stack.""" stack = _agent_stack.get() if stack is None: @@ -134,8 +135,7 @@ def _push_agent(agent_name): _agent_stack.set(stack) -def _pop_agent(): - # type: () -> Optional[str] +def _pop_agent() -> "Optional[str]": """Pop an agent name from the stack and return it.""" stack = _agent_stack.get() if stack: @@ -147,8 +147,7 @@ def _pop_agent(): return None -def _get_current_agent(): - # type: () -> Optional[str] +def _get_current_agent() -> "Optional[str]": """Get the current agent name (top of stack) without removing it.""" stack = _agent_stack.get() if stack: @@ -160,8 +159,11 @@ class LangchainIntegration(Integration): identifier = "langchain" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True, max_spans=None): - # type: (LangchainIntegration, bool, Optional[int]) -> None + def __init__( + self: "LangchainIntegration", + include_prompts: bool = True, + max_spans: "Optional[int]" = None, + ) -> None: self.include_prompts = include_prompts self.max_spans = max_spans @@ -174,8 +176,7 @@ def __init__(self, include_prompts=True, max_spans=None): ) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: manager._configure = _wrap_configure(manager._configure) if AgentExecutor is not None: @@ -194,34 +195,31 @@ def setup_once(): class WatchedSpan: - span = None # type: Span - children = [] # type: List[WatchedSpan] - is_pipeline = False # type: bool + span: "Span" = None # type: ignore[assignment] + children: "List[WatchedSpan]" = [] + is_pipeline: bool = False - def __init__(self, span): - # type: (Span) -> None + def __init__(self, span: "Span") -> None: self.span = span class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc] """Callback handler that creates Sentry spans.""" - def __init__(self, max_span_map_size, include_prompts): - # type: (Optional[int], bool) -> None - self.span_map = OrderedDict() # type: OrderedDict[UUID, WatchedSpan] + def __init__( + self, max_span_map_size: "Optional[int]", include_prompts: bool + ) -> None: + self.span_map: OrderedDict[UUID, WatchedSpan] = OrderedDict() self.max_span_map_size = max_span_map_size self.include_prompts = include_prompts - def gc_span_map(self): - # type: () -> None - + 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, watched_span = self.span_map.popitem(last=False) self._exit_span(watched_span, run_id) - def _handle_error(self, run_id, error): - # type: (UUID, Any) -> None + def _handle_error(self, run_id: "UUID", error: "Any") -> None: with capture_internal_exceptions(): if not run_id or run_id not in self.span_map: return @@ -235,17 +233,20 @@ def _handle_error(self, run_id, error): span.__exit__(None, None, None) del self.span_map[run_id] - def _normalize_langchain_message(self, message): - # type: (BaseMessage) -> Any + def _normalize_langchain_message(self, message: "BaseMessage") -> "Any": parsed = {"role": message.type, "content": message.content} parsed.update(message.additional_kwargs) return parsed - def _create_span(self, run_id, parent_id, **kwargs): - # type: (SentryLangchainCallback, UUID, Optional[Any], Any) -> WatchedSpan - watched_span = None # type: Optional[WatchedSpan] + def _create_span( + self: "SentryLangchainCallback", + run_id: "UUID", + parent_id: "Optional[Any]", + **kwargs: "Any", + ) -> "WatchedSpan": + watched_span: "Optional[WatchedSpan]" = None if parent_id: - parent_span = self.span_map.get(parent_id) # type: Optional[WatchedSpan] + parent_span: "Optional[WatchedSpan]" = self.span_map.get(parent_id) if parent_span: watched_span = WatchedSpan(parent_span.span.start_child(**kwargs)) parent_span.children.append(watched_span) @@ -258,8 +259,9 @@ def _create_span(self, run_id, parent_id, **kwargs): self.gc_span_map() return watched_span - def _exit_span(self, span_data, run_id): - # type: (SentryLangchainCallback, WatchedSpan, UUID) -> None + def _exit_span( + self: "SentryLangchainCallback", span_data: "WatchedSpan", run_id: "UUID" + ) -> None: if span_data.is_pipeline: set_ai_pipeline_name(None) @@ -267,17 +269,16 @@ def _exit_span(self, span_data, run_id): del self.span_map[run_id] def on_llm_start( - self, - serialized, - prompts, + self: "SentryLangchainCallback", + serialized: "Dict[str, Any]", + prompts: "List[str]", *, - run_id, - tags=None, - parent_run_id=None, - metadata=None, - **kwargs, - ): - # type: (SentryLangchainCallback, Dict[str, Any], List[str], UUID, Optional[List[str]], Optional[UUID], Optional[Dict[str, Any]], Any) -> Any + run_id: "UUID", + tags: "Optional[List[str]]" = None, + parent_run_id: "Optional[UUID]" = None, + metadata: "Optional[Dict[str, Any]]" = None, + **kwargs: "Any", + ) -> "Any": """Run when LLM starts running.""" with capture_internal_exceptions(): if not run_id: @@ -340,8 +341,14 @@ def on_llm_start( unpack=False, ) - def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs): - # type: (SentryLangchainCallback, Dict[str, Any], List[List[BaseMessage]], UUID, Any) -> Any + def on_chat_model_start( + self: "SentryLangchainCallback", + serialized: "Dict[str, Any]", + messages: "List[List[BaseMessage]]", + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when Chat Model starts running.""" with capture_internal_exceptions(): if not run_id: @@ -406,8 +413,13 @@ def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs): unpack=False, ) - def on_chat_model_end(self, response, *, run_id, **kwargs): - # type: (SentryLangchainCallback, LLMResult, UUID, Any) -> Any + def on_chat_model_end( + self: "SentryLangchainCallback", + response: "LLMResult", + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when Chat Model ends running.""" with capture_internal_exceptions(): if not run_id or run_id not in self.span_map: @@ -426,8 +438,13 @@ def on_chat_model_end(self, response, *, run_id, **kwargs): _record_token_usage(span, response) self._exit_span(span_data, run_id) - def on_llm_end(self, response, *, run_id, **kwargs): - # type: (SentryLangchainCallback, LLMResult, UUID, Any) -> Any + def on_llm_end( + self: "SentryLangchainCallback", + response: "LLMResult", + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when LLM ends running.""" with capture_internal_exceptions(): if not run_id or run_id not in self.span_map: @@ -483,18 +500,33 @@ def on_llm_end(self, response, *, run_id, **kwargs): _record_token_usage(span, response) self._exit_span(span_data, run_id) - def on_llm_error(self, error, *, run_id, **kwargs): - # type: (SentryLangchainCallback, Union[Exception, KeyboardInterrupt], UUID, Any) -> Any + def on_llm_error( + self: "SentryLangchainCallback", + error: "Union[Exception, KeyboardInterrupt]", + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when LLM errors.""" self._handle_error(run_id, error) - def on_chat_model_error(self, error, *, run_id, **kwargs): - # type: (SentryLangchainCallback, Union[Exception, KeyboardInterrupt], UUID, Any) -> Any + def on_chat_model_error( + self: "SentryLangchainCallback", + error: "Union[Exception, KeyboardInterrupt]", + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when Chat Model errors.""" self._handle_error(run_id, error) - def on_agent_finish(self, finish, *, run_id, **kwargs): - # type: (SentryLangchainCallback, AgentFinish, UUID, Any) -> Any + def on_agent_finish( + self: "SentryLangchainCallback", + finish: "AgentFinish", + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": with capture_internal_exceptions(): if not run_id or run_id not in self.span_map: return @@ -509,8 +541,14 @@ def on_agent_finish(self, finish, *, run_id, **kwargs): self._exit_span(span_data, run_id) - def on_tool_start(self, serialized, input_str, *, run_id, **kwargs): - # type: (SentryLangchainCallback, Dict[str, Any], str, UUID, Any) -> Any + def on_tool_start( + self: "SentryLangchainCallback", + serialized: "Dict[str, Any]", + input_str: str, + *, + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when tool starts running.""" with capture_internal_exceptions(): if not run_id: @@ -545,8 +583,9 @@ def on_tool_start(self, serialized, input_str, *, run_id, **kwargs): kwargs.get("inputs", [input_str]), ) - def on_tool_end(self, output, *, run_id, **kwargs): - # type: (SentryLangchainCallback, str, UUID, Any) -> Any + def on_tool_end( + self: "SentryLangchainCallback", output: str, *, run_id: "UUID", **kwargs: "Any" + ) -> "Any": """Run when tool ends running.""" with capture_internal_exceptions(): if not run_id or run_id not in self.span_map: @@ -560,14 +599,20 @@ def on_tool_end(self, output, *, run_id, **kwargs): self._exit_span(span_data, run_id) - def on_tool_error(self, error, *args, run_id, **kwargs): - # type: (SentryLangchainCallback, Union[Exception, KeyboardInterrupt], UUID, Any) -> Any + def on_tool_error( + self, + error: "SentryLangchainCallback", + *args: "Union[Exception, KeyboardInterrupt]", + run_id: "UUID", + **kwargs: "Any", + ) -> "Any": """Run when tool errors.""" self._handle_error(run_id, error) -def _extract_tokens(token_usage): - # type: (Any) -> tuple[Optional[int], Optional[int], Optional[int]] +def _extract_tokens( + token_usage: "Any", +) -> "tuple[Optional[int], Optional[int], Optional[int]]": if not token_usage: return None, None, None @@ -582,8 +627,9 @@ def _extract_tokens(token_usage): return input_tokens, output_tokens, total_tokens -def _extract_tokens_from_generations(generations): - # type: (Any) -> tuple[Optional[int], Optional[int], Optional[int]] +def _extract_tokens_from_generations( + generations: "Any", +) -> "tuple[Optional[int], Optional[int], Optional[int]]": """Extract token usage from response.generations structure.""" if not generations: return None, None, None @@ -607,8 +653,7 @@ def _extract_tokens_from_generations(generations): ) -def _get_token_usage(obj): - # type: (Any) -> Optional[Dict[str, Any]] +def _get_token_usage(obj: "Any") -> "Optional[Dict[str, Any]]": """ Check multiple paths to extract token usage from different objects. """ @@ -636,8 +681,7 @@ def _get_token_usage(obj): return None -def _record_token_usage(span, response): - # type: (Span, Any) -> None +def _record_token_usage(span: "Span", response: "Any") -> None: token_usage = _get_token_usage(response) if token_usage: input_tokens, output_tokens, total_tokens = _extract_tokens(token_usage) @@ -656,8 +700,9 @@ def _record_token_usage(span, response): span.set_data(SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS, total_tokens) -def _get_request_data(obj, args, kwargs): - # type: (Any, Any, Any) -> tuple[Optional[str], Optional[List[Any]]] +def _get_request_data( + obj: "Any", args: "Any", kwargs: "Any" +) -> "tuple[Optional[str], Optional[List[Any]]]": """ Get the agent name and available tools for the agent. """ @@ -684,8 +729,7 @@ def _get_request_data(obj, args, kwargs): return (agent_name, tools) -def _simplify_langchain_tools(tools): - # type: (Any) -> Optional[List[Any]] +def _simplify_langchain_tools(tools: "Any") -> "Optional[List[Any]]": """Parse and simplify tools into a cleaner format.""" if not tools: return None @@ -750,8 +794,7 @@ def _simplify_langchain_tools(tools): return simplified_tools if simplified_tools else None -def _set_tools_on_span(span, tools): - # type: (Span, Any) -> None +def _set_tools_on_span(span: "Span", tools: "Any") -> None: """Set available tools data on a span if tools are provided.""" if tools is not None: simplified_tools = _simplify_langchain_tools(tools) @@ -764,19 +807,15 @@ def _set_tools_on_span(span, tools): ) -def _wrap_configure(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - +def _wrap_configure(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) def new_configure( - callback_manager_cls, # type: type - inheritable_callbacks=None, # type: Callbacks - local_callbacks=None, # type: Callbacks - *args, # type: Any - **kwargs, # type: Any - ): - # type: (...) -> Any - + callback_manager_cls: type, + inheritable_callbacks: Callbacks = None, + local_callbacks: Callbacks = None, + *args: Any, + **kwargs: Any, + ) -> Any: integration = sentry_sdk.get_client().get_integration(LangchainIntegration) if integration is None: return f( @@ -848,12 +887,9 @@ def new_configure( return new_configure -def _wrap_agent_executor_invoke(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - +def _wrap_agent_executor_invoke(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_invoke(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_invoke(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(LangchainIntegration) if integration is None: return f(self, *args, **kwargs) @@ -914,12 +950,9 @@ def new_invoke(self, *args, **kwargs): return new_invoke -def _wrap_agent_executor_stream(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - +def _wrap_agent_executor_stream(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_stream(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_stream(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(LangchainIntegration) if integration is None: return f(self, *args, **kwargs) @@ -968,9 +1001,8 @@ def new_stream(self, *args, **kwargs): old_iterator = result - def new_iterator(): - # type: () -> Iterator[Any] - exc_info = (None, None, None) # type: tuple[Any, Any, Any] + def new_iterator() -> "Iterator[Any]": + exc_info: tuple[Any, Any, Any] = (None, None, None) try: for event in old_iterator: yield event @@ -995,9 +1027,8 @@ def new_iterator(): _pop_agent() span.__exit__(*exc_info) - async def new_iterator_async(): - # type: () -> AsyncIterator[Any] - exc_info = (None, None, None) # type: tuple[Any, Any, Any] + async def new_iterator_async() -> "AsyncIterator[Any]": + exc_info: tuple[Any, Any, Any] = (None, None, None) try: async for event in old_iterator: yield event @@ -1032,8 +1063,7 @@ async def new_iterator_async(): return new_stream -def _patch_embeddings_provider(provider_class): - # type: (Any) -> None +def _patch_embeddings_provider(provider_class: "Any") -> None: """Patch an embeddings provider class with monitoring wrappers.""" if provider_class is None: return @@ -1054,13 +1084,11 @@ def _patch_embeddings_provider(provider_class): ) -def _wrap_embedding_method(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_embedding_method(f: "Callable[..., Any]") -> "Callable[..., Any]": """Wrap sync embedding methods (embed_documents and embed_query).""" @wraps(f) - def new_embedding_method(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_embedding_method(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(LangchainIntegration) if integration is None: return f(self, *args, **kwargs) @@ -1094,13 +1122,13 @@ def new_embedding_method(self, *args, **kwargs): return new_embedding_method -def _wrap_async_embedding_method(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_async_embedding_method(f: "Callable[..., Any]") -> "Callable[..., Any]": """Wrap async embedding methods (aembed_documents and aembed_query).""" @wraps(f) - async def new_async_embedding_method(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + async def new_async_embedding_method( + self: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(LangchainIntegration) if integration is None: return await f(self, *args, **kwargs) diff --git a/sentry_sdk/integrations/langgraph.py b/sentry_sdk/integrations/langgraph.py index 5bb0e0fd08..ad3996a9d9 100644 --- a/sentry_sdk/integrations/langgraph.py +++ b/sentry_sdk/integrations/langgraph.py @@ -24,13 +24,11 @@ class LanggraphIntegration(Integration): identifier = "langgraph" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True): - # type: (LanggraphIntegration, bool) -> None + def __init__(self: "LanggraphIntegration", include_prompts: bool = True) -> None: self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: # LangGraph lets users create agents using a StateGraph or the Functional API. # StateGraphs are then compiled to a CompiledStateGraph. Both CompiledStateGraph and # the functional API execute on a Pregel instance. Pregel is the runtime for the graph @@ -45,8 +43,7 @@ def setup_once(): Pregel.ainvoke = _wrap_pregel_ainvoke(Pregel.ainvoke) -def _get_graph_name(graph_obj): - # type: (Any) -> Optional[str] +def _get_graph_name(graph_obj: "Any") -> "Optional[str]": for attr in ["name", "graph_name", "__name__", "_name"]: if hasattr(graph_obj, attr): name = getattr(graph_obj, attr) @@ -55,8 +52,7 @@ def _get_graph_name(graph_obj): return None -def _normalize_langgraph_message(message): - # type: (Any) -> Any +def _normalize_langgraph_message(message: "Any") -> "Any": if not hasattr(message, "content"): return None @@ -71,8 +67,7 @@ def _normalize_langgraph_message(message): return parsed -def _parse_langgraph_messages(state): - # type: (Any) -> Optional[List[Any]] +def _parse_langgraph_messages(state: "Any") -> "Optional[List[Any]]": if not state: return None @@ -103,11 +98,9 @@ def _parse_langgraph_messages(state): return normalized_messages if normalized_messages else None -def _wrap_state_graph_compile(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_state_graph_compile(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_compile(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_compile(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(LanggraphIntegration) if integration is None: return f(self, *args, **kwargs) @@ -149,12 +142,9 @@ def new_compile(self, *args, **kwargs): return new_compile -def _wrap_pregel_invoke(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - +def _wrap_pregel_invoke(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - def new_invoke(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + def new_invoke(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(LanggraphIntegration) if integration is None: return f(self, *args, **kwargs) @@ -206,12 +196,9 @@ def new_invoke(self, *args, **kwargs): return new_invoke -def _wrap_pregel_ainvoke(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - +def _wrap_pregel_ainvoke(f: "Callable[..., Any]") -> "Callable[..., Any]": @wraps(f) - async def new_ainvoke(self, *args, **kwargs): - # type: (Any, Any, Any) -> Any + async def new_ainvoke(self: Any, *args: Any, **kwargs: Any) -> Any: integration = sentry_sdk.get_client().get_integration(LanggraphIntegration) if integration is None: return await f(self, *args, **kwargs) @@ -262,8 +249,9 @@ async def new_ainvoke(self, *args, **kwargs): return new_ainvoke -def _get_new_messages(input_messages, output_messages): - # type: (Optional[List[Any]], Optional[List[Any]]) -> Optional[List[Any]] +def _get_new_messages( + input_messages: "Optional[List[Any]]", output_messages: "Optional[List[Any]]" +) -> "Optional[List[Any]]": """Extract only the new messages added during this invocation.""" if not output_messages: return None @@ -280,8 +268,7 @@ def _get_new_messages(input_messages, output_messages): return new_messages if new_messages else None -def _extract_llm_response_text(messages): - # type: (Optional[List[Any]]) -> Optional[str] +def _extract_llm_response_text(messages: "Optional[List[Any]]") -> "Optional[str]": if not messages: return None @@ -296,8 +283,7 @@ def _extract_llm_response_text(messages): return None -def _extract_tool_calls(messages): - # type: (Optional[List[Any]]) -> Optional[List[Any]] +def _extract_tool_calls(messages: "Optional[List[Any]]") -> "Optional[List[Any]]": if not messages: return None @@ -311,8 +297,12 @@ def _extract_tool_calls(messages): return tool_calls if tool_calls else None -def _set_response_attributes(span, input_messages, result, integration): - # type: (Any, Optional[List[Any]], Any, LanggraphIntegration) -> None +def _set_response_attributes( + span: "Any", + input_messages: "Optional[List[Any]]", + result: "Any", + integration: "LanggraphIntegration", +) -> None: if not (should_send_default_pii() and integration.include_prompts): return diff --git a/sentry_sdk/integrations/launchdarkly.py b/sentry_sdk/integrations/launchdarkly.py index 6dfc1958b7..2d86fc5ca4 100644 --- a/sentry_sdk/integrations/launchdarkly.py +++ b/sentry_sdk/integrations/launchdarkly.py @@ -20,8 +20,7 @@ class LaunchDarklyIntegration(Integration): identifier = "launchdarkly" - def __init__(self, ld_client=None): - # type: (LDClient | None) -> None + def __init__(self, ld_client: "LDClient | None" = None) -> None: """ :param client: An initialized LDClient instance. If a client is not provided, this integration will attempt to use the shared global instance. @@ -38,24 +37,27 @@ def __init__(self, ld_client=None): client.add_hook(LaunchDarklyHook()) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: pass class LaunchDarklyHook(Hook): @property - def metadata(self): - # type: () -> Metadata + def metadata(self) -> "Metadata": return Metadata(name="sentry-flag-auditor") - def after_evaluation(self, series_context, data, detail): - # type: (EvaluationSeriesContext, dict[Any, Any], EvaluationDetail) -> dict[Any, Any] + def after_evaluation( + self, + series_context: "EvaluationSeriesContext", + data: "dict[Any, Any]", + detail: "EvaluationDetail", + ) -> "dict[Any, Any]": if isinstance(detail.value, bool): add_feature_flag(series_context.key, detail.value) return data - def before_evaluation(self, series_context, data): - # type: (EvaluationSeriesContext, dict[Any, Any]) -> dict[Any, Any] + def before_evaluation( + self, series_context: "EvaluationSeriesContext", data: "dict[Any, Any]" + ) -> "dict[Any, Any]": return data # No-op. diff --git a/sentry_sdk/integrations/litellm.py b/sentry_sdk/integrations/litellm.py index 35fb1b1048..08cb217962 100644 --- a/sentry_sdk/integrations/litellm.py +++ b/sentry_sdk/integrations/litellm.py @@ -23,8 +23,7 @@ raise DidNotEnable("LiteLLM not installed") -def _get_metadata_dict(kwargs): - # type: (Dict[str, Any]) -> Dict[str, Any] +def _get_metadata_dict(kwargs: "Dict[str, Any]") -> "Dict[str, Any]": """Get the metadata dictionary from the kwargs.""" litellm_params = kwargs.setdefault("litellm_params", {}) @@ -36,8 +35,7 @@ def _get_metadata_dict(kwargs): return metadata -def _input_callback(kwargs): - # type: (Dict[str, Any]) -> None +def _input_callback(kwargs: "Dict[str, Any]") -> None: """Handle the start of a request.""" integration = sentry_sdk.get_client().get_integration(LiteLLMIntegration) @@ -138,8 +136,12 @@ def _input_callback(kwargs): set_data_normalized(span, f"gen_ai.litellm.{key}", value) -def _success_callback(kwargs, completion_response, start_time, end_time): - # type: (Dict[str, Any], Any, datetime, datetime) -> None +def _success_callback( + kwargs: "Dict[str, Any]", + completion_response: "Any", + start_time: "datetime", + end_time: "datetime", +) -> None: """Handle successful completion.""" span = _get_metadata_dict(kwargs).get("_sentry_span") @@ -198,8 +200,12 @@ def _success_callback(kwargs, completion_response, start_time, end_time): span.__exit__(None, None, None) -def _failure_callback(kwargs, exception, start_time, end_time): - # type: (Dict[str, Any], Exception, datetime, datetime) -> None +def _failure_callback( + kwargs: "Dict[str, Any]", + exception: Exception, + start_time: "datetime", + end_time: "datetime", +) -> None: """Handle request failure.""" span = _get_metadata_dict(kwargs).get("_sentry_span") if span is None: @@ -266,13 +272,11 @@ class LiteLLMIntegration(Integration): identifier = "litellm" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True): - # type: (LiteLLMIntegration, bool) -> None + def __init__(self: "LiteLLMIntegration", include_prompts: bool = True) -> None: self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: """Set up LiteLLM callbacks for monitoring.""" litellm.input_callback = litellm.input_callback or [] if _input_callback not in litellm.input_callback: diff --git a/sentry_sdk/integrations/litestar.py b/sentry_sdk/integrations/litestar.py index 0cb9f4b972..47c89f62e6 100644 --- a/sentry_sdk/integrations/litestar.py +++ b/sentry_sdk/integrations/litestar.py @@ -55,13 +55,12 @@ class LitestarIntegration(Integration): def __init__( self, - failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int] + failed_request_status_codes: "Set[int]" = _DEFAULT_FAILED_REQUEST_STATUS_CODES, ) -> None: self.failed_request_status_codes = failed_request_status_codes @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: patch_app_init() patch_middlewares() patch_http_route_handle() @@ -78,9 +77,9 @@ def setup_once(): class SentryLitestarASGIMiddleware(SentryAsgiMiddleware): - def __init__(self, app, span_origin=LitestarIntegration.origin): - # type: (ASGIApp, str) -> None - + def __init__( + self, app: "ASGIApp", span_origin: str = LitestarIntegration.origin + ) -> None: super().__init__( app=app, unsafe_context_data=False, @@ -90,8 +89,7 @@ def __init__(self, app, span_origin=LitestarIntegration.origin): asgi_version=3, ) - def _capture_request_exception(self, exc): - # type: (Exception) -> None + def _capture_request_exception(self, exc: Exception) -> None: """Avoid catching exceptions from request handlers. Those exceptions are already handled in Litestar.after_exception handler. @@ -100,8 +98,7 @@ def _capture_request_exception(self, exc): pass -def patch_app_init(): - # type: () -> None +def patch_app_init() -> None: """ Replaces the Litestar class's `__init__` function in order to inject `after_exception` handlers and set the `SentryLitestarASGIMiddleware` as the outmost middleware in the stack. @@ -112,8 +109,7 @@ def patch_app_init(): old__init__ = Litestar.__init__ @ensure_integration_enabled(LitestarIntegration, old__init__) - def injection_wrapper(self, *args, **kwargs): - # type: (Litestar, *Any, **Any) -> None + def injection_wrapper(self: "Litestar", *args: "Any", **kwargs: "Any") -> None: kwargs["after_exception"] = [ exception_handler, *(kwargs.get("after_exception") or []), @@ -126,13 +122,11 @@ def injection_wrapper(self, *args, **kwargs): Litestar.__init__ = injection_wrapper -def patch_middlewares(): - # type: () -> None +def patch_middlewares() -> None: old_resolve_middleware_stack = BaseRouteHandler.resolve_middleware @ensure_integration_enabled(LitestarIntegration, old_resolve_middleware_stack) - def resolve_middleware_wrapper(self): - # type: (BaseRouteHandler) -> list[Middleware] + def resolve_middleware_wrapper(self: "BaseRouteHandler") -> "list[Middleware]": return [ enable_span_for_middleware(middleware) for middleware in old_resolve_middleware_stack(self) @@ -141,8 +135,7 @@ def resolve_middleware_wrapper(self): BaseRouteHandler.resolve_middleware = resolve_middleware_wrapper -def enable_span_for_middleware(middleware): - # type: (Middleware) -> Middleware +def enable_span_for_middleware(middleware: "Middleware") -> "Middleware": if ( not hasattr(middleware, "__call__") # noqa: B004 or middleware is SentryLitestarASGIMiddleware @@ -150,12 +143,16 @@ def enable_span_for_middleware(middleware): return middleware if isinstance(middleware, DefineMiddleware): - old_call = middleware.middleware.__call__ # type: ASGIApp + old_call: "ASGIApp" = middleware.middleware.__call__ else: old_call = middleware.__call__ - async def _create_span_call(self, scope, receive, send): - # type: (MiddlewareProtocol, LitestarScope, Receive, Send) -> None + async def _create_span_call( + self: "MiddlewareProtocol", + scope: "LitestarScope", + receive: "Receive", + send: "Send", + ) -> None: if sentry_sdk.get_client().get_integration(LitestarIntegration) is None: return await old_call(self, scope, receive, send) @@ -168,8 +165,9 @@ async def _create_span_call(self, scope, receive, send): middleware_span.set_tag("litestar.middleware_name", middleware_name) # Creating spans for the "receive" callback - async def _sentry_receive(*args, **kwargs): - # type: (*Any, **Any) -> Union[HTTPReceiveMessage, WebSocketReceiveMessage] + async def _sentry_receive( + *args: "Any", **kwargs: "Any" + ) -> "Union[HTTPReceiveMessage, WebSocketReceiveMessage]": if sentry_sdk.get_client().get_integration(LitestarIntegration) is None: return await receive(*args, **kwargs) with sentry_sdk.start_span( @@ -185,8 +183,7 @@ async def _sentry_receive(*args, **kwargs): new_receive = _sentry_receive if not receive_patched else receive # Creating spans for the "send" callback - async def _sentry_send(message): - # type: (Message) -> None + async def _sentry_send(message: "Message") -> None: if sentry_sdk.get_client().get_integration(LitestarIntegration) is None: return await send(message) with sentry_sdk.start_span( @@ -214,17 +211,19 @@ async def _sentry_send(message): return middleware -def patch_http_route_handle(): - # type: () -> None +def patch_http_route_handle() -> None: old_handle = HTTPRoute.handle - async def handle_wrapper(self, scope, receive, send): - # type: (HTTPRoute, HTTPScope, Receive, Send) -> None + async def handle_wrapper( + self: "HTTPRoute", scope: "HTTPScope", receive: "Receive", send: "Send" + ) -> None: if sentry_sdk.get_client().get_integration(LitestarIntegration) is None: return await old_handle(self, scope, receive, send) sentry_scope = sentry_sdk.get_isolation_scope() - request = scope["app"].request_class(scope=scope, receive=receive, send=send) # type: Request[Any, Any] + request: "Request[Any, Any]" = scope["app"].request_class( + scope=scope, receive=receive, send=send + ) extracted_request_data = ConnectionDataExtractor( parse_body=True, parse_query=True )(request) @@ -232,8 +231,7 @@ async def handle_wrapper(self, scope, receive, send): request_data = await body - def event_processor(event, _): - # type: (Event, Hint) -> Event + def event_processor(event: "Event", _: "Hint") -> "Event": route_handler = scope.get("route_handler") request_info = event.get("request", {}) @@ -277,8 +275,7 @@ def event_processor(event, _): HTTPRoute.handle = handle_wrapper -def retrieve_user_from_scope(scope): - # type: (LitestarScope) -> Optional[dict[str, Any]] +def retrieve_user_from_scope(scope: "LitestarScope") -> "Optional[dict[str, Any]]": scope_user = scope.get("user") if isinstance(scope_user, dict): return scope_user @@ -289,9 +286,8 @@ def retrieve_user_from_scope(scope): @ensure_integration_enabled(LitestarIntegration) -def exception_handler(exc, scope): - # type: (Exception, LitestarScope) -> None - user_info = None # type: Optional[dict[str, Any]] +def exception_handler(exc: Exception, scope: "LitestarScope") -> None: + user_info: Optional[dict[str, Any]] = None if should_send_default_pii(): user_info = retrieve_user_from_scope(scope) if user_info and isinstance(user_info, dict): diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 9c68596be8..6492526c21 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -60,9 +60,8 @@ def ignore_logger( - name, # type: str -): - # type: (...) -> None + name: str, +) -> None: """This disables recording (both in breadcrumbs and as events) calls to a logger of a specific name. Among other uses, many of our integrations use this to prevent their actions being recorded as breadcrumbs. Exposed @@ -78,11 +77,10 @@ class LoggingIntegration(Integration): def __init__( self, - level=DEFAULT_LEVEL, - event_level=DEFAULT_EVENT_LEVEL, - sentry_logs_level=DEFAULT_LEVEL, - ): - # type: (Optional[int], Optional[int], Optional[int]) -> None + level: "Optional[int]" = DEFAULT_LEVEL, + event_level: "Optional[int]" = DEFAULT_EVENT_LEVEL, + sentry_logs_level: "Optional[int]" = DEFAULT_LEVEL, + ) -> None: self._handler = None self._breadcrumb_handler = None self._sentry_logs_handler = None @@ -96,8 +94,7 @@ def __init__( if event_level is not None: self._handler = EventHandler(level=event_level) - def _handle_record(self, record): - # type: (LogRecord) -> None + def _handle_record(self, record: "LogRecord") -> None: if self._handler is not None and record.levelno >= self._handler.level: self._handler.handle(record) @@ -114,12 +111,10 @@ def _handle_record(self, record): self._sentry_logs_handler.handle(record) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: old_callhandlers = logging.Logger.callHandlers - def sentry_patched_callhandlers(self, record): - # type: (Any, LogRecord) -> Any + def sentry_patched_callhandlers(self: "Any", record: "LogRecord") -> "Any": # keeping a local reference because the # global might be discarded on shutdown ignored_loggers = _IGNORED_LOGGERS @@ -175,22 +170,19 @@ class _BaseHandler(logging.Handler): ) ) - def _can_record(self, record): - # type: (LogRecord) -> bool + def _can_record(self, record: "LogRecord") -> bool: """Prevents ignored loggers from recording""" for logger in _IGNORED_LOGGERS: if fnmatch(record.name.strip(), logger): return False return True - def _logging_to_event_level(self, record): - # type: (LogRecord) -> str + def _logging_to_event_level(self, record: "LogRecord") -> str: return LOGGING_TO_EVENT_LEVEL.get( record.levelno, record.levelname.lower() if record.levelname else "" ) - def _extra_from_record(self, record): - # type: (LogRecord) -> MutableMapping[str, object] + def _extra_from_record(self, record: "LogRecord") -> "MutableMapping[str, object]": return { k: v for k, v in vars(record).items() @@ -206,14 +198,12 @@ class EventHandler(_BaseHandler): Note that you do not have to use this class if the logging integration is enabled, which it is by default. """ - def emit(self, record): - # type: (LogRecord) -> Any + def emit(self, record: "LogRecord") -> "Any": with capture_internal_exceptions(): self.format(record) return self._emit(record) - def _emit(self, record): - # type: (LogRecord) -> None + def _emit(self, record: "LogRecord") -> None: if not self._can_record(record): return @@ -300,14 +290,12 @@ class BreadcrumbHandler(_BaseHandler): Note that you do not have to use this class if the logging integration is enabled, which it is by default. """ - def emit(self, record): - # type: (LogRecord) -> Any + def emit(self, record: "LogRecord") -> "Any": with capture_internal_exceptions(): self.format(record) return self._emit(record) - def _emit(self, record): - # type: (LogRecord) -> None + def _emit(self, record: "LogRecord") -> None: if not self._can_record(record): return @@ -315,8 +303,7 @@ def _emit(self, record): self._breadcrumb_from_record(record), hint={"log_record": record} ) - def _breadcrumb_from_record(self, record): - # type: (LogRecord) -> Dict[str, Any] + def _breadcrumb_from_record(self, record: "LogRecord") -> "Dict[str, Any]": return { "type": "log", "level": self._logging_to_event_level(record), @@ -334,8 +321,7 @@ class SentryLogsHandler(_BaseHandler): Note that you do not have to use this class if the logging integration is enabled, which it is by default. """ - def emit(self, record): - # type: (LogRecord) -> Any + def emit(self, record: "LogRecord") -> "Any": with capture_internal_exceptions(): self.format(record) if not self._can_record(record): @@ -350,14 +336,15 @@ def emit(self, record): self._capture_log_from_record(client, record) - def _capture_log_from_record(self, client, record): - # type: (BaseClient, LogRecord) -> None + def _capture_log_from_record( + self, client: "BaseClient", record: "LogRecord" + ) -> None: otel_severity_number, otel_severity_text = _log_level_to_otel( record.levelno, SEVERITY_TO_OTEL_SEVERITY ) project_root = client.options["project_root"] - attrs = self._extra_from_record(record) # type: Any + attrs: "Any" = self._extra_from_record(record) attrs["sentry.origin"] = "auto.log.stdlib" parameters_set = False diff --git a/sentry_sdk/integrations/loguru.py b/sentry_sdk/integrations/loguru.py index 96d2b6a7ae..6c4da26c48 100644 --- a/sentry_sdk/integrations/loguru.py +++ b/sentry_sdk/integrations/loguru.py @@ -66,21 +66,20 @@ class LoggingLevels(enum.IntEnum): class LoguruIntegration(Integration): identifier = "loguru" - level = DEFAULT_LEVEL # type: Optional[int] - event_level = DEFAULT_EVENT_LEVEL # type: Optional[int] + level: "Optional[int]" = DEFAULT_LEVEL + event_level: "Optional[int]" = DEFAULT_EVENT_LEVEL breadcrumb_format = DEFAULT_FORMAT event_format = DEFAULT_FORMAT - sentry_logs_level = DEFAULT_LEVEL # type: Optional[int] + sentry_logs_level: "Optional[int]" = DEFAULT_LEVEL def __init__( self, - level=DEFAULT_LEVEL, - event_level=DEFAULT_EVENT_LEVEL, - breadcrumb_format=DEFAULT_FORMAT, - event_format=DEFAULT_FORMAT, - sentry_logs_level=DEFAULT_LEVEL, - ): - # type: (Optional[int], Optional[int], str | loguru.FormatFunction, str | loguru.FormatFunction, Optional[int]) -> None + level: "Optional[int]" = DEFAULT_LEVEL, + event_level: "Optional[int]" = DEFAULT_EVENT_LEVEL, + breadcrumb_format: "str | loguru.FormatFunction" = DEFAULT_FORMAT, + event_format: "str | loguru.FormatFunction" = DEFAULT_FORMAT, + sentry_logs_level: "Optional[int]" = DEFAULT_LEVEL, + ) -> None: LoguruIntegration.level = level LoguruIntegration.event_level = event_level LoguruIntegration.breadcrumb_format = breadcrumb_format @@ -88,8 +87,7 @@ def __init__( LoguruIntegration.sentry_logs_level = sentry_logs_level @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: if LoguruIntegration.level is not None: logger.add( LoguruBreadcrumbHandler(level=LoguruIntegration.level), @@ -112,8 +110,7 @@ def setup_once(): class _LoguruBaseHandler(_BaseHandler): - def __init__(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def __init__(self, *args: "Any", **kwargs: "Any") -> None: if kwargs.get("level"): kwargs["level"] = SENTRY_LEVEL_FROM_LOGURU_LEVEL.get( kwargs.get("level", ""), DEFAULT_LEVEL @@ -121,8 +118,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def _logging_to_event_level(self, record): - # type: (LogRecord) -> str + def _logging_to_event_level(self, record: "LogRecord") -> str: try: return SENTRY_LEVEL_FROM_LOGURU_LEVEL[ LoggingLevels(record.levelno).name @@ -143,8 +139,7 @@ class LoguruBreadcrumbHandler(_LoguruBaseHandler, BreadcrumbHandler): pass -def loguru_sentry_logs_handler(message): - # type: (Message) -> None +def loguru_sentry_logs_handler(message: "Message") -> None: # This is intentionally a callable sink instead of a standard logging handler # since otherwise we wouldn't get direct access to message.record client = sentry_sdk.get_client() @@ -167,7 +162,7 @@ def loguru_sentry_logs_handler(message): record["level"].no, SEVERITY_TO_OTEL_SEVERITY ) - attrs = {"sentry.origin": "auto.log.loguru"} # type: dict[str, Any] + attrs: "dict[str, Any]" = {"sentry.origin": "auto.log.loguru"} project_root = client.options["project_root"] if record.get("file"): diff --git a/sentry_sdk/integrations/mcp.py b/sentry_sdk/integrations/mcp.py index 7b72aa4763..6356b0b8cf 100644 --- a/sentry_sdk/integrations/mcp.py +++ b/sentry_sdk/integrations/mcp.py @@ -33,8 +33,7 @@ class MCPIntegration(Integration): identifier = "mcp" origin = "auto.ai.mcp" - def __init__(self, include_prompts=True): - # type: (bool) -> None + def __init__(self, include_prompts: bool = True) -> None: """ Initialize the MCP integration. @@ -45,16 +44,14 @@ def __init__(self, include_prompts=True): self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: """ Patches MCP server classes to instrument handler execution. """ _patch_lowlevel_server() -def _get_request_context_data(): - # type: () -> tuple[Optional[str], Optional[str], str] +def _get_request_context_data() -> "tuple[Optional[str], Optional[str], str]": """ Extract request ID, session ID, and MCP transport type from the request context. @@ -64,9 +61,9 @@ def _get_request_context_data(): - session_id: May be None if not available - mcp_transport: "http", "sse", "stdio" """ - request_id = None # type: Optional[str] - session_id = None # type: Optional[str] - mcp_transport = "stdio" # type: str + request_id: "Optional[str]" = None + session_id: "Optional[str]" = None + mcp_transport: str = "stdio" try: ctx = request_ctx.get() @@ -96,8 +93,9 @@ def _get_request_context_data(): return request_id, session_id, mcp_transport -def _get_span_config(handler_type, item_name): - # type: (str, str) -> tuple[str, str, str, Optional[str]] +def _get_span_config( + handler_type: str, item_name: str +) -> "tuple[str, str, str, Optional[str]]": """ Get span configuration based on handler type. @@ -123,16 +121,15 @@ def _get_span_config(handler_type, item_name): def _set_span_input_data( - span, - handler_name, - span_data_key, - mcp_method_name, - arguments, - request_id, - session_id, - mcp_transport, -): - # type: (Any, str, str, str, dict[str, Any], Optional[str], Optional[str], str) -> None + span: "Any", + handler_name: str, + span_data_key: str, + mcp_method_name: str, + arguments: "dict[str, Any]", + request_id: "Optional[str]", + session_id: "Optional[str]", + mcp_transport: str, +) -> None: """Set input span data for MCP handlers.""" # Set handler identifier @@ -158,8 +155,7 @@ def _set_span_input_data( span.set_data(f"mcp.request.argument.{k}", safe_serialize(v)) -def _extract_tool_result_content(result): - # type: (Any) -> Any +def _extract_tool_result_content(result: "Any") -> "Any": """ Extract meaningful content from MCP tool result. @@ -199,8 +195,9 @@ def _extract_tool_result_content(result): return result -def _set_span_output_data(span, result, result_data_key, handler_type): - # type: (Any, Any, Optional[str], str) -> None +def _set_span_output_data( + span: "Any", result: "Any", result_data_key: "Optional[str]", handler_type: str +) -> None: """Set output span data for MCP handlers.""" if result is None: return @@ -224,7 +221,7 @@ def _set_span_output_data(span, result, result_data_key, handler_type): elif handler_type == "prompt": # For prompts, count messages and set role/content only for single-message prompts try: - messages = None # type: Optional[list[str]] + messages: "Optional[list[str]]" = None message_count = 0 # Check if result has messages attribute (GetPromptResult) @@ -282,8 +279,9 @@ def _set_span_output_data(span, result, result_data_key, handler_type): # Handler data preparation and wrapping -def _prepare_handler_data(handler_type, original_args): - # type: (str, tuple[Any, ...]) -> tuple[str, dict[str, Any], str, str, str, Optional[str]] +def _prepare_handler_data( + handler_type: str, original_args: "tuple[Any, ...]" +) -> "tuple[str, dict[str, Any], str, str, str, Optional[str]]": """ Prepare common handler data for both async and sync wrappers. @@ -319,8 +317,9 @@ def _prepare_handler_data(handler_type, original_args): ) -async def _async_handler_wrapper(handler_type, func, original_args): - # type: (str, Callable[..., Any], tuple[Any, ...]) -> Any +async def _async_handler_wrapper( + handler_type: str, func: "Callable[..., Any]", original_args: "tuple[Any, ...]" +) -> "Any": """ Async wrapper for MCP handlers. @@ -384,8 +383,9 @@ async def _async_handler_wrapper(handler_type, func, original_args): return result -def _sync_handler_wrapper(handler_type, func, original_args): - # type: (str, Callable[..., Any], tuple[Any, ...]) -> Any +def _sync_handler_wrapper( + handler_type: str, func: "Callable[..., Any]", original_args: "tuple[Any, ...]" +) -> "Any": """ Sync wrapper for MCP handlers. @@ -449,8 +449,9 @@ def _sync_handler_wrapper(handler_type, func, original_args): return result -def _create_instrumented_handler(handler_type, func): - # type: (str, Callable[..., Any]) -> Callable[..., Any] +def _create_instrumented_handler( + handler_type: str, func: "Callable[..., Any]" +) -> "Callable[..., Any]": """ Create an instrumented version of a handler function (async or sync). @@ -470,25 +471,25 @@ def _create_instrumented_handler(handler_type, func): if inspect.iscoroutinefunction(func): @wraps(func) - async def async_wrapper(*args): - # type: (*Any) -> Any + async def async_wrapper(*args: "Any") -> "Any": return await _async_handler_wrapper(handler_type, func, args) return async_wrapper else: @wraps(func) - def sync_wrapper(*args): - # type: (*Any) -> Any + def sync_wrapper(*args: "Any") -> "Any": return _sync_handler_wrapper(handler_type, func, args) return sync_wrapper def _create_instrumented_decorator( - original_decorator, handler_type, *decorator_args, **decorator_kwargs -): - # type: (Callable[..., Any], str, *Any, **Any) -> Callable[..., Any] + original_decorator: "Callable[..., Any]", + handler_type: str, + *decorator_args: "Any", + **decorator_kwargs: "Any", +) -> "Callable[..., Any]": """ Create an instrumented version of an MCP decorator. @@ -512,8 +513,7 @@ def _create_instrumented_decorator( A decorator function that instruments handlers before registering them """ - def instrumented_decorator(func): - # type: (Callable[..., Any]) -> Callable[..., Any] + def instrumented_decorator(func: "Callable[..., Any]") -> "Callable[..., Any]": # First wrap the handler with instrumentation instrumented_func = _create_instrumented_handler(handler_type, func) # Then register it with the original MCP decorator @@ -524,16 +524,16 @@ def instrumented_decorator(func): return instrumented_decorator -def _patch_lowlevel_server(): - # type: () -> None +def _patch_lowlevel_server() -> None: """ Patches the mcp.server.lowlevel.Server class to instrument handler execution. """ # Patch call_tool decorator original_call_tool = Server.call_tool - def patched_call_tool(self, **kwargs): - # type: (Server, **Any) -> Callable[[Callable[..., Any]], Callable[..., Any]] + def patched_call_tool( + self: "Server", **kwargs: "Any" + ) -> "Callable[[Callable[..., Any]], Callable[..., Any]]": """Patched version of Server.call_tool that adds Sentry instrumentation.""" return lambda func: _create_instrumented_decorator( original_call_tool, "tool", self, **kwargs @@ -544,8 +544,9 @@ def patched_call_tool(self, **kwargs): # Patch get_prompt decorator original_get_prompt = Server.get_prompt - def patched_get_prompt(self): - # type: (Server) -> Callable[[Callable[..., Any]], Callable[..., Any]] + def patched_get_prompt( + self: "Server", + ) -> "Callable[[Callable[..., Any]], Callable[..., Any]]": """Patched version of Server.get_prompt that adds Sentry instrumentation.""" return lambda func: _create_instrumented_decorator( original_get_prompt, "prompt", self @@ -556,8 +557,9 @@ def patched_get_prompt(self): # Patch read_resource decorator original_read_resource = Server.read_resource - def patched_read_resource(self): - # type: (Server) -> Callable[[Callable[..., Any]], Callable[..., Any]] + def patched_read_resource( + self: "Server", + ) -> "Callable[[Callable[..., Any]], Callable[..., Any]]": """Patched version of Server.read_resource that adds Sentry instrumentation.""" return lambda func: _create_instrumented_decorator( original_read_resource, "resource", self diff --git a/sentry_sdk/integrations/modules.py b/sentry_sdk/integrations/modules.py index ce3ee78665..1ed0955af2 100644 --- a/sentry_sdk/integrations/modules.py +++ b/sentry_sdk/integrations/modules.py @@ -14,11 +14,9 @@ class ModulesIntegration(Integration): identifier = "modules" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: @add_global_event_processor - def processor(event, hint): - # type: (Event, Any) -> Event + def processor(event: Event, hint: Any) -> Event: if event.get("type") == "transaction": return event diff --git a/sentry_sdk/integrations/openai.py b/sentry_sdk/integrations/openai.py index 40064e5c72..a9ff51dcd7 100644 --- a/sentry_sdk/integrations/openai.py +++ b/sentry_sdk/integrations/openai.py @@ -56,8 +56,11 @@ class OpenAIIntegration(Integration): identifier = "openai" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True, tiktoken_encoding_name=None): - # type: (OpenAIIntegration, bool, Optional[str]) -> None + def __init__( + self: "OpenAIIntegration", + include_prompts: bool = True, + tiktoken_encoding_name: "Optional[str]" = None, + ) -> None: self.include_prompts = include_prompts self.tiktoken_encoding = None @@ -67,8 +70,7 @@ def __init__(self, include_prompts=True, tiktoken_encoding_name=None): self.tiktoken_encoding = tiktoken.get_encoding(tiktoken_encoding_name) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: Completions.create = _wrap_chat_completion_create(Completions.create) AsyncCompletions.create = _wrap_async_chat_completion_create( AsyncCompletions.create @@ -81,15 +83,13 @@ def setup_once(): Responses.create = _wrap_responses_create(Responses.create) AsyncResponses.create = _wrap_async_responses_create(AsyncResponses.create) - def count_tokens(self, s): - # type: (OpenAIIntegration, str) -> int + def count_tokens(self: "OpenAIIntegration", s: str) -> int: if self.tiktoken_encoding is not None: return len(self.tiktoken_encoding.encode_ordinary(s)) return 0 -def _capture_exception(exc, manual_span_cleanup=True): - # type: (Any, bool) -> None +def _capture_exception(exc: "Any", manual_span_cleanup: bool = True) -> None: # Close an eventually open span # We need to do this by hand because we are not using the start_span context manager current_span = sentry_sdk.get_current_span() @@ -106,8 +106,7 @@ def _capture_exception(exc, manual_span_cleanup=True): sentry_sdk.capture_event(event, hint=hint) -def _get_usage(usage, names): - # type: (Any, List[str]) -> int +def _get_usage(usage: "Any", names: "List[str]") -> int: for name in names: if hasattr(usage, name) and isinstance(getattr(usage, name), int): return getattr(usage, name) @@ -115,14 +114,17 @@ def _get_usage(usage, names): def _calculate_token_usage( - messages, response, span, streaming_message_responses, count_tokens -): - # type: (Optional[Iterable[ChatCompletionMessageParam]], Any, Span, Optional[List[str]], Callable[..., Any]) -> None - input_tokens = 0 # type: Optional[int] - input_tokens_cached = 0 # type: Optional[int] - output_tokens = 0 # type: Optional[int] - output_tokens_reasoning = 0 # type: Optional[int] - total_tokens = 0 # type: Optional[int] + messages: "Optional[Iterable[ChatCompletionMessageParam]]", + response: "Any", + span: "Span", + streaming_message_responses: "Optional[List[str]]", + count_tokens: "Callable[..., Any]", +) -> None: + input_tokens: Optional[int] = 0 + input_tokens_cached: "Optional[int]" = 0 + output_tokens: "Optional[int]" = 0 + output_tokens_reasoning: "Optional[int]" = 0 + total_tokens: "Optional[int]" = 0 if hasattr(response, "usage"): input_tokens = _get_usage(response.usage, ["input_tokens", "prompt_tokens"]) @@ -175,8 +177,12 @@ def _calculate_token_usage( ) -def _set_input_data(span, kwargs, operation, integration): - # type: (Span, dict[str, Any], str, OpenAIIntegration) -> None +def _set_input_data( + span: "Span", + kwargs: "dict[str, Any]", + operation: str, + integration: "OpenAIIntegration", +) -> None: # Input messages (the prompt or data sent to the model) messages = kwargs.get("messages") if messages is None: @@ -233,8 +239,13 @@ def _set_input_data(span, kwargs, operation, integration): ) -def _set_output_data(span, response, kwargs, integration, finish_span=True): - # type: (Span, Any, dict[str, Any], OpenAIIntegration, bool) -> None +def _set_output_data( + span: "Span", + response: "Any", + kwargs: "dict[str, Any]", + integration: "OpenAIIntegration", + finish_span: bool = True, +) -> None: if hasattr(response, "model"): set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_MODEL, response.model) @@ -264,10 +275,10 @@ def _set_output_data(span, response, kwargs, integration, finish_span=True): elif hasattr(response, "output"): if should_send_default_pii() and integration.include_prompts: - output_messages = { + output_messages: "dict[str, list[Any]]" = { "response": [], "tool": [], - } # type: (dict[str, list[Any]]) + } for output in response.output: if output.type == "function_call": @@ -303,8 +314,7 @@ def _set_output_data(span, response, kwargs, integration, finish_span=True): old_iterator = response._iterator - def new_iterator(): - # type: () -> Iterator[ChatCompletionChunk] + def new_iterator() -> "Iterator[ChatCompletionChunk]": count_tokens_manually = True for x in old_iterator: with capture_internal_exceptions(): @@ -359,8 +369,7 @@ def new_iterator(): if finish_span: span.__exit__(None, None, None) - async def new_iterator_async(): - # type: () -> AsyncIterator[ChatCompletionChunk] + async def new_iterator_async() -> "AsyncIterator[ChatCompletionChunk]": count_tokens_manually = True async for x in old_iterator: with capture_internal_exceptions(): @@ -424,8 +433,7 @@ async def new_iterator_async(): span.__exit__(None, None, None) -def _new_chat_completion_common(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _new_chat_completion_common(f: "Any", *args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return f(*args, **kwargs) @@ -459,10 +467,8 @@ def _new_chat_completion_common(f, *args, **kwargs): return response -def _wrap_chat_completion_create(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - def _execute_sync(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _wrap_chat_completion_create(f: "Callable[..., Any]") -> "Callable[..., Any]": + def _execute_sync(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _new_chat_completion_common(f, *args, **kwargs) try: @@ -482,8 +488,7 @@ def _execute_sync(f, *args, **kwargs): return e.value @wraps(f) - def _sentry_patched_create_sync(*args, **kwargs): - # type: (Any, Any) -> Any + def _sentry_patched_create_sync(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None or "messages" not in kwargs: # no "messages" means invalid call (in all versions of openai), let it return error @@ -494,10 +499,8 @@ def _sentry_patched_create_sync(*args, **kwargs): return _sentry_patched_create_sync -def _wrap_async_chat_completion_create(f): - # type: (Callable[..., Any]) -> Callable[..., Any] - async def _execute_async(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _wrap_async_chat_completion_create(f: "Callable[..., Any]") -> "Callable[..., Any]": + async def _execute_async(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _new_chat_completion_common(f, *args, **kwargs) try: @@ -517,8 +520,7 @@ async def _execute_async(f, *args, **kwargs): return e.value @wraps(f) - async def _sentry_patched_create_async(*args, **kwargs): - # type: (Any, Any) -> Any + async def _sentry_patched_create_async(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None or "messages" not in kwargs: # no "messages" means invalid call (in all versions of openai), let it return error @@ -529,8 +531,7 @@ async def _sentry_patched_create_async(*args, **kwargs): return _sentry_patched_create_async -def _new_embeddings_create_common(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _new_embeddings_create_common(f: "Any", *args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return f(*args, **kwargs) @@ -552,10 +553,8 @@ def _new_embeddings_create_common(f, *args, **kwargs): return response -def _wrap_embeddings_create(f): - # type: (Any) -> Any - def _execute_sync(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _wrap_embeddings_create(f: "Any") -> "Any": + def _execute_sync(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _new_embeddings_create_common(f, *args, **kwargs) try: @@ -575,8 +574,7 @@ def _execute_sync(f, *args, **kwargs): return e.value @wraps(f) - def _sentry_patched_create_sync(*args, **kwargs): - # type: (Any, Any) -> Any + def _sentry_patched_create_sync(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return f(*args, **kwargs) @@ -586,10 +584,8 @@ def _sentry_patched_create_sync(*args, **kwargs): return _sentry_patched_create_sync -def _wrap_async_embeddings_create(f): - # type: (Any) -> Any - async def _execute_async(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _wrap_async_embeddings_create(f: "Any") -> "Any": + async def _execute_async(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _new_embeddings_create_common(f, *args, **kwargs) try: @@ -609,8 +605,7 @@ async def _execute_async(f, *args, **kwargs): return e.value @wraps(f) - async def _sentry_patched_create_async(*args, **kwargs): - # type: (Any, Any) -> Any + async def _sentry_patched_create_async(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return await f(*args, **kwargs) @@ -620,8 +615,7 @@ async def _sentry_patched_create_async(*args, **kwargs): return _sentry_patched_create_async -def _new_responses_create_common(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _new_responses_create_common(f: "Any", *args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return f(*args, **kwargs) @@ -645,10 +639,8 @@ def _new_responses_create_common(f, *args, **kwargs): return response -def _wrap_responses_create(f): - # type: (Any) -> Any - def _execute_sync(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _wrap_responses_create(f: "Any") -> "Any": + def _execute_sync(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _new_responses_create_common(f, *args, **kwargs) try: @@ -668,8 +660,7 @@ def _execute_sync(f, *args, **kwargs): return e.value @wraps(f) - def _sentry_patched_create_sync(*args, **kwargs): - # type: (Any, Any) -> Any + def _sentry_patched_create_sync(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return f(*args, **kwargs) @@ -679,10 +670,8 @@ def _sentry_patched_create_sync(*args, **kwargs): return _sentry_patched_create_sync -def _wrap_async_responses_create(f): - # type: (Any) -> Any - async def _execute_async(f, *args, **kwargs): - # type: (Any, Any, Any) -> Any +def _wrap_async_responses_create(f: "Any") -> "Any": + async def _execute_async(f: Any, *args: Any, **kwargs: Any) -> Any: gen = _new_responses_create_common(f, *args, **kwargs) try: @@ -702,8 +691,7 @@ async def _execute_async(f, *args, **kwargs): return e.value @wraps(f) - async def _sentry_patched_responses_async(*args, **kwargs): - # type: (Any, Any) -> Any + async def _sentry_patched_responses_async(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(OpenAIIntegration) if integration is None: return await f(*args, **kwargs) @@ -713,8 +701,7 @@ async def _sentry_patched_responses_async(*args, **kwargs): return _sentry_patched_responses_async -def _is_given(obj): - # type: (Any) -> bool +def _is_given(obj: "Any") -> bool: """ Check for givenness safely across different openai versions. """ diff --git a/sentry_sdk/integrations/openai_agents/__init__.py b/sentry_sdk/integrations/openai_agents/__init__.py index 1f138b5e65..3ad7d2ee8d 100644 --- a/sentry_sdk/integrations/openai_agents/__init__.py +++ b/sentry_sdk/integrations/openai_agents/__init__.py @@ -21,8 +21,7 @@ raise DidNotEnable("OpenAI Agents not installed") -def _patch_runner(): - # type: () -> None +def _patch_runner() -> None: # Create the root span for one full agent run (including eventual handoffs) # Note agents.run.DEFAULT_AGENT_RUNNER.run_sync is a wrapper around # agents.run.DEFAULT_AGENT_RUNNER.run. It does not need to be wrapped separately. @@ -35,15 +34,13 @@ def _patch_runner(): _patch_agent_run() -def _patch_model(): - # type: () -> None +def _patch_model() -> None: agents.run.AgentRunner._get_model = classmethod( _create_get_model_wrapper(agents.run.AgentRunner._get_model), ) -def _patch_tools(): - # type: () -> None +def _patch_tools() -> None: agents.run.AgentRunner._get_all_tools = classmethod( _create_get_all_tools_wrapper(agents.run.AgentRunner._get_all_tools), ) @@ -53,8 +50,7 @@ class OpenAIAgentsIntegration(Integration): identifier = "openai_agents" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: _patch_error_tracing() _patch_tools() _patch_model() diff --git a/sentry_sdk/integrations/openai_agents/patches/agent_run.py b/sentry_sdk/integrations/openai_agents/patches/agent_run.py index 43944daa13..29649af945 100644 --- a/sentry_sdk/integrations/openai_agents/patches/agent_run.py +++ b/sentry_sdk/integrations/openai_agents/patches/agent_run.py @@ -23,8 +23,7 @@ raise DidNotEnable("OpenAI Agents not installed") -def _patch_agent_run(): - # type: () -> None +def _patch_agent_run() -> None: """ Patches AgentRunner methods to create agent invocation spans. This directly patches the execution flow to track when agents start and stop. @@ -35,8 +34,11 @@ def _patch_agent_run(): original_execute_handoffs = agents._run_impl.RunImpl.execute_handoffs original_execute_final_output = agents._run_impl.RunImpl.execute_final_output - def _start_invoke_agent_span(context_wrapper, agent, kwargs): - # type: (agents.RunContextWrapper, agents.Agent, dict[str, Any]) -> Span + def _start_invoke_agent_span( + context_wrapper: "agents.RunContextWrapper", + agent: "agents.Agent", + kwargs: "dict[str, Any]", + ) -> "Span": """Start an agent invocation span""" # Store the agent on the context wrapper so we can access it later context_wrapper._sentry_current_agent = agent @@ -45,13 +47,13 @@ def _start_invoke_agent_span(context_wrapper, agent, kwargs): return span - def _has_active_agent_span(context_wrapper): - # type: (agents.RunContextWrapper) -> bool + def _has_active_agent_span(context_wrapper: "agents.RunContextWrapper") -> bool: """Check if there's an active agent span for this context""" return getattr(context_wrapper, "_sentry_current_agent", None) is not None - def _get_current_agent(context_wrapper): - # type: (agents.RunContextWrapper) -> Optional[agents.Agent] + def _get_current_agent( + context_wrapper: "agents.RunContextWrapper", + ) -> "Optional[agents.Agent]": """Get the current agent from context wrapper""" return getattr(context_wrapper, "_sentry_current_agent", None) @@ -60,8 +62,9 @@ def _get_current_agent(context_wrapper): if hasattr(original_run_single_turn, "__func__") else original_run_single_turn ) - async def patched_run_single_turn(cls, *args, **kwargs): - # type: (agents.Runner, *Any, **Any) -> Any + async def patched_run_single_turn( + cls: "agents.Runner", *args: "Any", **kwargs: "Any" + ) -> "Any": """Patched _run_single_turn that creates agent invocation spans""" agent = kwargs.get("agent") context_wrapper = kwargs.get("context_wrapper") @@ -96,8 +99,9 @@ async def patched_run_single_turn(cls, *args, **kwargs): if hasattr(original_execute_handoffs, "__func__") else original_execute_handoffs ) - async def patched_execute_handoffs(cls, *args, **kwargs): - # type: (agents.Runner, *Any, **Any) -> Any + async def patched_execute_handoffs( + cls: "agents.Runner", *args: "Any", **kwargs: "Any" + ) -> "Any": """Patched execute_handoffs that creates handoff spans and ends agent span for handoffs""" context_wrapper = kwargs.get("context_wrapper") @@ -126,8 +130,9 @@ async def patched_execute_handoffs(cls, *args, **kwargs): if hasattr(original_execute_final_output, "__func__") else original_execute_final_output ) - async def patched_execute_final_output(cls, *args, **kwargs): - # type: (agents.Runner, *Any, **Any) -> Any + async def patched_execute_final_output( + cls: "agents.Runner", *args: "Any", **kwargs: "Any" + ) -> "Any": """Patched execute_final_output that ends agent span for final outputs""" agent = kwargs.get("agent") diff --git a/sentry_sdk/integrations/openai_agents/patches/error_tracing.py b/sentry_sdk/integrations/openai_agents/patches/error_tracing.py index 2695f8a753..8598d9c4fd 100644 --- a/sentry_sdk/integrations/openai_agents/patches/error_tracing.py +++ b/sentry_sdk/integrations/openai_agents/patches/error_tracing.py @@ -11,8 +11,7 @@ from typing import Any, Callable, Optional -def _patch_error_tracing(): - # type: () -> None +def _patch_error_tracing() -> None: """ Patches agents error tracing function to inject our span error logic when a tool execution fails. @@ -49,8 +48,9 @@ def _patch_error_tracing(): original_attach_error = error_tracing_module.attach_error_to_current_span @wraps(original_attach_error) - def sentry_attach_error_to_current_span(error, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_attach_error_to_current_span( + error: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": """ Wraps agents' error attachment to also set Sentry span status to error. This allows us to properly track tool execution errors even though diff --git a/sentry_sdk/integrations/openai_agents/patches/models.py b/sentry_sdk/integrations/openai_agents/patches/models.py index 8431ff3237..a9b3c16a22 100644 --- a/sentry_sdk/integrations/openai_agents/patches/models.py +++ b/sentry_sdk/integrations/openai_agents/patches/models.py @@ -18,8 +18,9 @@ raise DidNotEnable("OpenAI Agents not installed") -def _create_get_model_wrapper(original_get_model): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _create_get_model_wrapper( + original_get_model: "Callable[..., Any]", +) -> "Callable[..., Any]": """ Wraps the agents.Runner._get_model method to wrap the get_response method of the model to create a AI client span. """ @@ -29,9 +30,9 @@ def _create_get_model_wrapper(original_get_model): if hasattr(original_get_model, "__func__") else original_get_model ) - def wrapped_get_model(cls, agent, run_config): - # type: (agents.Runner, agents.Agent, agents.RunConfig) -> agents.Model - + def wrapped_get_model( + cls: "agents.Runner", agent: "agents.Agent", run_config: "agents.RunConfig" + ) -> "agents.Model": # copy the model to double patching its methods. We use copy on purpose here (instead of deepcopy) # because we only patch its direct methods, all underlying data can remain unchanged. model = copy.copy(original_get_model(agent, run_config)) @@ -41,8 +42,7 @@ def wrapped_get_model(cls, agent, run_config): original_fetch_response = model._fetch_response @wraps(original_fetch_response) - async def wrapped_fetch_response(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def wrapped_fetch_response(*args: "Any", **kwargs: "Any") -> "Any": response = await original_fetch_response(*args, **kwargs) if hasattr(response, "model"): agent._sentry_raw_response_model = str(response.model) @@ -53,8 +53,7 @@ async def wrapped_fetch_response(*args, **kwargs): original_get_response = model.get_response @wraps(original_get_response) - async def wrapped_get_response(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def wrapped_get_response(*args: "Any", **kwargs: "Any") -> "Any": with ai_client_span(agent, kwargs) as span: result = await original_get_response(*args, **kwargs) diff --git a/sentry_sdk/integrations/openai_agents/patches/runner.py b/sentry_sdk/integrations/openai_agents/patches/runner.py index 736a820d35..1d3bbc894b 100644 --- a/sentry_sdk/integrations/openai_agents/patches/runner.py +++ b/sentry_sdk/integrations/openai_agents/patches/runner.py @@ -17,8 +17,7 @@ from typing import Any, Callable -def _create_run_wrapper(original_func): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _create_run_wrapper(original_func: "Callable[..., Any]") -> "Callable[..., Any]": """ Wraps the agents.Runner.run methods to create a root span for the agent workflow runs. @@ -27,8 +26,7 @@ def _create_run_wrapper(original_func): """ @wraps(original_func) - async def wrapper(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def wrapper(*args: "Any", **kwargs: "Any") -> "Any": # Isolate each workflow so that when agents are run in asyncio tasks they # don't touch each other's scopes with sentry_sdk.isolation_scope(): diff --git a/sentry_sdk/integrations/openai_agents/patches/tools.py b/sentry_sdk/integrations/openai_agents/patches/tools.py index b359d32678..22de456de8 100644 --- a/sentry_sdk/integrations/openai_agents/patches/tools.py +++ b/sentry_sdk/integrations/openai_agents/patches/tools.py @@ -15,8 +15,9 @@ raise DidNotEnable("OpenAI Agents not installed") -def _create_get_all_tools_wrapper(original_get_all_tools): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _create_get_all_tools_wrapper( + original_get_all_tools: "Callable[..., Any]", +) -> "Callable[..., Any]": """ Wraps the agents.Runner._get_all_tools method of the Runner class to wrap all function tools with Sentry instrumentation. """ @@ -26,9 +27,11 @@ def _create_get_all_tools_wrapper(original_get_all_tools): if hasattr(original_get_all_tools, "__func__") else original_get_all_tools ) - async def wrapped_get_all_tools(cls, agent, context_wrapper): - # type: (agents.Runner, agents.Agent, agents.RunContextWrapper) -> list[agents.Tool] - + async def wrapped_get_all_tools( + cls: "agents.Runner", + agent: "agents.Agent", + context_wrapper: "agents.RunContextWrapper", + ) -> "list[agents.Tool]": # Get the original tools tools = await original_get_all_tools(agent, context_wrapper) @@ -42,11 +45,13 @@ async def wrapped_get_all_tools(cls, agent, context_wrapper): # Create a new FunctionTool with our wrapped invoke method original_on_invoke = tool.on_invoke_tool - def create_wrapped_invoke(current_tool, current_on_invoke): - # type: (agents.Tool, Callable[..., Any]) -> Callable[..., Any] + def create_wrapped_invoke( + current_tool: "agents.Tool", current_on_invoke: "Callable[..., Any]" + ) -> "Callable[..., Any]": @wraps(current_on_invoke) - async def sentry_wrapped_on_invoke_tool(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def sentry_wrapped_on_invoke_tool( + *args: Any, **kwargs: Any + ) -> Any: with execute_tool_span(current_tool, *args, **kwargs) as span: # We can not capture exceptions in tool execution here because # `_on_invoke_tool` is swallowing the exception here: diff --git a/sentry_sdk/integrations/openai_agents/spans/agent_workflow.py b/sentry_sdk/integrations/openai_agents/spans/agent_workflow.py index ef69b856e3..1734595f8e 100644 --- a/sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +++ b/sentry_sdk/integrations/openai_agents/spans/agent_workflow.py @@ -9,9 +9,7 @@ import agents -def agent_workflow_span(agent): - # type: (agents.Agent) -> sentry_sdk.tracing.Span - +def agent_workflow_span(agent: "agents.Agent") -> "sentry_sdk.tracing.Span": # Create a transaction or a span if an transaction is already active span = get_start_span_function()( name=f"{agent.name} workflow", diff --git a/sentry_sdk/integrations/openai_agents/spans/ai_client.py b/sentry_sdk/integrations/openai_agents/spans/ai_client.py index 8f233fbc14..1e188aa097 100644 --- a/sentry_sdk/integrations/openai_agents/spans/ai_client.py +++ b/sentry_sdk/integrations/openai_agents/spans/ai_client.py @@ -17,8 +17,9 @@ from typing import Any, Optional -def ai_client_span(agent, get_response_kwargs): - # type: (Agent, dict[str, Any]) -> sentry_sdk.tracing.Span +def ai_client_span( + agent: "Agent", get_response_kwargs: "dict[str, Any]" +) -> "sentry_sdk.tracing.Span": # TODO-anton: implement other types of operations. Now "chat" is hardcoded. model_name = agent.model.model if hasattr(agent.model, "model") else agent.model span = sentry_sdk.start_span( @@ -36,9 +37,12 @@ def ai_client_span(agent, get_response_kwargs): def update_ai_client_span( - span, agent, get_response_kwargs, result, response_model=None -): - # type: (sentry_sdk.tracing.Span, Agent, dict[str, Any], Any, Optional[str]) -> None + span: "sentry_sdk.tracing.Span", + agent: "Agent", + get_response_kwargs: "dict[str, Any]", + result: "Any", + response_model: "Optional[str]" = None, +) -> None: _set_usage_data(span, result.usage) _set_output_data(span, result) _create_mcp_execute_tool_spans(span, result) diff --git a/sentry_sdk/integrations/openai_agents/spans/execute_tool.py b/sentry_sdk/integrations/openai_agents/spans/execute_tool.py index 5f9e4cb340..aa89da1610 100644 --- a/sentry_sdk/integrations/openai_agents/spans/execute_tool.py +++ b/sentry_sdk/integrations/openai_agents/spans/execute_tool.py @@ -12,8 +12,9 @@ from typing import Any -def execute_tool_span(tool, *args, **kwargs): - # type: (agents.Tool, *Any, **Any) -> sentry_sdk.tracing.Span +def execute_tool_span( + tool: "agents.Tool", *args: "Any", **kwargs: "Any" +) -> "sentry_sdk.tracing.Span": span = sentry_sdk.start_span( op=OP.GEN_AI_EXECUTE_TOOL, name=f"execute_tool {tool.name}", @@ -35,8 +36,12 @@ def execute_tool_span(tool, *args, **kwargs): return span -def update_execute_tool_span(span, agent, tool, result): - # type: (sentry_sdk.tracing.Span, agents.Agent, agents.Tool, Any) -> None +def update_execute_tool_span( + span: "sentry_sdk.tracing.Span", + agent: "agents.Agent", + tool: "agents.Tool", + result: "Any", +) -> None: _set_agent_data(span, agent) if isinstance(result, str) and result.startswith( diff --git a/sentry_sdk/integrations/openai_agents/spans/handoff.py b/sentry_sdk/integrations/openai_agents/spans/handoff.py index 78e6788c7d..c514505b17 100644 --- a/sentry_sdk/integrations/openai_agents/spans/handoff.py +++ b/sentry_sdk/integrations/openai_agents/spans/handoff.py @@ -9,8 +9,9 @@ import agents -def handoff_span(context, from_agent, to_agent_name): - # type: (agents.RunContextWrapper, agents.Agent, str) -> None +def handoff_span( + context: "agents.RunContextWrapper", from_agent: "agents.Agent", to_agent_name: str +) -> None: with sentry_sdk.start_span( op=OP.GEN_AI_HANDOFF, name=f"handoff from {from_agent.name} to {to_agent_name}", diff --git a/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py b/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py index f6311a2150..c3a3a04dc9 100644 --- a/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +++ b/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py @@ -19,8 +19,9 @@ from typing import Any, Optional -def invoke_agent_span(context, agent, kwargs): - # type: (agents.RunContextWrapper, agents.Agent, dict[str, Any]) -> sentry_sdk.tracing.Span +def invoke_agent_span( + context: "agents.RunContextWrapper", agent: "agents.Agent", kwargs: "dict[str, Any]" +) -> "sentry_sdk.tracing.Span": start_span_function = get_start_span_function() span = start_span_function( op=OP.GEN_AI_INVOKE_AGENT, @@ -79,8 +80,9 @@ def invoke_agent_span(context, agent, kwargs): return span -def update_invoke_agent_span(context, agent, output): - # type: (agents.RunContextWrapper, agents.Agent, Any) -> None +def update_invoke_agent_span( + context: "agents.RunContextWrapper", agent: "agents.Agent", output: "Any" +) -> None: span = getattr(context, "_sentry_agent_span", None) if span: @@ -97,8 +99,11 @@ def update_invoke_agent_span(context, agent, output): delattr(context, "_sentry_agent_span") -def end_invoke_agent_span(context_wrapper, agent, output=None): - # type: (agents.RunContextWrapper, agents.Agent, Optional[Any]) -> None +def end_invoke_agent_span( + context_wrapper: "agents.RunContextWrapper", + agent: "agents.Agent", + output: "Optional[Any]" = None, +) -> None: """End the agent invocation span""" # Clear the stored agent if hasattr(context_wrapper, "_sentry_current_agent"): diff --git a/sentry_sdk/integrations/openai_agents/utils.py b/sentry_sdk/integrations/openai_agents/utils.py index be325c6951..a24d0e909d 100644 --- a/sentry_sdk/integrations/openai_agents/utils.py +++ b/sentry_sdk/integrations/openai_agents/utils.py @@ -27,8 +27,7 @@ raise DidNotEnable("OpenAI Agents not installed") -def _capture_exception(exc): - # type: (Any) -> None +def _capture_exception(exc: "Any") -> None: set_span_errored() event, hint = event_from_exception( @@ -39,8 +38,7 @@ def _capture_exception(exc): sentry_sdk.capture_event(event, hint=hint) -def _record_exception_on_span(span, error): - # type: (Span, Exception) -> Any +def _record_exception_on_span(span: "Span", error: Exception) -> "Any": set_span_errored(span) span.set_data("span.status", "error") @@ -53,8 +51,7 @@ def _record_exception_on_span(span, error): span.set_data("error.message", error_message) -def _set_agent_data(span, agent): - # type: (sentry_sdk.tracing.Span, agents.Agent) -> None +def _set_agent_data(span: "sentry_sdk.tracing.Span", agent: "agents.Agent") -> None: span.set_data( SPANDATA.GEN_AI_SYSTEM, "openai" ) # See footnote for https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/#gen-ai-system for explanation why. @@ -97,8 +94,7 @@ def _set_agent_data(span, agent): ) -def _set_usage_data(span, usage): - # type: (sentry_sdk.tracing.Span, Usage) -> None +def _set_usage_data(span: "sentry_sdk.tracing.Span", usage: "Usage") -> None: span.set_data(SPANDATA.GEN_AI_USAGE_INPUT_TOKENS, usage.input_tokens) span.set_data( SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED, @@ -112,8 +108,9 @@ def _set_usage_data(span, usage): span.set_data(SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS, usage.total_tokens) -def _set_input_data(span, get_response_kwargs): - # type: (sentry_sdk.tracing.Span, dict[str, Any]) -> None +def _set_input_data( + span: "sentry_sdk.tracing.Span", get_response_kwargs: "dict[str, Any]" +) -> None: if not should_send_default_pii(): return request_messages = [] @@ -169,15 +166,14 @@ def _set_input_data(span, get_response_kwargs): ) -def _set_output_data(span, result): - # type: (sentry_sdk.tracing.Span, Any) -> None +def _set_output_data(span: "sentry_sdk.tracing.Span", result: "Any") -> None: if not should_send_default_pii(): return - output_messages = { + output_messages: "dict[str, list[Any]]" = { "response": [], "tool": [], - } # type: (dict[str, list[Any]]) + } for output in result.output: if output.type == "function_call": @@ -201,8 +197,9 @@ def _set_output_data(span, result): ) -def _create_mcp_execute_tool_spans(span, result): - # type: (sentry_sdk.tracing.Span, agents.Result) -> None +def _create_mcp_execute_tool_spans( + span: "sentry_sdk.tracing.Span", result: "agents.Result" +) -> None: for output in result.output: if output.__class__.__name__ == "McpCall": with sentry_sdk.start_span( diff --git a/sentry_sdk/integrations/openfeature.py b/sentry_sdk/integrations/openfeature.py index 3ac73edd93..281604fe38 100644 --- a/sentry_sdk/integrations/openfeature.py +++ b/sentry_sdk/integrations/openfeature.py @@ -17,19 +17,18 @@ class OpenFeatureIntegration(Integration): identifier = "openfeature" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: # Register the hook within the global openfeature hooks list. api.add_hooks(hooks=[OpenFeatureHook()]) class OpenFeatureHook(Hook): - def after(self, hook_context, details, hints): - # type: (Any, Any, Any) -> None + def after(self, hook_context: "Any", details: "Any", hints: "Any") -> None: if isinstance(details.value, bool): add_feature_flag(details.flag_key, details.value) - def error(self, hook_context, exception, hints): - # type: (HookContext, Exception, HookHints) -> None + def error( + self, hook_context: "HookContext", exception: Exception, hints: "HookHints" + ) -> None: if isinstance(hook_context.default_value, bool): add_feature_flag(hook_context.flag_key, hook_context.default_value) diff --git a/sentry_sdk/integrations/opentelemetry/integration.py b/sentry_sdk/integrations/opentelemetry/integration.py index 43e0396c16..83588a2b38 100644 --- a/sentry_sdk/integrations/opentelemetry/integration.py +++ b/sentry_sdk/integrations/opentelemetry/integration.py @@ -31,8 +31,7 @@ class OpenTelemetryIntegration(Integration): identifier = "opentelemetry" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: logger.warning( "[OTel] Initializing highly experimental OpenTelemetry support. " "Use at your own risk." @@ -44,15 +43,13 @@ def setup_once(): logger.debug("[OTel] Finished setting up OpenTelemetry integration") -def _setup_sentry_tracing(): - # type: () -> None +def _setup_sentry_tracing() -> None: provider = TracerProvider() provider.add_span_processor(SentrySpanProcessor()) trace.set_tracer_provider(provider) set_global_textmap(SentryPropagator()) -def _setup_instrumentors(): - # type: () -> None +def _setup_instrumentors() -> None: for instrumentor, kwargs in CONFIGURABLE_INSTRUMENTATIONS.items(): instrumentor().instrument(**kwargs) diff --git a/sentry_sdk/integrations/opentelemetry/propagator.py b/sentry_sdk/integrations/opentelemetry/propagator.py index b84d582d6e..98b735c5e0 100644 --- a/sentry_sdk/integrations/opentelemetry/propagator.py +++ b/sentry_sdk/integrations/opentelemetry/propagator.py @@ -42,8 +42,12 @@ class SentryPropagator(TextMapPropagator): Propagates tracing headers for Sentry's tracing system in a way OTel understands. """ - def extract(self, carrier, context=None, getter=default_getter): - # type: (CarrierT, Optional[Context], Getter[CarrierT]) -> Context + def extract( + self, + carrier: "CarrierT", + context: "Optional[Context]" = None, + getter: "Getter[CarrierT]" = default_getter, + ) -> "Context": if context is None: context = get_current() @@ -84,8 +88,12 @@ def extract(self, carrier, context=None, getter=default_getter): modified_context = trace.set_span_in_context(span, context) return modified_context - def inject(self, carrier, context=None, setter=default_setter): - # type: (CarrierT, Optional[Context], Setter[CarrierT]) -> None + def inject( + self, + carrier: "CarrierT", + context: "Optional[Context]" = None, + setter: "Setter[CarrierT]" = default_setter, + ) -> None: if context is None: context = get_current() @@ -112,6 +120,5 @@ def inject(self, carrier, context=None, setter=default_setter): setter.set(carrier, BAGGAGE_HEADER_NAME, baggage_data) @property - def fields(self): - # type: () -> Set[str] + def fields(self) -> "Set[str]": return {SENTRY_TRACE_HEADER_NAME, BAGGAGE_HEADER_NAME} diff --git a/sentry_sdk/integrations/opentelemetry/span_processor.py b/sentry_sdk/integrations/opentelemetry/span_processor.py index 32142b640b..8f167585f4 100644 --- a/sentry_sdk/integrations/opentelemetry/span_processor.py +++ b/sentry_sdk/integrations/opentelemetry/span_processor.py @@ -36,8 +36,9 @@ SPAN_ORIGIN = "auto.otel" -def link_trace_context_to_error_event(event, otel_span_map): - # type: (Event, dict[str, Union[Transaction, SentrySpan]]) -> Event +def link_trace_context_to_error_event( + event: "Event", otel_span_map: "dict[str, Union[Transaction, SentrySpan]]" +) -> "Event": client = get_client() if client.options["instrumenter"] != INSTRUMENTER.OTEL: @@ -71,27 +72,23 @@ class SentrySpanProcessor(SpanProcessor): """ # The mapping from otel span ids to sentry spans - otel_span_map = {} # type: dict[str, Union[Transaction, SentrySpan]] + otel_span_map: "dict[str, Union[Transaction, SentrySpan]]" = {} # The currently open spans. Elements will be discarded after SPAN_MAX_TIME_OPEN_MINUTES - open_spans = {} # type: dict[int, set[str]] + open_spans: "dict[int, set[str]]" = {} - def __new__(cls): - # type: () -> SentrySpanProcessor + def __new__(cls) -> "SentrySpanProcessor": if not hasattr(cls, "instance"): cls.instance = super().__new__(cls) return cls.instance - def __init__(self): - # type: () -> None + def __init__(self) -> None: @add_global_event_processor - def global_event_processor(event, hint): - # type: (Event, Hint) -> Event + def global_event_processor(event: Event, hint: Hint) -> Event: return link_trace_context_to_error_event(event, self.otel_span_map) - def _prune_old_spans(self): - # type: (SentrySpanProcessor) -> None + def _prune_old_spans(self: "SentrySpanProcessor") -> None: """ Prune spans that have been open for too long. """ @@ -108,8 +105,11 @@ def _prune_old_spans(self): for span_id in self.open_spans.pop(span_start_minutes): self.otel_span_map.pop(span_id, None) - def on_start(self, otel_span, parent_context=None): - # type: (OTelSpan, Optional[context_api.Context]) -> None + def on_start( + self, + otel_span: "OTelSpan", + parent_context: "Optional[context_api.Context]" = None, + ) -> None: client = get_client() if not client.parsed_dsn: @@ -170,8 +170,7 @@ def on_start(self, otel_span, parent_context=None): self._prune_old_spans() - def on_end(self, otel_span): - # type: (OTelSpan) -> None + def on_end(self, otel_span: "OTelSpan") -> None: client = get_client() if client.options["instrumenter"] != INSTRUMENTER.OTEL: @@ -216,8 +215,7 @@ def on_end(self, otel_span): self._prune_old_spans() - def _is_sentry_span(self, otel_span): - # type: (OTelSpan) -> bool + def _is_sentry_span(self, otel_span: "OTelSpan") -> bool: """ Break infinite loop: HTTP requests to Sentry are caught by OTel and send again to Sentry. @@ -235,8 +233,7 @@ def _is_sentry_span(self, otel_span): return False - def _get_otel_context(self, otel_span): - # type: (OTelSpan) -> dict[str, Any] + def _get_otel_context(self, otel_span: "OTelSpan") -> "dict[str, Any]": """ Returns the OTel context for Sentry. See: https://develop.sentry.dev/sdk/performance/opentelemetry/#step-5-add-opentelemetry-context @@ -251,12 +248,13 @@ def _get_otel_context(self, otel_span): return ctx - def _get_trace_data(self, otel_span, parent_context): - # type: (OTelSpan, Optional[context_api.Context]) -> dict[str, Any] + def _get_trace_data( + self, otel_span: "OTelSpan", parent_context: "Optional[context_api.Context]" + ) -> "dict[str, Any]": """ Extracts tracing information from one OTel span and its parent OTel context. """ - trace_data = {} # type: dict[str, Any] + trace_data: "dict[str, Any]" = {} span_context = otel_span.get_span_context() span_id = format_span_id(span_context.span_id) @@ -281,8 +279,9 @@ def _get_trace_data(self, otel_span, parent_context): return trace_data - def _update_span_with_otel_status(self, sentry_span, otel_span): - # type: (SentrySpan, OTelSpan) -> None + def _update_span_with_otel_status( + self, sentry_span: "SentrySpan", otel_span: "OTelSpan" + ) -> None: """ Set the Sentry span status from the OTel span """ @@ -295,8 +294,9 @@ def _update_span_with_otel_status(self, sentry_span, otel_span): sentry_span.set_status(SPANSTATUS.INTERNAL_ERROR) - def _update_span_with_otel_data(self, sentry_span, otel_span): - # type: (SentrySpan, OTelSpan) -> None + def _update_span_with_otel_data( + self, sentry_span: "SentrySpan", otel_span: "OTelSpan" + ) -> None: """ Convert OTel span data and update the Sentry span with it. This should eventually happen on the server when ingesting the spans. @@ -360,8 +360,9 @@ def _update_span_with_otel_data(self, sentry_span, otel_span): sentry_span.op = op sentry_span.description = description - def _update_transaction_with_otel_data(self, sentry_span, otel_span): - # type: (SentrySpan, OTelSpan) -> None + def _update_transaction_with_otel_data( + self, sentry_span: "SentrySpan", otel_span: "OTelSpan" + ) -> None: if otel_span.attributes is None: return diff --git a/sentry_sdk/integrations/otlp.py b/sentry_sdk/integrations/otlp.py index 046ce916f6..5d5c4a6e04 100644 --- a/sentry_sdk/integrations/otlp.py +++ b/sentry_sdk/integrations/otlp.py @@ -20,8 +20,7 @@ from typing import Optional, Dict, Any, Tuple -def otel_propagation_context(): - # type: () -> Optional[Tuple[str, str]] +def otel_propagation_context() -> "Optional[Tuple[str, str]]": """ Get the (trace_id, span_id) from opentelemetry if exists. """ @@ -33,8 +32,7 @@ def otel_propagation_context(): return (trace.format_trace_id(ctx.trace_id), trace.format_span_id(ctx.span_id)) -def setup_otlp_traces_exporter(dsn=None): - # type: (Optional[str]) -> None +def setup_otlp_traces_exporter(dsn: "Optional[str]" = None) -> None: tracer_provider = trace.get_tracer_provider() if not isinstance(tracer_provider, TracerProvider): @@ -58,22 +56,23 @@ def setup_otlp_traces_exporter(dsn=None): class OTLPIntegration(Integration): identifier = "otlp" - def __init__(self, setup_otlp_traces_exporter=True, setup_propagator=True): - # type: (bool, bool) -> None + def __init__( + self, setup_otlp_traces_exporter: bool = True, setup_propagator: bool = True + ) -> None: self.setup_otlp_traces_exporter = setup_otlp_traces_exporter self.setup_propagator = setup_propagator @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: logger.debug("[OTLP] Setting up trace linking for all events") register_external_propagation_context(otel_propagation_context) - def setup_once_with_options(self, options=None): - # type: (Optional[Dict[str, Any]]) -> None + def setup_once_with_options( + self, options: "Optional[Dict[str, Any]]" = None + ) -> None: if self.setup_otlp_traces_exporter: logger.debug("[OTLP] Setting up OTLP exporter") - dsn = options.get("dsn") if options else None # type: Optional[str] + dsn: "Optional[str]" = options.get("dsn") if options else None setup_otlp_traces_exporter(dsn) if self.setup_propagator: diff --git a/sentry_sdk/integrations/pure_eval.py b/sentry_sdk/integrations/pure_eval.py index 6ac10dfe1b..0236f418ce 100644 --- a/sentry_sdk/integrations/pure_eval.py +++ b/sentry_sdk/integrations/pure_eval.py @@ -35,12 +35,9 @@ class PureEvalIntegration(Integration): identifier = "pure_eval" @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: @add_global_event_processor - def add_executing_info(event, hint): - # type: (Event, Optional[Hint]) -> Optional[Event] + def add_executing_info(event: Event, hint: Optional[Hint]) -> Optional[Event]: if sentry_sdk.get_client().get_integration(PureEvalIntegration) is None: return event @@ -81,8 +78,7 @@ def add_executing_info(event, hint): return event -def pure_eval_frame(frame): - # type: (FrameType) -> Dict[str, Any] +def pure_eval_frame(frame: "FrameType") -> "Dict[str, Any]": source = executing.Source.for_frame(frame) if not source.tree: return {} @@ -103,16 +99,14 @@ def pure_eval_frame(frame): evaluator = pure_eval.Evaluator.from_frame(frame) expressions = evaluator.interesting_expressions_grouped(scope) - def closeness(expression): - # type: (Tuple[List[Any], Any]) -> Tuple[int, int] + def closeness(expression: "Tuple[List[Any], Any]") -> "Tuple[int, int]": # Prioritise expressions with a node closer to the statement executed # without being after that statement # A higher return value is better - the expression will appear # earlier in the list of values and is less likely to be trimmed nodes, _value = expression - def start(n): - # type: (ast.expr) -> Tuple[int, int] + def start(n: "ast.expr") -> "Tuple[int, int]": return (n.lineno, n.col_offset) nodes_before_stmt = [ diff --git a/sentry_sdk/integrations/pydantic_ai/__init__.py b/sentry_sdk/integrations/pydantic_ai/__init__.py index 9fccafd6d2..11dd171944 100644 --- a/sentry_sdk/integrations/pydantic_ai/__init__.py +++ b/sentry_sdk/integrations/pydantic_ai/__init__.py @@ -19,8 +19,7 @@ class PydanticAIIntegration(Integration): identifier = "pydantic_ai" origin = f"auto.ai.{identifier}" - def __init__(self, include_prompts=True): - # type: (bool) -> None + def __init__(self, include_prompts: bool = True) -> None: """ Initialize the Pydantic AI integration. @@ -31,8 +30,7 @@ def __init__(self, include_prompts=True): self.include_prompts = include_prompts @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: """ Set up the pydantic-ai integration. diff --git a/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py b/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py index cceb11fc90..d158d892d5 100644 --- a/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +++ b/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py @@ -22,26 +22,24 @@ class _StreamingContextManagerWrapper: def __init__( self, - agent, - original_ctx_manager, - user_prompt, - model, - model_settings, - is_streaming=True, - ): - # type: (Any, Any, Any, Any, Any, bool) -> None + agent: "Any", + original_ctx_manager: "Any", + user_prompt: "Any", + model: "Any", + model_settings: "Any", + is_streaming: bool = True, + ) -> None: self.agent = agent self.original_ctx_manager = original_ctx_manager self.user_prompt = user_prompt self.model = model self.model_settings = model_settings self.is_streaming = is_streaming - self._isolation_scope = None # type: Any - self._span = None # type: Optional[sentry_sdk.tracing.Span] - self._result = None # type: Any + self._isolation_scope: "Any" = None + self._span: "Optional[sentry_sdk.tracing.Span]" = None + self._result: "Any" = None - async def __aenter__(self): - # type: () -> Any + async def __aenter__(self) -> "Any": # Set up isolation scope and invoke_agent span self._isolation_scope = sentry_sdk.isolation_scope() self._isolation_scope.__enter__() @@ -65,8 +63,7 @@ async def __aenter__(self): self._result = result return result - async def __aexit__(self, exc_type, exc_val, exc_tb): - # type: (Any, Any, Any) -> None + async def __aexit__(self, exc_type: "Any", exc_val: "Any", exc_tb: "Any") -> None: try: # Exit the original context manager first await self.original_ctx_manager.__aexit__(exc_type, exc_val, exc_tb) @@ -87,8 +84,9 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): self._isolation_scope.__exit__(exc_type, exc_val, exc_tb) -def _create_run_wrapper(original_func, is_streaming=False): - # type: (Callable[..., Any], bool) -> Callable[..., Any] +def _create_run_wrapper( + original_func: "Callable[..., Any]", is_streaming: bool = False +) -> "Callable[..., Any]": """ Wraps the Agent.run method to create an invoke_agent span. @@ -98,8 +96,7 @@ def _create_run_wrapper(original_func, is_streaming=False): """ @wraps(original_func) - async def wrapper(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + async def wrapper(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": # Isolate each workflow so that when agents are run in asyncio tasks they # don't touch each other's scopes with sentry_sdk.isolation_scope(): @@ -133,15 +130,15 @@ async def wrapper(self, *args, **kwargs): return wrapper -def _create_streaming_wrapper(original_func): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _create_streaming_wrapper( + original_func: "Callable[..., Any]", +) -> "Callable[..., Any]": """ Wraps run_stream method that returns an async context manager. """ @wraps(original_func) - def wrapper(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def wrapper(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": # Extract parameters for the span user_prompt = kwargs.get("user_prompt") or (args[0] if args else None) model = kwargs.get("model") @@ -163,8 +160,9 @@ def wrapper(self, *args, **kwargs): return wrapper -def _create_streaming_events_wrapper(original_func): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _create_streaming_events_wrapper( + original_func: "Callable[..., Any]", +) -> "Callable[..., Any]": """ Wraps run_stream_events method - no span needed as it delegates to run(). @@ -173,8 +171,7 @@ def _create_streaming_events_wrapper(original_func): """ @wraps(original_func) - async def wrapper(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + async def wrapper(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": # Just call the original generator - it will call run() which has the instrumentation try: async for event in original_func(self, *args, **kwargs): @@ -186,8 +183,7 @@ async def wrapper(self, *args, **kwargs): return wrapper -def _patch_agent_run(): - # type: () -> None +def _patch_agent_run() -> None: """ Patches the Agent run methods to create spans for agent execution. diff --git a/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py b/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py index 6de4c3e80a..56e46d869f 100644 --- a/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +++ b/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py @@ -20,8 +20,7 @@ from typing import Any, Callable -def _extract_span_data(node, ctx): - # type: (Any, Any) -> tuple[list[Any], Any, Any] +def _extract_span_data(node: "Any", ctx: "Any") -> "tuple[list[Any], Any, Any]": """Extract common data needed for creating chat spans. Returns: @@ -46,8 +45,7 @@ def _extract_span_data(node, ctx): return messages, model, model_settings -def _patch_graph_nodes(): - # type: () -> None +def _patch_graph_nodes() -> None: """ Patches the graph node execution to create appropriate spans. @@ -59,8 +57,7 @@ def _patch_graph_nodes(): original_model_request_run = ModelRequestNode.run @wraps(original_model_request_run) - async def wrapped_model_request_run(self, ctx): - # type: (Any, Any) -> Any + async def wrapped_model_request_run(self: "Any", ctx: "Any") -> "Any": messages, model, model_settings = _extract_span_data(self, ctx) with ai_client_span(messages, None, model, model_settings) as span: @@ -79,14 +76,14 @@ async def wrapped_model_request_run(self, ctx): # Patch ModelRequestNode.stream for streaming requests original_model_request_stream = ModelRequestNode.stream - def create_wrapped_stream(original_stream_method): - # type: (Callable[..., Any]) -> Callable[..., Any] + def create_wrapped_stream( + original_stream_method: "Callable[..., Any]", + ) -> "Callable[..., Any]": """Create a wrapper for ModelRequestNode.stream that creates chat spans.""" @asynccontextmanager @wraps(original_stream_method) - async def wrapped_model_request_stream(self, ctx): - # type: (Any, Any) -> Any + async def wrapped_model_request_stream(self: "Any", ctx: "Any") -> "Any": messages, model, model_settings = _extract_span_data(self, ctx) # Create chat span for streaming request diff --git a/sentry_sdk/integrations/pydantic_ai/patches/model_request.py b/sentry_sdk/integrations/pydantic_ai/patches/model_request.py index f33a031b07..94a96161f3 100644 --- a/sentry_sdk/integrations/pydantic_ai/patches/model_request.py +++ b/sentry_sdk/integrations/pydantic_ai/patches/model_request.py @@ -15,8 +15,7 @@ from typing import Any -def _patch_model_request(): - # type: () -> None +def _patch_model_request() -> None: """ Patches model request execution to create AI client spans. @@ -29,8 +28,9 @@ def _patch_model_request(): original_request = models.Model.request @wraps(original_request) - async def wrapped_request(self, messages, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> Any + async def wrapped_request( + self: "Any", messages: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": # Pass all messages (full conversation history) with ai_client_span(messages, None, self, None) as span: result = await original_request(self, messages, *args, **kwargs) diff --git a/sentry_sdk/integrations/pydantic_ai/patches/tools.py b/sentry_sdk/integrations/pydantic_ai/patches/tools.py index e3872a5bbc..e4251d671c 100644 --- a/sentry_sdk/integrations/pydantic_ai/patches/tools.py +++ b/sentry_sdk/integrations/pydantic_ai/patches/tools.py @@ -27,8 +27,7 @@ raise DidNotEnable("pydantic-ai not installed") -def _patch_tool_execution(): - # type: () -> None +def _patch_tool_execution() -> None: """ Patch ToolManager._call_tool to create execute_tool spans. @@ -44,9 +43,9 @@ def _patch_tool_execution(): original_call_tool = ToolManager._call_tool @wraps(original_call_tool) - async def wrapped_call_tool(self, call, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> Any - + async def wrapped_call_tool( + self: "Any", call: "Any", *args: "Any", **kwargs: "Any" + ) -> "Any": # Extract tool info before calling original name = call.tool_name tool = self.tools.get(name) if self.tools else None diff --git a/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py b/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py index b3749b16c9..cb34f36e4f 100644 --- a/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +++ b/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py @@ -40,8 +40,7 @@ ThinkingPart = None -def _set_input_messages(span, messages): - # type: (sentry_sdk.tracing.Span, Any) -> None +def _set_input_messages(span: "sentry_sdk.tracing.Span", messages: "Any") -> None: """Set input messages data on a span.""" if not _should_send_prompts(): return @@ -81,7 +80,7 @@ def _set_input_messages(span, messages): elif BaseToolReturnPart and isinstance(part, BaseToolReturnPart): role = "tool" - content = [] # type: List[Dict[str, Any] | str] + content: "List[Dict[str, Any] | str]" = [] tool_calls = None tool_call_id = None @@ -115,7 +114,7 @@ def _set_input_messages(span, messages): # Add message if we have content or tool calls if content or tool_calls: - message = {"role": role} # type: Dict[str, Any] + message: "Dict[str, Any]" = {"role": role} if content: message["content"] = content if tool_calls: @@ -133,8 +132,7 @@ def _set_input_messages(span, messages): pass -def _set_output_data(span, response): - # type: (sentry_sdk.tracing.Span, Any) -> None +def _set_output_data(span: "sentry_sdk.tracing.Span", response: "Any") -> None: """Set output data on a span.""" if not _should_send_prompts(): return @@ -175,8 +173,9 @@ def _set_output_data(span, response): pass -def ai_client_span(messages, agent, model, model_settings): - # type: (Any, Any, Any, Any) -> sentry_sdk.tracing.Span +def ai_client_span( + messages: "Any", agent: "Any", model: "Any", model_settings: "Any" +) -> "sentry_sdk.tracing.Span": """Create a span for an AI client call (model request). Args: @@ -217,8 +216,9 @@ def ai_client_span(messages, agent, model, model_settings): return span -def update_ai_client_span(span, model_response): - # type: (sentry_sdk.tracing.Span, Any) -> None +def update_ai_client_span( + span: "sentry_sdk.tracing.Span", model_response: "Any" +) -> None: """Update the AI client span with response data.""" if not span: return diff --git a/sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py b/sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py index 329895778d..cc18302f87 100644 --- a/sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +++ b/sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py @@ -11,8 +11,9 @@ from typing import Any, Optional -def execute_tool_span(tool_name, tool_args, agent, tool_type="function"): - # type: (str, Any, Any, str) -> sentry_sdk.tracing.Span +def execute_tool_span( + tool_name: str, tool_args: "Any", agent: "Any", tool_type: str = "function" +) -> "sentry_sdk.tracing.Span": """Create a span for tool execution. Args: @@ -39,8 +40,7 @@ def execute_tool_span(tool_name, tool_args, agent, tool_type="function"): return span -def update_execute_tool_span(span, result): - # type: (sentry_sdk.tracing.Span, Any) -> None +def update_execute_tool_span(span: "sentry_sdk.tracing.Span", result: "Any") -> None: """Update the execute tool span with the result.""" if not span: return diff --git a/sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py b/sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py index ee451b7e6b..629b3d1206 100644 --- a/sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +++ b/sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py @@ -17,8 +17,13 @@ from typing import Any -def invoke_agent_span(user_prompt, agent, model, model_settings, is_streaming=False): - # type: (Any, Any, Any, Any, bool) -> sentry_sdk.tracing.Span +def invoke_agent_span( + user_prompt: "Any", + agent: "Any", + model: "Any", + model_settings: "Any", + is_streaming: bool = False, +) -> "sentry_sdk.tracing.Span": """Create a span for invoking the agent.""" # Determine agent name for span name = "agent" @@ -104,8 +109,7 @@ def invoke_agent_span(user_prompt, agent, model, model_settings, is_streaming=Fa return span -def update_invoke_agent_span(span, result): - # type: (sentry_sdk.tracing.Span, Any) -> None +def update_invoke_agent_span(span: "sentry_sdk.tracing.Span", result: "Any") -> None: """Update and close the invoke agent span.""" if not span or not result: return diff --git a/sentry_sdk/integrations/pydantic_ai/spans/utils.py b/sentry_sdk/integrations/pydantic_ai/spans/utils.py index f5251622de..c70afd5f31 100644 --- a/sentry_sdk/integrations/pydantic_ai/spans/utils.py +++ b/sentry_sdk/integrations/pydantic_ai/spans/utils.py @@ -10,8 +10,9 @@ from pydantic_ai.usage import RequestUsage, RunUsage # type: ignore -def _set_usage_data(span, usage): - # type: (sentry_sdk.tracing.Span, Union[RequestUsage, RunUsage]) -> None +def _set_usage_data( + span: "sentry_sdk.tracing.Span", usage: "Union[RequestUsage, RunUsage]" +) -> None: """Set token usage data on a span. This function works with both RequestUsage (single request) and diff --git a/sentry_sdk/integrations/pydantic_ai/utils.py b/sentry_sdk/integrations/pydantic_ai/utils.py index 532fb7ddb6..743f3078f2 100644 --- a/sentry_sdk/integrations/pydantic_ai/utils.py +++ b/sentry_sdk/integrations/pydantic_ai/utils.py @@ -13,19 +13,19 @@ # Store the current agent context in a contextvar for re-entrant safety # Using a list as a stack to support nested agent calls -_agent_context_stack = ContextVar("pydantic_ai_agent_context_stack", default=[]) # type: ContextVar[list[dict[str, Any]]] +_agent_context_stack: "ContextVar[list[dict[str, Any]]]" = ContextVar( + "pydantic_ai_agent_context_stack", default=[] +) -def push_agent(agent, is_streaming=False): - # type: (Any, bool) -> None +def push_agent(agent: "Any", is_streaming: bool = False) -> None: """Push an agent context onto the stack along with its streaming flag.""" stack = _agent_context_stack.get().copy() stack.append({"agent": agent, "is_streaming": is_streaming}) _agent_context_stack.set(stack) -def pop_agent(): - # type: () -> None +def pop_agent() -> None: """Pop an agent context from the stack.""" stack = _agent_context_stack.get().copy() if stack: @@ -33,8 +33,7 @@ def pop_agent(): _agent_context_stack.set(stack) -def get_current_agent(): - # type: () -> Any +def get_current_agent() -> "Any": """Get the current agent from the contextvar stack.""" stack = _agent_context_stack.get() if stack: @@ -42,8 +41,7 @@ def get_current_agent(): return None -def get_is_streaming(): - # type: () -> bool +def get_is_streaming() -> bool: """Get the streaming flag from the contextvar stack.""" stack = _agent_context_stack.get() if stack: @@ -51,8 +49,7 @@ def get_is_streaming(): return False -def _should_send_prompts(): - # type: () -> bool +def _should_send_prompts() -> bool: """ Check if prompts should be sent to Sentry. @@ -72,8 +69,7 @@ def _should_send_prompts(): return getattr(integration, "include_prompts", False) -def _set_agent_data(span, agent): - # type: (sentry_sdk.tracing.Span, Any) -> None +def _set_agent_data(span: "sentry_sdk.tracing.Span", agent: "Any") -> None: """Set agent-related data on a span. Args: @@ -90,8 +86,7 @@ def _set_agent_data(span, agent): span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_obj.name) -def _get_model_name(model_obj): - # type: (Any) -> Optional[str] +def _get_model_name(model_obj: "Any") -> "Optional[str]": """Extract model name from a model object. Args: @@ -116,8 +111,9 @@ def _get_model_name(model_obj): return str(model_obj) -def _set_model_data(span, model, model_settings): - # type: (sentry_sdk.tracing.Span, Any, Any) -> None +def _set_model_data( + span: "sentry_sdk.tracing.Span", model: "Any", model_settings: "Any" +) -> None: """Set model-related data on a span. Args: @@ -172,8 +168,7 @@ def _set_model_data(span, model, model_settings): span.set_data(spandata_key, value) -def _set_available_tools(span, agent): - # type: (sentry_sdk.tracing.Span, Any) -> None +def _set_available_tools(span: "sentry_sdk.tracing.Span", agent: "Any") -> None: """Set available tools data on a span from an agent's function toolset. Args: @@ -211,8 +206,7 @@ def _set_available_tools(span, agent): pass -def _capture_exception(exc): - # type: (Any) -> None +def _capture_exception(exc: "Any") -> None: set_span_errored() event, hint = event_from_exception( diff --git a/sentry_sdk/integrations/pymongo.py b/sentry_sdk/integrations/pymongo.py index f65ad73687..640f348165 100644 --- a/sentry_sdk/integrations/pymongo.py +++ b/sentry_sdk/integrations/pymongo.py @@ -42,8 +42,7 @@ ] -def _strip_pii(command): - # type: (Dict[str, Any]) -> Dict[str, Any] +def _strip_pii(command: "Dict[str, Any]") -> "Dict[str, Any]": for key in command: is_safe_field = key in SAFE_COMMAND_ATTRIBUTES if is_safe_field: @@ -85,8 +84,7 @@ def _strip_pii(command): return command -def _get_db_data(event): - # type: (Any) -> Dict[str, Any] +def _get_db_data(event: "Any") -> "Dict[str, Any]": data = {} data[SPANDATA.DB_SYSTEM] = "mongodb" @@ -107,16 +105,16 @@ def _get_db_data(event): class CommandTracer(monitoring.CommandListener): - def __init__(self): - # type: () -> None - self._ongoing_operations = {} # type: Dict[int, Span] + def __init__(self) -> None: + self._ongoing_operations: Dict[int, Span] = {} - def _operation_key(self, event): - # type: (Union[CommandFailedEvent, CommandStartedEvent, CommandSucceededEvent]) -> int + def _operation_key( + self, + event: "Union[CommandFailedEvent, CommandStartedEvent, CommandSucceededEvent]", + ) -> int: return event.request_id - def started(self, event): - # type: (CommandStartedEvent) -> None + def started(self, event: "CommandStartedEvent") -> None: if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None: return @@ -140,7 +138,7 @@ def started(self, event): except TypeError: pass - data = {"operation_ids": {}} # type: Dict[str, Any] + data: "Dict[str, Any]" = {"operation_ids": {}} data["operation_ids"]["operation"] = event.operation_id data["operation_ids"]["request"] = event.request_id @@ -179,8 +177,7 @@ def started(self, event): self._ongoing_operations[self._operation_key(event)] = span.__enter__() - def failed(self, event): - # type: (CommandFailedEvent) -> None + def failed(self, event: "CommandFailedEvent") -> None: if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None: return @@ -191,8 +188,7 @@ def failed(self, event): except KeyError: return - def succeeded(self, event): - # type: (CommandSucceededEvent) -> None + def succeeded(self, event: "CommandSucceededEvent") -> None: if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None: return @@ -209,6 +205,5 @@ class PyMongoIntegration(Integration): origin = f"auto.db.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: monitoring.register(CommandTracer()) diff --git a/sentry_sdk/integrations/pyramid.py b/sentry_sdk/integrations/pyramid.py index d1475ada65..63353ec285 100644 --- a/sentry_sdk/integrations/pyramid.py +++ b/sentry_sdk/integrations/pyramid.py @@ -40,8 +40,7 @@ if getattr(Request, "authenticated_userid", None): - def authenticated_userid(request): - # type: (Request) -> Optional[Any] + def authenticated_userid(request: "Request") -> "Optional[Any]": return request.authenticated_userid else: @@ -58,8 +57,7 @@ class PyramidIntegration(Integration): transaction_style = "" - def __init__(self, transaction_style="route_name"): - # type: (str) -> None + def __init__(self, transaction_style: str = "route_name") -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -68,15 +66,15 @@ def __init__(self, transaction_style="route_name"): self.transaction_style = transaction_style @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: from pyramid import router old_call_view = router._call_view @functools.wraps(old_call_view) - def sentry_patched_call_view(registry, request, *args, **kwargs): - # type: (Any, Request, *Any, **Any) -> Response + def sentry_patched_call_view( + registry: "Any", request: "Request", *args: "Any", **kwargs: "Any" + ) -> "Response": integration = sentry_sdk.get_client().get_integration(PyramidIntegration) if integration is None: return old_call_view(registry, request, *args, **kwargs) @@ -96,8 +94,9 @@ def sentry_patched_call_view(registry, request, *args, **kwargs): if hasattr(Request, "invoke_exception_view"): old_invoke_exception_view = Request.invoke_exception_view - def sentry_patched_invoke_exception_view(self, *args, **kwargs): - # type: (Request, *Any, **Any) -> Any + def sentry_patched_invoke_exception_view( + self: "Request", *args: "Any", **kwargs: "Any" + ) -> "Any": rv = old_invoke_exception_view(self, *args, **kwargs) if ( @@ -116,10 +115,12 @@ def sentry_patched_invoke_exception_view(self, *args, **kwargs): old_wsgi_call = router.Router.__call__ @ensure_integration_enabled(PyramidIntegration, old_wsgi_call) - def sentry_patched_wsgi_call(self, environ, start_response): - # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse - def sentry_patched_inner_wsgi_call(environ, start_response): - # type: (Dict[str, Any], Callable[..., Any]) -> Any + def sentry_patched_wsgi_call( + self: "Any", environ: "Dict[str, str]", start_response: "Callable[..., Any]" + ) -> "_ScopedResponse": + def sentry_patched_inner_wsgi_call( + environ: Dict[str, Any], start_response: Callable[..., Any] + ) -> Any: try: return old_wsgi_call(self, environ, start_response) except Exception: @@ -137,8 +138,7 @@ def sentry_patched_inner_wsgi_call(environ, start_response): @ensure_integration_enabled(PyramidIntegration) -def _capture_exception(exc_info): - # type: (ExcInfo) -> None +def _capture_exception(exc_info: "ExcInfo") -> None: if exc_info[0] is None or issubclass(exc_info[0], HTTPException): return @@ -151,8 +151,9 @@ def _capture_exception(exc_info): sentry_sdk.capture_event(event, hint=hint) -def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (sentry_sdk.Scope, str, Request) -> None +def _set_transaction_name_and_source( + scope: "sentry_sdk.Scope", transaction_style: str, request: "Request" +) -> None: try: name_for_style = { "route_name": request.matched_route.name, @@ -167,40 +168,33 @@ def _set_transaction_name_and_source(scope, transaction_style, request): class PyramidRequestExtractor(RequestExtractor): - def url(self): - # type: () -> str + def url(self) -> str: return self.request.path_url - def env(self): - # type: () -> Dict[str, str] + def env(self) -> "Dict[str, str]": return self.request.environ - def cookies(self): - # type: () -> RequestCookies + def cookies(self) -> "RequestCookies": return self.request.cookies - def raw_data(self): - # type: () -> str + def raw_data(self) -> str: return self.request.text - def form(self): - # type: () -> Dict[str, str] + def form(self) -> "Dict[str, str]": return { key: value for key, value in self.request.POST.items() if not getattr(value, "filename", None) } - def files(self): - # type: () -> Dict[str, _FieldStorageWithFile] + def files(self) -> "Dict[str, _FieldStorageWithFile]": return { key: value for key, value in self.request.POST.items() if getattr(value, "filename", None) } - def size_of_file(self, postdata): - # type: (_FieldStorageWithFile) -> int + def size_of_file(self, postdata: "_FieldStorageWithFile") -> int: file = postdata.file try: return os.fstat(file.fileno()).st_size @@ -208,10 +202,10 @@ def size_of_file(self, postdata): return 0 -def _make_event_processor(weak_request, integration): - # type: (Callable[[], Request], PyramidIntegration) -> EventProcessor - def pyramid_event_processor(event, hint): - # type: (Event, Dict[str, Any]) -> Event +def _make_event_processor( + weak_request: "Callable[[], Request]", integration: "PyramidIntegration" +) -> "EventProcessor": + def pyramid_event_processor(event: Event, hint: Dict[str, Any]) -> Event: request = weak_request() if request is None: return event diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index 64f7e0bcd2..21d1284217 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -60,8 +60,7 @@ class QuartIntegration(Integration): transaction_style = "" - def __init__(self, transaction_style="endpoint"): - # type: (str) -> None + def __init__(self, transaction_style: str = "endpoint") -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -70,9 +69,7 @@ def __init__(self, transaction_style="endpoint"): self.transaction_style = transaction_style @staticmethod - def setup_once(): - # type: () -> None - + def setup_once() -> None: request_started.connect(_request_websocket_started) websocket_started.connect(_request_websocket_started) got_background_exception.connect(_capture_exception) @@ -83,12 +80,12 @@ def setup_once(): patch_scaffold_route() -def patch_asgi_app(): - # type: () -> None +def patch_asgi_app() -> None: old_app = Quart.__call__ - async def sentry_patched_asgi_app(self, scope, receive, send): - # type: (Any, Any, Any, Any) -> Any + async def sentry_patched_asgi_app( + self: "Any", scope: "Any", receive: "Any", send: "Any" + ) -> "Any": if sentry_sdk.get_client().get_integration(QuartIntegration) is None: return await old_app(self, scope, receive, send) @@ -102,25 +99,20 @@ async def sentry_patched_asgi_app(self, scope, receive, send): Quart.__call__ = sentry_patched_asgi_app -def patch_scaffold_route(): - # type: () -> None +def patch_scaffold_route() -> None: old_route = Scaffold.route - def _sentry_route(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_route(*args: "Any", **kwargs: "Any") -> "Any": old_decorator = old_route(*args, **kwargs) - def decorator(old_func): - # type: (Any) -> Any - + def decorator(old_func: "Any") -> "Any": if inspect.isfunction(old_func) and not asyncio.iscoroutinefunction( old_func ): @wraps(old_func) @ensure_integration_enabled(QuartIntegration, old_func) - def _sentry_func(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_func(*args: "Any", **kwargs: "Any") -> "Any": current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() @@ -140,9 +132,9 @@ def _sentry_func(*args, **kwargs): Scaffold.route = _sentry_route -def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (sentry_sdk.Scope, str, Request) -> None - +def _set_transaction_name_and_source( + scope: "sentry_sdk.Scope", transaction_style: str, request: "Request" +) -> None: try: name_for_style = { "url": request.url_rule.rule, @@ -156,8 +148,7 @@ def _set_transaction_name_and_source(scope, transaction_style, request): pass -async def _request_websocket_started(app, **kwargs): - # type: (Quart, **Any) -> None +async def _request_websocket_started(app: "Quart", **kwargs: "Any") -> None: integration = sentry_sdk.get_client().get_integration(QuartIntegration) if integration is None: return @@ -178,10 +169,10 @@ async def _request_websocket_started(app, **kwargs): scope.add_event_processor(evt_processor) -def _make_request_event_processor(app, request, integration): - # type: (Quart, Request, QuartIntegration) -> EventProcessor - def inner(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_request_event_processor( + app: "Quart", request: "Request", integration: "QuartIntegration" +) -> "EventProcessor": + def inner(event: Event, hint: dict[str, Any]) -> Event: # if the request is gone we are fine not logging the data from # it. This might happen if the processor is pushed away to # another thread. @@ -207,8 +198,9 @@ def inner(event, hint): return inner -async def _capture_exception(sender, exception, **kwargs): - # type: (Quart, Union[ValueError, BaseException], **Any) -> None +async def _capture_exception( + sender: "Quart", exception: "Union[ValueError, BaseException]", **kwargs: "Any" +) -> None: integration = sentry_sdk.get_client().get_integration(QuartIntegration) if integration is None: return @@ -222,8 +214,7 @@ async def _capture_exception(sender, exception, **kwargs): sentry_sdk.capture_event(event, hint=hint) -def _add_user_to_event(event): - # type: (Event) -> None +def _add_user_to_event(event: "Event") -> None: if quart_auth is None: return diff --git a/sentry_sdk/integrations/ray.py b/sentry_sdk/integrations/ray.py index 08e78b7585..a3a18fca9f 100644 --- a/sentry_sdk/integrations/ray.py +++ b/sentry_sdk/integrations/ray.py @@ -27,8 +27,7 @@ from sentry_sdk.utils import ExcInfo -def _check_sentry_initialized(): - # type: () -> None +def _check_sentry_initialized() -> None: if sentry_sdk.get_client().is_active(): return @@ -37,14 +36,13 @@ def _check_sentry_initialized(): ) -def _patch_ray_remote(): - # type: () -> None +def _patch_ray_remote() -> None: old_remote = ray.remote @functools.wraps(old_remote) - def new_remote(f=None, *args, **kwargs): - # type: (Optional[Callable[..., Any]], *Any, **Any) -> Callable[..., Any] - + def new_remote( + f: "Optional[Callable[..., Any]]" = None, *args: "Any", **kwargs: "Any" + ) -> "Callable[..., Any]": if inspect.isclass(f): # Ray Actors # (https://docs.ray.io/en/latest/ray-core/actors.html) @@ -52,11 +50,13 @@ def new_remote(f=None, *args, **kwargs): # (Only Ray Tasks are supported) return old_remote(f, *args, **kwargs) - def wrapper(user_f): - # type: (Callable[..., Any]) -> Any + def wrapper(user_f: "Callable[..., Any]") -> "Any": @functools.wraps(user_f) - def new_func(*f_args, _sentry_tracing=None, **f_kwargs): - # type: (Any, Optional[dict[str, Any]], Any) -> Any + def new_func( + *f_args: Any, + _sentry_tracing: Optional[dict[str, Any]] = None, + **f_kwargs: Any, + ) -> Any: _check_sentry_initialized() transaction = sentry_sdk.continue_trace( @@ -98,8 +98,9 @@ def new_func(*f_args, _sentry_tracing=None, **f_kwargs): rv = old_remote(*args, **kwargs)(new_func) old_remote_method = rv.remote - def _remote_method_with_header_propagation(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _remote_method_with_header_propagation( + *args: "Any", **kwargs: "Any" + ) -> "Any": """ Ray Client """ @@ -137,8 +138,7 @@ def _remote_method_with_header_propagation(*args, **kwargs): ray.remote = new_remote -def _capture_exception(exc_info, **kwargs): - # type: (ExcInfo, **Any) -> None +def _capture_exception(exc_info: "ExcInfo", **kwargs: "Any") -> None: client = sentry_sdk.get_client() event, hint = event_from_exception( @@ -157,8 +157,7 @@ class RayIntegration(Integration): origin = f"auto.queue.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = package_version("ray") _check_minimum_version(RayIntegration, version) diff --git a/sentry_sdk/integrations/redis/__init__.py b/sentry_sdk/integrations/redis/__init__.py index 9595794e74..a5b67eb7f6 100644 --- a/sentry_sdk/integrations/redis/__init__.py +++ b/sentry_sdk/integrations/redis/__init__.py @@ -17,8 +17,11 @@ class RedisIntegration(Integration): identifier = "redis" - def __init__(self, max_data_size=_DEFAULT_MAX_DATA_SIZE, cache_prefixes=None): - # type: (Optional[int], Optional[list[str]]) -> None + def __init__( + self, + max_data_size: "Optional[int]" = _DEFAULT_MAX_DATA_SIZE, + cache_prefixes: "Optional[list[str]]" = None, + ) -> None: self.max_data_size = max_data_size self.cache_prefixes = cache_prefixes if cache_prefixes is not None else [] @@ -31,8 +34,7 @@ def __init__(self, max_data_size=_DEFAULT_MAX_DATA_SIZE, cache_prefixes=None): ) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: try: from redis import StrictRedis, client except ImportError: diff --git a/sentry_sdk/integrations/redis/_async_common.py b/sentry_sdk/integrations/redis/_async_common.py index b96986fba3..1afc355843 100644 --- a/sentry_sdk/integrations/redis/_async_common.py +++ b/sentry_sdk/integrations/redis/_async_common.py @@ -23,15 +23,16 @@ def patch_redis_async_pipeline( - pipeline_cls, is_cluster, get_command_args_fn, set_db_data_fn -): - # type: (Union[type[Pipeline[Any]], type[ClusterPipeline[Any]]], bool, Any, Callable[[Span, Any], None]) -> None + pipeline_cls: "Union[type[Pipeline[Any]], type[ClusterPipeline[Any]]]", + is_cluster: bool, + get_command_args_fn: "Any", + set_db_data_fn: "Callable[[Span, Any], None]", +) -> None: old_execute = pipeline_cls.execute from sentry_sdk.integrations.redis import RedisIntegration - async def _sentry_execute(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + async def _sentry_execute(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": if sentry_sdk.get_client().get_integration(RedisIntegration) is None: return await old_execute(self, *args, **kwargs) @@ -63,14 +64,18 @@ async def _sentry_execute(self, *args, **kwargs): pipeline_cls.execute = _sentry_execute # type: ignore -def patch_redis_async_client(cls, is_cluster, set_db_data_fn): - # type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Span, Any], None]) -> None +def patch_redis_async_client( + cls: "Union[type[StrictRedis[Any]], type[RedisCluster[Any]]]", + is_cluster: bool, + set_db_data_fn: "Callable[[Span, Any], None]", +) -> None: old_execute_command = cls.execute_command from sentry_sdk.integrations.redis import RedisIntegration - async def _sentry_execute_command(self, name, *args, **kwargs): - # type: (Any, str, *Any, **Any) -> Any + async def _sentry_execute_command( + self: "Any", name: str, *args: "Any", **kwargs: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(RedisIntegration) if integration is None: return await old_execute_command(self, name, *args, **kwargs) diff --git a/sentry_sdk/integrations/redis/_sync_common.py b/sentry_sdk/integrations/redis/_sync_common.py index 72f3eb7778..4624260f6a 100644 --- a/sentry_sdk/integrations/redis/_sync_common.py +++ b/sentry_sdk/integrations/redis/_sync_common.py @@ -21,18 +21,16 @@ def patch_redis_pipeline( - pipeline_cls, - is_cluster, - get_command_args_fn, - set_db_data_fn, -): - # type: (Any, bool, Any, Callable[[Span, Any], None]) -> None + pipeline_cls: "Any", + is_cluster: bool, + get_command_args_fn: "Any", + set_db_data_fn: "Callable[[Span, Any], None]", +) -> None: old_execute = pipeline_cls.execute from sentry_sdk.integrations.redis import RedisIntegration - def sentry_patched_execute(self, *args, **kwargs): - # type: (Any, *Any, **Any) -> Any + def sentry_patched_execute(self: "Any", *args: "Any", **kwargs: "Any") -> "Any": if sentry_sdk.get_client().get_integration(RedisIntegration) is None: return old_execute(self, *args, **kwargs) @@ -62,8 +60,9 @@ def sentry_patched_execute(self, *args, **kwargs): pipeline_cls.execute = sentry_patched_execute -def patch_redis_client(cls, is_cluster, set_db_data_fn): - # type: (Any, bool, Callable[[Span, Any], None]) -> None +def patch_redis_client( + cls: "Any", is_cluster: bool, set_db_data_fn: "Callable[[Span, Any], None]" +) -> None: """ This function can be used to instrument custom redis client classes or subclasses. @@ -72,8 +71,9 @@ def patch_redis_client(cls, is_cluster, set_db_data_fn): from sentry_sdk.integrations.redis import RedisIntegration - def sentry_patched_execute_command(self, name, *args, **kwargs): - # type: (Any, str, *Any, **Any) -> Any + def sentry_patched_execute_command( + self: "Any", name: str, *args: "Any", **kwargs: "Any" + ) -> "Any": integration = sentry_sdk.get_client().get_integration(RedisIntegration) if integration is None: return old_execute_command(self, name, *args, **kwargs) diff --git a/sentry_sdk/integrations/redis/modules/caches.py b/sentry_sdk/integrations/redis/modules/caches.py index 07b418ad0d..ee5a7d3943 100644 --- a/sentry_sdk/integrations/redis/modules/caches.py +++ b/sentry_sdk/integrations/redis/modules/caches.py @@ -17,8 +17,7 @@ from typing import Any, Optional -def _get_op(name): - # type: (str) -> Optional[str] +def _get_op(name: str) -> "Optional[str]": op = None if name.lower() in GET_COMMANDS: op = OP.CACHE_GET @@ -28,8 +27,12 @@ def _get_op(name): return op -def _compile_cache_span_properties(redis_command, args, kwargs, integration): - # type: (str, tuple[Any, ...], dict[str, Any], RedisIntegration) -> dict[str, Any] +def _compile_cache_span_properties( + redis_command: str, + args: "tuple[Any, ...]", + kwargs: "dict[str, Any]", + integration: "RedisIntegration", +) -> "dict[str, Any]": key = _get_safe_key(redis_command, args, kwargs) key_as_string = _key_as_string(key) keys_as_string = key_as_string.split(", ") @@ -62,8 +65,12 @@ def _compile_cache_span_properties(redis_command, args, kwargs, integration): return properties -def _get_cache_span_description(redis_command, args, kwargs, integration): - # type: (str, tuple[Any, ...], dict[str, Any], RedisIntegration) -> str +def _get_cache_span_description( + redis_command: str, + args: "tuple[Any, ...]", + kwargs: "dict[str, Any]", + integration: "RedisIntegration", +) -> str: description = _key_as_string(_get_safe_key(redis_command, args, kwargs)) if integration.max_data_size and len(description) > integration.max_data_size: @@ -72,8 +79,12 @@ def _get_cache_span_description(redis_command, args, kwargs, integration): return description -def _set_cache_data(span, redis_client, properties, return_value): - # type: (Span, Any, dict[str, Any], Optional[Any]) -> None +def _set_cache_data( + span: "Span", + redis_client: "Any", + properties: "dict[str, Any]", + return_value: "Optional[Any]", +) -> None: with capture_internal_exceptions(): span.set_data(SPANDATA.CACHE_KEY, properties["key"]) diff --git a/sentry_sdk/integrations/redis/modules/queries.py b/sentry_sdk/integrations/redis/modules/queries.py index a4229a4d5d..3e8a820f44 100644 --- a/sentry_sdk/integrations/redis/modules/queries.py +++ b/sentry_sdk/integrations/redis/modules/queries.py @@ -15,8 +15,9 @@ from typing import Any -def _compile_db_span_properties(integration, redis_command, args): - # type: (RedisIntegration, str, tuple[Any, ...]) -> dict[str, Any] +def _compile_db_span_properties( + integration: "RedisIntegration", redis_command: str, args: "tuple[Any, ...]" +) -> "dict[str, Any]": description = _get_db_span_description(integration, redis_command, args) properties = { @@ -27,8 +28,9 @@ def _compile_db_span_properties(integration, redis_command, args): return properties -def _get_db_span_description(integration, command_name, args): - # type: (RedisIntegration, str, tuple[Any, ...]) -> str +def _get_db_span_description( + integration: "RedisIntegration", command_name: str, args: "tuple[Any, ...]" +) -> str: description = command_name with capture_internal_exceptions(): @@ -40,8 +42,7 @@ def _get_db_span_description(integration, command_name, args): return description -def _set_db_data_on_span(span, connection_params): - # type: (Span, dict[str, Any]) -> None +def _set_db_data_on_span(span: "Span", connection_params: "dict[str, Any]") -> None: span.set_data(SPANDATA.DB_SYSTEM, "redis") db = connection_params.get("db") @@ -57,8 +58,7 @@ def _set_db_data_on_span(span, connection_params): span.set_data(SPANDATA.SERVER_PORT, port) -def _set_db_data(span, redis_instance): - # type: (Span, Redis[Any]) -> None +def _set_db_data(span: "Span", redis_instance: "Redis[Any]") -> None: try: _set_db_data_on_span(span, redis_instance.connection_pool.connection_kwargs) except AttributeError: diff --git a/sentry_sdk/integrations/redis/rb.py b/sentry_sdk/integrations/redis/rb.py index 1b3e2e530c..e2ce863fe8 100644 --- a/sentry_sdk/integrations/redis/rb.py +++ b/sentry_sdk/integrations/redis/rb.py @@ -8,8 +8,7 @@ from sentry_sdk.integrations.redis.modules.queries import _set_db_data -def _patch_rb(): - # type: () -> None +def _patch_rb() -> None: try: import rb.clients # type: ignore except ImportError: diff --git a/sentry_sdk/integrations/redis/redis.py b/sentry_sdk/integrations/redis/redis.py index c92958a32d..8011001456 100644 --- a/sentry_sdk/integrations/redis/redis.py +++ b/sentry_sdk/integrations/redis/redis.py @@ -16,13 +16,11 @@ from typing import Any, Sequence -def _get_redis_command_args(command): - # type: (Any) -> Sequence[Any] +def _get_redis_command_args(command: "Any") -> "Sequence[Any]": return command[0] -def _patch_redis(StrictRedis, client): # noqa: N803 - # type: (Any, Any) -> None +def _patch_redis(StrictRedis: "Any", client: "Any") -> None: # noqa: N803 patch_redis_client( StrictRedis, is_cluster=False, diff --git a/sentry_sdk/integrations/redis/redis_cluster.py b/sentry_sdk/integrations/redis/redis_cluster.py index 52936d1512..b73a8e730c 100644 --- a/sentry_sdk/integrations/redis/redis_cluster.py +++ b/sentry_sdk/integrations/redis/redis_cluster.py @@ -26,15 +26,17 @@ from sentry_sdk.tracing import Span -def _set_async_cluster_db_data(span, async_redis_cluster_instance): - # type: (Span, AsyncRedisCluster[Any]) -> None +def _set_async_cluster_db_data( + span: "Span", async_redis_cluster_instance: "AsyncRedisCluster[Any]" +) -> None: default_node = async_redis_cluster_instance.get_default_node() if default_node is not None and default_node.connection_kwargs is not None: _set_db_data_on_span(span, default_node.connection_kwargs) -def _set_async_cluster_pipeline_db_data(span, async_redis_cluster_pipeline_instance): - # type: (Span, AsyncClusterPipeline[Any]) -> None +def _set_async_cluster_pipeline_db_data( + span: "Span", async_redis_cluster_pipeline_instance: "AsyncClusterPipeline[Any]" +) -> None: with capture_internal_exceptions(): client = getattr(async_redis_cluster_pipeline_instance, "cluster_client", None) if client is None: @@ -52,8 +54,9 @@ def _set_async_cluster_pipeline_db_data(span, async_redis_cluster_pipeline_insta ) -def _set_cluster_db_data(span, redis_cluster_instance): - # type: (Span, RedisCluster[Any]) -> None +def _set_cluster_db_data( + span: "Span", redis_cluster_instance: "RedisCluster[Any]" +) -> None: default_node = redis_cluster_instance.get_default_node() if default_node is not None: @@ -64,8 +67,7 @@ def _set_cluster_db_data(span, redis_cluster_instance): _set_db_data_on_span(span, connection_params) -def _patch_redis_cluster(): - # type: () -> None +def _patch_redis_cluster() -> None: """Patches the cluster module on redis SDK (as opposed to rediscluster library)""" try: from redis import RedisCluster, cluster diff --git a/sentry_sdk/integrations/redis/redis_py_cluster_legacy.py b/sentry_sdk/integrations/redis/redis_py_cluster_legacy.py index ad1c23633f..3437aa1f2f 100644 --- a/sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +++ b/sentry_sdk/integrations/redis/redis_py_cluster_legacy.py @@ -13,8 +13,7 @@ from sentry_sdk.integrations.redis.utils import _parse_rediscluster_command -def _patch_rediscluster(): - # type: () -> None +def _patch_rediscluster() -> None: try: import rediscluster # type: ignore except ImportError: diff --git a/sentry_sdk/integrations/redis/utils.py b/sentry_sdk/integrations/redis/utils.py index 7bb73f3372..81d544a75a 100644 --- a/sentry_sdk/integrations/redis/utils.py +++ b/sentry_sdk/integrations/redis/utils.py @@ -16,8 +16,7 @@ from sentry_sdk.tracing import Span -def _get_safe_command(name, args): - # type: (str, Sequence[Any]) -> str +def _get_safe_command(name: str, args: "Sequence[Any]") -> str: command_parts = [name] name_low = name.lower() @@ -44,8 +43,7 @@ def _get_safe_command(name, args): return command -def _safe_decode(key): - # type: (Any) -> str +def _safe_decode(key: "Any") -> str: if isinstance(key, bytes): try: return key.decode() @@ -55,8 +53,7 @@ def _safe_decode(key): return str(key) -def _key_as_string(key): - # type: (Any) -> str +def _key_as_string(key: "Any") -> str: if isinstance(key, (dict, list, tuple)): key = ", ".join(_safe_decode(x) for x in key) elif isinstance(key, bytes): @@ -69,8 +66,11 @@ def _key_as_string(key): return key -def _get_safe_key(method_name, args, kwargs): - # type: (str, Optional[tuple[Any, ...]], Optional[dict[str, Any]]) -> Optional[tuple[str, ...]] +def _get_safe_key( + method_name: str, + args: "Optional[tuple[Any, ...]]", + kwargs: "Optional[dict[str, Any]]", +) -> "Optional[tuple[str, ...]]": """ Gets the key (or keys) from the given method_name. The method_name could be a redis command or a django caching command @@ -100,19 +100,17 @@ def _get_safe_key(method_name, args, kwargs): return key -def _parse_rediscluster_command(command): - # type: (Any) -> Sequence[Any] +def _parse_rediscluster_command(command: "Any") -> "Sequence[Any]": return command.args def _set_pipeline_data( - span, - is_cluster, - get_command_args_fn, - is_transaction, - commands_seq, -): - # type: (Span, bool, Any, bool, Sequence[Any]) -> None + span: "Span", + is_cluster: bool, + get_command_args_fn: "Any", + is_transaction: bool, + commands_seq: "Sequence[Any]", +) -> None: span.set_tag("redis.is_cluster", is_cluster) span.set_tag("redis.transaction", is_transaction) @@ -133,8 +131,7 @@ def _set_pipeline_data( ) -def _set_client_data(span, is_cluster, name, *args): - # type: (Span, bool, str, *Any) -> None +def _set_client_data(span: "Span", is_cluster: bool, name: str, *args: "Any") -> None: span.set_tag("redis.is_cluster", is_cluster) if name: span.set_tag("redis.command", name) diff --git a/sentry_sdk/integrations/rq.py b/sentry_sdk/integrations/rq.py index 6d7fcf723b..0f74abe77d 100644 --- a/sentry_sdk/integrations/rq.py +++ b/sentry_sdk/integrations/rq.py @@ -39,16 +39,16 @@ class RqIntegration(Integration): origin = f"auto.queue.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = parse_version(RQ_VERSION) _check_minimum_version(RqIntegration, version) old_perform_job = Worker.perform_job @ensure_integration_enabled(RqIntegration, old_perform_job) - def sentry_patched_perform_job(self, job, *args, **kwargs): - # type: (Any, Job, *Queue, **Any) -> bool + def sentry_patched_perform_job( + self: "Any", job: "Job", *args: "Queue", **kwargs: "Any" + ) -> bool: with sentry_sdk.new_scope() as scope: scope.clear_breadcrumbs() scope.add_event_processor(_make_event_processor(weakref.ref(job))) @@ -82,8 +82,9 @@ def sentry_patched_perform_job(self, job, *args, **kwargs): old_handle_exception = Worker.handle_exception - def sentry_patched_handle_exception(self, job, *exc_info, **kwargs): - # type: (Worker, Any, *Any, **Any) -> Any + def sentry_patched_handle_exception( + self: "Worker", job: "Any", *exc_info: "Any", **kwargs: "Any" + ) -> "Any": retry = ( hasattr(job, "retries_left") and job.retries_left @@ -100,8 +101,9 @@ def sentry_patched_handle_exception(self, job, *exc_info, **kwargs): old_enqueue_job = Queue.enqueue_job @ensure_integration_enabled(RqIntegration, old_enqueue_job) - def sentry_patched_enqueue_job(self, job, **kwargs): - # type: (Queue, Any, **Any) -> Any + def sentry_patched_enqueue_job( + self: "Queue", job: "Any", **kwargs: "Any" + ) -> "Any": scope = sentry_sdk.get_current_scope() if scope.span is not None: job.meta["_sentry_trace_headers"] = dict( @@ -115,10 +117,8 @@ def sentry_patched_enqueue_job(self, job, **kwargs): ignore_logger("rq.worker") -def _make_event_processor(weak_job): - # type: (Callable[[], Job]) -> EventProcessor - def event_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_event_processor(weak_job: "Callable[[], Job]") -> "EventProcessor": + def event_processor(event: Event, hint: dict[str, Any]) -> Event: job = weak_job() if job is not None: with capture_internal_exceptions(): @@ -148,8 +148,7 @@ def event_processor(event, hint): return event_processor -def _capture_exception(exc_info, **kwargs): - # type: (ExcInfo, **Any) -> None +def _capture_exception(exc_info: "ExcInfo", **kwargs: "Any") -> None: client = sentry_sdk.get_client() event, hint = event_from_exception( diff --git a/sentry_sdk/integrations/rust_tracing.py b/sentry_sdk/integrations/rust_tracing.py index e4c211814f..5f1d970cbe 100644 --- a/sentry_sdk/integrations/rust_tracing.py +++ b/sentry_sdk/integrations/rust_tracing.py @@ -58,8 +58,7 @@ class EventTypeMapping(Enum): Event = auto() -def tracing_level_to_sentry_level(level): - # type: (str) -> sentry_sdk._types.LogLevelStr +def tracing_level_to_sentry_level(level: str) -> "sentry_sdk._types.LogLevelStr": level = RustTracingLevel(level) if level in (RustTracingLevel.Trace, RustTracingLevel.Debug): return "debug" @@ -99,15 +98,15 @@ def process_event(event: Dict[str, Any]) -> None: logger = metadata.get("target") level = tracing_level_to_sentry_level(metadata.get("level")) - message = event.get("message") # type: sentry_sdk._types.Any + message: "sentry_sdk._types.Any" = event.get("message") contexts = extract_contexts(event) - sentry_event = { + sentry_event: "sentry_sdk._types.Event" = { "logger": logger, "level": level, "message": message, "contexts": contexts, - } # type: sentry_sdk._types.Event + } sentry_sdk.capture_event(sentry_event) diff --git a/sentry_sdk/integrations/sanic.py b/sentry_sdk/integrations/sanic.py index bd8f1f329b..880da50cfe 100644 --- a/sentry_sdk/integrations/sanic.py +++ b/sentry_sdk/integrations/sanic.py @@ -60,8 +60,9 @@ class SanicIntegration(Integration): origin = f"auto.http.{identifier}" version = None - def __init__(self, unsampled_statuses=frozenset({404})): - # type: (Optional[Container[int]]) -> None + def __init__( + self, unsampled_statuses: "Optional[Container[int]]" = frozenset({404}) + ) -> None: """ The unsampled_statuses parameter can be used to specify for which HTTP statuses the transactions should not be sent to Sentry. By default, transactions are sent for all @@ -71,8 +72,7 @@ def __init__(self, unsampled_statuses=frozenset({404})): self._unsampled_statuses = unsampled_statuses or set() @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: SanicIntegration.version = parse_version(SANIC_VERSION) _check_minimum_version(SanicIntegration, SanicIntegration.version) @@ -104,56 +104,45 @@ def setup_once(): class SanicRequestExtractor(RequestExtractor): - def content_length(self): - # type: () -> int + def content_length(self) -> int: if self.request.body is None: return 0 return len(self.request.body) - def cookies(self): - # type: () -> Dict[str, str] + def cookies(self) -> "Dict[str, str]": return dict(self.request.cookies) - def raw_data(self): - # type: () -> bytes + def raw_data(self) -> bytes: return self.request.body - def form(self): - # type: () -> RequestParameters + def form(self) -> "RequestParameters": return self.request.form - def is_json(self): - # type: () -> bool + def is_json(self) -> bool: raise NotImplementedError() - def json(self): - # type: () -> Optional[Any] + def json(self) -> "Optional[Any]": return self.request.json - def files(self): - # type: () -> RequestParameters + def files(self) -> "RequestParameters": return self.request.files - def size_of_file(self, file): - # type: (Any) -> int + def size_of_file(self, file: "Any") -> int: return len(file.body or ()) -def _setup_sanic(): - # type: () -> None +def _setup_sanic() -> None: Sanic._startup = _startup ErrorHandler.lookup = _sentry_error_handler_lookup -def _setup_legacy_sanic(): - # type: () -> None +def _setup_legacy_sanic() -> None: Sanic.handle_request = _legacy_handle_request Router.get = _legacy_router_get ErrorHandler.lookup = _sentry_error_handler_lookup -async def _startup(self): - # type: (Sanic) -> None +async def _startup(self: "Sanic") -> None: # This happens about as early in the lifecycle as possible, just after the # Request object is created. The body has not yet been consumed. self.signal("http.lifecycle.request")(_context_enter) @@ -172,8 +161,7 @@ async def _startup(self): await old_startup(self) -async def _context_enter(request): - # type: (Request) -> None +async def _context_enter(request: "Request") -> None: request.ctx._sentry_do_integration = ( sentry_sdk.get_client().get_integration(SanicIntegration) is not None ) @@ -200,8 +188,9 @@ async def _context_enter(request): ).__enter__() -async def _context_exit(request, response=None): - # type: (Request, Optional[BaseHTTPResponse]) -> None +async def _context_exit( + request: "Request", response: "Optional[BaseHTTPResponse]" = None +) -> None: with capture_internal_exceptions(): if not request.ctx._sentry_do_integration: return @@ -223,8 +212,7 @@ async def _context_exit(request, response=None): request.ctx._sentry_scope.__exit__(None, None, None) -async def _set_transaction(request, route, **_): - # type: (Request, Route, **Any) -> None +async def _set_transaction(request: "Request", route: "Route", **_: "Any") -> None: if request.ctx._sentry_do_integration: with capture_internal_exceptions(): scope = sentry_sdk.get_current_scope() @@ -232,8 +220,9 @@ async def _set_transaction(request, route, **_): scope.set_transaction_name(route_name, source=TransactionSource.COMPONENT) -def _sentry_error_handler_lookup(self, exception, *args, **kwargs): - # type: (Any, Exception, *Any, **Any) -> Optional[object] +def _sentry_error_handler_lookup( + self: "Any", exception: Exception, *args: "Any", **kwargs: "Any" +) -> "Optional[object]": _capture_exception(exception) old_error_handler = old_error_handler_lookup(self, exception, *args, **kwargs) @@ -243,8 +232,9 @@ def _sentry_error_handler_lookup(self, exception, *args, **kwargs): if sentry_sdk.get_client().get_integration(SanicIntegration) is None: return old_error_handler - async def sentry_wrapped_error_handler(request, exception): - # type: (Request, Exception) -> Any + async def sentry_wrapped_error_handler( + request: "Request", exception: Exception + ) -> "Any": try: response = old_error_handler(request, exception) if isawaitable(response): @@ -266,8 +256,9 @@ async def sentry_wrapped_error_handler(request, exception): return sentry_wrapped_error_handler -async def _legacy_handle_request(self, request, *args, **kwargs): - # type: (Any, Request, *Any, **Any) -> Any +async def _legacy_handle_request( + self: "Any", request: "Request", *args: "Any", **kwargs: "Any" +) -> "Any": if sentry_sdk.get_client().get_integration(SanicIntegration) is None: return await old_handle_request(self, request, *args, **kwargs) @@ -284,8 +275,7 @@ async def _legacy_handle_request(self, request, *args, **kwargs): return response -def _legacy_router_get(self, *args): - # type: (Any, Union[Any, Request]) -> Any +def _legacy_router_get(self: "Any", *args: "Union[Any, Request]") -> "Any": rv = old_router_get(self, *args) if sentry_sdk.get_client().get_integration(SanicIntegration) is not None: with capture_internal_exceptions(): @@ -315,8 +305,7 @@ def _legacy_router_get(self, *args): @ensure_integration_enabled(SanicIntegration) -def _capture_exception(exception): - # type: (Union[ExcInfo, BaseException]) -> None +def _capture_exception(exception: "Union[ExcInfo, BaseException]") -> None: with capture_internal_exceptions(): event, hint = event_from_exception( exception, @@ -330,11 +319,8 @@ def _capture_exception(exception): sentry_sdk.capture_event(event, hint=hint) -def _make_request_processor(weak_request): - # type: (Callable[[], Request]) -> EventProcessor - def sanic_processor(event, hint): - # type: (Event, Optional[Hint]) -> Optional[Event] - +def _make_request_processor(weak_request: "Callable[[], Request]") -> "EventProcessor": + def sanic_processor(event: Event, hint: Optional[Hint]) -> Optional[Event]: try: if hint and issubclass(hint["exc_info"][0], SanicException): return None diff --git a/sentry_sdk/integrations/serverless.py b/sentry_sdk/integrations/serverless.py index 760c07ffad..83fa0fde3f 100644 --- a/sentry_sdk/integrations/serverless.py +++ b/sentry_sdk/integrations/serverless.py @@ -1,47 +1,37 @@ import sys from functools import wraps +from typing import TYPE_CHECKING import sentry_sdk from sentry_sdk.utils import event_from_exception, reraise -from typing import TYPE_CHECKING - if TYPE_CHECKING: - from typing import Any - from typing import Callable - from typing import TypeVar - from typing import Union - from typing import Optional - from typing import overload + from typing import Any, Callable, Optional, TypeVar, Union, overload F = TypeVar("F", bound=Callable[..., Any]) else: - def overload(x): - # type: (F) -> F + def overload(x: "F") -> "F": return x @overload -def serverless_function(f, flush=True): - # type: (F, bool) -> F +def serverless_function(f: "F", flush: bool = True) -> "F": pass @overload -def serverless_function(f=None, flush=True): # noqa: F811 - # type: (None, bool) -> Callable[[F], F] +def serverless_function(f: None = None, flush: bool = True) -> "Callable[[F], F]": # noqa: F811 pass -def serverless_function(f=None, flush=True): # noqa - # type: (Optional[F], bool) -> Union[F, Callable[[F], F]] - def wrapper(f): - # type: (F) -> F +def serverless_function( # noqa + f: "Optional[F]" = None, flush: bool = True +) -> "Union[F, Callable[[F], F]]": + def wrapper(f: F) -> F: @wraps(f) - def inner(*args, **kwargs): - # type: (*Any, **Any) -> Any + def inner(*args: Any, **kwargs: Any) -> Any: with sentry_sdk.isolation_scope() as scope: scope.clear_breadcrumbs() @@ -61,8 +51,7 @@ def inner(*args, **kwargs): return wrapper(f) -def _capture_and_reraise(): - # type: () -> None +def _capture_and_reraise() -> None: exc_info = sys.exc_info() client = sentry_sdk.get_client() if client.is_active(): diff --git a/sentry_sdk/integrations/socket.py b/sentry_sdk/integrations/socket.py index babf61aa7a..472b909d28 100644 --- a/sentry_sdk/integrations/socket.py +++ b/sentry_sdk/integrations/socket.py @@ -17,8 +17,7 @@ class SocketIntegration(Integration): origin = f"auto.socket.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: """ patches two of the most used functions of socket: create_connection and getaddrinfo(dns resolver) """ @@ -26,9 +25,9 @@ def setup_once(): _patch_getaddrinfo() -def _get_span_description(host, port): - # type: (Union[bytes, str, None], Union[bytes, str, int, None]) -> str - +def _get_span_description( + host: "Union[bytes, str, None]", port: "Union[bytes, str, int, None]" +) -> str: try: host = host.decode() # type: ignore except (UnicodeDecodeError, AttributeError): @@ -43,16 +42,14 @@ def _get_span_description(host, port): return description -def _patch_create_connection(): - # type: () -> None +def _patch_create_connection() -> None: real_create_connection = socket.create_connection def create_connection( - address, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore - source_address=None, - ): - # type: (Tuple[Optional[str], int], Optional[float], Optional[Tuple[Union[bytearray, bytes, str], int]])-> socket.socket + address: "Tuple[Optional[str], int]", + timeout: "Optional[float]" = socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore + source_address: "Optional[Tuple[Union[bytearray, bytes, str], int]]" = None, + ) -> "socket.socket": integration = sentry_sdk.get_client().get_integration(SocketIntegration) if integration is None: return real_create_connection(address, timeout, source_address) @@ -73,12 +70,17 @@ def create_connection( socket.create_connection = create_connection # type: ignore -def _patch_getaddrinfo(): - # type: () -> None +def _patch_getaddrinfo() -> None: real_getaddrinfo = socket.getaddrinfo - def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): - # type: (Union[bytes, str, None], Union[bytes, str, int, None], int, int, int, int) -> List[Tuple[AddressFamily, SocketKind, int, str, Union[Tuple[str, int], Tuple[str, int, int, int], Tuple[int, bytes]]]] + def getaddrinfo( + host: "Union[bytes, str, None]", + port: "Union[bytes, str, int, None]", + family: int = 0, + type: int = 0, + proto: int = 0, + flags: int = 0, + ) -> "List[Tuple[AddressFamily, SocketKind, int, str, Union[Tuple[str, int], Tuple[str, int, int, int], Tuple[int, bytes]]]]": integration = sentry_sdk.get_client().get_integration(SocketIntegration) if integration is None: return real_getaddrinfo(host, port, family, type, proto, flags) diff --git a/sentry_sdk/integrations/spark/spark_driver.py b/sentry_sdk/integrations/spark/spark_driver.py index b22dc2c807..5ce8102853 100644 --- a/sentry_sdk/integrations/spark/spark_driver.py +++ b/sentry_sdk/integrations/spark/spark_driver.py @@ -16,13 +16,11 @@ class SparkIntegration(Integration): identifier = "spark" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: _setup_sentry_tracing() -def _set_app_properties(): - # type: () -> None +def _set_app_properties() -> None: """ Set properties in driver that propagate to worker processes, allowing for workers to have access to those properties. This allows worker integration to have access to app_name and application_id. @@ -41,8 +39,7 @@ def _set_app_properties(): ) -def _start_sentry_listener(sc): - # type: (SparkContext) -> None +def _start_sentry_listener(sc: "SparkContext") -> None: """ Start java gateway server to add custom `SparkListener` """ @@ -54,13 +51,11 @@ def _start_sentry_listener(sc): sc._jsc.sc().addSparkListener(listener) -def _add_event_processor(sc): - # type: (SparkContext) -> None +def _add_event_processor(sc: "SparkContext") -> None: scope = sentry_sdk.get_isolation_scope() @scope.add_event_processor - def process_event(event, hint): - # type: (Event, Hint) -> Optional[Event] + def process_event(event: "Event", hint: "Hint") -> "Optional[Event]": with capture_internal_exceptions(): if sentry_sdk.get_client().get_integration(SparkIntegration) is None: return event @@ -90,23 +85,21 @@ def process_event(event, hint): return event -def _activate_integration(sc): - # type: (SparkContext) -> None - +def _activate_integration(sc: "SparkContext") -> None: _start_sentry_listener(sc) _set_app_properties() _add_event_processor(sc) -def _patch_spark_context_init(): - # type: () -> None +def _patch_spark_context_init() -> None: from pyspark import SparkContext spark_context_init = SparkContext._do_init @ensure_integration_enabled(SparkIntegration, spark_context_init) - def _sentry_patched_spark_context_init(self, *args, **kwargs): - # type: (SparkContext, *Any, **Any) -> Optional[Any] + def _sentry_patched_spark_context_init( + self: "SparkContext", *args: "Any", **kwargs: "Any" + ) -> "Optional[Any]": rv = spark_context_init(self, *args, **kwargs) _activate_integration(self) return rv @@ -114,8 +107,7 @@ def _sentry_patched_spark_context_init(self, *args, **kwargs): SparkContext._do_init = _sentry_patched_spark_context_init -def _setup_sentry_tracing(): - # type: () -> None +def _setup_sentry_tracing() -> None: from pyspark import SparkContext if SparkContext._active_spark_context is not None: @@ -125,103 +117,79 @@ def _setup_sentry_tracing(): class SparkListener: - def onApplicationEnd(self, applicationEnd): # noqa: N802,N803 - # type: (Any) -> None + def onApplicationEnd(self, applicationEnd: "Any") -> None: # noqa: N802,N803 pass - def onApplicationStart(self, applicationStart): # noqa: N802,N803 - # type: (Any) -> None + def onApplicationStart(self, applicationStart: "Any") -> None: # noqa: N802,N803 pass - def onBlockManagerAdded(self, blockManagerAdded): # noqa: N802,N803 - # type: (Any) -> None + def onBlockManagerAdded(self, blockManagerAdded: "Any") -> None: # noqa: N802,N803 pass - def onBlockManagerRemoved(self, blockManagerRemoved): # noqa: N802,N803 - # type: (Any) -> None + def onBlockManagerRemoved(self, blockManagerRemoved: "Any") -> None: # noqa: N802,N803 pass - def onBlockUpdated(self, blockUpdated): # noqa: N802,N803 - # type: (Any) -> None + def onBlockUpdated(self, blockUpdated: "Any") -> None: # noqa: N802,N803 pass - def onEnvironmentUpdate(self, environmentUpdate): # noqa: N802,N803 - # type: (Any) -> None + def onEnvironmentUpdate(self, environmentUpdate: "Any") -> None: # noqa: N802,N803 pass - def onExecutorAdded(self, executorAdded): # noqa: N802,N803 - # type: (Any) -> None + def onExecutorAdded(self, executorAdded: "Any") -> None: # noqa: N802,N803 pass - def onExecutorBlacklisted(self, executorBlacklisted): # noqa: N802,N803 - # type: (Any) -> None + def onExecutorBlacklisted(self, executorBlacklisted: "Any") -> None: # noqa: N802,N803 pass def onExecutorBlacklistedForStage( # noqa: N802 self, - executorBlacklistedForStage, # noqa: N803 - ): - # type: (Any) -> None + executorBlacklistedForStage: "Any", # noqa: N803 + ) -> None: pass - def onExecutorMetricsUpdate(self, executorMetricsUpdate): # noqa: N802,N803 - # type: (Any) -> None + def onExecutorMetricsUpdate(self, executorMetricsUpdate: "Any") -> None: # noqa: N802,N803 pass - def onExecutorRemoved(self, executorRemoved): # noqa: N802,N803 - # type: (Any) -> None + def onExecutorRemoved(self, executorRemoved: "Any") -> None: # noqa: N802,N803 pass - def onJobEnd(self, jobEnd): # noqa: N802,N803 - # type: (Any) -> None + def onJobEnd(self, jobEnd: "Any") -> None: # noqa: N802,N803 pass - def onJobStart(self, jobStart): # noqa: N802,N803 - # type: (Any) -> None + def onJobStart(self, jobStart: "Any") -> None: # noqa: N802,N803 pass - def onNodeBlacklisted(self, nodeBlacklisted): # noqa: N802,N803 - # type: (Any) -> None + def onNodeBlacklisted(self, nodeBlacklisted: "Any") -> None: # noqa: N802,N803 pass - def onNodeBlacklistedForStage(self, nodeBlacklistedForStage): # noqa: N802,N803 - # type: (Any) -> None + def onNodeBlacklistedForStage(self, nodeBlacklistedForStage: "Any") -> None: # noqa: N802,N803 pass - def onNodeUnblacklisted(self, nodeUnblacklisted): # noqa: N802,N803 - # type: (Any) -> None + def onNodeUnblacklisted(self, nodeUnblacklisted: "Any") -> None: # noqa: N802,N803 pass - def onOtherEvent(self, event): # noqa: N802,N803 - # type: (Any) -> None + def onOtherEvent(self, event: "Any") -> None: # noqa: N802,N803 pass - def onSpeculativeTaskSubmitted(self, speculativeTask): # noqa: N802,N803 - # type: (Any) -> None + def onSpeculativeTaskSubmitted(self, speculativeTask: "Any") -> None: # noqa: N802,N803 pass - def onStageCompleted(self, stageCompleted): # noqa: N802,N803 - # type: (Any) -> None + def onStageCompleted(self, stageCompleted: "Any") -> None: # noqa: N802,N803 pass - def onStageSubmitted(self, stageSubmitted): # noqa: N802,N803 - # type: (Any) -> None + def onStageSubmitted(self, stageSubmitted: "Any") -> None: # noqa: N802,N803 pass - def onTaskEnd(self, taskEnd): # noqa: N802,N803 - # type: (Any) -> None + def onTaskEnd(self, taskEnd: "Any") -> None: # noqa: N802,N803 pass - def onTaskGettingResult(self, taskGettingResult): # noqa: N802,N803 - # type: (Any) -> None + def onTaskGettingResult(self, taskGettingResult: "Any") -> None: # noqa: N802,N803 pass - def onTaskStart(self, taskStart): # noqa: N802,N803 - # type: (Any) -> None + def onTaskStart(self, taskStart: "Any") -> None: # noqa: N802,N803 pass - def onUnpersistRDD(self, unpersistRDD): # noqa: N802,N803 - # type: (Any) -> None + def onUnpersistRDD(self, unpersistRDD: "Any") -> None: # noqa: N802,N803 pass class Java: @@ -231,25 +199,22 @@ class Java: class SentryListener(SparkListener): def _add_breadcrumb( self, - level, # type: str - message, # type: str - data=None, # type: Optional[dict[str, Any]] - ): - # type: (...) -> None + level: str, + message: str, + data: "Optional[dict[str, Any]]" = None, + ) -> None: sentry_sdk.get_isolation_scope().add_breadcrumb( level=level, message=message, data=data ) - def onJobStart(self, jobStart): # noqa: N802,N803 - # type: (Any) -> None + def onJobStart(self, jobStart: "Any") -> None: # noqa: N802,N803 sentry_sdk.get_isolation_scope().clear_breadcrumbs() message = "Job {} Started".format(jobStart.jobId()) self._add_breadcrumb(level="info", message=message) _set_app_properties() - def onJobEnd(self, jobEnd): # noqa: N802,N803 - # type: (Any) -> None + def onJobEnd(self, jobEnd: "Any") -> None: # noqa: N802,N803 level = "" message = "" data = {"result": jobEnd.jobResult().toString()} @@ -263,8 +228,7 @@ def onJobEnd(self, jobEnd): # noqa: N802,N803 self._add_breadcrumb(level=level, message=message, data=data) - def onStageSubmitted(self, stageSubmitted): # noqa: N802,N803 - # type: (Any) -> None + def onStageSubmitted(self, stageSubmitted: "Any") -> None: # noqa: N802,N803 stage_info = stageSubmitted.stageInfo() message = "Stage {} Submitted".format(stage_info.stageId()) @@ -276,8 +240,7 @@ def onStageSubmitted(self, stageSubmitted): # noqa: N802,N803 self._add_breadcrumb(level="info", message=message, data=data) _set_app_properties() - def onStageCompleted(self, stageCompleted): # noqa: N802,N803 - # type: (Any) -> None + def onStageCompleted(self, stageCompleted: "Any") -> None: # noqa: N802,N803 from py4j.protocol import Py4JJavaError # type: ignore stage_info = stageCompleted.stageInfo() @@ -301,8 +264,7 @@ def onStageCompleted(self, stageCompleted): # noqa: N802,N803 self._add_breadcrumb(level=level, message=message, data=data) -def _get_attempt_id(stage_info): - # type: (Any) -> Optional[int] +def _get_attempt_id(stage_info: "Any") -> "Optional[int]": try: return stage_info.attemptId() except Exception: diff --git a/sentry_sdk/integrations/spark/spark_worker.py b/sentry_sdk/integrations/spark/spark_worker.py index 5340a0b350..f1dffdf50b 100644 --- a/sentry_sdk/integrations/spark/spark_worker.py +++ b/sentry_sdk/integrations/spark/spark_worker.py @@ -23,15 +23,13 @@ class SparkWorkerIntegration(Integration): identifier = "spark_worker" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: import pyspark.daemon as original_daemon original_daemon.worker_main = _sentry_worker_main -def _capture_exception(exc_info): - # type: (ExcInfo) -> None +def _capture_exception(exc_info: "ExcInfo") -> None: client = sentry_sdk.get_client() mechanism = {"type": "spark", "handled": False} @@ -53,22 +51,20 @@ def _capture_exception(exc_info): if rv: rv.reverse() hint = event_hint_with_exc_info(exc_info) - event = {"level": "error", "exception": {"values": rv}} # type: Event + event: "Event" = {"level": "error", "exception": {"values": rv}} _tag_task_context() sentry_sdk.capture_event(event, hint=hint) -def _tag_task_context(): - # type: () -> None +def _tag_task_context() -> None: from pyspark.taskcontext import TaskContext scope = sentry_sdk.get_isolation_scope() @scope.add_event_processor - def process_event(event, hint): - # type: (Event, Hint) -> Optional[Event] + def process_event(event: "Event", hint: "Hint") -> "Optional[Event]": with capture_internal_exceptions(): integration = sentry_sdk.get_client().get_integration( SparkWorkerIntegration @@ -103,8 +99,7 @@ def process_event(event, hint): return event -def _sentry_worker_main(*args, **kwargs): - # type: (*Optional[Any], **Optional[Any]) -> None +def _sentry_worker_main(*args: "Optional[Any]", **kwargs: "Optional[Any]") -> None: import pyspark.worker as original_worker try: diff --git a/sentry_sdk/integrations/sqlalchemy.py b/sentry_sdk/integrations/sqlalchemy.py index 0e039f93f3..24a1145223 100644 --- a/sentry_sdk/integrations/sqlalchemy.py +++ b/sentry_sdk/integrations/sqlalchemy.py @@ -29,8 +29,7 @@ class SqlalchemyIntegration(Integration): origin = f"auto.db.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = parse_version(SQLALCHEMY_VERSION) _check_minimum_version(SqlalchemyIntegration, version) @@ -41,9 +40,14 @@ def setup_once(): @ensure_integration_enabled(SqlalchemyIntegration) def _before_cursor_execute( - conn, cursor, statement, parameters, context, executemany, *args -): - # type: (Any, Any, Any, Any, Any, bool, *Any) -> None + conn: "Any", + cursor: "Any", + statement: "Any", + parameters: "Any", + context: "Any", + executemany: bool, + *args: "Any", +) -> None: ctx_mgr = record_sql_queries( cursor, statement, @@ -62,27 +66,34 @@ def _before_cursor_execute( @ensure_integration_enabled(SqlalchemyIntegration) -def _after_cursor_execute(conn, cursor, statement, parameters, context, *args): - # type: (Any, Any, Any, Any, Any, *Any) -> None - ctx_mgr = getattr(context, "_sentry_sql_span_manager", None) # type: Optional[ContextManager[Any]] +def _after_cursor_execute( + conn: "Any", + cursor: "Any", + statement: "Any", + parameters: "Any", + context: "Any", + *args: "Any", +) -> None: + ctx_mgr: Optional[ContextManager[Any]] = getattr( + context, "_sentry_sql_span_manager", None + ) if ctx_mgr is not None: context._sentry_sql_span_manager = None ctx_mgr.__exit__(None, None, None) - span = getattr(context, "_sentry_sql_span", None) # type: Optional[Span] + span: "Optional[Span]" = getattr(context, "_sentry_sql_span", None) if span is not None: with capture_internal_exceptions(): add_query_source(span) -def _handle_error(context, *args): - # type: (Any, *Any) -> None +def _handle_error(context: "Any", *args: "Any") -> None: execution_context = context.execution_context if execution_context is None: return - span = getattr(execution_context, "_sentry_sql_span", None) # type: Optional[Span] + span: "Optional[Span]" = getattr(execution_context, "_sentry_sql_span", None) if span is not None: span.set_status(SPANSTATUS.INTERNAL_ERROR) @@ -90,7 +101,9 @@ def _handle_error(context, *args): # _after_cursor_execute does not get called for crashing SQL stmts. Judging # from SQLAlchemy codebase it does seem like any error coming into this # handler is going to be fatal. - ctx_mgr = getattr(execution_context, "_sentry_sql_span_manager", None) # type: Optional[ContextManager[Any]] + ctx_mgr: "Optional[ContextManager[Any]]" = getattr( + execution_context, "_sentry_sql_span_manager", None + ) if ctx_mgr is not None: execution_context._sentry_sql_span_manager = None @@ -98,8 +111,7 @@ def _handle_error(context, *args): # See: https://docs.sqlalchemy.org/en/20/dialects/index.html -def _get_db_system(name): - # type: (str) -> Optional[str] +def _get_db_system(name: str) -> "Optional[str]": name = str(name) if "sqlite" in name: @@ -120,8 +132,7 @@ def _get_db_system(name): return None -def _set_db_data(span, conn): - # type: (Span, Any) -> None +def _set_db_data(span: "Span", conn: "Any") -> None: db_system = _get_db_system(conn.engine.name) if db_system is not None: span.set_data(SPANDATA.DB_SYSTEM, db_system) diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index 0705da3a4c..c3fa4976a7 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -87,12 +87,11 @@ class StarletteIntegration(Integration): def __init__( self, - transaction_style="url", # type: str - failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Union[Set[int], list[HttpStatusCodeRange], None] - middleware_spans=True, # type: bool - http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: tuple[str, ...] - ): - # type: (...) -> None + transaction_style: str = "url", + failed_request_status_codes: "Union[Set[int], list[HttpStatusCodeRange], None]" = _DEFAULT_FAILED_REQUEST_STATUS_CODES, + middleware_spans: bool = True, + http_methods_to_capture: "tuple[str, ...]" = DEFAULT_HTTP_METHODS_TO_CAPTURE, + ) -> None: if transaction_style not in TRANSACTION_STYLE_VALUES: raise ValueError( "Invalid value for transaction_style: %s (must be in %s)" @@ -103,7 +102,9 @@ def __init__( self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture)) if isinstance(failed_request_status_codes, Set): - self.failed_request_status_codes = failed_request_status_codes # type: Container[int] + self.failed_request_status_codes: "Container[int]" = ( + failed_request_status_codes + ) else: warnings.warn( "Passing a list or None for failed_request_status_codes is deprecated. " @@ -120,8 +121,7 @@ def __init__( ) @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = parse_version(STARLETTE_VERSION) if version is None: @@ -137,12 +137,16 @@ def setup_once(): patch_templates() -def _enable_span_for_middleware(middleware_class): - # type: (Any) -> type +def _enable_span_for_middleware(middleware_class: "Any") -> type: old_call = middleware_class.__call__ - async def _create_span_call(app, scope, receive, send, **kwargs): - # type: (Any, Dict[str, Any], Callable[[], Awaitable[Dict[str, Any]]], Callable[[Dict[str, Any]], Awaitable[None]], Any) -> None + async def _create_span_call( + app: "Any", + scope: "Dict[str, Any]", + receive: "Callable[[], Awaitable[Dict[str, Any]]]", + send: "Callable[[Dict[str, Any]], Awaitable[None]]", + **kwargs: "Any", + ) -> None: integration = sentry_sdk.get_client().get_integration(StarletteIntegration) if integration is None or not integration.middleware_spans: return await old_call(app, scope, receive, send, **kwargs) @@ -165,8 +169,7 @@ async def _create_span_call(app, scope, receive, send, **kwargs): middleware_span.set_tag("starlette.middleware_name", middleware_name) # Creating spans for the "receive" callback - async def _sentry_receive(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def _sentry_receive(*args: "Any", **kwargs: "Any") -> "Any": with sentry_sdk.start_span( op=OP.MIDDLEWARE_STARLETTE_RECEIVE, name=getattr(receive, "__qualname__", str(receive)), @@ -180,8 +183,7 @@ async def _sentry_receive(*args, **kwargs): new_receive = _sentry_receive if not receive_patched else receive # Creating spans for the "send" callback - async def _sentry_send(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def _sentry_send(*args: "Any", **kwargs: "Any") -> "Any": with sentry_sdk.start_span( op=OP.MIDDLEWARE_STARLETTE_SEND, name=getattr(send, "__qualname__", str(send)), @@ -209,8 +211,7 @@ async def _sentry_send(*args, **kwargs): @ensure_integration_enabled(StarletteIntegration) -def _capture_exception(exception, handled=False): - # type: (BaseException, **Any) -> None +def _capture_exception(exception: BaseException, handled: "Any" = False) -> None: event, hint = event_from_exception( exception, client_options=sentry_sdk.get_client().options, @@ -220,8 +221,7 @@ def _capture_exception(exception, handled=False): sentry_sdk.capture_event(event, hint=hint) -def patch_exception_middleware(middleware_class): - # type: (Any) -> None +def patch_exception_middleware(middleware_class: "Any") -> None: """ Capture all exceptions in Starlette app and also extract user information. @@ -232,15 +232,15 @@ def patch_exception_middleware(middleware_class): if not_yet_patched: - def _sentry_middleware_init(self, *args, **kwargs): - # type: (Any, Any, Any) -> None + def _sentry_middleware_init(self: "Any", *args: "Any", **kwargs: "Any") -> None: old_middleware_init(self, *args, **kwargs) # Patch existing exception handlers old_handlers = self._exception_handlers.copy() - async def _sentry_patched_exception_handler(self, *args, **kwargs): - # type: (Any, Any, Any) -> None + async def _sentry_patched_exception_handler( + self: "Any", *args: "Any", **kwargs: "Any" + ) -> None: integration = sentry_sdk.get_client().get_integration( StarletteIntegration ) @@ -278,8 +278,12 @@ async def _sentry_patched_exception_handler(self, *args, **kwargs): old_call = middleware_class.__call__ - async def _sentry_exceptionmiddleware_call(self, scope, receive, send): - # type: (Dict[str, Any], Dict[str, Any], Callable[[], Awaitable[Dict[str, Any]]], Callable[[Dict[str, Any]], Awaitable[None]]) -> None + async def _sentry_exceptionmiddleware_call( + self: "Dict[str, Any]", + scope: "Dict[str, Any]", + receive: "Callable[[], Awaitable[Dict[str, Any]]]", + send: "Callable[[Dict[str, Any]], Awaitable[None]]", + ) -> None: # Also add the user (that was eventually set by be Authentication middle # that was called before this middleware). This is done because the authentication # middleware sets the user in the scope and then (in the same function) @@ -298,8 +302,7 @@ async def _sentry_exceptionmiddleware_call(self, scope, receive, send): @ensure_integration_enabled(StarletteIntegration) -def _add_user_to_sentry_scope(scope): - # type: (Dict[str, Any]) -> None +def _add_user_to_sentry_scope(scope: "Dict[str, Any]") -> None: """ Extracts user information from the ASGI scope and adds it to Sentry's scope. @@ -310,7 +313,7 @@ def _add_user_to_sentry_scope(scope): if not should_send_default_pii(): return - user_info = {} # type: Dict[str, Any] + user_info: "Dict[str, Any]" = {} starlette_user = scope["user"] username = getattr(starlette_user, "username", None) @@ -329,8 +332,7 @@ def _add_user_to_sentry_scope(scope): sentry_scope.set_user(user_info) -def patch_authentication_middleware(middleware_class): - # type: (Any) -> None +def patch_authentication_middleware(middleware_class: "Any") -> None: """ Add user information to Sentry scope. """ @@ -340,16 +342,19 @@ def patch_authentication_middleware(middleware_class): if not_yet_patched: - async def _sentry_authenticationmiddleware_call(self, scope, receive, send): - # type: (Dict[str, Any], Dict[str, Any], Callable[[], Awaitable[Dict[str, Any]]], Callable[[Dict[str, Any]], Awaitable[None]]) -> None + async def _sentry_authenticationmiddleware_call( + self: "Dict[str, Any]", + scope: "Dict[str, Any]", + receive: "Callable[[], Awaitable[Dict[str, Any]]]", + send: "Callable[[Dict[str, Any]], Awaitable[None]]", + ) -> None: await old_call(self, scope, receive, send) _add_user_to_sentry_scope(scope) middleware_class.__call__ = _sentry_authenticationmiddleware_call -def patch_middlewares(): - # type: () -> None +def patch_middlewares() -> None: """ Patches Starlettes `Middleware` class to record spans for every middleware invoked. @@ -360,8 +365,9 @@ def patch_middlewares(): if not_yet_patched: - def _sentry_middleware_init(self, cls, *args, **kwargs): - # type: (Any, Any, Any, Any) -> None + def _sentry_middleware_init( + self: "Any", cls: "Any", *args: "Any", **kwargs: "Any" + ) -> None: if cls == SentryAsgiMiddleware: return old_middleware_init(self, cls, *args, **kwargs) @@ -377,15 +383,15 @@ def _sentry_middleware_init(self, cls, *args, **kwargs): Middleware.__init__ = _sentry_middleware_init -def patch_asgi_app(): - # type: () -> None +def patch_asgi_app() -> None: """ Instrument Starlette ASGI app using the SentryAsgiMiddleware. """ old_app = Starlette.__call__ - async def _sentry_patched_asgi_app(self, scope, receive, send): - # type: (Starlette, StarletteScope, Receive, Send) -> None + async def _sentry_patched_asgi_app( + self: "Starlette", scope: "StarletteScope", receive: "Receive", send: "Send" + ) -> None: integration = sentry_sdk.get_client().get_integration(StarletteIntegration) if integration is None: return await old_app(self, scope, receive, send) @@ -410,8 +416,7 @@ async def _sentry_patched_asgi_app(self, scope, receive, send): # This was vendored in from Starlette to support Starlette 0.19.1 because # this function was only introduced in 0.20.x -def _is_async_callable(obj): - # type: (Any) -> bool +def _is_async_callable(obj: "Any") -> bool: while isinstance(obj, functools.partial): obj = obj.func @@ -420,19 +425,16 @@ def _is_async_callable(obj): ) -def patch_request_response(): - # type: () -> None +def patch_request_response() -> None: old_request_response = starlette.routing.request_response - def _sentry_request_response(func): - # type: (Callable[[Any], Any]) -> ASGIApp + def _sentry_request_response(func: "Callable[[Any], Any]") -> "ASGIApp": old_func = func is_coroutine = _is_async_callable(old_func) if is_coroutine: - async def _sentry_async_func(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def _sentry_async_func(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration( StarletteIntegration ) @@ -451,11 +453,10 @@ async def _sentry_async_func(*args, **kwargs): extractor = StarletteRequestExtractor(request) info = await extractor.extract_request_info() - def _make_request_event_processor(req, integration): - # type: (Any, Any) -> Callable[[Event, dict[str, Any]], Event] - def event_processor(event, hint): - # type: (Event, Dict[str, Any]) -> Event - + def _make_request_event_processor( + req: "Any", integration: "Any" + ) -> "Callable[[Event, dict[str, Any]], Event]": + def event_processor(event: Event, hint: Dict[str, Any]) -> Event: # Add info from request to event request_info = event.get("request", {}) if info: @@ -481,8 +482,7 @@ def event_processor(event, hint): else: @functools.wraps(old_func) - def _sentry_sync_func(*args, **kwargs): - # type: (*Any, **Any) -> Any + def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration( StarletteIntegration ) @@ -506,11 +506,10 @@ def _sentry_sync_func(*args, **kwargs): extractor = StarletteRequestExtractor(request) cookies = extractor.extract_cookies_from_request() - def _make_request_event_processor(req, integration): - # type: (Any, Any) -> Callable[[Event, dict[str, Any]], Event] - def event_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event - + def _make_request_event_processor( + req: "Any", integration: "Any" + ) -> "Callable[[Event, dict[str, Any]], Event]": + def event_processor(event: Event, hint: dict[str, Any]) -> Event: # Extract information from request request_info = event.get("request", {}) if cookies: @@ -536,9 +535,7 @@ def event_processor(event, hint): starlette.routing.request_response = _sentry_request_response -def patch_templates(): - # type: () -> None - +def patch_templates() -> None: # If markupsafe is not installed, then Jinja2 is not installed # (markupsafe is a dependency of Jinja2) # In this case we do not need to patch the Jinja2Templates class @@ -557,10 +554,10 @@ def patch_templates(): if not_yet_patched: - def _sentry_jinja2templates_init(self, *args, **kwargs): - # type: (Jinja2Templates, *Any, **Any) -> None - def add_sentry_trace_meta(request): - # type: (Request) -> Dict[str, Any] + def _sentry_jinja2templates_init( + self: "Jinja2Templates", *args: "Any", **kwargs: "Any" + ) -> None: + def add_sentry_trace_meta(request: Request) -> Dict[str, Any]: trace_meta = Markup( sentry_sdk.get_current_scope().trace_propagation_meta() ) @@ -584,25 +581,26 @@ class StarletteRequestExtractor: (like form data or cookies) and adds it to the Sentry event. """ - request = None # type: Request + request: "Request" = None - def __init__(self, request): - # type: (StarletteRequestExtractor, Request) -> None + def __init__(self: "StarletteRequestExtractor", request: "Request") -> None: self.request = request - def extract_cookies_from_request(self): - # type: (StarletteRequestExtractor) -> Optional[Dict[str, Any]] - cookies = None # type: Optional[Dict[str, Any]] + def extract_cookies_from_request( + self: "StarletteRequestExtractor", + ) -> "Optional[Dict[str, Any]]": + cookies: Optional[Dict[str, Any]] = None if should_send_default_pii(): cookies = self.cookies() return cookies - async def extract_request_info(self): - # type: (StarletteRequestExtractor) -> Optional[Dict[str, Any]] + async def extract_request_info( + self: "StarletteRequestExtractor", + ) -> "Optional[Dict[str, Any]]": client = sentry_sdk.get_client() - request_info = {} # type: Dict[str, Any] + request_info: "Dict[str, Any]" = {} with capture_internal_exceptions(): # Add cookies @@ -646,19 +644,16 @@ async def extract_request_info(self): request_info["data"] = AnnotatedValue.removed_because_raw_data() return request_info - async def content_length(self): - # type: (StarletteRequestExtractor) -> Optional[int] + async def content_length(self: "StarletteRequestExtractor") -> "Optional[int]": if "content-length" in self.request.headers: return int(self.request.headers["content-length"]) return None - def cookies(self): - # type: (StarletteRequestExtractor) -> Dict[str, Any] + def cookies(self: "StarletteRequestExtractor") -> "Dict[str, Any]": return self.request.cookies - async def form(self): - # type: (StarletteRequestExtractor) -> Any + async def form(self: "StarletteRequestExtractor") -> "Any": if multipart is None: return None @@ -670,12 +665,10 @@ async def form(self): return await self.request.form() - def is_json(self): - # type: (StarletteRequestExtractor) -> bool + def is_json(self: "StarletteRequestExtractor") -> bool: return _is_json_content_type(self.request.headers.get("content-type")) - async def json(self): - # type: (StarletteRequestExtractor) -> Optional[Dict[str, Any]] + async def json(self: "StarletteRequestExtractor") -> "Optional[Dict[str, Any]]": if not self.is_json(): return None try: @@ -684,8 +677,7 @@ async def json(self): return None -def _transaction_name_from_router(scope): - # type: (StarletteScope) -> Optional[str] +def _transaction_name_from_router(scope: "StarletteScope") -> "Optional[str]": router = scope.get("router") if not router: return None @@ -702,8 +694,9 @@ def _transaction_name_from_router(scope): return None -def _set_transaction_name_and_source(scope, transaction_style, request): - # type: (sentry_sdk.Scope, str, Any) -> None +def _set_transaction_name_and_source( + scope: "sentry_sdk.Scope", transaction_style: str, request: "Any" +) -> None: name = None source = SOURCE_FOR_STYLE[transaction_style] @@ -722,8 +715,9 @@ def _set_transaction_name_and_source(scope, transaction_style, request): scope.set_transaction_name(name, source=source) -def _get_transaction_from_middleware(app, asgi_scope, integration): - # type: (Any, Dict[str, Any], StarletteIntegration) -> Tuple[Optional[str], Optional[str]] +def _get_transaction_from_middleware( + app: "Any", asgi_scope: "Dict[str, Any]", integration: "StarletteIntegration" +) -> "Tuple[Optional[str], Optional[str]]": name = None source = None diff --git a/sentry_sdk/integrations/starlite.py b/sentry_sdk/integrations/starlite.py index 855b87ad60..75987083d6 100644 --- a/sentry_sdk/integrations/starlite.py +++ b/sentry_sdk/integrations/starlite.py @@ -51,16 +51,16 @@ class StarliteIntegration(Integration): origin = f"auto.http.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: patch_app_init() patch_middlewares() patch_http_route_handle() class SentryStarliteASGIMiddleware(SentryAsgiMiddleware): - def __init__(self, app, span_origin=StarliteIntegration.origin): - # type: (ASGIApp, str) -> None + def __init__( + self, app: "ASGIApp", span_origin: str = StarliteIntegration.origin + ) -> None: super().__init__( app=app, unsafe_context_data=False, @@ -71,8 +71,7 @@ def __init__(self, app, span_origin=StarliteIntegration.origin): ) -def patch_app_init(): - # type: () -> None +def patch_app_init() -> None: """ Replaces the Starlite class's `__init__` function in order to inject `after_exception` handlers and set the `SentryStarliteASGIMiddleware` as the outmost middleware in the stack. @@ -83,8 +82,7 @@ def patch_app_init(): old__init__ = Starlite.__init__ @ensure_integration_enabled(StarliteIntegration, old__init__) - def injection_wrapper(self, *args, **kwargs): - # type: (Starlite, *Any, **Any) -> None + def injection_wrapper(self: "Starlite", *args: "Any", **kwargs: "Any") -> None: after_exception = kwargs.pop("after_exception", []) kwargs.update( after_exception=[ @@ -104,13 +102,11 @@ def injection_wrapper(self, *args, **kwargs): Starlite.__init__ = injection_wrapper -def patch_middlewares(): - # type: () -> None +def patch_middlewares() -> None: old_resolve_middleware_stack = BaseRouteHandler.resolve_middleware @ensure_integration_enabled(StarliteIntegration, old_resolve_middleware_stack) - def resolve_middleware_wrapper(self): - # type: (BaseRouteHandler) -> list[Middleware] + def resolve_middleware_wrapper(self: "BaseRouteHandler") -> "list[Middleware]": return [ enable_span_for_middleware(middleware) for middleware in old_resolve_middleware_stack(self) @@ -119,8 +115,7 @@ def resolve_middleware_wrapper(self): BaseRouteHandler.resolve_middleware = resolve_middleware_wrapper -def enable_span_for_middleware(middleware): - # type: (Middleware) -> Middleware +def enable_span_for_middleware(middleware: "Middleware") -> "Middleware": if ( not hasattr(middleware, "__call__") # noqa: B004 or middleware is SentryStarliteASGIMiddleware @@ -128,12 +123,16 @@ def enable_span_for_middleware(middleware): return middleware if isinstance(middleware, DefineMiddleware): - old_call = middleware.middleware.__call__ # type: ASGIApp + old_call: "ASGIApp" = middleware.middleware.__call__ else: old_call = middleware.__call__ - async def _create_span_call(self, scope, receive, send): - # type: (MiddlewareProtocol, StarliteScope, Receive, Send) -> None + async def _create_span_call( + self: "MiddlewareProtocol", + scope: "StarliteScope", + receive: "Receive", + send: "Send", + ) -> None: if sentry_sdk.get_client().get_integration(StarliteIntegration) is None: return await old_call(self, scope, receive, send) @@ -146,8 +145,9 @@ async def _create_span_call(self, scope, receive, send): middleware_span.set_tag("starlite.middleware_name", middleware_name) # Creating spans for the "receive" callback - async def _sentry_receive(*args, **kwargs): - # type: (*Any, **Any) -> Union[HTTPReceiveMessage, WebSocketReceiveMessage] + async def _sentry_receive( + *args: "Any", **kwargs: "Any" + ) -> "Union[HTTPReceiveMessage, WebSocketReceiveMessage]": if sentry_sdk.get_client().get_integration(StarliteIntegration) is None: return await receive(*args, **kwargs) with sentry_sdk.start_span( @@ -163,8 +163,7 @@ async def _sentry_receive(*args, **kwargs): new_receive = _sentry_receive if not receive_patched else receive # Creating spans for the "send" callback - async def _sentry_send(message): - # type: (Message) -> None + async def _sentry_send(message: "Message") -> None: if sentry_sdk.get_client().get_integration(StarliteIntegration) is None: return await send(message) with sentry_sdk.start_span( @@ -192,17 +191,19 @@ async def _sentry_send(message): return middleware -def patch_http_route_handle(): - # type: () -> None +def patch_http_route_handle() -> None: old_handle = HTTPRoute.handle - async def handle_wrapper(self, scope, receive, send): - # type: (HTTPRoute, HTTPScope, Receive, Send) -> None + async def handle_wrapper( + self: "HTTPRoute", scope: "HTTPScope", receive: "Receive", send: "Send" + ) -> None: if sentry_sdk.get_client().get_integration(StarliteIntegration) is None: return await old_handle(self, scope, receive, send) sentry_scope = sentry_sdk.get_isolation_scope() - request = scope["app"].request_class(scope=scope, receive=receive, send=send) # type: Request[Any, Any] + request: "Request[Any, Any]" = scope["app"].request_class( + scope=scope, receive=receive, send=send + ) extracted_request_data = ConnectionDataExtractor( parse_body=True, parse_query=True )(request) @@ -210,8 +211,7 @@ async def handle_wrapper(self, scope, receive, send): request_data = await body - def event_processor(event, _): - # type: (Event, Hint) -> Event + def event_processor(event: "Event", _: "Hint") -> "Event": route_handler = scope.get("route_handler") request_info = event.get("request", {}) @@ -254,8 +254,7 @@ def event_processor(event, _): HTTPRoute.handle = handle_wrapper -def retrieve_user_from_scope(scope): - # type: (StarliteScope) -> Optional[dict[str, Any]] +def retrieve_user_from_scope(scope: "StarliteScope") -> "Optional[dict[str, Any]]": scope_user = scope.get("user") if not scope_user: return None @@ -274,9 +273,8 @@ def retrieve_user_from_scope(scope): @ensure_integration_enabled(StarliteIntegration) -def exception_handler(exc, scope, _): - # type: (Exception, StarliteScope, State) -> None - user_info = None # type: Optional[dict[str, Any]] +def exception_handler(exc: Exception, scope: "StarliteScope", _: "State") -> None: + user_info: Optional[dict[str, Any]] = None if should_send_default_pii(): user_info = retrieve_user_from_scope(scope) if user_info and isinstance(user_info, dict): diff --git a/sentry_sdk/integrations/statsig.py b/sentry_sdk/integrations/statsig.py index 1d84eb8aa2..42e71a50f5 100644 --- a/sentry_sdk/integrations/statsig.py +++ b/sentry_sdk/integrations/statsig.py @@ -19,8 +19,7 @@ class StatsigIntegration(Integration): identifier = "statsig" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = parse_version(STATSIG_VERSION) _check_minimum_version(StatsigIntegration, version, "statsig") @@ -28,8 +27,9 @@ def setup_once(): old_check_gate = statsig_module.check_gate @wraps(old_check_gate) - def sentry_check_gate(user, gate, *args, **kwargs): - # type: (StatsigUser, str, *Any, **Any) -> Any + def sentry_check_gate( + user: "StatsigUser", gate: str, *args: "Any", **kwargs: "Any" + ) -> "Any": enabled = old_check_gate(user, gate, *args, **kwargs) add_feature_flag(gate, enabled) return enabled diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py index 3db97e5685..bf0c626fa8 100644 --- a/sentry_sdk/integrations/stdlib.py +++ b/sentry_sdk/integrations/stdlib.py @@ -35,25 +35,25 @@ from sentry_sdk._types import Event, Hint -_RUNTIME_CONTEXT = { +_RUNTIME_CONTEXT: "dict[str, object]" = { "name": platform.python_implementation(), "version": "%s.%s.%s" % (sys.version_info[:3]), "build": sys.version, -} # type: dict[str, object] +} class StdlibIntegration(Integration): identifier = "stdlib" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: _install_httplib() _install_subprocess() @add_global_event_processor - def add_python_runtime_context(event, hint): - # type: (Event, Hint) -> Optional[Event] + def add_python_runtime_context( + event: "Event", hint: "Hint" + ) -> "Optional[Event]": if sentry_sdk.get_client().get_integration(StdlibIntegration) is not None: contexts = event.setdefault("contexts", {}) if isinstance(contexts, dict) and "runtime" not in contexts: @@ -62,13 +62,13 @@ def add_python_runtime_context(event, hint): return event -def _install_httplib(): - # type: () -> None +def _install_httplib() -> None: real_putrequest = HTTPConnection.putrequest real_getresponse = HTTPConnection.getresponse - def putrequest(self, method, url, *args, **kwargs): - # type: (HTTPConnection, str, str, *Any, **Any) -> Any + def putrequest( + self: "HTTPConnection", method: str, url: str, *args: "Any", **kwargs: "Any" + ) -> "Any": host = self.host port = self.port default_port = self.default_port @@ -124,8 +124,7 @@ def putrequest(self, method, url, *args, **kwargs): return rv - def getresponse(self, *args, **kwargs): - # type: (HTTPConnection, *Any, **Any) -> Any + def getresponse(self: "HTTPConnection", *args: "Any", **kwargs: "Any") -> "Any": span = getattr(self, "_sentrysdk_span", None) if span is None: @@ -148,8 +147,13 @@ def getresponse(self, *args, **kwargs): HTTPConnection.getresponse = getresponse # type: ignore[method-assign] -def _init_argument(args, kwargs, name, position, setdefault_callback=None): - # type: (List[Any], Dict[Any, Any], str, int, Optional[Callable[[Any], Any]]) -> Any +def _init_argument( + args: "List[Any]", + kwargs: "Dict[Any, Any]", + name: str, + position: int, + setdefault_callback: "Optional[Callable[[Any], Any]]" = None, +) -> "Any": """ given (*args, **kwargs) of a function call, retrieve (and optionally set a default for) an argument by either name or position. @@ -179,13 +183,13 @@ def _init_argument(args, kwargs, name, position, setdefault_callback=None): return rv -def _install_subprocess(): - # type: () -> None +def _install_subprocess() -> None: old_popen_init = subprocess.Popen.__init__ @ensure_integration_enabled(StdlibIntegration, old_popen_init) - def sentry_patched_popen_init(self, *a, **kw): - # type: (subprocess.Popen[Any], *Any, **Any) -> None + def sentry_patched_popen_init( + self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any" + ) -> None: # Convert from tuple to list to be able to set values. a = list(a) @@ -241,8 +245,9 @@ def sentry_patched_popen_init(self, *a, **kw): old_popen_wait = subprocess.Popen.wait @ensure_integration_enabled(StdlibIntegration, old_popen_wait) - def sentry_patched_popen_wait(self, *a, **kw): - # type: (subprocess.Popen[Any], *Any, **Any) -> Any + def sentry_patched_popen_wait( + self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any" + ) -> "Any": with sentry_sdk.start_span( op=OP.SUBPROCESS_WAIT, origin="auto.subprocess.stdlib.subprocess", @@ -255,8 +260,9 @@ def sentry_patched_popen_wait(self, *a, **kw): old_popen_communicate = subprocess.Popen.communicate @ensure_integration_enabled(StdlibIntegration, old_popen_communicate) - def sentry_patched_popen_communicate(self, *a, **kw): - # type: (subprocess.Popen[Any], *Any, **Any) -> Any + def sentry_patched_popen_communicate( + self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any" + ) -> "Any": with sentry_sdk.start_span( op=OP.SUBPROCESS_COMMUNICATE, origin="auto.subprocess.stdlib.subprocess", @@ -267,6 +273,5 @@ def sentry_patched_popen_communicate(self, *a, **kw): subprocess.Popen.communicate = sentry_patched_popen_communicate # type: ignore -def get_subprocess_traceparent_headers(): - # type: () -> EnvironHeaders +def get_subprocess_traceparent_headers() -> "EnvironHeaders": return EnvironHeaders(os.environ, prefix="SUBPROCESS_") diff --git a/sentry_sdk/integrations/strawberry.py b/sentry_sdk/integrations/strawberry.py index f30e95e7f6..8b81673a3d 100644 --- a/sentry_sdk/integrations/strawberry.py +++ b/sentry_sdk/integrations/strawberry.py @@ -63,8 +63,7 @@ class StrawberryIntegration(Integration): identifier = "strawberry" origin = f"auto.graphql.{identifier}" - def __init__(self, async_execution=None): - # type: (Optional[bool]) -> None + def __init__(self, async_execution: "Optional[bool]" = None) -> None: if async_execution not in (None, False, True): raise ValueError( 'Invalid value for async_execution: "{}" (must be bool)'.format( @@ -74,8 +73,7 @@ def __init__(self, async_execution=None): self.async_execution = async_execution @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: version = package_version("strawberry-graphql") _check_minimum_version(StrawberryIntegration, version, "strawberry-graphql") @@ -83,20 +81,20 @@ def setup_once(): _patch_views() -def _patch_schema_init(): - # type: () -> None +def _patch_schema_init() -> None: old_schema_init = Schema.__init__ @functools.wraps(old_schema_init) - def _sentry_patched_schema_init(self, *args, **kwargs): - # type: (Schema, Any, Any) -> None + def _sentry_patched_schema_init( + self: "Schema", *args: "Any", **kwargs: "Any" + ) -> None: integration = sentry_sdk.get_client().get_integration(StrawberryIntegration) if integration is None: return old_schema_init(self, *args, **kwargs) extensions = kwargs.get("extensions") or [] - should_use_async_extension = None # type: Optional[bool] + should_use_async_extension: "Optional[bool]" = None if integration.async_execution is not None: should_use_async_extension = integration.async_execution else: @@ -132,17 +130,15 @@ def _sentry_patched_schema_init(self, *args, **kwargs): class SentryAsyncExtension(SchemaExtension): def __init__( - self, + self: "Any", *, - execution_context=None, - ): - # type: (Any, Optional[ExecutionContext]) -> None + execution_context: "Optional[ExecutionContext]" = None, + ) -> None: if execution_context: self.execution_context = execution_context @cached_property - def _resource_name(self): - # type: () -> str + def _resource_name(self) -> str: query_hash = self.hash_query(self.execution_context.query) # type: ignore if self.execution_context.operation_name: @@ -150,12 +146,10 @@ def _resource_name(self): return query_hash - def hash_query(self, query): - # type: (str) -> str + def hash_query(self, query: str) -> str: return hashlib.md5(query.encode("utf-8")).hexdigest() - def on_operation(self): - # type: () -> Generator[None, None, None] + def on_operation(self) -> "Generator[None, None, None]": self._operation_name = self.execution_context.operation_name operation_type = "query" @@ -216,8 +210,7 @@ def on_operation(self): self.graphql_span.finish() - def on_validate(self): - # type: () -> Generator[None, None, None] + def on_validate(self) -> "Generator[None, None, None]": self.validation_span = self.graphql_span.start_child( op=OP.GRAPHQL_VALIDATE, name="validation", @@ -228,8 +221,7 @@ def on_validate(self): self.validation_span.finish() - def on_parse(self): - # type: () -> Generator[None, None, None] + def on_parse(self) -> "Generator[None, None, None]": self.parsing_span = self.graphql_span.start_child( op=OP.GRAPHQL_PARSE, name="parsing", @@ -240,12 +232,21 @@ def on_parse(self): self.parsing_span.finish() - def should_skip_tracing(self, _next, info): - # type: (Callable[[Any, GraphQLResolveInfo, Any, Any], Any], GraphQLResolveInfo) -> bool + def should_skip_tracing( + self, + _next: "Callable[[Any, GraphQLResolveInfo, Any, Any], Any]", + info: "GraphQLResolveInfo", + ) -> bool: return strawberry_should_skip_tracing(_next, info) - async def _resolve(self, _next, root, info, *args, **kwargs): - # type: (Callable[[Any, GraphQLResolveInfo, Any, Any], Any], Any, GraphQLResolveInfo, str, Any) -> Any + async def _resolve( + self, + _next: "Callable[[Any, GraphQLResolveInfo, Any, Any], Any]", + root: "Any", + info: "GraphQLResolveInfo", + *args: str, + **kwargs: "Any", + ) -> "Any": result = _next(root, info, *args, **kwargs) if isawaitable(result): @@ -253,8 +254,14 @@ async def _resolve(self, _next, root, info, *args, **kwargs): return result - async def resolve(self, _next, root, info, *args, **kwargs): - # type: (Callable[[Any, GraphQLResolveInfo, Any, Any], Any], Any, GraphQLResolveInfo, str, Any) -> Any + async def resolve( + self, + _next: "Callable[[Any, GraphQLResolveInfo, Any, Any], Any]", + root: "Any", + info: "GraphQLResolveInfo", + *args: str, + **kwargs: "Any", + ) -> "Any": if self.should_skip_tracing(_next, info): return await self._resolve(_next, root, info, *args, **kwargs) @@ -274,8 +281,14 @@ async def resolve(self, _next, root, info, *args, **kwargs): class SentrySyncExtension(SentryAsyncExtension): - def resolve(self, _next, root, info, *args, **kwargs): - # type: (Callable[[Any, Any, Any, Any], Any], Any, GraphQLResolveInfo, str, Any) -> Any + def resolve( + self, + _next: "Callable[[Any, Any, Any, Any], Any]", + root: "Any", + info: "GraphQLResolveInfo", + *args: str, + **kwargs: "Any", + ) -> "Any": if self.should_skip_tracing(_next, info): return _next(root, info, *args, **kwargs) @@ -294,24 +307,26 @@ def resolve(self, _next, root, info, *args, **kwargs): return _next(root, info, *args, **kwargs) -def _patch_views(): - # type: () -> None +def _patch_views() -> None: old_async_view_handle_errors = async_base_view.AsyncBaseHTTPView._handle_errors old_sync_view_handle_errors = sync_base_view.SyncBaseHTTPView._handle_errors - def _sentry_patched_async_view_handle_errors(self, errors, response_data): - # type: (Any, List[GraphQLError], GraphQLHTTPResponse) -> None + def _sentry_patched_async_view_handle_errors( + self: "Any", errors: "List[GraphQLError]", response_data: "GraphQLHTTPResponse" + ) -> None: old_async_view_handle_errors(self, errors, response_data) _sentry_patched_handle_errors(self, errors, response_data) - def _sentry_patched_sync_view_handle_errors(self, errors, response_data): - # type: (Any, List[GraphQLError], GraphQLHTTPResponse) -> None + def _sentry_patched_sync_view_handle_errors( + self: "Any", errors: "List[GraphQLError]", response_data: "GraphQLHTTPResponse" + ) -> None: old_sync_view_handle_errors(self, errors, response_data) _sentry_patched_handle_errors(self, errors, response_data) @ensure_integration_enabled(StrawberryIntegration) - def _sentry_patched_handle_errors(self, errors, response_data): - # type: (Any, List[GraphQLError], GraphQLHTTPResponse) -> None + def _sentry_patched_handle_errors( + self: "Any", errors: "List[GraphQLError]", response_data: "GraphQLHTTPResponse" + ) -> None: if not errors: return @@ -339,18 +354,17 @@ def _sentry_patched_handle_errors(self, errors, response_data): ) -def _make_request_event_processor(execution_context): - # type: (ExecutionContext) -> EventProcessor - - def inner(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_request_event_processor( + execution_context: "ExecutionContext", +) -> "EventProcessor": + def inner(event: Event, hint: dict[str, Any]) -> Event: with capture_internal_exceptions(): if should_send_default_pii(): request_data = event.setdefault("request", {}) request_data["api_target"] = "graphql" if not request_data.get("data"): - data = {"query": execution_context.query} # type: dict[str, Any] + data: "dict[str, Any]" = {"query": execution_context.query} if execution_context.variables: data["variables"] = execution_context.variables if execution_context.operation_name: @@ -369,11 +383,10 @@ def inner(event, hint): return inner -def _make_response_event_processor(response_data): - # type: (GraphQLHTTPResponse) -> EventProcessor - - def inner(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_response_event_processor( + response_data: "GraphQLHTTPResponse", +) -> "EventProcessor": + def inner(event: Event, hint: dict[str, Any]) -> Event: with capture_internal_exceptions(): if should_send_default_pii(): contexts = event.setdefault("contexts", {}) @@ -384,8 +397,7 @@ def inner(event, hint): return inner -def _guess_if_using_async(extensions): - # type: (List[SchemaExtension]) -> Optional[bool] +def _guess_if_using_async(extensions: "List[SchemaExtension]") -> "Optional[bool]": if StrawberrySentryAsyncExtension in extensions: return True elif StrawberrySentrySyncExtension in extensions: diff --git a/sentry_sdk/integrations/sys_exit.py b/sentry_sdk/integrations/sys_exit.py index 2341e11359..c59ae5e51f 100644 --- a/sentry_sdk/integrations/sys_exit.py +++ b/sentry_sdk/integrations/sys_exit.py @@ -24,23 +24,19 @@ class SysExitIntegration(Integration): identifier = "sys_exit" - def __init__(self, *, capture_successful_exits=False): - # type: (bool) -> None + def __init__(self, *, capture_successful_exits: bool = False) -> None: self._capture_successful_exits = capture_successful_exits @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: SysExitIntegration._patch_sys_exit() @staticmethod - def _patch_sys_exit(): - # type: () -> None - old_exit = sys.exit # type: Callable[[Union[str, int, None]], NoReturn] + def _patch_sys_exit() -> None: + old_exit: Callable[[Union[str, int, None]], NoReturn] = sys.exit @functools.wraps(old_exit) - def sentry_patched_exit(__status=0): - # type: (Union[str, int, None]) -> NoReturn + def sentry_patched_exit(__status: "Union[str, int, None]" = 0) -> "NoReturn": # @ensure_integration_enabled ensures that this is non-None integration = sentry_sdk.get_client().get_integration(SysExitIntegration) if integration is None: @@ -60,8 +56,7 @@ def sentry_patched_exit(__status=0): sys.exit = sentry_patched_exit -def _capture_exception(exc): - # type: (SystemExit) -> None +def _capture_exception(exc: SystemExit) -> None: event, hint = event_from_exception( exc, client_options=sentry_sdk.get_client().options, diff --git a/sentry_sdk/integrations/threading.py b/sentry_sdk/integrations/threading.py index cfe54c829c..7414e1c61e 100644 --- a/sentry_sdk/integrations/threading.py +++ b/sentry_sdk/integrations/threading.py @@ -31,8 +31,9 @@ class ThreadingIntegration(Integration): identifier = "threading" - def __init__(self, propagate_hub=None, propagate_scope=True): - # type: (Optional[bool], bool) -> None + def __init__( + self, propagate_hub: "Optional[bool]" = None, propagate_scope: bool = True + ) -> None: if propagate_hub is not None: logger.warning( "Deprecated: propagate_hub is deprecated. This will be removed in the future." @@ -48,8 +49,7 @@ def __init__(self, propagate_hub=None, propagate_scope=True): self.propagate_scope = propagate_hub @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: old_start = Thread.start try: @@ -71,8 +71,7 @@ def setup_once(): ) @wraps(old_start) - def sentry_start(self, *a, **kw): - # type: (Thread, *Any, **Any) -> Any + def sentry_start(self: "Thread", *a: "Any", **kw: "Any") -> "Any": integration = sentry_sdk.get_client().get_integration(ThreadingIntegration) if integration is None: return old_start(self, *a, **kw) @@ -118,13 +117,14 @@ def sentry_start(self, *a, **kw): ) -def _wrap_run(isolation_scope_to_use, current_scope_to_use, old_run_func): - # type: (Optional[sentry_sdk.Scope], Optional[sentry_sdk.Scope], F) -> F +def _wrap_run( + isolation_scope_to_use: "Optional[sentry_sdk.Scope]", + current_scope_to_use: "Optional[sentry_sdk.Scope]", + old_run_func: "F", +) -> "F": @wraps(old_run_func) - def run(*a, **kw): - # type: (*Any, **Any) -> Any - def _run_old_run_func(): - # type: () -> Any + def run(*a: Any, **kw: Any) -> Any: + def _run_old_run_func() -> Any: try: self = current_thread() return old_run_func(self, *a[1:], **kw) @@ -141,15 +141,20 @@ def _run_old_run_func(): return run # type: ignore -def _wrap_threadpool_executor_submit(func, is_async_emulated_with_threads): - # type: (Callable[..., Future[T]], bool) -> Callable[..., Future[T]] +def _wrap_threadpool_executor_submit( + func: "Callable[..., Future[T]]", is_async_emulated_with_threads: bool +) -> "Callable[..., Future[T]]": """ Wrap submit call to propagate scopes on task submission. """ @wraps(func) - def sentry_submit(self, fn, *args, **kwargs): - # type: (ThreadPoolExecutor, Callable[..., T], *Any, **Any) -> Future[T] + def sentry_submit( + self: "ThreadPoolExecutor", + fn: "Callable[..., T]", + *args: "Any", + **kwargs: "Any", + ) -> "Future[T]": integration = sentry_sdk.get_client().get_integration(ThreadingIntegration) if integration is None: return func(self, fn, *args, **kwargs) @@ -164,8 +169,7 @@ def sentry_submit(self, fn, *args, **kwargs): isolation_scope = None current_scope = None - def wrapped_fn(*args, **kwargs): - # type: (*Any, **Any) -> Any + def wrapped_fn(*args: "Any", **kwargs: "Any") -> "Any": if isolation_scope is not None and current_scope is not None: with use_isolation_scope(isolation_scope): with use_scope(current_scope): @@ -178,8 +182,7 @@ def wrapped_fn(*args, **kwargs): return sentry_submit -def _capture_exception(): - # type: () -> ExcInfo +def _capture_exception() -> "ExcInfo": exc_info = sys.exc_info() client = sentry_sdk.get_client() diff --git a/sentry_sdk/integrations/tornado.py b/sentry_sdk/integrations/tornado.py index 83fe5e94e8..a4433f4c46 100644 --- a/sentry_sdk/integrations/tornado.py +++ b/sentry_sdk/integrations/tornado.py @@ -47,8 +47,7 @@ class TornadoIntegration(Integration): origin = f"auto.http.{identifier}" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: _check_minimum_version(TornadoIntegration, TORNADO_VERSION) if not HAS_REAL_CONTEXTVARS: @@ -68,16 +67,18 @@ def setup_once(): if awaitable: # Starting Tornado 6 RequestHandler._execute method is a standard Python coroutine (async/await) # In that case our method should be a coroutine function too - async def sentry_execute_request_handler(self, *args, **kwargs): - # type: (RequestHandler, *Any, **Any) -> Any + async def sentry_execute_request_handler( + self: "RequestHandler", *args: "Any", **kwargs: "Any" + ) -> "Any": with _handle_request_impl(self): return await old_execute(self, *args, **kwargs) else: @coroutine # type: ignore - def sentry_execute_request_handler(self, *args, **kwargs): - # type: (RequestHandler, *Any, **Any) -> Any + def sentry_execute_request_handler( + self: "RequestHandler", *args: "Any", **kwargs: "Any" + ) -> "Any": with _handle_request_impl(self): result = yield from old_execute(self, *args, **kwargs) return result @@ -86,8 +87,14 @@ def sentry_execute_request_handler(self, *args, **kwargs): old_log_exception = RequestHandler.log_exception - def sentry_log_exception(self, ty, value, tb, *args, **kwargs): - # type: (Any, type, BaseException, Any, *Any, **Any) -> Optional[Any] + def sentry_log_exception( + self: "Any", + ty: type, + value: BaseException, + tb: "Any", + *args: "Any", + **kwargs: "Any", + ) -> "Optional[Any]": _capture_exception(ty, value, tb) return old_log_exception(self, ty, value, tb, *args, **kwargs) @@ -95,8 +102,7 @@ def sentry_log_exception(self, ty, value, tb, *args, **kwargs): @contextlib.contextmanager -def _handle_request_impl(self): - # type: (RequestHandler) -> Generator[None, None, None] +def _handle_request_impl(self: "RequestHandler") -> "Generator[None, None, None]": integration = sentry_sdk.get_client().get_integration(TornadoIntegration) if integration is None: @@ -130,8 +136,7 @@ def _handle_request_impl(self): @ensure_integration_enabled(TornadoIntegration) -def _capture_exception(ty, value, tb): - # type: (type, BaseException, Any) -> None +def _capture_exception(ty: type, value: BaseException, tb: "Any") -> None: if isinstance(value, HTTPError): return @@ -144,10 +149,10 @@ def _capture_exception(ty, value, tb): sentry_sdk.capture_event(event, hint=hint) -def _make_event_processor(weak_handler): - # type: (Callable[[], RequestHandler]) -> EventProcessor - def tornado_processor(event, hint): - # type: (Event, dict[str, Any]) -> Event +def _make_event_processor( + weak_handler: "Callable[[], RequestHandler]", +) -> "EventProcessor": + def tornado_processor(event: Event, hint: dict[str, Any]) -> Event: handler = weak_handler() if handler is None: return event @@ -186,35 +191,28 @@ def tornado_processor(event, hint): class TornadoRequestExtractor(RequestExtractor): - def content_length(self): - # type: () -> int + def content_length(self) -> int: if self.request.body is None: return 0 return len(self.request.body) - def cookies(self): - # type: () -> Dict[str, str] + def cookies(self) -> "Dict[str, str]": return {k: v.value for k, v in self.request.cookies.items()} - def raw_data(self): - # type: () -> bytes + def raw_data(self) -> bytes: return self.request.body - def form(self): - # type: () -> Dict[str, Any] + def form(self) -> "Dict[str, Any]": return { k: [v.decode("latin1", "replace") for v in vs] for k, vs in self.request.body_arguments.items() } - def is_json(self): - # type: () -> bool + def is_json(self) -> bool: return _is_json_content_type(self.request.headers.get("content-type")) - def files(self): - # type: () -> Dict[str, Any] + def files(self) -> "Dict[str, Any]": return {k: v[0] for k, v in self.request.files.items() if v} - def size_of_file(self, file): - # type: (Any) -> int + def size_of_file(self, file: "Any") -> int: return len(file.body or ()) diff --git a/sentry_sdk/integrations/trytond.py b/sentry_sdk/integrations/trytond.py index 2c44c593a4..03e71734da 100644 --- a/sentry_sdk/integrations/trytond.py +++ b/sentry_sdk/integrations/trytond.py @@ -14,18 +14,18 @@ class TrytondWSGIIntegration(Integration): identifier = "trytond_wsgi" origin = f"auto.http.{identifier}" - def __init__(self): # type: () -> None + def __init__(self) -> None: pass @staticmethod - def setup_once(): # type: () -> None + def setup_once() -> None: app.wsgi_app = SentryWsgiMiddleware( app.wsgi_app, span_origin=TrytondWSGIIntegration.origin, ) @ensure_integration_enabled(TrytondWSGIIntegration) - def error_handler(e): # type: (Exception) -> None + def error_handler(e: Exception) -> None: if isinstance(e, TrytonException): return else: diff --git a/sentry_sdk/integrations/typer.py b/sentry_sdk/integrations/typer.py index 8879d6d0d0..f486ec5e37 100644 --- a/sentry_sdk/integrations/typer.py +++ b/sentry_sdk/integrations/typer.py @@ -30,15 +30,16 @@ class TyperIntegration(Integration): identifier = "typer" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: typer.main.except_hook = _make_excepthook(typer.main.except_hook) # type: ignore -def _make_excepthook(old_excepthook): - # type: (Excepthook) -> Excepthook - def sentry_sdk_excepthook(type_, value, traceback): - # type: (Type[BaseException], BaseException, Optional[TracebackType]) -> None +def _make_excepthook(old_excepthook: "Excepthook") -> "Excepthook": + def sentry_sdk_excepthook( + type_: Type[BaseException], + value: BaseException, + traceback: Optional[TracebackType], + ) -> None: integration = sentry_sdk.get_client().get_integration(TyperIntegration) # Note: If we replace this with ensure_integration_enabled then diff --git a/sentry_sdk/integrations/unleash.py b/sentry_sdk/integrations/unleash.py index 6daa0a411f..304f5c3bd1 100644 --- a/sentry_sdk/integrations/unleash.py +++ b/sentry_sdk/integrations/unleash.py @@ -14,14 +14,14 @@ class UnleashIntegration(Integration): identifier = "unleash" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: # Wrap and patch evaluation methods (class methods) old_is_enabled = UnleashClient.is_enabled @wraps(old_is_enabled) - def sentry_is_enabled(self, feature, *args, **kwargs): - # type: (UnleashClient, str, *Any, **Any) -> Any + def sentry_is_enabled( + self: "UnleashClient", feature: str, *args: "Any", **kwargs: "Any" + ) -> "Any": enabled = old_is_enabled(self, feature, *args, **kwargs) # We have no way of knowing what type of unleash feature this is, so we have to treat diff --git a/sentry_sdk/integrations/unraisablehook.py b/sentry_sdk/integrations/unraisablehook.py index cfb8212c71..95e5784436 100644 --- a/sentry_sdk/integrations/unraisablehook.py +++ b/sentry_sdk/integrations/unraisablehook.py @@ -18,15 +18,14 @@ class UnraisablehookIntegration(Integration): identifier = "unraisablehook" @staticmethod - def setup_once(): - # type: () -> None + def setup_once() -> None: sys.unraisablehook = _make_unraisable(sys.unraisablehook) -def _make_unraisable(old_unraisablehook): - # type: (Callable[[sys.UnraisableHookArgs], Any]) -> Callable[[sys.UnraisableHookArgs], Any] - def sentry_sdk_unraisablehook(unraisable): - # type: (sys.UnraisableHookArgs) -> None +def _make_unraisable( + old_unraisablehook: "Callable[[sys.UnraisableHookArgs], Any]", +) -> "Callable[[sys.UnraisableHookArgs], Any]": + def sentry_sdk_unraisablehook(unraisable: sys.UnraisableHookArgs) -> None: integration = sentry_sdk.get_client().get_integration(UnraisablehookIntegration) # Note: If we replace this with ensure_integration_enabled then diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index fa79ec96da..1576e21a17 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -1,18 +1,18 @@ import sys from functools import partial +from typing import TYPE_CHECKING import sentry_sdk -from sentry_sdk._werkzeug import get_host, _get_headers +from sentry_sdk._werkzeug import _get_headers, get_host from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP -from sentry_sdk.scope import should_send_default_pii from sentry_sdk.integrations._wsgi_common import ( DEFAULT_HTTP_METHODS_TO_CAPTURE, _filter_headers, nullcontext, ) +from sentry_sdk.scope import should_send_default_pii, use_isolation_scope from sentry_sdk.sessions import track_session -from sentry_sdk.scope import use_isolation_scope from sentry_sdk.tracing import Transaction, TransactionSource from sentry_sdk.utils import ( ContextVar, @@ -21,41 +21,36 @@ reraise, ) -from typing import TYPE_CHECKING - if TYPE_CHECKING: - from typing import Callable - from typing import Dict - from typing import Iterator - from typing import Any - from typing import Tuple - from typing import Optional - from typing import TypeVar - from typing import Protocol + from typing import Any, Callable, Dict, Iterator, Optional, Protocol, Tuple, TypeVar - from sentry_sdk.utils import ExcInfo from sentry_sdk._types import Event, EventProcessor + from sentry_sdk.utils import ExcInfo WsgiResponseIter = TypeVar("WsgiResponseIter") WsgiResponseHeaders = TypeVar("WsgiResponseHeaders") WsgiExcInfo = TypeVar("WsgiExcInfo") class StartResponse(Protocol): - def __call__(self, status, response_headers, exc_info=None): # type: ignore - # type: (str, WsgiResponseHeaders, Optional[WsgiExcInfo]) -> WsgiResponseIter + def __call__( + self, + status: str, + response_headers: "WsgiResponseHeaders", + exc_info: "Optional[WsgiExcInfo]" = None, + ) -> "WsgiResponseIter": # type: ignore pass _wsgi_middleware_applied = ContextVar("sentry_wsgi_middleware_applied") -def wsgi_decoding_dance(s, charset="utf-8", errors="replace"): - # type: (str, str, str) -> str +def wsgi_decoding_dance(s: str, charset: str = "utf-8", errors: str = "replace") -> str: return s.encode("latin1").decode(charset, errors) -def get_request_url(environ, use_x_forwarded_for=False): - # type: (Dict[str, str], bool) -> str +def get_request_url( + environ: "Dict[str, str]", use_x_forwarded_for: bool = False +) -> str: """Return the absolute URL without query string for the given WSGI environment.""" script_name = environ.get("SCRIPT_NAME", "").rstrip("/") @@ -79,19 +74,19 @@ class SentryWsgiMiddleware: def __init__( self, - app, # type: Callable[[Dict[str, str], Callable[..., Any]], Any] - use_x_forwarded_for=False, # type: bool - span_origin="manual", # type: str - http_methods_to_capture=DEFAULT_HTTP_METHODS_TO_CAPTURE, # type: Tuple[str, ...] - ): - # type: (...) -> None + app: "Callable[[Dict[str, str], Callable[..., Any]], Any]", + use_x_forwarded_for: bool = False, + span_origin: str = "manual", + http_methods_to_capture: "Tuple[str, ...]" = DEFAULT_HTTP_METHODS_TO_CAPTURE, + ) -> None: self.app = app self.use_x_forwarded_for = use_x_forwarded_for self.span_origin = span_origin self.http_methods_to_capture = http_methods_to_capture - def __call__(self, environ, start_response): - # type: (Dict[str, str], Callable[..., Any]) -> _ScopedResponse + def __call__( + self, environ: "Dict[str, str]", start_response: "Callable[..., Any]" + ) -> "_ScopedResponse": if _wsgi_middleware_applied.get(False): return self.app(environ, start_response) @@ -143,14 +138,13 @@ def __call__(self, environ, start_response): return _ScopedResponse(scope, response) -def _sentry_start_response( # type: ignore - old_start_response, # type: StartResponse - transaction, # type: Optional[Transaction] - status, # type: str - response_headers, # type: WsgiResponseHeaders - exc_info=None, # type: Optional[WsgiExcInfo] -): - # type: (...) -> WsgiResponseIter +def _sentry_start_response( + old_start_response: "StartResponse", + transaction: "Optional[Transaction]", + status: str, + response_headers: "WsgiResponseHeaders", + exc_info: "Optional[WsgiExcInfo]" = None, +) -> "WsgiResponseIter": # type: ignore[type-var] with capture_internal_exceptions(): status_int = int(status.split(" ", 1)[0]) if transaction is not None: @@ -165,8 +159,7 @@ def _sentry_start_response( # type: ignore return old_start_response(status, response_headers, exc_info) -def _get_environ(environ): - # type: (Dict[str, str]) -> Iterator[Tuple[str, str]] +def _get_environ(environ: "Dict[str, str]") -> "Iterator[Tuple[str, str]]": """ Returns our explicitly included environment variables we want to capture (server name, port and remote addr if pii is enabled). @@ -182,8 +175,7 @@ def _get_environ(environ): yield key, environ[key] -def get_client_ip(environ): - # type: (Dict[str, str]) -> Optional[Any] +def get_client_ip(environ: "Dict[str, str]") -> "Optional[Any]": """ Infer the user IP address from various headers. This cannot be used in security sensitive situations since the value may be forged from a client, @@ -202,8 +194,7 @@ def get_client_ip(environ): return environ.get("REMOTE_ADDR") -def _capture_exception(): - # type: () -> ExcInfo +def _capture_exception() -> "ExcInfo": """ Captures the current exception and sends it to Sentry. Returns the ExcInfo tuple to it can be reraised afterwards. @@ -237,13 +228,13 @@ class _ScopedResponse: __slots__ = ("_response", "_scope") - def __init__(self, scope, response): - # type: (sentry_sdk.scope.Scope, Iterator[bytes]) -> None + def __init__( + self, scope: "sentry_sdk.scope.Scope", response: "Iterator[bytes]" + ) -> None: self._scope = scope self._response = response - def __iter__(self): - # type: () -> Iterator[bytes] + def __iter__(self) -> "Iterator[bytes]": iterator = iter(self._response) while True: @@ -257,8 +248,7 @@ def __iter__(self): yield chunk - def close(self): - # type: () -> None + def close(self) -> None: with use_isolation_scope(self._scope): try: self._response.close() # type: ignore @@ -268,8 +258,9 @@ def close(self): reraise(*_capture_exception()) -def _make_wsgi_event_processor(environ, use_x_forwarded_for): - # type: (Dict[str, str], bool) -> EventProcessor +def _make_wsgi_event_processor( + environ: "Dict[str, str]", use_x_forwarded_for: bool +) -> "EventProcessor": # It's a bit unfortunate that we have to extract and parse the request data # from the environ so eagerly, but there are a few good reasons for this. # @@ -289,8 +280,7 @@ def _make_wsgi_event_processor(environ, use_x_forwarded_for): env = dict(_get_environ(environ)) headers = _filter_headers(dict(_get_headers(environ))) - def event_processor(event, hint): - # type: (Event, Dict[str, Any]) -> Event + def event_processor(event: "Event", hint: "Dict[str, Any]") -> "Event": with capture_internal_exceptions(): # if the code below fails halfway through we at least have some data request_info = event.setdefault("request", {}) diff --git a/sentry_sdk/logger.py b/sentry_sdk/logger.py index b90ac034bb..0c8c658881 100644 --- a/sentry_sdk/logger.py +++ b/sentry_sdk/logger.py @@ -21,17 +21,17 @@ class _dict_default_key(dict): # type: ignore[type-arg] """dict that returns the key if missing.""" - def __missing__(self, key): - # type: (str) -> str + def __missing__(self, key: str) -> str: return "{" + key + "}" -def _capture_log(severity_text, severity_number, template, **kwargs): - # type: (str, int, str, **Any) -> None +def _capture_log( + severity_text: str, severity_number: int, template: str, **kwargs: "Any" +) -> None: client = get_client() body = template - attrs = {} # type: dict[str, str | bool | float | int] + attrs: "dict[str, str | bool | float | int]" = {} if "attributes" in kwargs: attrs.update(kwargs.pop("attributes")) for k, v in kwargs.items(): @@ -78,8 +78,7 @@ def _capture_log(severity_text, severity_number, template, **kwargs): fatal = functools.partial(_capture_log, "fatal", 21) -def _otel_severity_text(otel_severity_number): - # type: (int) -> str +def _otel_severity_text(otel_severity_number: int) -> str: for (lower, upper), severity in OTEL_RANGES: if lower <= otel_severity_number <= upper: return severity @@ -87,8 +86,7 @@ def _otel_severity_text(otel_severity_number): return "default" -def _log_level_to_otel(level, mapping): - # type: (int, dict[Any, int]) -> tuple[int, str] +def _log_level_to_otel(level: int, mapping: "dict[Any, int]") -> "tuple[int, str]": for py_level, otel_severity_number in sorted(mapping.items(), reverse=True): if level >= py_level: return otel_severity_number, _otel_severity_text(otel_severity_number) diff --git a/sentry_sdk/metrics.py b/sentry_sdk/metrics.py index 03bde137bd..d8f2159f7e 100644 --- a/sentry_sdk/metrics.py +++ b/sentry_sdk/metrics.py @@ -14,16 +14,15 @@ def _capture_metric( - name, # type: str - metric_type, # type: MetricType - value, # type: float - unit=None, # type: Optional[str] - attributes=None, # type: Optional[dict[str, Any]] -): - # type: (...) -> None + name: str, + metric_type: "MetricType", + value: float, + unit: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, +) -> None: client = sentry_sdk.get_client() - attrs = {} # type: dict[str, Union[str, bool, float, int]] + attrs: "dict[str, Union[str, bool, float, int]]" = {} if attributes: for k, v in attributes.items(): attrs[k] = ( @@ -37,7 +36,7 @@ def _capture_metric( else safe_repr(v) ) - metric = { + metric: "Metric" = { "timestamp": time.time(), "trace_id": None, "span_id": None, @@ -46,36 +45,33 @@ def _capture_metric( "value": float(value), "unit": unit, "attributes": attrs, - } # type: Metric + } client._capture_metric(metric) def count( - name, # type: str - value, # type: float - unit=None, # type: Optional[str] - attributes=None, # type: Optional[dict[str, Any]] -): - # type: (...) -> None + name: str, + value: float, + unit: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, +) -> None: _capture_metric(name, "counter", value, unit, attributes) def gauge( - name, # type: str - value, # type: float - unit=None, # type: Optional[str] - attributes=None, # type: Optional[dict[str, Any]] -): - # type: (...) -> None + name: str, + value: float, + unit: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, +) -> None: _capture_metric(name, "gauge", value, unit, attributes) def distribution( - name, # type: str - value, # type: float - unit=None, # type: Optional[str] - attributes=None, # type: Optional[dict[str, Any]] -): - # type: (...) -> None + name: str, + value: float, + unit: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, +) -> None: _capture_metric(name, "distribution", value, unit, attributes) diff --git a/sentry_sdk/monitor.py b/sentry_sdk/monitor.py index b82a528851..3148f5c272 100644 --- a/sentry_sdk/monitor.py +++ b/sentry_sdk/monitor.py @@ -23,21 +23,21 @@ class Monitor: name = "sentry.monitor" - def __init__(self, transport, interval=10): - # type: (sentry_sdk.transport.Transport, float) -> None - self.transport = transport # type: sentry_sdk.transport.Transport - self.interval = interval # type: float + def __init__( + self, transport: "sentry_sdk.transport.Transport", interval: float = 10 + ) -> None: + self.transport: sentry_sdk.transport.Transport = transport + self.interval: float = interval self._healthy = True - self._downsample_factor = 0 # type: int + self._downsample_factor: int = 0 - self._thread = None # type: Optional[Thread] + self._thread: "Optional[Thread]" = None self._thread_lock = Lock() - self._thread_for_pid = None # type: Optional[int] + self._thread_for_pid: "Optional[int]" = None self._running = True - def _ensure_running(self): - # type: () -> None + def _ensure_running(self) -> None: """ Check that the monitor has an active thread to run in, or create one if not. @@ -52,8 +52,7 @@ def _ensure_running(self): if self._thread_for_pid == os.getpid() and self._thread is not None: return None - def _thread(): - # type: (...) -> None + def _thread() -> None: while self._running: time.sleep(self.interval) if self._running: @@ -74,13 +73,11 @@ def _thread(): return None - def run(self): - # type: () -> None + def run(self) -> None: self.check_health() self.set_downsample_factor() - def set_downsample_factor(self): - # type: () -> None + def set_downsample_factor(self) -> None: if self._healthy: if self._downsample_factor > 0: logger.debug( @@ -95,8 +92,7 @@ def set_downsample_factor(self): self._downsample_factor, ) - def check_health(self): - # type: () -> None + def check_health(self) -> None: """ Perform the actual health checks, currently only checks if the transport is rate-limited. @@ -104,17 +100,14 @@ def check_health(self): """ self._healthy = self.transport.is_healthy() - def is_healthy(self): - # type: () -> bool + def is_healthy(self) -> bool: self._ensure_running() return self._healthy @property - def downsample_factor(self): - # type: () -> int + def downsample_factor(self) -> int: self._ensure_running() return self._downsample_factor - def kill(self): - # type: () -> None + def kill(self) -> None: self._running = False diff --git a/sentry_sdk/profiler/continuous_profiler.py b/sentry_sdk/profiler/continuous_profiler.py index 165bd13837..a4c16a63d5 100644 --- a/sentry_sdk/profiler/continuous_profiler.py +++ b/sentry_sdk/profiler/continuous_profiler.py @@ -61,18 +61,21 @@ from gevent.monkey import get_original from gevent.threadpool import ThreadPool as _ThreadPool - ThreadPool = _ThreadPool # type: Optional[Type[_ThreadPool]] + ThreadPool: "Optional[Type[_ThreadPool]]" = _ThreadPool thread_sleep = get_original("time", "sleep") except ImportError: thread_sleep = time.sleep ThreadPool = None -_scheduler = None # type: Optional[ContinuousScheduler] +_scheduler: "Optional[ContinuousScheduler]" = None -def setup_continuous_profiler(options, sdk_info, capture_func): - # type: (Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> bool +def setup_continuous_profiler( + options: "Dict[str, Any]", + sdk_info: "SDKInfo", + capture_func: "Callable[[Envelope], None]", +) -> bool: global _scheduler already_initialized = _scheduler is not None @@ -125,16 +128,13 @@ def setup_continuous_profiler(options, sdk_info, capture_func): return True -def is_profile_session_sampled(): - # type: () -> bool +def is_profile_session_sampled() -> bool: if _scheduler is None: return False return _scheduler.sampled -def try_autostart_continuous_profiler(): - # type: () -> None - +def try_autostart_continuous_profiler() -> None: # TODO: deprecate this as it'll be replaced by the auto lifecycle option if _scheduler is None: @@ -146,25 +146,21 @@ def try_autostart_continuous_profiler(): _scheduler.manual_start() -def try_profile_lifecycle_trace_start(): - # type: () -> Union[ContinuousProfile, None] +def try_profile_lifecycle_trace_start() -> "Union[ContinuousProfile, None]": if _scheduler is None: return None return _scheduler.auto_start() -def start_profiler(): - # type: () -> None +def start_profiler() -> None: if _scheduler is None: return _scheduler.manual_start() -def start_profile_session(): - # type: () -> None - +def start_profile_session() -> None: warnings.warn( "The `start_profile_session` function is deprecated. Please use `start_profile` instead.", DeprecationWarning, @@ -173,17 +169,14 @@ def start_profile_session(): start_profiler() -def stop_profiler(): - # type: () -> None +def stop_profiler() -> None: if _scheduler is None: return _scheduler.manual_stop() -def stop_profile_session(): - # type: () -> None - +def stop_profile_session() -> None: warnings.warn( "The `stop_profile_session` function is deprecated. Please use `stop_profile` instead.", DeprecationWarning, @@ -192,24 +185,22 @@ def stop_profile_session(): stop_profiler() -def teardown_continuous_profiler(): - # type: () -> None +def teardown_continuous_profiler() -> None: stop_profiler() global _scheduler _scheduler = None -def get_profiler_id(): - # type: () -> Union[str, None] +def get_profiler_id() -> "Union[str, None]": if _scheduler is None: return None return _scheduler.profiler_id -def determine_profile_session_sampling_decision(sample_rate): - # type: (Union[float, None]) -> bool - +def determine_profile_session_sampling_decision( + sample_rate: "Union[float, None]", +) -> bool: # `None` is treated as `0.0` if not sample_rate: return False @@ -220,16 +211,20 @@ def determine_profile_session_sampling_decision(sample_rate): class ContinuousProfile: active: bool = True - def stop(self): - # type: () -> None + def stop(self) -> None: self.active = False class ContinuousScheduler: - mode = "unknown" # type: ContinuousProfilerMode - - def __init__(self, frequency, options, sdk_info, capture_func): - # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None + mode: "ContinuousProfilerMode" = "unknown" + + def __init__( + self, + frequency: int, + options: "Dict[str, Any]", + sdk_info: "SDKInfo", + capture_func: "Callable[[Envelope], None]", + ) -> None: self.interval = 1.0 / frequency self.options = options self.sdk_info = sdk_info @@ -242,18 +237,16 @@ def __init__(self, frequency, options, sdk_info, capture_func): ) self.sampler = self.make_sampler() - self.buffer = None # type: Optional[ProfileBuffer] - self.pid = None # type: Optional[int] + self.buffer: "Optional[ProfileBuffer]" = None + self.pid: "Optional[int]" = None self.running = False self.soft_shutdown = False - self.new_profiles = deque(maxlen=128) # type: Deque[ContinuousProfile] - self.active_profiles = set() # type: Set[ContinuousProfile] - - def is_auto_start_enabled(self): - # type: () -> bool + self.new_profiles: "Deque[ContinuousProfile]" = deque(maxlen=128) + self.active_profiles: "Set[ContinuousProfile]" = set() + def is_auto_start_enabled(self) -> bool: # Ensure that the scheduler only autostarts once per process. # This is necessary because many web servers use forks to spawn # additional processes. And the profiler is only spawned on the @@ -268,8 +261,7 @@ def is_auto_start_enabled(self): return experiments.get("continuous_profiling_auto_start") - def auto_start(self): - # type: () -> Union[ContinuousProfile, None] + def auto_start(self) -> "Union[ContinuousProfile, None]": if not self.sampled: return None @@ -285,8 +277,7 @@ def auto_start(self): return profile - def manual_start(self): - # type: () -> None + def manual_start(self) -> None: if not self.sampled: return @@ -295,48 +286,40 @@ def manual_start(self): self.ensure_running() - def manual_stop(self): - # type: () -> None + def manual_stop(self) -> None: if self.lifecycle != "manual": return self.teardown() - def ensure_running(self): - # type: () -> None + def ensure_running(self) -> None: raise NotImplementedError - def teardown(self): - # type: () -> None + def teardown(self) -> None: raise NotImplementedError - def pause(self): - # type: () -> None + def pause(self) -> None: raise NotImplementedError - def reset_buffer(self): - # type: () -> None + def reset_buffer(self) -> None: self.buffer = ProfileBuffer( self.options, self.sdk_info, PROFILE_BUFFER_SECONDS, self.capture_func ) @property - def profiler_id(self): - # type: () -> Union[str, None] + def profiler_id(self) -> "Union[str, None]": if self.buffer is None: return None return self.buffer.profiler_id - def make_sampler(self): - # type: () -> Callable[..., bool] + def make_sampler(self) -> "Callable[..., bool]": cwd = os.getcwd() cache = LRUCache(max_size=256) if self.lifecycle == "trace": - def _sample_stack(*args, **kwargs): - # type: (*Any, **Any) -> bool + def _sample_stack(*args: "Any", **kwargs: "Any") -> bool: """ Take a sample of the stack on all the threads in the process. This should be called at a regular interval to collect samples. @@ -401,8 +384,7 @@ def _sample_stack(*args, **kwargs): else: - def _sample_stack(*args, **kwargs): - # type: (*Any, **Any) -> bool + def _sample_stack(*args: "Any", **kwargs: "Any") -> bool: """ Take a sample of the stack on all the threads in the process. This should be called at a regular interval to collect samples. @@ -428,8 +410,7 @@ def _sample_stack(*args, **kwargs): return _sample_stack - def run(self): - # type: () -> None + def run(self) -> None: last = time.perf_counter() while self.running: @@ -466,19 +447,22 @@ class ThreadContinuousScheduler(ContinuousScheduler): the sampler at a regular interval. """ - mode = "thread" # type: ContinuousProfilerMode + mode: "ContinuousProfilerMode" = "thread" name = "sentry.profiler.ThreadContinuousScheduler" - def __init__(self, frequency, options, sdk_info, capture_func): - # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None + def __init__( + self, + frequency: int, + options: "Dict[str, Any]", + sdk_info: "SDKInfo", + capture_func: "Callable[[Envelope], None]", + ) -> None: super().__init__(frequency, options, sdk_info, capture_func) - self.thread = None # type: Optional[threading.Thread] + self.thread: "Optional[threading.Thread]" = None self.lock = threading.Lock() - def ensure_running(self): - # type: () -> None - + def ensure_running(self) -> None: self.soft_shutdown = False pid = os.getpid() @@ -514,8 +498,7 @@ def ensure_running(self): self.running = False self.thread = None - def teardown(self): - # type: () -> None + def teardown(self) -> None: if self.running: self.running = False @@ -540,22 +523,24 @@ class GeventContinuousScheduler(ContinuousScheduler): results in a sample containing only the sampler's code. """ - mode = "gevent" # type: ContinuousProfilerMode - - def __init__(self, frequency, options, sdk_info, capture_func): - # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None + mode: "ContinuousProfilerMode" = "gevent" + def __init__( + self, + frequency: int, + options: "Dict[str, Any]", + sdk_info: "SDKInfo", + capture_func: "Callable[[Envelope], None]", + ) -> None: if ThreadPool is None: raise ValueError("Profiler mode: {} is not available".format(self.mode)) super().__init__(frequency, options, sdk_info, capture_func) - self.thread = None # type: Optional[_ThreadPool] + self.thread: "Optional[_ThreadPool]" = None self.lock = threading.Lock() - def ensure_running(self): - # type: () -> None - + def ensure_running(self) -> None: self.soft_shutdown = False pid = os.getpid() @@ -587,8 +572,7 @@ def ensure_running(self): self.running = False self.thread = None - def teardown(self): - # type: () -> None + def teardown(self) -> None: if self.running: self.running = False @@ -603,8 +587,13 @@ def teardown(self): class ProfileBuffer: - def __init__(self, options, sdk_info, buffer_size, capture_func): - # type: (Dict[str, Any], SDKInfo, int, Callable[[Envelope], None]) -> None + def __init__( + self, + options: "Dict[str, Any]", + sdk_info: "SDKInfo", + buffer_size: int, + capture_func: "Callable[[Envelope], None]", + ) -> None: self.options = options self.sdk_info = sdk_info self.buffer_size = buffer_size @@ -626,8 +615,7 @@ def __init__(self, options, sdk_info, buffer_size, capture_func): datetime.now(timezone.utc).timestamp() - self.start_monotonic_time ) - def write(self, monotonic_time, sample): - # type: (float, ExtractedSample) -> None + def write(self, monotonic_time: float, sample: "ExtractedSample") -> None: if self.should_flush(monotonic_time): self.flush() self.chunk = ProfileChunk() @@ -635,15 +623,12 @@ def write(self, monotonic_time, sample): self.chunk.write(self.start_timestamp + monotonic_time, sample) - def should_flush(self, monotonic_time): - # type: (float) -> bool - + def should_flush(self, monotonic_time: float) -> bool: # If the delta between the new monotonic time and the start monotonic time # exceeds the buffer size, it means we should flush the chunk return monotonic_time - self.start_monotonic_time >= self.buffer_size - def flush(self): - # type: () -> None + def flush(self) -> None: chunk = self.chunk.to_json(self.profiler_id, self.options, self.sdk_info) envelope = Envelope() envelope.add_profile_chunk(chunk) @@ -651,18 +636,16 @@ def flush(self): class ProfileChunk: - def __init__(self): - # type: () -> None + def __init__(self) -> None: self.chunk_id = uuid.uuid4().hex - self.indexed_frames = {} # type: Dict[FrameId, int] - self.indexed_stacks = {} # type: Dict[StackId, int] - self.frames = [] # type: List[ProcessedFrame] - self.stacks = [] # type: List[ProcessedStack] - self.samples = [] # type: List[ProcessedSample] + self.indexed_frames: "Dict[FrameId, int]" = {} + self.indexed_stacks: "Dict[StackId, int]" = {} + self.frames: "List[ProcessedFrame]" = [] + self.stacks: "List[ProcessedStack]" = [] + self.samples: "List[ProcessedSample]" = [] - def write(self, ts, sample): - # type: (float, ExtractedSample) -> None + def write(self, ts: float, sample: "ExtractedSample") -> None: for tid, (stack_id, frame_ids, frames) in sample: try: # Check if the stack is indexed first, this lets us skip @@ -690,8 +673,9 @@ def write(self, ts, sample): # When this happens, we abandon the current sample as it's bad. capture_internal_exception(sys.exc_info()) - def to_json(self, profiler_id, options, sdk_info): - # type: (str, Dict[str, Any], SDKInfo) -> Dict[str, Any] + def to_json( + self, profiler_id: str, options: "Dict[str, Any]", sdk_info: "SDKInfo" + ) -> "Dict[str, Any]": profile = { "frames": self.frames, "stacks": self.stacks, diff --git a/sentry_sdk/profiler/transaction_profiler.py b/sentry_sdk/profiler/transaction_profiler.py index d228f77de9..f4471cd33f 100644 --- a/sentry_sdk/profiler/transaction_profiler.py +++ b/sentry_sdk/profiler/transaction_profiler.py @@ -102,7 +102,7 @@ from gevent.monkey import get_original from gevent.threadpool import ThreadPool as _ThreadPool - ThreadPool = _ThreadPool # type: Optional[Type[_ThreadPool]] + ThreadPool: "Optional[Type[_ThreadPool]]" = _ThreadPool thread_sleep = get_original("time", "sleep") except ImportError: thread_sleep = time.sleep @@ -110,7 +110,7 @@ ThreadPool = None -_scheduler = None # type: Optional[Scheduler] +_scheduler: "Optional[Scheduler]" = None # The minimum number of unique samples that must exist in a profile to be @@ -118,8 +118,7 @@ PROFILE_MINIMUM_SAMPLES = 2 -def has_profiling_enabled(options): - # type: (Dict[str, Any]) -> bool +def has_profiling_enabled(options: "Dict[str, Any]") -> bool: profiles_sampler = options["profiles_sampler"] if profiles_sampler is not None: return True @@ -141,8 +140,7 @@ def has_profiling_enabled(options): return False -def setup_profiler(options): - # type: (Dict[str, Any]) -> bool +def setup_profiler(options: "Dict[str, Any]") -> bool: global _scheduler if _scheduler is not None: @@ -192,9 +190,7 @@ def setup_profiler(options): return True -def teardown_profiler(): - # type: () -> None - +def teardown_profiler() -> None: global _scheduler if _scheduler is not None: @@ -209,41 +205,40 @@ def teardown_profiler(): class Profile: def __init__( self, - sampled, # type: Optional[bool] - start_ns, # type: int - hub=None, # type: Optional[sentry_sdk.Hub] - scheduler=None, # type: Optional[Scheduler] - ): - # type: (...) -> None + sampled: "Optional[bool]", + start_ns: int, + hub: "Optional[sentry_sdk.Hub]" = None, + scheduler: "Optional[Scheduler]" = None, + ) -> None: self.scheduler = _scheduler if scheduler is None else scheduler - self.event_id = uuid.uuid4().hex # type: str + self.event_id: str = uuid.uuid4().hex - self.sampled = sampled # type: Optional[bool] + self.sampled: "Optional[bool]" = sampled # Various framework integrations are capable of overwriting the active thread id. # If it is set to `None` at the end of the profile, we fall back to the default. - self._default_active_thread_id = get_current_thread_meta()[0] or 0 # type: int - self.active_thread_id = None # type: Optional[int] + self._default_active_thread_id: int = get_current_thread_meta()[0] or 0 + self.active_thread_id: "Optional[int]" = None try: - self.start_ns = start_ns # type: int + self.start_ns: int = start_ns except AttributeError: self.start_ns = 0 - self.stop_ns = 0 # type: int - self.active = False # type: bool + self.stop_ns: int = 0 + self.active: bool = False - self.indexed_frames = {} # type: Dict[FrameId, int] - self.indexed_stacks = {} # type: Dict[StackId, int] - self.frames = [] # type: List[ProcessedFrame] - self.stacks = [] # type: List[ProcessedStack] - self.samples = [] # type: List[ProcessedSample] + self.indexed_frames: "Dict[FrameId, int]" = {} + self.indexed_stacks: "Dict[StackId, int]" = {} + self.frames: "List[ProcessedFrame]" = [] + self.stacks: "List[ProcessedStack]" = [] + self.samples: "List[ProcessedSample]" = [] self.unique_samples = 0 # Backwards compatibility with the old hub property - self._hub = None # type: Optional[sentry_sdk.Hub] + self._hub: "Optional[sentry_sdk.Hub]" = None if hub is not None: self._hub = hub warnings.warn( @@ -252,8 +247,7 @@ def __init__( stacklevel=2, ) - def update_active_thread_id(self): - # type: () -> None + def update_active_thread_id(self) -> None: self.active_thread_id = get_current_thread_meta()[0] logger.debug( "[Profiling] updating active thread id to {tid}".format( @@ -261,8 +255,9 @@ def update_active_thread_id(self): ) ) - def _set_initial_sampling_decision(self, sampling_context): - # type: (SamplingContext) -> None + def _set_initial_sampling_decision( + self, sampling_context: "SamplingContext" + ) -> None: """ Sets the profile's sampling decision according to the following precedence rules: @@ -334,8 +329,7 @@ def _set_initial_sampling_decision(self, sampling_context): ) ) - def start(self): - # type: () -> None + def start(self) -> None: if not self.sampled or self.active: return @@ -346,8 +340,7 @@ def start(self): self.start_ns = nanosecond_time() self.scheduler.start_profiling(self) - def stop(self): - # type: () -> None + def stop(self) -> None: if not self.sampled or not self.active: return @@ -356,8 +349,7 @@ def stop(self): self.active = False self.stop_ns = nanosecond_time() - def __enter__(self): - # type: () -> Profile + def __enter__(self) -> "Profile": scope = sentry_sdk.get_isolation_scope() old_profile = scope.profile scope.profile = self @@ -368,8 +360,9 @@ def __enter__(self): return self - def __exit__(self, ty, value, tb): - # type: (Optional[Any], Optional[Any], Optional[Any]) -> None + def __exit__( + self, ty: "Optional[Any]", value: "Optional[Any]", tb: "Optional[Any]" + ) -> None: with capture_internal_exceptions(): self.stop() @@ -378,8 +371,7 @@ def __exit__(self, ty, value, tb): scope.profile = old_profile - def write(self, ts, sample): - # type: (int, ExtractedSample) -> None + def write(self, ts: int, sample: "ExtractedSample") -> None: if not self.active: return @@ -422,18 +414,16 @@ def write(self, ts, sample): # When this happens, we abandon the current sample as it's bad. capture_internal_exception(sys.exc_info()) - def process(self): - # type: () -> ProcessedProfile - + def process(self) -> "ProcessedProfile": # This collects the thread metadata at the end of a profile. Doing it # this way means that any threads that terminate before the profile ends # will not have any metadata associated with it. - thread_metadata = { + thread_metadata: Dict[str, ProcessedThreadMetadata] = { str(thread.ident): { "name": str(thread.name), } for thread in threading.enumerate() - } # type: Dict[str, ProcessedThreadMetadata] + } return { "frames": self.frames, @@ -442,8 +432,9 @@ def process(self): "thread_metadata": thread_metadata, } - def to_json(self, event_opt, options): - # type: (Event, Dict[str, Any]) -> Dict[str, Any] + def to_json( + self, event_opt: "Event", options: "Dict[str, Any]" + ) -> "Dict[str, Any]": profile = self.process() set_in_app_in_frames( @@ -493,8 +484,7 @@ def to_json(self, event_opt, options): ], } - def valid(self): - # type: () -> bool + def valid(self) -> bool: client = sentry_sdk.get_client() if not client.is_active(): return False @@ -520,8 +510,7 @@ def valid(self): return True @property - def hub(self): - # type: () -> Optional[sentry_sdk.Hub] + def hub(self) -> "Optional[sentry_sdk.Hub]": warnings.warn( "The `hub` attribute is deprecated. Please do not access it.", DeprecationWarning, @@ -530,8 +519,7 @@ def hub(self): return self._hub @hub.setter - def hub(self, value): - # type: (Optional[sentry_sdk.Hub]) -> None + def hub(self, value: "Optional[sentry_sdk.Hub]") -> None: warnings.warn( "The `hub` attribute is deprecated. Please do not set it.", DeprecationWarning, @@ -541,39 +529,35 @@ def hub(self, value): class Scheduler(ABC): - mode = "unknown" # type: ProfilerMode + mode: "ProfilerMode" = "unknown" - def __init__(self, frequency): - # type: (int) -> None + def __init__(self, frequency: int) -> None: self.interval = 1.0 / frequency self.sampler = self.make_sampler() # cap the number of new profiles at any time so it does not grow infinitely - self.new_profiles = deque(maxlen=128) # type: Deque[Profile] - self.active_profiles = set() # type: Set[Profile] + self.new_profiles: "Deque[Profile]" = deque(maxlen=128) + self.active_profiles: "Set[Profile]" = set() - def __enter__(self): - # type: () -> Scheduler + def __enter__(self) -> "Scheduler": self.setup() return self - def __exit__(self, ty, value, tb): - # type: (Optional[Any], Optional[Any], Optional[Any]) -> None + def __exit__( + self, ty: "Optional[Any]", value: "Optional[Any]", tb: "Optional[Any]" + ) -> None: self.teardown() @abstractmethod - def setup(self): - # type: () -> None + def setup(self) -> None: pass @abstractmethod - def teardown(self): - # type: () -> None + def teardown(self) -> None: pass - def ensure_running(self): - # type: () -> None + def ensure_running(self) -> None: """ Ensure the scheduler is running. By default, this method is a no-op. The method should be overridden by any implementation for which it is @@ -581,19 +565,16 @@ def ensure_running(self): """ return None - def start_profiling(self, profile): - # type: (Profile) -> None + def start_profiling(self, profile: "Profile") -> None: self.ensure_running() self.new_profiles.append(profile) - def make_sampler(self): - # type: () -> Callable[..., None] + def make_sampler(self) -> "Callable[..., None]": cwd = os.getcwd() cache = LRUCache(max_size=256) - def _sample_stack(*args, **kwargs): - # type: (*Any, **Any) -> None + def _sample_stack(*args: "Any", **kwargs: "Any") -> None: """ Take a sample of the stack on all the threads in the process. This should be called at a regular interval to collect samples. @@ -664,32 +645,28 @@ class ThreadScheduler(Scheduler): the sampler at a regular interval. """ - mode = "thread" # type: ProfilerMode + mode: "ProfilerMode" = "thread" name = "sentry.profiler.ThreadScheduler" - def __init__(self, frequency): - # type: (int) -> None + def __init__(self, frequency: int) -> None: super().__init__(frequency=frequency) # used to signal to the thread that it should stop self.running = False - self.thread = None # type: Optional[threading.Thread] - self.pid = None # type: Optional[int] + self.thread: "Optional[threading.Thread]" = None + self.pid: "Optional[int]" = None self.lock = threading.Lock() - def setup(self): - # type: () -> None + def setup(self) -> None: pass - def teardown(self): - # type: () -> None + def teardown(self) -> None: if self.running: self.running = False if self.thread is not None: self.thread.join() - def ensure_running(self): - # type: () -> None + def ensure_running(self) -> None: """ Check that the profiler has an active thread to run in, and start one if that's not the case. @@ -727,8 +704,7 @@ def ensure_running(self): self.thread = None return - def run(self): - # type: () -> None + def run(self) -> None: last = time.perf_counter() while self.running: @@ -760,12 +736,10 @@ class GeventScheduler(Scheduler): results in a sample containing only the sampler's code. """ - mode = "gevent" # type: ProfilerMode + mode: "ProfilerMode" = "gevent" name = "sentry.profiler.GeventScheduler" - def __init__(self, frequency): - # type: (int) -> None - + def __init__(self, frequency: int) -> None: if ThreadPool is None: raise ValueError("Profiler mode: {} is not available".format(self.mode)) @@ -773,27 +747,24 @@ def __init__(self, frequency): # used to signal to the thread that it should stop self.running = False - self.thread = None # type: Optional[_ThreadPool] - self.pid = None # type: Optional[int] + self.thread: "Optional[_ThreadPool]" = None + self.pid: "Optional[int]" = None # This intentionally uses the gevent patched threading.Lock. # The lock will be required when first trying to start profiles # as we need to spawn the profiler thread from the greenlets. self.lock = threading.Lock() - def setup(self): - # type: () -> None + def setup(self) -> None: pass - def teardown(self): - # type: () -> None + def teardown(self) -> None: if self.running: self.running = False if self.thread is not None: self.thread.join() - def ensure_running(self): - # type: () -> None + def ensure_running(self) -> None: pid = os.getpid() # is running on the right process @@ -820,8 +791,7 @@ def ensure_running(self): self.thread = None return - def run(self): - # type: () -> None + def run(self) -> None: last = time.perf_counter() while self.running: diff --git a/sentry_sdk/profiler/utils.py b/sentry_sdk/profiler/utils.py index 7d311e91f4..3d122101ad 100644 --- a/sentry_sdk/profiler/utils.py +++ b/sentry_sdk/profiler/utils.py @@ -63,15 +63,12 @@ if PY311: - def get_frame_name(frame): - # type: (FrameType) -> str + def get_frame_name(frame: "FrameType") -> str: return frame.f_code.co_qualname else: - def get_frame_name(frame): - # type: (FrameType) -> str - + def get_frame_name(frame: "FrameType") -> str: f_code = frame.f_code co_varnames = f_code.co_varnames @@ -113,13 +110,11 @@ def get_frame_name(frame): return name -def frame_id(raw_frame): - # type: (FrameType) -> FrameId +def frame_id(raw_frame: "FrameType") -> "FrameId": return (raw_frame.f_code.co_filename, raw_frame.f_lineno, get_frame_name(raw_frame)) -def extract_frame(fid, raw_frame, cwd): - # type: (FrameId, FrameType, str) -> ProcessedFrame +def extract_frame(fid: "FrameId", raw_frame: "FrameType", cwd: str) -> "ProcessedFrame": abs_path = raw_frame.f_code.co_filename try: @@ -148,12 +143,11 @@ def extract_frame(fid, raw_frame, cwd): def extract_stack( - raw_frame, # type: Optional[FrameType] - cache, # type: LRUCache - cwd, # type: str - max_stack_depth=MAX_STACK_DEPTH, # type: int -): - # type: (...) -> ExtractedStack + raw_frame: "Optional[FrameType]", + cache: "LRUCache", + cwd: str, + max_stack_depth: int = MAX_STACK_DEPTH, +) -> "ExtractedStack": """ Extracts the stack starting the specified frame. The extracted stack assumes the specified frame is the top of the stack, and works back @@ -163,7 +157,7 @@ def extract_stack( only the first `MAX_STACK_DEPTH` frames will be returned. """ - raw_frames = deque(maxlen=max_stack_depth) # type: Deque[FrameType] + raw_frames: "Deque[FrameType]" = deque(maxlen=max_stack_depth) while raw_frame is not None: f_back = raw_frame.f_back diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 466e1b5b12..4bb7d1fb6f 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -93,7 +93,7 @@ # In case this is a http server (think web framework) with multiple users # the data will be added to events of all users. # Typically this is used for process wide data such as the release. -_global_scope = None # type: Optional[Scope] +_global_scope: "Optional[Scope]" = None # Holds data for the active request. # This is used to isolate data for different requests or users. @@ -105,11 +105,11 @@ # This can be used to manually add additional data to a span. _current_scope = ContextVar("current_scope", default=None) -global_event_processors = [] # type: List[EventProcessor] +global_event_processors: "List[EventProcessor]" = [] # A function returning a (trace_id, span_id) tuple # from an external tracing source (such as otel) -_external_propagation_context_fn = None # type: Optional[Callable[[], Optional[Tuple[str, str]]]] +_external_propagation_context_fn: "Optional[Callable[[], Optional[Tuple[str, str]]]]" = None class ScopeType(Enum): @@ -120,12 +120,10 @@ class ScopeType(Enum): class _ScopeManager: - def __init__(self, hub=None): - # type: (Optional[Any]) -> None - self._old_scopes = [] # type: List[Scope] + def __init__(self, hub: "Optional[Any]" = None) -> None: + self._old_scopes: List[Scope] = [] - def __enter__(self): - # type: () -> Scope + def __enter__(self) -> "Scope": isolation_scope = Scope.get_isolation_scope() self._old_scopes.append(isolation_scope) @@ -135,46 +133,40 @@ def __enter__(self): return forked_scope - def __exit__(self, exc_type, exc_value, tb): - # type: (Any, Any, Any) -> None + def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None: old_scope = self._old_scopes.pop() _isolation_scope.set(old_scope) -def add_global_event_processor(processor): - # type: (EventProcessor) -> None +def add_global_event_processor(processor: "EventProcessor") -> None: global_event_processors.append(processor) -def register_external_propagation_context(fn): - # type: (Callable[[], Optional[Tuple[str, str]]]) -> None +def register_external_propagation_context( + fn: "Callable[[], Optional[Tuple[str, str]]]", +) -> None: global _external_propagation_context_fn _external_propagation_context_fn = fn -def remove_external_propagation_context(): - # type: () -> None +def remove_external_propagation_context() -> None: global _external_propagation_context_fn _external_propagation_context_fn = None -def get_external_propagation_context(): - # type: () -> Optional[Tuple[str, str]] +def get_external_propagation_context() -> "Optional[Tuple[str, str]]": return ( _external_propagation_context_fn() if _external_propagation_context_fn else None ) -def _attr_setter(fn): - # type: (Any) -> Any +def _attr_setter(fn: "Any") -> "Any": return property(fset=fn, doc=fn.__doc__) -def _disable_capture(fn): - # type: (F) -> F +def _disable_capture(fn: "F") -> "F": @wraps(fn) - def wrapper(self, *args, **kwargs): - # type: (Any, *Dict[str, Any], **Any) -> Any + def wrapper(self: Any, *args: Dict[str, Any], **kwargs: Any) -> Any: if not self._should_capture: return try: @@ -227,19 +219,22 @@ class Scope: "_flags", ) - def __init__(self, ty=None, client=None): - # type: (Optional[ScopeType], Optional[sentry_sdk.Client]) -> None + def __init__( + self, + ty: "Optional[ScopeType]" = None, + client: "Optional[sentry_sdk.Client]" = None, + ) -> None: self._type = ty - self._event_processors = [] # type: List[EventProcessor] - self._error_processors = [] # type: List[ErrorProcessor] + self._event_processors: "List[EventProcessor]" = [] + self._error_processors: "List[ErrorProcessor]" = [] - self._name = None # type: Optional[str] - self._propagation_context = None # type: Optional[PropagationContext] - self._n_breadcrumbs_truncated = 0 # type: int - self._gen_ai_original_message_count = {} # type: Dict[str, int] + self._name: "Optional[str]" = None + self._propagation_context: "Optional[PropagationContext]" = None + self._n_breadcrumbs_truncated: int = 0 + self._gen_ai_original_message_count: "Dict[str, int]" = {} - self.client = NonRecordingClient() # type: sentry_sdk.client.BaseClient + self.client: "sentry_sdk.client.BaseClient" = NonRecordingClient() if client is not None: self.set_client(client) @@ -249,13 +244,12 @@ def __init__(self, ty=None, client=None): incoming_trace_information = self._load_trace_data_from_env() self.generate_propagation_context(incoming_data=incoming_trace_information) - def __copy__(self): - # type: () -> Scope + def __copy__(self) -> "Scope": """ Returns a copy of this scope. This also creates a copy of all referenced data structures. """ - rv = object.__new__(self.__class__) # type: Scope + rv: "Scope" = object.__new__(self.__class__) rv._type = self._type rv.client = self.client @@ -292,8 +286,7 @@ def __copy__(self): return rv @classmethod - def get_current_scope(cls): - # type: () -> Scope + def get_current_scope(cls) -> "Scope": """ .. versionadded:: 2.0.0 @@ -307,8 +300,7 @@ def get_current_scope(cls): return current_scope @classmethod - def set_current_scope(cls, new_current_scope): - # type: (Scope) -> None + def set_current_scope(cls, new_current_scope: "Scope") -> None: """ .. versionadded:: 2.0.0 @@ -318,8 +310,7 @@ def set_current_scope(cls, new_current_scope): _current_scope.set(new_current_scope) @classmethod - def get_isolation_scope(cls): - # type: () -> Scope + def get_isolation_scope(cls) -> "Scope": """ .. versionadded:: 2.0.0 @@ -333,8 +324,7 @@ def get_isolation_scope(cls): return isolation_scope @classmethod - def set_isolation_scope(cls, new_isolation_scope): - # type: (Scope) -> None + def set_isolation_scope(cls, new_isolation_scope: "Scope") -> None: """ .. versionadded:: 2.0.0 @@ -344,8 +334,7 @@ def set_isolation_scope(cls, new_isolation_scope): _isolation_scope.set(new_isolation_scope) @classmethod - def get_global_scope(cls): - # type: () -> Scope + def get_global_scope(cls) -> "Scope": """ .. versionadded:: 2.0.0 @@ -358,8 +347,7 @@ def get_global_scope(cls): return _global_scope @classmethod - def last_event_id(cls): - # type: () -> Optional[str] + def last_event_id(cls) -> "Optional[str]": """ .. versionadded:: 2.2.0 @@ -374,8 +362,11 @@ def last_event_id(cls): """ return cls.get_isolation_scope()._last_event_id - def _merge_scopes(self, additional_scope=None, additional_scope_kwargs=None): - # type: (Optional[Scope], Optional[Dict[str, Any]]) -> Scope + def _merge_scopes( + self, + additional_scope: "Optional[Scope]" = None, + additional_scope_kwargs: "Optional[Dict[str, Any]]" = None, + ) -> "Scope": """ Merges global, isolation and current scope into a new scope and adds the given additional scope or additional scope kwargs to it. @@ -409,8 +400,7 @@ def _merge_scopes(self, additional_scope=None, additional_scope_kwargs=None): return final_scope @classmethod - def get_client(cls): - # type: () -> sentry_sdk.client.BaseClient + def get_client(cls) -> "sentry_sdk.client.BaseClient": """ .. versionadded:: 2.0.0 @@ -446,8 +436,9 @@ def get_client(cls): return NonRecordingClient() - def set_client(self, client=None): - # type: (Optional[sentry_sdk.client.BaseClient]) -> None + def set_client( + self, client: "Optional[sentry_sdk.client.BaseClient]" = None + ) -> None: """ .. versionadded:: 2.0.0 @@ -459,8 +450,7 @@ def set_client(self, client=None): """ self.client = client if client is not None else NonRecordingClient() - def fork(self): - # type: () -> Scope + def fork(self) -> "Scope": """ .. versionadded:: 2.0.0 @@ -469,8 +459,7 @@ def fork(self): forked_scope = copy(self) return forked_scope - def _load_trace_data_from_env(self): - # type: () -> Optional[Dict[str, str]] + def _load_trace_data_from_env(self) -> "Optional[Dict[str, str]]": """ Load Sentry trace id and baggage from environment variables. Can be disabled by setting SENTRY_USE_ENVIRONMENT to "false". @@ -496,15 +485,15 @@ def _load_trace_data_from_env(self): return incoming_trace_information or None - def set_new_propagation_context(self): - # type: () -> None + def set_new_propagation_context(self) -> None: """ Creates a new propagation context and sets it as `_propagation_context`. Overwriting existing one. """ self._propagation_context = PropagationContext() - def generate_propagation_context(self, incoming_data=None): - # type: (Optional[Dict[str, str]]) -> None + def generate_propagation_context( + self, incoming_data: "Optional[Dict[str, str]]" = None + ) -> None: """ Makes sure the propagation context is set on the scope. If there is `incoming_data` overwrite existing propagation context. @@ -520,8 +509,7 @@ def generate_propagation_context(self, incoming_data=None): if self._propagation_context is None: self.set_new_propagation_context() - def get_dynamic_sampling_context(self): - # type: () -> Optional[Dict[str, str]] + 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. @@ -535,8 +523,7 @@ def get_dynamic_sampling_context(self): return self._propagation_context.dynamic_sampling_context - def get_traceparent(self, *args, **kwargs): - # type: (Any, Any) -> Optional[str] + def get_traceparent(self, *args: "Any", **kwargs: "Any") -> "Optional[str]": """ Returns the Sentry "sentry-trace" header (aka the traceparent) from the currently active span or the scopes Propagation Context. @@ -558,8 +545,7 @@ def get_traceparent(self, *args, **kwargs): # Fall back to isolation scope's traceparent. It always has one return self.get_isolation_scope().get_traceparent() - def get_baggage(self, *args, **kwargs): - # type: (Any, Any) -> Optional[Baggage] + def get_baggage(self, *args: "Any", **kwargs: "Any") -> "Optional[Baggage]": """ Returns the Sentry "baggage" header containing trace information from the currently active span or the scopes Propagation Context. @@ -577,8 +563,7 @@ def get_baggage(self, *args, **kwargs): # Fall back to isolation scope's baggage. It always has one return self.get_isolation_scope().get_baggage() - def get_trace_context(self): - # type: () -> Dict[str, Any] + def get_trace_context(self) -> "Dict[str, Any]": """ Returns the Sentry "trace" context from the Propagation Context. """ @@ -602,8 +587,7 @@ def get_trace_context(self): "dynamic_sampling_context": self.get_dynamic_sampling_context(), } - def trace_propagation_meta(self, *args, **kwargs): - # type: (*Any, **Any) -> str + 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. @@ -632,8 +616,7 @@ def trace_propagation_meta(self, *args, **kwargs): return meta - def iter_headers(self): - # type: () -> Iterator[Tuple[str, str]] + def iter_headers(self) -> "Iterator[Tuple[str, str]]": """ Creates a generator which returns the `sentry-trace` and `baggage` headers from the Propagation Context. """ @@ -647,8 +630,9 @@ def iter_headers(self): baggage = Baggage(dsc).serialize() yield BAGGAGE_HEADER_NAME, baggage - def iter_trace_propagation_headers(self, *args, **kwargs): - # type: (Any, Any) -> Generator[Tuple[str, str], None, None] + def iter_trace_propagation_headers( + self, *args: "Any", **kwargs: "Any" + ) -> "Generator[Tuple[str, str], None, None]": """ Return HTTP headers which allow propagation of trace data. @@ -689,8 +673,7 @@ def iter_trace_propagation_headers(self, *args, **kwargs): for header in isolation_scope.iter_headers(): yield header - def get_active_propagation_context(self): - # type: () -> Optional[PropagationContext] + def get_active_propagation_context(self) -> "Optional[PropagationContext]": if self._propagation_context is not None: return self._propagation_context @@ -704,38 +687,36 @@ def get_active_propagation_context(self): return None - def clear(self): - # type: () -> None + def clear(self) -> None: """Clears the entire scope.""" - self._level = None # type: Optional[LogLevelStr] - self._fingerprint = None # type: Optional[List[str]] - self._transaction = None # type: Optional[str] - self._transaction_info = {} # type: dict[str, str] - self._user = None # type: Optional[Dict[str, Any]] + self._level: "Optional[LogLevelStr]" = None + self._fingerprint: "Optional[List[str]]" = None + self._transaction: "Optional[str]" = None + self._transaction_info: "dict[str, str]" = {} + self._user: "Optional[Dict[str, Any]]" = None - self._tags = {} # type: Dict[str, Any] - self._contexts = {} # type: Dict[str, Dict[str, Any]] - self._extras = {} # type: dict[str, Any] - self._attachments = [] # type: List[Attachment] + self._tags: "Dict[str, Any]" = {} + self._contexts: "Dict[str, Dict[str, Any]]" = {} + self._extras: "dict[str, Any]" = {} + self._attachments: "List[Attachment]" = [] self.clear_breadcrumbs() - self._should_capture = True # type: bool + self._should_capture: bool = True - self._span = None # type: Optional[Span] - self._session = None # type: Optional[Session] - self._force_auto_session_tracking = None # type: Optional[bool] + self._span: "Optional[Span]" = None + self._session: "Optional[Session]" = None + self._force_auto_session_tracking: "Optional[bool]" = None - self._profile = None # type: Optional[Profile] + self._profile: "Optional[Profile]" = None self._propagation_context = None # self._last_event_id is only applicable to isolation scopes - self._last_event_id = None # type: Optional[str] - self._flags = None # type: Optional[FlagBuffer] + self._last_event_id: "Optional[str]" = None + self._flags: "Optional[FlagBuffer]" = None @_attr_setter - def level(self, value): - # type: (LogLevelStr) -> None + def level(self, value: "LogLevelStr") -> None: """ When set this overrides the level. @@ -750,8 +731,7 @@ def level(self, value): self._level = value - def set_level(self, value): - # type: (LogLevelStr) -> None + def set_level(self, value: "LogLevelStr") -> None: """ Sets the level for the scope. @@ -760,14 +740,12 @@ def set_level(self, value): self._level = value @_attr_setter - def fingerprint(self, value): - # type: (Optional[List[str]]) -> None + def fingerprint(self, value: "Optional[List[str]]") -> None: """When set this overrides the default fingerprint.""" self._fingerprint = value @property - def transaction(self): - # type: () -> Any + def transaction(self) -> "Any": # would be type: () -> Optional[Transaction], see https://github.com/python/mypy/issues/3004 """Return the transaction (root span) in the scope, if any.""" @@ -784,8 +762,7 @@ def transaction(self): return self._span.containing_transaction @transaction.setter - def transaction(self, value): - # type: (Any) -> None + def transaction(self, value: "Any") -> None: # would be type: (Optional[str]) -> None, see https://github.com/python/mypy/issues/3004 """When set this forces a specific transaction name to be set. @@ -808,8 +785,7 @@ def transaction(self, value): if self._span and self._span.containing_transaction: self._span.containing_transaction.name = value - def set_transaction_name(self, name, source=None): - # type: (str, Optional[str]) -> None + def set_transaction_name(self, name: str, source: "Optional[str]" = None) -> None: """Set the transaction name and optionally the transaction source.""" self._transaction = name @@ -822,8 +798,7 @@ def set_transaction_name(self, name, source=None): self._transaction_info["source"] = source @_attr_setter - def user(self, value): - # type: (Optional[Dict[str, Any]]) -> None + 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()`.", @@ -832,8 +807,7 @@ def user(self, value): ) self.set_user(value) - def set_user(self, value): - # type: (Optional[Dict[str, Any]]) -> None + def set_user(self, value: "Optional[Dict[str, Any]]") -> None: """Sets a user for the scope.""" self._user = value session = self.get_isolation_scope()._session @@ -841,14 +815,12 @@ def set_user(self, value): session.update(user=value) @property - def span(self): - # type: () -> Optional[Span] + def span(self) -> "Optional[Span]": """Get/set current tracing span or transaction.""" return self._span @span.setter - def span(self, span): - # type: (Optional[Span]) -> None + def span(self, span: "Optional[Span]") -> None: self._span = span # XXX: this differs from the implementation in JS, there Scope.setSpan # does not set Scope._transactionName. @@ -860,18 +832,14 @@ def span(self, span): self._transaction_info["source"] = transaction.source @property - def profile(self): - # type: () -> Optional[Profile] + def profile(self) -> "Optional[Profile]": return self._profile @profile.setter - def profile(self, profile): - # type: (Optional[Profile]) -> None - + def profile(self, profile: "Optional[Profile]") -> None: self._profile = profile - def set_tag(self, key, value): - # type: (str, Any) -> None + def set_tag(self, key: str, value: "Any") -> None: """ Sets a tag for a key to a specific value. @@ -881,8 +849,7 @@ def set_tag(self, key, value): """ self._tags[key] = value - def set_tags(self, tags): - # type: (Mapping[str, object]) -> None + def set_tags(self, tags: "Mapping[str, object]") -> None: """Sets multiple tags at once. This method updates multiple tags at once. The tags are passed as a dictionary @@ -900,8 +867,7 @@ def set_tags(self, tags): """ self._tags.update(tags) - def remove_tag(self, key): - # type: (str) -> None + def remove_tag(self, key: str) -> None: """ Removes a specific tag. @@ -911,10 +877,9 @@ def remove_tag(self, key): def set_context( self, - key, # type: str - value, # type: Dict[str, Any] - ): - # type: (...) -> None + key: str, + value: "Dict[str, Any]", + ) -> None: """ Binds a context at a certain key to a specific value. """ @@ -922,44 +887,39 @@ def set_context( def remove_context( self, - key, # type: str - ): - # type: (...) -> None + key: str, + ) -> None: """Removes a context.""" self._contexts.pop(key, None) def set_extra( self, - key, # type: str - value, # type: Any - ): - # type: (...) -> None + key: str, + value: "Any", + ) -> None: """Sets an extra key to a specific value.""" self._extras[key] = value def remove_extra( self, - key, # type: str - ): - # type: (...) -> None + key: str, + ) -> None: """Removes a specific extra key.""" self._extras.pop(key, None) - def clear_breadcrumbs(self): - # type: () -> None + def clear_breadcrumbs(self) -> None: """Clears breadcrumb buffer.""" - self._breadcrumbs = deque() # type: Deque[Breadcrumb] + self._breadcrumbs: "Deque[Breadcrumb]" = deque() self._n_breadcrumbs_truncated = 0 def add_attachment( self, - bytes=None, # type: Union[None, bytes, Callable[[], bytes]] - filename=None, # type: Optional[str] - path=None, # type: Optional[str] - content_type=None, # type: Optional[str] - add_to_transactions=False, # type: bool - ): - # type: (...) -> None + bytes: "Union[None, bytes, Callable[[], bytes]]" = None, + filename: "Optional[str]" = None, + path: "Optional[str]" = None, + content_type: "Optional[str]" = None, + add_to_transactions: bool = False, + ) -> None: """Adds an attachment to future events sent from this scope. The parameters are the same as for the :py:class:`sentry_sdk.attachments.Attachment` constructor. @@ -974,8 +934,12 @@ def add_attachment( ) ) - def add_breadcrumb(self, crumb=None, hint=None, **kwargs): - # type: (Optional[Breadcrumb], Optional[BreadcrumbHint], Any) -> None + def add_breadcrumb( + self, + crumb: "Optional[Breadcrumb]" = None, + hint: "Optional[BreadcrumbHint]" = None, + **kwargs: "Any", + ) -> None: """ Adds a breadcrumb. @@ -993,12 +957,12 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): before_breadcrumb = client.options.get("before_breadcrumb") max_breadcrumbs = client.options.get("max_breadcrumbs", DEFAULT_MAX_BREADCRUMBS) - crumb = dict(crumb or ()) # type: Breadcrumb + crumb: "Breadcrumb" = dict(crumb or ()) crumb.update(kwargs) if not crumb: return - hint = dict(hint or ()) # type: Hint + hint: "Hint" = dict(hint or ()) if crumb.get("timestamp") is None: crumb["timestamp"] = datetime.now(timezone.utc) @@ -1021,12 +985,11 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): def start_transaction( self, - transaction=None, - instrumenter=INSTRUMENTER.SENTRY, - custom_sampling_context=None, - **kwargs, - ): - # type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan] + transaction: "Optional[Transaction]" = None, + instrumenter: str = INSTRUMENTER.SENTRY, + custom_sampling_context: "Optional[SamplingContext]" = None, + **kwargs: "Unpack[TransactionKwargs]", + ) -> "Union[Transaction, NoOpSpan]": """ Start and return a transaction. @@ -1073,7 +1036,7 @@ def start_transaction( # kwargs at this point has type TransactionKwargs, since we have removed # the client and custom_sampling_context from it. - transaction_kwargs = kwargs # type: TransactionKwargs + transaction_kwargs: "TransactionKwargs" = kwargs # if we haven't been given a transaction, make one if transaction is None: @@ -1123,8 +1086,9 @@ def start_transaction( return transaction - def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): - # type: (str, Any) -> Span + def start_span( + self, instrumenter: str = INSTRUMENTER.SENTRY, **kwargs: "Any" + ) -> "Span": """ Start a span whose parent is the currently active span or transaction, if any. @@ -1179,9 +1143,13 @@ def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): return span def continue_trace( - self, environ_or_headers, op=None, name=None, source=None, origin="manual" - ): - # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], str) -> Transaction + self, + environ_or_headers: "Dict[str, Any]", + op: "Optional[str]" = None, + name: "Optional[str]" = None, + source: "Optional[str]" = None, + origin: str = "manual", + ) -> "Transaction": """ Sets the propagation context from environment or headers and returns a transaction. """ @@ -1207,8 +1175,13 @@ def continue_trace( **optional_kwargs, ) - def capture_event(self, event, hint=None, scope=None, **scope_kwargs): - # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] + def capture_event( + self, + event: "Event", + hint: "Optional[Hint]" = None, + scope: "Optional[Scope]" = None, + **scope_kwargs: "Any", + ) -> "Optional[str]": """ Captures an event. @@ -1239,8 +1212,13 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): return event_id - def capture_message(self, message, level=None, scope=None, **scope_kwargs): - # type: (str, Optional[LogLevelStr], Optional[Scope], Any) -> Optional[str] + def capture_message( + self, + message: str, + level: "Optional[LogLevelStr]" = None, + scope: "Optional[Scope]" = None, + **scope_kwargs: "Any", + ) -> "Optional[str]": """ Captures a message. @@ -1263,15 +1241,19 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): if level is None: level = "info" - event = { + event: "Event" = { "message": message, "level": level, - } # type: Event + } return self.capture_event(event, scope=scope, **scope_kwargs) - def capture_exception(self, error=None, scope=None, **scope_kwargs): - # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + def capture_exception( + self, + error: "Optional[Union[BaseException, ExcInfo]]" = None, + scope: "Optional[Scope]" = None, + **scope_kwargs: "Any", + ) -> "Optional[str]": """Captures an exception. :param error: An exception to capture. If `None`, `sys.exc_info()` will be used. @@ -1304,8 +1286,7 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): return None - def start_session(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def start_session(self, *args: "Any", **kwargs: "Any") -> None: """Starts a new session.""" session_mode = kwargs.pop("session_mode", "application") @@ -1319,8 +1300,7 @@ def start_session(self, *args, **kwargs): session_mode=session_mode, ) - def end_session(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def end_session(self, *args: "Any", **kwargs: "Any") -> None: """Ends the current session if there is one.""" session = self._session self._session = None @@ -1329,8 +1309,7 @@ def end_session(self, *args, **kwargs): session.close() self.get_client().capture_session(session) - def stop_auto_session_tracking(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def stop_auto_session_tracking(self, *args: "Any", **kwargs: "Any") -> None: """Stops automatic session tracking. This temporarily session tracking for the current scope when called. @@ -1339,8 +1318,7 @@ def stop_auto_session_tracking(self, *args, **kwargs): self.end_session() self._force_auto_session_tracking = False - def resume_auto_session_tracking(self): - # type: (...) -> None + def resume_auto_session_tracking(self) -> None: """Resumes automatic session tracking for the current scope if disabled earlier. This requires that generally automatic session tracking is enabled. @@ -1349,9 +1327,8 @@ def resume_auto_session_tracking(self): def add_event_processor( self, - func, # type: EventProcessor - ): - # type: (...) -> None + func: "EventProcessor", + ) -> None: """Register a scope local event processor on the scope. :param func: This function behaves like `before_send.` @@ -1367,10 +1344,9 @@ def add_event_processor( def add_error_processor( self, - func, # type: ErrorProcessor - cls=None, # type: Optional[Type[BaseException]] - ): - # type: (...) -> None + func: "ErrorProcessor", + cls: "Optional[Type[BaseException]]" = None, + ) -> None: """Register a scope local error processor on the scope. :param func: A callback that works similar to an event processor but is invoked with the original exception info triple as second argument. @@ -1381,8 +1357,7 @@ def add_error_processor( cls_ = cls # For mypy. real_func = func - def func(event, exc_info): - # type: (Event, ExcInfo) -> Optional[Event] + def func(event: "Event", exc_info: "ExcInfo") -> "Optional[Event]": try: is_inst = isinstance(exc_info[1], cls_) except Exception: @@ -1393,13 +1368,15 @@ def func(event, exc_info): self._error_processors.append(func) - def _apply_level_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_level_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if self._level is not None: event["level"] = self._level - def _apply_breadcrumbs_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_breadcrumbs_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: event.setdefault("breadcrumbs", {}) # This check is just for mypy - @@ -1421,38 +1398,45 @@ def _apply_breadcrumbs_to_event(self, event, hint, options): logger.debug("Error when sorting breadcrumbs", exc_info=err) pass - def _apply_user_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_user_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if event.get("user") is None and self._user is not None: event["user"] = self._user - def _apply_transaction_name_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_transaction_name_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if event.get("transaction") is None and self._transaction is not None: event["transaction"] = self._transaction - def _apply_transaction_info_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_transaction_info_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if event.get("transaction_info") is None and self._transaction_info is not None: event["transaction_info"] = self._transaction_info - def _apply_fingerprint_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_fingerprint_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if event.get("fingerprint") is None and self._fingerprint is not None: event["fingerprint"] = self._fingerprint - def _apply_extra_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_extra_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if self._extras: event.setdefault("extra", {}).update(self._extras) - def _apply_tags_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_tags_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if self._tags: event.setdefault("tags", {}).update(self._tags) - def _apply_contexts_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_contexts_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: if self._contexts: event.setdefault("contexts", {}).update(self._contexts) @@ -1462,21 +1446,20 @@ def _apply_contexts_to_event(self, event, hint, options): if contexts.get("trace") is None: contexts["trace"] = self.get_trace_context() - def _apply_flags_to_event(self, event, hint, options): - # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + def _apply_flags_to_event( + self, event: "Event", hint: "Hint", options: "Optional[Dict[str, Any]]" + ) -> None: flags = self.flags.get() if len(flags) > 0: event.setdefault("contexts", {}).setdefault("flags", {}).update( {"values": flags} ) - def _drop(self, cause, ty): - # type: (Any, str) -> Optional[Any] + def _drop(self, cause: "Any", ty: str) -> "Optional[Any]": logger.info("%s (%s) dropped event", ty, cause) return None - def run_error_processors(self, event, hint): - # type: (Event, Hint) -> Optional[Event] + def run_error_processors(self, event: "Event", hint: "Hint") -> "Optional[Event]": """ Runs the error processors on the event and returns the modified event. """ @@ -1497,8 +1480,7 @@ def run_error_processors(self, event, hint): return event - def run_event_processors(self, event, hint): - # type: (Event, Hint) -> Optional[Event] + def run_event_processors(self, event: "Event", hint: "Hint") -> "Optional[Event]": """ Runs the event processors on the event and returns the modified event. """ @@ -1530,11 +1512,10 @@ def run_event_processors(self, event, hint): @_disable_capture def apply_to_event( self, - event, # type: Event - hint, # type: Hint - options=None, # type: Optional[Dict[str, Any]] - ): - # type: (...) -> Optional[Event] + event: "Event", + hint: "Hint", + options: "Optional[Dict[str, Any]]" = None, + ) -> "Optional[Event]": """Applies the information contained on the scope to the given event.""" ty = event.get("type") is_transaction = ty == "transaction" @@ -1580,8 +1561,7 @@ def apply_to_event( return event - def update_from_scope(self, scope): - # type: (Scope) -> None + def update_from_scope(self, scope: "Scope") -> None: """Update the scope with another scope's data.""" if scope._level is not None: self._level = scope._level @@ -1628,14 +1608,13 @@ def update_from_scope(self, scope): def update_from_kwargs( self, - user=None, # type: Optional[Any] - level=None, # type: Optional[LogLevelStr] - extras=None, # type: Optional[Dict[str, Any]] - contexts=None, # type: Optional[Dict[str, Dict[str, Any]]] - tags=None, # type: Optional[Dict[str, str]] - fingerprint=None, # type: Optional[List[str]] - ): - # type: (...) -> None + user: "Optional[Any]" = None, + level: "Optional[LogLevelStr]" = None, + extras: "Optional[Dict[str, Any]]" = None, + contexts: "Optional[Dict[str, Dict[str, Any]]]" = None, + tags: "Optional[Dict[str, str]]" = None, + fingerprint: "Optional[List[str]]" = None, + ) -> None: """Update the scope's attributes.""" if level is not None: self._level = level @@ -1650,8 +1629,7 @@ def update_from_kwargs( if fingerprint is not None: self._fingerprint = fingerprint - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "<%s id=%s name=%s type=%s>" % ( self.__class__.__name__, hex(id(self)), @@ -1660,8 +1638,7 @@ def __repr__(self): ) @property - def flags(self): - # type: () -> FlagBuffer + def flags(self) -> "FlagBuffer": if self._flags is None: max_flags = ( self.get_client().options["_experiments"].get("max_flags") @@ -1672,8 +1649,7 @@ def flags(self): @contextmanager -def new_scope(): - # type: () -> Generator[Scope, None, None] +def new_scope() -> "Generator[Scope, None, None]": """ .. versionadded:: 2.0.0 @@ -1710,8 +1686,7 @@ def new_scope(): @contextmanager -def use_scope(scope): - # type: (Scope) -> Generator[Scope, None, None] +def use_scope(scope: "Scope") -> "Generator[Scope, None, None]": """ .. versionadded:: 2.0.0 @@ -1748,8 +1723,7 @@ def use_scope(scope): @contextmanager -def isolation_scope(): - # type: () -> Generator[Scope, None, None] +def isolation_scope() -> "Generator[Scope, None, None]": """ .. versionadded:: 2.0.0 @@ -1797,8 +1771,7 @@ def isolation_scope(): @contextmanager -def use_isolation_scope(isolation_scope): - # type: (Scope) -> Generator[Scope, None, None] +def use_isolation_scope(isolation_scope: "Scope") -> "Generator[Scope, None, None]": """ .. versionadded:: 2.0.0 @@ -1843,8 +1816,7 @@ def use_isolation_scope(isolation_scope): capture_internal_exception(sys.exc_info()) -def should_send_default_pii(): - # type: () -> bool +def should_send_default_pii() -> bool: """Shortcut for `Scope.get_client().should_send_default_pii()`.""" return Scope.get_client().should_send_default_pii() diff --git a/sentry_sdk/scrubber.py b/sentry_sdk/scrubber.py index b0576c7e95..2857c4edaa 100644 --- a/sentry_sdk/scrubber.py +++ b/sentry_sdk/scrubber.py @@ -60,9 +60,12 @@ class EventScrubber: def __init__( - self, denylist=None, recursive=False, send_default_pii=False, pii_denylist=None - ): - # type: (Optional[List[str]], bool, bool, Optional[List[str]]) -> None + self, + denylist: "Optional[List[str]]" = None, + recursive: bool = False, + send_default_pii: bool = False, + pii_denylist: "Optional[List[str]]" = None, + ) -> None: """ A scrubber that goes through the event payload and removes sensitive data configured through denylists. @@ -82,8 +85,7 @@ def __init__( self.denylist = [x.lower() for x in self.denylist] self.recursive = recursive - def scrub_list(self, lst): - # type: (object) -> None + def scrub_list(self, lst: object) -> None: """ If a list is passed to this method, the method recursively searches the list and any nested lists for any dictionaries. The method calls scrub_dict on all dictionaries @@ -97,8 +99,7 @@ def scrub_list(self, lst): self.scrub_dict(v) # no-op unless v is a dict self.scrub_list(v) # no-op unless v is a list - def scrub_dict(self, d): - # type: (object) -> None + def scrub_dict(self, d: object) -> None: """ If a dictionary is passed to this method, the method scrubs the dictionary of any sensitive data. The method calls itself recursively on any nested dictionaries ( @@ -117,8 +118,7 @@ def scrub_dict(self, d): self.scrub_dict(v) # no-op unless v is a dict self.scrub_list(v) # no-op unless v is a list - def scrub_request(self, event): - # type: (Event) -> None + def scrub_request(self, event: "Event") -> None: with capture_internal_exceptions(): if "request" in event: if "headers" in event["request"]: @@ -128,20 +128,17 @@ def scrub_request(self, event): if "data" in event["request"]: self.scrub_dict(event["request"]["data"]) - def scrub_extra(self, event): - # type: (Event) -> None + def scrub_extra(self, event: "Event") -> None: with capture_internal_exceptions(): if "extra" in event: self.scrub_dict(event["extra"]) - def scrub_user(self, event): - # type: (Event) -> None + def scrub_user(self, event: "Event") -> None: with capture_internal_exceptions(): if "user" in event: self.scrub_dict(event["user"]) - def scrub_breadcrumbs(self, event): - # type: (Event) -> None + def scrub_breadcrumbs(self, event: "Event") -> None: with capture_internal_exceptions(): if "breadcrumbs" in event: if ( @@ -152,23 +149,20 @@ def scrub_breadcrumbs(self, event): if "data" in value: self.scrub_dict(value["data"]) - def scrub_frames(self, event): - # type: (Event) -> None + def scrub_frames(self, event: "Event") -> None: with capture_internal_exceptions(): for frame in iter_event_frames(event): if "vars" in frame: self.scrub_dict(frame["vars"]) - def scrub_spans(self, event): - # type: (Event) -> None + def scrub_spans(self, event: "Event") -> None: with capture_internal_exceptions(): if "spans" in event: for span in cast(List[Dict[str, object]], event["spans"]): if "data" in span: self.scrub_dict(span["data"]) - def scrub_event(self, event): - # type: (Event) -> None + def scrub_event(self, event: "Event") -> None: self.scrub_request(event) self.scrub_extra(event) self.scrub_user(event) diff --git a/sentry_sdk/serializer.py b/sentry_sdk/serializer.py index 1775b1b555..63ed96c0e2 100644 --- a/sentry_sdk/serializer.py +++ b/sentry_sdk/serializer.py @@ -55,37 +55,32 @@ CYCLE_MARKER = "" -global_repr_processors = [] # type: List[ReprProcessor] +global_repr_processors: "List[ReprProcessor]" = [] -def add_global_repr_processor(processor): - # type: (ReprProcessor) -> None +def add_global_repr_processor(processor: "ReprProcessor") -> None: global_repr_processors.append(processor) -sequence_types = [Sequence, Set] # type: List[type] +sequence_types: "List[type]" = [Sequence, Set] -def add_repr_sequence_type(ty): - # type: (type) -> None +def add_repr_sequence_type(ty: type) -> None: sequence_types.append(ty) class Memo: __slots__ = ("_ids", "_objs") - def __init__(self): - # type: () -> None - self._ids = {} # type: Dict[int, Any] - self._objs = [] # type: List[Any] + def __init__(self) -> None: + self._ids: Dict[int, Any] = {} + self._objs: "List[Any]" = [] - def memoize(self, obj): - # type: (Any) -> ContextManager[bool] + def memoize(self, obj: "Any") -> "ContextManager[bool]": self._objs.append(obj) return self - def __enter__(self): - # type: () -> bool + def __enter__(self) -> bool: obj = self._objs[-1] if id(obj) in self._ids: return True @@ -95,16 +90,14 @@ def __enter__(self): def __exit__( self, - ty, # type: Optional[Type[BaseException]] - value, # type: Optional[BaseException] - tb, # type: Optional[TracebackType] - ): - # type: (...) -> None + ty: "Optional[Type[BaseException]]", + value: "Optional[BaseException]", + tb: "Optional[TracebackType]", + ) -> None: self._ids.pop(id(self._objs.pop()), None) -def serialize(event, **kwargs): - # type: (Dict[str, Any], **Any) -> Dict[str, Any] +def serialize(event: "Dict[str, Any]", **kwargs: "Any") -> "Dict[str, Any]": """ A very smart serializer that takes a dict and emits a json-friendly dict. Currently used for serializing the final Event and also prematurely while fetching the stack @@ -125,16 +118,15 @@ def serialize(event, **kwargs): """ memo = Memo() - path = [] # type: List[Segment] - meta_stack = [] # type: List[Dict[str, Any]] + path: "List[Segment]" = [] + meta_stack: "List[Dict[str, Any]]" = [] - keep_request_bodies = kwargs.pop("max_request_body_size", None) == "always" # type: bool - max_value_length = kwargs.pop("max_value_length", None) # type: Optional[int] + keep_request_bodies: bool = kwargs.pop("max_request_body_size", None) == "always" + max_value_length: "Optional[int]" = kwargs.pop("max_value_length", None) is_vars = kwargs.pop("is_vars", False) - custom_repr = kwargs.pop("custom_repr", None) # type: Callable[..., Optional[str]] + custom_repr: "Callable[..., Optional[str]]" = kwargs.pop("custom_repr", None) - def _safe_repr_wrapper(value): - # type: (Any) -> str + def _safe_repr_wrapper(value: "Any") -> str: try: repr_value = None if custom_repr is not None: @@ -143,8 +135,7 @@ def _safe_repr_wrapper(value): except Exception: return safe_repr(value) - def _annotate(**meta): - # type: (**Any) -> None + def _annotate(**meta: "Any") -> None: while len(meta_stack) <= len(path): try: segment = path[len(meta_stack) - 1] @@ -156,8 +147,7 @@ def _annotate(**meta): meta_stack[-1].setdefault("", {}).update(meta) - def _is_databag(): - # type: () -> Optional[bool] + def _is_databag() -> "Optional[bool]": """ A databag is any value that we need to trim. True for stuff like vars, request bodies, breadcrumbs and extra. @@ -185,8 +175,7 @@ def _is_databag(): return False - def _is_span_attribute(): - # type: () -> Optional[bool] + def _is_span_attribute() -> "Optional[bool]": try: if path[0] == "spans" and path[2] == "data": return True @@ -195,8 +184,7 @@ def _is_span_attribute(): return False - def _is_request_body(): - # type: () -> Optional[bool] + def _is_request_body() -> "Optional[bool]": try: if path[0] == "request" and path[1] == "data": return True @@ -206,15 +194,14 @@ def _is_request_body(): return False def _serialize_node( - obj, # type: Any - is_databag=None, # type: Optional[bool] - is_request_body=None, # type: Optional[bool] - should_repr_strings=None, # type: Optional[bool] - segment=None, # type: Optional[Segment] - remaining_breadth=None, # type: Optional[Union[int, float]] - remaining_depth=None, # type: Optional[Union[int, float]] - ): - # type: (...) -> Any + obj: "Any", + is_databag: "Optional[bool]" = None, + is_request_body: "Optional[bool]" = None, + should_repr_strings: "Optional[bool]" = None, + segment: "Optional[Segment]" = None, + remaining_breadth: "Optional[Union[int, float]]" = None, + remaining_depth: "Optional[Union[int, float]]" = None, + ) -> "Any": if segment is not None: path.append(segment) @@ -243,22 +230,20 @@ def _serialize_node( path.pop() del meta_stack[len(path) + 1 :] - def _flatten_annotated(obj): - # type: (Any) -> Any + def _flatten_annotated(obj: "Any") -> "Any": if isinstance(obj, AnnotatedValue): _annotate(**obj.metadata) obj = obj.value return obj def _serialize_node_impl( - obj, - is_databag, - is_request_body, - should_repr_strings, - remaining_depth, - remaining_breadth, - ): - # type: (Any, Optional[bool], Optional[bool], Optional[bool], Optional[Union[float, int]], Optional[Union[float, int]]) -> Any + obj: "Any", + is_databag: "Optional[bool]", + is_request_body: "Optional[bool]", + should_repr_strings: "Optional[bool]", + remaining_depth: "Optional[Union[float, int]]", + remaining_breadth: "Optional[Union[float, int]]", + ) -> "Any": if isinstance(obj, AnnotatedValue): should_repr_strings = False if should_repr_strings is None: @@ -323,7 +308,7 @@ def _serialize_node_impl( # might mutate our dictionary while we're still iterating over it. obj = dict(obj.items()) - rv_dict = {} # type: Dict[str, Any] + rv_dict: "Dict[str, Any]" = {} i = 0 for k, v in obj.items(): diff --git a/sentry_sdk/session.py b/sentry_sdk/session.py index af9551c56e..f523527bf8 100644 --- a/sentry_sdk/session.py +++ b/sentry_sdk/session.py @@ -14,15 +14,13 @@ from sentry_sdk._types import SessionStatus -def _minute_trunc(ts): - # type: (datetime) -> datetime +def _minute_trunc(ts: "datetime") -> "datetime": return ts.replace(second=0, microsecond=0) def _make_uuid( - val, # type: Union[str, uuid.UUID] -): - # type: (...) -> uuid.UUID + val: "Union[str, uuid.UUID]", +) -> "uuid.UUID": if isinstance(val, uuid.UUID): return val return uuid.UUID(val) @@ -31,21 +29,20 @@ def _make_uuid( class Session: def __init__( self, - sid=None, # type: Optional[Union[str, uuid.UUID]] - did=None, # type: Optional[str] - timestamp=None, # type: Optional[datetime] - started=None, # type: Optional[datetime] - duration=None, # type: Optional[float] - status=None, # type: Optional[SessionStatus] - release=None, # type: Optional[str] - environment=None, # type: Optional[str] - user_agent=None, # type: Optional[str] - ip_address=None, # type: Optional[str] - errors=None, # type: Optional[int] - user=None, # type: Optional[Any] - session_mode="application", # type: str - ): - # type: (...) -> None + sid: "Optional[Union[str, uuid.UUID]]" = None, + did: "Optional[str]" = None, + timestamp: "Optional[datetime]" = None, + started: "Optional[datetime]" = None, + duration: "Optional[float]" = None, + status: "Optional[SessionStatus]" = None, + release: "Optional[str]" = None, + environment: "Optional[str]" = None, + user_agent: "Optional[str]" = None, + ip_address: "Optional[str]" = None, + errors: "Optional[int]" = None, + user: "Optional[Any]" = None, + session_mode: str = "application", + ) -> None: if sid is None: sid = uuid.uuid4() if started is None: @@ -53,14 +50,14 @@ def __init__( if status is None: status = "ok" self.status = status - self.did = None # type: Optional[str] + self.did: "Optional[str]" = None self.started = started - self.release = None # type: Optional[str] - self.environment = None # type: Optional[str] - self.duration = None # type: Optional[float] - self.user_agent = None # type: Optional[str] - self.ip_address = None # type: Optional[str] - self.session_mode = session_mode # type: str + self.release: "Optional[str]" = None + self.environment: "Optional[str]" = None + self.duration: "Optional[float]" = None + self.user_agent: "Optional[str]" = None + self.ip_address: "Optional[str]" = None + self.session_mode: str = session_mode self.errors = 0 self.update( @@ -77,26 +74,24 @@ def __init__( ) @property - def truncated_started(self): - # type: (...) -> datetime + def truncated_started(self) -> "datetime": return _minute_trunc(self.started) def update( self, - sid=None, # type: Optional[Union[str, uuid.UUID]] - did=None, # type: Optional[str] - timestamp=None, # type: Optional[datetime] - started=None, # type: Optional[datetime] - duration=None, # type: Optional[float] - status=None, # type: Optional[SessionStatus] - release=None, # type: Optional[str] - environment=None, # type: Optional[str] - user_agent=None, # type: Optional[str] - ip_address=None, # type: Optional[str] - errors=None, # type: Optional[int] - user=None, # type: Optional[Any] - ): - # type: (...) -> None + sid: "Optional[Union[str, uuid.UUID]]" = None, + did: "Optional[str]" = None, + timestamp: "Optional[datetime]" = None, + started: "Optional[datetime]" = None, + duration: "Optional[float]" = None, + status: "Optional[SessionStatus]" = None, + release: "Optional[str]" = None, + environment: "Optional[str]" = None, + user_agent: "Optional[str]" = None, + ip_address: "Optional[str]" = None, + errors: "Optional[int]" = None, + user: "Optional[Any]" = None, + ) -> None: # If a user is supplied we pull some data form it if user: if ip_address is None: @@ -131,9 +126,8 @@ def update( def close( self, - status=None, # type: Optional[SessionStatus] - ): - # type: (...) -> Any + status: "Optional[SessionStatus]" = None, + ) -> "Any": if status is None and self.status == "ok": status = "exited" if status is not None: @@ -141,9 +135,8 @@ def close( def get_json_attrs( self, - with_user_info=True, # type: Optional[bool] - ): - # type: (...) -> Any + with_user_info: "Optional[bool]" = True, + ) -> "Any": attrs = {} if self.release is not None: attrs["release"] = self.release @@ -156,15 +149,14 @@ def get_json_attrs( attrs["user_agent"] = self.user_agent return attrs - def to_json(self): - # type: (...) -> Any - rv = { + def to_json(self) -> "Any": + rv: Dict[str, Any] = { "sid": str(self.sid), "init": True, "started": format_timestamp(self.started), "timestamp": format_timestamp(self.timestamp), "status": self.status, - } # type: Dict[str, Any] + } if self.errors: rv["errors"] = self.errors if self.did is not None: diff --git a/sentry_sdk/sessions.py b/sentry_sdk/sessions.py index 2bf4ee707a..2b7ed8487d 100644 --- a/sentry_sdk/sessions.py +++ b/sentry_sdk/sessions.py @@ -20,8 +20,9 @@ from typing import Union -def is_auto_session_tracking_enabled(hub=None): - # type: (Optional[sentry_sdk.Hub]) -> Union[Any, bool, None] +def is_auto_session_tracking_enabled( + hub: "Optional[sentry_sdk.Hub]" = None, +) -> "Union[Any, bool, None]": """DEPRECATED: Utility function to find out if session tracking is enabled.""" # Internal callers should use private _is_auto_session_tracking_enabled, instead. @@ -45,8 +46,9 @@ def is_auto_session_tracking_enabled(hub=None): @contextmanager -def auto_session_tracking(hub=None, session_mode="application"): - # type: (Optional[sentry_sdk.Hub], str) -> Generator[None, None, None] +def auto_session_tracking( + hub: "Optional[sentry_sdk.Hub]" = None, session_mode: str = "application" +) -> "Generator[None, None, None]": """DEPRECATED: Use track_session instead Starts and stops a session automatically around a block. """ @@ -71,8 +73,7 @@ def auto_session_tracking(hub=None, session_mode="application"): hub.end_session() -def is_auto_session_tracking_enabled_scope(scope): - # type: (sentry_sdk.Scope) -> bool +def is_auto_session_tracking_enabled_scope(scope: "sentry_sdk.Scope") -> bool: """ DEPRECATED: Utility function to find out if session tracking is enabled. """ @@ -88,8 +89,7 @@ def is_auto_session_tracking_enabled_scope(scope): return _is_auto_session_tracking_enabled(scope) -def _is_auto_session_tracking_enabled(scope): - # type: (sentry_sdk.Scope) -> bool +def _is_auto_session_tracking_enabled(scope: "sentry_sdk.Scope") -> bool: """ Utility function to find out if session tracking is enabled. """ @@ -103,8 +103,9 @@ def _is_auto_session_tracking_enabled(scope): @contextmanager -def auto_session_tracking_scope(scope, session_mode="application"): - # type: (sentry_sdk.Scope, str) -> Generator[None, None, None] +def auto_session_tracking_scope( + scope: "sentry_sdk.Scope", session_mode: str = "application" +) -> "Generator[None, None, None]": """DEPRECATED: This function is a deprecated alias for track_session. Starts and stops a session automatically around a block. """ @@ -120,8 +121,9 @@ def auto_session_tracking_scope(scope, session_mode="application"): @contextmanager -def track_session(scope, session_mode="application"): - # type: (sentry_sdk.Scope, str) -> Generator[None, None, None] +def track_session( + scope: "sentry_sdk.Scope", session_mode: str = "application" +) -> "Generator[None, None, None]": """ Start a new session in the provided scope, assuming session tracking is enabled. This is a no-op context manager if session tracking is not enabled. @@ -141,30 +143,27 @@ def track_session(scope, session_mode="application"): MAX_ENVELOPE_ITEMS = 100 -def make_aggregate_envelope(aggregate_states, attrs): - # type: (Any, Any) -> Any +def make_aggregate_envelope(aggregate_states: "Any", attrs: "Any") -> "Any": return {"attrs": dict(attrs), "aggregates": list(aggregate_states.values())} class SessionFlusher: def __init__( self, - capture_func, # type: Callable[[Envelope], None] - flush_interval=60, # type: int - ): - # type: (...) -> None + capture_func: "Callable[[Envelope], None]", + flush_interval: int = 60, + ) -> None: self.capture_func = capture_func self.flush_interval = flush_interval - self.pending_sessions = [] # type: List[Any] - self.pending_aggregates = {} # type: Dict[Any, Any] - self._thread = None # type: Optional[Thread] + self.pending_sessions: "List[Any]" = [] + self.pending_aggregates: "Dict[Any, Any]" = {} + self._thread: "Optional[Thread]" = None self._thread_lock = Lock() self._aggregate_lock = Lock() - self._thread_for_pid = None # type: Optional[int] + self._thread_for_pid: "Optional[int]" = None self.__shutdown_requested = Event() - def flush(self): - # type: (...) -> None + def flush(self) -> None: pending_sessions = self.pending_sessions self.pending_sessions = [] @@ -190,8 +189,7 @@ def flush(self): if len(envelope.items) > 0: self.capture_func(envelope) - def _ensure_running(self): - # type: (...) -> None + def _ensure_running(self) -> None: """ Check that we have an active thread to run in, or create one if not. @@ -205,8 +203,7 @@ def _ensure_running(self): if self._thread_for_pid == os.getpid() and self._thread is not None: return None - def _thread(): - # type: (...) -> None + def _thread() -> None: running = True while running: running = not self.__shutdown_requested.wait(self.flush_interval) @@ -229,9 +226,8 @@ def _thread(): def add_aggregate_session( self, - session, # type: Session - ): - # type: (...) -> None + session: "Session", + ) -> None: # NOTE on `session.did`: # the protocol can deal with buckets that have a distinct-id, however # in practice we expect the python SDK to have an extremely high cardinality @@ -261,15 +257,13 @@ def add_aggregate_session( def add_session( self, - session, # type: Session - ): - # type: (...) -> None + session: "Session", + ) -> None: if session.session_mode == "request": self.add_aggregate_session(session) else: self.pending_sessions.append(session.to_json()) self._ensure_running() - def kill(self): - # type: (...) -> None + def kill(self) -> None: self.__shutdown_requested.set() diff --git a/sentry_sdk/spotlight.py b/sentry_sdk/spotlight.py index cb69ea4b76..f70ea9d341 100644 --- a/sentry_sdk/spotlight.py +++ b/sentry_sdk/spotlight.py @@ -49,16 +49,13 @@ class SpotlightClient: INITIAL_RETRY_DELAY = 1.0 # Start with 1 second MAX_RETRY_DELAY = 60.0 # Max 60 seconds - def __init__(self, url): - # type: (str) -> None + def __init__(self, url: str) -> None: self.url = url self.http = urllib3.PoolManager() self._retry_delay = self.INITIAL_RETRY_DELAY - self._last_error_time = 0.0 # type: float - - def capture_envelope(self, envelope): - # type: (Envelope) -> None + self._last_error_time: float = 0.0 + def capture_envelope(self, envelope: "Envelope") -> None: # Check if we're in backoff period - skip sending to avoid blocking if self._last_error_time > 0: time_since_error = time.time() - self._last_error_time @@ -119,11 +116,10 @@ def capture_envelope(self, envelope): ) class SpotlightMiddleware(MiddlewareMixin): # type: ignore[misc] - _spotlight_script = None # type: Optional[str] - _spotlight_url = None # type: Optional[str] + _spotlight_script: "Optional[str]" = None + _spotlight_url: "Optional[str]" = None - def __init__(self, get_response): - # type: (Self, Callable[..., HttpResponse]) -> None + def __init__(self: "Self", get_response: "Callable[..., HttpResponse]") -> None: super().__init__(get_response) import sentry_sdk.api @@ -140,8 +136,7 @@ def __init__(self, get_response): self._spotlight_url = urllib.parse.urljoin(spotlight_client.url, "../") @property - def spotlight_script(self): - # type: (Self) -> Optional[str] + def spotlight_script(self: "Self") -> "Optional[str]": if self._spotlight_url is not None and self._spotlight_script is None: try: spotlight_js_url = urllib.parse.urljoin( @@ -165,8 +160,9 @@ def spotlight_script(self): return self._spotlight_script - def process_response(self, _request, response): - # type: (Self, HttpRequest, HttpResponse) -> Optional[HttpResponse] + def process_response( + self: "Self", _request: "HttpRequest", response: "HttpResponse" + ) -> "Optional[HttpResponse]": content_type_header = tuple( p.strip() for p in response.headers.get("Content-Type", "").lower().split(";") @@ -210,8 +206,9 @@ def process_response(self, _request, response): return response - def process_exception(self, _request, exception): - # type: (Self, HttpRequest, Exception) -> Optional[HttpResponseServerError] + def process_exception( + self: "Self", _request: "HttpRequest", exception: Exception + ) -> "Optional[HttpResponseServerError]": if not settings.DEBUG or not self._spotlight_url: return None @@ -236,8 +233,9 @@ def process_exception(self, _request, exception): settings = None -def _resolve_spotlight_url(spotlight_config, sentry_logger): - # type: (Any, Any) -> Optional[str] +def _resolve_spotlight_url( + spotlight_config: "Any", sentry_logger: "Any" +) -> "Optional[str]": """ Resolve the Spotlight URL based on config and environment variable. @@ -249,8 +247,8 @@ def _resolve_spotlight_url(spotlight_config, sentry_logger): spotlight_env_value = os.environ.get("SENTRY_SPOTLIGHT") # Parse env var to determine if it's a boolean or URL - spotlight_from_env = None # type: Optional[bool] - spotlight_env_url = None # type: Optional[str] + spotlight_from_env: "Optional[bool]" = None + spotlight_env_url: "Optional[str]" = None if spotlight_env_value: parsed = env_to_bool(spotlight_env_value, strict=True) if parsed is None: @@ -298,8 +296,7 @@ def _resolve_spotlight_url(spotlight_config, sentry_logger): return None -def setup_spotlight(options): - # type: (Dict[str, Any]) -> Optional[SpotlightClient] +def setup_spotlight(options: "Dict[str, Any]") -> "Optional[SpotlightClient]": url = _resolve_spotlight_url(options.get("spotlight"), sentry_logger) if url is None: diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 5a9a053418..a8f398f646 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -2,9 +2,10 @@ import warnings from datetime import datetime, timedelta, timezone from enum import Enum +from typing import TYPE_CHECKING import sentry_sdk -from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA, SPANTEMPLATE +from sentry_sdk.consts import INSTRUMENTER, SPANDATA, SPANSTATUS, SPANTEMPLATE from sentry_sdk.profiler.continuous_profiler import get_profiler_id from sentry_sdk.utils import ( capture_internal_exceptions, @@ -15,36 +16,35 @@ should_be_treated_as_error, ) -from typing import TYPE_CHECKING - - if TYPE_CHECKING: from collections.abc import Callable, Mapping, MutableMapping - from typing import Any - from typing import Dict - from typing import Iterator - from typing import List - from typing import Optional - from typing import overload - from typing import ParamSpec - from typing import Tuple - from typing import Union - from typing import TypeVar - from typing import Set + from typing import ( + Any, + Dict, + Iterator, + List, + Optional, + ParamSpec, + Set, + Tuple, + TypeVar, + Union, + overload, + ) from typing_extensions import TypedDict, Unpack P = ParamSpec("P") R = TypeVar("R") - from sentry_sdk.profiler.continuous_profiler import ContinuousProfile - from sentry_sdk.profiler.transaction_profiler import Profile from sentry_sdk._types import ( Event, MeasurementUnit, - SamplingContext, MeasurementValue, + SamplingContext, ) + from sentry_sdk.profiler.continuous_profiler import ContinuousProfile + from sentry_sdk.profiler.transaction_profiler import Profile class SpanKwargs(TypedDict, total=False): trace_id: str @@ -140,8 +140,7 @@ class TransactionSource(str, Enum): URL = "url" VIEW = "view" - def __str__(self): - # type: () -> str + def __str__(self) -> str: return self.value @@ -163,8 +162,7 @@ def __str__(self): } -def get_span_status_from_http_code(http_status_code): - # type: (int) -> str +def get_span_status_from_http_code(http_status_code: int) -> str: """ Returns the Sentry status corresponding to the given HTTP status code. @@ -207,19 +205,17 @@ class _SpanRecorder: __slots__ = ("maxlen", "spans", "dropped_spans") - def __init__(self, maxlen): - # type: (int) -> None + def __init__(self, maxlen: int) -> None: # FIXME: this is `maxlen - 1` only to preserve historical behavior # enforced by tests. # Either this should be changed to `maxlen` or the JS SDK implementation # should be changed to match a consistent interpretation of what maxlen # limits: either transaction+spans or only child spans. self.maxlen = maxlen - 1 - self.spans = [] # type: List[Span] - self.dropped_spans = 0 # type: int + self.spans: "List[Span]" = [] + self.dropped_spans: int = 0 - def add(self, span): - # type: (Span) -> None + def add(self, span: "Span") -> None: if len(self.spans) > self.maxlen: span._span_recorder = None self.dropped_spans += 1 @@ -285,22 +281,21 @@ class Span: def __init__( self, - trace_id=None, # type: Optional[str] - span_id=None, # type: Optional[str] - parent_span_id=None, # type: Optional[str] - same_process_as_parent=True, # type: bool - sampled=None, # type: Optional[bool] - op=None, # type: Optional[str] - description=None, # type: Optional[str] - hub=None, # type: Optional[sentry_sdk.Hub] # deprecated - status=None, # type: Optional[str] - containing_transaction=None, # type: Optional[Transaction] - start_timestamp=None, # type: Optional[Union[datetime, float]] - scope=None, # type: Optional[sentry_sdk.Scope] - origin="manual", # type: str - name=None, # type: Optional[str] - ): - # type: (...) -> None + trace_id: "Optional[str]" = None, + span_id: "Optional[str]" = None, + parent_span_id: "Optional[str]" = None, + same_process_as_parent: bool = True, + sampled: "Optional[bool]" = None, + op: "Optional[str]" = None, + description: "Optional[str]" = None, + hub: "Optional[sentry_sdk.Hub]" = None, # deprecated + status: "Optional[str]" = None, + containing_transaction: "Optional[Transaction]" = None, + start_timestamp: "Optional[Union[datetime, float]]" = None, + scope: "Optional[sentry_sdk.Scope]" = None, + origin: str = "manual", + name: "Optional[str]" = None, + ) -> None: self._trace_id = trace_id self._span_id = span_id self.parent_span_id = parent_span_id @@ -312,11 +307,11 @@ def __init__( self.hub = hub # backwards compatibility self.scope = scope self.origin = origin - self._measurements = {} # type: Dict[str, MeasurementValue] - self._tags = {} # type: MutableMapping[str, str] - self._data = {} # type: Dict[str, Any] + self._measurements: "Dict[str, MeasurementValue]" = {} + self._tags: "MutableMapping[str, str]" = {} + self._data: "Dict[str, Any]" = {} self._containing_transaction = containing_transaction - self._flags = {} # type: Dict[str, bool] + self._flags: "Dict[str, bool]" = {} self._flags_capacity = 10 if hub is not None: @@ -341,48 +336,42 @@ def __init__( pass #: End timestamp of span - self.timestamp = None # type: Optional[datetime] + self.timestamp: "Optional[datetime]" = None - self._span_recorder = None # type: Optional[_SpanRecorder] + self._span_recorder: "Optional[_SpanRecorder]" = None self.update_active_thread() self.set_profiler_id(get_profiler_id()) # TODO this should really live on the Transaction class rather than the Span # class - def init_span_recorder(self, maxlen): - # type: (int) -> None + def init_span_recorder(self, maxlen: int) -> None: if self._span_recorder is None: self._span_recorder = _SpanRecorder(maxlen) @property - def trace_id(self): - # type: () -> str + def trace_id(self) -> str: if not self._trace_id: self._trace_id = uuid.uuid4().hex return self._trace_id @trace_id.setter - def trace_id(self, value): - # type: (str) -> None + def trace_id(self, value: str) -> None: self._trace_id = value @property - def span_id(self): - # type: () -> str + def span_id(self) -> str: if not self._span_id: self._span_id = uuid.uuid4().hex[16:] return self._span_id @span_id.setter - def span_id(self, value): - # type: (str) -> None + def span_id(self, value: str) -> None: self._span_id = value - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return ( "<%s(op=%r, description:%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r, origin=%r)>" % ( @@ -397,16 +386,16 @@ def __repr__(self): ) ) - def __enter__(self): - # type: () -> Span + def __enter__(self) -> "Span": scope = self.scope or sentry_sdk.get_current_scope() old_span = scope.span scope.span = self self._context_manager_state = (scope, old_span) return self - def __exit__(self, ty, value, tb): - # type: (Optional[Any], Optional[Any], Optional[Any]) -> None + def __exit__( + self, ty: "Optional[Any]", value: "Optional[Any]", tb: "Optional[Any]" + ) -> None: if value is not None and should_be_treated_as_error(ty, value): self.set_status(SPANSTATUS.INTERNAL_ERROR) @@ -417,8 +406,7 @@ def __exit__(self, ty, value, tb): scope.span = old_span @property - def containing_transaction(self): - # type: () -> Optional[Transaction] + def containing_transaction(self) -> "Optional[Transaction]": """The ``Transaction`` that this span belongs to. The ``Transaction`` is the root of the span tree, so one could also think of this ``Transaction`` as the "root span".""" @@ -428,8 +416,9 @@ def containing_transaction(self): # referencing themselves) return self._containing_transaction - def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): - # type: (str, **Any) -> Span + def start_child( + self, instrumenter: str = INSTRUMENTER.SENTRY, **kwargs: "Any" + ) -> "Span": """ Start a sub-span from the current span or transaction. @@ -473,10 +462,9 @@ def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): @classmethod def continue_from_environ( cls, - environ, # type: Mapping[str, str] - **kwargs, # type: Any - ): - # type: (...) -> Transaction + environ: "Mapping[str, str]", + **kwargs: "Any", + ) -> "Transaction": """ DEPRECATED: Use :py:meth:`sentry_sdk.continue_trace`. @@ -496,12 +484,11 @@ def continue_from_environ( @classmethod def continue_from_headers( cls, - headers, # type: Mapping[str, str] + headers: "Mapping[str, str]", *, - _sample_rand=None, # type: Optional[str] - **kwargs, # type: Any - ): - # type: (...) -> Transaction + _sample_rand: "Optional[str]" = None, + **kwargs: "Any", + ) -> "Transaction": """ DEPRECATED: Use :py:meth:`sentry_sdk.continue_trace`. @@ -538,8 +525,7 @@ def continue_from_headers( return transaction - def iter_headers(self): - # type: () -> Iterator[Tuple[str, str]] + def iter_headers(self) -> "Iterator[Tuple[str, str]]": """ Creates a generator which returns the span's ``sentry-trace`` and ``baggage`` headers. If the span's containing transaction doesn't yet have a ``baggage`` value, @@ -561,10 +547,9 @@ def iter_headers(self): @classmethod def from_traceparent( cls, - traceparent, # type: Optional[str] - **kwargs, # type: Any - ): - # type: (...) -> Optional[Transaction] + traceparent: "Optional[str]", + **kwargs: "Any", + ) -> "Optional[Transaction]": """ DEPRECATED: Use :py:meth:`sentry_sdk.continue_trace`. @@ -578,8 +563,7 @@ def from_traceparent( {SENTRY_TRACE_HEADER_NAME: traceparent}, **kwargs ) - def to_traceparent(self): - # type: () -> str + def to_traceparent(self) -> str: if self.sampled is True: sampled = "1" elif self.sampled is False: @@ -593,8 +577,7 @@ def to_traceparent(self): return traceparent - def to_baggage(self): - # type: () -> Optional[Baggage] + def to_baggage(self) -> "Optional[Baggage]": """Returns the :py:class:`~sentry_sdk.tracing_utils.Baggage` associated with this ``Span``, if any. (Taken from the root of the span tree.) """ @@ -602,29 +585,25 @@ def to_baggage(self): return self.containing_transaction.get_baggage() return None - def set_tag(self, key, value): - # type: (str, Any) -> None + def set_tag(self, key: str, value: "Any") -> None: self._tags[key] = value - def set_data(self, key, value): - # type: (str, Any) -> None + def set_data(self, key: str, value: "Any") -> None: self._data[key] = value - def update_data(self, data): - # type: (Dict[str, Any]) -> None + def update_data(self, data: "Dict[str, Any]") -> None: self._data.update(data) - def set_flag(self, flag, result): - # type: (str, bool) -> None + def set_flag(self, flag: str, result: bool) -> None: if len(self._flags) < self._flags_capacity: self._flags[flag] = result - def set_status(self, value): - # type: (str) -> None + def set_status(self, value: str) -> None: self.status = value - def set_measurement(self, name, value, unit=""): - # type: (str, float, MeasurementUnit) -> None + def set_measurement( + self, name: str, value: float, unit: "MeasurementUnit" = "" + ) -> None: """ .. deprecated:: 2.28.0 This function is deprecated and will be removed in the next major release. @@ -637,34 +616,34 @@ def set_measurement(self, name, value, unit=""): ) self._measurements[name] = {"value": value, "unit": unit} - def set_thread(self, thread_id, thread_name): - # type: (Optional[int], Optional[str]) -> None - + def set_thread( + self, thread_id: "Optional[int]", thread_name: "Optional[str]" + ) -> None: if thread_id is not None: self.set_data(SPANDATA.THREAD_ID, str(thread_id)) if thread_name is not None: self.set_data(SPANDATA.THREAD_NAME, thread_name) - def set_profiler_id(self, profiler_id): - # type: (Optional[str]) -> None + def set_profiler_id(self, profiler_id: "Optional[str]") -> None: if profiler_id is not None: self.set_data(SPANDATA.PROFILER_ID, profiler_id) - def set_http_status(self, http_status): - # type: (int) -> None + def set_http_status(self, http_status: int) -> None: self.set_tag( "http.status_code", str(http_status) ) # TODO-neel remove in major, we keep this for backwards compatibility self.set_data(SPANDATA.HTTP_STATUS_CODE, http_status) self.set_status(get_span_status_from_http_code(http_status)) - def is_success(self): - # type: () -> bool + def is_success(self) -> bool: return self.status == "ok" - def finish(self, scope=None, end_timestamp=None): - # type: (Optional[sentry_sdk.Scope], Optional[Union[float, datetime]]) -> Optional[str] + def finish( + self, + scope: "Optional[sentry_sdk.Scope]" = None, + end_timestamp: "Optional[Union[float, datetime]]" = None, + ) -> "Optional[str]": """ Sets the end timestamp of the span. @@ -701,11 +680,10 @@ def finish(self, scope=None, end_timestamp=None): return None - def to_json(self): - # type: () -> Dict[str, Any] + def to_json(self) -> "Dict[str, Any]": """Returns a JSON-compatible representation of the span.""" - rv = { + rv: "Dict[str, Any]" = { "trace_id": self.trace_id, "span_id": self.span_id, "parent_span_id": self.parent_span_id, @@ -715,7 +693,7 @@ def to_json(self): "start_timestamp": self.start_timestamp, "timestamp": self.timestamp, "origin": self.origin, - } # type: Dict[str, Any] + } if self.status: rv["status"] = self.status @@ -737,16 +715,15 @@ def to_json(self): return rv - def get_trace_context(self): - # type: () -> Any - rv = { + def get_trace_context(self) -> "Any": + rv: Dict[str, Any] = { "trace_id": self.trace_id, "span_id": self.span_id, "parent_span_id": self.parent_span_id, "op": self.op, "description": self.description, "origin": self.origin, - } # type: Dict[str, Any] + } if self.status: rv["status"] = self.status @@ -770,8 +747,7 @@ def get_trace_context(self): return rv - def get_profile_context(self): - # type: () -> Optional[ProfileContext] + def get_profile_context(self) -> "Optional[ProfileContext]": profiler_id = self._data.get(SPANDATA.PROFILER_ID) if profiler_id is None: return None @@ -780,8 +756,7 @@ def get_profile_context(self): "profiler_id": profiler_id, } - def update_active_thread(self): - # type: () -> None + def update_active_thread(self) -> None: thread_id, thread_name = get_current_thread_meta() self.set_thread(thread_id, thread_name) @@ -820,23 +795,22 @@ class Transaction(Span): def __init__( # type: ignore[misc] self, - name="", # type: str - parent_sampled=None, # type: Optional[bool] - baggage=None, # type: Optional[Baggage] - source=TransactionSource.CUSTOM, # type: str - **kwargs, # type: Unpack[SpanKwargs] - ): - # type: (...) -> None + name: str = "", + parent_sampled: "Optional[bool]" = None, + baggage: "Optional[Baggage]" = None, + source: str = TransactionSource.CUSTOM, + **kwargs: "Unpack[SpanKwargs]", + ) -> None: super().__init__(**kwargs) self.name = name self.source = source - self.sample_rate = None # type: Optional[float] + self.sample_rate: "Optional[float]" = None self.parent_sampled = parent_sampled - self._measurements = {} # type: Dict[str, MeasurementValue] - self._contexts = {} # type: Dict[str, Any] - self._profile = None # type: Optional[Profile] - self._continuous_profile = None # type: Optional[ContinuousProfile] + self._measurements: "Dict[str, MeasurementValue]" = {} + self._contexts: "Dict[str, Any]" = {} + self._profile: "Optional[Profile]" = None + self._continuous_profile: "Optional[ContinuousProfile]" = None self._baggage = baggage baggage_sample_rand = ( @@ -847,8 +821,7 @@ def __init__( # type: ignore[misc] else: self._sample_rand = _generate_sample_rand(self.trace_id) - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return ( "<%s(name=%r, op=%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r, source=%r, origin=%r)>" % ( @@ -864,8 +837,7 @@ def __repr__(self): ) ) - def _possibly_started(self): - # type: () -> bool + def _possibly_started(self) -> bool: """Returns whether the transaction might have been started. If this returns False, we know that the transaction was not started @@ -876,8 +848,7 @@ def _possibly_started(self): # We must explicitly check self.sampled is False since self.sampled can be None return self._span_recorder is not None or self.sampled is False - def __enter__(self): - # type: () -> Transaction + def __enter__(self) -> "Transaction": if not self._possibly_started(): logger.debug( "Transaction was entered without being started with sentry_sdk.start_transaction." @@ -892,8 +863,9 @@ def __enter__(self): return self - def __exit__(self, ty, value, tb): - # type: (Optional[Any], Optional[Any], Optional[Any]) -> None + def __exit__( + self, ty: "Optional[Any]", value: "Optional[Any]", tb: "Optional[Any]" + ) -> None: if self._profile is not None: self._profile.__exit__(ty, value, tb) @@ -903,8 +875,7 @@ def __exit__(self, ty, value, tb): super().__exit__(ty, value, tb) @property - def containing_transaction(self): - # type: () -> Transaction + def containing_transaction(self) -> "Transaction": """The root element of the span tree. In the case of a transaction it is the transaction itself. """ @@ -916,10 +887,9 @@ def containing_transaction(self): def _get_scope_from_finish_args( self, - scope_arg, # type: Optional[Union[sentry_sdk.Scope, sentry_sdk.Hub]] - hub_arg, # type: Optional[Union[sentry_sdk.Scope, sentry_sdk.Hub]] - ): - # type: (...) -> Optional[sentry_sdk.Scope] + scope_arg: "Optional[Union[sentry_sdk.Scope, sentry_sdk.Hub]]", + hub_arg: "Optional[Union[sentry_sdk.Scope, sentry_sdk.Hub]]", + ) -> "Optional[sentry_sdk.Scope]": """ Logic to get the scope from the arguments passed to finish. This function exists for backwards compatibility with the old finish. @@ -947,20 +917,18 @@ def _get_scope_from_finish_args( return scope_or_hub - def _get_log_representation(self): - # type: () -> str + def _get_log_representation(self) -> str: return "{op}transaction <{name}>".format( op=("<" + self.op + "> " if self.op else ""), name=self.name ) def finish( self, - scope=None, # type: Optional[sentry_sdk.Scope] - end_timestamp=None, # type: Optional[Union[float, datetime]] + scope: "Optional[sentry_sdk.Scope]" = None, + end_timestamp: "Optional[Union[float, datetime]]" = None, *, - hub=None, # type: Optional[sentry_sdk.Hub] - ): - # type: (...) -> Optional[str] + hub: "Optional[sentry_sdk.Hub]" = None, + ) -> "Optional[str]": """Finishes the transaction and sends it to Sentry. All finished spans in the transaction will also be sent to Sentry. @@ -981,7 +949,9 @@ def finish( # For backwards compatibility, we must handle the case where `scope` # or `hub` could both either be a `Scope` or a `Hub`. - scope = self._get_scope_from_finish_args(scope, hub) # type: Optional[sentry_sdk.Scope] + scope: "Optional[sentry_sdk.Scope]" = self._get_scope_from_finish_args( + scope, hub + ) scope = scope or self.scope or sentry_sdk.get_current_scope() client = sentry_sdk.get_client() @@ -1078,7 +1048,7 @@ def finish( if profile_context is not None: contexts.update({"profile": profile_context}) - event = { + event: "Event" = { "type": "transaction", "transaction": self.name, "transaction_info": {"source": self.source}, @@ -1087,7 +1057,7 @@ def finish( "timestamp": self.timestamp, "start_timestamp": self.start_timestamp, "spans": finished_spans, - } # type: Event + } if dropped_spans > 0: event["_dropped_spans"] = dropped_spans @@ -1100,8 +1070,9 @@ def finish( return scope.capture_event(event) - def set_measurement(self, name, value, unit=""): - # type: (str, float, MeasurementUnit) -> None + def set_measurement( + self, name: str, value: float, unit: "MeasurementUnit" = "" + ) -> None: """ .. deprecated:: 2.28.0 This function is deprecated and will be removed in the next major release. @@ -1114,8 +1085,7 @@ def set_measurement(self, name, value, unit=""): ) self._measurements[name] = {"value": value, "unit": unit} - def set_context(self, key, value): - # type: (str, dict[str, Any]) -> None + def set_context(self, key: str, value: "dict[str, Any]") -> None: """Sets a context. Transactions can have multiple contexts and they should follow the format described in the "Contexts Interface" documentation. @@ -1125,16 +1095,14 @@ def set_context(self, key, value): """ self._contexts[key] = value - def set_http_status(self, http_status): - # type: (int) -> None + def set_http_status(self, http_status: int) -> None: """Sets the status of the Transaction according to the given HTTP status. :param http_status: The HTTP status code.""" super().set_http_status(http_status) self.set_context("response", {"status_code": http_status}) - def to_json(self): - # type: () -> Dict[str, Any] + def to_json(self) -> "Dict[str, Any]": """Returns a JSON-compatible representation of the transaction.""" rv = super().to_json() @@ -1144,8 +1112,7 @@ def to_json(self): return rv - def get_trace_context(self): - # type: () -> Any + def get_trace_context(self) -> "Any": trace_context = super().get_trace_context() if self._data: @@ -1153,8 +1120,7 @@ def get_trace_context(self): return trace_context - def get_baggage(self): - # type: () -> Baggage + def get_baggage(self) -> "Baggage": """Returns the :py:class:`~sentry_sdk.tracing_utils.Baggage` associated with the Transaction. @@ -1165,8 +1131,9 @@ def get_baggage(self): return self._baggage - def _set_initial_sampling_decision(self, sampling_context): - # type: (SamplingContext) -> None + def _set_initial_sampling_decision( + self, sampling_context: "SamplingContext" + ) -> None: """ Sets the transaction's sampling decision, according to the following precedence rules: @@ -1267,98 +1234,83 @@ def _set_initial_sampling_decision(self, sampling_context): class NoOpSpan(Span): - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "<%s>" % self.__class__.__name__ @property - def containing_transaction(self): - # type: () -> Optional[Transaction] + def containing_transaction(self) -> "Optional[Transaction]": return None - def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): - # type: (str, **Any) -> NoOpSpan + def start_child( + self, instrumenter: str = INSTRUMENTER.SENTRY, **kwargs: "Any" + ) -> "NoOpSpan": return NoOpSpan() - def to_traceparent(self): - # type: () -> str + def to_traceparent(self) -> str: return "" - def to_baggage(self): - # type: () -> Optional[Baggage] + def to_baggage(self) -> "Optional[Baggage]": return None - def get_baggage(self): - # type: () -> Optional[Baggage] + def get_baggage(self) -> "Optional[Baggage]": return None - def iter_headers(self): - # type: () -> Iterator[Tuple[str, str]] + def iter_headers(self) -> "Iterator[Tuple[str, str]]": return iter(()) - def set_tag(self, key, value): - # type: (str, Any) -> None + def set_tag(self, key: str, value: "Any") -> None: pass - def set_data(self, key, value): - # type: (str, Any) -> None + def set_data(self, key: str, value: "Any") -> None: pass - def update_data(self, data): - # type: (Dict[str, Any]) -> None + def update_data(self, data: "Dict[str, Any]") -> None: pass - def set_status(self, value): - # type: (str) -> None + def set_status(self, value: str) -> None: pass - def set_http_status(self, http_status): - # type: (int) -> None + def set_http_status(self, http_status: int) -> None: pass - def is_success(self): - # type: () -> bool + def is_success(self) -> bool: return True - def to_json(self): - # type: () -> Dict[str, Any] + def to_json(self) -> "Dict[str, Any]": return {} - def get_trace_context(self): - # type: () -> Any + def get_trace_context(self) -> "Any": return {} - def get_profile_context(self): - # type: () -> Any + def get_profile_context(self) -> "Any": return {} def finish( self, - scope=None, # type: Optional[sentry_sdk.Scope] - end_timestamp=None, # type: Optional[Union[float, datetime]] + scope: "Optional[sentry_sdk.Scope]" = None, + end_timestamp: "Optional[Union[float, datetime]]" = None, *, - hub=None, # type: Optional[sentry_sdk.Hub] - ): - # type: (...) -> Optional[str] + hub: "Optional[sentry_sdk.Hub]" = None, + ) -> "Optional[str]": """ The `hub` parameter is deprecated. Please use the `scope` parameter, instead. """ pass - def set_measurement(self, name, value, unit=""): - # type: (str, float, MeasurementUnit) -> None + def set_measurement( + self, name: str, value: float, unit: "MeasurementUnit" = "" + ) -> None: pass - def set_context(self, key, value): - # type: (str, dict[str, Any]) -> None + def set_context(self, key: str, value: "dict[str, Any]") -> None: pass - def init_span_recorder(self, maxlen): - # type: (int) -> None + def init_span_recorder(self, maxlen: int) -> None: pass - def _set_initial_sampling_decision(self, sampling_context): - # type: (SamplingContext) -> None + def _set_initial_sampling_decision( + self, sampling_context: "SamplingContext" + ) -> None: pass @@ -1366,23 +1318,30 @@ def _set_initial_sampling_decision(self, sampling_context): @overload def trace( - func=None, *, op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT - ): - # type: (None, Optional[str], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Callable[[Callable[P, R]], Callable[P, R]] + func: None = None, + *, + op: "Optional[str]" = None, + name: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, + template: "SPANTEMPLATE" = SPANTEMPLATE.DEFAULT, + ) -> "Callable[[Callable[P, R]], Callable[P, R]]": # Handles: @trace() and @trace(op="custom") pass @overload - def trace(func): - # type: (Callable[P, R]) -> Callable[P, R] + def trace(func: "Callable[P, R]") -> "Callable[P, R]": # Handles: @trace pass def trace( - func=None, *, op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT -): - # type: (Optional[Callable[P, R]], Optional[str], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]] + func: "Optional[Callable[P, R]]" = None, + *, + op: "Optional[str]" = None, + name: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, + template: "SPANTEMPLATE" = SPANTEMPLATE.DEFAULT, +) -> "Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]]": """ Decorator to start a child span around a function call. @@ -1469,8 +1428,8 @@ def calculate_interest_rate(amount, rate, years): from sentry_sdk.tracing_utils import ( Baggage, EnvironHeaders, - extract_sentrytrace_data, _generate_sample_rand, + extract_sentrytrace_data, has_tracing_enabled, maybe_create_breadcrumbs_from_span, ) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 69ba197ddf..5202c79c52 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -63,23 +63,19 @@ class EnvironHeaders(Mapping): # type: ignore def __init__( self, - environ, # type: Mapping[str, str] - prefix="HTTP_", # type: str - ): - # type: (...) -> None + environ: "Mapping[str, str]", + prefix: str = "HTTP_", + ) -> None: self.environ = environ self.prefix = prefix - def __getitem__(self, key): - # type: (str) -> Optional[Any] + def __getitem__(self, key: str) -> "Optional[Any]": return self.environ[self.prefix + key.replace("-", "_").upper()] - def __len__(self): - # type: () -> int + def __len__(self) -> int: return sum(1 for _ in iter(self)) - def __iter__(self): - # type: () -> Generator[str, None, None] + def __iter__(self) -> "Generator[str, None, None]": for k in self.environ: if not isinstance(k, str): continue @@ -91,8 +87,7 @@ def __iter__(self): yield k[len(self.prefix) :] -def has_tracing_enabled(options): - # type: (Optional[Dict[str, Any]]) -> bool +def has_tracing_enabled(options: "Optional[Dict[str, Any]]") -> bool: """ Returns True if either traces_sample_rate or traces_sampler is defined and enable_tracing is set and not false. @@ -111,16 +106,14 @@ def has_tracing_enabled(options): @contextlib.contextmanager def record_sql_queries( - cursor, # type: Any - query, # type: Any - params_list, # type: Any - paramstyle, # type: Optional[str] - executemany, # type: bool - record_cursor_repr=False, # type: bool - span_origin="manual", # type: str -): - # type: (...) -> Generator[sentry_sdk.tracing.Span, None, None] - + cursor: "Any", + query: "Any", + params_list: "Any", + paramstyle: "Optional[str]", + executemany: bool, + record_cursor_repr: bool = False, + span_origin: str = "manual", +) -> "Generator[sentry_sdk.tracing.Span, None, None]": # TODO: Bring back capturing of params by default if sentry_sdk.get_client().options["_experiments"].get("record_sql_params", False): if not params_list or params_list == [None]: @@ -157,8 +150,9 @@ def record_sql_queries( yield span -def maybe_create_breadcrumbs_from_span(scope, span): - # type: (sentry_sdk.Scope, sentry_sdk.tracing.Span) -> None +def maybe_create_breadcrumbs_from_span( + scope: "sentry_sdk.Scope", span: "sentry_sdk.tracing.Span" +) -> None: if span.op == OP.DB_REDIS: scope.add_breadcrumb( message=span.description, type="redis", category="redis", data=span._tags @@ -189,8 +183,7 @@ def maybe_create_breadcrumbs_from_span(scope, span): ) -def _get_frame_module_abs_path(frame): - # type: (FrameType) -> Optional[str] +def _get_frame_module_abs_path(frame: "FrameType") -> "Optional[str]": try: return frame.f_code.co_filename except Exception: @@ -198,14 +191,13 @@ def _get_frame_module_abs_path(frame): def _should_be_included( - is_sentry_sdk_frame, # type: bool - namespace, # type: Optional[str] - in_app_include, # type: Optional[list[str]] - in_app_exclude, # type: Optional[list[str]] - abs_path, # type: Optional[str] - project_root, # type: Optional[str] -): - # type: (...) -> bool + is_sentry_sdk_frame: bool, + namespace: "Optional[str]", + in_app_include: "Optional[list[str]]", + in_app_exclude: "Optional[list[str]]", + abs_path: "Optional[str]", + project_root: "Optional[str]", +) -> bool: # in_app_include takes precedence over in_app_exclude should_be_included = _module_in_list(namespace, in_app_include) should_be_excluded = _is_external_source(abs_path) or _module_in_list( @@ -217,18 +209,22 @@ def _should_be_included( ) -def add_source(span, project_root, in_app_include, in_app_exclude): - # type: (sentry_sdk.tracing.Span, Optional[str], Optional[list[str]], Optional[list[str]]) -> None +def add_source( + span: "sentry_sdk.tracing.Span", + project_root: "Optional[str]", + in_app_include: "Optional[list[str]]", + in_app_exclude: "Optional[list[str]]", +) -> None: """ Adds OTel compatible source code information to the span """ # Find the correct frame - frame = sys._getframe() # type: Union[FrameType, None] + frame: "Union[FrameType, None]" = sys._getframe() while frame is not None: abs_path = _get_frame_module_abs_path(frame) try: - namespace = frame.f_globals.get("__name__") # type: Optional[str] + namespace: "Optional[str]" = frame.f_globals.get("__name__") except Exception: namespace = None @@ -286,8 +282,7 @@ def add_source(span, project_root, in_app_include, in_app_exclude): span.set_data(SPANDATA.CODE_FUNCTION, frame.f_code.co_name) -def add_query_source(span): - # type: (sentry_sdk.tracing.Span) -> None +def add_query_source(span: "sentry_sdk.tracing.Span") -> None: """ Adds OTel compatible source code information to a database query span """ @@ -317,8 +312,7 @@ def add_query_source(span): ) -def add_http_request_source(span): - # type: (sentry_sdk.tracing.Span) -> None +def add_http_request_source(span: "sentry_sdk.tracing.Span") -> None: """ Adds OTel compatible source code information to a span for an outgoing HTTP request """ @@ -348,8 +342,9 @@ def add_http_request_source(span): ) -def extract_sentrytrace_data(header): - # type: (Optional[str]) -> Optional[Dict[str, Union[str, bool, None]]] +def extract_sentrytrace_data( + header: "Optional[str]", +) -> "Optional[Dict[str, Union[str, bool, None]]]": """ Given a `sentry-trace` header string, return a dictionary of data. """ @@ -380,9 +375,7 @@ def extract_sentrytrace_data(header): } -def _format_sql(cursor, sql): - # type: (Any, str) -> Optional[str] - +def _format_sql(cursor: "Any", sql: str) -> "Optional[str]": real_sql = None # If we're using psycopg2, it could be that we're @@ -415,14 +408,13 @@ class PropagationContext: def __init__( self, - trace_id=None, # type: Optional[str] - span_id=None, # type: Optional[str] - parent_span_id=None, # type: Optional[str] - parent_sampled=None, # type: Optional[bool] - dynamic_sampling_context=None, # type: Optional[Dict[str, str]] - baggage=None, # type: Optional[Baggage] - ): - # type: (...) -> None + trace_id: "Optional[str]" = None, + span_id: "Optional[str]" = None, + parent_span_id: "Optional[str]" = None, + parent_sampled: "Optional[bool]" = None, + dynamic_sampling_context: "Optional[Dict[str, str]]" = None, + baggage: "Optional[Baggage]" = None, + ) -> None: self._trace_id = trace_id """The trace id of the Sentry trace.""" @@ -446,8 +438,9 @@ def __init__( self.baggage = Baggage(dynamic_sampling_context) @classmethod - def from_incoming_data(cls, incoming_data): - # type: (Dict[str, Any]) -> PropagationContext + def from_incoming_data( + cls, incoming_data: "Dict[str, Any]" + ) -> "PropagationContext": propagation_context = PropagationContext() normalized_data = normalize_incoming_data(incoming_data) @@ -475,8 +468,7 @@ def from_incoming_data(cls, incoming_data): return propagation_context @property - def trace_id(self): - # type: () -> str + def trace_id(self) -> str: """The trace id of the Sentry trace.""" if not self._trace_id: # New trace, don't fill in sample_rand @@ -485,13 +477,11 @@ def trace_id(self): return self._trace_id @trace_id.setter - def trace_id(self, value): - # type: (str) -> None + def trace_id(self, value: str) -> None: self._trace_id = value @property - def span_id(self): - # type: () -> str + def span_id(self) -> str: """The span id of the currently executed span.""" if not self._span_id: self._span_id = uuid.uuid4().hex[16:] @@ -499,17 +489,14 @@ def span_id(self): return self._span_id @span_id.setter - def span_id(self, value): - # type: (str) -> None + def span_id(self, value: str) -> None: self._span_id = value @property - def dynamic_sampling_context(self): - # type: () -> Optional[Dict[str, Any]] + def dynamic_sampling_context(self) -> "Optional[Dict[str, Any]]": return self.baggage.dynamic_sampling_context() if self.baggage else None - def update(self, other_dict): - # type: (Dict[str, Any]) -> None + def update(self, other_dict: "Dict[str, Any]") -> None: """ Updates the PropagationContext with data from the given dictionary. """ @@ -519,8 +506,7 @@ def update(self, other_dict): except AttributeError: pass - def __repr__(self): - # type: (...) -> str + def __repr__(self) -> str: return "".format( self._trace_id, self._span_id, @@ -529,8 +515,7 @@ def __repr__(self): self.baggage, ) - def _fill_sample_rand(self): - # type: () -> None + def _fill_sample_rand(self) -> None: """ Ensure that there is a valid sample_rand value in the baggage. @@ -577,8 +562,7 @@ def _fill_sample_rand(self): self.baggage.sentry_items["sample_rand"] = f"{sample_rand:.6f}" # noqa: E231 - def _sample_rand(self): - # type: () -> Optional[str] + def _sample_rand(self) -> "Optional[str]": """Convenience method to get the sample_rand value from the baggage.""" if self.baggage is None: return None @@ -602,9 +586,9 @@ class Baggage: def __init__( self, - sentry_items, # type: Dict[str, str] - third_party_items="", # type: str - mutable=True, # type: bool + sentry_items: "Dict[str, str]", + third_party_items: str = "", + mutable: bool = True, ): self.sentry_items = sentry_items self.third_party_items = third_party_items @@ -613,11 +597,10 @@ def __init__( @classmethod def from_incoming_header( cls, - header, # type: Optional[str] + header: "Optional[str]", *, - _sample_rand=None, # type: Optional[str] - ): - # type: (...) -> Baggage + _sample_rand: "Optional[str]" = None, + ) -> "Baggage": """ freeze if incoming header already has sentry baggage """ @@ -647,10 +630,8 @@ def from_incoming_header( return Baggage(sentry_items, third_party_items, mutable) @classmethod - def from_options(cls, scope): - # type: (sentry_sdk.scope.Scope) -> Optional[Baggage] - - sentry_items = {} # type: Dict[str, str] + def from_options(cls, scope: "sentry_sdk.scope.Scope") -> "Optional[Baggage]": + sentry_items: Dict[str, str] = {} third_party_items = "" mutable = False @@ -682,14 +663,15 @@ def from_options(cls, scope): return Baggage(sentry_items, third_party_items, mutable) @classmethod - def populate_from_transaction(cls, transaction): - # type: (sentry_sdk.tracing.Transaction) -> Baggage + def populate_from_transaction( + cls, transaction: "sentry_sdk.tracing.Transaction" + ) -> "Baggage": """ Populate fresh baggage entry with sentry_items and make it immutable if this is the head SDK which originates traces. """ client = sentry_sdk.get_client() - sentry_items = {} # type: Dict[str, str] + sentry_items: "Dict[str, str]" = {} if not client.is_active(): return Baggage(sentry_items) @@ -730,12 +712,10 @@ def populate_from_transaction(cls, transaction): return Baggage(sentry_items, mutable=False) - def freeze(self): - # type: () -> None + def freeze(self) -> None: self.mutable = False - def dynamic_sampling_context(self): - # type: () -> Dict[str, str] + def dynamic_sampling_context(self) -> "Dict[str, str]": header = {} for key, item in self.sentry_items.items(): @@ -743,8 +723,7 @@ def dynamic_sampling_context(self): return header - def serialize(self, include_third_party=False): - # type: (bool) -> str + def serialize(self, include_third_party: bool = False) -> str: items = [] for key, val in self.sentry_items.items(): @@ -758,8 +737,7 @@ def serialize(self, include_third_party=False): return ",".join(items) @staticmethod - def strip_sentry_baggage(header): - # type: (str) -> str + def strip_sentry_baggage(header: str) -> str: """Remove Sentry baggage from the given header. Given a Baggage header, return a new Baggage header with all Sentry baggage items removed. @@ -772,8 +750,7 @@ def strip_sentry_baggage(header): ) ) - def _sample_rand(self): - # type: () -> Optional[float] + def _sample_rand(self) -> "Optional[float]": """Convenience method to get the sample_rand value from the sentry_items. We validate the value and parse it as a float before returning it. The value is considered @@ -786,13 +763,11 @@ def _sample_rand(self): return None - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return f'' -def should_propagate_trace(client, url): - # type: (sentry_sdk.client.BaseClient, str) -> bool +def should_propagate_trace(client: "sentry_sdk.client.BaseClient", url: str) -> bool: """ Returns True if url matches trace_propagation_targets configured in the given client. Otherwise, returns False. """ @@ -804,8 +779,7 @@ def should_propagate_trace(client, url): return match_regex_list(url, trace_propagation_targets, substring_matching=True) -def normalize_incoming_data(incoming_data): - # type: (Dict[str, Any]) -> Dict[str, Any] +def normalize_incoming_data(incoming_data: "Dict[str, Any]") -> "Dict[str, Any]": """ Normalizes incoming data so the keys are all lowercase with dashes instead of underscores and stripped from known prefixes. """ @@ -821,9 +795,11 @@ def normalize_incoming_data(incoming_data): def create_span_decorator( - op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT -): - # type: (Optional[Union[str, OP]], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Any + op: "Optional[Union[str, OP]]" = None, + name: "Optional[str]" = None, + attributes: "Optional[dict[str, Any]]" = None, + template: "SPANTEMPLATE" = SPANTEMPLATE.DEFAULT, +) -> "Any": """ Create a span decorator that can wrap both sync and async functions. @@ -842,15 +818,13 @@ def create_span_decorator( """ from sentry_sdk.scope import should_send_default_pii - def span_decorator(f): - # type: (Any) -> Any + def span_decorator(f: "Any") -> "Any": """ Decorator to create a span for the given function. """ @functools.wraps(f) - async def async_wrapper(*args, **kwargs): - # type: (*Any, **Any) -> Any + async def async_wrapper(*args: "Any", **kwargs: "Any") -> "Any": current_span = get_current_span() if current_span is None: @@ -887,8 +861,7 @@ async def async_wrapper(*args, **kwargs): pass @functools.wraps(f) - def sync_wrapper(*args, **kwargs): - # type: (*Any, **Any) -> Any + def sync_wrapper(*args: "Any", **kwargs: "Any") -> "Any": current_span = get_current_span() if current_span is None: @@ -932,8 +905,7 @@ def sync_wrapper(*args, **kwargs): return span_decorator -def get_current_span(scope=None): - # type: (Optional[sentry_sdk.Scope]) -> Optional[Span] +def get_current_span(scope: "Optional[sentry_sdk.Scope]" = None) -> "Optional[Span]": """ Returns the currently active span if there is one running, otherwise `None` """ @@ -942,8 +914,7 @@ def get_current_span(scope=None): return current_span -def set_span_errored(span=None): - # type: (Optional[Span]) -> None +def set_span_errored(span: "Optional[Span]" = None) -> None: """ Set the status of the current or given span to INTERNAL_ERROR. Also sets the status of the transaction (root span) to INTERNAL_ERROR. @@ -956,11 +927,10 @@ def set_span_errored(span=None): def _generate_sample_rand( - trace_id, # type: Optional[str] + trace_id: "Optional[str]", *, - interval=(0.0, 1.0), # type: tuple[float, float] -): - # type: (...) -> float + interval: "tuple[float, float]" = (0.0, 1.0), +) -> float: """Generate a sample_rand value from a trace ID. The generated value will be pseudorandomly chosen from the provided @@ -987,8 +957,9 @@ def _generate_sample_rand( return sample_rand_scaled / 1_000_000 -def _sample_rand_range(parent_sampled, sample_rate): - # type: (Optional[bool], Optional[float]) -> tuple[float, float] +def _sample_rand_range( + parent_sampled: "Optional[bool]", sample_rate: "Optional[float]" +) -> "tuple[float, float]": """ Compute the lower (inclusive) and upper (exclusive) bounds of the range of values that a generated sample_rand value must fall into, given the parent_sampled and @@ -1002,8 +973,7 @@ def _sample_rand_range(parent_sampled, sample_rate): return sample_rate, 1.0 -def _get_value(source, key): - # type: (Any, str) -> Optional[Any] +def _get_value(source: "Any", key: str) -> "Optional[Any]": """ Gets a value from a source object. The source can be a dict or an object. It is checked for dictionary keys and object attributes. @@ -1020,8 +990,11 @@ def _get_value(source, key): return value -def _get_span_name(template, name, kwargs=None): - # type: (Union[str, SPANTEMPLATE], str, Optional[dict[str, Any]]) -> str +def _get_span_name( + template: "Union[str, SPANTEMPLATE]", + name: str, + kwargs: "Optional[dict[str, Any]]" = None, +) -> str: """ Get the name of the span based on the template and the name. """ @@ -1046,27 +1019,30 @@ def _get_span_name(template, name, kwargs=None): return span_name -def _get_span_op(template): - # type: (Union[str, SPANTEMPLATE]) -> str +def _get_span_op(template: "Union[str, SPANTEMPLATE]") -> str: """ Get the operation of the span based on the template. """ - mapping = { + mapping: "dict[Union[str, SPANTEMPLATE], Union[str, OP]]" = { SPANTEMPLATE.AI_CHAT: OP.GEN_AI_CHAT, SPANTEMPLATE.AI_AGENT: OP.GEN_AI_INVOKE_AGENT, SPANTEMPLATE.AI_TOOL: OP.GEN_AI_EXECUTE_TOOL, - } # type: dict[Union[str, SPANTEMPLATE], Union[str, OP]] + } op = mapping.get(template, OP.FUNCTION) return str(op) -def _get_input_attributes(template, send_pii, args, kwargs): - # type: (Union[str, SPANTEMPLATE], bool, tuple[Any, ...], dict[str, Any]) -> dict[str, Any] +def _get_input_attributes( + template: "Union[str, SPANTEMPLATE]", + send_pii: bool, + args: "tuple[Any, ...]", + kwargs: "dict[str, Any]", +) -> "dict[str, Any]": """ Get input attributes for the given span template. """ - attributes = {} # type: dict[str, Any] + attributes: "dict[str, Any]" = {} if template in [SPANTEMPLATE.AI_AGENT, SPANTEMPLATE.AI_TOOL, SPANTEMPLATE.AI_CHAT]: mapping = { @@ -1082,8 +1058,7 @@ def _get_input_attributes(template, send_pii, args, kwargs): "top_k": (SPANDATA.GEN_AI_REQUEST_TOP_K, int), } - def _set_from_key(key, value): - # type: (str, Any) -> None + def _set_from_key(key: str, value: "Any") -> None: if key in mapping: (attribute, data_type) = mapping[key] if value is not None and isinstance(value, data_type): @@ -1118,15 +1093,13 @@ def _set_from_key(key, value): return attributes -def _get_usage_attributes(usage): - # type: (Any) -> dict[str, Any] +def _get_usage_attributes(usage: "Any") -> "dict[str, Any]": """ Get usage attributes. """ attributes = {} - def _set_from_keys(attribute, keys): - # type: (str, tuple[str, ...]) -> None + def _set_from_keys(attribute: str, keys: "tuple[str, ...]") -> None: for key in keys: value = _get_value(usage, key) if value is not None and isinstance(value, int): @@ -1148,12 +1121,13 @@ def _set_from_keys(attribute, keys): return attributes -def _get_output_attributes(template, send_pii, result): - # type: (Union[str, SPANTEMPLATE], bool, Any) -> dict[str, Any] +def _get_output_attributes( + template: "Union[str, SPANTEMPLATE]", send_pii: bool, result: "Any" +) -> "dict[str, Any]": """ Get output attributes for the given span template. """ - attributes = {} # type: dict[str, Any] + attributes: "dict[str, Any]" = {} if template in [SPANTEMPLATE.AI_AGENT, SPANTEMPLATE.AI_TOOL, SPANTEMPLATE.AI_CHAT]: with capture_internal_exceptions(): @@ -1187,8 +1161,15 @@ def _get_output_attributes(template, send_pii, result): return attributes -def _set_input_attributes(span, template, send_pii, name, f, args, kwargs): - # type: (Span, Union[str, SPANTEMPLATE], bool, str, Any, tuple[Any, ...], dict[str, Any]) -> None +def _set_input_attributes( + span: "Span", + template: "Union[str, SPANTEMPLATE]", + send_pii: bool, + name: str, + f: "Any", + args: "tuple[Any, ...]", + kwargs: "dict[str, Any]", +) -> None: """ Set span input attributes based on the given span template. @@ -1199,7 +1180,7 @@ def _set_input_attributes(span, template, send_pii, name, f, args, kwargs): :param args: The arguments to the wrapped function. :param kwargs: The keyword arguments to the wrapped function. """ - attributes = {} # type: dict[str, Any] + attributes: "dict[str, Any]" = {} if template == SPANTEMPLATE.AI_AGENT: attributes = { @@ -1224,8 +1205,9 @@ def _set_input_attributes(span, template, send_pii, name, f, args, kwargs): span.update_data(attributes or {}) -def _set_output_attributes(span, template, send_pii, result): - # type: (Span, Union[str, SPANTEMPLATE], bool, Any) -> None +def _set_output_attributes( + span: "Span", template: "Union[str, SPANTEMPLATE]", send_pii: bool, result: "Any" +) -> None: """ Set span output attributes based on the given span template. @@ -1237,8 +1219,7 @@ def _set_output_attributes(span, template, send_pii, result): span.update_data(_get_output_attributes(template, send_pii, result) or {}) -def _should_continue_trace(baggage): - # type: (Optional[Baggage]) -> bool +def _should_continue_trace(baggage: "Optional[Baggage]") -> bool: """ Check if we should continue the incoming trace according to the strict_trace_continuation spec. https://develop.sentry.dev/sdk/telemetry/traces/#stricttracecontinuation @@ -1259,7 +1240,9 @@ def _should_continue_trace(baggage): ) return False - strict_trace_continuation = client.options.get("strict_trace_continuation", False) # type: bool + strict_trace_continuation: bool = client.options.get( + "strict_trace_continuation", False + ) if strict_trace_continuation: if (baggage_org_id is not None and client_org_id is None) or ( baggage_org_id is None and client_org_id is not None diff --git a/sentry_sdk/transport.py b/sentry_sdk/transport.py index 07274e9278..4a982f2ed2 100644 --- a/sentry_sdk/transport.py +++ b/sentry_sdk/transport.py @@ -64,18 +64,16 @@ class Transport(ABC): A transport is used to send an event to sentry. """ - parsed_dsn = None # type: Optional[Dsn] + parsed_dsn: "Optional[Dsn]" = None - def __init__(self, options=None): - # type: (Self, Optional[Dict[str, Any]]) -> None + def __init__(self: "Self", options: "Optional[Dict[str, Any]]" = None) -> None: self.options = options if options and options["dsn"] is not None and options["dsn"]: self.parsed_dsn = Dsn(options["dsn"], options.get("org_id")) else: self.parsed_dsn = None - def capture_event(self, event): - # type: (Self, Event) -> None + def capture_event(self: "Self", event: "Event") -> None: """ DEPRECATED: Please use capture_envelope instead. @@ -94,8 +92,7 @@ def capture_event(self, event): self.capture_envelope(envelope) @abstractmethod - def capture_envelope(self, envelope): - # type: (Self, Envelope) -> None + def capture_envelope(self: "Self", envelope: "Envelope") -> None: """ Send an envelope to Sentry. @@ -106,11 +103,10 @@ def capture_envelope(self, envelope): pass def flush( - self, - timeout, - callback=None, - ): - # type: (Self, float, Optional[Any]) -> None + self: "Self", + timeout: float, + callback: "Optional[Any]" = None, + ) -> None: """ Wait `timeout` seconds for the current events to be sent out. @@ -119,8 +115,7 @@ def flush( """ return None - def kill(self): - # type: (Self) -> None + def kill(self: "Self") -> None: """ Forcefully kills the transport. @@ -131,13 +126,12 @@ def kill(self): def record_lost_event( self, - reason, # type: str - data_category=None, # type: Optional[EventDataCategory] - item=None, # type: Optional[Item] + reason: str, + data_category: "Optional[EventDataCategory]" = None, + item: "Optional[Item]" = None, *, - quantity=1, # type: int - ): - # type: (...) -> None + quantity: int = 1, + ) -> None: """This increments a counter for event loss by reason and data category by the given positive-int quantity (default 1). @@ -154,13 +148,13 @@ def record_lost_event( """ return None - def is_healthy(self): - # type: (Self) -> bool + def is_healthy(self: "Self") -> bool: return True -def _parse_rate_limits(header, now=None): - # type: (str, Optional[datetime]) -> Iterable[Tuple[Optional[EventDataCategory], datetime]] +def _parse_rate_limits( + header: str, now: "Optional[datetime]" = None +) -> "Iterable[Tuple[Optional[EventDataCategory], datetime]]": if now is None: now = datetime.now(timezone.utc) @@ -181,19 +175,20 @@ class BaseHttpTransport(Transport): TIMEOUT = 30 # seconds - def __init__(self, options): - # type: (Self, Dict[str, Any]) -> None + def __init__(self: "Self", options: "Dict[str, Any]") -> None: from sentry_sdk.consts import VERSION Transport.__init__(self, options) assert self.parsed_dsn is not None - self.options = options # type: Dict[str, Any] + self.options: "Dict[str, Any]" = options self._worker = BackgroundWorker(queue_size=options["transport_queue_size"]) self._auth = self.parsed_dsn.to_auth("sentry.python/%s" % VERSION) - self._disabled_until = {} # type: Dict[Optional[EventDataCategory], datetime] + self._disabled_until: "Dict[Optional[EventDataCategory], datetime]" = {} # We only use this Retry() class for the `get_retry_after` method it exposes self._retry = urllib3.util.Retry() - self._discarded_events = defaultdict(int) # type: DefaultDict[Tuple[EventDataCategory, str], int] + self._discarded_events: "DefaultDict[Tuple[EventDataCategory, str], int]" = ( + defaultdict(int) + ) self._last_client_report_sent = time.time() self._pool = self._make_pool() @@ -242,13 +237,12 @@ def __init__(self, options): def record_lost_event( self, - reason, # type: str - data_category=None, # type: Optional[EventDataCategory] - item=None, # type: Optional[Item] + reason: str, + data_category: "Optional[EventDataCategory]" = None, + item: "Optional[Item]" = None, *, - quantity=1, # type: int - ): - # type: (...) -> None + quantity: int = 1, + ) -> None: if not self.options["send_client_reports"]: return @@ -281,13 +275,14 @@ def record_lost_event( self._discarded_events[data_category, reason] += quantity - def _get_header_value(self, response, header): - # type: (Self, Any, str) -> Optional[str] + def _get_header_value( + self: "Self", response: "Any", header: str + ) -> "Optional[str]": return response.headers.get(header) - def _update_rate_limits(self, response): - # type: (Self, Union[urllib3.BaseHTTPResponse, httpcore.Response]) -> None - + def _update_rate_limits( + self: "Self", response: "Union[urllib3.BaseHTTPResponse, httpcore.Response]" + ) -> None: # new sentries with more rate limit insights. We honor this header # no matter of the status code to update our internal rate limits. header = self._get_header_value(response, "x-sentry-rate-limits") @@ -311,16 +306,13 @@ def _update_rate_limits(self, response): ) def _send_request( - self, - body, - headers, - endpoint_type=EndpointType.ENVELOPE, - envelope=None, - ): - # type: (Self, bytes, Dict[str, str], EndpointType, Optional[Envelope]) -> None - - def record_loss(reason): - # type: (str) -> None + self: "Self", + body: bytes, + headers: "Dict[str, str]", + endpoint_type: "EndpointType" = EndpointType.ENVELOPE, + envelope: "Optional[Envelope]" = None, + ) -> None: + def record_loss(reason: str) -> None: if envelope is None: self.record_lost_event(reason, data_category="error") else: @@ -367,12 +359,12 @@ def record_loss(reason): finally: response.close() - def on_dropped_event(self, _reason): - # type: (Self, str) -> None + def on_dropped_event(self: "Self", _reason: str) -> None: return None - def _fetch_pending_client_report(self, force=False, interval=60): - # type: (Self, bool, int) -> Optional[Item] + def _fetch_pending_client_report( + self: "Self", force: bool = False, interval: int = 60 + ) -> "Optional[Item]": if not self.options["send_client_reports"]: return None @@ -402,38 +394,30 @@ def _fetch_pending_client_report(self, force=False, interval=60): type="client_report", ) - def _flush_client_reports(self, force=False): - # type: (Self, bool) -> None + def _flush_client_reports(self: "Self", force: bool = False) -> None: client_report = self._fetch_pending_client_report(force=force, interval=60) if client_report is not None: self.capture_envelope(Envelope(items=[client_report])) - def _check_disabled(self, category): - # type: (str) -> bool - def _disabled(bucket): - # type: (Any) -> bool + def _check_disabled(self, category: str) -> bool: + def _disabled(bucket: Any) -> bool: ts = self._disabled_until.get(bucket) return ts is not None and ts > datetime.now(timezone.utc) return _disabled(category) or _disabled(None) - def _is_rate_limited(self): - # type: (Self) -> bool + def _is_rate_limited(self: "Self") -> bool: return any( ts > datetime.now(timezone.utc) for ts in self._disabled_until.values() ) - def _is_worker_full(self): - # type: (Self) -> bool + def _is_worker_full(self: "Self") -> bool: return self._worker.full() - def is_healthy(self): - # type: (Self) -> bool + def is_healthy(self: "Self") -> bool: return not (self._is_worker_full() or self._is_rate_limited()) - def _send_envelope(self, envelope): - # type: (Self, Envelope) -> None - + def _send_envelope(self: "Self", envelope: "Envelope") -> None: # remove all items from the envelope which are over quota new_items = [] for item in envelope.items: @@ -484,8 +468,9 @@ def _send_envelope(self, envelope): ) return None - def _serialize_envelope(self, envelope): - # type: (Self, Envelope) -> tuple[Optional[str], io.BytesIO] + def _serialize_envelope( + self: "Self", envelope: "Envelope" + ) -> "tuple[Optional[str], io.BytesIO]": content_encoding = None body = io.BytesIO() if self._compression_level == 0 or self._compression_algo is None: @@ -506,12 +491,10 @@ def _serialize_envelope(self, envelope): return content_encoding, body - def _get_pool_options(self): - # type: (Self) -> Dict[str, Any] + def _get_pool_options(self: "Self") -> "Dict[str, Any]": raise NotImplementedError() - def _in_no_proxy(self, parsed_dsn): - # type: (Self, Dsn) -> bool + def _in_no_proxy(self: "Self", parsed_dsn: "Dsn") -> bool: no_proxy = getproxies().get("no") if not no_proxy: return False @@ -521,27 +504,25 @@ def _in_no_proxy(self, parsed_dsn): return True return False - def _make_pool(self): - # type: (Self) -> Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool] + def _make_pool( + self: "Self", + ) -> "Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]": raise NotImplementedError() def _request( - self, - method, - endpoint_type, - body, - headers, - ): - # type: (Self, str, EndpointType, Any, Mapping[str, str]) -> Union[urllib3.BaseHTTPResponse, httpcore.Response] + self: "Self", + method: str, + endpoint_type: "EndpointType", + body: "Any", + headers: "Mapping[str, str]", + ) -> "Union[urllib3.BaseHTTPResponse, httpcore.Response]": raise NotImplementedError() def capture_envelope( self, - envelope, # type: Envelope - ): - # type: (...) -> None - def send_envelope_wrapper(): - # type: () -> None + envelope: "Envelope", + ) -> None: + def send_envelope_wrapper() -> None: with capture_internal_exceptions(): self._send_envelope(envelope) self._flush_client_reports() @@ -552,25 +533,22 @@ def send_envelope_wrapper(): self.record_lost_event("queue_overflow", item=item) def flush( - self, - timeout, - callback=None, - ): - # type: (Self, float, Optional[Callable[[int, float], None]]) -> None + self: "Self", + timeout: float, + callback: "Optional[Callable[[int, float], None]]" = None, + ) -> None: logger.debug("Flushing HTTP transport") if timeout > 0: self._worker.submit(lambda: self._flush_client_reports(force=True)) self._worker.flush(timeout, callback) - def kill(self): - # type: (Self) -> None + def kill(self: "Self") -> None: logger.debug("Killing HTTP transport") self._worker.kill() @staticmethod - def _warn_hub_cls(): - # type: () -> None + def _warn_hub_cls() -> 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.", @@ -579,15 +557,13 @@ def _warn_hub_cls(): ) @property - def hub_cls(self): - # type: (Self) -> type[sentry_sdk.Hub] + def hub_cls(self: "Self") -> "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: (Self, type[sentry_sdk.Hub]) -> None + def hub_cls(self: "Self", value: "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 @@ -597,9 +573,7 @@ class HttpTransport(BaseHttpTransport): if TYPE_CHECKING: _pool: Union[PoolManager, ProxyManager] - def _get_pool_options(self): - # type: (Self) -> Dict[str, Any] - + def _get_pool_options(self: "Self") -> "Dict[str, Any]": num_pools = self.options.get("_experiments", {}).get("transport_num_pools") options = { "num_pools": 2 if num_pools is None else int(num_pools), @@ -607,7 +581,7 @@ def _get_pool_options(self): "timeout": urllib3.Timeout(total=self.TIMEOUT), } - socket_options = None # type: Optional[List[Tuple[int, int, int | bytes]]] + socket_options: "Optional[List[Tuple[int, int, int | bytes]]]" = None if self.options["socket_options"] is not None: socket_options = self.options["socket_options"] @@ -640,8 +614,7 @@ def _get_pool_options(self): return options - def _make_pool(self): - # type: (Self) -> Union[PoolManager, ProxyManager] + def _make_pool(self: "Self") -> "Union[PoolManager, ProxyManager]": if self.parsed_dsn is None: raise ValueError("Cannot create HTTP-based transport without valid DSN") @@ -687,13 +660,12 @@ def _make_pool(self): return urllib3.PoolManager(**opts) def _request( - self, - method, - endpoint_type, - body, - headers, - ): - # type: (Self, str, EndpointType, Any, Mapping[str, str]) -> urllib3.BaseHTTPResponse + self: "Self", + method: str, + endpoint_type: "EndpointType", + body: "Any", + headers: "Mapping[str, str]", + ) -> "urllib3.BaseHTTPResponse": return self._pool.request( method, self._auth.get_api_url(endpoint_type), @@ -708,8 +680,7 @@ def _request( except ImportError: # Sorry, no Http2Transport for you class Http2Transport(HttpTransport): - def __init__(self, options): - # type: (Self, Dict[str, Any]) -> None + def __init__(self: "Self", options: "Dict[str, Any]") -> None: super().__init__(options) logger.warning( "You tried to use HTTP2Transport but don't have httpcore[http2] installed. Falling back to HTTPTransport." @@ -727,8 +698,9 @@ class Http2Transport(BaseHttpTransport): # type: ignore httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool ] - def _get_header_value(self, response, header): - # type: (Self, httpcore.Response, str) -> Optional[str] + def _get_header_value( + self: "Self", response: "httpcore.Response", header: str + ) -> "Optional[str]": return next( ( val.decode("ascii") @@ -739,13 +711,12 @@ def _get_header_value(self, response, header): ) def _request( - self, - method, - endpoint_type, - body, - headers, - ): - # type: (Self, str, EndpointType, Any, Mapping[str, str]) -> httpcore.Response + self: "Self", + method: str, + endpoint_type: "EndpointType", + body: "Any", + headers: "Mapping[str, str]", + ) -> "httpcore.Response": response = self._pool.request( method, self._auth.get_api_url(endpoint_type), @@ -762,13 +733,12 @@ def _request( ) return response - def _get_pool_options(self): - # type: (Self) -> Dict[str, Any] - options = { + def _get_pool_options(self: "Self") -> "Dict[str, Any]": + options: Dict[str, Any] = { "http2": self.parsed_dsn is not None and self.parsed_dsn.scheme == "https", "retries": 3, - } # type: Dict[str, Any] + } socket_options = ( self.options["socket_options"] @@ -799,8 +769,9 @@ def _get_pool_options(self): return options - def _make_pool(self): - # type: (Self) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool] + def _make_pool( + self: "Self", + ) -> "Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]": if self.parsed_dsn is None: raise ValueError("Cannot create HTTP-based transport without valid DSN") proxy = None @@ -851,17 +822,15 @@ class _FunctionTransport(Transport): def __init__( self, - func, # type: Callable[[Event], None] - ): - # type: (...) -> None + func: "Callable[[Event], None]", + ) -> None: Transport.__init__(self) self._func = func def capture_event( self, - event, # type: Event - ): - # type: (...) -> None + event: "Event", + ) -> None: self._func(event) return None @@ -874,14 +843,15 @@ def capture_envelope(self, envelope: Envelope) -> None: self.capture_event(event) -def make_transport(options): - # type: (Dict[str, Any]) -> Optional[Transport] +def make_transport(options: "Dict[str, Any]") -> "Optional[Transport]": ref_transport = options["transport"] use_http2_transport = options.get("_experiments", {}).get("transport_http2", False) # By default, we use the http transport class - transport_cls = Http2Transport if use_http2_transport else HttpTransport # type: Type[Transport] + transport_cls: "Type[Transport]" = ( + Http2Transport if use_http2_transport else HttpTransport + ) if isinstance(ref_transport, Transport): return ref_transport diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index aeb622ec8a..678ef4c385 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -24,42 +24,42 @@ # Python 3.10 and below BaseExceptionGroup = None # type: ignore +from typing import TYPE_CHECKING + import sentry_sdk from sentry_sdk._compat import PY37 +from sentry_sdk._types import SENSITIVE_DATA_SUBSTITUTE, Annotated, AnnotatedValue from sentry_sdk.consts import ( DEFAULT_ADD_FULL_STACK, DEFAULT_MAX_STACK_FRAMES, DEFAULT_MAX_VALUE_LENGTH, EndpointType, ) -from sentry_sdk._types import Annotated, AnnotatedValue, SENSITIVE_DATA_SUBSTITUTE - -from typing import TYPE_CHECKING if TYPE_CHECKING: from types import FrameType, TracebackType from typing import ( Any, Callable, - cast, ContextManager, Dict, Iterator, List, NoReturn, Optional, - overload, ParamSpec, Set, Tuple, Type, TypeVar, Union, + cast, + overload, ) from gevent.hub import Hub - from sentry_sdk._types import Event, ExcInfo, Log, Hint, Metric + from sentry_sdk._types import Event, ExcInfo, Hint, Log, Metric P = ParamSpec("P") R = TypeVar("R") @@ -87,8 +87,7 @@ """ -def env_to_bool(value, *, strict=False): - # type: (Any, Optional[bool]) -> bool | None +def env_to_bool(value: "Any", *, strict: "Optional[bool]" = False) -> "bool | None": """Casts an ENV variable value to boolean using the constants defined above. In strict mode, it may return None if the value doesn't match any of the predefined values. """ @@ -103,14 +102,12 @@ def env_to_bool(value, *, strict=False): return None if strict else bool(value) -def json_dumps(data): - # type: (Any) -> bytes +def json_dumps(data: "Any") -> bytes: """Serialize data into a compact JSON representation encoded as UTF-8.""" return json.dumps(data, allow_nan=False, separators=(",", ":")).encode("utf-8") -def get_git_revision(): - # type: () -> Optional[str] +def get_git_revision() -> "Optional[str]": try: with open(os.path.devnull, "w+") as null: # prevent command prompt windows from popping up on windows @@ -137,8 +134,7 @@ def get_git_revision(): return revision -def get_default_release(): - # type: () -> Optional[str] +def get_default_release() -> "Optional[str]": """Try to guess a default release.""" release = os.environ.get("SENTRY_RELEASE") if release: @@ -161,8 +157,7 @@ def get_default_release(): return None -def get_sdk_name(installed_integrations): - # type: (List[str]) -> str +def get_sdk_name(installed_integrations: "List[str]") -> str: """Return the SDK name including the name of the used web framework.""" # Note: I can not use for example sentry_sdk.integrations.django.DjangoIntegration.identifier @@ -200,12 +195,15 @@ def get_sdk_name(installed_integrations): class CaptureInternalException: __slots__ = () - def __enter__(self): - # type: () -> ContextManager[Any] + def __enter__(self) -> "ContextManager[Any]": return self - def __exit__(self, ty, value, tb): - # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> bool + def __exit__( + self, + ty: "Optional[Type[BaseException]]", + value: "Optional[BaseException]", + tb: "Optional[TracebackType]", + ) -> bool: if ty is not None and value is not None: capture_internal_exception((ty, value, tb)) @@ -215,13 +213,11 @@ def __exit__(self, ty, value, tb): _CAPTURE_INTERNAL_EXCEPTION = CaptureInternalException() -def capture_internal_exceptions(): - # type: () -> ContextManager[Any] +def capture_internal_exceptions() -> "ContextManager[Any]": return _CAPTURE_INTERNAL_EXCEPTION -def capture_internal_exception(exc_info): - # type: (ExcInfo) -> None +def capture_internal_exception(exc_info: "ExcInfo") -> None: """ Capture an exception that is likely caused by a bug in the SDK itself. @@ -232,13 +228,11 @@ def capture_internal_exception(exc_info): logger.error("Internal error in sentry_sdk", exc_info=exc_info) -def to_timestamp(value): - # type: (datetime) -> float +def to_timestamp(value: "datetime") -> float: return (value - epoch).total_seconds() -def format_timestamp(value): - # type: (datetime) -> str +def format_timestamp(value: "datetime") -> str: """Formats a timestamp in RFC 3339 format. Any datetime objects with a non-UTC timezone are converted to UTC, so that all timestamps are formatted in UTC. @@ -253,8 +247,7 @@ def format_timestamp(value): ISO_TZ_SEPARATORS = frozenset(("+", "-")) -def datetime_from_isoformat(value): - # type: (str) -> datetime +def datetime_from_isoformat(value: str) -> "datetime": try: result = datetime.fromisoformat(value) except (AttributeError, ValueError): @@ -275,8 +268,9 @@ def datetime_from_isoformat(value): return result.astimezone(timezone.utc) -def event_hint_with_exc_info(exc_info=None): - # type: (Optional[ExcInfo]) -> Dict[str, Optional[ExcInfo]] +def event_hint_with_exc_info( + exc_info: "Optional[ExcInfo]" = None, +) -> "Dict[str, Optional[ExcInfo]]": """Creates a hint with the exc info filled in.""" if exc_info is None: exc_info = sys.exc_info() @@ -296,8 +290,9 @@ class Dsn: ORG_ID_REGEX = re.compile(r"^o(\d+)\.") - def __init__(self, value, org_id=None): - # type: (Union[Dsn, str], Optional[str]) -> None + def __init__( + self, value: "Union[Dsn, str]", org_id: "Optional[str]" = None + ) -> None: if isinstance(value, Dsn): self.__dict__ = dict(value.__dict__) return @@ -313,13 +308,13 @@ def __init__(self, value, org_id=None): self.host = parts.hostname if org_id is not None: - self.org_id = org_id # type: Optional[str] + self.org_id: "Optional[str]" = org_id else: org_id_match = Dsn.ORG_ID_REGEX.match(self.host) self.org_id = org_id_match.group(1) if org_id_match else None if parts.port is None: - self.port = self.scheme == "https" and 443 or 80 # type: int + self.port: int = self.scheme == "https" and 443 or 80 else: self.port = parts.port @@ -339,16 +334,14 @@ def __init__(self, value, org_id=None): self.path = "/".join(path) + "/" @property - def netloc(self): - # type: () -> str + def netloc(self) -> str: """The netloc part of a DSN.""" rv = self.host if (self.scheme, self.port) not in (("http", 80), ("https", 443)): rv = "%s:%s" % (rv, self.port) return rv - def to_auth(self, client=None): - # type: (Optional[Any]) -> Auth + def to_auth(self, client: "Optional[Any]" = None) -> "Auth": """Returns the auth info object for this dsn.""" return Auth( scheme=self.scheme, @@ -360,8 +353,7 @@ def to_auth(self, client=None): client=client, ) - def __str__(self): - # type: () -> str + def __str__(self) -> str: return "%s://%s%s@%s%s%s" % ( self.scheme, self.public_key, @@ -377,16 +369,15 @@ class Auth: def __init__( self, - scheme, - host, - project_id, - public_key, - secret_key=None, - version=7, - client=None, - path="/", - ): - # type: (str, str, str, str, Optional[str], int, Optional[Any], str) -> None + scheme: str, + host: str, + project_id: str, + public_key: str, + secret_key: "Optional[str]" = None, + version: int = 7, + client: "Optional[Any]" = None, + path: str = "/", + ) -> None: self.scheme = scheme self.host = host self.path = path @@ -398,9 +389,8 @@ def __init__( def get_api_url( self, - type=EndpointType.ENVELOPE, # type: EndpointType - ): - # type: (...) -> str + type: "EndpointType" = EndpointType.ENVELOPE, + ) -> str: """Returns the API url for storing events.""" return "%s://%s%sapi/%s/%s/" % ( self.scheme, @@ -410,8 +400,7 @@ def get_api_url( type.value, ) - def to_header(self): - # type: () -> str + def to_header(self) -> str: """Returns the auth header a string.""" rv = [("sentry_key", self.public_key), ("sentry_version", self.version)] if self.client is not None: @@ -421,21 +410,18 @@ def to_header(self): return "Sentry " + ", ".join("%s=%s" % (key, value) for key, value in rv) -def get_type_name(cls): - # type: (Optional[type]) -> Optional[str] +def get_type_name(cls: "Optional[type]") -> "Optional[str]": return getattr(cls, "__qualname__", None) or getattr(cls, "__name__", None) -def get_type_module(cls): - # type: (Optional[type]) -> Optional[str] +def get_type_module(cls: "Optional[type]") -> "Optional[str]": mod = getattr(cls, "__module__", None) if mod not in (None, "builtins", "__builtins__"): return mod return None -def should_hide_frame(frame): - # type: (FrameType) -> bool +def should_hide_frame(frame: "FrameType") -> bool: try: mod = frame.f_globals["__name__"] if mod.startswith("sentry_sdk."): @@ -453,9 +439,8 @@ def should_hide_frame(frame): return False -def iter_stacks(tb): - # type: (Optional[TracebackType]) -> Iterator[TracebackType] - tb_ = tb # type: Optional[TracebackType] +def iter_stacks(tb: "Optional[TracebackType]") -> "Iterator[TracebackType]": + tb_: Optional[TracebackType] = tb while tb_ is not None: if not should_hide_frame(tb_.tb_frame): yield tb_ @@ -463,18 +448,17 @@ def iter_stacks(tb): def get_lines_from_file( - filename, # type: str - lineno, # type: int - max_length=None, # type: Optional[int] - loader=None, # type: Optional[Any] - module=None, # type: Optional[str] -): - # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]] + filename: str, + lineno: int, + max_length: "Optional[int]" = None, + loader: "Optional[Any]" = None, + module: "Optional[str]" = None, +) -> "Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]]": context_lines = 5 source = None if loader is not None and hasattr(loader, "get_source"): try: - source_str = loader.get_source(module) # type: Optional[str] + source_str: "Optional[str]" = loader.get_source(module) except (ImportError, IOError): source_str = None if source_str is not None: @@ -509,13 +493,12 @@ def get_lines_from_file( def get_source_context( - frame, # type: FrameType - tb_lineno, # type: Optional[int] - max_value_length=None, # type: Optional[int] -): - # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]] + frame: "FrameType", + tb_lineno: "Optional[int]", + max_value_length: "Optional[int]" = None, +) -> "Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]]": try: - abs_path = frame.f_code.co_filename # type: Optional[str] + abs_path: "Optional[str]" = frame.f_code.co_filename except Exception: abs_path = None try: @@ -536,24 +519,23 @@ def get_source_context( return [], None, [] -def safe_str(value): - # type: (Any) -> str +def safe_str(value: "Any") -> str: try: return str(value) except Exception: return safe_repr(value) -def safe_repr(value): - # type: (Any) -> str +def safe_repr(value: "Any") -> str: try: return repr(value) except Exception: return "" -def filename_for_module(module, abs_path): - # type: (Optional[str], Optional[str]) -> Optional[str] +def filename_for_module( + module: "Optional[str]", abs_path: "Optional[str]" +) -> "Optional[str]": if not abs_path or not module: return abs_path @@ -577,14 +559,13 @@ def filename_for_module(module, abs_path): def serialize_frame( - frame, - tb_lineno=None, - include_local_variables=True, - include_source_context=True, - max_value_length=None, - custom_repr=None, -): - # type: (FrameType, Optional[int], bool, bool, Optional[int], Optional[Callable[..., Optional[str]]]) -> Dict[str, Any] + frame: "FrameType", + tb_lineno: "Optional[int]" = None, + include_local_variables: bool = True, + include_source_context: bool = True, + max_value_length: "Optional[int]" = None, + custom_repr: "Optional[Callable[..., Optional[str]]]" = None, +) -> "Dict[str, Any]": f_code = getattr(frame, "f_code", None) if not f_code: abs_path = None @@ -605,13 +586,13 @@ def serialize_frame( except Exception: os_abs_path = None - rv = { + rv: "Dict[str, Any]" = { "filename": filename_for_module(module, abs_path) or None, "abs_path": os_abs_path, "function": function or "", "module": module, "lineno": tb_lineno, - } # type: Dict[str, Any] + } if include_source_context: rv["pre_context"], rv["context_line"], rv["post_context"] = get_source_context( @@ -629,15 +610,14 @@ def serialize_frame( def current_stacktrace( - include_local_variables=True, # type: bool - include_source_context=True, # type: bool - max_value_length=None, # type: Optional[int] -): - # type: (...) -> Dict[str, Any] + include_local_variables: bool = True, + include_source_context: bool = True, + max_value_length: "Optional[int]" = None, +) -> "Dict[str, Any]": __tracebackhide__ = True frames = [] - f = sys._getframe() # type: Optional[FrameType] + f: "Optional[FrameType]" = sys._getframe() while f is not None: if not should_hide_frame(f): frames.append( @@ -655,24 +635,22 @@ def current_stacktrace( return {"frames": frames} -def get_errno(exc_value): - # type: (BaseException) -> Optional[Any] +def get_errno(exc_value: BaseException) -> "Optional[Any]": return getattr(exc_value, "errno", None) -def get_error_message(exc_value): - # type: (Optional[BaseException]) -> str - message = safe_str( +def get_error_message(exc_value: "Optional[BaseException]") -> str: + message: str = safe_str( getattr(exc_value, "message", "") or getattr(exc_value, "detail", "") or safe_str(exc_value) - ) # type: str + ) # __notes__ should be a list of strings when notes are added # via add_note, but can be anything else if __notes__ is set # directly. We only support strings in __notes__, since that # is the correct use. - notes = getattr(exc_value, "__notes__", None) # type: object + notes: object = getattr(exc_value, "__notes__", None) if isinstance(notes, list) and len(notes) > 0: message += "\n" + "\n".join(note for note in notes if isinstance(note, str)) @@ -680,24 +658,23 @@ def get_error_message(exc_value): def single_exception_from_error_tuple( - exc_type, # type: Optional[type] - exc_value, # type: Optional[BaseException] - tb, # type: Optional[TracebackType] - client_options=None, # type: Optional[Dict[str, Any]] - mechanism=None, # type: Optional[Dict[str, Any]] - exception_id=None, # type: Optional[int] - parent_id=None, # type: Optional[int] - source=None, # type: Optional[str] - full_stack=None, # type: Optional[list[dict[str, Any]]] -): - # type: (...) -> Dict[str, Any] + exc_type: "Optional[type]", + exc_value: "Optional[BaseException]", + tb: "Optional[TracebackType]", + client_options: "Optional[Dict[str, Any]]" = None, + mechanism: "Optional[Dict[str, Any]]" = None, + exception_id: "Optional[int]" = None, + parent_id: "Optional[int]" = None, + source: "Optional[str]" = None, + full_stack: "Optional[list[dict[str, Any]]]" = None, +) -> "Dict[str, Any]": """ Creates a dict that goes into the events `exception.values` list and is ingestible by Sentry. See the Exception Interface documentation for more details: https://develop.sentry.dev/sdk/event-payloads/exception/ """ - exception_value = {} # type: Dict[str, Any] + exception_value: "Dict[str, Any]" = {} exception_value["mechanism"] = ( mechanism.copy() if mechanism else {"type": "generic", "handled": True} ) @@ -746,7 +723,7 @@ def single_exception_from_error_tuple( max_value_length = client_options["max_value_length"] custom_repr = client_options.get("custom_repr") - frames = [ + frames: "List[Dict[str, Any]]" = [ serialize_frame( tb.tb_frame, tb_lineno=tb.tb_lineno, @@ -758,7 +735,7 @@ def single_exception_from_error_tuple( # Process at most MAX_STACK_FRAMES + 1 frames, to avoid hanging on # processing a super-long stacktrace. for tb, _ in zip(iter_stacks(tb), range(MAX_STACK_FRAMES + 1)) - ] # type: List[Dict[str, Any]] + ] if len(frames) > MAX_STACK_FRAMES: # If we have more frames than the limit, we remove the stacktrace completely. @@ -786,12 +763,11 @@ def single_exception_from_error_tuple( if HAS_CHAINED_EXCEPTIONS: - def walk_exception_chain(exc_info): - # type: (ExcInfo) -> Iterator[ExcInfo] + def walk_exception_chain(exc_info: "ExcInfo") -> "Iterator[ExcInfo]": exc_type, exc_value, tb = exc_info seen_exceptions = [] - seen_exception_ids = set() # type: Set[int] + seen_exception_ids: "Set[int]" = set() while ( exc_type is not None @@ -818,23 +794,21 @@ def walk_exception_chain(exc_info): else: - def walk_exception_chain(exc_info): - # type: (ExcInfo) -> Iterator[ExcInfo] + def walk_exception_chain(exc_info: "ExcInfo") -> "Iterator[ExcInfo]": yield exc_info def exceptions_from_error( - exc_type, # type: Optional[type] - exc_value, # type: Optional[BaseException] - tb, # type: Optional[TracebackType] - client_options=None, # type: Optional[Dict[str, Any]] - mechanism=None, # type: Optional[Dict[str, Any]] - exception_id=0, # type: int - parent_id=0, # type: int - source=None, # type: Optional[str] - full_stack=None, # type: Optional[list[dict[str, Any]]] -): - # type: (...) -> Tuple[int, List[Dict[str, Any]]] + exc_type: "Optional[type]", + exc_value: "Optional[BaseException]", + tb: "Optional[TracebackType]", + client_options: "Optional[Dict[str, Any]]" = None, + mechanism: "Optional[Dict[str, Any]]" = None, + exception_id: int = 0, + parent_id: int = 0, + source: "Optional[str]" = None, + full_stack: "Optional[list[dict[str, Any]]]" = None, +) -> "Tuple[int, List[Dict[str, Any]]]": """ Creates the list of exceptions. This can include chained exceptions and exceptions from an ExceptionGroup. @@ -927,12 +901,11 @@ def exceptions_from_error( def exceptions_from_error_tuple( - exc_info, # type: ExcInfo - client_options=None, # type: Optional[Dict[str, Any]] - mechanism=None, # type: Optional[Dict[str, Any]] - full_stack=None, # type: Optional[list[dict[str, Any]]] -): - # type: (...) -> List[Dict[str, Any]] + exc_info: "ExcInfo", + client_options: "Optional[Dict[str, Any]]" = None, + mechanism: "Optional[Dict[str, Any]]" = None, + full_stack: "Optional[list[dict[str, Any]]]" = None, +) -> "List[Dict[str, Any]]": exc_type, exc_value, tb = exc_info is_exception_group = BaseExceptionGroup is not None and isinstance( @@ -970,16 +943,14 @@ def exceptions_from_error_tuple( return exceptions -def to_string(value): - # type: (str) -> str +def to_string(value: str) -> str: try: return str(value) except UnicodeDecodeError: return repr(value)[1:-1] -def iter_event_stacktraces(event): - # type: (Event) -> Iterator[Annotated[Dict[str, Any]]] +def iter_event_stacktraces(event: "Event") -> "Iterator[Annotated[Dict[str, Any]]]": if "stacktrace" in event: yield event["stacktrace"] if "threads" in event: @@ -992,8 +963,7 @@ def iter_event_stacktraces(event): yield exception["stacktrace"] -def iter_event_frames(event): - # type: (Event) -> Iterator[Dict[str, Any]] +def iter_event_frames(event: "Event") -> "Iterator[Dict[str, Any]]": for stacktrace in iter_event_stacktraces(event): if isinstance(stacktrace, AnnotatedValue): stacktrace = stacktrace.value or {} @@ -1002,8 +972,12 @@ def iter_event_frames(event): yield frame -def handle_in_app(event, in_app_exclude=None, in_app_include=None, project_root=None): - # type: (Event, Optional[List[str]], Optional[List[str]], Optional[str]) -> Event +def handle_in_app( + event: "Event", + in_app_exclude: "Optional[List[str]]" = None, + in_app_include: "Optional[List[str]]" = None, + project_root: "Optional[str]" = None, +) -> "Event": for stacktrace in iter_event_stacktraces(event): if isinstance(stacktrace, AnnotatedValue): stacktrace = stacktrace.value or {} @@ -1018,8 +992,12 @@ def handle_in_app(event, in_app_exclude=None, in_app_include=None, project_root= return event -def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=None): - # type: (Any, Optional[List[str]], Optional[List[str]], Optional[str]) -> Optional[Any] +def set_in_app_in_frames( + frames: "Any", + in_app_exclude: "Optional[List[str]]", + in_app_include: "Optional[List[str]]", + project_root: "Optional[str]" = None, +) -> "Optional[Any]": if not frames: return None @@ -1057,8 +1035,7 @@ def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=No return frames -def exc_info_from_error(error): - # type: (Union[BaseException, ExcInfo]) -> ExcInfo +def exc_info_from_error(error: "Union[BaseException, ExcInfo]") -> "ExcInfo": if isinstance(error, tuple) and len(error) == 3: exc_type, exc_value, tb = error elif isinstance(error, BaseException): @@ -1086,8 +1063,11 @@ def exc_info_from_error(error): return exc_info -def merge_stack_frames(frames, full_stack, client_options): - # type: (List[Dict[str, Any]], List[Dict[str, Any]], Optional[Dict[str, Any]]) -> List[Dict[str, Any]] +def merge_stack_frames( + frames: "List[Dict[str, Any]]", + full_stack: "List[Dict[str, Any]]", + client_options: "Optional[Dict[str, Any]]", +) -> "List[Dict[str, Any]]": """ Add the missing frames from full_stack to frames and return the merged list. """ @@ -1127,11 +1107,10 @@ def merge_stack_frames(frames, full_stack, client_options): def event_from_exception( - exc_info, # type: Union[BaseException, ExcInfo] - client_options=None, # type: Optional[Dict[str, Any]] - mechanism=None, # type: Optional[Dict[str, Any]] -): - # type: (...) -> Tuple[Event, Dict[str, Any]] + exc_info: "Union[BaseException, ExcInfo]", + client_options: "Optional[Dict[str, Any]]" = None, + mechanism: "Optional[Dict[str, Any]]" = None, +) -> "Tuple[Event, Dict[str, Any]]": exc_info = exc_info_from_error(exc_info) hint = event_hint_with_exc_info(exc_info) @@ -1156,8 +1135,7 @@ def event_from_exception( ) -def _module_in_list(name, items): - # type: (Optional[str], Optional[List[str]]) -> bool +def _module_in_list(name: "Optional[str]", items: "Optional[List[str]]") -> bool: if name is None: return False @@ -1171,8 +1149,7 @@ def _module_in_list(name, items): return False -def _is_external_source(abs_path): - # type: (Optional[str]) -> bool +def _is_external_source(abs_path: "Optional[str]") -> bool: # check if frame is in 'site-packages' or 'dist-packages' if abs_path is None: return False @@ -1183,8 +1160,9 @@ def _is_external_source(abs_path): return external_source -def _is_in_project_root(abs_path, project_root): - # type: (Optional[str], Optional[str]) -> bool +def _is_in_project_root( + abs_path: "Optional[str]", project_root: "Optional[str]" +) -> bool: if abs_path is None or project_root is None: return False @@ -1195,8 +1173,7 @@ def _is_in_project_root(abs_path, project_root): return False -def _truncate_by_bytes(string, max_bytes): - # type: (str, int) -> str +def _truncate_by_bytes(string: str, max_bytes: int) -> str: """ Truncate a UTF-8-encodable string to the last full codepoint so that it fits in max_bytes. """ @@ -1205,16 +1182,16 @@ def _truncate_by_bytes(string, max_bytes): return truncated + "..." -def _get_size_in_bytes(value): - # type: (str) -> Optional[int] +def _get_size_in_bytes(value: str) -> "Optional[int]": try: return len(value.encode("utf-8")) except (UnicodeEncodeError, UnicodeDecodeError): return None -def strip_string(value, max_length=None): - # type: (str, Optional[int]) -> Union[AnnotatedValue, str] +def strip_string( + value: str, max_length: "Optional[int]" = None +) -> "Union[AnnotatedValue, str]": if not value: return value @@ -1242,8 +1219,7 @@ def strip_string(value, max_length=None): ) -def parse_version(version): - # type: (str) -> Optional[Tuple[int, ...]] +def parse_version(version: str) -> "Optional[Tuple[int, ...]]": """ Parses a version string into a tuple of integers. This uses the parsing loging from PEP 440: @@ -1287,15 +1263,14 @@ def parse_version(version): try: release = pattern.match(version).groupdict()["release"] # type: ignore - release_tuple = tuple(map(int, release.split(".")[:3])) # type: Tuple[int, ...] + release_tuple: "Tuple[int, ...]" = tuple(map(int, release.split(".")[:3])) except (TypeError, ValueError, AttributeError): return None return release_tuple -def _is_contextvars_broken(): - # type: () -> bool +def _is_contextvars_broken() -> bool: """ Returns whether gevent/eventlet have patched the stdlib in a way where thread locals are now more "correct" than contextvars. """ @@ -1346,32 +1321,27 @@ def _is_contextvars_broken(): return False -def _make_threadlocal_contextvars(local): - # type: (type) -> type +def _make_threadlocal_contextvars(local: type) -> type: class ContextVar: # Super-limited impl of ContextVar - def __init__(self, name, default=None): - # type: (str, Any) -> None + def __init__(self, name: str, default: "Any" = None) -> None: self._name = name self._default = default self._local = local() self._original_local = local() - def get(self, default=None): - # type: (Any) -> Any + def get(self, default: "Any" = None) -> "Any": return getattr(self._local, "value", default or self._default) - def set(self, value): - # type: (Any) -> Any + def set(self, value: "Any") -> "Any": token = str(random.getrandbits(64)) original_value = self.get() setattr(self._original_local, token, original_value) self._local.value = value return token - def reset(self, token): - # type: (Any) -> None + def reset(self, token: "Any") -> None: self._local.value = getattr(self._original_local, token) # delete the original value (this way it works in Python 3.6+) del self._original_local.__dict__[token] @@ -1379,8 +1349,7 @@ def reset(self, token): return ContextVar -def _get_contextvars(): - # type: () -> Tuple[bool, type] +def _get_contextvars() -> "Tuple[bool, type]": """ Figure out the "right" contextvars installation to use. Returns a `contextvars.ContextVar`-like class with a limited API. @@ -1429,10 +1398,9 @@ def _get_contextvars(): """ -def qualname_from_function(func): - # type: (Callable[..., Any]) -> Optional[str] +def qualname_from_function(func: "Callable[..., Any]") -> "Optional[str]": """Return the qualified name of func. Works with regular function, lambda, partial and partialmethod.""" - func_qualname = None # type: Optional[str] + func_qualname: "Optional[str]" = None # Python 2 try: @@ -1473,8 +1441,7 @@ def qualname_from_function(func): return func_qualname -def transaction_from_function(func): - # type: (Callable[..., Any]) -> Optional[str] +def transaction_from_function(func: "Callable[..., Any]") -> "Optional[str]": return qualname_from_function(func) @@ -1493,9 +1460,12 @@ class TimeoutThread(threading.Thread): """ def __init__( - self, waiting_time, configured_timeout, isolation_scope=None, current_scope=None - ): - # type: (float, int, Optional[sentry_sdk.Scope], Optional[sentry_sdk.Scope]) -> None + self, + waiting_time: float, + configured_timeout: int, + isolation_scope: "Optional[sentry_sdk.Scope]" = None, + current_scope: "Optional[sentry_sdk.Scope]" = None, + ) -> None: threading.Thread.__init__(self) self.waiting_time = waiting_time self.configured_timeout = configured_timeout @@ -1505,12 +1475,10 @@ def __init__( self._stop_event = threading.Event() - def stop(self): - # type: () -> None + def stop(self) -> None: self._stop_event.set() - def _capture_exception(self): - # type: () -> ExcInfo + def _capture_exception(self) -> "ExcInfo": exc_info = sys.exc_info() client = sentry_sdk.get_client() @@ -1523,9 +1491,7 @@ def _capture_exception(self): return exc_info - def run(self): - # type: () -> None - + def run(self) -> None: self._stop_event.wait(self.waiting_time) if self._stop_event.is_set(): @@ -1557,8 +1523,7 @@ def run(self): ) -def to_base64(original): - # type: (str) -> Optional[str] +def to_base64(original: str) -> "Optional[str]": """ Convert a string to base64, via UTF-8. Returns None on invalid input. """ @@ -1574,8 +1539,7 @@ def to_base64(original): return base64_string -def from_base64(base64_string): - # type: (str) -> Optional[str] +def from_base64(base64_string: str) -> "Optional[str]": """ Convert a string from base64, via UTF-8. Returns None on invalid input. """ @@ -1599,8 +1563,12 @@ def from_base64(base64_string): Components = namedtuple("Components", ["scheme", "netloc", "path", "query", "fragment"]) -def sanitize_url(url, remove_authority=True, remove_query_values=True, split=False): - # type: (str, bool, bool, bool) -> Union[str, Components] +def sanitize_url( + url: str, + remove_authority: bool = True, + remove_query_values: bool = True, + split: bool = False, +) -> "Union[str, Components]": """ Removes the authority and query parameter values from a given URL. """ @@ -1646,8 +1614,7 @@ def sanitize_url(url, remove_authority=True, remove_query_values=True, split=Fal ParsedUrl = namedtuple("ParsedUrl", ["url", "query", "fragment"]) -def parse_url(url, sanitize=True): - # type: (str, bool) -> ParsedUrl +def parse_url(url: str, sanitize: bool = True) -> "ParsedUrl": """ Splits a URL into a url (including path), query and fragment. If sanitize is True, the query parameters will be sanitized to remove sensitive data. The autority (username and password) @@ -1674,8 +1641,7 @@ def parse_url(url, sanitize=True): ) -def is_valid_sample_rate(rate, source): - # type: (Any, str) -> bool +def is_valid_sample_rate(rate: "Any", source: str) -> bool: """ Checks the given sample rate to make sure it is valid type and value (a boolean or a number between 0 and 1, inclusive). @@ -1705,8 +1671,11 @@ def is_valid_sample_rate(rate, source): return True -def match_regex_list(item, regex_list=None, substring_matching=False): - # type: (str, Optional[List[str]], bool) -> bool +def match_regex_list( + item: str, + regex_list: "Optional[List[str]]" = None, + substring_matching: bool = False, +) -> bool: if regex_list is None: return False @@ -1721,8 +1690,7 @@ def match_regex_list(item, regex_list=None, substring_matching=False): return False -def is_sentry_url(client, url): - # type: (sentry_sdk.client.BaseClient, str) -> bool +def is_sentry_url(client: "sentry_sdk.client.BaseClient", url: str) -> bool: """ Determines whether the given URL matches the Sentry DSN. """ @@ -1734,8 +1702,7 @@ def is_sentry_url(client, url): ) -def _generate_installed_modules(): - # type: () -> Iterator[Tuple[str, str]] +def _generate_installed_modules() -> "Iterator[Tuple[str, str]]": try: from importlib import metadata @@ -1763,27 +1730,23 @@ def _generate_installed_modules(): yield _normalize_module_name(info.key), info.version -def _normalize_module_name(name): - # type: (str) -> str +def _normalize_module_name(name: str) -> str: return name.lower() -def _replace_hyphens_dots_and_underscores_with_dashes(name): - # type: (str) -> str +def _replace_hyphens_dots_and_underscores_with_dashes(name: str) -> str: # https://peps.python.org/pep-0503/#normalized-names return re.sub(r"[-_.]+", "-", name) -def _get_installed_modules(): - # type: () -> Dict[str, str] +def _get_installed_modules() -> "Dict[str, str]": global _installed_modules if _installed_modules is None: _installed_modules = dict(_generate_installed_modules()) return _installed_modules -def package_version(package): - # type: (str) -> Optional[Tuple[int, ...]] +def package_version(package: str) -> "Optional[Tuple[int, ...]]": normalized_package = _normalize_module_name( _replace_hyphens_dots_and_underscores_with_dashes(package) ) @@ -1799,16 +1762,18 @@ def package_version(package): return parse_version(version) -def reraise(tp, value, tb=None): - # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[Any]) -> NoReturn +def reraise( + tp: "Optional[Type[BaseException]]", + value: "Optional[BaseException]", + tb: "Optional[Any]" = None, +) -> "NoReturn": assert value is not None if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value -def _no_op(*_a, **_k): - # type: (*Any, **Any) -> None +def _no_op(*_a: "Any", **_k: "Any") -> None: """No-op function for ensure_integration_enabled.""" pass @@ -1817,25 +1782,20 @@ def _no_op(*_a, **_k): @overload def ensure_integration_enabled( - integration, # type: type[sentry_sdk.integrations.Integration] - original_function, # type: Callable[P, R] - ): - # type: (...) -> Callable[[Callable[P, R]], Callable[P, R]] - ... + integration: "type[sentry_sdk.integrations.Integration]", + original_function: "Callable[P, R]", + ) -> "Callable[[Callable[P, R]], Callable[P, R]]": ... @overload def ensure_integration_enabled( - integration, # type: type[sentry_sdk.integrations.Integration] - ): - # type: (...) -> Callable[[Callable[P, None]], Callable[P, None]] - ... + integration: "type[sentry_sdk.integrations.Integration]", + ) -> "Callable[[Callable[P, None]], Callable[P, None]]": ... def ensure_integration_enabled( - integration, # type: type[sentry_sdk.integrations.Integration] - original_function=_no_op, # type: Union[Callable[P, R], Callable[P, None]] -): - # type: (...) -> Callable[[Callable[P, R]], Callable[P, R]] + integration: "type[sentry_sdk.integrations.Integration]", + original_function: "Union[Callable[P, R], Callable[P, None]]" = _no_op, +) -> "Callable[[Callable[P, R]], Callable[P, R]]": """ Ensures a given integration is enabled prior to calling a Sentry-patched function. @@ -1862,10 +1822,8 @@ def patch_my_function(): # ensure the default _no_op function is only used when R is None. original_function = cast(Callable[P, R], original_function) - def patcher(sentry_patched_function): - # type: (Callable[P, R]) -> Callable[P, R] - def runner(*args: "P.args", **kwargs: "P.kwargs"): - # type: (...) -> R + def patcher(sentry_patched_function: "Callable[P, R]") -> "Callable[P, R]": + def runner(*args: "P.args", **kwargs: "P.kwargs") -> R: if sentry_sdk.get_client().get_integration(integration) is None: return original_function(*args, **kwargs) @@ -1881,19 +1839,16 @@ def runner(*args: "P.args", **kwargs: "P.kwargs"): if PY37: - def nanosecond_time(): - # type: () -> int + def nanosecond_time() -> int: return time.perf_counter_ns() else: - def nanosecond_time(): - # type: () -> int + def nanosecond_time() -> int: return int(time.perf_counter() * 1e9) -def now(): - # type: () -> float +def now() -> float: return time.perf_counter() @@ -1903,23 +1858,21 @@ def now(): except ImportError: # it's not great that the signatures are different, get_hub can't return None # consider adding an if TYPE_CHECKING to change the signature to Optional[Hub] - def get_gevent_hub(): # type: ignore[misc] - # type: () -> Optional[Hub] + def get_gevent_hub() -> "Optional[Hub]": # type: ignore[misc] return None - def is_module_patched(mod_name): - # type: (str) -> bool + def is_module_patched(mod_name: str) -> bool: # unable to import from gevent means no modules have been patched return False -def is_gevent(): - # type: () -> bool +def is_gevent() -> bool: return is_module_patched("threading") or is_module_patched("_thread") -def get_current_thread_meta(thread=None): - # type: (Optional[threading.Thread]) -> Tuple[Optional[int], Optional[str]] +def get_current_thread_meta( + thread: "Optional[threading.Thread]" = None, +) -> "Tuple[Optional[int], Optional[str]]": """ Try to get the id of the current thread, with various fall backs. """ @@ -1969,8 +1922,7 @@ def get_current_thread_meta(thread=None): return None, None -def should_be_treated_as_error(ty, value): - # type: (Any, Any) -> bool +def should_be_treated_as_error(ty: "Any", value: "Any") -> bool: if ty == SystemExit and hasattr(value, "code") and value.code in (0, None): # https://docs.python.org/3/library/exceptions.html#SystemExit return False @@ -1982,8 +1934,7 @@ def should_be_treated_as_error(ty, value): T = TypeVar("T") -def try_convert(convert_func, value): - # type: (Callable[[Any], T], Any) -> Optional[T] +def try_convert(convert_func: "Callable[[Any], T]", value: "Any") -> "Optional[T]": """ Attempt to convert from an unknown type to a specific type, using the given function. Return None if the conversion fails, i.e. if the function @@ -2001,12 +1952,12 @@ def try_convert(convert_func, value): return None -def safe_serialize(data): - # type: (Any) -> str +def safe_serialize(data: "Any") -> str: """Safely serialize to a readable string.""" - def serialize_item(item): - # type: (Any) -> Union[str, dict[Any, Any], list[Any], tuple[Any, ...]] + def serialize_item( + item: "Any", + ) -> "Union[str, dict[Any, Any], list[Any], tuple[Any, ...]]": if callable(item): try: module = getattr(item, "__module__", None) @@ -2047,8 +1998,7 @@ def serialize_item(item): return str(data) -def has_logs_enabled(options): - # type: (Optional[dict[str, Any]]) -> bool +def has_logs_enabled(options: "Optional[dict[str, Any]]") -> bool: if options is None: return False @@ -2058,8 +2008,9 @@ def has_logs_enabled(options): ) -def get_before_send_log(options): - # type: (Optional[dict[str, Any]]) -> Optional[Callable[[Log, Hint], Optional[Log]]] +def get_before_send_log( + options: "Optional[dict[str, Any]]", +) -> "Optional[Callable[[Log, Hint], Optional[Log]]]": if options is None: return None @@ -2068,16 +2019,16 @@ def get_before_send_log(options): ) -def has_metrics_enabled(options): - # type: (Optional[dict[str, Any]]) -> bool +def has_metrics_enabled(options: "Optional[dict[str, Any]]") -> bool: if options is None: return False return bool(options.get("enable_metrics", True)) -def get_before_send_metric(options): - # type: (Optional[dict[str, Any]]) -> Optional[Callable[[Metric, Hint], Optional[Metric]]] +def get_before_send_metric( + options: "Optional[dict[str, Any]]", +) -> "Optional[Callable[[Metric, Hint], Optional[Metric]]]": if options is None: return None diff --git a/sentry_sdk/worker.py b/sentry_sdk/worker.py index b04ea582bc..6fc8f20cad 100644 --- a/sentry_sdk/worker.py +++ b/sentry_sdk/worker.py @@ -18,29 +18,25 @@ class BackgroundWorker: - def __init__(self, queue_size=DEFAULT_QUEUE_SIZE): - # type: (int) -> None - self._queue = Queue(queue_size) # type: Queue + def __init__(self, queue_size: int = DEFAULT_QUEUE_SIZE) -> None: + self._queue: Queue = Queue(queue_size) self._lock = threading.Lock() - self._thread = None # type: Optional[threading.Thread] - self._thread_for_pid = None # type: Optional[int] + self._thread: "Optional[threading.Thread]" = None + self._thread_for_pid: "Optional[int]" = None @property - def is_alive(self): - # type: () -> bool + def is_alive(self) -> bool: if self._thread_for_pid != os.getpid(): return False if not self._thread: return False return self._thread.is_alive() - def _ensure_thread(self): - # type: () -> None + def _ensure_thread(self) -> None: if not self.is_alive: self.start() - def _timed_queue_join(self, timeout): - # type: (float) -> bool + def _timed_queue_join(self, timeout: float) -> bool: deadline = time() + timeout queue = self._queue @@ -57,8 +53,7 @@ def _timed_queue_join(self, timeout): finally: queue.all_tasks_done.release() - def start(self): - # type: () -> None + def start(self) -> None: with self._lock: if not self.is_alive: self._thread = threading.Thread( @@ -74,8 +69,7 @@ def start(self): # send out events. self._thread = None - def kill(self): - # type: () -> None + def kill(self) -> None: """ Kill worker thread. Returns immediately. Not useful for waiting on shutdown for events, use `flush` for that. @@ -91,20 +85,17 @@ def kill(self): self._thread = None self._thread_for_pid = None - def flush(self, timeout, callback=None): - # type: (float, Optional[Any]) -> None + def flush(self, timeout: float, callback: "Optional[Any]" = None) -> None: logger.debug("background worker got flush request") with self._lock: if self.is_alive and timeout > 0.0: self._wait_flush(timeout, callback) logger.debug("background worker flushed") - def full(self): - # type: () -> bool + def full(self) -> bool: return self._queue.full() - def _wait_flush(self, timeout, callback): - # type: (float, Optional[Any]) -> None + def _wait_flush(self, timeout: float, callback: "Optional[Any]") -> None: initial_timeout = min(0.1, timeout) if not self._timed_queue_join(initial_timeout): pending = self._queue.qsize() + 1 @@ -116,8 +107,7 @@ def _wait_flush(self, timeout, callback): pending = self._queue.qsize() + 1 logger.error("flush timed out, dropped %s events", pending) - def submit(self, callback): - # type: (Callable[[], None]) -> bool + def submit(self, callback: "Callable[[], None]") -> bool: self._ensure_thread() try: self._queue.put_nowait(callback) @@ -125,8 +115,7 @@ def submit(self, callback): except FullError: return False - def _target(self): - # type: () -> None + def _target(self) -> None: while True: callback = self._queue.get() try: