main
1[project]
2name = "openai"
3version = "2.9.0"
4description = "The official Python library for the openai API"
5dynamic = ["readme"]
6license = "Apache-2.0"
7authors = [
8{ name = "OpenAI", email = "support@openai.com" },
9]
10
11dependencies = [
12 "httpx>=0.23.0, <1",
13 "pydantic>=1.9.0, <3",
14 "typing-extensions>=4.11, <5",
15 "anyio>=3.5.0, <5",
16 "distro>=1.7.0, <2",
17 "sniffio",
18 "tqdm > 4",
19 "jiter>=0.10.0, <1",
20]
21
22requires-python = ">= 3.9"
23classifiers = [
24 "Typing :: Typed",
25 "Intended Audience :: Developers",
26 "Programming Language :: Python :: 3.9",
27 "Programming Language :: Python :: 3.10",
28 "Programming Language :: Python :: 3.11",
29 "Programming Language :: Python :: 3.12",
30 "Programming Language :: Python :: 3.13",
31 "Programming Language :: Python :: 3.14",
32 "Operating System :: OS Independent",
33 "Operating System :: POSIX",
34 "Operating System :: MacOS",
35 "Operating System :: POSIX :: Linux",
36 "Operating System :: Microsoft :: Windows",
37 "Topic :: Software Development :: Libraries :: Python Modules",
38 "License :: OSI Approved :: Apache Software License"
39]
40
41[project.urls]
42Homepage = "https://github.com/openai/openai-python"
43Repository = "https://github.com/openai/openai-python"
44
45[project.scripts]
46openai = "openai.cli:main"
47
48[project.optional-dependencies]
49aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"]
50realtime = ["websockets >= 13, < 16"]
51datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"]
52voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"]
53
54[tool.rye]
55managed = true
56# version pins are in requirements-dev.lock
57dev-dependencies = [
58 "pyright==1.1.399",
59 "mypy==1.17",
60 "respx",
61 "pytest",
62 "pytest-asyncio",
63 "ruff",
64 "time-machine",
65 "nox",
66 "dirty-equals>=0.6.0",
67 "importlib-metadata>=6.7.0",
68 "rich>=13.7.1",
69 "inline-snapshot>=0.28.0",
70 "azure-identity >=1.14.1",
71 "types-tqdm > 4",
72 "types-pyaudio > 0",
73 "trio >=0.22.2",
74 "nest_asyncio==1.6.0",
75 "pytest-xdist>=3.6.1",
76 "griffe>=1",
77]
78
79[tool.rye.scripts]
80format = { chain = [
81 "format:ruff",
82 "format:docs",
83 "fix:ruff",
84 # run formatting again to fix any inconsistencies when imports are stripped
85 "format:ruff",
86]}
87"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md"
88"format:ruff" = "ruff format"
89
90"lint" = { chain = [
91 "check:ruff",
92 "typecheck",
93 "check:importable",
94]}
95"check:ruff" = "ruff check ."
96"fix:ruff" = "ruff check --fix ."
97
98"check:importable" = "python -c 'import openai'"
99
100typecheck = { chain = [
101 "typecheck:pyright",
102 "typecheck:mypy"
103]}
104"typecheck:pyright" = "pyright"
105"typecheck:verify-types" = "pyright --verifytypes openai --ignoreexternal"
106"typecheck:mypy" = "mypy ."
107
108[build-system]
109requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"]
110build-backend = "hatchling.build"
111
112[tool.hatch.build]
113include = [
114 "src/*"
115]
116
117[tool.hatch.build.targets.wheel]
118packages = ["src/openai"]
119
120[tool.hatch.build.targets.sdist]
121# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc)
122include = [
123 "/*.toml",
124 "/*.json",
125 "/*.lock",
126 "/*.md",
127 "/mypy.ini",
128 "/noxfile.py",
129 "bin/*",
130 "examples/*",
131 "src/*",
132 "tests/*",
133]
134
135[tool.hatch.metadata.hooks.fancy-pypi-readme]
136content-type = "text/markdown"
137
138[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
139path = "README.md"
140
141[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
142# replace relative links with absolute links
143pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
144replacement = '[\1](https://github.com/openai/openai-python/tree/main/\g<2>)'
145
146[tool.pytest.ini_options]
147testpaths = ["tests"]
148addopts = "--tb=short -n auto"
149xfail_strict = true
150asyncio_mode = "auto"
151asyncio_default_fixture_loop_scope = "session"
152filterwarnings = [
153 "error"
154]
155
156[tool.inline-snapshot]
157format-command="ruff format --stdin-filename {filename}"
158
159[tool.pyright]
160# this enables practically every flag given by pyright.
161# there are a couple of flags that are still disabled by
162# default in strict mode as they are experimental and niche.
163typeCheckingMode = "strict"
164pythonVersion = "3.9"
165
166exclude = [
167 "_dev",
168 ".venv",
169 ".nox",
170 ".git",
171
172 # uses inline `uv` script dependencies
173 # which means it can't be type checked
174 "examples/realtime/audio_util.py",
175 "examples/realtime/push_to_talk_app.py"
176]
177
178reportImplicitOverride = true
179reportOverlappingOverload = false
180
181reportImportCycles = false
182reportPrivateUsage = false
183
184[tool.mypy]
185pretty = true
186show_error_codes = true
187
188# Exclude _files.py because mypy isn't smart enough to apply
189# the correct type narrowing and as this is an internal module
190# it's fine to just use Pyright.
191#
192# We also exclude our `tests` as mypy doesn't always infer
193# types correctly and Pyright will still catch any type errors.
194#
195# realtime examples use inline `uv` script dependencies
196# which means it can't be type checked
197exclude = [
198 'src/openai/_files.py',
199 '_dev/.*.py',
200 'tests/.*',
201 'src/openai/_utils/_logs.py',
202 'examples/realtime/audio_util.py',
203 'examples/realtime/push_to_talk_app.py',
204]
205
206strict_equality = true
207implicit_reexport = true
208check_untyped_defs = true
209no_implicit_optional = true
210
211warn_return_any = true
212warn_unreachable = true
213warn_unused_configs = true
214
215# Turn these options off as it could cause conflicts
216# with the Pyright options.
217warn_unused_ignores = false
218warn_redundant_casts = false
219
220disallow_any_generics = true
221disallow_untyped_defs = true
222disallow_untyped_calls = true
223disallow_subclassing_any = true
224disallow_incomplete_defs = true
225disallow_untyped_decorators = true
226cache_fine_grained = true
227
228# By default, mypy reports an error if you assign a value to the result
229# of a function call that doesn't return anything. We do this in our test
230# cases:
231# ```
232# result = ...
233# assert result is None
234# ```
235# Changing this codegen to make mypy happy would increase complexity
236# and would not be worth it.
237disable_error_code = "func-returns-value,overload-cannot-match"
238
239# https://github.com/python/mypy/issues/12162
240[[tool.mypy.overrides]]
241module = "black.files.*"
242ignore_errors = true
243ignore_missing_imports = true
244
245[tool.ruff]
246line-length = 120
247output-format = "grouped"
248target-version = "py38"
249
250[tool.ruff.format]
251docstring-code-format = true
252
253[tool.ruff.lint]
254select = [
255 # isort
256 "I",
257 # bugbear rules
258 "B",
259 # remove unused imports
260 "F401",
261 # check for missing future annotations
262 "FA102",
263 # bare except statements
264 "E722",
265 # unused arguments
266 "ARG",
267 # print statements
268 "T201",
269 "T203",
270 # misuse of typing.TYPE_CHECKING
271 "TC004",
272 # import rules
273 "TID251",
274]
275ignore = [
276 # mutable defaults
277 "B006",
278]
279unfixable = [
280 # disable auto fix for print statements
281 "T201",
282 "T203",
283]
284
285extend-safe-fixes = ["FA102"]
286
287[tool.ruff.lint.flake8-tidy-imports.banned-api]
288"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead"
289
290[tool.ruff.lint.isort]
291length-sort = true
292length-sort-straight = true
293combine-as-imports = true
294extra-standard-library = ["typing_extensions"]
295known-first-party = ["openai", "tests"]
296
297[tool.ruff.lint.per-file-ignores]
298"bin/**.py" = ["T201", "T203"]
299"scripts/**.py" = ["T201", "T203"]
300"tests/**.py" = ["T201", "T203"]
301"examples/**.py" = ["T201", "T203"]