workers-py 1.1.8__py3-none-any.whl → 1.2.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
@@ -106,7 +106,6 @@ def sync_command(force=False, directly_requested=True):
106
106
  create_pyodide_venv,
107
107
  create_workers_venv,
108
108
  parse_requirements,
109
- install_pyodide_build,
110
109
  install_requirements,
111
110
  )
112
111
 
@@ -129,7 +128,6 @@ def sync_command(force=False, directly_requested=True):
129
128
  create_workers_venv()
130
129
 
131
130
  # Set up Pyodide virtual env
132
- install_pyodide_build()
133
131
  create_pyodide_venv()
134
132
 
135
133
  # Generate requirements.txt from pyproject.toml by directly parsing the TOML file then install into vendor folder.
pywrangler/sync.py CHANGED
@@ -1,8 +1,10 @@
1
1
  import logging
2
2
  import os
3
3
  import shutil
4
- from pathlib import Path
5
4
  import tempfile
5
+ from contextlib import contextmanager
6
+ from pathlib import Path
7
+ from typing import Literal
6
8
 
7
9
  import click
8
10
 
@@ -58,8 +60,33 @@ def check_wrangler_config():
58
60
  raise click.exceptions.Exit(code=1)
59
61
 
60
62
 
61
- def _get_python_version():
62
- return os.environ.get("_PYWRANGLER_PYTHON_VERSION", "3.12")
63
+ 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
+ )
72
+
73
+
74
+ def _get_uv_pyodide_interp_name():
75
+ match _get_python_version():
76
+ case "3.12":
77
+ v = "3.12.7"
78
+ case "3.13":
79
+ v = "3.13.2"
80
+ return f"cpython-{v}-emscripten-wasm32-musl"
81
+
82
+
83
+ def _get_pyodide_index():
84
+ match _get_python_version():
85
+ case "3.12":
86
+ v = "0.27.7"
87
+ case "3.13":
88
+ v = "0.28.3"
89
+ return "https://index.pyodide.org/" + v
63
90
 
64
91
 
65
92
  def _get_venv_python_version() -> str | None:
@@ -125,65 +152,18 @@ def create_workers_venv():
125
152
  )
126
153
 
127
154
 
128
- def _get_pyodide_cli_path():
129
- venv_bin_path = VENV_WORKERS_PATH / ("Scripts" if os.name == "nt" else "bin")
130
- pyodide_cli_path = venv_bin_path / ("pyodide.exe" if os.name == "nt" else "pyodide")
131
- return pyodide_cli_path
132
-
133
-
134
- def install_pyodide_build():
135
- pyodide_cli_path = _get_pyodide_cli_path()
136
-
137
- if pyodide_cli_path.is_file():
138
- logger.debug(
139
- f"pyodide-build CLI already found at {pyodide_cli_path} (skipping install.)"
140
- )
141
- return
142
-
143
- logger.debug(
144
- f"Installing pyodide-build in {VENV_WORKERS_PATH} using 'uv pip install'..."
145
- )
146
- venv_bin_path = pyodide_cli_path.parent
147
-
148
- # Ensure the python executable path is correct for the venv
149
- venv_python_executable = venv_bin_path / (
150
- "python.exe" if os.name == "nt" else "python"
151
- )
152
- if not venv_python_executable.is_file():
153
- logger.error(f"Python executable not found at {venv_python_executable}")
154
- raise click.exceptions.Exit(code=1)
155
-
156
- run_command(["uv", "pip", "install", "-p", str(venv_python_executable), "pip"])
157
-
158
- run_command(
159
- [
160
- "uv",
161
- "pip",
162
- "install",
163
- "-p",
164
- str(venv_python_executable),
165
- "pyodide-build==0.30.7",
166
- ]
167
- )
168
-
169
-
170
155
  def create_pyodide_venv():
171
- pyodide_cli_path = _get_pyodide_cli_path()
172
156
  if PYODIDE_VENV_PATH.is_dir():
173
157
  logger.debug(
174
158
  f"Pyodide virtual environment at {PYODIDE_VENV_PATH} already exists."
175
159
  )
176
160
  return
177
161
 
178
- # Workaround to fix caching issue on some machines.
179
- #
180
- # Fix is here: pyodide/pyodide-build#239
181
- logger.debug("Installing xbuildenv...")
182
- run_command([str(pyodide_cli_path), "xbuildenv", "install"])
183
-
184
162
  logger.debug(f"Creating Pyodide virtual environment at {PYODIDE_VENV_PATH}...")
185
163
  PYODIDE_VENV_PATH.parent.mkdir(parents=True, exist_ok=True)
186
- run_command([str(pyodide_cli_path), "venv", str(PYODIDE_VENV_PATH)])
164
+ interp_name = _get_uv_pyodide_interp_name()
165
+ run_command(["uv", "python", "install", interp_name])
166
+ run_command(["uv", "venv", PYODIDE_VENV_PATH, "--python", interp_name])
187
167
 
188
168
 
189
169
  def parse_requirements() -> list[str]:
@@ -202,6 +182,15 @@ def parse_requirements() -> list[str]:
202
182
  raise click.exceptions.Exit(code=1)
203
183
 
204
184
 
185
+ @contextmanager
186
+ def temp_requirements_file(requirements: list[str]):
187
+ # Write dependencies to a requirements.txt-style temp file.
188
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as temp_file:
189
+ temp_file.write("\n".join(requirements))
190
+ temp_file.flush()
191
+ yield temp_file.name
192
+
193
+
205
194
  def _install_requirements_to_vendor(requirements: list[str]):
206
195
  vendor_path = PROJECT_ROOT / "python_modules"
207
196
  logger.debug(f"Using vendor path: {vendor_path}")
@@ -212,97 +201,72 @@ def _install_requirements_to_vendor(requirements: list[str]):
212
201
  )
213
202
  return
214
203
 
215
- # Write dependencies to a requirements.txt-style temp file.
216
- with tempfile.NamedTemporaryFile(
217
- mode="w", suffix=".txt", dir=PYODIDE_VENV_PATH
218
- ) as temp_file:
219
- temp_file.write("\n".join(requirements))
220
- temp_file.flush()
221
- temp_file_path = Path(temp_file.name)
222
-
223
- # Install packages into vendor directory
224
- vendor_path.mkdir(parents=True, exist_ok=True)
225
- pyodide_venv_pip_path = (
226
- PYODIDE_VENV_PATH
227
- / ("Scripts" if os.name == "nt" else "bin")
228
- / ("pip.exe" if os.name == "nt" else "pip")
229
- )
230
- relative_vendor_path = vendor_path.relative_to(PROJECT_ROOT)
231
- logger.info(
232
- f"Installing packages into [bold]{relative_vendor_path}[/bold] using Pyodide pip...",
233
- extra={"markup": True},
234
- )
204
+ # Install packages into vendor directory
205
+ vendor_path.mkdir(parents=True, exist_ok=True)
206
+ relative_vendor_path = vendor_path.relative_to(PROJECT_ROOT)
207
+ logger.info(
208
+ f"Installing packages into [bold]{relative_vendor_path}[/bold]...",
209
+ extra={"markup": True},
210
+ )
211
+ with temp_requirements_file(requirements) as requirements_file:
235
212
  run_command(
236
213
  [
237
- str(pyodide_venv_pip_path),
214
+ "uv",
215
+ "pip",
238
216
  "install",
239
- "-t",
240
- str(vendor_path),
217
+ "--no-build",
241
218
  "-r",
242
- str(temp_file_path),
243
- ]
219
+ requirements_file,
220
+ "--extra-index-url",
221
+ _get_pyodide_index(),
222
+ "--index-strategy",
223
+ "unsafe-best-match",
224
+ ],
225
+ env=os.environ | {"VIRTUAL_ENV": PYODIDE_VENV_PATH},
226
+ )
227
+ pyv = _get_python_version()
228
+ shutil.rmtree(vendor_path)
229
+ shutil.copytree(
230
+ PYODIDE_VENV_PATH / f"lib/python{pyv}/site-packages", vendor_path
244
231
  )
245
232
 
246
- # Create a pyvenv.cfg file in python_modules to mark it as a virtual environment
247
- (vendor_path / "pyvenv.cfg").touch()
248
- VENDOR_TOKEN.write_text("")
233
+ # Create a pyvenv.cfg file in python_modules to mark it as a virtual environment
234
+ (vendor_path / "pyvenv.cfg").touch()
235
+ VENDOR_TOKEN.touch()
249
236
 
250
- logger.info(
251
- f"Packages installed in [bold]{relative_vendor_path}[/bold].",
252
- extra={"markup": True},
253
- )
237
+ logger.info(
238
+ f"Packages installed in [bold]{relative_vendor_path}[/bold].",
239
+ extra={"markup": True},
240
+ )
254
241
 
255
242
 
256
243
  def _install_requirements_to_venv(requirements: list[str]):
257
244
  # Create a requirements file for .venv-workers that includes webtypy and pyodide-py
258
- VENV_REQUIREMENTS_PATH.parent.mkdir(parents=True, exist_ok=True)
259
-
245
+ relative_venv_workers_path = VENV_WORKERS_PATH.relative_to(PROJECT_ROOT)
260
246
  requirements = requirements.copy()
261
247
  requirements.append("webtypy")
262
248
  requirements.append("pyodide-py")
263
249
 
264
- # Write dependencies to a requirements.txt-style temp file.
265
- with tempfile.NamedTemporaryFile(
266
- mode="w", suffix=".txt", dir=VENV_REQUIREMENTS_PATH.parent
267
- ) as temp_file:
268
- temp_file.write("\n".join(requirements))
269
- temp_file.flush()
270
- temp_file_path = Path(temp_file.name)
271
-
272
- # Install packages into .venv-workers so that user's IDE can see the packages.
273
- venv_bin_path = VENV_WORKERS_PATH / ("Scripts" if os.name == "nt" else "bin")
274
- venv_python_executable = venv_bin_path / (
275
- "python.exe" if os.name == "nt" else "python"
250
+ logger.info(
251
+ f"Installing packages into [bold]{relative_venv_workers_path}[/bold]...",
252
+ extra={"markup": True},
253
+ )
254
+ with temp_requirements_file(requirements) as requirements_file:
255
+ run_command(
256
+ [
257
+ "uv",
258
+ "pip",
259
+ "install",
260
+ "-r",
261
+ requirements_file,
262
+ ],
263
+ env=os.environ | {"VIRTUAL_ENV": VENV_WORKERS_PATH},
276
264
  )
277
-
278
- # For nicer logs, output the relative path.
279
- relative_venv_workers_path = VENV_WORKERS_PATH.relative_to(PROJECT_ROOT)
280
- if venv_python_executable.is_file():
281
- logger.info(
282
- f"Installing packages into [bold]{relative_venv_workers_path}[/bold] using uv pip...",
283
- extra={"markup": True},
284
- )
285
- run_command(
286
- [
287
- "uv",
288
- "pip",
289
- "install",
290
- "-p",
291
- venv_python_executable,
292
- "-r",
293
- str(temp_file_path),
294
- ]
295
- )
296
- VENV_WORKERS_TOKEN.write_text("")
297
- logger.info(
298
- f"Packages installed in [bold]{relative_venv_workers_path}[/bold].",
299
- extra={"markup": True},
300
- )
301
- else:
302
- logger.warning(
303
- f"Python executable not found at {venv_python_executable}. Skipping installation in [bold]{relative_venv_workers_path}[/bold].",
304
- extra={"markup": True},
305
- )
265
+ VENV_WORKERS_TOKEN.touch()
266
+ logger.info(
267
+ f"Packages installed in [bold]{relative_venv_workers_path}[/bold].",
268
+ extra={"markup": True},
269
+ )
306
270
 
307
271
 
308
272
  def install_requirements(requirements: list[str]):
@@ -310,6 +274,12 @@ def install_requirements(requirements: list[str]):
310
274
  _install_requirements_to_venv(requirements)
311
275
 
312
276
 
277
+ def _is_out_of_date(token: Path, time: float) -> bool:
278
+ if not token.exists():
279
+ return True
280
+ return time > token.stat().st_mtime
281
+
282
+
313
283
  def is_sync_needed():
314
284
  """
315
285
  Checks if pyproject.toml has been modified since the last sync.
@@ -323,20 +293,6 @@ def is_sync_needed():
323
293
  return True
324
294
 
325
295
  pyproject_mtime = PYPROJECT_TOML_PATH.stat().st_mtime
326
-
327
- # Check if .venv-workers exists and get its timestamp
328
- if not VENV_WORKERS_TOKEN.exists():
329
- return True
330
-
331
- venv_mtime = VENV_WORKERS_TOKEN.stat().st_mtime
332
- venv_needs_update = pyproject_mtime > venv_mtime
333
- if venv_needs_update:
334
- return True
335
-
336
- # Check if vendor directory exists and get its timestamp
337
- if not VENDOR_TOKEN.exists():
338
- return True
339
-
340
- vendor_mtime = VENDOR_TOKEN.stat().st_mtime
341
- vendor_needs_update = pyproject_mtime > vendor_mtime
342
- return vendor_needs_update
296
+ return _is_out_of_date(VENDOR_TOKEN, pyproject_mtime) or _is_out_of_date(
297
+ VENV_WORKERS_TOKEN, pyproject_mtime
298
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workers-py
3
- Version: 1.1.8
3
+ Version: 1.2.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
@@ -0,0 +1,9 @@
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=4LbwXlzi7YXXhsgYT4unJOwX9DHSLI6sREcf7abJAks,9441
5
+ pywrangler/utils.py,sha256=wfkT7GbKtgtjHXtV3AjNeb25ohdAfrprdZIlqqidiQU,3269
6
+ workers_py-1.2.0.dist-info/METADATA,sha256=s9qthgJPRW1inmDvDDl_UR-V0KA-M_Hewwp6GJxjq04,1749
7
+ workers_py-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ workers_py-1.2.0.dist-info/entry_points.txt,sha256=pt6X-Nv5-gSiKUwrnvLwzlSXs9yP37m7zdTAi8f6nAM,50
9
+ workers_py-1.2.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=BnGGrdksWP-qBj-b0ipji-61Q0kZJohyr2KZZz-XG5s,5150
4
- pywrangler/sync.py,sha256=vcaL_jOWCC6vWEaJnTnbnxgdgFeW30pndRcMRWOv5pw,11286
5
- pywrangler/utils.py,sha256=wfkT7GbKtgtjHXtV3AjNeb25ohdAfrprdZIlqqidiQU,3269
6
- workers_py-1.1.8.dist-info/METADATA,sha256=rE874u2Io40CgLK1JPBMO5eMTwyychzO14K_jO5Emrc,1749
7
- workers_py-1.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- workers_py-1.1.8.dist-info/entry_points.txt,sha256=pt6X-Nv5-gSiKUwrnvLwzlSXs9yP37m7zdTAi8f6nAM,50
9
- workers_py-1.1.8.dist-info/RECORD,,