mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-23 21:56:02 +00:00
simplex-chat-python: split Client from Bot, add request/response API (#6976)
* simplex-chat-python: split Client from Bot, add request/response API
Client is now the base class for SimpleX participants that talk TO
services (monitors, probes, automated participants). Bot extends Client
with server features (address, auto-accept, welcome, commands).
New methods on Client (inherited by Bot):
connect_to(link) idempotent contact handshake
send_and_wait(id, text) send a message and await the reply
events() async iterator over chat events
@on_message(contact_id=N) filter by sender in decorators
BotProfile renamed to Profile (alias kept). New ContactAlreadyExistsError
subclass for cleaner error handling.
* simplex-chat-python: narrow event payload type per @on_event tag
@client.on_event("contactConnected") now types the handler's event
parameter as CEvt.ContactConnected instead of the unnarrowed
CEvt.ChatEvent union — mirroring how @on_message narrows by
content_type.
The 50 overloads are generated by the Haskell codegen into _events.py
(as a Protocol class), so new events stay in sync automatically.
Client.on_event is exposed as a property typed as that Protocol; the
runtime implementation is unchanged.
This commit is contained in:
@@ -83,12 +83,48 @@ responsesCodeText =
|
||||
eventsCodeText :: Text
|
||||
eventsCodeText =
|
||||
("# API Events\n# " <> autoGenerated <> "\n")
|
||||
<> pythonImports
|
||||
<> "from __future__ import annotations\n"
|
||||
<> "from collections.abc import Awaitable, Callable\n"
|
||||
<> "from typing import Literal, NotRequired, Protocol, TypedDict, overload\n"
|
||||
<> "from . import _types as T\n"
|
||||
<> unionTypeCodePy moduleMember "T." "ChatEvent" chatEventConstrs
|
||||
<> onEventProtocolCode chatEventConstrs
|
||||
where
|
||||
chatEventConstrs = L.fromList $ concatMap catEvents chatEventsDocs
|
||||
catEvents CECategory {mainEvents, otherEvents} = map eventType $ mainEvents ++ otherEvents
|
||||
|
||||
-- | Render the `OnEventDecorator` Protocol — one `__call__` overload per
|
||||
-- event tag, narrowing the handler's event parameter from the unnarrowed
|
||||
-- `ChatEvent` union to the specific tagged TypedDict. Plus a fallback
|
||||
-- overload for `event: str` that keeps the unnarrowed shape so non-literal
|
||||
-- tags don't trigger a type error.
|
||||
--
|
||||
-- `Client.on_event` is typed as a `OnEventDecorator` (via a property) so
|
||||
-- callers get per-tag narrowing without per-tag handwritten overloads
|
||||
-- in client.py.
|
||||
onEventProtocolCode :: L.NonEmpty ATUnionMember -> Text
|
||||
onEventProtocolCode members =
|
||||
"\n\nclass OnEventDecorator(Protocol):\n"
|
||||
<> " \"\"\"Per-tag narrowing protocol for ``Client.on_event``.\n"
|
||||
<> "\n"
|
||||
<> " ``@client.on_event(\"contactConnected\")`` types the handler's\n"
|
||||
<> " ``evt`` parameter as :class:`ContactConnected` rather than the\n"
|
||||
<> " unnarrowed :data:`ChatEvent` union.\n"
|
||||
<> " \"\"\"\n"
|
||||
<> foldMap overloadCode (L.toList members)
|
||||
<> "\n @overload\n"
|
||||
<> " def __call__(self, event: str, /) -> Callable[\n"
|
||||
<> " [Callable[[\"ChatEvent\"], Awaitable[None]]],\n"
|
||||
<> " Callable[[\"ChatEvent\"], Awaitable[None]],\n"
|
||||
<> " ]: ...\n"
|
||||
where
|
||||
overloadCode (ATUnionMember tag _) =
|
||||
"\n @overload\n"
|
||||
<> " def __call__(self, event: Literal[\"" <> T.pack tag <> "\"], /) -> Callable[\n"
|
||||
<> " [Callable[[\"" <> pyConstrName tag <> "\"], Awaitable[None]]],\n"
|
||||
<> " Callable[[\"" <> pyConstrName tag <> "\"], Awaitable[None]],\n"
|
||||
<> " ]: ...\n"
|
||||
|
||||
typesCodeText :: Text
|
||||
typesCodeText =
|
||||
("# API Types\n# " <> autoGenerated <> "\n")
|
||||
|
||||
Reference in New Issue
Block a user