Skip to content

Commit e121751

Browse files
committed
update documentation
1 parent c7125c1 commit e121751

File tree

4 files changed

+201
-72
lines changed

4 files changed

+201
-72
lines changed

.clinerules/python-best-practices.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,11 @@ async def test_plugin_attach():
132132
## Documentation Requirements
133133

134134
### Docstrings
135-
- **Format:** Use Google-style docstrings
136-
- **Required:** All public classes, methods, and functions
137-
- **Parameters:** Document all parameters with types
135+
- **Format:** Use Google-style docstrings exclusively
136+
- **Required:** All public classes, methods, and functions must have docstrings
137+
- **Conciseness:** Keep documentation as concise as possible while being clear
138+
- **No Implementation Details:** Focus on what the function does, not how it does it
139+
- **Parameters:** Document all parameters with types and brief descriptions
138140
- **Returns:** Document return values and types
139141
- **Raises:** Document exceptions that may be raised
140142
- **Example:**
@@ -156,6 +158,15 @@ async def join_room(self, room_id: int, username: str, pin: Optional[str] = None
156158
"""
157159
```
158160

161+
### Documentation Style Guidelines
162+
- **Concise Descriptions:** Use clear, brief descriptions that focus on purpose and behavior
163+
- **Avoid Implementation Details:** Don't document internal algorithms, data structures, or implementation specifics
164+
- **User-Focused:** Write from the perspective of someone using the API, not implementing it
165+
- **Consistent Terminology:** Use consistent terms throughout the codebase
166+
- **Examples:** Include usage examples for complex functions when helpful
167+
- **Bad Example:** "This method iterates through the internal session dictionary and calls the transport's send method with a JSON-serialized message"
168+
- **Good Example:** "Send a message to the Janus server and return a transaction for tracking the response"
169+
159170
### Code Comments
160171
- **Complex Logic:** Comment complex algorithms or WebRTC-specific code
161172
- **TODOs:** Use `# TODO:` for future improvements

janus_client/plugin_base.py

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from abc import ABC, abstractmethod
3+
from typing import Optional
34

45
from aiortc import (
56
RTCPeerConnection,
@@ -14,43 +15,50 @@
1415

1516

1617
class JanusPlugin(ABC):
17-
"""Base class to inherit when implementing a plugin"""
18+
"""Base class for implementing Janus plugins."""
1819

1920
name: str = "janus.plugin.base.dummy"
20-
"""Plugin name
21+
"""Plugin name that must match the plugin name in Janus server."""
2122

22-
Must override to match plugin name in Janus server.
23-
"""
24-
25-
__id: str
26-
"""Plugin handle ID. Given by Janus."""
23+
__id: Optional[int]
24+
"""Plugin handle ID assigned by Janus."""
2725

2826
__session: JanusSession
29-
"""Session instance this plugin is created from."""
27+
"""Session instance this plugin is attached to."""
3028

3129
_pc: RTCPeerConnection
32-
"""A WebRTC PeerConnection. A plugin handle is expected to have
33-
only 1 PC.
34-
"""
30+
"""WebRTC PeerConnection for this plugin handle."""
3531

3632
def __init__(self) -> None:
3733
self.__id = None
3834
self._pc = RTCPeerConnection()
3935

4036
@property
41-
def id(self) -> int:
37+
def id(self) -> Optional[int]:
38+
"""Get the plugin handle ID.
39+
40+
Returns:
41+
The plugin handle ID assigned by Janus, or None if not attached.
42+
"""
4243
return self.__id
4344

44-
async def attach(self, session: JanusSession):
45+
async def attach(self, session: JanusSession) -> None:
46+
"""Attach this plugin to a Janus session.
47+
48+
Args:
49+
session: The JanusSession to attach this plugin to.
50+
51+
Raises:
52+
Exception: If plugin is already attached to a session.
53+
"""
4554
if self.__id:
4655
raise Exception(f"Plugin already attached to session ({self.__session})")
4756

4857
self.__session = session
4958
self.__id = await session.attach_plugin(self)
5059

51-
async def destroy(self):
52-
"""Destroy plugin handle"""
53-
60+
async def destroy(self) -> None:
61+
"""Destroy the plugin handle and clean up resources."""
5462
message_transaction = await self.send({"janus": "detach"})
5563
await message_transaction.get()
5664
await message_transaction.done()
@@ -67,31 +75,66 @@ async def send(
6775
self,
6876
message: dict,
6977
) -> MessageTransaction:
70-
"""Send raw message to plugin
78+
"""Send a message to this plugin handle.
79+
80+
Automatically attaches the plugin handle ID to the message.
7181
72-
Will auto attach plugin ID to the message.
82+
Args:
83+
message: JSON serializable dictionary to send to the plugin.
7384
74-
:param message: JSON serializable dictionary to send
75-
:return: Synchronous reply from server
85+
Returns:
86+
MessageTransaction for tracking the response.
87+
88+
Raises:
89+
Exception: If plugin is not attached to a session.
7690
"""
91+
if self.__id is None:
92+
raise Exception("Plugin not attached to session")
7793

7894
self.__sanitize_message(message=message)
7995

8096
return await self.__session.send(message, handle_id=self.__id)
8197

8298
@abstractmethod
83-
async def on_receive(self, response: dict):
84-
"""Handle asynchronous events from Janus"""
99+
async def on_receive(self, response: dict) -> None:
100+
"""Handle asynchronous events from Janus.
101+
102+
This method must be implemented by subclasses to handle
103+
plugin-specific messages and events.
104+
105+
Args:
106+
response: The response message from Janus.
107+
"""
85108
pass
86109

87110
async def create_jsep(self, pc: RTCPeerConnection, trickle: bool = False) -> dict:
111+
"""Create a JSEP object from a peer connection's local description.
112+
113+
Args:
114+
pc: The RTCPeerConnection to extract the description from.
115+
trickle: Whether to enable trickle ICE.
116+
117+
Returns:
118+
A JSEP dictionary containing SDP, type, and trickle settings.
119+
"""
88120
return {
89121
"sdp": pc.localDescription.sdp,
90122
"trickle": trickle,
91123
"type": pc.localDescription.type,
92124
}
93125

94-
async def on_receive_jsep(self, jsep: dict):
126+
async def on_receive_jsep(self, jsep: dict) -> None:
127+
"""Handle incoming JSEP (WebRTC signaling) messages.
128+
129+
Sets the remote description on the peer connection from the
130+
received JSEP offer or answer.
131+
132+
Args:
133+
jsep: The JSEP message containing SDP and type.
134+
135+
Raises:
136+
Exception: If the peer connection is in closed state.
137+
"""
95138
if self._pc:
96139
if self._pc.signalingState == "closed":
97140
raise Exception("Received JSEP when PeerConnection is closed")
@@ -100,13 +143,15 @@ async def on_receive_jsep(self, jsep: dict):
100143
RTCSessionDescription(sdp=jsep["sdp"], type=jsep["type"])
101144
)
102145

103-
async def trickle(self, sdpMLineIndex, candidate):
104-
"""Send WebRTC candidates to Janus
146+
async def trickle(
147+
self, sdpMLineIndex: int, candidate: Optional[str] = None
148+
) -> None:
149+
"""Send WebRTC ICE candidates to Janus using trickle ICE.
105150
106-
:param sdpMLineIndex: (I don't know what is this)
107-
:param candidate: Candidate payload. (I got it from WebRTC instance callback)
151+
Args:
152+
sdpMLineIndex: The SDP media line index for the candidate.
153+
candidate: The ICE candidate string, or None to signal end of candidates.
108154
"""
109-
110155
candidate_payload = dict()
111156
if candidate:
112157
candidate_payload = {
@@ -119,6 +164,5 @@ async def trickle(self, sdpMLineIndex, candidate):
119164
# TODO: test it
120165
candidate_payload = None
121166

122-
# await self.send({"janus": "trickle", "candidate": candidate_payload})
123167
await self.send({"janus": "trickle", "candidate": candidate_payload})
124168
# TODO: Implement sending an array of candidates

0 commit comments

Comments
 (0)