dagflows 0.2.2__tar.gz → 0.2.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.
Files changed (53) hide show
  1. {dagflows-0.2.2 → dagflows-0.2.3}/PKG-INFO +1 -1
  2. {dagflows-0.2.2 → dagflows-0.2.3}/pyproject.toml +1 -1
  3. dagflows-0.2.3/src/dagflows/_runtime/ready.py +38 -0
  4. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/runner.py +5 -0
  5. dagflows-0.2.3/tests/test_ready.py +67 -0
  6. {dagflows-0.2.2 → dagflows-0.2.3}/uv.lock +1 -1
  7. {dagflows-0.2.2 → dagflows-0.2.3}/.gitattributes +0 -0
  8. {dagflows-0.2.2 → dagflows-0.2.3}/.github/workflows/release.yml +0 -0
  9. {dagflows-0.2.2 → dagflows-0.2.3}/.github/workflows/test.yml +0 -0
  10. {dagflows-0.2.2 → dagflows-0.2.3}/.gitignore +0 -0
  11. {dagflows-0.2.2 → dagflows-0.2.3}/LICENSE +0 -0
  12. {dagflows-0.2.2 → dagflows-0.2.3}/Makefile +0 -0
  13. {dagflows-0.2.2 → dagflows-0.2.3}/README.md +0 -0
  14. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/__init__.py +0 -0
  15. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/__main__.py +0 -0
  16. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_authoring/__init__.py +0 -0
  17. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_authoring/config.py +0 -0
  18. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_authoring/node.py +0 -0
  19. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_authoring/workflow.py +0 -0
  20. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_cli/__init__.py +0 -0
  21. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_cli/build.py +0 -0
  22. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_cli/dev.py +0 -0
  23. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/__init__.py +0 -0
  24. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/binding.py +0 -0
  25. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/content.py +0 -0
  26. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/decode.py +0 -0
  27. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/dispatch.py +0 -0
  28. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/encode.py +0 -0
  29. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/envelope.py +0 -0
  30. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/errors.py +0 -0
  31. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/http.py +0 -0
  32. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/jsonstream.py +0 -0
  33. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/multipart.py +0 -0
  34. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/outstream.py +0 -0
  35. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/_runtime/result.py +0 -0
  36. {dagflows-0.2.2 → dagflows-0.2.3}/src/dagflows/py.typed +0 -0
  37. {dagflows-0.2.2 → dagflows-0.2.3}/tests/conftest.py +0 -0
  38. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_binding.py +0 -0
  39. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_cli.py +0 -0
  40. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_dispatch.py +0 -0
  41. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_emit.py +0 -0
  42. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_envelope.py +0 -0
  43. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_http.py +0 -0
  44. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_input.py +0 -0
  45. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_jsonstream.py +0 -0
  46. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_multipart.py +0 -0
  47. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_outstream.py +0 -0
  48. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_package.py +0 -0
  49. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_readme.py +0 -0
  50. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_result.py +0 -0
  51. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_roundtrip.py +0 -0
  52. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_runner.py +0 -0
  53. {dagflows-0.2.2 → dagflows-0.2.3}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dagflows
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: SDK for authoring and running Dagflows workflows
5
5
  Project-URL: Homepage, https://github.com/dagflows/sdk-python
6
6
  Project-URL: Repository, https://github.com/dagflows/sdk-python
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dagflows"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "SDK for authoring and running Dagflows workflows"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,38 @@
1
+ """
2
+ Ready signal: tells the guest agent the runtime has finished starting.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import os
8
+
9
+ # Descriptor the guest agent listens on for the runtime's ready signal.
10
+ READY_FD_ENV = "DAGFLOWS_READY_FD"
11
+
12
+
13
+ def signal_ready() -> None:
14
+ """Notify the guest agent that runtime initialization is complete and the handler is starting.
15
+
16
+ The node's execution timeout begins counting from this signal, ensuring that
17
+ interpreter startup and module imports do not consume the user's execution budget.
18
+
19
+ The signal dynamically tightens the execution deadline. If never sent, the agent
20
+ terminates the process at the hard ceiling (init allowance + timeout).
21
+
22
+ This function is idempotent and safe to call multiple times. If the ready file
23
+ descriptor is unset or unavailable, it silently no-ops.
24
+ """
25
+ try:
26
+ fd = int(os.environ.get(READY_FD_ENV, ""))
27
+ except ValueError:
28
+ return
29
+
30
+ if fd <= 0:
31
+ return
32
+
33
+ try:
34
+ os.write(fd, b".")
35
+ except OSError:
36
+ # The descriptor is the platform's to provide. A node cannot do
37
+ # anything about a broken one, and only its own deadline is affected.
38
+ pass
@@ -14,6 +14,7 @@ from typing import Any
14
14
  from . import binding
15
15
  from .envelope import OUTPUT_ENV, Ctx, Inputs, load
16
16
  from .errors import Fail
17
+ from .ready import signal_ready
17
18
  from .result import Upload, to_envelope
18
19
 
19
20
  Handler = Callable[..., Any]
@@ -102,6 +103,10 @@ def run(handler: Handler) -> None:
102
103
  ctx, inputs = load()
103
104
 
104
105
  try:
106
+ # The handler starts here, so this is where the node's timeout should
107
+ # begin being charged. Everything above was the runtime starting up.
108
+ signal_ready()
109
+
105
110
  answer = _call(handler, ctx, inputs)
106
111
  envelope = to_envelope(
107
112
  answer,
@@ -0,0 +1,67 @@
1
+ """The ready signal that starts a node's timeout at its handler."""
2
+
3
+ import json
4
+ import os
5
+
6
+ import pytest
7
+
8
+ import dagflows
9
+ from dagflows._runtime.ready import READY_FD_ENV, signal_ready
10
+
11
+
12
+ @pytest.fixture
13
+ def ready_file(tmp_path, monkeypatch):
14
+ """Point the ready descriptor at a file and report what the signal wrote."""
15
+ path = tmp_path / "signal"
16
+ fd = os.open(path, os.O_WRONLY | os.O_CREAT)
17
+ monkeypatch.setenv(READY_FD_ENV, str(fd))
18
+
19
+ try:
20
+ yield path
21
+ finally:
22
+ os.close(fd)
23
+
24
+
25
+ def test_it_writes_a_single_byte_to_the_descriptor_the_platform_names(ready_file):
26
+ signal_ready()
27
+
28
+ assert ready_file.read_bytes() == b"."
29
+
30
+
31
+ def test_it_does_nothing_when_no_descriptor_is_named(monkeypatch):
32
+ monkeypatch.delenv(READY_FD_ENV, raising=False)
33
+
34
+ # An older worker passes no descriptor. The node still runs; it just spends
35
+ # its own timeout on startup, as it did before.
36
+ signal_ready()
37
+
38
+
39
+ @pytest.mark.parametrize("value", ["", "0", "-1", "not-a-number", "9999"])
40
+ def test_it_does_nothing_when_the_descriptor_is_unusable(monkeypatch, value):
41
+ monkeypatch.setenv(READY_FD_ENV, value)
42
+
43
+ signal_ready()
44
+
45
+
46
+ def test_run_signals_ready_before_the_handler_runs_not_after(tmp_path, monkeypatch, ready_file):
47
+ """A signal sent after the handler charges startup to the node, so order is the point."""
48
+ envelope = {"ctx": {"node_key": "n", "inline_max_bytes": 1 << 20}, "payload": {"inputs": {}}}
49
+ in_path = tmp_path / "in.json"
50
+ out_path = tmp_path / "out.json"
51
+ in_path.write_text(json.dumps(envelope), encoding="utf-8")
52
+
53
+ monkeypatch.setenv("DAGFLOWS_INPUT", str(in_path))
54
+ monkeypatch.setenv("DAGFLOWS_OUTPUT", str(out_path))
55
+
56
+ seen = {}
57
+
58
+ def handler(ctx, inputs):
59
+ # Reading the signal file as the handler's first act: a byte here proves
60
+ # the signal was sent before the handler was entered.
61
+ seen["at_entry"] = ready_file.read_bytes()
62
+ return {"ok": True}
63
+
64
+ dagflows.run(handler)
65
+
66
+ assert seen["at_entry"] == b"."
67
+ assert json.loads(out_path.read_text(encoding="utf-8"))["status"] == "SUCCESS"
@@ -35,7 +35,7 @@ wheels = [
35
35
 
36
36
  [[package]]
37
37
  name = "dagflows"
38
- version = "0.2.2"
38
+ version = "0.2.3"
39
39
  source = { editable = "." }
40
40
 
41
41
  [package.optional-dependencies]
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
File without changes
File without changes
File without changes
File without changes
File without changes