Files
2026-07-13 12:38:34 +08:00

92 lines
2.7 KiB
Python

"""
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