defl/defl/_stringAssembly_.py
2024-09-11 11:14:03 -04:00

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()