Skip to main content

Объектное хранилище (ObjectStorage protocol + MinIO adapter + image normalization for Skillery)

Project description

s-storagekit

Объектное хранилище для Skillery: портабельное async-API для key→bytes операций с реализациями S3-compatible протокола (MinIO) и вспомогательными утилитами нормализации изображений (Pillow).

Установка

pip install s-storagekit>=0.1.0

Быстрый старт

Использование MinIO

from storagekit import MinioObjectStorage, ObjectNotFoundError

# Инициализация
storage = MinioObjectStorage(
    endpoint="minio:9000",
    access_key="minioadmin",
    secret_key="minioadmin",
    bucket="my-bucket",
    secure=False,  # http в dev, https в prod
)

# Запись объекта
await storage.put("avatars/user_1/photo.jpg", image_bytes, "image/jpeg")

# Чтение объекта
data, content_type = await storage.get("avatars/user_1/photo.jpg")

# Удаление объекта
await storage.delete("avatars/user_1/photo.jpg")

Обработка изображений

from storagekit import (
    normalize_image,
    normalize_square_avatar,
    square_avatar_variants,
)

# Квадратный аватар одного размера
jpeg_bytes, ct = normalize_square_avatar(raw_image, size=256)
await storage.put(f"avatars/user_1/{token}.jpg", jpeg_bytes, ct)

# Несколько вариантов размеров (responsive)
variants = square_avatar_variants(raw_image, sizes=[64, 128, 256])
for size, blob in variants:
    key = f"avatars/user_1/{token}_{size}.jpg"
    await storage.put(key, blob, "image/jpeg")

# Масштабирование обложки (сохраняет пропорции)
cover_data, ct = normalize_image(raw_image, max_px=1200)
await storage.put(f"skills/42/cover_{token}.jpg", cover_data, ct)

Тестирование

import pytest
from storagekit.testing import InMemoryObjectStorage, fake_image_bytes

@pytest.fixture
def storage():
    return InMemoryObjectStorage()

@pytest.mark.asyncio
async def test_avatar_upload(storage):
    raw = fake_image_bytes(100, 100)
    jpeg_bytes, _ = normalize_square_avatar(raw, size=256)
    
    await storage.put("avatars/test.jpg", jpeg_bytes, "image/jpeg")
    data, ct = await storage.get("avatars/test.jpg")
    
    assert ct == "image/jpeg"
    assert len(data) > 0

Presigned URL (без auth)

from storagekit import PresignedUrlGenerator

gen = PresignedUrlGenerator(
    endpoint="minio:9000",
    access_key="minioadmin",
    secret_key="minioadmin",
    bucket="my-bucket",
)

# Генерировать URL с TTL (по умолчанию 1 час)
url = await gen.generate_presigned_get_url("avatars/user_1/photo.jpg")
# Клиент может скачать файл по этому URL без токена

API

Протоколы

class ObjectStorage(Protocol):
    """Async key→bytes хранилище."""
    
    async def put(self, key: str, data: bytes, content_type: str) -> None:
        """Записать/перезаписать объект."""
        ...
    
    async def get(self, key: str) -> tuple[bytes, str]:
        """Вернуть (data, content_type). Бросает ObjectNotFoundError."""
        ...
    
    async def delete(self, key: str) -> None:
        """Удалить объект. Отсутствие не ошибка (idempotent)."""
        ...

Исключения

class ObjectNotFoundError(Exception):
    """Объект не найден. Имеет атрибут .key."""
    def __init__(self, key: str) -> None: ...

Реализации

# S3-compatible хранилище (prod/dev)
class MinioObjectStorage:
    def __init__(
        self,
        *,
        endpoint: str,        # host:port БЕЗ схемы
        access_key: str,
        secret_key: str,
        bucket: str,
        secure: bool = False, # http (dev) vs https (prod)
    ) -> None: ...

Image-утилиты

# Список допустимых MIME-типов входных файлов
ALLOWED_IMAGE_CONTENT_TYPES: frozenset[str]

# Квадратный аватар (center cover-crop) → JPEG
def normalize_square_avatar(data: bytes, *, size: int) -> tuple[bytes, str]: ...

# Несколько вариантов размеров одного аватара
def square_avatar_variants(
    data: bytes, *, sizes: list[int]
) -> list[tuple[int, bytes]]: ...

# Масштабирование по длинной стороне (сохраняет пропорции)
def normalize_image(data: bytes, *, max_px: int) -> tuple[bytes, str]: ...

Утилиты

# Канонический ключ snapshot'а версии
def snapshot_key(*, skill_id: int | str, semver: str) -> str:
    """Возвращает f'skills/{skill_id}/versions/{semver}.tar.gz'."""

Тестирование

# Фейк-хранилище в памяти
class InMemoryObjectStorage:
    async def put(self, key: str, data: bytes, content_type: str) -> None: ...
    async def get(self, key: str) -> tuple[bytes, str]: ...
    async def delete(self, key: str) -> None: ...

# Генерировать тестовый PNG-файл
def fake_image_bytes(width: int = 100, height: int = 100) -> bytes: ...

Параметризация

Параметр Значение Примечание
MinIO endpoint host:port (БЕЗ схемы) dev: minio:9000, prod: minio.example.com:9000
MinIO secure False (dev), True (prod) http vs https
Avatar sizes [64, 128, 256] (default) Можно переопределить в клиенте
Skill cover max px 1200 (constant) Не параметризуется
JPEG quality 85 (constant) Не параметризуется

Зависимости

  • minio>=7.0 — S3 SDK
  • anyio>=3.0 — async threading
  • Pillow>=10.0 — image processing

Лицензия

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

s_storagekit-0.1.0.tar.gz (9.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

s_storagekit-0.1.0-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

Details for the file s_storagekit-0.1.0.tar.gz.

File metadata

  • Download URL: s_storagekit-0.1.0.tar.gz
  • Upload date:
  • Size: 9.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for s_storagekit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 27dca9659a4c8950aeca698bc49ffb9e6ce64e236294356be0d4a2d274175f03
MD5 2b58b14205624024bdc5fa48e5f156aa
BLAKE2b-256 eab872718faeb184485cffd95302c308defe627a80aecf27986158a11152fa2f

See more details on using hashes here.

File details

Details for the file s_storagekit-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: s_storagekit-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for s_storagekit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e68ecc9f143bca0fd1bd2e9fb7054508e528a876f001c06ac85d154aa38c5794
MD5 f726178a2c70e102b64bda272f597848
BLAKE2b-256 dab683e00eef463e0dd64727cc0a32c415dc80dadcae80b9c541603637b609b2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page