Skip to content

Commit 1503fb5

Browse files
chore: migrate development type checking to ty
1 parent 35fc9f3 commit 1503fb5

174 files changed

Lines changed: 1308 additions & 1877 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.castiron.stats.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
schema_version: 1
2-
generation_id: c181b60a-9c3f-4fab-adb0-ec99e1b9164b
2+
generation_id: 5f43d20e-1fa7-4356-a5cf-9ba76ebd71ce
33
openapi_spec_hash: d86a987b0943f67209611629cfe2926f
44
openapi_transformed_spec_hash: f98b6939664e5171dadad31a12ecf50a
55
config_hash: 0e042fd083471fc089bcbfe7011a7d01
6-
codegen_sha: e7d3eec72178efe4bd1c41d2923c5636e5f41500
7-
codegen_hash: 5808ff69b455e321be73db3a9c2640505dddd64b0418583f71301147baa3171d
6+
codegen_sha: eaa28252a89f885820afa16bbd1a753ec589a3c1
7+
codegen_hash: e1d1fbd9e1e10bd7f346d72af1f6a726fbf8c6f7ef655552bfc339f2be788c90

‎.devcontainer/devcontainer.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
"customizations": {
1313
"vscode": {
1414
"extensions": [
15-
"ms-python.python"
15+
"ms-python.python",
16+
"astral-sh.ty"
1617
],
1718
"settings": {
1819
"terminal.integrated.shell.linux": "/bin/bash",
1920
"python.pythonPath": ".venv/bin/python",
2021
"python.defaultInterpreterPath": ".venv/bin/python",
21-
"python.typeChecking": "basic",
2222
"terminal.integrated.env.linux": {
2323
"PATH": "/home/vscode/.local/bin:${env:PATH}"
2424
}

‎.github/workflows/ci.yml‎

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ jobs:
7777
with:
7878
persist-credentials: false
7979

80-
- uses: ./.github/actions/setup-node-tooling
81-
8280
- name: Set up uv
8381
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
8482
with:
@@ -160,8 +158,6 @@ jobs:
160158
- name: Use this job's Playwright installation
161159
run: printf 'PLAYWRIGHT_BROWSERS_PATH=%s/openai-python-playwright\n' "$RUNNER_TEMP" >> "$GITHUB_ENV"
162160

163-
- uses: ./.github/actions/setup-node-tooling
164-
165161
- name: Set up uv
166162
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
167163
with:
@@ -170,11 +166,9 @@ jobs:
170166
enable-cache: true
171167

172168
- name: Check the optional Playwright demo
173-
env:
174-
PYRIGHT_PROJECT: scripts/pyrightconfig.browser-demo.json
175169
run: >-
176-
uv run --locked --all-extras --group browser-demo python -c
177-
'import os, sys; os.execv("./scripts/run-pyright", ["./scripts/run-pyright", "--pythonpath", sys.executable])'
170+
uv run --locked --all-extras --group browser-demo
171+
ty check --extra-search-path examples/browser examples/browser-use-demo
178172
179173
- name: Install Chromium for local demo security checks
180174
run: uv run --locked --all-extras --group browser-demo playwright install --with-deps chromium
@@ -195,7 +189,11 @@ jobs:
195189
strategy:
196190
fail-fast: false
197191
matrix:
198-
provider: [browser, computer]
192+
include:
193+
- provider: browser
194+
directory: examples/browser
195+
- provider: computer
196+
directory: examples/computer-desktop
199197
steps:
200198
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
201199
with:
@@ -204,8 +202,6 @@ jobs:
204202
- name: Use this job's Playwright installation
205203
run: printf 'PLAYWRIGHT_BROWSERS_PATH=%s/openai-python-playwright\n' "$RUNNER_TEMP" >> "$GITHUB_ENV"
206204

207-
- uses: ./.github/actions/setup-node-tooling
208-
209205
- name: Set up uv
210206
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
211207
with:
@@ -214,13 +210,9 @@ jobs:
214210
enable-cache: true
215211

216212
- name: Check optional provider types against this checkout
217-
env:
218-
PYRIGHT_PROJECT: scripts/pyrightconfig.${{ matrix.provider }}-example.json
219213
run: >-
220-
uv run --locked --all-extras --no-default-groups --group dev --group provider-${{ matrix.provider }} python -c
221-
'import os, pathlib, sys; import openai;
222-
assert pathlib.Path(openai.__file__).resolve() == pathlib.Path("src/openai/__init__.py").resolve(), "Provider check replaced this OpenAI checkout";
223-
os.execv("./scripts/run-pyright", ["./scripts/run-pyright", "--pythonpath", sys.executable])'
214+
uv run --locked --all-extras --no-default-groups --group dev --group provider-${{ matrix.provider }}
215+
ty check ${{ matrix.directory }}
224216
225217
- name: Install Chromium for local browser security checks
226218
if: matrix.provider == 'browser'
@@ -253,7 +245,10 @@ jobs:
253245
with:
254246
persist-credentials: false
255247

256-
- uses: ./.github/actions/setup-node-tooling
248+
- name: Set up Node for the mock-server tooling
249+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
250+
with:
251+
node-version-file: .node-version
257252

258253
- name: Set up Python ${{ matrix.python-version }}
259254
id: setup-python
@@ -271,6 +266,9 @@ jobs:
271266
- name: Bootstrap
272267
run: ./scripts/bootstrap
273268

269+
- name: Install the pinned mock-server tooling
270+
run: ./scripts/steady/install
271+
274272
- name: Check concurrent Steady publication
275273
run: node scripts/steady/publication.test.cjs
276274

‎CONTRIBUTING.md‎

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,39 +27,42 @@ must include a release note naming the final compatible SDK release.
2727
### With uv
2828

2929
We use [uv](https://docs.astral.sh/uv/) to manage Python, dependencies, and builds.
30-
Install uv 0.12.1 or newer, Node.js at the version in `.node-version`, and
31-
the exact pnpm version in `package.json` (`npm install --global pnpm@11.19.0`).
32-
The Node toolchain is for repository development only, not SDK users or builds.
33-
Then run:
30+
Install uv 0.12.1 or newer. To run the runtime tests, also install Node.js at
31+
the version in `.node-version` for the mock-server tooling. Then run:
3432

3533
```sh
3634
$ ./scripts/bootstrap
3735
```
3836

39-
This installs all SDK extras and Python development tools from `uv.lock`, plus
40-
the contributor tools from `pnpm-lock.yaml`. Bootstrap requires an already
41-
installed Node.js and pnpm; it does not download or switch those tools for you.
42-
Run a
43-
command with `uv run --locked --all-extras`, or activate `.venv` in your shell.
37+
This installs all SDK extras and Python development tools from `uv.lock`.
38+
Run a command with `uv run --locked --all-extras`, or activate `.venv` in your shell.
4439
Use `uv lock` after changing dependencies, and commit the updated `uv.lock`.
4540
It is the authoritative lockfile; `uv export` can produce a requirements file
4641
for tools that need one.
4742

48-
For Node tooling updates, edit `package.json`, run
49-
`pnpm install --lockfile-only --ignore-scripts`, review the dependency and
50-
integrity changes, and commit `pnpm-lock.yaml`. Normal bootstrap uses a frozen
51-
lockfile. pnpm rejects releases that have not cleared the eight-day cooldown,
52-
including packages whose registry metadata omits their publication date.
53-
Pyright runs the local Microsoft package and never invokes npm or
54-
downloads a runtime. Do not use `npx`, `pnpm dlx`, or Corepack auto-downloads.
43+
The separate breaking-change CI job checks previous-version callers with Pyright.
44+
To run that compatibility gate locally, install Node.js at the version in
45+
`.node-version` and the exact pnpm version in `package.json`, then run
46+
`./scripts/bootstrap-node`. Pyright and pnpm are not required for normal bootstrap or lint. Runtime tests
47+
still require Node.js for the Steady mock-server tooling. Before running them, install
48+
the pinned mock server and runtime:
49+
50+
```sh
51+
$ ./scripts/steady/install
52+
```
53+
54+
Node tooling uses the frozen `pnpm-lock.yaml`, an eight-day release cooldown,
55+
and disabled lifecycle scripts. Update it with `pnpm install --lockfile-only
56+
--ignore-scripts` and review the integrity changes. Do not use `npx`, `pnpm dlx`,
57+
or Corepack auto-downloads to run compatibility checks.
5558

5659
The default environment uses Pydantic v2. `./scripts/test` also runs the
5760
Pydantic-v1 suite in a separate, locked environment. To run that lane alone,
5861
use `./scripts/test-pydantic-v1`.
5962

6063
### Dependency update policy
6164

62-
Routine Python, Node-tooling, and GitHub Actions updates have an eight-day cooldown. uv also
65+
Routine Python, compatibility-checker, and GitHub Actions updates have an eight-day cooldown. uv also
6366
excludes Python distribution artifacts uploaded within the last eight days.
6467
Dependabot security updates remain enabled and are exempt from Dependabot's
6568
version-update cooldown.
@@ -209,9 +212,9 @@ installation, integrity checks, and mock-server lifecycle.
209212

210213
Most tests require you to [set up a mock server](https://github.com/openai-oss-forks/steady) against the OpenAPI spec to run the tests.
211214

212-
`./scripts/bootstrap` installs the pinned Steady source and Deno runtime. `./scripts/test` starts it automatically when no mock server is running.
215+
`./scripts/steady/install` installs the pinned Steady source and Deno runtime. `./scripts/test` starts it automatically when no mock server is running.
213216
Neither `./scripts/test` nor `./scripts/mock` downloads mock tooling. If the local
214-
tool is missing, rerun bootstrap. `TEST_API_BASE_URL` and an already-running
217+
tool is missing, rerun `./scripts/steady/install`. `TEST_API_BASE_URL` and an already-running
215218
mock server remain supported. To start the server yourself:
216219

217220
```sh
@@ -224,7 +227,13 @@ $ ./scripts/test
224227

225228
## Linting and formatting
226229

227-
This repository uses [Ruff](https://github.com/astral-sh/ruff) to lint and format Python code.
230+
This repository uses [Ruff](https://github.com/astral-sh/ruff) to lint and format Python code,
231+
and [ty](https://docs.astral.sh/ty/) to typecheck it. These are development tools;
232+
installing the SDK does not install a typechecker or require consumers to switch checkers.
233+
Consumers use their own checker and configuration. Keep public annotations, overloads,
234+
exports, and `py.typed` compatible across checkers; repository-only checker settings
235+
are not part of that contract. Preserve compatibility with both supported Pydantic
236+
major versions and the Python versions covered by CI.
228237

229238
To lint:
230239

‎examples/audio.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def main() -> None:
2929
# Legacy translation endpoint: shuts down February 26, 2027.
3030
# New integrations should use Realtime translations instead:
3131
# https://developers.openai.com/api/docs/deprecations
32-
translation = openai.audio.translations.create( # pyright: ignore[reportDeprecated]
32+
translation = openai.audio.translations.create( # ty: ignore[deprecated]
3333
model="whisper-1",
3434
file=speech_file_path,
3535
)

‎examples/browser-use-demo/booking.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ async def read_page(self, args: ReadPageInput, *, cancelled: Callable[[], bool]
9999
await handle.dispose()
100100
continue
101101
ref = secrets.token_hex(12)
102+
item: BrowserReadElement
102103
if role == "textbox":
103-
item: BrowserReadElement = {
104+
item = {
104105
"ref": ref,
105106
"role": "textbox",
106107
"label": label,

‎examples/browser-use-demo/exercise.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ async def hold_verified(*, cancelled: Callable[[], bool] | None = None) -> None:
247247
await asyncio.wait_for(release.wait(), timeout=5)
248248
await original_capture(cancelled=cancelled)
249249

250-
current.capture = hold_verified
250+
current.capture = hold_verified # ty: ignore[invalid-assignment] # Test replaces a callable with a controlled fixture.
251251
try:
252252
await post("/api/approval", {**binding(offered), "approvalId": offered["approval"]["id"], "approved": True})
253253
await asyncio.wait_for(entered.wait(), timeout=5)
@@ -277,7 +277,7 @@ async def hold_verified(*, cancelled: Callable[[], bool] | None = None) -> None:
277277
assert stopped["result"] == "unverified", "The old agent result survived human editing and Stop"
278278
finally:
279279
release.set()
280-
current.capture = original_capture
280+
current.capture = original_capture # ty: ignore[invalid-assignment] # Test replaces a callable with a controlled fixture.
281281
await post("/api/start", {"taskId": "reservation", "api": "decisions", "mode": "rehearsal"})
282282
offered = await until(lambda s: s["approval"] is not None)
283283
await post("/api/take-control", binding(offered))

‎examples/computer-desktop/guest/install.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ def install(root: Path) -> None:
108108
b"BP22-LATE-EFI-PASSED",
109109
b"BP22-LATE-COMPLETE",
110110
)
111-
if reason or process.returncode or not all(value in outputs["stdout"] for value in checks):
112-
raise RuntimeError(reason or "Installer did not complete its fixed guest checks.")
111+
if process.returncode or not all(value in outputs["stdout"] for value in checks):
112+
raise RuntimeError("Installer did not complete its fixed guest checks.")
113113
finally:
114114
if process is not None:
115115
if process.poll() is None:

‎examples/computer-desktop/guest/setup.py‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
import subprocess
1717
import urllib.parse
1818
import urllib.request
19-
from typing import NamedTuple
19+
from typing import IO, NamedTuple
2020
from pathlib import Path
21+
from http.client import HTTPMessage
2122

2223
HERE = Path(__file__).resolve().parent
2324
# This trusted checkout path also works when the verifier is started with python -I -S.
@@ -90,9 +91,8 @@ class Media(NamedTuple):
9091

9192

9293
class OfficialRedirects(urllib.request.HTTPRedirectHandler):
93-
# This Python 3.10 bootstrap uses only the standard library, which has no override decorator.
94-
def redirect_request( # pyright: ignore[reportImplicitOverride]
95-
self, req: urllib.request.Request, fp: object, code: int, msg: str, headers: object, newurl: str
94+
def redirect_request(
95+
self, req: urllib.request.Request, fp: IO[bytes], code: int, msg: str, headers: HTTPMessage, newurl: str
9696
) -> urllib.request.Request | None:
9797
url = urllib.parse.urlsplit(newurl)
9898
if (
@@ -103,7 +103,7 @@ def redirect_request( # pyright: ignore[reportImplicitOverride]
103103
or url.password is not None
104104
):
105105
raise ValueError("Debian redirected outside the fixed official download hosts.")
106-
return super().redirect_request(req, fp, code, msg, headers, newurl) # type: ignore[arg-type]
106+
return super().redirect_request(req, fp, code, msg, headers, newurl)
107107

108108

109109
def digest(path: Path, algorithm: str = "sha256") -> str:

‎examples/computer-desktop/guest/test_action_admission.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def controlled_reply(request: object) -> dict[str, object]:
4848
raise TimeoutError("Fixture read was not released.")
4949
return {"ok": True}
5050

51-
action.validate = recorded_validate
52-
action.reply = controlled_reply
51+
action.validate = recorded_validate # ty: ignore[invalid-assignment] # Test replaces a callable with a controlled fixture.
52+
action.reply = controlled_reply # ty: ignore[invalid-assignment] # Test replaces a callable with a controlled fixture.
5353
action.main()
5454

5555

0 commit comments

Comments
 (0)