workers-py 1.2.1__py3-none-any.whl → 1.4.0__py3-none-any.whl

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.
pywrangler/cli.py CHANGED
@@ -57,6 +57,11 @@ class ProxyToWranglerGroup(click.Group):
57
57
  if cmd_name in ["dev", "publish", "deploy", "versions"]:
58
58
  ctx.invoke(sync_command, force=False, directly_requested=False)
59
59
 
60
+ if cmd_name == "dev":
61
+ from pywrangler.sync import check_wrangler_version
62
+
63
+ check_wrangler_version()
64
+
60
65
  _proxy_to_wrangler(cmd_name, remaining_args)
61
66
  sys.exit(0)
62
67
 
pywrangler/metadata.py ADDED
@@ -0,0 +1,16 @@
1
+ from typing import Literal, NamedTuple
2
+ from datetime import datetime
3
+
4
+
5
+ class PythonCompatVersion(NamedTuple):
6
+ version: Literal["3.12", "3.13"]
7
+ compat_flag: str
8
+ compat_date: datetime | None
9
+
10
+
11
+ PYTHON_COMPAT_VERSIONS = [
12
+ PythonCompatVersion(
13
+ "3.13", "python_workers_20250116", datetime.strptime("2025-09-29", "%Y-%m-%d")
14
+ ),
15
+ PythonCompatVersion("3.12", "python_workers", None),
16
+ ]
pywrangler/sync.py CHANGED
@@ -1,17 +1,21 @@
1
1
  import logging
2
2
  import os
3
+ import re
3
4
  import shutil
4
5
  import tempfile
5
6
  from contextlib import contextmanager
7
+ from datetime import datetime
6
8
  from pathlib import Path
7
9
  from typing import Literal
8
10
 
9
11
  import click
12
+ import pyjson5
10
13
 
11
14
  from pywrangler.utils import (
12
15
  run_command,
13
16
  find_pyproject_toml,
14
17
  )
18
+ from pywrangler.metadata import PYTHON_COMPAT_VERSIONS
15
19
 
16
20
  try:
17
21
  import tomllib # Standard in Python 3.11+
@@ -60,15 +64,88 @@ def check_wrangler_config():
60
64
  raise click.exceptions.Exit(code=1)
61
65
 
62
66
 
67
+ def _parse_wrangler_config() -> dict:
68
+ """
69
+ Parse wrangler configuration from either wrangler.toml or wrangler.jsonc.
70
+
71
+ Returns:
72
+ dict: Parsed configuration data
73
+ """
74
+ wrangler_toml = PROJECT_ROOT / "wrangler.toml"
75
+ wrangler_jsonc = PROJECT_ROOT / "wrangler.jsonc"
76
+
77
+ if wrangler_toml.is_file():
78
+ try:
79
+ with open(wrangler_toml, "rb") as f:
80
+ return tomllib.load(f)
81
+ except tomllib.TOMLDecodeError as e:
82
+ logger.error(f"Error parsing {wrangler_toml}: {e}")
83
+ raise click.exceptions.Exit(code=1)
84
+
85
+ if wrangler_jsonc.is_file():
86
+ try:
87
+ with open(wrangler_jsonc, "r") as f:
88
+ content = f.read()
89
+ return pyjson5.loads(content)
90
+ except (pyjson5.Json5DecoderError, ValueError) as e:
91
+ logger.error(f"Error parsing {wrangler_jsonc}: {e}")
92
+ raise click.exceptions.Exit(code=1)
93
+
94
+ return {}
95
+
96
+
63
97
  def _get_python_version() -> Literal["3.12", "3.13"]:
64
- res = os.environ.get("_PYWRANGLER_PYTHON_VERSION", "3.12")
65
- match res:
66
- case "3.12" | "3.13":
67
- return res
68
- case _:
69
- raise ValueError(
70
- f"Unexpected value for Python version '{res}', expected '3.12' or '3.13'"
71
- )
98
+ """
99
+ Determine Python version from wrangler configuration.
100
+
101
+ Returns:
102
+ Python version string
103
+ """
104
+ config = _parse_wrangler_config()
105
+
106
+ if not config:
107
+ logger.error("No wrangler config found")
108
+ raise click.exceptions.Exit(code=1)
109
+
110
+ compat_flags = config.get("compatibility_flags", [])
111
+
112
+ if "compatibility_date" not in config:
113
+ logger.error("No compatibility_date specified in wrangler config")
114
+ raise click.exceptions.Exit(code=1)
115
+ try:
116
+ compat_date = datetime.strptime(config.get("compatibility_date"), "%Y-%m-%d")
117
+ except ValueError:
118
+ logger.error(
119
+ f"Invalid compatibility_date format: {config.get('compatibility_date')}"
120
+ )
121
+ raise click.exceptions.Exit(code=1)
122
+
123
+ # Check if python_workers base flag is present (required for Python workers)
124
+ if "python_workers" not in compat_flags:
125
+ logger.error("`python_workers` compat flag not specified in wrangler config")
126
+ raise click.exceptions.Exit(code=1)
127
+
128
+ # Find the most specific Python version based on compat flags and date
129
+ # Sort by version descending to prioritize newer versions
130
+ sorted_versions = sorted(
131
+ PYTHON_COMPAT_VERSIONS, key=lambda x: x.version, reverse=True
132
+ )
133
+
134
+ for py_version in sorted_versions:
135
+ # Check if the specific compat flag is present
136
+ if py_version.compat_flag in compat_flags:
137
+ return py_version.version
138
+
139
+ # For versions with compat_date, also check the date requirement
140
+ if (
141
+ py_version.compat_date
142
+ and compat_date
143
+ and compat_date >= py_version.compat_date
144
+ ):
145
+ return py_version.version
146
+
147
+ logger.error("Could not determine Python version from wrangler config")
148
+ raise click.exceptions.Exit(code=1)
72
149
 
73
150
 
74
151
  def _get_uv_pyodide_interp_name():
@@ -118,7 +195,7 @@ def create_workers_venv():
118
195
  Creates a virtual environment at `VENV_WORKERS_PATH` if it doesn't exist.
119
196
  """
120
197
  wanted_python_version = _get_python_version()
121
- logger.debug(f"Using python version: {wanted_python_version}")
198
+ logger.debug(f"Using python version from wrangler config: {wanted_python_version}")
122
199
 
123
200
  if VENV_WORKERS_PATH.is_dir():
124
201
  installed_version = _get_venv_python_version()
@@ -153,6 +230,7 @@ def create_workers_venv():
153
230
 
154
231
 
155
232
  MIN_UV_VERSION = (0, 8, 10)
233
+ MIN_WRANGLER_VERSION = (4, 42, 1)
156
234
 
157
235
 
158
236
  def check_uv_version():
@@ -167,6 +245,48 @@ def check_uv_version():
167
245
  raise click.exceptions.Exit(code=1)
168
246
 
169
247
 
248
+ def check_wrangler_version():
249
+ """
250
+ Check that the installed wrangler version is at least 4.42.1.
251
+
252
+ Raises:
253
+ click.exceptions.Exit: If wrangler is not installed or version is too old.
254
+ """
255
+ result = run_command(
256
+ ["npx", "--yes", "wrangler", "--version"], capture_output=True, check=False
257
+ )
258
+ if result.returncode != 0:
259
+ logger.error("Failed to get wrangler version. Is wrangler installed?")
260
+ logger.error("Install wrangler with: npm install wrangler@latest")
261
+ raise click.exceptions.Exit(code=1)
262
+
263
+ # Parse version from output like "wrangler 4.42.1" or " ⛅️ wrangler 4.42.1"
264
+ version_line = result.stdout.strip()
265
+ # Extract version number using regex
266
+ version_match = re.search(r"wrangler\s+(\d+)\.(\d+)\.(\d+)", version_line)
267
+
268
+ if not version_match:
269
+ logger.error(f"Could not parse wrangler version from: {version_line}")
270
+ logger.error("Install wrangler with: npm install wrangler@latest")
271
+ raise click.exceptions.Exit(code=1)
272
+
273
+ major, minor, patch = map(int, version_match.groups())
274
+ current_version = (major, minor, patch)
275
+
276
+ if current_version < MIN_WRANGLER_VERSION:
277
+ min_version_str = ".".join(str(x) for x in MIN_WRANGLER_VERSION)
278
+ current_version_str = ".".join(str(x) for x in current_version)
279
+ logger.error(
280
+ f"wrangler version at least {min_version_str} required, have {current_version_str}."
281
+ )
282
+ logger.error("Update wrangler with: npm install wrangler@latest")
283
+ raise click.exceptions.Exit(code=1)
284
+
285
+ logger.debug(
286
+ f"wrangler version {'.'.join(str(x) for x in current_version)} is sufficient"
287
+ )
288
+
289
+
170
290
  def create_pyodide_venv():
171
291
  if PYODIDE_VENV_PATH.is_dir():
172
292
  logger.debug(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workers-py
3
- Version: 1.2.1
3
+ Version: 1.4.0
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
@@ -9,6 +9,7 @@ Classifier: Operating System :: OS Independent
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Requires-Python: >=3.10
11
11
  Requires-Dist: click<9.0.0,>=8.0.0
12
+ Requires-Dist: pyjson5>=1.6.0
12
13
  Requires-Dist: pyodide-cli
13
14
  Requires-Dist: rich>=13.0.0
14
15
  Provides-Extra: build
@@ -75,7 +76,14 @@ workers-py = { path = "../workers-py" }
75
76
 
76
77
  Then run via `uv run pywrangler`.
77
78
 
78
- ## Tests
79
+ #### Lint
80
+
81
+ ```
82
+ uv run ruff check --fix
83
+ uv run ruff format
84
+ ```
85
+
86
+ #### Tests
79
87
 
80
88
  ```
81
89
  $ uv cache clean
@@ -0,0 +1,10 @@
1
+ pywrangler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ pywrangler/__main__.py,sha256=BnrUM7YiBmlM4HAn2MI9hP1kVNtzeK_kEgQhRy5HTq0,38
3
+ pywrangler/cli.py,sha256=DX0fX6akSkO34zn41zTqCJJeuFRjFjEzwKNIJFZRomk,5235
4
+ pywrangler/metadata.py,sha256=vttmaCtouSr9ADj8ncvNGqeaWEGFP8pamH2T6ohFjnA,408
5
+ pywrangler/sync.py,sha256=_4pwiStnLZ5vD9pZx7aJ3XayMWa5COR5oJt1tOa8VrU,14295
6
+ pywrangler/utils.py,sha256=wfkT7GbKtgtjHXtV3AjNeb25ohdAfrprdZIlqqidiQU,3269
7
+ workers_py-1.4.0.dist-info/METADATA,sha256=IBJmUfLc1uzsRjVmyzWBGP-WygBcE6us5ZZe0HnmtGk,1828
8
+ workers_py-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ workers_py-1.4.0.dist-info/entry_points.txt,sha256=pt6X-Nv5-gSiKUwrnvLwzlSXs9yP37m7zdTAi8f6nAM,50
10
+ workers_py-1.4.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- pywrangler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- pywrangler/__main__.py,sha256=BnrUM7YiBmlM4HAn2MI9hP1kVNtzeK_kEgQhRy5HTq0,38
3
- pywrangler/cli.py,sha256=3hMjtOOib3HaTqCkuMGQe7MbBZNnim2ByHcDY4JxFlw,5091
4
- pywrangler/sync.py,sha256=zTX-zZ5OJ7701N07zGnE0GeSebXGeBKJlFRrhNwBy4M,9961
5
- pywrangler/utils.py,sha256=wfkT7GbKtgtjHXtV3AjNeb25ohdAfrprdZIlqqidiQU,3269
6
- workers_py-1.2.1.dist-info/METADATA,sha256=o50STsChreiwJ1o3SxrVVUM1vbz4yIrSPLPR495b1_s,1733
7
- workers_py-1.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- workers_py-1.2.1.dist-info/entry_points.txt,sha256=pt6X-Nv5-gSiKUwrnvLwzlSXs9yP37m7zdTAi8f6nAM,50
9
- workers_py-1.2.1.dist-info/RECORD,,