feat: add psagentai agent with deployment and configuration
Initial project setup including the main agent script, configuration files, profile JSON state, systemd service unit, startup script, deployment documentation, and Python dependencies. Includes .env, venv/, and __pycache__/ which should likely be excluded via .gitignore to avoid committing secrets and artifacts.
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
from .constants import MIME_TYPE, PNG_CHUNK_TYPE
|
||||
from .exceptions import InvalidImageStreamError
|
||||
from .helpers import BIG_ENDIAN, StreamReader
|
||||
from .image import BaseImageHeader
|
||||
|
||||
|
||||
class Png(BaseImageHeader):
|
||||
"""Image header parser for PNG images."""
|
||||
|
||||
@property
|
||||
def content_type(self):
|
||||
"""MIME content type for this image, unconditionally `image/png` for PNG
|
||||
images."""
|
||||
return MIME_TYPE.PNG
|
||||
|
||||
@property
|
||||
def default_ext(self):
|
||||
"""Default filename extension, always 'png' for PNG images."""
|
||||
return "png"
|
||||
|
||||
@classmethod
|
||||
def from_stream(cls, stream):
|
||||
"""Return a |Png| instance having header properties parsed from image in
|
||||
`stream`."""
|
||||
parser = _PngParser.parse(stream)
|
||||
|
||||
px_width = parser.px_width
|
||||
px_height = parser.px_height
|
||||
horz_dpi = parser.horz_dpi
|
||||
vert_dpi = parser.vert_dpi
|
||||
|
||||
return cls(px_width, px_height, horz_dpi, vert_dpi)
|
||||
|
||||
|
||||
class _PngParser:
|
||||
"""Parses a PNG image stream to extract the image properties found in its chunks."""
|
||||
|
||||
def __init__(self, chunks):
|
||||
super(_PngParser, self).__init__()
|
||||
self._chunks = chunks
|
||||
|
||||
@classmethod
|
||||
def parse(cls, stream):
|
||||
"""Return a |_PngParser| instance containing the header properties parsed from
|
||||
the PNG image in `stream`."""
|
||||
chunks = _Chunks.from_stream(stream)
|
||||
return cls(chunks)
|
||||
|
||||
@property
|
||||
def px_width(self):
|
||||
"""The number of pixels in each row of the image."""
|
||||
IHDR = self._chunks.IHDR
|
||||
return IHDR.px_width
|
||||
|
||||
@property
|
||||
def px_height(self):
|
||||
"""The number of stacked rows of pixels in the image."""
|
||||
IHDR = self._chunks.IHDR
|
||||
return IHDR.px_height
|
||||
|
||||
@property
|
||||
def horz_dpi(self):
|
||||
"""Integer dots per inch for the width of this image.
|
||||
|
||||
Defaults to 72 when not present in the file, as is often the case.
|
||||
"""
|
||||
pHYs = self._chunks.pHYs
|
||||
if pHYs is None:
|
||||
return 72
|
||||
return self._dpi(pHYs.units_specifier, pHYs.horz_px_per_unit)
|
||||
|
||||
@property
|
||||
def vert_dpi(self):
|
||||
"""Integer dots per inch for the height of this image.
|
||||
|
||||
Defaults to 72 when not present in the file, as is often the case.
|
||||
"""
|
||||
pHYs = self._chunks.pHYs
|
||||
if pHYs is None:
|
||||
return 72
|
||||
return self._dpi(pHYs.units_specifier, pHYs.vert_px_per_unit)
|
||||
|
||||
@staticmethod
|
||||
def _dpi(units_specifier, px_per_unit):
|
||||
"""Return dots per inch value calculated from `units_specifier` and
|
||||
`px_per_unit`."""
|
||||
if units_specifier == 1 and px_per_unit:
|
||||
return int(round(px_per_unit * 0.0254))
|
||||
return 72
|
||||
|
||||
|
||||
class _Chunks:
|
||||
"""Collection of the chunks parsed from a PNG image stream."""
|
||||
|
||||
def __init__(self, chunk_iterable):
|
||||
super(_Chunks, self).__init__()
|
||||
self._chunks = list(chunk_iterable)
|
||||
|
||||
@classmethod
|
||||
def from_stream(cls, stream):
|
||||
"""Return a |_Chunks| instance containing the PNG chunks in `stream`."""
|
||||
chunk_parser = _ChunkParser.from_stream(stream)
|
||||
chunks = list(chunk_parser.iter_chunks())
|
||||
return cls(chunks)
|
||||
|
||||
@property
|
||||
def IHDR(self):
|
||||
"""IHDR chunk in PNG image."""
|
||||
match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.IHDR # noqa
|
||||
IHDR = self._find_first(match)
|
||||
if IHDR is None:
|
||||
raise InvalidImageStreamError("no IHDR chunk in PNG image")
|
||||
return IHDR
|
||||
|
||||
@property
|
||||
def pHYs(self):
|
||||
"""PHYs chunk in PNG image, or |None| if not present."""
|
||||
match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.pHYs # noqa
|
||||
return self._find_first(match)
|
||||
|
||||
def _find_first(self, match):
|
||||
"""Return first chunk in stream order returning True for function `match`."""
|
||||
for chunk in self._chunks:
|
||||
if match(chunk):
|
||||
return chunk
|
||||
return None
|
||||
|
||||
|
||||
class _ChunkParser:
|
||||
"""Extracts chunks from a PNG image stream."""
|
||||
|
||||
def __init__(self, stream_rdr):
|
||||
super(_ChunkParser, self).__init__()
|
||||
self._stream_rdr = stream_rdr
|
||||
|
||||
@classmethod
|
||||
def from_stream(cls, stream):
|
||||
"""Return a |_ChunkParser| instance that can extract the chunks from the PNG
|
||||
image in `stream`."""
|
||||
stream_rdr = StreamReader(stream, BIG_ENDIAN)
|
||||
return cls(stream_rdr)
|
||||
|
||||
def iter_chunks(self):
|
||||
"""Generate a |_Chunk| subclass instance for each chunk in this parser's PNG
|
||||
stream, in the order encountered in the stream."""
|
||||
for chunk_type, offset in self._iter_chunk_offsets():
|
||||
chunk = _ChunkFactory(chunk_type, self._stream_rdr, offset)
|
||||
yield chunk
|
||||
|
||||
def _iter_chunk_offsets(self):
|
||||
"""Generate a (chunk_type, chunk_offset) 2-tuple for each of the chunks in the
|
||||
PNG image stream.
|
||||
|
||||
Iteration stops after the IEND chunk is returned.
|
||||
"""
|
||||
chunk_offset = 8
|
||||
while True:
|
||||
chunk_data_len = self._stream_rdr.read_long(chunk_offset)
|
||||
chunk_type = self._stream_rdr.read_str(4, chunk_offset, 4)
|
||||
data_offset = chunk_offset + 8
|
||||
yield chunk_type, data_offset
|
||||
if chunk_type == "IEND":
|
||||
break
|
||||
# incr offset for chunk len long, chunk type, chunk data, and CRC
|
||||
chunk_offset += 4 + 4 + chunk_data_len + 4
|
||||
|
||||
|
||||
def _ChunkFactory(chunk_type, stream_rdr, offset):
|
||||
"""Return a |_Chunk| subclass instance appropriate to `chunk_type` parsed from
|
||||
`stream_rdr` at `offset`."""
|
||||
chunk_cls_map = {
|
||||
PNG_CHUNK_TYPE.IHDR: _IHDRChunk,
|
||||
PNG_CHUNK_TYPE.pHYs: _pHYsChunk,
|
||||
}
|
||||
chunk_cls = chunk_cls_map.get(chunk_type, _Chunk)
|
||||
return chunk_cls.from_offset(chunk_type, stream_rdr, offset)
|
||||
|
||||
|
||||
class _Chunk:
|
||||
"""Base class for specific chunk types.
|
||||
|
||||
Also serves as the default chunk type.
|
||||
"""
|
||||
|
||||
def __init__(self, chunk_type):
|
||||
super(_Chunk, self).__init__()
|
||||
self._chunk_type = chunk_type
|
||||
|
||||
@classmethod
|
||||
def from_offset(cls, chunk_type, stream_rdr, offset):
|
||||
"""Return a default _Chunk instance that only knows its chunk type."""
|
||||
return cls(chunk_type)
|
||||
|
||||
@property
|
||||
def type_name(self):
|
||||
"""The chunk type name, e.g. 'IHDR', 'pHYs', etc."""
|
||||
return self._chunk_type
|
||||
|
||||
|
||||
class _IHDRChunk(_Chunk):
|
||||
"""IHDR chunk, contains the image dimensions."""
|
||||
|
||||
def __init__(self, chunk_type, px_width, px_height):
|
||||
super(_IHDRChunk, self).__init__(chunk_type)
|
||||
self._px_width = px_width
|
||||
self._px_height = px_height
|
||||
|
||||
@classmethod
|
||||
def from_offset(cls, chunk_type, stream_rdr, offset):
|
||||
"""Return an _IHDRChunk instance containing the image dimensions extracted from
|
||||
the IHDR chunk in `stream` at `offset`."""
|
||||
px_width = stream_rdr.read_long(offset)
|
||||
px_height = stream_rdr.read_long(offset, 4)
|
||||
return cls(chunk_type, px_width, px_height)
|
||||
|
||||
@property
|
||||
def px_width(self):
|
||||
return self._px_width
|
||||
|
||||
@property
|
||||
def px_height(self):
|
||||
return self._px_height
|
||||
|
||||
|
||||
class _pHYsChunk(_Chunk):
|
||||
"""PYHs chunk, contains the image dpi information."""
|
||||
|
||||
def __init__(self, chunk_type, horz_px_per_unit, vert_px_per_unit, units_specifier):
|
||||
super(_pHYsChunk, self).__init__(chunk_type)
|
||||
self._horz_px_per_unit = horz_px_per_unit
|
||||
self._vert_px_per_unit = vert_px_per_unit
|
||||
self._units_specifier = units_specifier
|
||||
|
||||
@classmethod
|
||||
def from_offset(cls, chunk_type, stream_rdr, offset):
|
||||
"""Return a _pHYsChunk instance containing the image resolution extracted from
|
||||
the pHYs chunk in `stream` at `offset`."""
|
||||
horz_px_per_unit = stream_rdr.read_long(offset)
|
||||
vert_px_per_unit = stream_rdr.read_long(offset, 4)
|
||||
units_specifier = stream_rdr.read_byte(offset, 8)
|
||||
return cls(chunk_type, horz_px_per_unit, vert_px_per_unit, units_specifier)
|
||||
|
||||
@property
|
||||
def horz_px_per_unit(self):
|
||||
return self._horz_px_per_unit
|
||||
|
||||
@property
|
||||
def vert_px_per_unit(self):
|
||||
return self._vert_px_per_unit
|
||||
|
||||
@property
|
||||
def units_specifier(self):
|
||||
return self._units_specifier
|
||||
Reference in New Issue
Block a user