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
6 changes: 4 additions & 2 deletions src/pywrangler/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import subprocess
import sys
import textwrap
from pathlib import Path
from typing import Never

import click
Expand All @@ -12,6 +13,7 @@
WRANGLER_CREATE_COMMAND,
check_wrangler_version,
log_startup_info,
run_command,
setup_logging,
write_success,
)
Expand Down Expand Up @@ -149,7 +151,7 @@ def _proxy_to_wrangler(command_name: str, args_list: list[str]) -> Never:
command_to_run = WRANGLER_COMMAND + [command_name] + args_list
logger.info(f"Passing command to npx wrangler: {' '.join(command_to_run)}")
try:
process = subprocess.run(command_to_run, check=False, cwd=".")
process = run_command(command_to_run, check=False, cwd=Path("."))
click.get_current_context().exit(process.returncode)
except FileNotFoundError as e:
logger.error(
Expand All @@ -162,7 +164,7 @@ def _proxy_to_create_cloudflare(args_list: list[str]) -> Never:
command_to_run = WRANGLER_CREATE_COMMAND + args_list
logger.info(f"Passing command to npx create-cloudflare: {' '.join(command_to_run)}")
try:
process = subprocess.run(command_to_run, check=False, cwd=".")
process = run_command(command_to_run, check=False, cwd=Path("."))
click.get_current_context().exit(process.returncode)
except FileNotFoundError as e:
logger.error(
Expand Down
12 changes: 7 additions & 5 deletions src/pywrangler/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def create_pyodide_venv() -> None:
pyodide_venv_path.parent.mkdir(parents=True, exist_ok=True)
interp_name = get_uv_pyodide_interp_name()
run_command(["uv", "python", "install", interp_name])
run_command(["uv", "venv", pyodide_venv_path, "--python", interp_name])
run_command(["uv", "venv", str(pyodide_venv_path), "--python", interp_name])


def parse_requirements() -> list[str]:
Expand Down Expand Up @@ -195,7 +195,7 @@ def _install_requirements_to_vendor(requirements: list[str]) -> None:
],
capture_output=True,
check=False,
env=os.environ | {"VIRTUAL_ENV": get_pyodide_venv_path()},
env=os.environ | {"VIRTUAL_ENV": str(get_pyodide_venv_path())},
)
if result.returncode != 0:
logger.warning(result.stdout.strip())
Expand Down Expand Up @@ -223,9 +223,11 @@ def _install_requirements_to_vendor(requirements: list[str]) -> None:

pyv = get_python_version()
shutil.rmtree(vendor_path)
shutil.copytree(
get_pyodide_venv_path() / f"lib/python{pyv}/site-packages", vendor_path

site_packages_path = (
f"lib/python{pyv}/site-packages" if os.name != "nt" else "Lib/site-packages"
)
shutil.copytree(get_pyodide_venv_path() / site_packages_path, vendor_path)

# Create a pyvenv.cfg file in python_modules to mark it as a virtual environment
(vendor_path / "pyvenv.cfg").touch()
Expand Down Expand Up @@ -273,7 +275,7 @@ def _install_requirements_to_venv(requirements: list[str]) -> None:
requirements_file,
],
check=False,
env=os.environ | {"VIRTUAL_ENV": venv_workers_path},
env=os.environ | {"VIRTUAL_ENV": str(venv_workers_path)},
capture_output=True,
)
if result.returncode != 0:
Expand Down
8 changes: 5 additions & 3 deletions src/pywrangler/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ def wrangler_types(outdir_arg: str | None, config: str | None, /) -> None:
stubs_dir.mkdir(parents=True, exist_ok=True)
with TemporaryDirectory() as tmp_str:
tmp = Path(tmp_str)
run_command(WRANGLER_COMMAND + args + [tmp / "worker-configuration.d.ts"])
run_command(WRANGLER_COMMAND + args + [str(tmp / "worker-configuration.d.ts")])
(tmp / "tsconfig.json").write_text(TSCONFIG)
(tmp / "package.json").write_text(PACKAGE_JSON)
run_command(["npm", "-C", tmp, "install"])
run_command(["npx", "@pyodide/ts-to-python", tmp, stubs_dir / "__init__.pyi"])
run_command(["npm", "-C", str(tmp), "install"])
run_command(
["npx", "@pyodide/ts-to-python", str(tmp), str(stubs_dir / "__init__.pyi")]
)
17 changes: 15 additions & 2 deletions src/pywrangler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
import os
import platform
import re
import shutil
import subprocess
import sys
import tomllib
from collections.abc import Mapping
from datetime import datetime
from functools import cache
from pathlib import Path
Expand Down Expand Up @@ -118,9 +120,9 @@ def write_success(msg: str) -> None:


def run_command(
command: list[str | Path],
command: list[str],
cwd: Path | None = None,
env: dict[str, str | Path] | None = None,
env: Mapping[str, str | Path] | None = None,
check: bool = True,
capture_output: bool = False,
) -> subprocess.CompletedProcess[str]:
Expand All @@ -138,6 +140,16 @@ def run_command(
A subprocess.CompletedProcess instance.
"""
logger.log(RUNNING_LEVEL, f"{' '.join(str(arg) for arg in command)}")

# Some tools like `npm` may be a batch file on Windows (npm.cmd), and calling them only by
# name may fails in subprocess.run. Use shutil.which to find the real name.
abspath = shutil.which(command[0])
if not abspath:
logger.error(f"Command not found: {command[0]}. Is it installed and in PATH?")
raise click.exceptions.Exit(code=1)

realname = str(Path(command[0]).with_name(Path(abspath).name))
command = [realname] + command[1:]
try:
kwargs = {}
if capture_output:
Expand All @@ -149,6 +161,7 @@ def run_command(
env=env,
check=check,
text=True,
encoding="utf-8",
**kwargs,
) # type: ignore[call-overload]
if process.stdout and not capture_output:
Expand Down
13 changes: 11 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ def test_dir(monkeypatch):
monkeypatch.setattr(
pywrangler_utils, "find_pyproject_toml", lambda: test_dir / "pyproject.toml"
)
yield test_dir.absolute()

try:
yield test_dir.absolute()
finally:
shutil.rmtree(test_dir, ignore_errors=True)


def create_test_pyproject(test_dir: Path, dependencies=None):
Expand Down Expand Up @@ -410,7 +414,12 @@ def test_proxy_to_wrangler_handles_subprocess_error(mock_subprocess_run):

# Verify the error was attempted to be called
mock_subprocess_run.assert_called_once_with(
["npx", "--yes", "wrangler", "unknown_command"], check=False, cwd="."
["npx", "--yes", "wrangler", "unknown_command"],
check=False,
cwd=Path("."),
env=None,
text=True,
encoding="utf-8",
)


Expand Down