From a9e82066efb854676672f686bf5dfffe55ce35ef Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:16:18 -0800 Subject: [PATCH 01/19] =?UTF-8?q?=E2=9C=A8=20Add=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 52 ++++++++++++++++-- every_python/main.py | 110 +++++++++++++++++++++++++------------ every_python/utils.py | 53 ++++++++++++++++-- tests/test_main.py | 4 +- 4 files changed, 173 insertions(+), 46 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c26cb5a..5944d45 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,20 +4,45 @@ on: push: branches: [ main ] paths: - - 'every-python/**' + - 'every_python/**' + - 'tests/**' + - 'pyproject.toml' - '.github/workflows/test.yml' pull_request: branches: [ main ] paths: - - 'every-python/**' + - 'every_python/**' + - 'tests/**' + - 'pyproject.toml' - '.github/workflows/test.yml' jobs: test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + include: + # x86_64-unknown-linux-gnu/gcc + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + toolchain: gcc + # aarch64-unknown-linux-gnu/gcc + - os: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + toolchain: gcc + # x86_64-pc-windows-msvc/msvc + - os: windows-latest + target: x86_64-pc-windows-msvc + toolchain: msvc + # x86_64-apple-darwin/clang + - os: macos-13 + target: x86_64-apple-darwin + toolchain: clang + # aarch64-apple-darwin/clang + - os: macos-latest + target: aarch64-apple-darwin + toolchain: clang steps: - uses: actions/checkout@v5 @@ -25,10 +50,27 @@ jobs: uses: actions/setup-python@v6 with: python-version: "3.13" + architecture: ${{ contains(matrix.target, 'aarch64') && 'arm64' || 'x64' }} - - name: Install system dependencies (Linux only) + - name: Install system dependencies (Linux) if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y lsof + run: | + sudo apt-get update + sudo apt-get install -y lsof + # Install LLVM for JIT testing using official script + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 20 + # Add LLVM tools to PATH + echo "$(llvm-config-20 --bindir)" >> $GITHUB_PATH + + - name: Install LLVM (macOS) + if: runner.os == 'macOS' + run: brew install llvm@20 + + - name: Install LLVM (Windows) + if: runner.os == 'Windows' + run: | + choco install llvm --version=20.0.0 -y + echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install uv uses: astral-sh/setup-uv@v7 diff --git a/every_python/main.py b/every_python/main.py index dced708..7125cce 100644 --- a/every_python/main.py +++ b/every_python/main.py @@ -1,4 +1,5 @@ import os +import platform import shutil import subprocess from pathlib import Path @@ -13,6 +14,7 @@ from every_python.runner import CommandResult, CommandRunner, get_runner from every_python.utils import ( BuildInfo, + python_binary_location, check_llvm_available, get_llvm_version_for_commit, ) @@ -96,7 +98,16 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - enable_jit = False elif not check_llvm_available(llvm_version): output.warning(f"Warning: LLVM {llvm_version} not found") - output.info(f"Install with: brew install llvm@{llvm_version}") + if platform.system() == "Darwin": + output.info(f"Install with: brew install llvm@{llvm_version}") + elif platform.system() == "Linux": + output.info( + f"Install with: apt install llvm-{llvm_version} clang-{llvm_version} lld-{llvm_version}" + ) + else: # Windows + output.info( + f"Install LLVM {llvm_version} from https://github.com/llvm/llvm-project/releases" + ) if not typer.confirm("Continue building without JIT?", default=True): raise typer.Exit(0) enable_jit = False @@ -129,12 +140,22 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - # Configure progress.update(task, description="Configuring build...") - configure_args = ["./configure", "--prefix", str(build_dir), "--with-pydebug"] - - # Add JIT flag if enabled - if enable_jit: - configure_args.append("--enable-experimental-jit") + if platform.system() == "Windows": + configure_args = ["PCbuild\\build.bat", "-c", "Debug"] + if enable_jit: + configure_args.append("--experimental-jit") + else: + configure_args = [ + "./configure", + "--prefix", + str(build_dir), + "--with-pydebug", + ] + + # Add JIT flag if enabled + if enable_jit: + configure_args.append("--experimental-jit") if verbose: progress.stop() output.status(f"Running: {' '.join(configure_args)}") @@ -153,43 +174,62 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - ) raise typer.Exit(1) - # Build + # Build and install import multiprocessing ncpu = multiprocessing.cpu_count() - if verbose: - output.status(f"Building with {ncpu} cores (this may a few minutes)...") - output.status(f"Running: make -j{ncpu}") + if platform.system() == "Windows": + # Windows: build.bat does both build and "install" (outputs to PCbuild/amd64) + # The configure step above already ran build.bat, so we're done + # Just move the output to our build directory + progress.update(task, description="Copying build artifacts...") + import shutil + + pcbuild_dir = REPO_DIR / "PCbuild" / "amd64" + if not pcbuild_dir.exists(): + progress.stop() + output.error(f"Build output not found at {pcbuild_dir}") + raise typer.Exit(1) + + build_dir.mkdir(parents=True, exist_ok=True) + shutil.copytree(pcbuild_dir, build_dir, dirs_exist_ok=True) else: - progress.update( - task, - description=f"Building with {ncpu} cores (this may a few minutes)...", - ) + # Unix: use make + if verbose: + output.status(f"Building with {ncpu} cores (this may a few minutes)...") + output.status(f"Running: make -j{ncpu}") + else: + progress.update( + task, + description=f"Building with {ncpu} cores (this may a few minutes)...", + ) - make_result = runner.run( - ["make", f"-j{ncpu}"], - cwd=REPO_DIR, - capture_output=not verbose, - ) + make_result = runner.run( + ["make", f"-j{ncpu}"], + cwd=REPO_DIR, + capture_output=not verbose, + ) - if not make_result.success: - if not verbose: - progress.stop() - output.error(f"Build failed: {make_result.stderr if not verbose else ''}") - raise typer.Exit(1) + if not make_result.success: + if not verbose: + progress.stop() + output.error( + f"Build failed: {make_result.stderr if not verbose else ''}" + ) + raise typer.Exit(1) - # Install to prefix - progress.update(task, description="Installing...") - install_result: CommandResult = runner.run( - ["make", "install"], - cwd=REPO_DIR, - ) + # Install to prefix + progress.update(task, description="Installing...") + install_result: CommandResult = runner.run( + ["make", "install"], + cwd=REPO_DIR, + ) - if not install_result.success: - progress.stop() - output.error(f"Install failed: {install_result.stderr}") - raise typer.Exit(1) + if not install_result.success: + progress.stop() + output.error(f"Install failed: {install_result.stderr}") + raise typer.Exit(1) progress.update(task, description=f"[green]✓ Built {commit[:7]}[/green]") @@ -254,7 +294,7 @@ def run( ) build_dir = build_python(commit, enable_jit=jit) - python_bin = build_dir / "bin" / "python3" + python_bin = python_binary_location(BUILDS_DIR, build_info) if not python_bin.exists(): output.error(f"Python binary not found at {python_bin}") diff --git a/every_python/utils.py b/every_python/utils.py index bff411d..9d6d83a 100644 --- a/every_python/utils.py +++ b/every_python/utils.py @@ -1,3 +1,4 @@ +import platform import re import subprocess from dataclasses import dataclass @@ -52,14 +53,50 @@ def _check_tool_available(tool: str, version: str) -> bool: if _check_tool_version(tool, version): return True - # Try Homebrew installation (checks both llvm@{version} and llvm) - brew_tool = _get_homebrew_llvm_tool(tool, version) - if brew_tool and _check_tool_version(brew_tool, version): - return True + # Platform-specific checks + if platform.system() == "Darwin": + # macOS: Try Homebrew installation + brew_tool = _get_homebrew_llvm_tool(tool, version) + if brew_tool and _check_tool_version(brew_tool, version): + return True + elif platform.system() == "Windows": + # Windows: Check Program Files + windows_tool = _get_windows_llvm_tool(tool, version) + if windows_tool and _check_tool_version(windows_tool, version): + return True return False +def _get_windows_llvm_tool(tool: str, version: str) -> str | None: + """Get the path to an LLVM tool from Windows installation.""" + # Common Windows LLVM installation paths + program_files = Path("C:/Program Files") + program_files_x86 = Path("C:/Program Files (x86)") + + possible_paths = [ + program_files / f"LLVM-{version}" / "bin" / f"{tool}.exe", + program_files / "LLVM" / "bin" / f"{tool}.exe", + program_files_x86 / f"LLVM-{version}" / "bin" / f"{tool}.exe", + program_files_x86 / "LLVM" / "bin" / f"{tool}.exe", + ] + + for tool_path in possible_paths: + if tool_path.exists(): + try: + result = subprocess.run( + [str(tool_path), "--version"], + capture_output=True, + timeout=5, + ) + if result.returncode == 0: + return str(tool_path) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + return None + + def _get_homebrew_llvm_tool(tool: str, version: str | None = None) -> str | None: """Get the path to an LLVM tool from Homebrew installation.""" # Try version-specific formula first (e.g., llvm@20) @@ -146,6 +183,14 @@ def _check_tool_version(tool_name: str, expected_version: str) -> bool: return False +def python_binary_location(builds_dir: Path, build_info: "BuildInfo") -> Path: + """Get the path to the Python binary for a given build.""" + if platform.system() == "Windows": + return builds_dir / build_info.directory_name / "python.exe" + else: + return builds_dir / build_info.directory_name / "bin" / "python3" + + @dataclass class BuildInfo: """Information about a Python build.""" diff --git a/tests/test_main.py b/tests/test_main.py index 1b3835d..4690bb6 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -165,14 +165,14 @@ def test_build_with_jit_available(self, tmp_path: Path): # Should create JIT build directory assert build_dir.name == "abc123d-jit" - # Should pass --enable-experimental-jit to configure + # Should pass --experimental-jit to configure configure_calls = [ call_args for call_args in mock_runner.run.call_args_list if "./configure" in str(call_args) ] assert len(configure_calls) > 0 - assert "--enable-experimental-jit" in str(configure_calls[0]) + assert "--experimental-jit" in str(configure_calls[0]) def test_build_with_jit_llvm_missing(self, tmp_path: Path): """Test building with JIT when LLVM is missing falls back to non-JIT.""" From 0a2c275bf9b1688141070fa14740b4d5274e03a9 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:25:54 -0800 Subject: [PATCH 02/19] =?UTF-8?q?=F0=9F=A7=AA=20Fix=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 45 +++++++++++++++++++------------------- tests/test_utils.py | 8 +++++-- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5944d45..b93a2db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,31 +18,29 @@ on: jobs: test: - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - # x86_64-unknown-linux-gnu/gcc - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - toolchain: gcc - # aarch64-unknown-linux-gnu/gcc - - os: ubuntu-24.04-arm - target: aarch64-unknown-linux-gnu - toolchain: gcc - # x86_64-pc-windows-msvc/msvc - - os: windows-latest - target: x86_64-pc-windows-msvc - toolchain: msvc - # x86_64-apple-darwin/clang - - os: macos-13 - target: x86_64-apple-darwin - toolchain: clang - # aarch64-apple-darwin/clang - - os: macos-latest - target: aarch64-apple-darwin - toolchain: clang + - target: x86_64-unknown-linux-gnu/gcc + architecture: x86_64 + runner: ubuntu-24.04 + - target: aarch64-unknown-linux-gnu/gcc + architecture: aarch64 + runner: ubuntu-24.04-arm + - target: x86_64-pc-windows-msvc/msvc + architecture: x64 + runner: windows-2022 + - target: aarch64-pc-windows-msvc/msvc + architecture: ARM64 + runner: windows-11-arm + - target: x86_64-apple-darwin/clang + architecture: x86_64 + runner: macos-15-intel + - target: aarch64-apple-darwin/clang + architecture: aarch64 + runner: macos-14 steps: - uses: actions/checkout@v5 @@ -50,7 +48,7 @@ jobs: uses: actions/setup-python@v6 with: python-version: "3.13" - architecture: ${{ contains(matrix.target, 'aarch64') && 'arm64' || 'x64' }} + architecture: ${{ matrix.architecture }} - name: Install system dependencies (Linux) if: runner.os == 'Linux' @@ -69,7 +67,8 @@ jobs: - name: Install LLVM (Windows) if: runner.os == 'Windows' run: | - choco install llvm --version=20.0.0 -y + # Install LLVM if not already present, or upgrade to latest + choco upgrade llvm -y --allow-downgrade echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install uv diff --git a/tests/test_utils.py b/tests/test_utils.py index 9be8c1b..4da583c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -178,10 +178,14 @@ def side_effect(tool, version): assert result is True + @patch("platform.system") @patch("every_python.utils._get_homebrew_llvm_tool") @patch("every_python.utils._check_tool_version") - def test_homebrew_tool_available(self, mock_check_version, mock_homebrew_tool): - """Test finding tool via Homebrew.""" + def test_homebrew_tool_available( + self, mock_check_version, mock_homebrew_tool, mock_platform + ): + """Test finding tool via Homebrew on macOS.""" + mock_platform.return_value = "Darwin" # Simulate macOS mock_check_version.return_value = False # Not in PATH mock_homebrew_tool.return_value = "/opt/homebrew/opt/llvm@20/bin/clang" From 0581bad90b679d8ed555a6aa28b627448628e65c Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:27:03 -0800 Subject: [PATCH 03/19] =?UTF-8?q?=F0=9F=92=9A=20Fix=20Python=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b93a2db..50fb2b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14.0" architecture: ${{ matrix.architecture }} - name: Install system dependencies (Linux) From 0064f7abbb81ff0d2d6b30ebd65dd1c4390a36f0 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:28:22 -0800 Subject: [PATCH 04/19] =?UTF-8?q?=F0=9F=92=9A=20Fix=20Python=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 50fb2b0..edf4d76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: "3.14.0" + python-version: "3.12" architecture: ${{ matrix.architecture }} - name: Install system dependencies (Linux) From 44048ccea4cbcd2b0da2dc8d7dbbd0e4cf07ded6 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:29:04 -0800 Subject: [PATCH 05/19] =?UTF-8?q?=F0=9F=92=9A=20Fix=20Python=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index edf4d76..f91a6bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: "3.11" architecture: ${{ matrix.architecture }} - name: Install system dependencies (Linux) From 1e7b383d000096d32c037889f1daf8c6a4ff0bc2 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:30:33 -0800 Subject: [PATCH 06/19] =?UTF-8?q?=F0=9F=92=9A=20More=20CI=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f91a6bd..c6492f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,10 +45,9 @@ jobs: - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@v5 with: python-version: "3.11" - architecture: ${{ matrix.architecture }} - name: Install system dependencies (Linux) if: runner.os == 'Linux' From 6ffe827d676415db19c3e6f672ebbec111925eb2 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:35:50 -0800 Subject: [PATCH 07/19] =?UTF-8?q?=E2=9C=85=20More=20test=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 7 ++----- tests/test_main.py | 6 +++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6492f6..d5b4ef6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,16 +47,14 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.14" - name: Install system dependencies (Linux) if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y lsof - # Install LLVM for JIT testing using official script sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 20 - # Add LLVM tools to PATH echo "$(llvm-config-20 --bindir)" >> $GITHUB_PATH - name: Install LLVM (macOS) @@ -66,8 +64,7 @@ jobs: - name: Install LLVM (Windows) if: runner.os == 'Windows' run: | - # Install LLVM if not already present, or upgrade to latest - choco upgrade llvm -y --allow-downgrade + choco install llvm --version=20.1.8 -y --force echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install uv diff --git a/tests/test_main.py b/tests/test_main.py index 4690bb6..92ab067 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -130,6 +130,7 @@ def test_build_without_jit(self, tmp_path: Path): with ( patch("every_python.main.REPO_DIR", repo_dir), patch("every_python.main.BUILDS_DIR", builds_dir), + patch("platform.system", return_value="Linux"), ): build_dir = build_python("abc123d", enable_jit=False) @@ -246,6 +247,7 @@ def test_build_already_exists(self, tmp_path: Path): with ( patch("every_python.main.REPO_DIR", repo_dir), patch("every_python.main.BUILDS_DIR", builds_dir), + patch("platform.system", return_value="Linux"), ): build_dir = build_python("abc123d", enable_jit=False) @@ -310,10 +312,12 @@ class TestRunCommand: @patch("os.execv") @patch("every_python.main.resolve_ref") + @patch("platform.system") def test_run_existing_build( - self, mock_resolve: Mock, mock_execv: Mock, tmp_path: Path + self, mock_platform: Mock, mock_resolve: Mock, mock_execv: Mock, tmp_path: Path ): """Test running with existing build.""" + mock_platform.return_value = "Linux" # Force Unix behavior mock_resolve.return_value = "abc123def456" builds_dir = tmp_path / "builds" From 52a0918c0caa6c6cf385984a3c4259f87f8854bd Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:44:53 -0800 Subject: [PATCH 08/19] =?UTF-8?q?=F0=9F=92=9A=20Move=20build=20to=20integr?= =?UTF-8?q?ation.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 72 ++++++++++++++ .github/workflows/test.yml | 18 ---- tests/test_main.py | 155 +----------------------------- 3 files changed, 74 insertions(+), 171 deletions(-) create mode 100644 .github/workflows/integration.yml diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml new file mode 100644 index 0000000..64362c4 --- /dev/null +++ b/.github/workflows/integration.yml @@ -0,0 +1,72 @@ +name: Integration Tests + +on: + workflow_dispatch: # Manual trigger only + push: + branches: [ main ] + paths: + - 'every_python/**' + - '.github/workflows/integration.yml' + +jobs: + integration: + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-24.04 + os: Linux + - runner: windows-2022 + os: Windows + - runner: macos-14 + os: macOS + steps: + - uses: actions/checkout@v5 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install system dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y lsof + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 20 + echo "$(llvm-config-20 --bindir)" >> $GITHUB_PATH + + - name: Install LLVM (macOS) + if: runner.os == 'macOS' + run: brew install llvm@20 + + - name: Install LLVM (Windows) + if: runner.os == 'Windows' + run: | + choco install llvm --version=20.1.8 -y --force + echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync + + - name: Test LLVM detection + run: | + uv run python -c "from every_python.utils import check_llvm_available; import sys; sys.exit(0 if check_llvm_available('20') else 1)" + + - name: Test build without JIT + run: uv run every-python install v3.13.0 --verbose + + - name: Verify non-JIT build works + run: uv run every-python run v3.13.0 -- --version + + - name: Test build with JIT + run: uv run every-python install main --jit --verbose + + - name: Verify JIT build works + run: uv run every-python run main --version diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5b4ef6..4cfffec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,24 +49,6 @@ jobs: with: python-version: "3.14" - - name: Install system dependencies (Linux) - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y lsof - sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 20 - echo "$(llvm-config-20 --bindir)" >> $GITHUB_PATH - - - name: Install LLVM (macOS) - if: runner.os == 'macOS' - run: brew install llvm@20 - - - name: Install LLVM (Windows) - if: runner.os == 'Windows' - run: | - choco install llvm --version=20.1.8 -y --force - echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Install uv uses: astral-sh/setup-uv@v7 with: diff --git a/tests/test_main.py b/tests/test_main.py index 92ab067..fe71d80 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -7,12 +7,11 @@ from every_python.main import ( app, - build_python, ensure_repo, resolve_ref, ) -from every_python.output import QuietOutputHandler, set_output -from every_python.runner import CommandResult, CommandRunner, set_runner +from every_python.output import set_output +from every_python.runner import set_runner runner = CliRunner() @@ -54,7 +53,6 @@ def test_skip_clone_if_exists(self, tmp_path: Path): ): ensure_repo() - # Should not have called git clone mock_run.assert_not_called() @@ -106,155 +104,6 @@ def test_resolve_invalid_ref(self, mock_run: Mock, tmp_path: Path): resolve_ref("invalid-ref") -class TestBuildPython: - """Test Python building logic.""" - - def test_build_without_jit(self, tmp_path: Path): - """Test building Python without JIT.""" - - # Create mock runner - mock_runner = Mock(spec=CommandRunner) - mock_runner.run_git.return_value = CommandResult( - returncode=0, stdout="", stderr="" - ) - mock_runner.run.return_value = CommandResult(returncode=0, stdout="", stderr="") - - # Set up dependency injection - set_runner(mock_runner) - set_output(QuietOutputHandler()) - - repo_dir = tmp_path / "cpython" - repo_dir.mkdir(parents=True) - builds_dir = tmp_path / "builds" - - with ( - patch("every_python.main.REPO_DIR", repo_dir), - patch("every_python.main.BUILDS_DIR", builds_dir), - patch("platform.system", return_value="Linux"), - ): - build_dir = build_python("abc123d", enable_jit=False) - - # Should create non-JIT build directory - assert build_dir.name == "abc123d" - assert not build_dir.name.endswith("-jit") - - def test_build_with_jit_available(self, tmp_path: Path): - """Test building Python with JIT when LLVM is available.""" - # Create mock runner - mock_runner = Mock(spec=CommandRunner) - mock_runner.run_git.return_value = CommandResult( - returncode=0, stdout="", stderr="" - ) - mock_runner.run.return_value = CommandResult(returncode=0, stdout="", stderr="") - - # Set up dependency injection - set_runner(mock_runner) - set_output(QuietOutputHandler()) - - repo_dir = tmp_path / "cpython" - repo_dir.mkdir(parents=True) - builds_dir = tmp_path / "builds" - - with ( - patch("every_python.main.REPO_DIR", repo_dir), - patch("every_python.main.BUILDS_DIR", builds_dir), - patch("every_python.main.get_llvm_version_for_commit", return_value="20"), - patch("every_python.main.check_llvm_available", return_value=True), - ): - build_dir = build_python("abc123d", enable_jit=True) - - # Should create JIT build directory - assert build_dir.name == "abc123d-jit" - - # Should pass --experimental-jit to configure - configure_calls = [ - call_args - for call_args in mock_runner.run.call_args_list - if "./configure" in str(call_args) - ] - assert len(configure_calls) > 0 - assert "--experimental-jit" in str(configure_calls[0]) - - def test_build_with_jit_llvm_missing(self, tmp_path: Path): - """Test building with JIT when LLVM is missing falls back to non-JIT.""" - # Create mock runner - mock_runner = Mock(spec=CommandRunner) - mock_runner.run_git.return_value = CommandResult( - returncode=0, stdout="", stderr="" - ) - mock_runner.run.return_value = CommandResult(returncode=0, stdout="", stderr="") - - # Set up dependency injection - set_runner(mock_runner) - set_output(QuietOutputHandler()) - - repo_dir = tmp_path / "cpython" - repo_dir.mkdir(parents=True) - builds_dir = tmp_path / "builds" - - with ( - patch("every_python.main.REPO_DIR", repo_dir), - patch("every_python.main.BUILDS_DIR", builds_dir), - patch("every_python.main.get_llvm_version_for_commit", return_value="20"), - patch("every_python.main.check_llvm_available", return_value=False), - patch("typer.confirm", return_value=True), - ): - build_dir = build_python("abc123d", enable_jit=True) - - # Should fall back to non-JIT build - assert build_dir.name == "abc123d" - assert not build_dir.name.endswith("-jit") - - def test_build_with_jit_not_available_in_commit(self, tmp_path: Path): - """Test building with JIT when JIT not available in commit.""" - # Create mock runner - mock_runner = Mock(spec=CommandRunner) - mock_runner.run_git.return_value = CommandResult( - returncode=0, stdout="", stderr="" - ) - mock_runner.run.return_value = CommandResult(returncode=0, stdout="", stderr="") - - # Set up dependency injection - set_runner(mock_runner) - set_output(QuietOutputHandler()) - - repo_dir = tmp_path / "cpython" - repo_dir.mkdir(parents=True) - builds_dir = tmp_path / "builds" - - with ( - patch("every_python.main.REPO_DIR", repo_dir), - patch("every_python.main.BUILDS_DIR", builds_dir), - patch("every_python.main.get_llvm_version_for_commit", return_value=None), - patch("typer.confirm", return_value=True), - ): - build_dir = build_python("abc123d", enable_jit=True) - - # Should fall back to non-JIT build - assert build_dir.name == "abc123d" - - def test_build_already_exists(self, tmp_path: Path): - """Test that existing build is reused.""" - repo_dir = tmp_path / "cpython" - repo_dir.mkdir(parents=True) - builds_dir = tmp_path / "builds" - builds_dir.mkdir(parents=True) - - # Create existing build - existing_build = builds_dir / "abc123d" - existing_build.mkdir() - - with ( - patch("every_python.main.REPO_DIR", repo_dir), - patch("every_python.main.BUILDS_DIR", builds_dir), - patch("platform.system", return_value="Linux"), - ): - build_dir = build_python("abc123d", enable_jit=False) - - # Should return existing build without running any configure/make - assert build_dir == existing_build - - class TestInstallCommand: """Test the install command.""" From fd36045add2b1944026261a4ed9fdd76dad113c4 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:47:36 -0800 Subject: [PATCH 09/19] =?UTF-8?q?=F0=9F=92=9A=20Update=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 1 - .github/workflows/lint.yml | 10 ++++++---- .github/workflows/typecheck.yml | 10 ++++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 64362c4..87a081d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -1,7 +1,6 @@ name: Integration Tests on: - workflow_dispatch: # Manual trigger only push: branches: [ main ] paths: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7f412da..430550f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,12 +4,14 @@ on: push: branches: [ main ] paths: - - 'every-python/**/*.py' + - 'every_python/**' + - 'tests/**' - '.github/workflows/lint.yml' pull_request: branches: [ main ] paths: - - 'every-python/**/*.py' + - 'every_python/**' + - 'tests/**' - '.github/workflows/lint.yml' jobs: @@ -32,7 +34,7 @@ jobs: run: uv sync - name: Run Ruff linter - run: uv run ruff check every-python/ + run: uv run ruff check every_python/ - name: Run Ruff formatter - run: uv run ruff format --check every-python/ \ No newline at end of file + run: uv run ruff format --check every_python/ \ No newline at end of file diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 4446654..06f0e32 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -4,14 +4,16 @@ on: push: branches: [ main ] paths: - - 'every-python/**/*.py' - - 'every-python/pyproject.toml' + - 'every_python/**' + - 'tests/**' + - 'pyproject.toml' - '.github/workflows/typecheck.yml' pull_request: branches: [ main ] paths: - - 'every-python/**/*.py' - - 'every-python/pyproject.toml' + - 'every_python/**' + - 'tests/**' + - 'pyproject.toml' - '.github/workflows/typecheck.yml' jobs: From 93607f42b34da57d5b8da8c6ca08edccba6c4ea9 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:48:54 -0800 Subject: [PATCH 10/19] Add integration.yml run on PR --- .github/workflows/integration.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 87a081d..25ab103 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -1,11 +1,17 @@ name: Integration Tests on: + workflow_dispatch: push: branches: [ main ] paths: - 'every_python/**' - '.github/workflows/integration.yml' + pull_request: + branches: [ main ] + paths: + - 'every_python/**' + - '.github/workflows/integration.yml' jobs: integration: From f008e45870b3d9cf2e0743da026ebe1f2ba00aa0 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:50:52 -0800 Subject: [PATCH 11/19] =?UTF-8?q?=F0=9F=92=9A=20Editable=20install?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 25ab103..9d6119e 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -58,7 +58,10 @@ jobs: enable-cache: true - name: Install dependencies - run: uv sync + run: uv sync --all-groups + + - name: Install package in editable mode + run: uv pip install -e . - name: Test LLVM detection run: | From 854d821d3af893145ca6ef009a5f264d87bc1e4f Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 18:56:59 -0800 Subject: [PATCH 12/19] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- every_python/main.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/every_python/main.py b/every_python/main.py index 7125cce..0072751 100644 --- a/every_python/main.py +++ b/every_python/main.py @@ -50,7 +50,7 @@ def ensure_repo() -> Path: output.error(f"Failed to clone CPython: {result.stderr}") raise typer.Exit(1) - output.success("✓ Repository cloned successfully") + output.success("Repository cloned successfully") return REPO_DIR @@ -155,7 +155,7 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - # Add JIT flag if enabled if enable_jit: - configure_args.append("--experimental-jit") + configure_args.append("--enable-experimental-jit") if verbose: progress.stop() output.status(f"Running: {' '.join(configure_args)}") @@ -231,7 +231,7 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - output.error(f"Install failed: {install_result.stderr}") raise typer.Exit(1) - progress.update(task, description=f"[green]✓ Built {commit[:7]}[/green]") + progress.update(task, description=f"[green]Built {commit[:7]}[/green]") return build_dir @@ -396,7 +396,7 @@ def parse_version(version_str: str) -> tuple[int, int, int, str]: commit_msg = "" if version != "unknown": - jit_text = "✓" if build_info.jit_enabled else "" + jit_text = "[JIT]" if build_info.jit_enabled else "" table.add_row( version.replace("Python ", ""), jit_text, @@ -426,7 +426,7 @@ def clean( if all: if BUILDS_DIR.exists(): shutil.rmtree(BUILDS_DIR) - output.success("✓ Removed all builds") + output.success("Removed all builds") else: output.warning("No builds to remove") elif ref: @@ -444,7 +444,7 @@ def clean( if removed: variants = " and ".join(removed) - output.success(f"✓ Removed {variants} build(s) for {commit[:7]}") + output.success(f"Removed {variants} build(s) for {commit[:7]}") else: output.warning(f"No builds found for {commit[:7]}") except typer.Exit: @@ -587,7 +587,7 @@ def is_bisect_done() -> bool: # Handle exit codes like every-ts if test_result.returncode == 0: - output.success("✓ Test passed (exit 0) - marking as good") + output.success("Test passed (exit 0) - marking as good") bisect_result = runner.run_git( ["bisect", "good"], REPO_DIR, check=True ) From 8c0206e1a661e12b6e6b1b80a2be25fa707052b7 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:09:38 -0800 Subject: [PATCH 13/19] =?UTF-8?q?=F0=9F=91=B7=20Fix=20ascii=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 2 +- every_python/main.py | 11 ++---- every_python/output.py | 61 +++++++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 9d6119e..978834d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -77,4 +77,4 @@ jobs: run: uv run every-python install main --jit --verbose - name: Verify JIT build works - run: uv run every-python run main --version + run: uv run every-python run main -- --version diff --git a/every_python/main.py b/every_python/main.py index 0072751..7c030e7 100644 --- a/every_python/main.py +++ b/every_python/main.py @@ -6,11 +6,10 @@ import typer from rich.console import Console -from rich.progress import Progress, SpinnerColumn, TextColumn from rich.table import Table from typing_extensions import Annotated -from every_python.output import get_output +from every_python.output import create_progress, get_output, jit_indicator from every_python.runner import CommandResult, CommandRunner, get_runner from every_python.utils import ( BuildInfo, @@ -124,11 +123,7 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - ) return build_dir - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: + with create_progress(console) as progress: # Checkout the commit task = progress.add_task(f"Checking out {commit[:7]}...", total=None) result = runner.run_git(["checkout", commit], REPO_DIR) @@ -396,7 +391,7 @@ def parse_version(version_str: str) -> tuple[int, int, int, str]: commit_msg = "" if version != "unknown": - jit_text = "[JIT]" if build_info.jit_enabled else "" + jit_text = jit_indicator() if build_info.jit_enabled else "" table.add_row( version.replace("Python ", ""), jit_text, diff --git a/every_python/output.py b/every_python/output.py index d865780..04ec9fc 100644 --- a/every_python/output.py +++ b/every_python/output.py @@ -1,7 +1,10 @@ """Output handler abstraction for testability and flexibility.""" +import os +import sys from abc import ABC, abstractmethod from rich.console import Console +from rich.progress import Progress, SpinnerColumn, TextColumn class OutputHandler(ABC): @@ -36,14 +39,21 @@ def status(self, message: str) -> None: class RichOutputHandler(OutputHandler): """Rich console output handler.""" - def __init__(self, console: Console | None = None): + def __init__(self, console: Console | None = None, use_unicode: bool = True): self.console = console or Console() + self.use_unicode = use_unicode + + def _format_success(self, message: str) -> str: + """Format success message with optional checkmark.""" + if self.use_unicode: + return f"✓ {message}" + return message def info(self, message: str) -> None: self.console.print(message) def success(self, message: str) -> None: - self.console.print(f"[green]{message}[/green]") + self.console.print(f"[green]{self._format_success(message)}[/green]") def warning(self, message: str) -> None: self.console.print(f"[yellow]{message}[/yellow]") @@ -81,11 +91,56 @@ def status(self, message: str) -> None: _default_output: OutputHandler | None = None +# Helpers for determining output capabilities +def _should_use_ascii() -> bool: + """Check if we should use ASCII-only output (no Unicode).""" + # Check if we're in CI with limited encoding support + if os.getenv("CI") and sys.stdout.encoding in ("cp1252", "ascii"): + return True + return False + + +def create_progress(console: Console) -> Progress: + """Create a Progress instance with appropriate settings for the environment.""" + if _should_use_ascii(): + # Use simple text-only progress without spinners for ASCII-only environments + return Progress( + TextColumn("[progress.description]{task.description}"), + console=console, + transient=True, + ) + else: + # Use fancy spinners for Unicode-capable environments + return Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + console=console, + ) + + +def jit_indicator() -> str: + """Get the JIT indicator based on environment.""" + if _should_use_ascii(): + return "[JIT]" + else: + return "✓" + + def get_output() -> OutputHandler: """Get the default output handler.""" global _default_output if _default_output is None: - _default_output = RichOutputHandler() + # Force ASCII-safe output in CI environments with limited encoding + force_ascii = _should_use_ascii() + console = ( + Console( + legacy_windows=False, # Disable legacy Windows rendering + force_terminal=not force_ascii, # Disable terminal features if ASCII-only + ) + if force_ascii + else Console() + ) + _default_output = RichOutputHandler(console, use_unicode=not force_ascii) return _default_output From 71c2d56cc971b2304354ac140bd3c061f80129e2 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:14:24 -0800 Subject: [PATCH 14/19] =?UTF-8?q?=F0=9F=90=9B=20Try=20a=20different=20wind?= =?UTF-8?q?ows=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- every_python/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/every_python/main.py b/every_python/main.py index 7c030e7..d7e9a27 100644 --- a/every_python/main.py +++ b/every_python/main.py @@ -137,7 +137,8 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - progress.update(task, description="Configuring build...") if platform.system() == "Windows": - configure_args = ["PCbuild\\build.bat", "-c", "Debug"] + # Windows build uses PCbuild\build.bat via cmd + configure_args = ["cmd", "/c", "PCbuild\\build.bat", "-c", "Debug"] if enable_jit: configure_args.append("--experimental-jit") else: From a856b09ab511e631da0920efe68d30318c278116 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:15:21 -0800 Subject: [PATCH 15/19] =?UTF-8?q?=F0=9F=90=9B=20Add=20jit=20flag=20to=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 978834d..396d0e6 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -77,4 +77,4 @@ jobs: run: uv run every-python install main --jit --verbose - name: Verify JIT build works - run: uv run every-python run main -- --version + run: uv run every-python run main -- --jit --version From 4e186788ed70908465759b49822aa40c01f9dae5 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:22:10 -0800 Subject: [PATCH 16/19] =?UTF-8?q?=F0=9F=90=9B=20Update=20binary=20location?= =?UTF-8?q?=20for=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- every_python/main.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/every_python/main.py b/every_python/main.py index d7e9a27..27440cf 100644 --- a/every_python/main.py +++ b/every_python/main.py @@ -176,19 +176,27 @@ def build_python(commit: str, enable_jit: bool = False, verbose: bool = False) - ncpu = multiprocessing.cpu_count() if platform.system() == "Windows": - # Windows: build.bat does both build and "install" (outputs to PCbuild/amd64) + # Windows: build.bat does both build and "install" (outputs to PCbuild/amd64 or PCbuild/win32) # The configure step above already ran build.bat, so we're done - # Just move the output to our build directory + # Just copy the output to our build directory progress.update(task, description="Copying build artifacts...") import shutil + # Try both amd64 and win32 architectures pcbuild_dir = REPO_DIR / "PCbuild" / "amd64" + if not pcbuild_dir.exists(): + pcbuild_dir = REPO_DIR / "PCbuild" / "win32" + if not pcbuild_dir.exists(): progress.stop() - output.error(f"Build output not found at {pcbuild_dir}") + output.error("Build output not found in PCbuild directory") raise typer.Exit(1) build_dir.mkdir(parents=True, exist_ok=True) + + if verbose: + output.status(f"Copying from {pcbuild_dir} to {build_dir}") + shutil.copytree(pcbuild_dir, build_dir, dirs_exist_ok=True) else: # Unix: use make From 14d90fcdcc650eb1e2ceecace26b62841f6460aa Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:23:58 -0800 Subject: [PATCH 17/19] =?UTF-8?q?=F0=9F=90=9B=20Typo=20in=20jit=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 2 +- test_jit_api.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test_jit_api.py diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 396d0e6..ada71ef 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -77,4 +77,4 @@ jobs: run: uv run every-python install main --jit --verbose - name: Verify JIT build works - run: uv run every-python run main -- --jit --version + run: uv run every-python run main --jit -- --version diff --git a/test_jit_api.py b/test_jit_api.py new file mode 100644 index 0000000..9b04496 --- /dev/null +++ b/test_jit_api.py @@ -0,0 +1,6 @@ +import sys +# Exit 0 (good) = feature doesn't exist yet +# Exit 1 (bad) = feature exists +if hasattr(sys, "_jit"): + sys.exit(1) # Feature exists - mark as "bad" +sys.exit(0) # Feature doesn't exist - mark as "good" \ No newline at end of file From 760929311b0b6cc1a03aa6d956e184cc4648f867 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:32:09 -0800 Subject: [PATCH 18/19] =?UTF-8?q?=F0=9F=90=9B=20Try=20Windows=20path=20aga?= =?UTF-8?q?in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- every_python/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/every_python/utils.py b/every_python/utils.py index 9d6d83a..d6e69c5 100644 --- a/every_python/utils.py +++ b/every_python/utils.py @@ -186,6 +186,10 @@ def _check_tool_version(tool_name: str, expected_version: str) -> bool: def python_binary_location(builds_dir: Path, build_info: "BuildInfo") -> Path: """Get the path to the Python binary for a given build.""" if platform.system() == "Windows": + # Windows debug builds use python_d.exe, try that first + debug_binary = builds_dir / build_info.directory_name / "python_d.exe" + if debug_binary.exists(): + return debug_binary return builds_dir / build_info.directory_name / "python.exe" else: return builds_dir / build_info.directory_name / "bin" / "python3" From 56f083024309b70cbaad1672a9320fd96f7dfc3f Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 10 Nov 2025 19:43:18 -0800 Subject: [PATCH 19/19] =?UTF-8?q?=F0=9F=92=9A=20Clean=20up=20CI=20for=20Wi?= =?UTF-8?q?ndows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index ada71ef..5d71f1d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -46,12 +46,6 @@ jobs: if: runner.os == 'macOS' run: brew install llvm@20 - - name: Install LLVM (Windows) - if: runner.os == 'Windows' - run: | - choco install llvm --version=20.1.8 -y --force - echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Install uv uses: astral-sh/setup-uv@v7 with: @@ -63,7 +57,8 @@ jobs: - name: Install package in editable mode run: uv pip install -e . - - name: Test LLVM detection + - name: Test LLVM detection (Linux/macOS) + if: runner.os != 'Windows' run: | uv run python -c "from every_python.utils import check_llvm_available; import sys; sys.exit(0 if check_llvm_available('20') else 1)"