Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/cli/src/pywrangler/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@


class PythonCompatVersion(NamedTuple):
version: Literal["3.12", "3.13"]
version: Literal["3.12", "3.13", "3.14"]
compat_flag: str
compat_date: datetime | None
experimental: bool = False


PYTHON_COMPAT_VERSIONS = [
PythonCompatVersion("3.14", "pythonWorkers20260610", None, experimental=True),
PythonCompatVersion(
"3.13", "python_workers_20250116", datetime.strptime("2025-09-29", "%Y-%m-%d")
),
Expand Down
10 changes: 9 additions & 1 deletion packages/cli/src/pywrangler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ def _parse_wrangler_config() -> WranglerConfig:


@cache
def get_python_version() -> Literal["3.12", "3.13"]:
def get_python_version() -> Literal["3.12", "3.13", "3.14"]:
"""
Determine Python version from wrangler configuration.

Expand Down Expand Up @@ -372,6 +372,10 @@ def get_python_version() -> Literal["3.12", "3.13"]:
)

for py_version in sorted_versions:
# Skip experimental versions unless the experimental compat flag is enabled
if "experimental" not in compat_flags and py_version.experimental:
continue

# Check if the specific compat flag is present
if py_version.compat_flag in compat_flags:
return py_version.version
Expand All @@ -390,6 +394,8 @@ def get_uv_pyodide_interp_name() -> str:
v = "3.12.7"
case "3.13":
v = "3.13.2"
case "3.14":
v = "3.14.2"
return f"cpython-{v}-emscripten-wasm32-musl"


Expand All @@ -407,6 +413,8 @@ def get_pyodide_index() -> str:
v = "0.27.7"
case "3.13":
v = "0.28.3"
case "3.14":
v = "314.0.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

314.0.0 is clearly a placeholder. If someone enables the experimental flag today, pywrangler sync will silently try to fetch from a non-existent index URL. Consider adding a comment, or raising an explicit error until a real Pyodide build for 3.14 is available.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

experimental flag will fail on publish for non-Cloudflare employees so it is safe.

return "https://index.pyodide.org/" + v


Expand Down
12 changes: 12 additions & 0 deletions packages/cli/tests/test_py_version_detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,15 @@ def test_main_get_python_version_integration(test_dir):

version = get_python_version()
assert version == "3.12"


def test_314_compat_flag_with_experimental(test_dir):
wrangler_toml = test_dir / "wrangler.toml"
wrangler_toml.write_text("""
name = "test-worker"
compatibility_flags = ["python_workers", "pythonWorkers20260610", "experimental"]
compatibility_date = "2026-06-10"
""")

version = get_python_version()
assert version == "3.14"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test covers the happy path, but there's no test for when pythonWorkers20260610 is present without the experimental flag. That's the key scenario the gating logic protects against — please add a negative test to verify it falls back to an earlier version.

Suggested change
assert version == "3.14"
version = get_python_version()
assert version == "3.14"
def test_314_compat_flag_without_experimental(test_dir):
"""Test that Python 3.14 is NOT selected without the experimental flag."""
wrangler_toml = test_dir / "wrangler.toml"
wrangler_toml.write_text("""
name = "test-worker"
compatibility_flags = ["python_workers", "pythonWorkers20260610"]
compatibility_date = "2026-06-10"
""")
version = get_python_version()
assert version == "3.12"

Loading