Skip to content
Merged
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
7 changes: 6 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
python-version: ['3.11', '3.12', '3.13', '3.14']
include:
- os: macos-latest
python-version: '3.12'
- os: windows-latest
python-version: '3.12'
runs-on: ${{ matrix.os }}

steps:
Expand Down
104 changes: 69 additions & 35 deletions packages/cli/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import shutil
import subprocess
import sys
from pathlib import Path
from textwrap import dedent
from unittest.mock import Mock, patch
Expand Down Expand Up @@ -84,16 +85,30 @@ def create_test_pyproject(test_dir: Path, dependencies=None):
return dependencies


def create_test_wrangler_jsonc(
test_dir: Path, main_path="src/worker.py", python_version="3.12"
):
"""Create a test wrangler.jsonc file with the given main path and Python version."""
def _wrangler_compat_config(python_version: str) -> tuple[str, str]:
"""Return (compat_flags_str, compat_date) for a given Python version."""
compat_flags = ["python_workers"]
if python_version == "3.13":
compat_flags.append("python_workers_20250116")
if python_version == "3.14":
compat_flags.append("python_workers_20260610")

compat_flags_str = ", ".join([f'"{flag}"' for flag in compat_flags])

compat_dates = {
"3.12": "2025-09-28",
"3.13": "2025-10-01",
"3.14": "2026-09-01",
}
compat_date = compat_dates.get(python_version, "2025-10-01")
return compat_flags_str, compat_date


def create_test_wrangler_jsonc(
test_dir: Path, main_path="src/worker.py", python_version="3.13"
):
compat_flags_str, compat_date = _wrangler_compat_config(python_version)

content = f"""
/**
* For more details on how to configure Wrangler, refer to:
Expand All @@ -107,7 +122,7 @@ def create_test_wrangler_jsonc(
"main": "{main_path}",

// Compatibility date
"compatibility_date": "2023-10-30",
"compatibility_date": "{compat_date}",

// Compatibility flags
"compatibility_flags": [{compat_flags_str}]
Expand All @@ -117,14 +132,16 @@ def create_test_wrangler_jsonc(


def create_test_wrangler_toml(
test_dir, main_path="dist/worker.js", python_version="3.12"
test_dir, main_path="dist/worker.js", python_version="3.13"
):
"""Create a test wrangler.toml file with the given main path and Python version."""
compat_flags = ["python_workers"]
if python_version == "3.13":
compat_flags.append("python_workers_20250116")
compat_flags_str, compat_date = _wrangler_compat_config(python_version)

compat_flags_str = ", ".join([f'"{flag}"' for flag in compat_flags])
compat_dates = {
"3.12": "2025-09-28",
"3.13": "2025-10-01",
"3.14": "2026-08-28",
}
Comment thread
ryanking13 marked this conversation as resolved.
compat_date = compat_dates.get(python_version, "2025-10-01")

content = dedent(f"""
# Name of the worker
Expand All @@ -134,7 +151,7 @@ def create_test_wrangler_toml(
main = "{main_path}"

# Compatibility date
compatibility_date = "2023-10-30"
compatibility_date = "{compat_date}"

# Compatibility flags
compatibility_flags = [{compat_flags_str}]
Expand Down Expand Up @@ -213,7 +230,7 @@ def test_sync_command_integration(dependencies, test_dir): # noqa: C901 (test c
if os.name == "nt":
site_packages_path = TEST_VENV_WORKERS / "Lib" / "site-packages"
else:
site_packages_path = TEST_VENV_WORKERS / "lib" / "python3.12" / "site-packages"
site_packages_path = TEST_VENV_WORKERS / "lib" / "python3.13" / "site-packages"
assert site_packages_path.exists(), (
"site-packages directory does not exist in .venv-workers"
)
Expand Down Expand Up @@ -434,6 +451,10 @@ def create_worker_pyproject_with_local_dep(
(test_dir / "pyproject.toml").write_text(content)


@pytest.mark.skipif(
sys.platform == "win32",
reason="FIXME Pyodide WASM interpreter cannot run setuptools build backends on Windows",
)
def test_sync_allow_build_local_dependency(test_dir):
"""End-to-end test for --allow-build with a local source dependency.

Expand Down Expand Up @@ -483,6 +504,10 @@ def test_sync_allow_build_local_dependency(test_dir):
)


@pytest.mark.skipif(
sys.platform == "win32",
reason="FIXME Pyodide WASM interpreter cannot run setuptools build backends on Windows",
)
def test_sync_allow_build_via_pyproject_config(test_dir):
"""End-to-end test for the [tool.pywrangler] allow-build config fallback.

Expand Down Expand Up @@ -526,7 +551,7 @@ def test_sync_command_handles_missing_pyproject():
{
"name": "test-worker",
"main": "src/worker.py",
"compatibility_date": "2023-10-30",
"compatibility_date": "2025-10-01",
"compatibility_flags": ["python_workers"]
}
""")
Expand Down Expand Up @@ -707,15 +732,19 @@ def test_proxy_to_wrangler_handles_subprocess_error(mock_subprocess_run):
# Should exit with 1 (error code)
assert result.exit_code == 1

# Verify the error was attempted to be called
mock_subprocess_run.assert_called_once_with(
["npx", "--yes", "wrangler", "unknown_command"],
check=False,
cwd=Path("."),
env=None,
text=True,
encoding="utf-8",
)
# Verify the error was attempted to be called.
mock_subprocess_run.assert_called_once()
call_args = mock_subprocess_run.call_args
cmd = call_args[0][0]
assert cmd[0].lower().startswith("npx")
assert cmd[1:] == ["--yes", "wrangler", "unknown_command"]
assert call_args[1] == {
"check": False,
"cwd": Path("."),
"env": None,
"text": True,
"encoding": "utf-8",
}


def test_sync_command_finds_pyproject_in_parent_directory(test_dir):
Expand Down Expand Up @@ -767,9 +796,8 @@ def test_sync_recreates_venv_on_python_version_mismatch(test_dir):
sync_cmd = ["uv", "run", "pywrangler", "sync"]
venv_path = test_dir / ".venv-workers"

# First run: Create venv with Python 3.12 (using basic python_workers flag)
print("\nRunning sync to create venv with Python 3.12...")
create_test_wrangler_jsonc(test_dir, python_version="3.12")
# First run: Create venv with Python 3.13
create_test_wrangler_jsonc(test_dir, python_version="3.13")
result1 = subprocess.run(
sync_cmd, capture_output=True, text=True, cwd=test_dir, check=False
)
Expand All @@ -780,11 +808,17 @@ def test_sync_recreates_venv_on_python_version_mismatch(test_dir):
assert venv_path.exists(), "Venv was not created on the first run."
initial_mtime = venv_path.stat().st_mtime

# Second run: Recreate venv with Python 3.13 (using python_workers_20250116 flag)
print("\nRunning sync to recreate venv with Python 3.13...")
# Second run: Recreate venv with Python 3.14
create_test_pyproject(test_dir)
create_test_wrangler_jsonc(test_dir, python_version="3.13")
result2 = subprocess.run(sync_cmd, text=True, cwd=test_dir, check=False)
create_test_wrangler_jsonc(test_dir, python_version="3.14")
# Use --force to ensure pylock.toml is recompiled for the new version
result2 = subprocess.run(
[*sync_cmd, "--force"],
capture_output=True,
text=True,
cwd=test_dir,
check=False,
)

assert result2.returncode == 0, (
f"Second sync failed: {result2.stdout}\n{result2.stderr}"
Expand All @@ -795,7 +829,7 @@ def test_sync_recreates_venv_on_python_version_mismatch(test_dir):
# Check that the venv was actually modified
assert final_mtime > initial_mtime, "Venv modification time did not change."

# Verify the python version in the new venv is 3.13.
# Verify the python version in the new venv is 3.14.
python_exe = venv_path / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
version_result = subprocess.run(
[python_exe, "--version"],
Expand All @@ -804,8 +838,8 @@ def test_sync_recreates_venv_on_python_version_mismatch(test_dir):
cwd=test_dir,
check=False,
)
assert "3.13" in version_result.stdout, (
f"Python version is not 3.13: {version_result.stdout}"
assert "3.14" in version_result.stdout, (
f"Python version is not 3.14: {version_result.stdout}"
)


Expand Down Expand Up @@ -852,7 +886,7 @@ def test_create_pyodide_venv_does_not_put_interpreter_on_path(test_dir, tmp_path
"""`create_pyodide_venv` must not place an interpreter on the user's PATH.

Integration test (exercises real `uv`). As of uv 0.8, `uv python install` links
a versioned executable (for the Pyodide build, `pyodide3.12`) into uv's
a versioned executable (for the Pyodide build, e.g. `pyodide3.13`) into uv's
executable directory, which is on PATH. That shadows real CPython for other tools
on the system, so the venv must be created without it.

Expand All @@ -863,7 +897,7 @@ def test_create_pyodide_venv_does_not_put_interpreter_on_path(test_dir, tmp_path
run the wasm interpreter to query it).
"""
create_test_pyproject(test_dir, dependencies=[])
create_test_wrangler_jsonc(test_dir, python_version="3.12")
create_test_wrangler_jsonc(test_dir, python_version="3.13")

bin_dir = tmp_path / "uv-bin"
install_dir = tmp_path / "uv-pythons"
Expand Down
26 changes: 18 additions & 8 deletions packages/cli/tests/test_py_version_detect.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
"""Tests for sync module Python version detection."""

import sys

import click
import pytest

# Import the functions we want to test
import pywrangler.utils as util_module

skip_on_windows = pytest.mark.skipif(
sys.platform == "win32", reason="Python 3.12 is not supported on Windows"
)


@pytest.fixture
def test_dir(tmp_path, monkeypatch):
Expand Down Expand Up @@ -76,17 +82,20 @@ def test_compat_date_boundary(test_dir):
version = get_python_version()
assert version == "3.13"

# Test one day before - should return 3.12 (base python_workers)

@skip_on_windows
def test_compat_date_boundary_before(test_dir):
"""Test one day before the boundary date returns 3.12."""
wrangler_toml = test_dir / "wrangler.toml"

wrangler_toml.write_text("""
name = "test-worker"
compatibility_flags = ["python_workers"]
compatibility_date = "2025-09-28"
""")

version = get_python_version()
assert (
version == "3.12"
) # Should be 3.12 because only python_workers flag is present
assert version == "3.12"


def test_no_wrangler_config(test_dir):
Expand Down Expand Up @@ -167,15 +176,16 @@ def test_main_get_python_version_integration(test_dir):
version = get_python_version()
assert version == "3.13"

# Test with config that specifies 3.12 (only python_workers flag)
wrangler_toml.write_text("""
if sys.platform != "win32":
# Test with config that specifies 3.12 (only python_workers flag)
wrangler_toml.write_text("""
name = "test-worker"
compatibility_date = "2024-09-09"
compatibility_flags = ["python_workers"]
""")

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


def test_314_compat_flag_with_experimental(test_dir):
Expand Down
1 change: 1 addition & 0 deletions packages/cli/tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ async def fetch(self, request: Request) -> Response:


@pytest.mark.skipif(sys.version_info < (3, 13), reason="We create Python 3.13+ syntax")
@pytest.mark.skipif(sys.platform == "win32", reason="Requires npx/wrangler toolchain")
def test_types(tmp_path):
"""Test that types are correctly revealed in a worker."""
config_path = tmp_path / "wrangler.toml"
Expand Down
Loading
Loading