Proper handling of async responses to peers

This commit is contained in:
Kovid Goyal 2021-10-31 21:09:24 +05:30
parent 218582ced8
commit d53d92b890
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 23 additions and 13 deletions

View File

@ -53,7 +53,7 @@ from .session import Session, create_sessions, get_os_window_sizing_data
from .tabs import (
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
)
from .types import _T, SingleKey, WindowSystemMouseEvent, ac
from .types import _T, AsyncResponse, SingleKey, WindowSystemMouseEvent, ac
from .typing import PopenType, TypedDict
from .utils import (
func_name, get_editor, get_new_os_window_size, get_primary_selection,
@ -435,7 +435,7 @@ class Boss:
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
self.window_id_map[window.id] = window
def _handle_remote_command(self, cmd: str, window: Optional[Window] = None, peer_id: int = 0) -> Optional[Dict[str, Any]]:
def _handle_remote_command(self, cmd: str, window: Optional[Window] = None, peer_id: int = 0) -> Union[Dict[str, Any], None, AsyncResponse]:
from .remote_control import handle_cmd
response = None
window = window or None
@ -491,7 +491,7 @@ class Boss:
tb = traceback.format_exc()
self.show_error(_('remote_control mapping failed'), tb)
def peer_message_received(self, msg_bytes: bytes, peer_id: int) -> Optional[bytes]:
def peer_message_received(self, msg_bytes: bytes, peer_id: int) -> Union[bytes, bool, None]:
cmd_prefix = b'\x1bP@kitty-cmd'
terminator = b'\x1b\\'
if msg_bytes.startswith(cmd_prefix) and msg_bytes.endswith(terminator):
@ -499,6 +499,8 @@ class Boss:
response = self._handle_remote_command(cmd, peer_id=peer_id)
if response is None:
return None
if isinstance(response, AsyncResponse):
return True
from kitty.remote_control import encode_response_for_peer
return encode_response_for_peer(response)
@ -528,9 +530,8 @@ class Boss:
def handle_remote_cmd(self, cmd: str, window: Optional[Window] = None) -> None:
response = self._handle_remote_command(cmd, window)
if response is not None:
if window is not None:
window.send_cmd_response(response)
if response is not None and not isinstance(response, AsyncResponse) and window is not None:
window.send_cmd_response(response)
def _cleanup_tab_after_window_removal(self, src_tab: Tab) -> None:
if len(src_tab) < 1:

View File

@ -424,7 +424,7 @@ parse_input(ChildMonitor *self) {
}
if (resp) {
if (PyBytes_Check(resp)) send_response_to_peer(msg->peer_id, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
else send_response_to_peer(msg->peer_id, NULL, 0);
else if (resp == Py_None) send_response_to_peer(msg->peer_id, NULL, 0);
Py_CLEAR(resp);
} else send_response_to_peer(msg->peer_id, NULL, 0);
}

View File

@ -10,6 +10,7 @@ from typing import (
from kitty.cli import get_defaults_from_seq, parse_args, parse_option_spec
from kitty.cli_stub import RCOptions as R
from kitty.constants import appname, list_kitty_resources, running_in_kitty
from kitty.types import AsyncResponse
if TYPE_CHECKING:
from kitty.boss import Boss as B
@ -64,7 +65,7 @@ class PayloadGetter:
no_response = NoResponse()
payload_get = object()
ResponseType = Union[bool, str, None, NoResponse]
ResponseType = Union[bool, str, None, NoResponse, AsyncResponse]
CmdReturnType = Union[Dict[str, Any], List[Any], Tuple[Any, ...], str, int, float, bool]
CmdGenerator = Iterator[CmdReturnType]
PayloadType = Optional[Union[CmdReturnType, CmdGenerator]]

View File

@ -3,9 +3,11 @@
from typing import TYPE_CHECKING, Optional
from kitty.types import AsyncResponse
from .base import (
MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions,
RemoteCommand, ResponseType, Window, no_response
RemoteCommand, ResponseType, Window
)
if TYPE_CHECKING:
@ -70,7 +72,7 @@ Exclude the currently active window from the list of windows to pick
wids = set()
boss.visual_window_select_action(tab, callback, payload_get('title') or 'Choose window', only_window_ids=wids)
break
return no_response
return AsyncResponse()
def cancel_async_request(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> None:
boss.cancel_current_visual_select()

View File

@ -6,9 +6,9 @@ import os
import re
import sys
import types
from time import monotonic
from contextlib import suppress
from functools import partial
from time import monotonic
from typing import (
Any, Dict, Generator, Iterable, List, Optional, Tuple, Union, cast
)
@ -21,10 +21,10 @@ from .rc.base import (
NoResponse, ParsingOfArgsFailed, PayloadGetter, all_command_names,
command_for_name, parse_subcommand_cli
)
from .types import AsyncResponse
from .typing import BossType, WindowType
from .utils import TTYIO, parse_address_spec
active_async_requests: Dict[str, float] = {}
@ -33,7 +33,7 @@ def encode_response_for_peer(response: Any) -> bytes:
return b'\x1bP@kitty-cmd' + json.dumps(response).encode('utf-8') + b'\x1b\\'
def handle_cmd(boss: BossType, window: Optional[WindowType], serialized_cmd: str, peer_id: int) -> Optional[Dict[str, Any]]:
def handle_cmd(boss: BossType, window: Optional[WindowType], serialized_cmd: str, peer_id: int) -> Union[Dict[str, Any], None, AsyncResponse]:
cmd = json.loads(serialized_cmd)
v = cmd['version']
no_response = cmd.get('no_response', False)
@ -63,6 +63,8 @@ def handle_cmd(boss: BossType, window: Optional[WindowType], serialized_cmd: str
raise
if isinstance(ans, NoResponse):
return None
if isinstance(ans, AsyncResponse):
return ans
response: Dict[str, Any] = {'ok': True}
if ans is not None:
response['data'] = ans

View File

@ -74,6 +74,10 @@ class WindowSystemMouseEvent(NamedTuple):
ConvertibleToNumbers = Union[str, bytes, int, float]
class AsyncResponse:
pass
if TYPE_CHECKING:
class RunOnce(Generic[_T]):