mirror of
https://github.com/markqvist/rnsh.git
synced 2024-10-01 01:15:37 -04:00
Lots more stuff, still more debugging to do
This commit is contained in:
parent
fcc73ba31a
commit
3183923c8c
1
rnsh/__version.py
Normal file
1
rnsh/__version.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = "0.0.1"
|
@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import errno
|
||||
import functools
|
||||
import signal
|
||||
@ -144,6 +145,20 @@ class TtyRestorer:
|
||||
"""
|
||||
termios.tcsetattr(self._fd, termios.TCSADRAIN, self._tattr)
|
||||
|
||||
|
||||
async def event_wait(evt: asyncio.Event, timeout: float) -> bool:
|
||||
"""
|
||||
Wait for event to be set, or timeout to expire.
|
||||
:param evt: asyncio.Event to wait on
|
||||
:param timeout: maximum number of seconds to wait.
|
||||
:return: True if event was set, False if timeout expired
|
||||
"""
|
||||
# suppress TimeoutError because we'll return False in case of timeout
|
||||
with contextlib.suppress(asyncio.TimeoutError):
|
||||
await asyncio.wait_for(evt.wait(), timeout)
|
||||
return evt.is_set()
|
||||
|
||||
|
||||
class CallbackSubprocess:
|
||||
# time between checks of child process
|
||||
PROCESS_POLL_TIME: float = 0.1
|
||||
@ -309,6 +324,31 @@ class CallbackSubprocess:
|
||||
def return_code(self) -> int | None:
|
||||
return self._return_code
|
||||
|
||||
# # from https://gist.github.com/bruce-shi/fd0e3f5e2360c64bc9ce2efb254744f7
|
||||
# from collections import defaultdict
|
||||
# class disable_signals(object):
|
||||
# def __init__(self, disabled_signals=None):
|
||||
# self.stashed_signals = defaultdict(list)
|
||||
# self.disabled_signals = disabled_signals or []
|
||||
#
|
||||
# def __enter__(self):
|
||||
# for signal in self.disabled_signals:
|
||||
# self.disconnect(signal)
|
||||
#
|
||||
# def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
# for signal in list(self.stashed_signals):
|
||||
# self.reconnect(signal)
|
||||
#
|
||||
# def disconnect(self, signal):
|
||||
# self.stashed_signals[signal] = signal.receivers
|
||||
# signal.receivers = []
|
||||
#
|
||||
# def reconnect(self, signal):
|
||||
# signal.receivers = self.stashed_signals.get(signal, [])
|
||||
# del self.stashed_signals[signal]
|
||||
# signal.sender_receivers_cache.clear()
|
||||
|
||||
|
||||
async def main():
|
||||
"""
|
||||
A test driver for the CallbackProcess class.
|
||||
|
@ -2,11 +2,15 @@ import asyncio
|
||||
import threading
|
||||
import time
|
||||
import logging as __logging
|
||||
from typing import Callable
|
||||
|
||||
module_logger = __logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RetryStatus:
|
||||
def __init__(self, id: any, try_limit: int, wait_delay: float, retry_callback: callable[any, int], timeout_callback: callable[any], tries: int = 1):
|
||||
self.id = id
|
||||
def __init__(self, tag: any, try_limit: int, wait_delay: float, retry_callback: Callable[[any, int], any],
|
||||
timeout_callback: Callable[[any, int], None], tries: int = 1):
|
||||
self.tag = tag
|
||||
self.try_limit = try_limit
|
||||
self.tries = tries
|
||||
self.wait_delay = wait_delay
|
||||
@ -25,22 +29,23 @@ class RetryStatus:
|
||||
|
||||
def timeout(self):
|
||||
self.completed = True
|
||||
self.timeout_callback(self.id)
|
||||
self.timeout_callback(self.tag, self.tries)
|
||||
|
||||
def retry(self):
|
||||
self.tries += 1
|
||||
self.retry_callback(self.id, self.tries)
|
||||
self.retry_callback(self.tag, self.tries)
|
||||
|
||||
|
||||
class RetryThread:
|
||||
def __init__(self, loop_period: float = 0.25):
|
||||
self._log = module_logger.getChild(self.__class__.__name__)
|
||||
self._loop_period = loop_period
|
||||
self._statuses: list[RetryStatus] = []
|
||||
self._id_counter = 0
|
||||
self._tag_counter = 0
|
||||
self._lock = threading.RLock()
|
||||
self._thread = threading.Thread(target=self._thread_run())
|
||||
self._run = True
|
||||
self._finished: asyncio.Future | None = None
|
||||
self._thread = threading.Thread(target=self._thread_run)
|
||||
self._thread.start()
|
||||
|
||||
def close(self, loop: asyncio.AbstractEventLoop | None = None) -> asyncio.Future | None:
|
||||
@ -52,6 +57,7 @@ class RetryThread:
|
||||
else:
|
||||
self._finished = loop.create_future()
|
||||
return self._finished
|
||||
|
||||
def _thread_run(self):
|
||||
last_run = time.monotonic()
|
||||
while self._run and self._finished is None:
|
||||
@ -65,52 +71,59 @@ class RetryThread:
|
||||
try:
|
||||
if not retry.completed:
|
||||
if retry.timed_out:
|
||||
self._log.debug(f"timed out {retry.id} after {retry.try_limit} tries")
|
||||
self._log.debug(f"timed out {retry.tag} after {retry.try_limit} tries")
|
||||
retry.timeout()
|
||||
prune.append(retry)
|
||||
else:
|
||||
self._log.debug(f"retrying {retry.id}, try {retry.tries + 1}/{retry.try_limit}")
|
||||
self._log.debug(f"retrying {retry.tag}, try {retry.tries + 1}/{retry.try_limit}")
|
||||
retry.retry()
|
||||
except Exception as e:
|
||||
self._log.error(f"error processing retry id {retry.id}: {e}")
|
||||
self._log.error(f"error processing retry id {retry.tag}: {e}")
|
||||
prune.append(retry)
|
||||
|
||||
with self._lock:
|
||||
for retry in prune:
|
||||
self._log.debug(f"pruned retry {retry.id}, retry count {retry.tries}/{retry.try_limit}")
|
||||
self._log.debug(f"pruned retry {retry.tag}, retry count {retry.tries}/{retry.try_limit}")
|
||||
self._statuses.remove(retry)
|
||||
if self._finished is not None:
|
||||
self._finished.set_result(None)
|
||||
|
||||
def _get_id(self):
|
||||
self._id_counter += 1
|
||||
return self._id_counter
|
||||
def _get_next_tag(self):
|
||||
self._tag_counter += 1
|
||||
return self._tag_counter
|
||||
|
||||
def begin(self, try_limit: int, wait_delay: float, try_callback: callable[[any | None, int], any], timeout_callback: callable[any, int], id: int | None = None) -> any:
|
||||
def begin(self, try_limit: int, wait_delay: float, try_callback: Callable[[any, int], any],
|
||||
timeout_callback: Callable[[any, int], None], tag: int | None = None) -> any:
|
||||
self._log.debug(f"running first try")
|
||||
id = try_callback(id, 1)
|
||||
self._log.debug(f"first try success, got id {id}")
|
||||
tag = try_callback(tag, 1)
|
||||
self._log.debug(f"first try success, got id {tag}")
|
||||
with self._lock:
|
||||
if id is None:
|
||||
id = self._get_id()
|
||||
self._statuses.append(RetryStatus(id=id,
|
||||
if tag is None:
|
||||
tag = self._get_next_tag()
|
||||
self._statuses.append(RetryStatus(tag=tag,
|
||||
tries=1,
|
||||
try_limit=try_limit,
|
||||
wait_delay=wait_delay,
|
||||
retry_callback=try_callback,
|
||||
timeout_callback=timeout_callback))
|
||||
self._log.debug(f"added retry timer for {id}")
|
||||
def complete(self, id: any):
|
||||
assert id is not None
|
||||
self._log.debug(f"added retry timer for {tag}")
|
||||
|
||||
def complete(self, tag: any):
|
||||
assert tag is not None
|
||||
status: RetryStatus | None = None
|
||||
with self._lock:
|
||||
status = next(filter(lambda l: l.id == id, self._statuses))
|
||||
assert status is not None
|
||||
status.completed = True
|
||||
self._statuses.remove(status)
|
||||
self._log.debug(f"completed {id}")
|
||||
status = next(filter(lambda l: l.tag == tag, self._statuses))
|
||||
if status is not None:
|
||||
status.completed = True
|
||||
self._statuses.remove(status)
|
||||
if status is not None:
|
||||
self._log.debug(f"completed {tag}")
|
||||
else:
|
||||
self._log.debug(f"status not found to complete {tag}")
|
||||
|
||||
def complete_all(self):
|
||||
with self._lock:
|
||||
for status in self._statuses:
|
||||
status.completed = True
|
||||
self._log.debug(f"completed {status.id}")
|
||||
self._log.debug(f"completed {status.tag}")
|
||||
self._statuses.clear()
|
||||
|
816
rnsh/rnsh.py
816
rnsh/rnsh.py
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,11 @@ import logging
|
||||
from logging import Handler, getLevelName
|
||||
from types import GenericAlias
|
||||
import os
|
||||
|
||||
import tty
|
||||
import termios
|
||||
import sys
|
||||
import RNS
|
||||
import json
|
||||
|
||||
class RnsHandler(Handler):
|
||||
"""
|
||||
@ -37,7 +40,12 @@ class RnsHandler(Handler):
|
||||
"""
|
||||
try:
|
||||
msg = self.format(record)
|
||||
|
||||
# tattr = termios.tcgetattr(sys.stdin.fileno())
|
||||
# json.dump(tattr, sys.stdout)
|
||||
# termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, tattr | termios.ONLRET | termios.ONLCR | termios.OPOST)
|
||||
RNS.log(msg, RnsHandler.get_rns_loglevel(record.levelno))
|
||||
# termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, tattr)
|
||||
except RecursionError: # See issue 36272
|
||||
raise
|
||||
except Exception:
|
||||
@ -49,7 +57,6 @@ class RnsHandler(Handler):
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
|
||||
log_format = '%(name)-40s %(message)s [%(threadName)s]'
|
||||
|
||||
logging.basicConfig(
|
||||
@ -57,4 +64,22 @@ logging.basicConfig(
|
||||
#format='%(asctime)s.%(msecs)03d %(levelname)-6s %(threadName)-15s %(name)-15s %(message)s',
|
||||
format=log_format,
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
handlers=[RnsHandler()])
|
||||
handlers=[RnsHandler()])
|
||||
|
||||
#hack for temporarily overriding term settings to make debug print right
|
||||
_rns_log_orig = RNS.log
|
||||
|
||||
def _rns_log(msg, level=3, _override_destination = False):
|
||||
tattr = termios.tcgetattr(sys.stdin.fileno())
|
||||
tattr_orig = tattr.copy()
|
||||
# tcflag_t c_iflag; /* input modes */
|
||||
# tcflag_t c_oflag; /* output modes */
|
||||
# tcflag_t c_cflag; /* control modes */
|
||||
# tcflag_t c_lflag; /* local modes */
|
||||
# cc_t c_cc[NCCS]; /* special characters */
|
||||
tattr[1] = tattr[1] | termios.ONLRET | termios.ONLCR | termios.OPOST
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, tattr)
|
||||
_rns_log_orig(msg, level, _override_destination)
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, tattr_orig)
|
||||
|
||||
RNS.log = _rns_log
|
Loading…
Reference in New Issue
Block a user