funcd-bundle 0.3.3__tar.gz → 0.3.5__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.3 → funcd_bundle-0.3.5}/PKG-INFO +1 -1
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/pyproject.toml +1 -1
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/src/funcd_bundle/bundle.py +13 -1
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/tests/test_bundle.py +56 -0
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/.gitignore +0 -0
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/README.md +0 -0
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/src/funcd_bundle/__init__.py +0 -0
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/src/funcd_bundle/cli.py +0 -0
- {funcd_bundle-0.3.3 → funcd_bundle-0.3.5}/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.5
|
|
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.5"
|
|
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"
|
|
@@ -84,7 +84,10 @@ def discover(project: Path) -> dict[str, Function]:
|
|
|
84
84
|
defaults = {project.resolve().name: DEFAULT_HANDLER}
|
|
85
85
|
functions = {}
|
|
86
86
|
for name, manifest in manifests.items():
|
|
87
|
-
|
|
87
|
+
try:
|
|
88
|
+
data = yaml.safe_load(manifest.read_text()) or {}
|
|
89
|
+
except yaml.YAMLError as err:
|
|
90
|
+
raise BundleError(f"{manifest}: {err}") from err
|
|
88
91
|
if not isinstance(data, dict):
|
|
89
92
|
raise BundleError(f"{manifest} is not a mapping")
|
|
90
93
|
main = data.get("main") or defaults[name]
|
|
@@ -347,6 +350,15 @@ def _with_main(text: str, handler: str) -> str:
|
|
|
347
350
|
if text[start:end].endswith("\n"):
|
|
348
351
|
entry += "\n"
|
|
349
352
|
return text[:start] + entry + text[end:]
|
|
353
|
+
if root.flow_style:
|
|
354
|
+
# a block line after a flow mapping is a second root: the key goes inside, after its brace
|
|
355
|
+
brace = next(
|
|
356
|
+
t
|
|
357
|
+
for t in yaml.scan(text, Loader=yaml.SafeLoader)
|
|
358
|
+
if isinstance(t, yaml.FlowMappingStartToken)
|
|
359
|
+
)
|
|
360
|
+
at = brace.end_mark.index
|
|
361
|
+
return text[:at] + entry + (", " if root.value else "") + text[at:]
|
|
350
362
|
if text and not text.endswith("\n"):
|
|
351
363
|
text += "\n"
|
|
352
364
|
return text + entry + "\n"
|
|
@@ -16,6 +16,7 @@ from collections.abc import Iterator
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
18
|
import pytest
|
|
19
|
+
import yaml
|
|
19
20
|
|
|
20
21
|
from funcd_bundle import BundleError, bundle, discover, host_platform
|
|
21
22
|
from funcd_bundle.bundle import _check
|
|
@@ -24,6 +25,7 @@ from funcd_bundle.cli import main
|
|
|
24
25
|
FOREIGN = {"linux/arm64": "linux/amd64", "linux/amd64": "linux/arm64"}
|
|
25
26
|
ELF_MACHINE = {"linux/amd64": 0x3E, "linux/arm64": 0xB7}
|
|
26
27
|
MANIFEST = "runtime: python314\nhandler: handle\nmain: src/handler.py\n"
|
|
28
|
+
EXAMPLES = Path(__file__).resolve().parents[2] / "examples"
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def _online() -> bool:
|
|
@@ -255,6 +257,18 @@ def test_default_handler_follows_funcdctl(tmp_path: Path) -> None:
|
|
|
255
257
|
)
|
|
256
258
|
|
|
257
259
|
|
|
260
|
+
def test_issue_r54_every_example_manifest_names_a_handler_it_ships() -> None:
|
|
261
|
+
examples = sorted({m.parent for m in EXAMPLES.glob("*/*funcdctl.yaml")})
|
|
262
|
+
assert examples, f"no example manifests under {EXAMPLES}"
|
|
263
|
+
missing = []
|
|
264
|
+
for example in examples:
|
|
265
|
+
try:
|
|
266
|
+
discover(example)
|
|
267
|
+
except BundleError as err:
|
|
268
|
+
missing.append(str(err))
|
|
269
|
+
assert not missing, f"funcdctl dev and funcd-bundle cannot find these handlers: {missing}"
|
|
270
|
+
|
|
271
|
+
|
|
258
272
|
def test_issue_154_stem_manifest_without_main_names_bundled_handler(tmp_path: Path) -> None:
|
|
259
273
|
manifest = "runtime: python314\nhandler: handle\n"
|
|
260
274
|
project = tmp_path / "project"
|
|
@@ -302,7 +316,49 @@ def test_issue_r29_main_key_is_rewritten_not_duplicated(tmp_path: Path, main: st
|
|
|
302
316
|
)
|
|
303
317
|
|
|
304
318
|
|
|
319
|
+
@pytest.mark.parametrize(
|
|
320
|
+
("manifest", "want"),
|
|
321
|
+
[
|
|
322
|
+
("{runtime: python314, handler: handle}\n", {"runtime": "python314", "handler": "handle"}),
|
|
323
|
+
("!!map &m {\n runtime: python314,\n}\n", {"runtime": "python314"}),
|
|
324
|
+
("{}\n", {}),
|
|
325
|
+
],
|
|
326
|
+
ids=["one-line", "tagged-multi-line", "empty"],
|
|
327
|
+
)
|
|
328
|
+
def test_issue_r49_flow_mapping_manifest_gets_main(
|
|
329
|
+
tmp_path: Path, manifest: str, want: dict[str, str]
|
|
330
|
+
) -> None:
|
|
331
|
+
project = tmp_path / "reader"
|
|
332
|
+
_write(
|
|
333
|
+
project,
|
|
334
|
+
{
|
|
335
|
+
"pyproject.toml": '[project]\nname = "reader"\nversion = "0.1.0"\nrequires-python = ">=3.12"\n',
|
|
336
|
+
"funcdctl.yaml": manifest,
|
|
337
|
+
"handler.py": "def handle(ctx, event): ...\n",
|
|
338
|
+
},
|
|
339
|
+
)
|
|
340
|
+
_uv("lock", "--offline", "--python", "3.14", cwd=project)
|
|
341
|
+
out = bundle(project, "reader", host_platform(), tmp_path / "out", hermetic=False, check=False)
|
|
342
|
+
|
|
343
|
+
assert yaml.safe_load((out / "funcdctl.yaml").read_text()) == want | {"main": "handler.py"}, (
|
|
344
|
+
"the bundle's manifest is valid YAML whose main names the bundled handler"
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
305
348
|
def test_missing_handler_is_named(tmp_path: Path) -> None:
|
|
306
349
|
_write(tmp_path, {"funcdctl.yaml": "runtime: python314\nmain: src/nope.py\n"})
|
|
307
350
|
with pytest.raises(BundleError, match="nope.py"):
|
|
308
351
|
bundle(tmp_path, tmp_path.name, host_platform(), tmp_path / "out", hermetic=False, check=False)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_issue_r48_invalid_manifest_yaml_is_reported(
|
|
355
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
356
|
+
) -> None:
|
|
357
|
+
_write(tmp_path, {"funcdctl.yaml": "runtime: [\n", "handler.py": ""})
|
|
358
|
+
monkeypatch.chdir(tmp_path)
|
|
359
|
+
|
|
360
|
+
assert main(["--no-check", "--out", str(tmp_path / "out")]) == 1, "a manifest that does not parse fails"
|
|
361
|
+
err = capsys.readouterr().err
|
|
362
|
+
assert err.startswith(f"funcd-bundle: {tmp_path / 'funcdctl.yaml'}: "), err
|
|
363
|
+
assert "while parsing a flow node" in err, "the parser message names the problem"
|
|
364
|
+
assert "Traceback" not in err
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|