Source code for imgproxykit.serialization

from imgproxykit.internal.encoding import encode_base64
from imgproxykit.transform import Transform
from imgproxykit.types import Argument


[docs] class PathSerializer: """ Serialize processing options into an unsigned imgproxy path. Used by :class:`~imgproxykit.Client` before signing the resulting path. Sources are encoded as URL-safe Base64 without padding. Processing options are included before the source in the supplied order. Pass processing options through :class:`~imgproxykit.Transform` as option instances or scalar keyword arguments. Preset options reference names configured on the imgproxy server and are not expanded here. Subclass this serializer and override :meth:`__call__` to customize path construction. Set the subclass on :attr:`imgproxykit.Client.path_serializer_cls` to use it in a client. """ __slots__ = ('_arguments_separator',) # TODO: Support the plain option for unencoded source URLs. def __init__(self, *, arguments_separator: str) -> None: """ Configure processing option serialization. Args: arguments_separator: Separator between processing option names and their arguments. Must match the server's `IMGPROXY_ARGUMENTS_SEPARATOR <https://docs.imgproxy.net/latest/configuration/options#miscellaneous>`__. """ self._arguments_separator = arguments_separator
[docs] def __call__( self, source: str, *, transform: Transform | None = None, ) -> str: """ Build an unsigned path using processing options and preset references. Serialize each option using the configured arguments separator before the encoded source. Output formats are included as processing segments. Omitted processing parameters are left to the server. Args: source: Complete source URI understood by the imgproxy server. Its UTF-8 representation is encoded as URL-safe Base64 without padding. The URI is neither validated nor resolved. transform: Reusable processing options and optional output format. Omit or pass ``None`` to add no processing options. Returns: Unsigned path beginning with ``/``, ready for signing. Contains neither the imgproxy address nor a signature segment. Examples: Build a square WebP thumbnail path: >>> from imgproxykit import Transform >>> from imgproxykit.serialization import PathSerializer >>> from imgproxykit.options import Resize >>> serializer = PathSerializer(arguments_separator=':') >>> thumbnail = Transform( ... Resize(width=96, height=96, mode='fill'), ... format='webp', ... ) >>> serializer('s3://images/photo.jpg', transform=thumbnail) '/resize:fill:96:96/format:webp/czM6Ly9pbWFnZXMvcGhvdG8uanBn' """ options = ( () if transform is None else tuple( self._serialize_segment(option.name, option.arguments) for option in transform.options ) ) return '/'.join(('', *options, encode_base64(source)))
def _serialize_segment( self, name: str, arguments: tuple[Argument, ...], ) -> str: """ Serialize an option name and positional arguments into a path segment. Internal ``None`` values become empty positions; trailing ``None`` values are omitted. Explicit zeroes and empty strings retain their positions. Booleans are serialized as ``1`` and ``0``. """ # TODO: Verify reserved characters in string arguments and preset names # against imgproxy before choosing encoding or rejection. end = len(arguments) while end and arguments[end - 1] is None: end -= 1 serialized = map(self._serialize_argument, arguments[:end]) return self._arguments_separator.join((name, *serialized)) @classmethod def _serialize_argument(cls, value: Argument) -> str: """Serialize one argument without treating false values as omitted.""" if value is None: return '' if isinstance(value, bool): return str(int(value)) return str(value)