From 6e10973c78c7464ed7894b5e5171cd554254acfc Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Sat, 9 May 2026 10:03:34 +0300 Subject: [PATCH] Pin Python SDK runtime dependency Make the Python SDK declare its published runtime package dependency directly and resolve the runtime version from that pin instead of inferring it from the SDK package version. Co-authored-by: Codex --- sdk/python/_runtime_setup.py | 35 +++++++++++++------ sdk/python/examples/README.md | 2 +- sdk/python/pyproject.toml | 6 ++-- .../test_artifact_workflow_and_binaries.py | 21 ++++++++++- sdk/python/uv.lock | 22 ++++++++++-- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/sdk/python/_runtime_setup.py b/sdk/python/_runtime_setup.py index 8d33da6c88..fcccaddebd 100644 --- a/sdk/python/_runtime_setup.py +++ b/sdk/python/_runtime_setup.py @@ -27,16 +27,21 @@ class RuntimeSetupError(RuntimeError): def pinned_runtime_version() -> str: - source_version = _source_tree_project_version() - if source_version is not None: - return _normalized_package_version(source_version) + source_pin = _source_tree_runtime_dependency_version() + if source_pin is not None: + return _normalized_package_version(source_pin) try: - return _normalized_package_version(importlib.metadata.version(SDK_PACKAGE_NAME)) + installed_pin = _installed_sdk_runtime_dependency_version() except importlib.metadata.PackageNotFoundError as exc: raise RuntimeSetupError( - f"Unable to resolve {SDK_PACKAGE_NAME} version for runtime pinning." + f"Unable to resolve {SDK_PACKAGE_NAME} metadata for runtime pinning." ) from exc + if installed_pin is None: + raise RuntimeSetupError( + f"Unable to resolve {PACKAGE_NAME} dependency pin from {SDK_PACKAGE_NAME}." + ) + return _normalized_package_version(installed_pin) def ensure_runtime_package_installed( @@ -399,20 +404,30 @@ def _release_tag(version: str) -> str: return f"rust-v{_codex_release_version(version)}" -def _source_tree_project_version() -> str | None: +def _source_tree_runtime_dependency_version() -> str | None: pyproject_path = Path(__file__).resolve().parent / "pyproject.toml" if not pyproject_path.exists(): return None - match = re.search( - r'(?m)^version = "([^"]+)"$', - pyproject_path.read_text(encoding="utf-8"), - ) + match = re.search(_runtime_dependency_pin_pattern(), pyproject_path.read_text()) if match is None: return None return match.group(1) +def _installed_sdk_runtime_dependency_version() -> str | None: + requirements = importlib.metadata.requires(SDK_PACKAGE_NAME) or [] + for requirement in requirements: + match = re.search(_runtime_dependency_pin_pattern(), requirement) + if match is not None: + return match.group(1) + return None + + +def _runtime_dependency_pin_pattern() -> str: + return rf'{re.escape(PACKAGE_NAME)}\s*==\s*"?([^",;\s]+)"?' + + __all__ = [ "PACKAGE_NAME", "SDK_PACKAGE_NAME", diff --git a/sdk/python/examples/README.md b/sdk/python/examples/README.md index 59428ba322..3a93c28a0c 100644 --- a/sdk/python/examples/README.md +++ b/sdk/python/examples/README.md @@ -28,7 +28,7 @@ will download the matching GitHub release artifact, stage a temporary local `openai-codex-cli-bin` package, install it into your active interpreter, and clean up the temporary files afterward. -The pinned runtime version comes from the SDK package version. +The pinned runtime version comes from the SDK package dependency. ## Run examples diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index f54838bfa8..6b92983c91 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "openai-codex-app-server-sdk" -version = "0.116.0a1" +version = "0.131.0a4" description = "Python SDK for Codex app-server v2" readme = "README.md" requires-python = ">=3.10" @@ -22,7 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules", ] -dependencies = ["pydantic>=2.12"] +dependencies = ["pydantic>=2.12", "openai-codex-cli-bin==0.131.0a4"] [project.urls] Homepage = "https://github.com/openai/codex" @@ -63,8 +63,10 @@ testpaths = ["tests"] [tool.uv] exclude-newer = "7 days" +exclude-newer-package = { openai-codex-cli-bin = "2026-05-10T00:00:00Z" } index-strategy = "first-index" [tool.uv.pip] exclude-newer = "7 days" +exclude-newer-package = { openai-codex-cli-bin = "2026-05-10T00:00:00Z" } index-strategy = "first-index" diff --git a/sdk/python/tests/test_artifact_workflow_and_binaries.py b/sdk/python/tests/test_artifact_workflow_and_binaries.py index 93be06ca8a..daa1f8f47a 100644 --- a/sdk/python/tests/test_artifact_workflow_and_binaries.py +++ b/sdk/python/tests/test_artifact_workflow_and_binaries.py @@ -164,7 +164,7 @@ def test_runtime_package_template_has_no_checked_in_binaries() -> None: def test_examples_readme_points_to_runtime_version_source_of_truth() -> None: readme = (ROOT / "examples" / "README.md").read_text() - assert "The pinned runtime version comes from the SDK package version." in readme + assert "The pinned runtime version comes from the SDK package dependency." in readme def test_runtime_distribution_name_is_consistent() -> None: @@ -211,12 +211,31 @@ def test_release_metadata_retries_without_invalid_auth( assert authorizations == ["Bearer invalid-token", None] +def test_source_sdk_package_pins_published_runtime() -> None: + pyproject = tomllib.loads((ROOT / "pyproject.toml").read_text()) + + assert { + "sdk_version": pyproject["project"]["version"], + "dependencies": pyproject["project"]["dependencies"], + } == { + "sdk_version": "0.131.0a4", + "dependencies": [ + "pydantic>=2.12", + "openai-codex-cli-bin==0.131.0a4", + ], + } + + def test_runtime_setup_uses_pep440_package_version_and_codex_release_tags() -> None: runtime_setup = _load_runtime_setup_module() pyproject = tomllib.loads((ROOT / "pyproject.toml").read_text()) assert runtime_setup.PACKAGE_NAME == "openai-codex-cli-bin" assert runtime_setup.pinned_runtime_version() == pyproject["project"]["version"] + assert ( + f"{runtime_setup.PACKAGE_NAME}=={pyproject['project']['version']}" + in pyproject["project"]["dependencies"] + ) assert ( runtime_setup._normalized_package_version("rust-v0.116.0-alpha.1") == "0.116.0a1" diff --git a/sdk/python/uv.lock b/sdk/python/uv.lock index d0e2cf7372..b8e3a8f662 100644 --- a/sdk/python/uv.lock +++ b/sdk/python/uv.lock @@ -3,9 +3,12 @@ revision = 3 requires-python = ">=3.10" [options] -exclude-newer = "2026-04-20T18:19:27.620299Z" +exclude-newer = "2026-05-02T06:28:46.47929Z" exclude-newer-span = "P7D" +[options.exclude-newer-package] +openai-codex-cli-bin = "2026-05-10T00:00:00Z" + [[package]] name = "annotated-types" version = "0.7.0" @@ -279,9 +282,10 @@ wheels = [ [[package]] name = "openai-codex-app-server-sdk" -version = "0.116.0a1" +version = "0.131.0a4" source = { editable = "." } dependencies = [ + { name = "openai-codex-cli-bin" }, { name = "pydantic" }, ] @@ -295,12 +299,26 @@ dev = [ [package.metadata] requires-dist = [ { name = "datamodel-code-generator", marker = "extra == 'dev'", specifier = "==0.31.2" }, + { name = "openai-codex-cli-bin", specifier = "==0.131.0a4" }, { name = "pydantic", specifier = ">=2.12" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11" }, ] provides-extras = ["dev"] +[[package]] +name = "openai-codex-cli-bin" +version = "0.131.0a4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/9f/f9fc4bb1b2b7a20d4d65143ebb4c4dcd2301a718183b539ecb5b1c0ac3ec/openai_codex_cli_bin-0.131.0a4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:db0f3cb7dda310641ac04fbaf3f128693a3817ab83ae59b67a3c9c74bd53f8b8", size = 88367585, upload-time = "2026-05-09T06:14:09.453Z" }, + { url = "https://files.pythonhosted.org/packages/dc/39/eb95ed0e8156669e895a192dec760be07dabe891c3c6340f7c6487b9a976/openai_codex_cli_bin-0.131.0a4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6cae5af6edca7f6d3f0bcbbd93cfc8a6dc3e33fb5955af21ae492b6d5d0dcb72", size = 79245567, upload-time = "2026-05-09T06:14:13.581Z" }, + { url = "https://files.pythonhosted.org/packages/0c/92/ade176fa78d746d5ff7a6e371d64740c0d95ab299b0dd58a5404b89b3915/openai_codex_cli_bin-0.131.0a4-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5728f9887baf62d7e72f4f242093b3ff81e26c81d80d346fe1eef7eda6838aa8", size = 77758628, upload-time = "2026-05-09T06:14:18.374Z" }, + { url = "https://files.pythonhosted.org/packages/28/e6/bfe6c65f8e3e5499f71b24c3b6e8d07e4d426543d25e429b9b141b544e5f/openai_codex_cli_bin-0.131.0a4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d7a47fd3667fbcc216593839c202deffa056e9b3d46c6933e72594d461f4fea0", size = 84535509, upload-time = "2026-05-09T06:14:22.851Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b7/53dc094a691ab6f2ca079e8e865b122843809ac4fad51cac4d59021e599d/openai_codex_cli_bin-0.131.0a4-py3-none-win_amd64.whl", hash = "sha256:c61bcf029672494c4c7fdc8567dbaa659a48bb75641d91c2ade27c1e46803434", size = 88185543, upload-time = "2026-05-09T06:14:27.282Z" }, + { url = "https://files.pythonhosted.org/packages/82/99/e0852ffcf9b4d2794fef83e0c3a267b3c773a776f136e9f7ce19f0c8df42/openai_codex_cli_bin-0.131.0a4-py3-none-win_arm64.whl", hash = "sha256:bbde750186861f102e346ac066f4e9608f515f7b71b16a6e8b7ef1ddc02a97a5", size = 81196380, upload-time = "2026-05-09T06:14:32.103Z" }, +] + [[package]] name = "packaging" version = "26.1"