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 <noreply@openai.com>
This commit is contained in:
Ahmed Ibrahim
2026-05-09 10:03:34 +03:00
parent becbd2a127
commit 6e10973c78
5 changed files with 70 additions and 16 deletions

View File

@@ -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",

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

22
sdk/python/uv.lock generated
View File

@@ -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"