Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .donna/project/core/top_level_architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@ The code is separated by layers/subsystems into subpackages:
- `donna.core` — code that not in the Donna's domain, but required to its functioning: domain-independent utils, basic classes for errors, exceptions and other entities, etc.
- `donna.domain` — code that is required by all Donna'specific logic: ID classes, common types, etc.
- `donna.machine` — code that implements the core Donna's logic — how Donna works regardless of external environments, i.e. pure domain behavior.
- `donna.context` — code that stores and provides execution-scoped runtime context for Donna's domain logic: artifact/state/primitive caches and scoped values like current actor/work unit/operation identifiers.
- `donna.world` — code that implements various worlds where Donna can find and manage artifacts: from artifacts discovery to their loading, parsing, updating. Also contains code related to configuration of Donna.
- `donna.protocol` — code that implements protocol via which Donna's core domain logic interacts with external environments: CLI, API, etc. Includes basic classes for information representing (for the external environments) and its formatting.
- `donna.cli` — code that implements the `donna` CLI tool, its commands, arguments parsing, etc.
- `donna.primitives` — code that implements basic building blocks for Donna's behavior: concrete implementations of various classes from the `donna.machine`.
- `donna.lib` — module that contains constructed primitives to be used in donna artifacts by referencing them by python import path. Like `donna.lib.workflow`, `donna.lib.goto`, etc.
- `donna.artifacts` — artifacts that are distributed with Donna itself: specifications of how it works, predefined workflows, etc.

## Data structures

- Do not use `dataclass` for data structures. Use `donna.core.entities.BaseEntity` (subclass of the `pydantic.BaseModel`) for complex data structures and Python classes with `__slots__` for very simple ones (like cache keys).

## Autotests

- No autotests in the project for now.
7 changes: 7 additions & 0 deletions changes/next_release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Changes

- ff-57 Added context storage/cache for globally accessible entities like artifacts, primitives, and session state.
- Added `donna.machine.context` module.
- Refactored `World` interface.
- gh-62 Added `donna version` command that prints the version of the tool.
- gh-60 Fixed CLI artifact extension recognition for `donna artifacts update`.
4 changes: 0 additions & 4 deletions changes/unreleased.md

This file was deleted.

39 changes: 13 additions & 26 deletions donna/cli/commands/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
TagOption,
)
from donna.cli.utils import cells_cli
from donna.context.context import context
from donna.core.errors import ErrorsList
from donna.core.result import Err, Ok, Result
from donna.domain.ids import FullArtifactIdPattern
from donna.machine import journal as machine_journal
from donna.machine.sessions import load_state
from donna.protocol.cell_shortcuts import operation_succeeded
from donna.protocol.cells import Cell
from donna.workspaces import artifacts as world_artifacts
from donna.workspaces import tmp as world_tmp
from donna.workspaces.artifacts import RENDER_CONTEXT_VIEW

artifacts_cli = typer.Typer()

Expand All @@ -44,20 +44,7 @@ def _parse_slug_with_extension(value: str) -> Result[tuple[str, str], ErrorsList


def _log_artifact_operation(message: str) -> None:
state_result = load_state()

if state_result.is_err():
# log nothing if we have no session state
return

state = state_result.unwrap()

machine_journal.add(
message=message,
current_task_id=str(state.current_task.id) if state.current_task else None,
current_work_unit_id=None,
current_operation_id=None,
)
machine_journal.add(message=message)


def _log_operation_on_artifacts(message: str, pattern: FullArtifactIdPattern, tags: TagOption | None) -> None:
Expand All @@ -79,7 +66,7 @@ def list(
) -> Iterable[Cell]:
_log_operation_on_artifacts("List artifacts", pattern, tags)

artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, tags=tags).unwrap()

return [artifact.node().status() for artifact in artifacts]

Expand All @@ -92,7 +79,7 @@ def view(
) -> Iterable[Cell]:
_log_operation_on_artifacts("View artifacts", pattern, tags)

artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, tags=tags).unwrap()
return [artifact.node().info() for artifact in artifacts]


Expand All @@ -105,12 +92,12 @@ def view(
@cells_cli
def fetch(id: FullArtifactIdArgument, output: OutputPathOption = None) -> Iterable[Cell]:
if output is None:
extension = world_artifacts.artifact_file_extension(id).unwrap()
extension = context().artifacts.file_extension(id).unwrap()
output = world_tmp.file_for_artifact(id, extension)

_log_artifact_operation(f"Fetch artifact `{id}` to '{output}'")

world_artifacts.fetch_artifact(id, output).unwrap()
context().artifacts.fetch(id, output).unwrap()

return [
operation_succeeded(f"Artifact `{id}` fetched to '{output}'", artifact_id=str(id), output_path=str(output))
Expand Down Expand Up @@ -155,7 +142,7 @@ def update(

_log_artifact_operation(f"Update artifact `{id}` from '{input_display}'")

world_artifacts.update_artifact(id, input_path, extension=extension).unwrap()
context().artifacts.update(id, input_path, extension=extension).unwrap()
return [
operation_succeeded(
f"Artifact `{id}` updated from '{input_display}'",
Expand All @@ -170,7 +157,7 @@ def update(
def copy(source_id: FullArtifactIdArgument, target_id: FullArtifactIdArgument) -> Iterable[Cell]:
_log_artifact_operation(f"Copy artifact from `{source_id}` to `{target_id}`")

world_artifacts.copy_artifact(source_id, target_id).unwrap()
context().artifacts.copy(source_id, target_id).unwrap()
return [
operation_succeeded(
f"Artifact `{source_id}` copied to `{target_id}`",
Expand All @@ -185,7 +172,7 @@ def copy(source_id: FullArtifactIdArgument, target_id: FullArtifactIdArgument) -
def move(source_id: FullArtifactIdArgument, target_id: FullArtifactIdArgument) -> Iterable[Cell]:
_log_artifact_operation(f"Move artifact from `{source_id}` to `{target_id}`")

world_artifacts.move_artifact(source_id, target_id).unwrap()
context().artifacts.move(source_id, target_id).unwrap()
return [
operation_succeeded(
f"Artifact `{source_id}` moved to `{target_id}`",
Expand All @@ -203,11 +190,11 @@ def remove(
) -> Iterable[Cell]:
_log_operation_on_artifacts("Remove artifacts", pattern, tags)

artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, tags=tags).unwrap()

cells: builtins.list[Cell] = []
for artifact in artifacts:
world_artifacts.remove_artifact(artifact.id).unwrap()
context().artifacts.remove(artifact.id).unwrap()
cells.append(operation_succeeded(f"Artifact `{artifact.id}` removed", artifact_id=str(artifact.id)))

return cells
Expand All @@ -221,7 +208,7 @@ def validate(
) -> Iterable[Cell]: # noqa: CCR001
_log_operation_on_artifacts("Validate artifacts", pattern, tags)

artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, tags=tags).unwrap()

errors = []

Expand Down
10 changes: 1 addition & 9 deletions donna/cli/commands/journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from donna.cli.application import app
from donna.cli.utils import cells_cli, output_cells
from donna.machine import journal as machine_journal
from donna.machine.sessions import load_state
from donna.protocol.cell_shortcuts import operation_succeeded
from donna.protocol.cells import Cell
from donna.protocol.modes import get_cell_formatter
Expand All @@ -19,14 +18,7 @@
def write(
message: str = typer.Argument(..., help="Single-line message to append to journal (newlines are not allowed)."),
) -> Iterable[Cell]:
state = load_state().unwrap()

machine_journal.add(
message=message,
current_task_id=str(state.current_task.id) if state.current_task else None,
current_work_unit_id=None,
current_operation_id=None,
).unwrap()
machine_journal.add(message=message).unwrap()
return [operation_succeeded("Journal record appended.")]


Expand Down
13 changes: 3 additions & 10 deletions donna/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,12 @@ def output_cells(cells: Iterable[Cell]) -> None:

def _write_errors_to_journal(errors: ErrorsList) -> None:
from donna.machine import journal as machine_journal
from donna.machine import sessions as machine_sessions

state_result = machine_sessions.load_state()
current_task_id = None
if state_result.is_ok():
state = state_result.unwrap()
current_task_id = str(state.current_task.id) if state.current_task else None

for error in errors:
message = f"Error: {error.node().journal_message()} [{error.code}]"

machine_journal.add(
message=message,
current_task_id=current_task_id,
current_work_unit_id=None,
current_operation_id=None,
actor_id="donna",
)

Expand Down Expand Up @@ -80,6 +70,8 @@ def _is_workspace_init_command() -> bool:


def try_initialize_donna(project_dir: pathlib.Path | None, protocol: Mode) -> None:
from donna.context import Context, set_context

if _is_workspace_init_command():
workspace_config.protocol.set(protocol)
if project_dir is not None:
Expand All @@ -89,6 +81,7 @@ def try_initialize_donna(project_dir: pathlib.Path | None, protocol: Mode) -> No
result = initialize_runtime(root_dir=project_dir, protocol=protocol)

if result.is_ok():
set_context(Context())
return

errors = result.unwrap_err()
Expand Down
3 changes: 3 additions & 0 deletions donna/context/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from donna.context.context import Context, context, reset_context, set_context

__all__ = ("Context", "context", "set_context", "reset_context")
Loading