workers-py 1.1.5__tar.gz → 1.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {workers_py-1.1.5 → workers_py-1.1.7}/CHANGELOG.md +17 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/PKG-INFO +1 -1
- {workers_py-1.1.5 → workers_py-1.1.7}/pyproject.toml +1 -1
- {workers_py-1.1.5 → workers_py-1.1.7}/src/pywrangler/cli.py +6 -5
- {workers_py-1.1.5 → workers_py-1.1.7}/src/pywrangler/sync.py +53 -4
- {workers_py-1.1.5 → workers_py-1.1.7}/src/pywrangler/utils.py +17 -4
- {workers_py-1.1.5 → workers_py-1.1.7}/tests/test_cli.py +57 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/uv.lock +1 -1
- {workers_py-1.1.5 → workers_py-1.1.7}/.github/workflows/commitlint.yml +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/.github/workflows/lint.yml +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/.github/workflows/release.yml +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/.github/workflows/tests.yml +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/.gitignore +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/CONTRIBUTING.md +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/README.md +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/src/pywrangler/__init__.py +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/src/pywrangler/__main__.py +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/tests/__init__.py +0 -0
- {workers_py-1.1.5 → workers_py-1.1.7}/workers.py +0 -0
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.1.7 (2025-08-28)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Check for venv python version mismatch
|
|
10
|
+
([`c7871f0`](https://github.com/cloudflare/workers-py/commit/c7871f07dcc2ad54f0cd9e0243ff5107cf43d9c9))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v1.1.6 (2025-08-27)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Sync: if nothing to do, only warn if user requested directly
|
|
18
|
+
([#26](https://github.com/cloudflare/workers-py/pull/26),
|
|
19
|
+
[`e142800`](https://github.com/cloudflare/workers-py/commit/e142800306cf4a021c10c629814265ed63d9cd90))
|
|
20
|
+
|
|
21
|
+
|
|
5
22
|
## v1.1.5 (2025-08-26)
|
|
6
23
|
|
|
7
24
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: workers-py
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.7
|
|
4
4
|
Summary: A set of libraries and tools for Python Workers
|
|
5
5
|
Project-URL: Homepage, https://github.com/cloudflare/workers-py
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/cloudflare/workers-py/issues
|
|
@@ -55,7 +55,7 @@ class ProxyToWranglerGroup(click.Group):
|
|
|
55
55
|
remaining_args = []
|
|
56
56
|
|
|
57
57
|
if cmd_name in ["dev", "publish", "deploy", "versions"]:
|
|
58
|
-
ctx.invoke(sync_command, force=False)
|
|
58
|
+
ctx.invoke(sync_command, force=False, directly_requested=False)
|
|
59
59
|
|
|
60
60
|
_proxy_to_wrangler(cmd_name, remaining_args)
|
|
61
61
|
sys.exit(0)
|
|
@@ -92,7 +92,7 @@ def app(ctx, debug=False):
|
|
|
92
92
|
|
|
93
93
|
@app.command("sync")
|
|
94
94
|
@click.option("--force", is_flag=True, help="Force sync even if no changes detected")
|
|
95
|
-
def sync_command(force=False):
|
|
95
|
+
def sync_command(force=False, directly_requested=True):
|
|
96
96
|
"""
|
|
97
97
|
Installs Python packages from pyproject.toml into src/vendor.
|
|
98
98
|
|
|
@@ -116,9 +116,10 @@ def sync_command(force=False):
|
|
|
116
116
|
# Check if sync is needed based on file timestamps
|
|
117
117
|
sync_needed = force or is_sync_needed()
|
|
118
118
|
if not sync_needed:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
if directly_requested:
|
|
120
|
+
logger.warning(
|
|
121
|
+
"pyproject.toml hasn't changed since last sync, use --force to ignore timestamp check"
|
|
122
|
+
)
|
|
122
123
|
return
|
|
123
124
|
|
|
124
125
|
# Check to make sure a wrangler config file exists.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import shutil
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
import tempfile
|
|
5
6
|
|
|
@@ -59,18 +60,66 @@ def _get_python_version():
|
|
|
59
60
|
return os.environ.get("_PYWRANGLER_PYTHON_VERSION", "3.12")
|
|
60
61
|
|
|
61
62
|
|
|
63
|
+
def _get_venv_python_version() -> str | None:
|
|
64
|
+
"""
|
|
65
|
+
Retrieves the Python version from the virtual environment.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The Python version string or None if it cannot be determined.
|
|
69
|
+
"""
|
|
70
|
+
venv_python = (
|
|
71
|
+
VENV_WORKERS_PATH / "Scripts" / "python.exe"
|
|
72
|
+
if os.name == "nt"
|
|
73
|
+
else VENV_WORKERS_PATH / "bin" / "python"
|
|
74
|
+
)
|
|
75
|
+
if not venv_python.is_file():
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
result = run_command(
|
|
79
|
+
[str(venv_python), "--version"], check=False, capture_output=True
|
|
80
|
+
)
|
|
81
|
+
if result.returncode != 0:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
return result.stdout.strip()
|
|
85
|
+
|
|
86
|
+
|
|
62
87
|
def create_workers_venv():
|
|
63
88
|
"""
|
|
64
89
|
Creates a virtual environment at `VENV_WORKERS_PATH` if it doesn't exist.
|
|
65
90
|
"""
|
|
91
|
+
wanted_python_version = _get_python_version()
|
|
92
|
+
logger.debug(f"Using python version: {wanted_python_version}")
|
|
93
|
+
|
|
66
94
|
if VENV_WORKERS_PATH.is_dir():
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
installed_version = _get_venv_python_version()
|
|
96
|
+
if installed_version:
|
|
97
|
+
if wanted_python_version in installed_version:
|
|
98
|
+
logger.debug(
|
|
99
|
+
f"Virtual environment at {VENV_WORKERS_PATH} already exists."
|
|
100
|
+
)
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
logger.warning(
|
|
104
|
+
f"Recreating virtual environment at {VENV_WORKERS_PATH} due to Python version mismatch. "
|
|
105
|
+
f"Found {installed_version}, expected {wanted_python_version}"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
logger.warning(
|
|
109
|
+
f"Could not determine python version for {VENV_WORKERS_PATH}, recreating."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
shutil.rmtree(VENV_WORKERS_PATH)
|
|
69
113
|
|
|
70
114
|
logger.debug(f"Creating virtual environment at {VENV_WORKERS_PATH}...")
|
|
71
|
-
python_version = _get_python_version()
|
|
72
115
|
run_command(
|
|
73
|
-
[
|
|
116
|
+
[
|
|
117
|
+
"uv",
|
|
118
|
+
"venv",
|
|
119
|
+
str(VENV_WORKERS_PATH),
|
|
120
|
+
"--python",
|
|
121
|
+
f"python{wanted_python_version}",
|
|
122
|
+
]
|
|
74
123
|
)
|
|
75
124
|
|
|
76
125
|
|
|
@@ -50,7 +50,21 @@ def run_command(
|
|
|
50
50
|
cwd: Path | None = None,
|
|
51
51
|
env: dict | None = None,
|
|
52
52
|
check: bool = True,
|
|
53
|
+
capture_output: bool = False,
|
|
53
54
|
):
|
|
55
|
+
"""
|
|
56
|
+
Runs a command and handles logging and errors.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
command: The command to run as a list of strings.
|
|
60
|
+
cwd: The working directory.
|
|
61
|
+
env: Environment variables.
|
|
62
|
+
check: If True, raise an exception on non-zero exit codes.
|
|
63
|
+
capture_output: If True, capture and return stdout/stderr.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
A subprocess.CompletedProcess instance.
|
|
67
|
+
"""
|
|
54
68
|
logger.log(RUNNING_LEVEL, f"{' '.join(str(arg) for arg in command)}")
|
|
55
69
|
try:
|
|
56
70
|
process = subprocess.run(
|
|
@@ -58,16 +72,15 @@ def run_command(
|
|
|
58
72
|
cwd=cwd,
|
|
59
73
|
env=env,
|
|
60
74
|
check=check,
|
|
61
|
-
|
|
62
|
-
stderr=subprocess.STDOUT,
|
|
75
|
+
capture_output=capture_output,
|
|
63
76
|
text=True,
|
|
64
77
|
)
|
|
65
|
-
if process.stdout:
|
|
78
|
+
if process.stdout and not capture_output:
|
|
66
79
|
logger.log(OUTPUT_LEVEL, f"{process.stdout.strip()}")
|
|
67
80
|
return process
|
|
68
81
|
except subprocess.CalledProcessError as e:
|
|
69
82
|
logger.error(
|
|
70
|
-
f"Error running command: {' '.join(str(arg) for arg in command)}\nExit code: {e.returncode}\nOutput:\n{e.stdout.strip()}"
|
|
83
|
+
f"Error running command: {' '.join(str(arg) for arg in command)}\nExit code: {e.returncode}\nOutput:\n{e.stdout.strip() if e.stdout else ''}{e.stderr.strip() if e.stderr else ''}"
|
|
71
84
|
)
|
|
72
85
|
raise click.exceptions.Exit(code=e.returncode)
|
|
73
86
|
except FileNotFoundError:
|
|
@@ -485,3 +485,60 @@ def test_sync_command_finds_pyproject_in_parent_directory(clean_test_dir):
|
|
|
485
485
|
assert TEST_VENV_WORKERS.exists(), (
|
|
486
486
|
f".venv-workers directory was not created at {TEST_VENV_WORKERS}"
|
|
487
487
|
)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_sync_recreates_venv_on_python_version_mismatch(clean_test_dir):
|
|
491
|
+
"""
|
|
492
|
+
Test that the sync command recreates the venv if the Python version
|
|
493
|
+
mismatches, using real system commands.
|
|
494
|
+
"""
|
|
495
|
+
# Create initial files in the clean test directory
|
|
496
|
+
create_test_pyproject([])
|
|
497
|
+
create_test_wrangler_jsonc()
|
|
498
|
+
|
|
499
|
+
original_dir = os.getcwd()
|
|
500
|
+
try:
|
|
501
|
+
os.chdir(clean_test_dir)
|
|
502
|
+
project_root = Path(original_dir)
|
|
503
|
+
sync_cmd = ["uvx", "--from", str(project_root), "pywrangler", "sync"]
|
|
504
|
+
venv_path = clean_test_dir / ".venv-workers"
|
|
505
|
+
|
|
506
|
+
# First run: Create venv with Python 3.12
|
|
507
|
+
print("\nRunning sync to create venv with Python 3.12...")
|
|
508
|
+
env = os.environ.copy()
|
|
509
|
+
env["_PYWRANGLER_PYTHON_VERSION"] = "3.12"
|
|
510
|
+
result1 = subprocess.run(sync_cmd, capture_output=True, text=True, env=env)
|
|
511
|
+
|
|
512
|
+
assert result1.returncode == 0, (
|
|
513
|
+
f"First sync failed: {result1.stdout}\n{result1.stderr}"
|
|
514
|
+
)
|
|
515
|
+
assert venv_path.exists(), "Venv was not created on the first run."
|
|
516
|
+
initial_mtime = venv_path.stat().st_mtime
|
|
517
|
+
|
|
518
|
+
# Second run: Recreate venv with Python 3.13
|
|
519
|
+
print("\nRunning sync to recreate venv with Python 3.13...")
|
|
520
|
+
env["_PYWRANGLER_PYTHON_VERSION"] = "3.13"
|
|
521
|
+
result2 = subprocess.run(sync_cmd, capture_output=True, text=True, env=env)
|
|
522
|
+
|
|
523
|
+
assert result2.returncode == 0, (
|
|
524
|
+
f"Second sync failed: {result2.stdout}\n{result2.stderr}"
|
|
525
|
+
)
|
|
526
|
+
assert venv_path.exists(), "Venv was not recreated."
|
|
527
|
+
final_mtime = venv_path.stat().st_mtime
|
|
528
|
+
|
|
529
|
+
# Check that the venv was actually modified
|
|
530
|
+
assert final_mtime > initial_mtime, "Venv modification time did not change."
|
|
531
|
+
|
|
532
|
+
# Verify the python version in the new venv is 3.13.
|
|
533
|
+
python_exe = venv_path / (
|
|
534
|
+
"Scripts/python.exe" if os.name == "nt" else "bin/python"
|
|
535
|
+
)
|
|
536
|
+
version_result = subprocess.run(
|
|
537
|
+
[str(python_exe), "--version"], capture_output=True, text=True
|
|
538
|
+
)
|
|
539
|
+
assert "3.13" in version_result.stdout, (
|
|
540
|
+
f"Python version is not 3.13: {version_result.stdout}"
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
finally:
|
|
544
|
+
os.chdir(original_dir)
|
|
@@ -402,7 +402,7 @@ wheels = [
|
|
|
402
402
|
|
|
403
403
|
[[package]]
|
|
404
404
|
name = "workers-py"
|
|
405
|
-
version = "1.1.
|
|
405
|
+
version = "1.1.7"
|
|
406
406
|
source = { editable = "." }
|
|
407
407
|
dependencies = [
|
|
408
408
|
{ name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|