dagflows 0.2.0__tar.gz → 0.2.2__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 (51) hide show
  1. {dagflows-0.2.0 → dagflows-0.2.2}/.gitignore +1 -0
  2. {dagflows-0.2.0 → dagflows-0.2.2}/Makefile +2 -2
  3. {dagflows-0.2.0 → dagflows-0.2.2}/PKG-INFO +1 -1
  4. {dagflows-0.2.0 → dagflows-0.2.2}/pyproject.toml +4 -1
  5. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/__init__.py +23 -2
  6. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/outstream.py +6 -2
  7. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_input.py +13 -5
  8. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_outstream.py +17 -0
  9. dagflows-0.2.2/tests/test_package.py +47 -0
  10. {dagflows-0.2.0 → dagflows-0.2.2}/uv.lock +2 -2
  11. {dagflows-0.2.0 → dagflows-0.2.2}/.gitattributes +0 -0
  12. {dagflows-0.2.0 → dagflows-0.2.2}/.github/workflows/release.yml +0 -0
  13. {dagflows-0.2.0 → dagflows-0.2.2}/.github/workflows/test.yml +0 -0
  14. {dagflows-0.2.0 → dagflows-0.2.2}/LICENSE +0 -0
  15. {dagflows-0.2.0 → dagflows-0.2.2}/README.md +0 -0
  16. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/__main__.py +0 -0
  17. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_authoring/__init__.py +0 -0
  18. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_authoring/config.py +0 -0
  19. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_authoring/node.py +0 -0
  20. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_authoring/workflow.py +0 -0
  21. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_cli/__init__.py +0 -0
  22. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_cli/build.py +0 -0
  23. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_cli/dev.py +0 -0
  24. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/__init__.py +0 -0
  25. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/binding.py +0 -0
  26. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/content.py +0 -0
  27. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/decode.py +0 -0
  28. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/dispatch.py +0 -0
  29. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/encode.py +0 -0
  30. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/envelope.py +0 -0
  31. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/errors.py +0 -0
  32. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/http.py +0 -0
  33. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/jsonstream.py +0 -0
  34. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/multipart.py +0 -0
  35. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/result.py +0 -0
  36. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/_runtime/runner.py +0 -0
  37. {dagflows-0.2.0 → dagflows-0.2.2}/src/dagflows/py.typed +0 -0
  38. {dagflows-0.2.0 → dagflows-0.2.2}/tests/conftest.py +0 -0
  39. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_binding.py +0 -0
  40. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_cli.py +0 -0
  41. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_dispatch.py +0 -0
  42. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_emit.py +0 -0
  43. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_envelope.py +0 -0
  44. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_http.py +0 -0
  45. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_jsonstream.py +0 -0
  46. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_multipart.py +0 -0
  47. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_readme.py +0 -0
  48. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_result.py +0 -0
  49. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_roundtrip.py +0 -0
  50. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_runner.py +0 -0
  51. {dagflows-0.2.0 → dagflows-0.2.2}/tests/test_workflow.py +0 -0
@@ -27,3 +27,4 @@ internal_refs/
27
27
 
28
28
  # Scratch venv used by `make wheel`
29
29
  .wheelcheck/
30
+ .venv-*/
@@ -31,7 +31,7 @@ readme: ## just the README, about five seconds
31
31
  matrix: ## the suite on every version requires-python claims
32
32
  @for v in 3.11 3.12 3.13; do \
33
33
  printf 'python %s: ' $$v; \
34
- uv run --python $$v --group dev pytest -q | tail -1; \
34
+ UV_PROJECT_ENVIRONMENT=.venv-matrix-$$v uv run --python $$v --group dev pytest -q | tail -1; \
35
35
  done
36
36
 
37
37
  build: ## build the artifacts and check the metadata is publishable
@@ -57,4 +57,4 @@ wheel: build ## prove the built wheel works, not just the working tree
57
57
  @$(VENV)/Scripts/python -m dagflows --help > /dev/null && echo "cli ok"
58
58
 
59
59
  clean: ## remove build and test leftovers
60
- rm -rf dist $(VENV) .pytest_cache
60
+ rm -rf dist $(VENV) .venv-matrix-* .pytest_cache
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dagflows
3
- Version: 0.2.0
3
+ Version: 0.2.2
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.0"
7
+ version = "0.2.2"
8
8
  description = "SDK for authoring and running Dagflows workflows"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -42,6 +42,9 @@ packages = ["src/dagflows"]
42
42
 
43
43
  [tool.pytest.ini_options]
44
44
  testpaths = ["tests"]
45
+ # A hang self-reports with every thread's stack instead of eating the CI
46
+ # timeout in silence. Found one deadlock already.
47
+ faulthandler_timeout = 300
45
48
 
46
49
  [tool.ruff]
47
50
  line-length = 100
@@ -37,6 +37,9 @@ _AUTHORING = {
37
37
 
38
38
 
39
39
  def __getattr__(name: str) -> Any:
40
+ if name == "__version__":
41
+ return _version()
42
+
40
43
  module = _AUTHORING.get(name)
41
44
  if module is None:
42
45
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -44,6 +47,26 @@ def __getattr__(name: str) -> Any:
44
47
  return getattr(import_module(module, __name__), name)
45
48
 
46
49
 
50
+ def _version() -> str:
51
+ """Return the installed package version from metadata."""
52
+ global _cached_version
53
+ if _cached_version:
54
+ return _cached_version
55
+
56
+ from importlib.metadata import PackageNotFoundError, version
57
+
58
+ try:
59
+ _cached_version = version("dagflows")
60
+ except PackageNotFoundError:
61
+ # An uninstalled source tree has no metadata to ask.
62
+ _cached_version = "0.0.0"
63
+
64
+ return _cached_version
65
+
66
+
67
+ _cached_version = ""
68
+
69
+
47
70
  def __dir__() -> list[str]:
48
71
  return sorted(__all__)
49
72
 
@@ -68,5 +91,3 @@ __all__ = [
68
91
  "Workflow",
69
92
  "run",
70
93
  ]
71
-
72
- __version__ = "0.1.0"
@@ -8,6 +8,7 @@ from types import TracebackType
8
8
  from typing import Any, Self
9
9
 
10
10
  from .content import ContentType
11
+ from .envelope import DEFAULT_INLINE_MAX_BYTES
11
12
  from .multipart import Multipart
12
13
  from .result import RowSink, Upload, Written, buffer_cap
13
14
 
@@ -23,11 +24,14 @@ class OutputStream:
23
24
  memory_limit_mb: int = 0,
24
25
  multipart: Multipart | None = None,
25
26
  ) -> None:
27
+ # Fall back to default limit when unset (0) to avoid immediately uploading or rejecting rows.
28
+ limit = inline_max_bytes if inline_max_bytes > 0 else DEFAULT_INLINE_MAX_BYTES
29
+
26
30
  self.content_type = content_type
27
31
  self._sink = RowSink(
28
32
  content_type,
29
- inline_max_bytes,
30
- buffer_cap(inline_max_bytes, upload, memory_limit_mb),
33
+ limit,
34
+ buffer_cap(limit, upload, memory_limit_mb),
31
35
  upload,
32
36
  multipart,
33
37
  )
@@ -5,7 +5,7 @@ Tests for inline and reference input handling and streaming.
5
5
  import asyncio
6
6
  import json
7
7
  import threading
8
- from http.server import BaseHTTPRequestHandler, HTTPServer
8
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
9
9
 
10
10
  import pytest
11
11
 
@@ -21,7 +21,8 @@ BODIES = {
21
21
  "/rows.json": b'[{"id": 1, "name": "ana"}, {"id": 2, "name": "bo"}]',
22
22
  "/object.json": b'{"id": 1}',
23
23
  "/blob.bin": bytes(range(256)) * 4,
24
- "/big.ndjson": b"".join(b'{"i": %d}\n' % i for i in range(20_000)),
24
+ # Large enough to exceed socket buffers so early-stop tests do not race against buffering.
25
+ "/big.ndjson": b'{"i": 0}\n' + (b'{"i": 1, "pad": "' + b"x" * 100 + b'"}\n') * 550_000,
25
26
  }
26
27
 
27
28
  served = {"gets": 0, "bytes": 0}
@@ -55,7 +56,9 @@ class Handler(BaseHTTPRequestHandler):
55
56
 
56
57
  @pytest.fixture(scope="module")
57
58
  def base():
58
- httpd = HTTPServer(("127.0.0.1", 0), Handler)
59
+ # Use daemon threads so server shutdown does not deadlock on blocked writes.
60
+ httpd = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
61
+ httpd.daemon_threads = True
59
62
  threading.Thread(target=httpd.serve_forever, daemon=True).start()
60
63
  yield f"http://127.0.0.1:{httpd.server_address[1]}"
61
64
  httpd.shutdown()
@@ -162,6 +165,7 @@ def test_iteration_is_re_entrant_rather_than_a_spent_generator(base):
162
165
 
163
166
 
164
167
  def test_stopping_early_does_not_pay_for_the_rest(base):
168
+ """Verify breaking out of iteration stops downloading remaining data."""
165
169
  handle = reference(base, "/big.ndjson", "application/x-ndjson")
166
170
 
167
171
  for row in handle:
@@ -268,8 +272,12 @@ def test_stopping_early_reads_at_most_one_batch_further(base, monkeypatch):
268
272
  monkeypatch.setattr(http, "stream", counting)
269
273
 
270
274
  async def first_only():
271
- async for row in reference(base, "/big.ndjson", "application/x-ndjson"):
272
- return row
275
+ # Close explicitly to ensure the connection is promptly terminated.
276
+ records = aiter(reference(base, "/big.ndjson", "application/x-ndjson"))
277
+ try:
278
+ return await anext(records)
279
+ finally:
280
+ await records.aclose()
273
281
 
274
282
  assert asyncio.run(first_only()) == {"i": 0}
275
283
  assert consumed < len(BODIES["/big.ndjson"]) / 4, (
@@ -85,6 +85,23 @@ def test_a_small_stream_stays_inline(no_network):
85
85
  assert "body" not in no_network
86
86
 
87
87
 
88
+ def test_a_zero_inline_limit_from_the_platform_means_the_default(no_network):
89
+ """Verify zero inline_max_bytes falls back to the default inline limit."""
90
+ raw = {"node_key": "n", "inline_max_bytes": 0, "memory_limit_mb": 512}
91
+
92
+ offered = Ctx.from_raw({**raw, "output_upload_url": "https://s/put", "output_key": "k"})
93
+ with offered.output_stream() as out:
94
+ out.write({"id": 1})
95
+
96
+ assert out.ref().block["type"] == "INLINE"
97
+ assert "body" not in no_network, "a small output must not be uploaded"
98
+
99
+ with Ctx.from_raw(raw).output_stream() as out:
100
+ out.write({"id": 1})
101
+
102
+ assert out.ref().block["type"] == "INLINE"
103
+
104
+
88
105
  def test_writing_outside_the_block_is_refused():
89
106
  stream = ctx().output_stream()
90
107
 
@@ -0,0 +1,47 @@
1
+ """
2
+ The package's claims about itself.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ import dagflows
10
+
11
+
12
+ def test_version_comes_from_the_installed_metadata():
13
+ """Ensure __version__ matches the installed package metadata."""
14
+ from importlib.metadata import PackageNotFoundError, version
15
+
16
+ try:
17
+ expected = version("dagflows")
18
+ except PackageNotFoundError:
19
+ pytest.skip("running from an uninstalled source tree, no metadata to compare")
20
+
21
+ assert dagflows.__version__ == expected
22
+
23
+
24
+ def test_version_agrees_with_pyproject():
25
+ """The end to end claim: what the code reports is what the build declares."""
26
+ from importlib.metadata import PackageNotFoundError, version
27
+
28
+ try:
29
+ version("dagflows")
30
+ except PackageNotFoundError:
31
+ pytest.skip("running from an uninstalled source tree")
32
+
33
+ pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
34
+ declared = ""
35
+ for line in pyproject.read_text(encoding="utf-8").splitlines():
36
+ if line.startswith("version = "):
37
+ declared = line.split('"')[1]
38
+ break
39
+
40
+ assert declared, "pyproject.toml declares no version"
41
+ assert dagflows.__version__ == declared
42
+
43
+
44
+ def test_an_unknown_attribute_still_raises():
45
+ """The __getattr__ gained a branch, the error for everything else stays."""
46
+ with pytest.raises(AttributeError, match="nonsense"):
47
+ dagflows.nonsense # noqa: B018 - the attribute access is what raises
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 2
2
+ revision = 3
3
3
  requires-python = ">=3.11"
4
4
 
5
5
  [[package]]
@@ -35,7 +35,7 @@ wheels = [
35
35
 
36
36
  [[package]]
37
37
  name = "dagflows"
38
- version = "0.2.0"
38
+ version = "0.2.2"
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