An asynchronous Python client for the Faye publish-subscribe messaging protocol.
- Asynchronous implementation using
asyncioandaiohttp/websockets - Support for both WebSocket and HTTP Long-Polling transports
- Automatic transport selection and fallback
- Extensible architecture with support for custom extensions
- Channel subscription management
- Message validation and channel validation
- Automatic reconnection with exponential backoff
- Comprehensive error handling and type hints
Install using pip:
pip install pyfayeOr with Poetry:
poetry add pyfayeimport asyncio
from faye import FayeClient
async def main():
# Create a client (defaults to WebSocket transport)
client = FayeClient("http://your-faye-server/faye")
# Connect to server
await client.connect()
# Subscribe to a channel
async def message_handler(message):
print(f"Received message: {message.data}")
await client.subscribe("/some/channel", message_handler)
# Publish a message
await client.publish("/some/channel", {"message": "Hello, World!"})
# Disconnect when done
await client.disconnect()
if __name__ == "__main__":
asyncio.run(main())from faye import Extension, Message
class SigningExtension(Extension):
def __init__(self, token: str):
self.token = token
async def process_outgoing(self, message: Message) -> Message | None:
"""Sign outgoing messages."""
if not message.ext:
message.ext = {}
message.ext["token"] = self.token
return message
# Create client with extension
client = FayeClient("http://your-faye-server/faye")
client.add_extension(SigningExtension("your-auth-token"))
await client.connect()from faye import Message
# Create multiple messages
messages = [
Message("/channel/1", data={"seq": 1}),
Message("/channel/2", data={"seq": 2})
]
# Send messages in batch
responses = await client.batch(messages)# Use HTTP Long-Polling transport
client = FayeClient("http://your-faye-server/faye", transport_type="long-polling")
# Use WebSocket transport (default)
client = FayeClient("http://your-faye-server/faye", transport_type="websocket")The main client class for interacting with a Faye server.
FayeClient(
url: str,
transport_type: str = "websocket",
extensions: list[Extension] | None = None
)url: Faye server URLtransport_type: Transport type to use ("websocket" or "long-polling")extensions: Optional list of extensions to use
client_id: The client ID assigned by the server (read-only)connected: Whether the client is currently connected (read-only)state: Current connection state as string (read-only)
async connect() -> None: Connect to the Faye serverasync disconnect() -> None: Disconnect from the serverasync subscribe(channel: str, callback: Callable[[Message], Awaitable[None]]) -> None: Subscribe to a channelasync unsubscribe(channel: str) -> None: Unsubscribe from a channelasync publish(channel: str, data: dict[str, Any] | str) -> None: Publish data to a channelasync batch(messages: list[Message]) -> list[Message | None]: Send multiple messages in batchadd_extension(extension: Extension) -> None: Add an extension to the clientremove_extension(extension: Extension) -> None: Remove an extension from the client
The message class representing Faye protocol messages.
Message(
channel: str,
client_id: str | None = None,
data: dict[str, Any] | str | None = None,
error: str | None = None,
ext: dict[str, Any] | None = None,
message_id: str | None = None,
version: str | None = None,
minimum_version: str | None = None,
supported_connection_types: list[str] | None = None,
connection_type: str | None = None,
subscription: str | None = None,
successful: bool | None = None,
advice: dict[str, Any] | None = None
)handshake(ext: dict[str, Any] | None = None) -> Messageconnect(client_id: str, connection_type: str = "websocket") -> Messagedisconnect(client_id: str) -> Messagesubscribe(client_id: str, subscription: str) -> Messageunsubscribe(client_id: str, subscription: str) -> Messagepublish(channel: str, data: dict[str, Any], client_id: str) -> Message
# Clone the repository
git clone https://github.com/mwhobrey/pyfaye.git
cd pyfaye
# Install dependencies
poetry install
# Run tests
poetry run pytest# Run all tests
poetry run pytest
# Run with coverage
poetry run pytest --cov=faye
# Run specific test file
poetry run pytest tests/transport/test_websocket.py# Run all checks
poetry run prerelease
# Format code
poetry run black src/
# Run linter
poetry run ruff check src/
# Run type checker
poetry run pytype src/This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request. When contributing:
- Fork the repository
- Create a feature branch
- Add tests for any new functionality
- Ensure all tests pass and code quality checks succeed
- Submit a pull request
For bug reports or feature requests, please open an issue on GitHub.