mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Add Bazel targets for on-demand schemas and Python SDK generation (#54096)
## What changed - Add `//bazel/schema:app-server` to generate stable and experimental schemas from Rust sources without depending on committed precomputed bundles. - Add `//sdk/python:generated` to generate Python SDK models, the notification registry, and API methods from the stable schema in scratch space, using dependencies from `uv.lock`. - Add `generate-types --no-format` to skip formatters for build outputs while preserving formatting by default. ## Testing Extend contract generation tests to regenerate in a temporary SDK copy, checking byte equality for formatted output and AST equality for unformatted output. GitOrigin-RevId: 9ca7daa8ac0a33c4106a9e95c2f48bbded07df21
This commit is contained in:
committed by
copyberry
parent
12fd621f62
commit
bc13ff239c
@@ -14,6 +14,7 @@ import tempfile
|
||||
import types
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Sequence, get_args, get_origin
|
||||
|
||||
@@ -545,7 +546,7 @@ def _normalized_schema_bundle_text(schema_dir: Path) -> str:
|
||||
return json.dumps(schema, indent=2, sort_keys=True) + "\n"
|
||||
|
||||
|
||||
def generate_v2_all(schema_dir: Path) -> None:
|
||||
def generate_v2_all(schema_dir: Path, *, format_output: bool = True) -> None:
|
||||
"""Regenerate the Pydantic v2 protocol model module from app-server schemas."""
|
||||
out_path = sdk_root() / "src" / "openai_codex" / "generated" / "v2_all.py"
|
||||
out_dir = out_path.parent
|
||||
@@ -556,6 +557,9 @@ def generate_v2_all(schema_dir: Path) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
normalized_bundle = Path(td) / schema_bundle_path(schema_dir).name
|
||||
normalized_bundle.write_text(_normalized_schema_bundle_text(schema_dir))
|
||||
if not format_output:
|
||||
# The pinned generator accepts an empty formatter list through TOML.
|
||||
(Path(td) / "pyproject.toml").write_text("[tool.datamodel-codegen]\nformatters = []\n")
|
||||
run_python_module(
|
||||
"datamodel_code_generator",
|
||||
[
|
||||
@@ -582,18 +586,13 @@ def generate_v2_all(schema_dir: Path) -> None:
|
||||
"--use-annotated",
|
||||
"--use-union-operator",
|
||||
"--disable-timestamp",
|
||||
# Keep the generated file formatted deterministically so the
|
||||
# checked-in artifact only changes when the schema does.
|
||||
"--formatters",
|
||||
"ruff-format",
|
||||
],
|
||||
cwd=sdk_root(),
|
||||
]
|
||||
+ (["--formatters", "ruff-format"] if format_output else []),
|
||||
cwd=sdk_root() if format_output else Path(td),
|
||||
)
|
||||
_preserve_inline_image_class_names(out_path)
|
||||
_require_nullable_field(out_path, "ChatgptAccount", r"email: str \| None")
|
||||
_require_nullable_field(
|
||||
out_path, "McpResourceReadTarget", r"link_id: Annotated\[\n(?: .*\n)+ \]"
|
||||
)
|
||||
_require_nullable_field(out_path, "McpResourceReadTarget", r"link_id: Annotated\[[\s\S]*?\]")
|
||||
_preserve_open_enum(out_path, "CodexErrorInfoValue", allow_empty=True)
|
||||
_preserve_reasoning_effort_enum(out_path)
|
||||
_preserve_thread_source_enum(out_path)
|
||||
@@ -1351,7 +1350,7 @@ def _render_thread_block(turn_fields: list[PublicFieldSpec], *, is_async: bool =
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generate_public_api_flat_methods() -> None:
|
||||
def generate_public_api_flat_methods(*, format_output: bool = True) -> None:
|
||||
"""Regenerate the public convenience methods from generated protocol models."""
|
||||
src_dir = sdk_root() / "src"
|
||||
public_api_path = src_dir / "openai_codex" / "api.py"
|
||||
@@ -1400,15 +1399,16 @@ def generate_public_api_flat_methods() -> None:
|
||||
_render_thread_block(turn_start_fields, is_async=True),
|
||||
)
|
||||
public_api_path.write_text(source)
|
||||
run_python_module("ruff", ["format", str(public_api_path)], cwd=sdk_root())
|
||||
if format_output:
|
||||
run_python_module("ruff", ["format", str(public_api_path)], cwd=sdk_root())
|
||||
|
||||
|
||||
def generate_types_from_schema_dir(schema_dir: Path) -> None:
|
||||
def generate_types_from_schema_dir(schema_dir: Path, *, format_output: bool = True) -> None:
|
||||
"""Regenerate every SDK artifact derived from an existing schema directory."""
|
||||
# v2_all is the authoritative generated surface.
|
||||
generate_v2_all(schema_dir)
|
||||
generate_v2_all(schema_dir, format_output=format_output)
|
||||
generate_notification_registry(schema_dir)
|
||||
generate_public_api_flat_methods()
|
||||
generate_public_api_flat_methods(format_output=format_output)
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
@@ -1423,6 +1423,12 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
type=Path,
|
||||
help="App-server JSON schema directory (defaults to tool.codex.codegen.schema-dir)",
|
||||
)
|
||||
generate_types_parser.add_argument(
|
||||
"--no-format",
|
||||
dest="format_output",
|
||||
action="store_false",
|
||||
help="Generate build outputs without running formatters",
|
||||
)
|
||||
|
||||
stage_sdk_parser = subparsers.add_parser(
|
||||
"stage-sdk",
|
||||
@@ -1483,9 +1489,13 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
||||
return build_parser().parse_args(list(argv) if argv is not None else None)
|
||||
|
||||
|
||||
def default_cli_ops() -> CliOps:
|
||||
def default_cli_ops(*, format_output: bool = True) -> CliOps:
|
||||
return CliOps(
|
||||
generate_types=generate_types_from_schema_dir,
|
||||
generate_types=(
|
||||
generate_types_from_schema_dir
|
||||
if format_output
|
||||
else partial(generate_types_from_schema_dir, format_output=False)
|
||||
),
|
||||
stage_python_sdk_package=stage_python_sdk_package,
|
||||
stage_python_runtime_package=stage_python_runtime_package,
|
||||
)
|
||||
@@ -1520,7 +1530,7 @@ def run_command(args: argparse.Namespace, ops: CliOps) -> None:
|
||||
|
||||
def main(argv: Sequence[str] | None = None, ops: CliOps | None = None) -> None:
|
||||
args = parse_args(argv)
|
||||
run_command(args, ops or default_cli_ops())
|
||||
run_command(args, ops or default_cli_ops(format_output=getattr(args, "format_output", True)))
|
||||
print("Done.")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user