""" Base resource class for representing resources in the composio client. """ import contextvars import functools import os import time import traceback import typing as t from composio.__version__ import __version__ from composio.client import HttpClient from composio.utils.logging import WithLogger from ._telemetry import Event, create_event, push_event PayloadT = t.TypeVar("PayloadT", bound=dict) allow_tracking = contextvars.ContextVar[bool]("allow_tracking", default=True) _environment = os.getenv("ENVIRONMENT", "development") def trace_method(method: t.Callable, name: str) -> t.Callable: """Wrap a method to log the call.""" # Check if the method is a class method if getattr(method, "__self__", None) is not None: return method @functools.wraps(method) def trace_wrapper(self, *args: t.Any, **kwargs: t.Any) -> t.Any: if not allow_tracking.get(): return method(self, *args, **kwargs) event: t.Optional[Event] = None start_time = time.time() event = create_event( type="metric", functionName=name, timestamp=time.time(), props={}, source={ "environment": _environment, # type: ignore "language": "python", "service": "sdk", "version": __version__, }, metadata={ "provider": self._client.provider, }, ) try: return method(self, *args, **kwargs) except Exception as e: _, payload = event payload["error"] = { "name": e.__class__.__name__, "message": str(e), "stack": traceback.format_exc(), } event = ("error", payload) raise e finally: if event is not None: event[1]["durationMs"] = (time.time() - start_time) * 1000 push_event(event=event) trace_wrapper.__name__ = method.__name__ return trace_wrapper class ResourceMeta(type): """Meta class for resource classes.""" def __init__(cls, name, bases, attrs): for attr in attrs: if attr.startswith("_") or not callable(getattr(cls, attr)): continue setattr(cls, attr, trace_method(getattr(cls, attr), f"{name}.{attr}")) class Resource(WithLogger, metaclass=ResourceMeta): """Base resource class for composio client.""" def sanitize_payload(self, payload: PayloadT) -> PayloadT: return {k: v for k, v in payload.items()} # type: ignore def __init__(self, client: HttpClient): super().__init__() self._client = client