Update the remaining docstrings, update pre-commit hook, add docstring formatting CI, update CONTRIBUTING with formatting guidance (#2089)
This commit is contained in:
@@ -4,6 +4,13 @@
|
||||
# SPDX-License-Identifier: BSD 2-Clause License
|
||||
#
|
||||
|
||||
"""Base object class providing event handling and lifecycle management.
|
||||
|
||||
This module provides the foundational BaseObject class that offers common
|
||||
functionality including unique identification, naming, event handling,
|
||||
and async cleanup for all Pipecat components.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import inspect
|
||||
from abc import ABC
|
||||
@@ -15,7 +22,20 @@ from pipecat.utils.utils import obj_count, obj_id
|
||||
|
||||
|
||||
class BaseObject(ABC):
|
||||
"""Abstract base class providing common functionality for Pipecat objects.
|
||||
|
||||
Provides unique identification, naming, event handling capabilities,
|
||||
and async lifecycle management for all Pipecat components. All major
|
||||
classes in the framework should inherit from this base class.
|
||||
"""
|
||||
|
||||
def __init__(self, *, name: Optional[str] = None):
|
||||
"""Initialize the base object.
|
||||
|
||||
Args:
|
||||
name: Optional custom name for the object. If not provided,
|
||||
generates a name using the class name and instance count.
|
||||
"""
|
||||
self._id: int = obj_id()
|
||||
self._name = name or f"{self.__class__.__name__}#{obj_count(self)}"
|
||||
|
||||
@@ -29,19 +49,44 @@ class BaseObject(ABC):
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
"""Get the unique identifier for this object.
|
||||
|
||||
Returns:
|
||||
The unique integer ID assigned to this object instance.
|
||||
"""
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get the name of this object.
|
||||
|
||||
Returns:
|
||||
The object's name, either custom-provided or auto-generated.
|
||||
"""
|
||||
return self._name
|
||||
|
||||
async def cleanup(self):
|
||||
"""Clean up resources and wait for running event handlers to complete.
|
||||
|
||||
This method should be called when the object is no longer needed.
|
||||
It waits for all currently executing event handler tasks to finish
|
||||
before returning.
|
||||
"""
|
||||
if self._event_tasks:
|
||||
event_names, tasks = zip(*self._event_tasks)
|
||||
logger.debug(f"{self} waiting on event handlers to finish {list(event_names)}...")
|
||||
await asyncio.wait(tasks)
|
||||
|
||||
def event_handler(self, event_name: str):
|
||||
"""Decorator for registering event handlers.
|
||||
|
||||
Args:
|
||||
event_name: The name of the event to handle.
|
||||
|
||||
Returns:
|
||||
The decorator function that registers the handler.
|
||||
"""
|
||||
|
||||
def decorator(handler):
|
||||
self.add_event_handler(event_name, handler)
|
||||
return handler
|
||||
@@ -49,18 +94,37 @@ class BaseObject(ABC):
|
||||
return decorator
|
||||
|
||||
def add_event_handler(self, event_name: str, handler):
|
||||
"""Add an event handler for the specified event.
|
||||
|
||||
Args:
|
||||
event_name: The name of the event to handle.
|
||||
handler: The function to call when the event occurs.
|
||||
Can be sync or async.
|
||||
"""
|
||||
if event_name in self._event_handlers:
|
||||
self._event_handlers[event_name].append(handler)
|
||||
else:
|
||||
logger.warning(f"Event handler {event_name} not registered")
|
||||
|
||||
def _register_event_handler(self, event_name: str):
|
||||
"""Register an event handler type.
|
||||
|
||||
Args:
|
||||
event_name: The name of the event type to register.
|
||||
"""
|
||||
if event_name not in self._event_handlers:
|
||||
self._event_handlers[event_name] = []
|
||||
else:
|
||||
logger.warning(f"Event handler {event_name} not registered")
|
||||
|
||||
async def _call_event_handler(self, event_name: str, *args, **kwargs):
|
||||
"""Call all registered handlers for the specified event.
|
||||
|
||||
Args:
|
||||
event_name: The name of the event to trigger.
|
||||
*args: Positional arguments to pass to event handlers.
|
||||
**kwargs: Keyword arguments to pass to event handlers.
|
||||
"""
|
||||
# If we haven't registered an event handler, we don't need to do
|
||||
# anything.
|
||||
if not self._event_handlers.get(event_name):
|
||||
@@ -76,6 +140,13 @@ class BaseObject(ABC):
|
||||
task.add_done_callback(self._event_task_finished)
|
||||
|
||||
async def _run_task(self, event_name: str, *args, **kwargs):
|
||||
"""Execute all handlers for an event.
|
||||
|
||||
Args:
|
||||
event_name: The name of the event being handled.
|
||||
*args: Positional arguments to pass to handlers.
|
||||
**kwargs: Keyword arguments to pass to handlers.
|
||||
"""
|
||||
try:
|
||||
for handler in self._event_handlers[event_name]:
|
||||
if inspect.iscoroutinefunction(handler):
|
||||
@@ -86,9 +157,19 @@ class BaseObject(ABC):
|
||||
logger.exception(f"Exception in event handler {event_name}: {e}")
|
||||
|
||||
def _event_task_finished(self, task: asyncio.Task):
|
||||
"""Clean up completed event handler tasks.
|
||||
|
||||
Args:
|
||||
task: The completed asyncio Task to remove from tracking.
|
||||
"""
|
||||
tuple_to_remove = next((t for t in self._event_tasks if t[1] == task), None)
|
||||
if tuple_to_remove:
|
||||
self._event_tasks.discard(tuple_to_remove)
|
||||
|
||||
def __str__(self):
|
||||
"""Return the string representation of this object.
|
||||
|
||||
Returns:
|
||||
The object's name as its string representation.
|
||||
"""
|
||||
return self.name
|
||||
|
||||
Reference in New Issue
Block a user