openai/openai-python

Public

mirrored from https://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
84c85bdffde6bdfe8fd152ba5eec371e9260d0a8

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/openai/_files.py

122lines · modecode

1from __future__ import annotations
2
3import io
4import os
5import pathlib
6from typing import overload
7from typing_extensions import TypeGuard
8
9import anyio
10
11from ._types import (
12 FileTypes,
13 FileContent,
14 RequestFiles,
15 HttpxFileTypes,
16 HttpxFileContent,
17 HttpxRequestFiles,
18)
19from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
20
21
22def is_file_content(obj: object) -> TypeGuard[FileContent]:
23 return (
24 isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
25 )
26
27
28def assert_is_file_content(obj: object, *, key: str | None = None) -> None:
29 if not is_file_content(obj):
30 prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`"
31 raise RuntimeError(
32 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"
33 ) from None
34
35
36@overload
37def to_httpx_files(files: None) -> None:
38 ...
39
40
41@overload
42def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles:
43 ...
44
45
46def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None:
47 if files is None:
48 return None
49
50 if is_mapping_t(files):
51 files = {key: _transform_file(file) for key, file in files.items()}
52 elif is_sequence_t(files):
53 files = [(key, _transform_file(file)) for key, file in files]
54 else:
55 raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence")
56
57 return files
58
59
60def _transform_file(file: FileTypes) -> HttpxFileTypes:
61 if is_file_content(file):
62 if isinstance(file, os.PathLike):
63 path = pathlib.Path(file)
64 return (path.name, path.read_bytes())
65
66 return file
67
68 if is_tuple_t(file):
69 return (file[0], _read_file_content(file[1]), *file[2:])
70
71 raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
72
73
74def _read_file_content(file: FileContent) -> HttpxFileContent:
75 if isinstance(file, os.PathLike):
76 return pathlib.Path(file).read_bytes()
77 return file
78
79
80@overload
81async def async_to_httpx_files(files: None) -> None:
82 ...
83
84
85@overload
86async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles:
87 ...
88
89
90async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None:
91 if files is None:
92 return None
93
94 if is_mapping_t(files):
95 files = {key: await _async_transform_file(file) for key, file in files.items()}
96 elif is_sequence_t(files):
97 files = [(key, await _async_transform_file(file)) for key, file in files]
98 else:
99 raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence")
100
101 return files
102
103
104async def _async_transform_file(file: FileTypes) -> HttpxFileTypes:
105 if is_file_content(file):
106 if isinstance(file, os.PathLike):
107 path = anyio.Path(file)
108 return (path.name, await path.read_bytes())
109
110 return file
111
112 if is_tuple_t(file):
113 return (file[0], await _async_read_file_content(file[1]), *file[2:])
114
115 raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
116
117
118async def _async_read_file_content(file: FileContent) -> HttpxFileContent:
119 if isinstance(file, os.PathLike):
120 return await anyio.Path(file).read_bytes()
121
122 return file
123