mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[2/8] Generate Python SDK types from pinned runtime (#21893)
## Why Once the SDK declares its runtime package, generated Python artifacts should come from that pinned runtime rather than whatever app-server schema happens to be in the current checkout. That keeps the generated API and model surface aligned with the runtime users install. ## What - Teach `scripts/update_sdk_artifacts.py generate-types` to invoke the pinned runtime package for schema generation. - Regenerate `v2_all.py`, `notification_registry.py`, and generated public wrapper methods from that schema. - Add freshness coverage so regenerating from the pinned runtime must leave checked-in artifacts unchanged. ## Stack 1. #21891 `[1/8]` Pin Python SDK runtime dependency 2. This PR `[2/8]` Generate Python SDK types from pinned runtime 3. #21895 `[3/8]` Run Python SDK tests in CI 4. #21896 `[4/8]` Define Python SDK public API surface 5. #21905 `[5/8]` Rename Python SDK package to `openai-codex` 6. #21910 `[6/8]` Add high-level Python SDK approval mode 7. #22014 `[7/8]` Add Python SDK app-server integration harness 8. #22021 `[8/8]` Add Python SDK Ruff formatting ## Verification - Added `test_generated_files_are_up_to_date` for pinned-runtime generation drift. - Added generator-structure tests for schema annotation and notification metadata generation. --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -16,6 +16,7 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _load_update_script_module():
|
||||
"""Load the maintenance script as a module so tests exercise real helpers."""
|
||||
script_path = ROOT / "scripts" / "update_sdk_artifacts.py"
|
||||
spec = importlib.util.spec_from_file_location("update_sdk_artifacts", script_path)
|
||||
if spec is None or spec.loader is None:
|
||||
@@ -27,6 +28,7 @@ def _load_update_script_module():
|
||||
|
||||
|
||||
def _load_runtime_setup_module():
|
||||
"""Load runtime setup without importing the SDK package under test."""
|
||||
runtime_setup_path = ROOT / "_runtime_setup.py"
|
||||
spec = importlib.util.spec_from_file_location("_runtime_setup", runtime_setup_path)
|
||||
if spec is None or spec.loader is None:
|
||||
@@ -40,11 +42,13 @@ def _load_runtime_setup_module():
|
||||
|
||||
|
||||
def test_generation_has_single_maintenance_entrypoint_script() -> None:
|
||||
"""Keep artifact workflows routed through one script instead of side entrypoints."""
|
||||
scripts = sorted(p.name for p in (ROOT / "scripts").glob("*.py"))
|
||||
assert scripts == ["update_sdk_artifacts.py"]
|
||||
|
||||
|
||||
def test_generate_types_wires_all_generation_steps() -> None:
|
||||
"""The type generation command should refresh every schema-derived artifact."""
|
||||
source = (ROOT / "scripts" / "update_sdk_artifacts.py").read_text()
|
||||
tree = ast.parse(source)
|
||||
|
||||
@@ -52,7 +56,8 @@ def test_generate_types_wires_all_generation_steps() -> None:
|
||||
(
|
||||
node
|
||||
for node in tree.body
|
||||
if isinstance(node, ast.FunctionDef) and node.name == "generate_types"
|
||||
if isinstance(node, ast.FunctionDef)
|
||||
and node.name == "generate_types_from_schema_dir"
|
||||
),
|
||||
None,
|
||||
)
|
||||
@@ -72,19 +77,19 @@ def test_generate_types_wires_all_generation_steps() -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_schema_normalization_only_flattens_string_literal_oneofs() -> None:
|
||||
def _load_runtime_schema_bundle(tmp_path: Path) -> dict:
|
||||
"""Ask the pinned runtime package for a real schema bundle used by tests."""
|
||||
script = _load_update_script_module()
|
||||
schema = json.loads(
|
||||
(
|
||||
ROOT.parent.parent
|
||||
/ "codex-rs"
|
||||
/ "app-server-protocol"
|
||||
/ "schema"
|
||||
/ "json"
|
||||
/ "codex_app_server_protocol.v2.schemas.json"
|
||||
).read_text()
|
||||
)
|
||||
schema_dir = script.generate_schema_from_pinned_runtime(tmp_path / "schema")
|
||||
return json.loads(script.schema_bundle_path(schema_dir).read_text())
|
||||
|
||||
|
||||
def test_schema_normalization_only_flattens_string_literal_oneofs(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Schema normalization should only flatten the enum-shaped oneOf variants."""
|
||||
script = _load_update_script_module()
|
||||
schema = _load_runtime_schema_bundle(tmp_path)
|
||||
definitions = schema["definitions"]
|
||||
flattened = [
|
||||
name
|
||||
@@ -94,27 +99,23 @@ def test_schema_normalization_only_flattens_string_literal_oneofs() -> None:
|
||||
]
|
||||
|
||||
assert flattened == [
|
||||
"AuthMode",
|
||||
"CommandExecOutputStream",
|
||||
"ExperimentalFeatureStage",
|
||||
"InputModality",
|
||||
"MessagePhase",
|
||||
"TurnItemsView",
|
||||
"PluginAvailability",
|
||||
"AuthMode",
|
||||
"InputModality",
|
||||
"ExperimentalFeatureStage",
|
||||
"CommandExecOutputStream",
|
||||
"ProcessOutputStream",
|
||||
]
|
||||
|
||||
|
||||
def test_python_codegen_schema_annotation_adds_stable_variant_titles() -> None:
|
||||
def test_python_codegen_schema_annotation_adds_stable_variant_titles(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Schema annotations should give generated protocol classes stable names."""
|
||||
script = _load_update_script_module()
|
||||
schema = json.loads(
|
||||
(
|
||||
ROOT.parent.parent
|
||||
/ "codex-rs"
|
||||
/ "app-server-protocol"
|
||||
/ "schema"
|
||||
/ "json"
|
||||
/ "codex_app_server_protocol.v2.schemas.json"
|
||||
).read_text()
|
||||
)
|
||||
|
||||
schema = _load_runtime_schema_bundle(tmp_path)
|
||||
script._annotate_schema(schema)
|
||||
definitions = schema["definitions"]
|
||||
|
||||
@@ -186,6 +187,25 @@ def test_runtime_distribution_name_is_consistent() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_source_sdk_package_pins_published_runtime() -> None:
|
||||
"""The source package metadata should pin the runtime wheel that ships schemas."""
|
||||
script = _load_update_script_module()
|
||||
pyproject = tomllib.loads((ROOT / "pyproject.toml").read_text())
|
||||
|
||||
assert {
|
||||
"sdk_version": pyproject["project"]["version"],
|
||||
"runtime_pin": script.pinned_runtime_version(),
|
||||
"dependencies": pyproject["project"]["dependencies"],
|
||||
} == {
|
||||
"sdk_version": "0.131.0a4",
|
||||
"runtime_pin": "0.131.0a4",
|
||||
"dependencies": [
|
||||
"pydantic>=2.12",
|
||||
"openai-codex-cli-bin==0.131.0a4",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_release_metadata_retries_without_invalid_auth(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
@@ -422,9 +442,7 @@ def test_runtime_resource_binaries_are_included_by_wheel_config(
|
||||
pyproject = tomllib.loads((staged / "pyproject.toml").read_text())
|
||||
assert {
|
||||
"include": pyproject["tool"]["hatch"]["build"]["targets"]["wheel"]["include"],
|
||||
"helper": (
|
||||
staged / "src" / "codex_cli_bin" / "bin" / "helper"
|
||||
).read_text(),
|
||||
"helper": (staged / "src" / "codex_cli_bin" / "bin" / "helper").read_text(),
|
||||
} == {
|
||||
"include": ["src/codex_cli_bin/bin/**"],
|
||||
"helper": "fake helper\n",
|
||||
|
||||
Reference in New Issue
Block a user