realtime 'working'
This commit is contained in:
parent
85c818539a
commit
7882598922
@ -1,6 +1,6 @@
|
|||||||
__title__ = "pocketbase"
|
__title__ = "pocketbase"
|
||||||
__description__ = "PocketBase client SDK for python."
|
__description__ = "PocketBase client SDK for python."
|
||||||
__version__ = "0.1.3"
|
__version__ = "0.2.0"
|
||||||
|
|
||||||
|
|
||||||
from .client import Client, ClientResponseError
|
from .client import Client, ClientResponseError
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import asyncio
|
import threading
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
@ -17,12 +17,8 @@ class Event:
|
|||||||
retry: int | None = None
|
retry: int | None = None
|
||||||
|
|
||||||
|
|
||||||
class SSEClient:
|
class EventLoop(threading.Thread):
|
||||||
"""Implementation of a server side event client"""
|
|
||||||
|
|
||||||
FIELD_SEPARATOR = ":"
|
FIELD_SEPARATOR = ":"
|
||||||
_listeners: dict = {}
|
|
||||||
_loop_running: bool = False
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -31,21 +27,30 @@ class SSEClient:
|
|||||||
headers: dict | None = None,
|
headers: dict | None = None,
|
||||||
payload: dict | None = None,
|
payload: dict | None = None,
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) -> None:
|
listeners: dict[str, Callable] | None = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
threading.Thread.__init__(self, **kwargs)
|
||||||
|
self.kill = False
|
||||||
|
self.client = httpx.Client()
|
||||||
self.url = url
|
self.url = url
|
||||||
self.method = method
|
self.method = method
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
self.payload = payload
|
self.payload = payload
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
self.client = httpx.AsyncClient()
|
self.listeners = listeners or {}
|
||||||
|
|
||||||
async def _read(self):
|
def _read(self):
|
||||||
"""Read the incoming event source stream and yield event chunks"""
|
"""Read the incoming event source stream and yield event chunks"""
|
||||||
data = b""
|
data = b""
|
||||||
async with self.client.stream(
|
with self.client.stream(
|
||||||
self.method, self.url, headers=self.headers, data=self.payload, timeout=None
|
self.method,
|
||||||
|
self.url,
|
||||||
|
headers=self.headers,
|
||||||
|
data=self.payload,
|
||||||
|
timeout=None,
|
||||||
) as r:
|
) as r:
|
||||||
async for chunk in r.aiter_bytes():
|
for chunk in r.iter_bytes():
|
||||||
for line in chunk.splitlines(True):
|
for line in chunk.splitlines(True):
|
||||||
data += line
|
data += line
|
||||||
if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")):
|
if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")):
|
||||||
@ -54,8 +59,8 @@ class SSEClient:
|
|||||||
if data:
|
if data:
|
||||||
yield data
|
yield data
|
||||||
|
|
||||||
async def _events(self):
|
def _events(self):
|
||||||
async for chunk in self._read():
|
for chunk in self._read():
|
||||||
event = Event()
|
event = Event()
|
||||||
for line in chunk.splitlines():
|
for line in chunk.splitlines():
|
||||||
line = line.decode(self.encoding)
|
line = line.decode(self.encoding)
|
||||||
@ -83,22 +88,52 @@ class SSEClient:
|
|||||||
event.event = event.event or "message"
|
event.event = event.event or "message"
|
||||||
yield event
|
yield event
|
||||||
|
|
||||||
async def _loop(self):
|
def run(self):
|
||||||
self._loop_running = True
|
for event in self._events():
|
||||||
async for event in self._events():
|
if self.kill:
|
||||||
if event.event in self._listeners:
|
break
|
||||||
self._listeners[event.event](event)
|
if event.event in self.listeners:
|
||||||
|
self.listeners[event.event](event)
|
||||||
|
|
||||||
|
|
||||||
|
class SSEClient:
|
||||||
|
"""Implementation of a server side event client"""
|
||||||
|
|
||||||
|
_listeners: dict = {}
|
||||||
|
_loop_thread: threading.Thread | None = None
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
url: str,
|
||||||
|
method: str = "GET",
|
||||||
|
headers: dict | None = None,
|
||||||
|
payload: dict | None = None,
|
||||||
|
encoding="utf-8",
|
||||||
|
) -> None:
|
||||||
|
self._listeners = {}
|
||||||
|
self._loop_thread = EventLoop(
|
||||||
|
url=url,
|
||||||
|
method=method,
|
||||||
|
headers=headers,
|
||||||
|
payload=payload,
|
||||||
|
encoding=encoding,
|
||||||
|
listeners=self._listeners,
|
||||||
|
name="loop",
|
||||||
|
)
|
||||||
|
self._loop_thread.daemon = True
|
||||||
|
self._loop_thread.start()
|
||||||
|
|
||||||
def add_event_listener(self, event: str, callback: Callable[[Event], None]) -> None:
|
def add_event_listener(self, event: str, callback: Callable[[Event], None]) -> None:
|
||||||
self._listeners[event] = callback
|
self._listeners[event] = callback
|
||||||
if not self._loop_running:
|
self._loop_thread.listeners = self._listeners
|
||||||
asyncio.run(self._loop())
|
|
||||||
|
|
||||||
def remove_event_listener(
|
def remove_event_listener(
|
||||||
self, event: str, callback: Callable[[Event], None]
|
self, event: str, callback: Callable[[Event], None]
|
||||||
) -> None:
|
) -> None:
|
||||||
if event in self._listeners:
|
if event in self._listeners:
|
||||||
self._listeners.pop(event)
|
self._listeners.pop(event)
|
||||||
|
self._loop_thread.listeners = self._listeners
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
pass
|
# TODO: does not work like this
|
||||||
|
self._loop_thread.kill = True
|
||||||
|
|||||||
@ -28,7 +28,7 @@ dynamic = ["readme", "version"]
|
|||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pocketbase"
|
name = "pocketbase"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
description = "PocketBase SDK for python."
|
description = "PocketBase SDK for python."
|
||||||
authors = ["Vithor Jaeger <vaphes@gmail.com>"]
|
authors = ["Vithor Jaeger <vaphes@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@ -2,4 +2,4 @@ from pocketbase import __version__
|
|||||||
|
|
||||||
|
|
||||||
def test_version():
|
def test_version():
|
||||||
assert __version__ == "0.1.3"
|
assert __version__ == "0.2.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user