pi-durable-python 1.3.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.
- pi_durable/__init__.py +320 -0
- pi_durable/context.py +328 -0
- pi_durable/delta.py +269 -0
- pi_durable/documents.py +366 -0
- pi_durable/entries.py +66 -0
- pi_durable/env/__init__.py +408 -0
- pi_durable/env/decode.py +48 -0
- pi_durable/env/line_scan.py +190 -0
- pi_durable/env/posix.py +1547 -0
- pi_durable/errors.py +89 -0
- pi_durable/harness/__init__.py +1 -0
- pi_durable/harness/agent.py +344 -0
- pi_durable/harness/compaction.py +673 -0
- pi_durable/harness/context.py +515 -0
- pi_durable/harness/define.py +111 -0
- pi_durable/harness/events.py +766 -0
- pi_durable/harness/generation.py +1079 -0
- pi_durable/harness/harness.py +570 -0
- pi_durable/harness/inbox.py +255 -0
- pi_durable/harness/live.py +374 -0
- pi_durable/harness/output.py +467 -0
- pi_durable/harness/prompt.py +288 -0
- pi_durable/harness/provider.py +78 -0
- pi_durable/harness/registry.py +136 -0
- pi_durable/harness/scheduler.py +1977 -0
- pi_durable/harness/submissions.py +357 -0
- pi_durable/harness/task_graph.py +334 -0
- pi_durable/harness/tool.py +1365 -0
- pi_durable/harness/types.py +772 -0
- pi_durable/harness/usage.py +88 -0
- pi_durable/harness/util.py +204 -0
- pi_durable/harness/validation.py +460 -0
- pi_durable/harness/view.py +324 -0
- pi_durable/ids.py +63 -0
- pi_durable/json.py +186 -0
- pi_durable/providers/__init__.py +362 -0
- pi_durable/providers/anthropic.py +603 -0
- pi_durable/providers/base.py +97 -0
- pi_durable/providers/json_parse.py +117 -0
- pi_durable/providers/openai.py +574 -0
- pi_durable/providers/request.py +35 -0
- pi_durable/providers/retry.py +127 -0
- pi_durable/providers/stream.py +183 -0
- pi_durable/providers/transcript.py +224 -0
- pi_durable/providers/usage.py +58 -0
- pi_durable/py.typed +0 -0
- pi_durable/search.py +115 -0
- pi_durable/session/__init__.py +5 -0
- pi_durable/session/drafts.py +762 -0
- pi_durable/session/forks.py +109 -0
- pi_durable/session/observation.py +374 -0
- pi_durable/session/session.py +1193 -0
- pi_durable/session/transaction.py +1088 -0
- pi_durable/storage/__init__.py +5 -0
- pi_durable/storage/jsonl/__init__.py +15 -0
- pi_durable/storage/jsonl/storage.py +958 -0
- pi_durable/storage/memory.py +889 -0
- pi_durable/storage/scan.py +57 -0
- pi_durable/storage/sqlite/__init__.py +26 -0
- pi_durable/storage/sqlite/database.py +62 -0
- pi_durable/storage/sqlite/migrations.py +191 -0
- pi_durable/storage/sqlite/posix.py +314 -0
- pi_durable/storage/sqlite/schema.py +97 -0
- pi_durable/storage/sqlite/storage.py +1130 -0
- pi_durable/tasks.py +89 -0
- pi_durable/testing/__init__.py +41 -0
- pi_durable/testing/assertions.py +44 -0
- pi_durable/testing/env_conformance.py +828 -0
- pi_durable/testing/runner.py +64 -0
- pi_durable/testing/storage_conformance.py +2008 -0
- pi_durable/testing/types.py +99 -0
- pi_durable/tool_search/__init__.py +159 -0
- pi_durable/tools/__init__.py +81 -0
- pi_durable/tools/_values.py +43 -0
- pi_durable/tools/bash.py +250 -0
- pi_durable/tools/edit.py +214 -0
- pi_durable/tools/edit_diff.py +452 -0
- pi_durable/tools/env.py +19 -0
- pi_durable/tools/file_mutation_queue.py +68 -0
- pi_durable/tools/image.py +248 -0
- pi_durable/tools/image_processor.py +77 -0
- pi_durable/tools/path_utils.py +111 -0
- pi_durable/tools/read.py +506 -0
- pi_durable/tools/write.py +71 -0
- pi_durable/truncate.py +185 -0
- pi_durable/types.py +1297 -0
- pi_durable_python-1.3.0.dist-info/METADATA +276 -0
- pi_durable_python-1.3.0.dist-info/RECORD +90 -0
- pi_durable_python-1.3.0.dist-info/WHEEL +4 -0
- pi_durable_python-1.3.0.dist-info/licenses/LICENSE +21 -0
pi_durable/tasks.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Executable durable task definitions.
|
|
2
|
+
|
|
3
|
+
The Session only needs the definition's initial checkpoint and persisted identity; the Harness
|
|
4
|
+
later schedules the phase map. Keeping the token here, in the kernel layer, lets transactions
|
|
5
|
+
create tasks without importing the harness.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable, Mapping
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, Generic, TypeVar
|
|
13
|
+
|
|
14
|
+
InputT = TypeVar("InputT")
|
|
15
|
+
S = TypeVar("S")
|
|
16
|
+
R = TypeVar("R")
|
|
17
|
+
H = TypeVar("H")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class TaskDefinition(Generic[InputT, S, R, H]):
|
|
22
|
+
"""Definition fields persisted and used when a task is created or recovered."""
|
|
23
|
+
|
|
24
|
+
name: str
|
|
25
|
+
version: int
|
|
26
|
+
initial: Callable[[InputT], S]
|
|
27
|
+
phases: Mapping[str, Any]
|
|
28
|
+
abort: Callable[..., Any]
|
|
29
|
+
migrate: Callable[..., Any] | None = None
|
|
30
|
+
hooks: H | None = None
|
|
31
|
+
|
|
32
|
+
def __getitem__(self, key: str) -> Any:
|
|
33
|
+
return getattr(self, key)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
37
|
+
class Task(Generic[InputT, S, R, H]):
|
|
38
|
+
"""Typed executable task token."""
|
|
39
|
+
|
|
40
|
+
definition: TaskDefinition[InputT, S, R, H]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _values(definition: Mapping[str, Any] | None, kwargs: Mapping[str, Any]) -> dict[str, Any]:
|
|
44
|
+
values = dict(definition or {})
|
|
45
|
+
values.update(kwargs)
|
|
46
|
+
return values
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def define_task(
|
|
50
|
+
definition: Mapping[str, Any] | None = None,
|
|
51
|
+
/,
|
|
52
|
+
**kwargs: Any,
|
|
53
|
+
) -> Task[Any, Any, Any, Any]:
|
|
54
|
+
"""Define an executable task; register it later with a Harness registry."""
|
|
55
|
+
|
|
56
|
+
values = _values(definition, kwargs)
|
|
57
|
+
name = values.get("name")
|
|
58
|
+
if not isinstance(name, str) or not name:
|
|
59
|
+
raise TypeError("Task name must be a non-empty string")
|
|
60
|
+
version = values.get("version")
|
|
61
|
+
if not isinstance(version, int) or isinstance(version, bool) or version < 1:
|
|
62
|
+
raise TypeError(f"Task {name} version must be a positive integer")
|
|
63
|
+
initial = values.get("initial")
|
|
64
|
+
if not callable(initial):
|
|
65
|
+
raise TypeError(f"Task {name} requires an initial() function")
|
|
66
|
+
phases = values.get("phases")
|
|
67
|
+
if not isinstance(phases, Mapping):
|
|
68
|
+
raise TypeError(f"Task {name} requires a phases mapping")
|
|
69
|
+
abort = values.get("abort")
|
|
70
|
+
if not callable(abort):
|
|
71
|
+
raise TypeError(f"Task {name} requires an abort() function")
|
|
72
|
+
migrate = values.get("migrate")
|
|
73
|
+
if migrate is not None and not callable(migrate):
|
|
74
|
+
raise TypeError(f"Task {name} migrate must be callable")
|
|
75
|
+
return Task(
|
|
76
|
+
TaskDefinition(
|
|
77
|
+
name=name,
|
|
78
|
+
version=version,
|
|
79
|
+
initial=initial,
|
|
80
|
+
phases=phases,
|
|
81
|
+
abort=abort,
|
|
82
|
+
migrate=migrate,
|
|
83
|
+
hooks=values.get("hooks"),
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
__all__ = ["Task", "TaskDefinition", "define_task"]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Conformance and test helpers for storage backends.
|
|
2
|
+
|
|
3
|
+
The suite is runner independent: :func:`create_storage_conformance` returns cases and
|
|
4
|
+
:func:`register_storage_conformance` registers them with a Vitest/Jest-compatible runner.
|
|
5
|
+
Benchmark helpers are deliberately excluded (see ``PARITY.md``).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .assertions import create_expect_assertions
|
|
9
|
+
from .env_conformance import create_env_conformance
|
|
10
|
+
from .runner import register_env_conformance, register_storage_conformance
|
|
11
|
+
from .storage_conformance import create_storage_conformance
|
|
12
|
+
from .types import (
|
|
13
|
+
EnvConformanceAssertions,
|
|
14
|
+
EnvConformanceCase,
|
|
15
|
+
EnvConformanceOptions,
|
|
16
|
+
EnvConformanceProvider,
|
|
17
|
+
ExpectLike,
|
|
18
|
+
StorageConformanceAssertions,
|
|
19
|
+
StorageConformanceCase,
|
|
20
|
+
StorageConformanceOptions,
|
|
21
|
+
StorageConformanceProvider,
|
|
22
|
+
StorageConformanceRunner,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"EnvConformanceAssertions",
|
|
27
|
+
"EnvConformanceCase",
|
|
28
|
+
"EnvConformanceOptions",
|
|
29
|
+
"EnvConformanceProvider",
|
|
30
|
+
"ExpectLike",
|
|
31
|
+
"StorageConformanceAssertions",
|
|
32
|
+
"StorageConformanceCase",
|
|
33
|
+
"StorageConformanceOptions",
|
|
34
|
+
"StorageConformanceProvider",
|
|
35
|
+
"StorageConformanceRunner",
|
|
36
|
+
"create_env_conformance",
|
|
37
|
+
"create_expect_assertions",
|
|
38
|
+
"create_storage_conformance",
|
|
39
|
+
"register_env_conformance",
|
|
40
|
+
"register_storage_conformance",
|
|
41
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Adapter from a Vitest/Jest-style ``expect`` to the conformance assertion vocabulary.
|
|
2
|
+
|
|
3
|
+
The adapter imports neither runner: it calls the snake_case matcher operations of a compatible
|
|
4
|
+
``expect`` callable, so translated suites can supply pytest-backed assertions without changing
|
|
5
|
+
the conformance cases.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Awaitable
|
|
11
|
+
|
|
12
|
+
from .types import ExpectLike, StorageConformanceAssertions
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _Assertions:
|
|
16
|
+
def __init__(self, expect: ExpectLike) -> None:
|
|
17
|
+
self._expect = expect
|
|
18
|
+
|
|
19
|
+
def ok(self, value: object, message: str | None = None) -> None:
|
|
20
|
+
self._expect(value, message).to_be_truthy()
|
|
21
|
+
|
|
22
|
+
def strict_equal(self, actual: object, expected: object) -> None:
|
|
23
|
+
self._expect(actual, None).to_be(expected)
|
|
24
|
+
|
|
25
|
+
def deep_equal(self, actual: object, expected: object) -> None:
|
|
26
|
+
self._expect(actual, None).to_equal(expected)
|
|
27
|
+
|
|
28
|
+
def partial_deep_equal(self, actual: object, expected: object) -> None:
|
|
29
|
+
self._expect(actual, None).to_match_object(expected)
|
|
30
|
+
|
|
31
|
+
def greater_than(self, actual: float, expected: float) -> None:
|
|
32
|
+
self._expect(actual, None).to_be_greater_than(expected)
|
|
33
|
+
|
|
34
|
+
async def rejects(self, operation: Awaitable[object], message_includes: str) -> None:
|
|
35
|
+
await self._expect(operation, None).rejects.to_throw(message_includes)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_expect_assertions(expect: ExpectLike) -> StorageConformanceAssertions:
|
|
39
|
+
"""Adapt a runner's ``expect`` to :class:`StorageConformanceAssertions` without importing it."""
|
|
40
|
+
|
|
41
|
+
return _Assertions(expect)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ["create_expect_assertions"]
|