Skip to content
Snippets Groups Projects
Commit 92165577 authored by teplyakov's avatar teplyakov
Browse files

feat: AUTO-4050 Add docstrings

parent 93beed6e
No related branches found
No related tags found
1 merge request!95feat: AUTO-4050 Add docstrings
"""
Модуль для загрузки файлов в систему Perxis.
Этот модуль предоставляет класс [`FileUploader`](#FileUploader), который поддерживает:
- Чтение файлов чанками
- Загрузку файлов в Perxis по частям
- Автоматическую публикацию загруженных файлов
Используется для работы с API Perxis, обеспечивая передачу файлов.
## Пример использования:
```python
import aiofiles
from aiofiles.os import path as aiopath
from perxis.channel import get_grpc_channel
from perxis.provider import PerxisFileProvider, PerxisDataProvider
from perxis.files.uploader import FileUploader
channel = get_grpc_channel(
target=..., api_key=..., project_name=..., project_version=...
)
uploader = FileUploader(
file_provider=PerxisFileProvider(channel=channel),
data_provider=PerxisDataProvider(
channel=channel,
space_id=...,
env_id=...,
),
)
async def main():
filepath = "path/to/file.txt"
async with aiofiles.open(file=filepath, mode="rb") as file:
size = await aiopath.getsize(filepath)
response = await uploader.upload_and_publish(
file_object=file,
file_name="file.txt",
file_size=size,
collection_id="files",
)
```
---
"""
import aiohttp import aiohttp
import asyncstdlib as a import asyncstdlib as a
from typing import AsyncGenerator
from aiofile import FileIOWrapperBase, TextFileWrapper from aiofile import FileIOWrapperBase, TextFileWrapper
from google.protobuf.struct_pb2 import Struct from google.protobuf.struct_pb2 import Struct
from perxis.items import items_pb2 from perxis.items import items_pb2
from perxis.provider import PerxisFileProvider, PerxisDataProvider from perxis.provider import PerxisFileProvider, PerxisDataProvider
__all__ = ("FileUploader",) __all__ = ("FileUploader",)
DEFAULT_CHUNK_SIZE: int = 5 * 2 ** 20 DEFAULT_CHUNK_SIZE: int = 5 * 2**20
async def read_chunks( async def read_chunks(
file_object: FileIOWrapperBase | TextFileWrapper, file_object: FileIOWrapperBase | TextFileWrapper, chunk_size: int
chunk_size: int ) -> AsyncGenerator[bytes | str, None]:
) -> bytes | str: """Читает файл чанками заданного размера.
Args:
file_object (FileIOWrapperBase | TextFileWrapper): Файловый объект.
chunk_size (int): Размер одного чанка в байтах.
Yields:
AsyncGenerator[bytes | str]: Чанки данных из файла.
"""
while True: while True:
data = await file_object.read(chunk_size) data = await file_object.read(chunk_size)
if not data: if not data:
...@@ -23,12 +81,27 @@ async def read_chunks( ...@@ -23,12 +81,27 @@ async def read_chunks(
class FileUploader: class FileUploader:
"""Класс для загрузки файлов в Perxis.
Attributes:
file_provider (PerxisFileProvider): Провайдер файлов.
data_provider (PerxisDataProvider): Провайдер данных.
chunk_size (int): Размер чанка для загрузки.
"""
def __init__( def __init__(
self, self,
file_provider: PerxisFileProvider, file_provider: PerxisFileProvider,
data_provider: PerxisDataProvider, data_provider: PerxisDataProvider,
chunk_size: int = DEFAULT_CHUNK_SIZE, chunk_size: int = DEFAULT_CHUNK_SIZE,
) -> None: ) -> None:
"""Инициализирует FileUploader.
Args:
file_provider (PerxisFileProvider): Провайдер файлов.
data_provider (PerxisDataProvider): Провайдер данных.
chunk_size (int, optional): Размер чанка. По умолчанию 5 МБ.
"""
self.file_provider = file_provider self.file_provider = file_provider
self.data_provider = data_provider self.data_provider = data_provider
self.chunk_size = chunk_size self.chunk_size = chunk_size
...@@ -38,14 +111,20 @@ class FileUploader: ...@@ -38,14 +111,20 @@ class FileUploader:
file_object: FileIOWrapperBase | TextFileWrapper, file_object: FileIOWrapperBase | TextFileWrapper,
part_urls: list[str], part_urls: list[str],
) -> list[str]: ) -> list[str]:
"""Загружает файл по частям в указанные URL.
Args:
file_object (FileIOWrapperBase | TextFileWrapper): Файловый объект.
part_urls (list[str]): URL-адреса частей для загрузки.
Returns:
list[str]: Список `ETag` загруженных частей.
"""
async with aiohttp.ClientSession( async with aiohttp.ClientSession(
connector=aiohttp.TCPConnector(ssl=False) connector=aiohttp.TCPConnector(ssl=False)
) as session: ) as session:
parts = [] parts = []
async for ix, chunk in a.enumerate( async for ix, chunk in a.enumerate(
read_chunks( read_chunks(file_object=file_object, chunk_size=self.chunk_size)
file_object=file_object, chunk_size=self.chunk_size
)
): ):
async with session.put(url=part_urls[ix], data=chunk) as response: async with session.put(url=part_urls[ix], data=chunk) as response:
response.raise_for_status() response.raise_for_status()
...@@ -57,18 +136,26 @@ class FileUploader: ...@@ -57,18 +136,26 @@ class FileUploader:
file_object: FileIOWrapperBase | TextFileWrapper, file_object: FileIOWrapperBase | TextFileWrapper,
file_name: str, file_name: str,
file_size: int, file_size: int,
collection_id: str collection_id: str,
) -> items_pb2.CreateResponse: ) -> items_pb2.CreateResponse:
"""Загружает файл в Perxis.
Args:
file_object (FileIOWrapperBase | TextFileWrapper): Файловый объект.
file_name (str): Имя файла.
file_size (int): Размер файла в байтах.
collection_id (str): ID коллекции.
Returns:
items_pb2.CreateResponse: Ответ API с информацией о созданном объекте.
"""
message = await self.file_provider.start_upload( message = await self.file_provider.start_upload(
file_name=file_name, file_name=file_name,
file_size=file_size, file_size=file_size,
) )
parts = await self.__put_chunks( parts = await self.__put_chunks(
file_object=file_object, file_object=file_object,
part_urls=message.upload.part_urls, part_urls=message.upload.part_urls,
) )
await self.file_provider.complete_upload( await self.file_provider.complete_upload(
file_id=message.upload.file.id, file_id=message.upload.file.id,
upload_id=message.upload.upload_id, upload_id=message.upload.upload_id,
...@@ -76,14 +163,8 @@ class FileUploader: ...@@ -76,14 +163,8 @@ class FileUploader:
) )
file, data = Struct(), Struct() file, data = Struct(), Struct()
file.update({ file.update({"id": message.upload.file.id, "name": file_name})
"id": message.upload.file.id, data.update({"name": file_name, "file": file})
"name": file_name,
})
data.update({
"name": file_name,
"file": file,
})
message = await self.data_provider.create( message = await self.data_provider.create(
data=data, data=data,
...@@ -93,11 +174,21 @@ class FileUploader: ...@@ -93,11 +174,21 @@ class FileUploader:
async def upload_and_publish( async def upload_and_publish(
self, self,
file_object, file_object: FileIOWrapperBase | TextFileWrapper,
file_name: str, file_name: str,
file_size: int, file_size: int,
collection_id: str collection_id: str,
) -> items_pb2.CreateResponse: ) -> items_pb2.CreateResponse:
"""Загружает файл и публикует его в системе Perxis.
Args:
file_object (FileIOWrapperBase | TextFileWrapper): Файловый объект.
file_name (str): Имя файла.
file_size (int): Размер файла в байтах.
collection_id (str): ID коллекции.
Returns:
items_pb2.CreateResponse: Ответ API с информацией о созданном и опубликованном объекте.
"""
message = await self.upload( message = await self.upload(
file_object=file_object, file_object=file_object,
file_name=file_name, file_name=file_name,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment