funcd-bundle 0.3.1__tar.gz → 0.3.3__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.
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/.gitignore +2 -0
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/PKG-INFO +1 -1
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/pyproject.toml +1 -1
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/src/funcd_bundle/bundle.py +22 -5
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/tests/test_bundle.py +47 -0
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/README.md +0 -0
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/src/funcd_bundle/__init__.py +0 -0
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/src/funcd_bundle/cli.py +0 -0
- {funcd_bundle-0.3.1 → funcd_bundle-0.3.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: funcd-bundle
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Bundle a Python funcd function and its locked dependencies for the funcd runtime
|
|
5
5
|
Project-URL: Homepage, https://github.com/pyvvo/funcd-python
|
|
6
6
|
Project-URL: Repository, https://github.com/pyvvo/funcd-python
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "funcd-bundle"
|
|
3
3
|
# Not bumped per release: the vX.Y.Z git tag is the version, stamped into the build at publish time.
|
|
4
|
-
version = "0.3.
|
|
4
|
+
version = "0.3.3"
|
|
5
5
|
description = "Bundle a Python funcd function and its locked dependencies for the funcd runtime"
|
|
6
6
|
authors = [{ name = "The funcd Authors" }]
|
|
7
7
|
readme = "README.md"
|
|
@@ -16,7 +16,6 @@ import csv
|
|
|
16
16
|
import io
|
|
17
17
|
import os
|
|
18
18
|
import platform as host
|
|
19
|
-
import re
|
|
20
19
|
import shlex
|
|
21
20
|
import shutil
|
|
22
21
|
import subprocess
|
|
@@ -38,7 +37,6 @@ DEFAULT_HANDLER = "handler.py"
|
|
|
38
37
|
RUNTIME_PACKAGE = "funcd-shim"
|
|
39
38
|
PLATFORMS = {"linux/amd64": "x86_64", "linux/arm64": "aarch64"}
|
|
40
39
|
SKIPPED = {"__pycache__", "tests"}
|
|
41
|
-
MAIN_LINE = re.compile(r"^main:.*$", re.MULTILINE)
|
|
42
40
|
# never streamed into a container: caches, virtualenvs and build outputs
|
|
43
41
|
NOT_COPIED = frozenset(
|
|
44
42
|
{".venv", ".git", "node_modules", "__pycache__", "dist", ".mypy_cache", ".pytest_cache", ".ruff_cache"}
|
|
@@ -329,12 +327,31 @@ def _copy_handler(project: Path, fn: Function, out: Path) -> list[Path]:
|
|
|
329
327
|
|
|
330
328
|
shutil.copytree(source, out, dirs_exist_ok=True, ignore=ignore)
|
|
331
329
|
sources = [p for p in source.rglob("*.py") if not SKIPPED.intersection(p.relative_to(source).parts)]
|
|
332
|
-
# inside the bundle the handler sits at the root, so
|
|
333
|
-
|
|
334
|
-
(out / GENERIC_MANIFEST).write_text(
|
|
330
|
+
# inside the bundle the handler sits at the root, so `main` must name it there (funcdctl dev reads it); a
|
|
331
|
+
# manifest without one gets one, else the bundle's generic manifest would default to handler.py
|
|
332
|
+
(out / GENERIC_MANIFEST).write_text(_with_main(fn.manifest.read_text(), fn.handler.name))
|
|
335
333
|
return sources
|
|
336
334
|
|
|
337
335
|
|
|
336
|
+
def _with_main(text: str, handler: str) -> str:
|
|
337
|
+
"""The manifest with its top-level `main` set to handler. The YAML parser finds the key, however it is
|
|
338
|
+
written; the rest stays as written, since a PyYAML dump would re-resolve YAML 1.1 scalars funcd reads as
|
|
339
|
+
YAML 1.2 text."""
|
|
340
|
+
entry = f"main: {handler}"
|
|
341
|
+
root = yaml.compose(text, Loader=yaml.SafeLoader)
|
|
342
|
+
if isinstance(root, yaml.MappingNode):
|
|
343
|
+
for key, value in root.value:
|
|
344
|
+
if isinstance(key, yaml.ScalarNode) and key.value == "main":
|
|
345
|
+
start, end = key.start_mark.index, value.end_mark.index
|
|
346
|
+
# a block scalar's span ends past its line break
|
|
347
|
+
if text[start:end].endswith("\n"):
|
|
348
|
+
entry += "\n"
|
|
349
|
+
return text[:start] + entry + text[end:]
|
|
350
|
+
if text and not text.endswith("\n"):
|
|
351
|
+
text += "\n"
|
|
352
|
+
return text + entry + "\n"
|
|
353
|
+
|
|
354
|
+
|
|
338
355
|
def _top_level_modules(out: Path) -> set[str]:
|
|
339
356
|
"""The importable top-level names of the vendored distributions, from each RECORD."""
|
|
340
357
|
names: set[str] = set()
|
|
@@ -255,6 +255,53 @@ def test_default_handler_follows_funcdctl(tmp_path: Path) -> None:
|
|
|
255
255
|
)
|
|
256
256
|
|
|
257
257
|
|
|
258
|
+
def test_issue_154_stem_manifest_without_main_names_bundled_handler(tmp_path: Path) -> None:
|
|
259
|
+
manifest = "runtime: python314\nhandler: handle\n"
|
|
260
|
+
project = tmp_path / "project"
|
|
261
|
+
_write(
|
|
262
|
+
project,
|
|
263
|
+
{
|
|
264
|
+
"pyproject.toml": '[project]\nname = "extract"\nversion = "0.1.0"\nrequires-python = ">=3.12"\n',
|
|
265
|
+
"extract.funcdctl.yaml": manifest,
|
|
266
|
+
"extract.py": "def handle(ctx, event): ...\n",
|
|
267
|
+
},
|
|
268
|
+
)
|
|
269
|
+
_uv("lock", "--offline", "--python", "3.14", cwd=project)
|
|
270
|
+
out = bundle(project, "extract", host_platform(), tmp_path / "extract", hermetic=False, check=False)
|
|
271
|
+
|
|
272
|
+
assert discover(out)["extract"].handler == out / "extract.py", (
|
|
273
|
+
"the bundle's generic manifest resolves to the bundled handler, not handler.py"
|
|
274
|
+
)
|
|
275
|
+
assert (out / "funcdctl.yaml").read_text() == manifest + "main: extract.py\n"
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@pytest.mark.parametrize(
|
|
279
|
+
"main",
|
|
280
|
+
[
|
|
281
|
+
'"main": src/handler.py\n',
|
|
282
|
+
"'main': src/handler.py\n",
|
|
283
|
+
"main:\n src/handler.py\n",
|
|
284
|
+
],
|
|
285
|
+
ids=["double-quoted-key", "single-quoted-key", "value-on-next-line"],
|
|
286
|
+
)
|
|
287
|
+
def test_issue_r29_main_key_is_rewritten_not_duplicated(tmp_path: Path, main: str) -> None:
|
|
288
|
+
project = tmp_path / "reader"
|
|
289
|
+
_write(
|
|
290
|
+
project,
|
|
291
|
+
{
|
|
292
|
+
"pyproject.toml": '[project]\nname = "reader"\nversion = "0.1.0"\nrequires-python = ">=3.12"\n',
|
|
293
|
+
"funcdctl.yaml": "runtime: python314\n" + main + "handler: handle\n",
|
|
294
|
+
"src/handler.py": "def handle(ctx, event): ...\n",
|
|
295
|
+
},
|
|
296
|
+
)
|
|
297
|
+
_uv("lock", "--offline", "--python", "3.14", cwd=project)
|
|
298
|
+
out = bundle(project, "reader", host_platform(), tmp_path / "out", hermetic=False, check=False)
|
|
299
|
+
|
|
300
|
+
assert (out / "funcdctl.yaml").read_text() == "runtime: python314\nmain: handler.py\nhandler: handle\n", (
|
|
301
|
+
"the manifest's one main names the bundled handler"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
258
305
|
def test_missing_handler_is_named(tmp_path: Path) -> None:
|
|
259
306
|
_write(tmp_path, {"funcdctl.yaml": "runtime: python314\nmain: src/nope.py\n"})
|
|
260
307
|
with pytest.raises(BundleError, match="nope.py"):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|