141 lines
4.4 KiB
Python
141 lines
4.4 KiB
Python
import atexit
|
|
import base64
|
|
import hashlib
|
|
import io
|
|
import itertools
|
|
import random
|
|
import re
|
|
import secrets
|
|
import select
|
|
import shlex
|
|
import string
|
|
import subprocess
|
|
import threading
|
|
from dataclasses import dataclass, field
|
|
from functools import partial
|
|
from typing import Generator
|
|
|
|
import toml
|
|
|
|
from ._ansii_ import cl, regAnsii, regAnsiiComp
|
|
from ._basic_ import listFromArgsStar
|
|
from ._jq_ import jdumps
|
|
from ._logger_ import log
|
|
from ._typing_ import *
|
|
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
|
class StringAssembly:
|
|
#| Beyond the initialization of the object, if you write directly to bytes it will disrupt events.
|
|
bytz: bytearray = field(default_factory=bytearray, kw_only=False)
|
|
# TODO how to deal with backspace \x7f
|
|
eof: bool = False
|
|
_lineReady: threading.Event = field(default_factory=threading.Event)
|
|
_charReady: threading.Event = field(default_factory=threading.Event)
|
|
|
|
def __post_init__(_):
|
|
if isinstance(_.bytz, str):
|
|
_.bytz = bytearray(_.bytz.encode('utf8'))
|
|
elif isinstance(_.bytz, bytes):
|
|
_.bytz = bytearray(_.bytz)
|
|
_._lineReady.clear()
|
|
_._charReady.clear()
|
|
|
|
def __len__(_):
|
|
return len(_.bytz)
|
|
|
|
def __add__(_, data) -> None:
|
|
assert isinstance(data, bytearray | bytes | str)
|
|
# TODO line intercept hook
|
|
if _.eof:
|
|
raise EOFError()
|
|
data = data.encode('utf8') if isinstance(data, str) else data
|
|
if data == b'':
|
|
_.eof = True
|
|
_.bytz += data
|
|
# if b'\x7f' in data:
|
|
_._setEvent()
|
|
|
|
def _setEvent(_):
|
|
if _.charReady():
|
|
_._charReady.set()
|
|
if _.lineReady():
|
|
_._lineReady.set()
|
|
else:
|
|
_._lineReady.clear()
|
|
else:
|
|
_._charReady.clear()
|
|
_._lineReady.clear()
|
|
|
|
def clear(_) -> None:
|
|
_.bytz = bytearray(b'')
|
|
_._lineReady.clear()
|
|
_._charReady.clear()
|
|
|
|
def charReady(_, timeout: float | None = None) -> bool | None:
|
|
if _.eof and not _.bytz:
|
|
return None
|
|
if timeout is not None:
|
|
timeout = None if timeout == -1 else timeout #| -1 is infinite wait
|
|
return _._charReady.wait(timeout=timeout)
|
|
return True if _.bytz else False
|
|
|
|
def lineReady(_, timeout: float | None = None) -> bool | None:
|
|
if _.eof and not _.bytz:
|
|
return None
|
|
if timeout is not None:
|
|
timeout = None if timeout == -1 else timeout #| -1 is infinite wait
|
|
return _._lineReady.wait(timeout=timeout)
|
|
if b'\n' in _.bytz or b'\r' in _.bytz or (_.eof and _.bytz):
|
|
return True
|
|
return False
|
|
|
|
def newLineIndex(_) -> int | None:
|
|
index = [_.bytz.index(i) for i in (b'\n', b'\r') if i in _.bytz]
|
|
if _.eof:
|
|
index.append(len(_.bytz))
|
|
if index:
|
|
return min(index)
|
|
|
|
def all(_):
|
|
return _.read(end=len(_.bytz), start=len(_.bytz))
|
|
|
|
def read(_, end: int, start: int):
|
|
a, _.bytz = (_.bytz[:end], _.bytz[start:])
|
|
_._setEvent()
|
|
return a
|
|
|
|
def readChar(_, count: int, stripNewLine: bool = True) -> bytearray:
|
|
index = []
|
|
if (lr := _.newLineIndex()) is not None:
|
|
index.append(lr + 1)
|
|
index.append(count)
|
|
index.append(len(_.bytz))
|
|
index = min(index)
|
|
start = index
|
|
end = index
|
|
res = _.read(end=end, start=start)
|
|
if stripNewLine and (res.endswith(b'\n') or res.endswith(b'\r')):
|
|
res = res[:-1]
|
|
if stripNewLine and (res == b'\n' or res == b'\n'):
|
|
return bytearray(b'')
|
|
return res
|
|
|
|
def readCharGroup(_, count: int, stripNewLine: bool = True) -> bytearray:
|
|
# TODO return ansi command \x1b[B -> defl.ANSII
|
|
# TODO return chars as b'kdfnkdfsnsdfjnsfj'
|
|
# # TODO return type enum ANSII or CHAR
|
|
# TODO maybe split on whitespace
|
|
raise NotImplementedError('')
|
|
|
|
def readLine(_, stripNewLine: bool = True) -> bytearray | None:
|
|
if (index := _.newLineIndex()) is not None:
|
|
return _.read(end=index, start=index + (1 if stripNewLine else 0))
|
|
|
|
def readLines(_, stripNewLine: bool = True) -> Generator[bytearray, None, None]:
|
|
while (rs := _.readLine(stripNewLine=stripNewLine)):
|
|
yield rs
|
|
|
|
def __iter__(_) -> Generator[bytearray | None, None, None]:
|
|
while _.lineReady():
|
|
yield _.readLine()
|