| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- from __future__ import annotations
- import io
- import os
- import pathlib
- from typing import overload
- from typing_extensions import TypeGuard
- import anyio
- from ._types import (
- FileTypes,
- FileContent,
- RequestFiles,
- HttpxFileTypes,
- Base64FileInput,
- HttpxFileContent,
- HttpxRequestFiles,
- )
- from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
- def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
- return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
- def is_file_content(obj: object) -> TypeGuard[FileContent]:
- return (
- isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
- )
- def assert_is_file_content(obj: object, *, key: str | None = None) -> None:
- if not is_file_content(obj):
- prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`"
- raise RuntimeError(
- f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/main#file-uploads"
- ) from None
- @overload
- def to_httpx_files(files: None) -> None: ...
- @overload
- def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ...
- def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None:
- if files is None:
- return None
- if is_mapping_t(files):
- files = {key: _transform_file(file) for key, file in files.items()}
- elif is_sequence_t(files):
- files = [(key, _transform_file(file)) for key, file in files]
- else:
- raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence")
- return files
- def _transform_file(file: FileTypes) -> HttpxFileTypes:
- if is_file_content(file):
- if isinstance(file, os.PathLike):
- path = pathlib.Path(file)
- return (path.name, path.read_bytes())
- return file
- if is_tuple_t(file):
- return (file[0], read_file_content(file[1]), *file[2:])
- raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
- def read_file_content(file: FileContent) -> HttpxFileContent:
- if isinstance(file, os.PathLike):
- return pathlib.Path(file).read_bytes()
- return file
- @overload
- async def async_to_httpx_files(files: None) -> None: ...
- @overload
- async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ...
- async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None:
- if files is None:
- return None
- if is_mapping_t(files):
- files = {key: await _async_transform_file(file) for key, file in files.items()}
- elif is_sequence_t(files):
- files = [(key, await _async_transform_file(file)) for key, file in files]
- else:
- raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence")
- return files
- async def _async_transform_file(file: FileTypes) -> HttpxFileTypes:
- if is_file_content(file):
- if isinstance(file, os.PathLike):
- path = anyio.Path(file)
- return (path.name, await path.read_bytes())
- return file
- if is_tuple_t(file):
- return (file[0], await async_read_file_content(file[1]), *file[2:])
- raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
- async def async_read_file_content(file: FileContent) -> HttpxFileContent:
- if isinstance(file, os.PathLike):
- return await anyio.Path(file).read_bytes()
- return file
|