Source code for httk.core.datastream.textstream_file_view

import io
from typing import Any, Iterator, NoReturn, Self

from ..views import unwrap
from .textstream_api import TextstreamAPI
from .textstream_backend import TextstreamBackend
from .textstream_like import TextstreamLike
from .textstream_view import TextstreamView


[docs] class TextstreamFileView(TextstreamView, io.TextIOBase, TextstreamAPI): """ A view presenting an underlying data streaming backend via the full io.TextIOBase API, which is a superset of TextstreamAPI. """ _backend: TextstreamBackend _readline_buffer: str def __new__(cls, obj: TextstreamLike, **hints: Any) -> Self: if isinstance(obj, cls): return obj backend = cls._prepare_backend(obj, hints) instance = super().__new__(cls) instance._backend = backend instance._readline_buffer = "" return instance def __init__(self, obj: TextstreamLike, **hints: Any) -> None: pass
[docs] def unwrap(self) -> Any: return unwrap(self._backend)
@property
[docs] def name(self) -> str | None: return self._backend.name
@property
[docs] def closed(self) -> bool: return self._backend.closed
[docs] def close(self) -> None: self._backend.close()
[docs] def readable(self) -> bool: return True
[docs] def writable(self) -> bool: return False
[docs] def seekable(self) -> bool: return hasattr(self._backend, "seek") and hasattr(self._backend, "tell")
[docs] def flush(self) -> None: if self.closed: raise ValueError("I/O operation on closed file.") flush = getattr(self._backend, "flush", None) if flush: flush()
[docs] def read(self, size: int | None = -1) -> str: if self.closed: raise ValueError("I/O operation on closed file.") if size is None or size < 0: if self._readline_buffer: prefix = self._readline_buffer self._readline_buffer = "" return prefix + self._backend.read() return self._backend.read() if size == 0: return "" if self._readline_buffer: if len(self._readline_buffer) >= size: out = self._readline_buffer[:size] self._readline_buffer = self._readline_buffer[size:] return out prefix = self._readline_buffer self._readline_buffer = "" return prefix + self._backend.read(size - len(prefix)) return self._backend.read(size)
[docs] def readline(self, size: int | None = -1) -> str: # type: ignore[override] ## https://github.com/python/mypy/issues/9643 if self.closed: raise ValueError("I/O operation on closed file.") if size == 0: return "" if size is None: size = -1 parts: list[str] = [] total = 0 while True: if self._readline_buffer: chunk = self._readline_buffer self._readline_buffer = "" else: to_read = 8192 if size >= 0: remaining = size - total if remaining <= 0: break to_read = min(to_read, remaining) chunk = self._backend.read(to_read) if chunk == "": break newline_pos = chunk.find("\n") if newline_pos != -1: newline_pos += 1 take = chunk[:newline_pos] rest = chunk[newline_pos:] if size >= 0 and total + len(take) > size: cutoff = size - total parts.append(take[:cutoff]) self._readline_buffer = take[cutoff:] + rest break parts.append(take) self._readline_buffer = rest break if size >= 0 and total + len(chunk) > size: cutoff = size - total parts.append(chunk[:cutoff]) self._readline_buffer = chunk[cutoff:] break parts.append(chunk) total += len(chunk) return "".join(parts)
[docs] def readlines(self, hint: int = -1) -> list[str]: # type: ignore[override] ## https://github.com/python/mypy/issues/9643 if self.closed: raise ValueError("I/O operation on closed file.") lines: list[str] = [] total = 0 while True: line = self.readline() if line == "": break lines.append(line) total += len(line) if hint >= 0 and total >= hint: break return lines
def __iter__(self) -> Iterator[str]: # type: ignore[override] ## https://github.com/python/mypy/issues/9643 return self def __next__(self) -> str: # type: ignore[override] ## https://github.com/python/mypy/issues/9643 line = self.readline() if line == "": raise StopIteration return line
[docs] def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: if self.closed: raise ValueError("I/O operation on closed file.") seek = getattr(self._backend, "seek", None) if not seek: raise io.UnsupportedOperation("underlying stream is not seekable") pos = seek(offset, whence) self._readline_buffer = "" return pos
[docs] def tell(self) -> int: if self.closed: raise ValueError("I/O operation on closed file.") tell = getattr(self._backend, "tell") if not tell: raise io.UnsupportedOperation("underlying stream does not support tell()") pos = tell() return pos - len(self._readline_buffer)
[docs] def detach(self) -> NoReturn: raise io.UnsupportedOperation("detach")
@property
[docs] def encoding(self) -> str | None: # type: ignore[override] ## these should not be writable return getattr(self._backend, "encoding", None)
@property
[docs] def errors(self) -> str | None: # type: ignore[override] ## these should not be writable return getattr(self._backend, "errors", None)
@property
[docs] def newlines(self) -> str | tuple[str, ...] | None: # type: ignore[override] ## these should not be writable return getattr(self._backend, "newlines", None)