prefer msgpack over json

This commit is contained in:
Will McGugan
2022-05-13 15:56:58 +01:00
parent 96ce4202a5
commit 2dfda6e4dc
5 changed files with 77 additions and 28 deletions

46
poetry.lock generated
View File

@@ -381,6 +381,14 @@ mkdocs-autorefs = ">=0.1"
pymdown-extensions = ">=6.3" pymdown-extensions = ">=6.3"
pytkdocs = ">=0.14.0" pytkdocs = ">=0.14.0"
[[package]]
name = "msgpack"
version = "1.0.3"
description = "MessagePack (de)serializer."
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "multidict" name = "multidict"
version = "6.0.2" version = "6.0.2"
@@ -765,7 +773,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "d801e69bdd847115e92104a8cdd51ba1f207a1b7c25c4f6c9fb88434594be975" content-hash = "05e80f2e4709cbc33327e1ddfaf7ae19a7f708fae251834d631317dc4cf4cd2f"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@@ -1121,6 +1129,42 @@ mkdocstrings = [
{file = "mkdocstrings-0.17.0-py3-none-any.whl", hash = "sha256:103fc1dd58cb23b7e0a6da5292435f01b29dc6fa0ba829132537f3f556f985de"}, {file = "mkdocstrings-0.17.0-py3-none-any.whl", hash = "sha256:103fc1dd58cb23b7e0a6da5292435f01b29dc6fa0ba829132537f3f556f985de"},
{file = "mkdocstrings-0.17.0.tar.gz", hash = "sha256:75b5cfa2039aeaf3a5f5cf0aa438507b0330ce76c8478da149d692daa7213a98"}, {file = "mkdocstrings-0.17.0.tar.gz", hash = "sha256:75b5cfa2039aeaf3a5f5cf0aa438507b0330ce76c8478da149d692daa7213a98"},
] ]
msgpack = [
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"},
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c3ca57c96c8e69c1a0d2926a6acf2d9a522b41dc4253a8945c4c6cd4981a4e3"},
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0a792c091bac433dfe0a70ac17fc2087d4595ab835b47b89defc8bbabcf5c73"},
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c58cdec1cb5fcea8c2f1771d7b5fec79307d056874f746690bd2bdd609ab147"},
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f97c0f35b3b096a330bb4a1a9247d0bd7e1f3a2eba7ab69795501504b1c2c39"},
{file = "msgpack-1.0.3-cp310-cp310-win32.whl", hash = "sha256:36a64a10b16c2ab31dcd5f32d9787ed41fe68ab23dd66957ca2826c7f10d0b85"},
{file = "msgpack-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c1ba333b4024c17c7591f0f372e2daa3c31db495a9b2af3cf664aef3c14354f7"},
{file = "msgpack-1.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c2140cf7a3ec475ef0938edb6eb363fa704159e0bf71dde15d953bacc1cf9d7d"},
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4c22717c74d44bcd7af353024ce71c6b55346dad5e2cc1ddc17ce8c4507c6b"},
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d733a15ade190540c703de209ffbc42a3367600421b62ac0c09fde594da6ec"},
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e03b06f2982aa98d4ddd082a210c3db200471da523f9ac197f2828e80e7770"},
{file = "msgpack-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:3d875631ecab42f65f9dce6f55ce6d736696ced240f2634633188de2f5f21af9"},
{file = "msgpack-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:40fb89b4625d12d6027a19f4df18a4de5c64f6f3314325049f219683e07e678a"},
{file = "msgpack-1.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eef0cf8db3857b2b556213d97dd82de76e28a6524853a9beb3264983391dc1a"},
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8c332f53ffff01953ad25131272506500b14750c1d0ce8614b17d098252fbc"},
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0903bd93cbd34653dd63bbfcb99d7539c372795201f39d16fdfde4418de43a"},
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1e6bfed4860d72106f4e0a1ab519546982b45689937b40257cfd820650b920"},
{file = "msgpack-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:d02cea2252abc3756b2ac31f781f7a98e89ff9759b2e7450a1c7a0d13302ff50"},
{file = "msgpack-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f30dd0dc4dfe6231ad253b6f9f7128ac3202ae49edd3f10d311adc358772dba"},
{file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f201d34dc89342fabb2a10ed7c9a9aaaed9b7af0f16a5923f1ae562b31258dea"},
{file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bb87f23ae7d14b7b3c21009c4b1705ec107cb21ee71975992f6aca571fb4a42a"},
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a3a5c4b16e9d0edb823fe54b59b5660cc8d4782d7bf2c214cb4b91a1940a8ef"},
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74da1e5fcf20ade12c6bf1baa17a2dc3604958922de8dc83cbe3eff22e8b611"},
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73a80bd6eb6bcb338c1ec0da273f87420829c266379c8c82fa14c23fb586cfa1"},
{file = "msgpack-1.0.3-cp38-cp38-win32.whl", hash = "sha256:9fce00156e79af37bb6db4e7587b30d11e7ac6a02cb5bac387f023808cd7d7f4"},
{file = "msgpack-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:9b6f2d714c506e79cbead331de9aae6837c8dd36190d02da74cb409b36162e8a"},
{file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:89908aea5f46ee1474cc37fbc146677f8529ac99201bc2faf4ef8edc023c2bf3"},
{file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:973ad69fd7e31159eae8f580f3f707b718b61141838321c6fa4d891c4a2cca52"},
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da24375ab4c50e5b7486c115a3198d207954fe10aaa5708f7b65105df09109b2"},
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a598d0685e4ae07a0672b59792d2cc767d09d7a7f39fd9bd37ff84e060b1a996"},
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c309a68cb5d6bbd0c50d5c71a25ae81f268c2dc675c6f4ea8ab2feec2ac4e2"},
{file = "msgpack-1.0.3-cp39-cp39-win32.whl", hash = "sha256:494471d65b25a8751d19c83f1a482fd411d7ca7a3b9e17d25980a74075ba0e88"},
{file = "msgpack-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:f01b26c2290cbd74316990ba84a14ac3d599af9cebefc543d241a66e785cf17d"},
{file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"},
]
multidict = [ multidict = [
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},

View File

@@ -28,6 +28,7 @@ rich = "^12.3.0"
click = "8.1.2" click = "8.1.2"
importlib-metadata = "^4.11.3" importlib-metadata = "^4.11.3"
typing-extensions = { version = "^4.0.0", python = "<3.8" } typing-extensions = { version = "^4.0.0", python = "<3.8" }
msgpack = "^1.0.3"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^6.2.3" pytest = "^6.2.3"

View File

@@ -1,11 +1,12 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import base64
import datetime import datetime
import inspect import inspect
import json import json
import msgpack
import pickle import pickle
from time import time
from asyncio import Queue, Task, QueueFull from asyncio import Queue, Task, QueueFull
from io import StringIO from io import StringIO
from typing import Type, Any, NamedTuple from typing import Type, Any, NamedTuple
@@ -97,7 +98,7 @@ class DevtoolsClient:
self.update_console_task: Task | None = None self.update_console_task: Task | None = None
self.console: DevtoolsConsole = DevtoolsConsole(file=StringIO()) self.console: DevtoolsConsole = DevtoolsConsole(file=StringIO())
self.websocket: ClientWebSocketResponse | None = None self.websocket: ClientWebSocketResponse | None = None
self.log_queue: Queue[str | Type[ClientShutdown]] | None = None self.log_queue: Queue[str | bytes | Type[ClientShutdown]] | None = None
self.spillover: int = 0 self.spillover: int = 0
async def connect(self) -> None: async def connect(self) -> None:
@@ -144,7 +145,10 @@ class DevtoolsClient:
if log is ClientShutdown: if log is ClientShutdown:
log_queue.task_done() log_queue.task_done()
break break
if isinstance(log, str):
await websocket.send_str(log) await websocket.send_str(log)
else:
await websocket.send_bytes(log)
log_queue.task_done() log_queue.task_done()
self.log_queue_task = asyncio.create_task(send_queued_logs()) self.log_queue_task = asyncio.create_task(send_queued_logs())
@@ -203,17 +207,18 @@ class DevtoolsClient:
segments = self.console.export_segments() segments = self.console.export_segments()
encoded_segments = self._encode_segments(segments) encoded_segments = self._encode_segments(segments)
message = json.dumps( message: bytes | None = msgpack.packb(
{ {
"type": "client_log", "type": "client_log",
"payload": { "payload": {
"timestamp": int(datetime.datetime.utcnow().timestamp()), "timestamp": int(time()),
"path": getattr(log.caller, "filename", ""), "path": getattr(log.caller, "filename", ""),
"line_number": getattr(log.caller, "lineno", 0), "line_number": getattr(log.caller, "lineno", 0),
"encoded_segments": encoded_segments, "segments": encoded_segments,
}, },
} }
) )
assert message is not None
try: try:
if self.log_queue: if self.log_queue:
self.log_queue.put_nowait(message) self.log_queue.put_nowait(message)
@@ -233,8 +238,8 @@ class DevtoolsClient:
except QueueFull: except QueueFull:
self.spillover += 1 self.spillover += 1
def _encode_segments(self, segments: list[Segment]) -> str: def _encode_segments(self, segments: list[Segment]) -> bytes:
"""Pickle and Base64 encode the list of Segments """Pickle a list of Segments
Args: Args:
segments (list[Segment]): A list of Segments to encode segments (list[Segment]): A list of Segments to encode
@@ -242,6 +247,5 @@ class DevtoolsClient:
Returns: Returns:
str: The Segment list pickled with pickle protocol v3, then base64 encoded str: The Segment list pickled with pickle protocol v3, then base64 encoded
""" """
pickled = pickle.dumps(segments, protocol=3) pickled = pickle.dumps(segments, protocol=pickle.HIGHEST_PROTOCOL)
encoded = base64.b64encode(pickled) return pickled
return str(encoded, encoding="utf-8")

View File

@@ -72,19 +72,13 @@ class DevConsoleLog:
def __rich_console__( def __rich_console__(
self, console: Console, options: ConsoleOptions self, console: Console, options: ConsoleOptions
) -> RenderResult: ) -> RenderResult:
local_time = ( local_time = datetime.fromtimestamp(self.unix_timestamp)
datetime.fromtimestamp(self.unix_timestamp)
.replace(tzinfo=timezone.utc)
.astimezone(tz=datetime.now().astimezone().tzinfo)
)
timezone_name = local_time.tzname()
table = Table.grid(expand=True) table = Table.grid(expand=True)
table.add_column()
table.add_column()
file_link = escape(f"file://{Path(self.path).absolute()}") file_link = escape(f"file://{Path(self.path).absolute()}")
file_and_line = escape(f"{Path(self.path).name}:{self.line_number}") file_and_line = escape(f"{Path(self.path).name}:{self.line_number}")
table.add_row( table.add_row(
f"[dim]{local_time.time()} {timezone_name}", f"[dim]{local_time.time()}",
Align.right( Align.right(
Text(f"{file_and_line}", style=Style(dim=True, link=file_link)) Text(f"{file_and_line}", style=Style(dim=True, link=file_link))
), ),

View File

@@ -14,6 +14,7 @@ from aiohttp.abc import Request
from aiohttp.web_ws import WebSocketResponse from aiohttp.web_ws import WebSocketResponse
from rich.console import Console from rich.console import Console
from rich.markup import escape from rich.markup import escape
import msgpack
from textual.devtools.renderables import ( from textual.devtools.renderables import (
DevConsoleLog, DevConsoleLog,
@@ -170,9 +171,9 @@ class ClientHandler:
path = message_json["payload"]["path"] path = message_json["payload"]["path"]
line_number = message_json["payload"]["line_number"] line_number = message_json["payload"]["line_number"]
timestamp = message_json["payload"]["timestamp"] timestamp = message_json["payload"]["timestamp"]
encoded_segments = message_json["payload"]["encoded_segments"] encoded_segments = message_json["payload"]["segments"]
decoded_segments = base64.b64decode(encoded_segments) # decoded_segments = base64.b64decode(encoded_segments)
segments = pickle.loads(decoded_segments) segments = pickle.loads(encoded_segments)
message_time = time() message_time = time()
if ( if (
last_message_time is not None last_message_time is not None
@@ -219,21 +220,26 @@ class ClientHandler:
await self.service.send_server_info(client_handler=self) await self.service.send_server_info(client_handler=self)
async for message in self.websocket: async for message in self.websocket:
message = cast(WSMessage, message) message = cast(WSMessage, message)
if message.type == WSMsgType.TEXT:
if message.type in (WSMsgType.TEXT, WSMsgType.BINARY):
try: try:
message_json = json.loads(message.data) if isinstance(message.data, bytes):
message = msgpack.unpackb(message.data)
else:
message = json.loads(message.data)
except JSONDecodeError: except JSONDecodeError:
self.service.console.print(escape(str(message.data))) self.service.console.print(escape(str(message.data)))
continue continue
type = message_json.get("type") type = message.get("type")
if not type: if not type:
continue continue
if ( if (
type in QUEUEABLE_TYPES type in QUEUEABLE_TYPES
and not self.service.shutdown_event.is_set() and not self.service.shutdown_event.is_set()
): ):
await self.incoming_queue.put(message_json) await self.incoming_queue.put(message)
elif message.type == WSMsgType.ERROR: elif message.type == WSMsgType.ERROR:
self.service.console.print( self.service.console.print(
DevConsoleNotice("Websocket error occurred", level="error") DevConsoleNotice("Websocket error occurred", level="error")