106 lines
2.7 KiB
Python
106 lines
2.7 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
__lazy_modules__ = {"inspect", "io"}
|
||
|
|
|
||
|
|
import inspect
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
from contextlib import contextmanager
|
||
|
|
from io import StringIO
|
||
|
|
from typing import IO, TYPE_CHECKING, Any, TextIO
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from collections.abc import Callable, Generator
|
||
|
|
|
||
|
|
IS_WIN32 = sys.platform == "win32"
|
||
|
|
|
||
|
|
|
||
|
|
class ProcInfo:
|
||
|
|
__slots__ = ("args", "pid", "stat", "uid")
|
||
|
|
|
||
|
|
def __init__(self, pid: int, uid: int | str, stat: str, args: str):
|
||
|
|
self.pid = pid
|
||
|
|
self.uid = uid
|
||
|
|
self.stat = stat
|
||
|
|
self.args = args
|
||
|
|
|
||
|
|
def __repr__(self) -> str:
|
||
|
|
return f"{self.__class__.__name__}({self.pid!r}, {self.uid!r}, {self.stat!r}, {self.args!r})"
|
||
|
|
|
||
|
|
|
||
|
|
@contextmanager
|
||
|
|
def captured_stdout(stdin: str = "") -> Generator[TextIO, None, None]:
|
||
|
|
"""
|
||
|
|
Captures stdout (similar to the redirect_stdout in Python 3.4+, but with slightly different arguments)
|
||
|
|
"""
|
||
|
|
prevstdin = sys.stdin
|
||
|
|
prevstdout = sys.stdout
|
||
|
|
sys.stdin = StringIO(stdin)
|
||
|
|
sys.stdout = StringIO()
|
||
|
|
try:
|
||
|
|
yield sys.stdout
|
||
|
|
finally:
|
||
|
|
sys.stdin = prevstdin
|
||
|
|
sys.stdout = prevstdout
|
||
|
|
|
||
|
|
|
||
|
|
class StaticProperty:
|
||
|
|
# This acts like a static property, allowing access via class or object.
|
||
|
|
# This is a non-data descriptor.
|
||
|
|
|
||
|
|
__slots__ = ("__doc__", "_function")
|
||
|
|
|
||
|
|
def __init__(self, function: Callable[[], Any]):
|
||
|
|
self._function = function
|
||
|
|
self.__doc__ = function.__doc__
|
||
|
|
|
||
|
|
def __get__(self, obj: object, klass: object = None) -> Any:
|
||
|
|
return self._function()
|
||
|
|
|
||
|
|
|
||
|
|
def getdoc(obj: object) -> str | None:
|
||
|
|
"""
|
||
|
|
This gets a docstring if available, and cleans it, but does not look up docs in
|
||
|
|
inheritance tree (Pre Python 3.5 behavior of ``inspect.getdoc``).
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
doc = obj.__doc__
|
||
|
|
except AttributeError:
|
||
|
|
return None
|
||
|
|
if not isinstance(doc, str):
|
||
|
|
return None
|
||
|
|
return inspect.cleandoc(doc)
|
||
|
|
|
||
|
|
|
||
|
|
def read_fd_decode_safely(fd: IO[str], size: int = 4096) -> tuple[bytes, str]:
|
||
|
|
"""
|
||
|
|
This reads a utf-8 file descriptor and returns a chunk, growing up to
|
||
|
|
three bytes if needed to decode the character at the end.
|
||
|
|
|
||
|
|
Returns the data and the decoded text.
|
||
|
|
"""
|
||
|
|
data = os.read(fd.fileno(), size)
|
||
|
|
for _ in range(3):
|
||
|
|
try:
|
||
|
|
return data, data.decode("utf-8")
|
||
|
|
except UnicodeDecodeError as e: # noqa: PERF203
|
||
|
|
if e.reason != "unexpected end of data":
|
||
|
|
raise
|
||
|
|
data += os.read(fd.fileno(), 1)
|
||
|
|
|
||
|
|
return data, data.decode("utf-8")
|
||
|
|
|
||
|
|
|
||
|
|
__all__ = [
|
||
|
|
"IS_WIN32",
|
||
|
|
"ProcInfo",
|
||
|
|
"StaticProperty",
|
||
|
|
"captured_stdout",
|
||
|
|
"getdoc",
|
||
|
|
"read_fd_decode_safely",
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def __dir__() -> list[str]:
|
||
|
|
return list(__all__)
|