donna 0.2.0__py3-none-any.whl → 0.2.2__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.
- donna/artifacts/intro.md +39 -0
- donna/artifacts/research/specs/report.md +163 -0
- donna/artifacts/research/work/research.md +198 -0
- donna/artifacts/rfc/specs/request_for_change.md +270 -0
- donna/artifacts/rfc/work/create.md +120 -0
- donna/artifacts/rfc/work/do.md +109 -0
- donna/artifacts/rfc/work/plan.md +68 -0
- donna/artifacts/usage/artifacts.md +55 -12
- donna/artifacts/usage/cli.md +114 -39
- donna/artifacts/usage/worlds.md +8 -2
- donna/cli/__main__.py +1 -1
- donna/cli/commands/artifacts.py +104 -17
- donna/cli/commands/sessions.py +8 -8
- donna/cli/commands/workspaces.py +42 -0
- donna/cli/errors.py +18 -0
- donna/cli/types.py +16 -9
- donna/cli/utils.py +2 -2
- donna/core/errors.py +1 -11
- donna/core/result.py +5 -8
- donna/core/utils.py +0 -3
- donna/lib/__init__.py +4 -0
- donna/lib/sources.py +1 -1
- donna/lib/worlds.py +2 -2
- donna/machine/action_requests.py +0 -5
- donna/machine/artifacts.py +8 -6
- donna/machine/primitives.py +5 -5
- donna/machine/sessions.py +13 -5
- donna/machine/state.py +4 -4
- donna/machine/tasks.py +4 -18
- donna/machine/templates.py +4 -2
- donna/primitives/artifacts/specification.py +13 -2
- donna/primitives/artifacts/workflow.py +11 -2
- donna/primitives/directives/list.py +86 -0
- donna/primitives/directives/view.py +52 -11
- donna/primitives/operations/finish_workflow.py +13 -2
- donna/primitives/operations/output.py +87 -0
- donna/primitives/operations/request_action.py +3 -9
- donna/primitives/operations/run_script.py +2 -2
- donna/protocol/utils.py +22 -0
- donna/workspaces/artifacts.py +238 -0
- donna/{world → workspaces}/artifacts_discovery.py +1 -1
- donna/{world → workspaces}/config.py +18 -11
- donna/{world → workspaces}/errors.py +55 -45
- donna/workspaces/initialization.py +78 -0
- donna/{world → workspaces}/markdown.py +21 -26
- donna/{world → workspaces}/sources/base.py +2 -2
- donna/{world → workspaces}/sources/markdown.py +8 -7
- donna/{world → workspaces}/templates.py +4 -4
- donna/workspaces/tmp.py +51 -0
- donna/{world → workspaces}/worlds/base.py +6 -3
- donna/{world → workspaces}/worlds/filesystem.py +30 -10
- donna/{world → workspaces}/worlds/python.py +12 -9
- donna-0.2.2.dist-info/METADATA +463 -0
- donna-0.2.2.dist-info/RECORD +92 -0
- {donna-0.2.0.dist-info → donna-0.2.2.dist-info}/WHEEL +1 -1
- donna/artifacts/work/do_it.md +0 -142
- donna/artifacts/work/do_it_fast.md +0 -98
- donna/artifacts/work/planning.md +0 -245
- donna/cli/commands/projects.py +0 -49
- donna/world/artifacts.py +0 -122
- donna/world/initialization.py +0 -42
- donna/world/tmp.py +0 -33
- donna/world/worlds/__init__.py +0 -0
- donna-0.2.0.dist-info/METADATA +0 -44
- donna-0.2.0.dist-info/RECORD +0 -85
- /donna/{artifacts/work → workspaces}/__init__.py +0 -0
- /donna/{world → workspaces}/sources/__init__.py +0 -0
- /donna/{world → workspaces/worlds}/__init__.py +0 -0
- {donna-0.2.0.dist-info → donna-0.2.2.dist-info}/entry_points.txt +0 -0
- {donna-0.2.0.dist-info → donna-0.2.2.dist-info}/licenses/LICENSE +0 -0
donna/machine/tasks.py
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import copy
|
|
2
|
-
import sys
|
|
3
2
|
from typing import TYPE_CHECKING, Any
|
|
4
3
|
|
|
5
|
-
import pydantic
|
|
6
|
-
|
|
7
4
|
from donna.core.entities import BaseEntity
|
|
8
5
|
from donna.core.errors import ErrorsList
|
|
9
6
|
from donna.core.result import Ok, Result, unwrap_to_error
|
|
10
7
|
from donna.domain.ids import FullArtifactSectionId, TaskId, WorkUnitId
|
|
11
8
|
from donna.protocol.cells import Cell
|
|
12
|
-
from donna.protocol.
|
|
9
|
+
from donna.protocol.utils import instant_output
|
|
13
10
|
|
|
14
11
|
if TYPE_CHECKING:
|
|
15
12
|
from donna.machine.changes import Change
|
|
@@ -19,9 +16,6 @@ class Task(BaseEntity):
|
|
|
19
16
|
id: TaskId
|
|
20
17
|
context: dict[str, Any]
|
|
21
18
|
|
|
22
|
-
# TODO: we may want to make queue items frozen later
|
|
23
|
-
model_config = pydantic.ConfigDict(frozen=False)
|
|
24
|
-
|
|
25
19
|
@classmethod
|
|
26
20
|
def build(cls, id: TaskId) -> "Task":
|
|
27
21
|
return Task(
|
|
@@ -61,8 +55,8 @@ class WorkUnit(BaseEntity):
|
|
|
61
55
|
def run(self, task: Task) -> Result[list["Change"], ErrorsList]:
|
|
62
56
|
from donna.machine import artifacts as machine_artifacts
|
|
63
57
|
from donna.machine.primitives import resolve_primitive
|
|
64
|
-
from donna.
|
|
65
|
-
from donna.
|
|
58
|
+
from donna.workspaces.artifacts import ArtifactRenderContext
|
|
59
|
+
from donna.workspaces.templates import RenderMode
|
|
66
60
|
|
|
67
61
|
render_context = ArtifactRenderContext(
|
|
68
62
|
primary_mode=RenderMode.execute,
|
|
@@ -72,17 +66,9 @@ class WorkUnit(BaseEntity):
|
|
|
72
66
|
operation = machine_artifacts.resolve(self.operation_id, render_context).unwrap()
|
|
73
67
|
operation_kind = resolve_primitive(operation.kind).unwrap()
|
|
74
68
|
|
|
75
|
-
##########################
|
|
76
|
-
# We log each operation here to help agent display the progress to the user
|
|
77
|
-
# TODO: not a good solution from the agent perspective
|
|
78
|
-
# let's hope there will some protocol appear that helps with that later
|
|
79
|
-
# TODO: not so good place for and way of logging, should do smth with that
|
|
80
69
|
log_message = f"{self.operation_id}: {operation.title}"
|
|
81
70
|
log_cell = Cell.build(kind="donna_log", media_type="text/plain", content=log_message)
|
|
82
|
-
|
|
83
|
-
sys.stdout.buffer.write(formatter.format_log(log_cell, single_mode=True) + b"\n\n")
|
|
84
|
-
sys.stdout.buffer.flush()
|
|
85
|
-
##########################
|
|
71
|
+
instant_output([log_cell])
|
|
86
72
|
|
|
87
73
|
changes = list(operation_kind.execute_section(task, self, operation))
|
|
88
74
|
|
donna/machine/templates.py
CHANGED
|
@@ -28,11 +28,12 @@ class Directive(Primitive, ABC):
|
|
|
28
28
|
self,
|
|
29
29
|
context: Context,
|
|
30
30
|
*argv: Any,
|
|
31
|
+
**kwargs: Any,
|
|
31
32
|
) -> Result[Any, ErrorsList]:
|
|
32
|
-
from donna.
|
|
33
|
+
from donna.workspaces import templates as world_templates
|
|
33
34
|
|
|
34
35
|
render_mode = context["render_mode"]
|
|
35
|
-
arguments_result = self._prepare_arguments(context, *argv)
|
|
36
|
+
arguments_result = self._prepare_arguments(context, *argv, **kwargs)
|
|
36
37
|
if arguments_result.is_err():
|
|
37
38
|
return arguments_result
|
|
38
39
|
|
|
@@ -52,6 +53,7 @@ class Directive(Primitive, ABC):
|
|
|
52
53
|
self,
|
|
53
54
|
context: Context,
|
|
54
55
|
*argv: Any,
|
|
56
|
+
**kwargs: Any,
|
|
55
57
|
) -> PreparedDirectiveResult:
|
|
56
58
|
return Ok(argv)
|
|
57
59
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, ClassVar
|
|
2
2
|
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
3
5
|
from donna.machine.artifacts import ArtifactSectionConfig
|
|
4
6
|
from donna.machine.primitives import Primitive
|
|
5
|
-
from donna.
|
|
7
|
+
from donna.workspaces.sources.markdown import MarkdownSectionMixin
|
|
6
8
|
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
pass
|
|
@@ -16,5 +18,14 @@ class Text(MarkdownSectionMixin, Primitive):
|
|
|
16
18
|
config_class: ClassVar[type[TextConfig]] = TextConfig
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
class SpecificationConfig(ArtifactSectionConfig):
|
|
22
|
+
@pydantic.field_validator("tags", mode="after")
|
|
23
|
+
@classmethod
|
|
24
|
+
def ensure_specification_tag(cls, value: list[str]) -> list[str]:
|
|
25
|
+
if "specification" in value:
|
|
26
|
+
return value
|
|
27
|
+
return [*value, "specification"]
|
|
28
|
+
|
|
29
|
+
|
|
19
30
|
class Specification(MarkdownSectionMixin, Primitive):
|
|
20
|
-
|
|
31
|
+
config_class: ClassVar[type[SpecificationConfig]] = SpecificationConfig
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, ClassVar, Iterable, cast
|
|
2
2
|
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
3
5
|
from donna.core import errors as core_errors
|
|
4
6
|
from donna.core.errors import ErrorsList
|
|
5
7
|
from donna.core.result import Err, Ok, Result, unwrap_to_error
|
|
@@ -8,8 +10,8 @@ from donna.machine.artifacts import Artifact, ArtifactSection, ArtifactSectionCo
|
|
|
8
10
|
from donna.machine.errors import ArtifactValidationError
|
|
9
11
|
from donna.machine.operations import FsmMode, OperationMeta
|
|
10
12
|
from donna.machine.primitives import Primitive
|
|
11
|
-
from donna.
|
|
12
|
-
from donna.
|
|
13
|
+
from donna.workspaces import markdown
|
|
14
|
+
from donna.workspaces.sources.markdown import MarkdownSectionMixin
|
|
13
15
|
|
|
14
16
|
if TYPE_CHECKING:
|
|
15
17
|
from donna.machine.changes import Change
|
|
@@ -97,6 +99,13 @@ def find_workflow_sections(start_operation_id: ArtifactSectionId, artifact: Arti
|
|
|
97
99
|
class WorkflowConfig(ArtifactSectionConfig):
|
|
98
100
|
start_operation_id: ArtifactSectionId
|
|
99
101
|
|
|
102
|
+
@pydantic.field_validator("tags", mode="after")
|
|
103
|
+
@classmethod
|
|
104
|
+
def ensure_workflow_tag(cls, value: list[str]) -> list[str]:
|
|
105
|
+
if "workflow" in value:
|
|
106
|
+
return value
|
|
107
|
+
return [*value, "workflow"]
|
|
108
|
+
|
|
100
109
|
|
|
101
110
|
class WorkflowMeta(ArtifactSectionMeta):
|
|
102
111
|
start_operation_id: ArtifactSectionId
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from jinja2.runtime import Context
|
|
4
|
+
|
|
5
|
+
from donna.core import errors as core_errors
|
|
6
|
+
from donna.core.errors import ErrorsList
|
|
7
|
+
from donna.core.result import Err, Ok, Result
|
|
8
|
+
from donna.domain.ids import FullArtifactIdPattern
|
|
9
|
+
from donna.machine.templates import Directive, PreparedDirectiveResult
|
|
10
|
+
from donna.protocol.modes import mode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EnvironmentError(core_errors.EnvironmentError):
|
|
14
|
+
cell_kind: str = "directive_error"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ListInvalidArguments(EnvironmentError):
|
|
18
|
+
code: str = "donna.directives.list.invalid_arguments"
|
|
19
|
+
message: str = (
|
|
20
|
+
"List directive requires exactly one positional argument: artifact_id_pattern (got {error.provided_count})."
|
|
21
|
+
)
|
|
22
|
+
ways_to_fix: list[str] = ["Provide exactly one argument: artifact_id_pattern."]
|
|
23
|
+
provided_count: int
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ListInvalidKeyword(EnvironmentError):
|
|
27
|
+
code: str = "donna.directives.list.invalid_keyword"
|
|
28
|
+
message: str = "List directive accepts only the `tags` keyword argument (got {error.keyword})."
|
|
29
|
+
ways_to_fix: list[str] = ["Remove unsupported keyword arguments."]
|
|
30
|
+
keyword: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ListInvalidTags(EnvironmentError):
|
|
34
|
+
code: str = "donna.directives.list.invalid_tags"
|
|
35
|
+
message: str = "List directive `tags` must be a list of strings."
|
|
36
|
+
ways_to_fix: list[str] = ["Provide tags as a list of strings, e.g. tags=['tag1', 'tag2']."]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class List(Directive):
|
|
40
|
+
def _prepare_arguments( # noqa: CCR001
|
|
41
|
+
self,
|
|
42
|
+
context: Context,
|
|
43
|
+
*argv: Any,
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
) -> PreparedDirectiveResult:
|
|
46
|
+
if argv is None or len(argv) != 1:
|
|
47
|
+
return Err([ListInvalidArguments(provided_count=0 if argv is None else len(argv))])
|
|
48
|
+
|
|
49
|
+
for keyword in kwargs:
|
|
50
|
+
if keyword != "tags":
|
|
51
|
+
return Err([ListInvalidKeyword(keyword=keyword)])
|
|
52
|
+
|
|
53
|
+
artifact_pattern_result = FullArtifactIdPattern.parse(str(argv[0]))
|
|
54
|
+
errors = artifact_pattern_result.err()
|
|
55
|
+
if errors is not None:
|
|
56
|
+
return Err(errors)
|
|
57
|
+
|
|
58
|
+
artifact_pattern = artifact_pattern_result.ok()
|
|
59
|
+
assert artifact_pattern is not None
|
|
60
|
+
|
|
61
|
+
tags = kwargs.get("tags")
|
|
62
|
+
if tags is None:
|
|
63
|
+
tags_list: list[str] = []
|
|
64
|
+
elif isinstance(tags, (list, tuple, set)):
|
|
65
|
+
tags_list = [str(tag) for tag in tags]
|
|
66
|
+
else:
|
|
67
|
+
return Err([ListInvalidTags()])
|
|
68
|
+
|
|
69
|
+
return Ok((artifact_pattern, tags_list))
|
|
70
|
+
|
|
71
|
+
def render_view(
|
|
72
|
+
self, context: Context, artifact_pattern: FullArtifactIdPattern, tags: list[str]
|
|
73
|
+
) -> Result[Any, ErrorsList]:
|
|
74
|
+
protocol = mode().value
|
|
75
|
+
tags_args = " ".join(f"--tag '{tag}'" for tag in tags)
|
|
76
|
+
tag_suffix = f" {tags_args}" if tags_args else ""
|
|
77
|
+
return Ok(f"{artifact_pattern} (donna -p {protocol} artifacts list '{artifact_pattern}'{tag_suffix})")
|
|
78
|
+
|
|
79
|
+
def render_analyze(
|
|
80
|
+
self, context: Context, artifact_pattern: FullArtifactIdPattern, tags: list[str]
|
|
81
|
+
) -> Result[Any, ErrorsList]:
|
|
82
|
+
if not tags:
|
|
83
|
+
return Ok(f"$$donna {self.analyze_id} {artifact_pattern} donna$$")
|
|
84
|
+
|
|
85
|
+
tags_marker = ",".join(tags)
|
|
86
|
+
return Ok(f"$$donna {self.analyze_id} {artifact_pattern} tags={tags_marker} donna$$")
|
|
@@ -5,7 +5,7 @@ from jinja2.runtime import Context
|
|
|
5
5
|
from donna.core import errors as core_errors
|
|
6
6
|
from donna.core.errors import ErrorsList
|
|
7
7
|
from donna.core.result import Err, Ok, Result
|
|
8
|
-
from donna.domain.ids import
|
|
8
|
+
from donna.domain.ids import FullArtifactIdPattern
|
|
9
9
|
from donna.machine.templates import Directive, PreparedDirectiveResult
|
|
10
10
|
from donna.protocol.modes import mode
|
|
11
11
|
|
|
@@ -16,30 +16,71 @@ class EnvironmentError(core_errors.EnvironmentError):
|
|
|
16
16
|
|
|
17
17
|
class ViewInvalidArguments(EnvironmentError):
|
|
18
18
|
code: str = "donna.directives.view.invalid_arguments"
|
|
19
|
-
message: str =
|
|
20
|
-
|
|
19
|
+
message: str = (
|
|
20
|
+
"View directive requires exactly one positional argument: artifact_id_pattern (got {error.provided_count})."
|
|
21
|
+
)
|
|
22
|
+
ways_to_fix: list[str] = ["Provide exactly one argument: artifact_id_pattern."]
|
|
21
23
|
provided_count: int
|
|
22
24
|
|
|
23
25
|
|
|
26
|
+
class ViewInvalidKeyword(EnvironmentError):
|
|
27
|
+
code: str = "donna.directives.view.invalid_keyword"
|
|
28
|
+
message: str = "View directive accepts only the `tags` keyword argument (got {error.keyword})."
|
|
29
|
+
ways_to_fix: list[str] = ["Remove unsupported keyword arguments."]
|
|
30
|
+
keyword: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ViewInvalidTags(EnvironmentError):
|
|
34
|
+
code: str = "donna.directives.view.invalid_tags"
|
|
35
|
+
message: str = "View directive `tags` must be a list of strings."
|
|
36
|
+
ways_to_fix: list[str] = ["Provide tags as a list of strings, e.g. tags=['tag1', 'tag2']."]
|
|
37
|
+
|
|
38
|
+
|
|
24
39
|
class View(Directive):
|
|
25
|
-
def _prepare_arguments(
|
|
40
|
+
def _prepare_arguments( # noqa: CCR001
|
|
26
41
|
self,
|
|
27
42
|
context: Context,
|
|
28
43
|
*argv: Any,
|
|
44
|
+
**kwargs: Any,
|
|
29
45
|
) -> PreparedDirectiveResult:
|
|
30
46
|
if argv is None or len(argv) != 1:
|
|
31
47
|
return Err([ViewInvalidArguments(provided_count=0 if argv is None else len(argv))])
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
for keyword in kwargs:
|
|
50
|
+
if keyword != "tags":
|
|
51
|
+
return Err([ViewInvalidKeyword(keyword=keyword)])
|
|
52
|
+
|
|
53
|
+
artifact_pattern_result = FullArtifactIdPattern.parse(str(argv[0]))
|
|
54
|
+
errors = artifact_pattern_result.err()
|
|
35
55
|
if errors is not None:
|
|
36
56
|
return Err(errors)
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
assert
|
|
58
|
+
artifact_pattern = artifact_pattern_result.ok()
|
|
59
|
+
assert artifact_pattern is not None
|
|
60
|
+
|
|
61
|
+
tags = kwargs.get("tags")
|
|
62
|
+
if tags is None:
|
|
63
|
+
tags_list: list[str] = []
|
|
64
|
+
elif isinstance(tags, (list, tuple, set)):
|
|
65
|
+
tags_list = [str(tag) for tag in tags]
|
|
66
|
+
else:
|
|
67
|
+
return Err([ViewInvalidTags()])
|
|
40
68
|
|
|
41
|
-
return Ok((
|
|
69
|
+
return Ok((artifact_pattern, tags_list))
|
|
42
70
|
|
|
43
|
-
def render_view(
|
|
71
|
+
def render_view(
|
|
72
|
+
self, context: Context, artifact_pattern: FullArtifactIdPattern, tags: list[str]
|
|
73
|
+
) -> Result[Any, ErrorsList]:
|
|
44
74
|
protocol = mode().value
|
|
45
|
-
|
|
75
|
+
tags_args = " ".join(f"--tag '{tag}'" for tag in tags)
|
|
76
|
+
tag_suffix = f" {tags_args}" if tags_args else ""
|
|
77
|
+
return Ok(f"{artifact_pattern} (donna -p {protocol} artifacts view '{artifact_pattern}'{tag_suffix})")
|
|
78
|
+
|
|
79
|
+
def render_analyze(
|
|
80
|
+
self, context: Context, artifact_pattern: FullArtifactIdPattern, tags: list[str]
|
|
81
|
+
) -> Result[Any, ErrorsList]:
|
|
82
|
+
if not tags:
|
|
83
|
+
return Ok(f"$$donna {self.analyze_id} {artifact_pattern} donna$$")
|
|
84
|
+
|
|
85
|
+
tags_marker = ",".join(tags)
|
|
86
|
+
return Ok(f"$$donna {self.analyze_id} {artifact_pattern} tags={tags_marker} donna$$")
|
|
@@ -5,8 +5,10 @@ from donna.core.result import Ok, Result
|
|
|
5
5
|
from donna.domain.ids import FullArtifactId
|
|
6
6
|
from donna.machine.artifacts import ArtifactSection, ArtifactSectionConfig, ArtifactSectionMeta
|
|
7
7
|
from donna.machine.operations import FsmMode, OperationConfig, OperationKind, OperationMeta
|
|
8
|
-
from donna.
|
|
9
|
-
from donna.
|
|
8
|
+
from donna.protocol.cells import Cell
|
|
9
|
+
from donna.protocol.utils import instant_output
|
|
10
|
+
from donna.workspaces import markdown
|
|
11
|
+
from donna.workspaces.sources.markdown import MarkdownSectionMixin
|
|
10
12
|
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
from donna.machine.changes import Change
|
|
@@ -21,6 +23,15 @@ class FinishWorkflow(MarkdownSectionMixin, OperationKind):
|
|
|
21
23
|
def execute_section(self, task: "Task", unit: "WorkUnit", operation: ArtifactSection) -> Iterator["Change"]:
|
|
22
24
|
from donna.machine.changes import ChangeFinishTask
|
|
23
25
|
|
|
26
|
+
output_text = operation.description
|
|
27
|
+
|
|
28
|
+
output_cell = Cell.build_markdown(
|
|
29
|
+
kind="operation_output",
|
|
30
|
+
content=output_text,
|
|
31
|
+
operation_id=str(unit.operation_id),
|
|
32
|
+
)
|
|
33
|
+
instant_output([output_cell])
|
|
34
|
+
|
|
24
35
|
yield ChangeFinishTask(task_id=task.id)
|
|
25
36
|
|
|
26
37
|
config_class: ClassVar[type[FinishWorkflowConfig]] = FinishWorkflowConfig
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, ClassVar, Iterator, cast
|
|
2
|
+
|
|
3
|
+
from donna.core.errors import ErrorsList
|
|
4
|
+
from donna.core.result import Err, Ok, Result
|
|
5
|
+
from donna.domain.ids import ArtifactSectionId, FullArtifactId
|
|
6
|
+
from donna.machine.artifacts import Artifact, ArtifactSection, ArtifactSectionConfig, ArtifactSectionMeta
|
|
7
|
+
from donna.machine.errors import ArtifactValidationError
|
|
8
|
+
from donna.machine.operations import OperationConfig, OperationKind, OperationMeta
|
|
9
|
+
from donna.protocol.cells import Cell
|
|
10
|
+
from donna.protocol.utils import instant_output
|
|
11
|
+
from donna.workspaces import markdown
|
|
12
|
+
from donna.workspaces.sources.markdown import MarkdownSectionMixin
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from donna.machine.changes import Change
|
|
16
|
+
from donna.machine.tasks import Task, WorkUnit
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OutputMissingNextOperation(ArtifactValidationError):
|
|
20
|
+
code: str = "donna.workflows.output_missing_next_operation_id"
|
|
21
|
+
message: str = "Output operation `{error.section_id}` must define `next_operation_id`."
|
|
22
|
+
ways_to_fix: list[str] = [
|
|
23
|
+
'Add `next_operation_id = "<next_operation_id>"` to the operation config block.',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OutputConfig(OperationConfig):
|
|
28
|
+
next_operation_id: ArtifactSectionId | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class OutputMeta(OperationMeta):
|
|
32
|
+
next_operation_id: ArtifactSectionId | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Output(MarkdownSectionMixin, OperationKind):
|
|
36
|
+
config_class: ClassVar[type[OutputConfig]] = OutputConfig
|
|
37
|
+
|
|
38
|
+
def markdown_construct_meta(
|
|
39
|
+
self,
|
|
40
|
+
artifact_id: "FullArtifactId",
|
|
41
|
+
source: markdown.SectionSource,
|
|
42
|
+
section_config: ArtifactSectionConfig,
|
|
43
|
+
description: str,
|
|
44
|
+
primary: bool = False,
|
|
45
|
+
) -> Result[ArtifactSectionMeta, ErrorsList]:
|
|
46
|
+
output_config = cast(OutputConfig, section_config)
|
|
47
|
+
|
|
48
|
+
allowed_transitions: set[ArtifactSectionId] = set()
|
|
49
|
+
if output_config.next_operation_id is not None:
|
|
50
|
+
allowed_transitions.add(output_config.next_operation_id)
|
|
51
|
+
|
|
52
|
+
return Ok(
|
|
53
|
+
OutputMeta(
|
|
54
|
+
fsm_mode=output_config.fsm_mode,
|
|
55
|
+
allowed_transtions=allowed_transitions,
|
|
56
|
+
next_operation_id=output_config.next_operation_id,
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def execute_section(self, task: "Task", unit: "WorkUnit", operation: ArtifactSection) -> Iterator["Change"]:
|
|
61
|
+
from donna.machine.changes import ChangeAddWorkUnit
|
|
62
|
+
|
|
63
|
+
meta = cast(OutputMeta, operation.meta)
|
|
64
|
+
|
|
65
|
+
output_text = operation.description
|
|
66
|
+
|
|
67
|
+
output_cell = Cell.build_markdown(
|
|
68
|
+
kind="operation_output",
|
|
69
|
+
content=output_text,
|
|
70
|
+
operation_id=str(unit.operation_id),
|
|
71
|
+
)
|
|
72
|
+
instant_output([output_cell])
|
|
73
|
+
|
|
74
|
+
next_operation_id = meta.next_operation_id
|
|
75
|
+
assert next_operation_id is not None
|
|
76
|
+
full_operation_id = unit.operation_id.full_artifact_id.to_full_local(next_operation_id)
|
|
77
|
+
|
|
78
|
+
yield ChangeAddWorkUnit(task_id=task.id, operation_id=full_operation_id)
|
|
79
|
+
|
|
80
|
+
def validate_section(self, artifact: Artifact, section_id: ArtifactSectionId) -> Result[None, ErrorsList]:
|
|
81
|
+
section = artifact.get_section(section_id).unwrap()
|
|
82
|
+
meta = cast(OutputMeta, section.meta)
|
|
83
|
+
|
|
84
|
+
if meta.next_operation_id is None:
|
|
85
|
+
return Err([OutputMissingNextOperation(artifact_id=artifact.id, section_id=section_id)])
|
|
86
|
+
|
|
87
|
+
return Ok(None)
|
|
@@ -10,8 +10,8 @@ from donna.domain.ids import ArtifactSectionId, FullArtifactId
|
|
|
10
10
|
from donna.machine.action_requests import ActionRequest
|
|
11
11
|
from donna.machine.artifacts import ArtifactSection, ArtifactSectionConfig, ArtifactSectionMeta
|
|
12
12
|
from donna.machine.operations import FsmMode, OperationConfig, OperationKind, OperationMeta
|
|
13
|
-
from donna.
|
|
14
|
-
from donna.
|
|
13
|
+
from donna.workspaces import markdown
|
|
14
|
+
from donna.workspaces.sources.markdown import MarkdownSectionMixin
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from donna.machine.changes import Change
|
|
@@ -74,13 +74,7 @@ class RequestAction(MarkdownSectionMixin, OperationKind):
|
|
|
74
74
|
def execute_section(self, task: "Task", unit: "WorkUnit", operation: ArtifactSection) -> Iterator["Change"]:
|
|
75
75
|
from donna.machine.changes import ChangeAddActionRequest
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
"scheme": operation,
|
|
79
|
-
"task": task,
|
|
80
|
-
"work_unit": unit,
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
request_text = operation.description.format(**context)
|
|
77
|
+
request_text = operation.description
|
|
84
78
|
|
|
85
79
|
full_operation_id = unit.operation_id
|
|
86
80
|
|
|
@@ -12,8 +12,8 @@ from donna.domain.ids import ArtifactSectionId, FullArtifactId
|
|
|
12
12
|
from donna.machine.artifacts import Artifact, ArtifactSection, ArtifactSectionConfig, ArtifactSectionMeta
|
|
13
13
|
from donna.machine.errors import ArtifactValidationError
|
|
14
14
|
from donna.machine.operations import OperationConfig, OperationKind, OperationMeta
|
|
15
|
-
from donna.
|
|
16
|
-
from donna.
|
|
15
|
+
from donna.workspaces import markdown
|
|
16
|
+
from donna.workspaces.sources.markdown import MarkdownSectionMixin
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from donna.machine.changes import Change
|
donna/protocol/utils.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from donna.protocol.cells import Cell
|
|
4
|
+
from donna.protocol.modes import get_cell_formatter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def instant_output(cells: list[Cell]) -> None:
|
|
8
|
+
if not cells:
|
|
9
|
+
return
|
|
10
|
+
|
|
11
|
+
formatter = get_cell_formatter()
|
|
12
|
+
|
|
13
|
+
formatted_cells: list[bytes] = []
|
|
14
|
+
for cell in cells:
|
|
15
|
+
# TODO: we should refactor that hardcoded check somehow
|
|
16
|
+
if cell.kind == "donna_log":
|
|
17
|
+
formatted_cells.append(formatter.format_log(cell, single_mode=True))
|
|
18
|
+
else:
|
|
19
|
+
formatted_cells.append(formatter.format_cell(cell, single_mode=False))
|
|
20
|
+
|
|
21
|
+
sys.stdout.buffer.write(b"\n\n".join(formatted_cells) + b"\n\n")
|
|
22
|
+
sys.stdout.buffer.flush()
|