py-data-engine 0.1.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.
- data_engine/__init__.py +37 -0
- data_engine/application/__init__.py +39 -0
- data_engine/application/actions.py +42 -0
- data_engine/application/catalog.py +151 -0
- data_engine/application/control.py +213 -0
- data_engine/application/details.py +73 -0
- data_engine/application/runtime.py +449 -0
- data_engine/application/workspace.py +62 -0
- data_engine/authoring/__init__.py +14 -0
- data_engine/authoring/builder.py +31 -0
- data_engine/authoring/execution/__init__.py +6 -0
- data_engine/authoring/execution/app.py +6 -0
- data_engine/authoring/execution/context.py +82 -0
- data_engine/authoring/execution/continuous.py +176 -0
- data_engine/authoring/execution/grouped.py +106 -0
- data_engine/authoring/execution/logging.py +83 -0
- data_engine/authoring/execution/polling.py +135 -0
- data_engine/authoring/execution/runner.py +210 -0
- data_engine/authoring/execution/single.py +171 -0
- data_engine/authoring/flow.py +361 -0
- data_engine/authoring/helpers.py +160 -0
- data_engine/authoring/model.py +59 -0
- data_engine/authoring/primitives.py +430 -0
- data_engine/authoring/services.py +42 -0
- data_engine/devtools/__init__.py +3 -0
- data_engine/devtools/project_ast_map.py +503 -0
- data_engine/docs/__init__.py +1 -0
- data_engine/docs/sphinx_source/_static/custom.css +13 -0
- data_engine/docs/sphinx_source/api.rst +42 -0
- data_engine/docs/sphinx_source/conf.py +37 -0
- data_engine/docs/sphinx_source/guides/app-runtime-and-workspaces.md +397 -0
- data_engine/docs/sphinx_source/guides/authoring-flow-modules.md +215 -0
- data_engine/docs/sphinx_source/guides/configuring-flows.md +185 -0
- data_engine/docs/sphinx_source/guides/core-concepts.md +208 -0
- data_engine/docs/sphinx_source/guides/database-methods.md +107 -0
- data_engine/docs/sphinx_source/guides/duckdb-helpers.md +462 -0
- data_engine/docs/sphinx_source/guides/flow-context.md +538 -0
- data_engine/docs/sphinx_source/guides/flow-methods.md +206 -0
- data_engine/docs/sphinx_source/guides/getting-started.md +271 -0
- data_engine/docs/sphinx_source/guides/project-inventory.md +5683 -0
- data_engine/docs/sphinx_source/guides/project-map.md +118 -0
- data_engine/docs/sphinx_source/guides/recipes.md +268 -0
- data_engine/docs/sphinx_source/index.rst +22 -0
- data_engine/domain/__init__.py +92 -0
- data_engine/domain/actions.py +69 -0
- data_engine/domain/catalog.py +128 -0
- data_engine/domain/details.py +214 -0
- data_engine/domain/diagnostics.py +56 -0
- data_engine/domain/errors.py +104 -0
- data_engine/domain/inspection.py +99 -0
- data_engine/domain/logs.py +118 -0
- data_engine/domain/operations.py +172 -0
- data_engine/domain/operator.py +72 -0
- data_engine/domain/runs.py +155 -0
- data_engine/domain/runtime.py +279 -0
- data_engine/domain/source_state.py +17 -0
- data_engine/domain/support.py +54 -0
- data_engine/domain/time.py +23 -0
- data_engine/domain/workspace.py +159 -0
- data_engine/flow_modules/__init__.py +1 -0
- data_engine/flow_modules/flow_module_compiler.py +179 -0
- data_engine/flow_modules/flow_module_loader.py +201 -0
- data_engine/helpers/__init__.py +25 -0
- data_engine/helpers/duckdb.py +705 -0
- data_engine/hosts/__init__.py +1 -0
- data_engine/hosts/daemon/__init__.py +23 -0
- data_engine/hosts/daemon/app.py +221 -0
- data_engine/hosts/daemon/bootstrap.py +69 -0
- data_engine/hosts/daemon/client.py +465 -0
- data_engine/hosts/daemon/commands.py +64 -0
- data_engine/hosts/daemon/composition.py +310 -0
- data_engine/hosts/daemon/constants.py +15 -0
- data_engine/hosts/daemon/entrypoints.py +97 -0
- data_engine/hosts/daemon/lifecycle.py +191 -0
- data_engine/hosts/daemon/manager.py +272 -0
- data_engine/hosts/daemon/ownership.py +126 -0
- data_engine/hosts/daemon/runtime_commands.py +188 -0
- data_engine/hosts/daemon/runtime_control.py +31 -0
- data_engine/hosts/daemon/server.py +84 -0
- data_engine/hosts/daemon/shared_state.py +147 -0
- data_engine/hosts/daemon/state_sync.py +101 -0
- data_engine/platform/__init__.py +1 -0
- data_engine/platform/identity.py +35 -0
- data_engine/platform/local_settings.py +146 -0
- data_engine/platform/theme.py +259 -0
- data_engine/platform/workspace_models.py +190 -0
- data_engine/platform/workspace_policy.py +333 -0
- data_engine/runtime/__init__.py +1 -0
- data_engine/runtime/file_watch.py +185 -0
- data_engine/runtime/ledger_models.py +116 -0
- data_engine/runtime/runtime_db.py +938 -0
- data_engine/runtime/shared_state.py +523 -0
- data_engine/services/__init__.py +49 -0
- data_engine/services/daemon.py +64 -0
- data_engine/services/daemon_state.py +40 -0
- data_engine/services/flow_catalog.py +102 -0
- data_engine/services/flow_execution.py +48 -0
- data_engine/services/ledger.py +85 -0
- data_engine/services/logs.py +65 -0
- data_engine/services/runtime_binding.py +105 -0
- data_engine/services/runtime_execution.py +126 -0
- data_engine/services/runtime_history.py +62 -0
- data_engine/services/settings.py +58 -0
- data_engine/services/shared_state.py +28 -0
- data_engine/services/theme.py +59 -0
- data_engine/services/workspace_provisioning.py +224 -0
- data_engine/services/workspaces.py +74 -0
- data_engine/ui/__init__.py +3 -0
- data_engine/ui/cli/__init__.py +19 -0
- data_engine/ui/cli/app.py +161 -0
- data_engine/ui/cli/commands_doctor.py +178 -0
- data_engine/ui/cli/commands_run.py +80 -0
- data_engine/ui/cli/commands_start.py +100 -0
- data_engine/ui/cli/commands_workspace.py +97 -0
- data_engine/ui/cli/dependencies.py +44 -0
- data_engine/ui/cli/parser.py +56 -0
- data_engine/ui/gui/__init__.py +25 -0
- data_engine/ui/gui/app.py +116 -0
- data_engine/ui/gui/bootstrap.py +487 -0
- data_engine/ui/gui/bootstrapper.py +140 -0
- data_engine/ui/gui/cache_models.py +23 -0
- data_engine/ui/gui/control_support.py +185 -0
- data_engine/ui/gui/controllers/__init__.py +6 -0
- data_engine/ui/gui/controllers/flows.py +439 -0
- data_engine/ui/gui/controllers/runtime.py +245 -0
- data_engine/ui/gui/dialogs/__init__.py +12 -0
- data_engine/ui/gui/dialogs/messages.py +88 -0
- data_engine/ui/gui/dialogs/previews.py +222 -0
- data_engine/ui/gui/helpers/__init__.py +62 -0
- data_engine/ui/gui/helpers/inspection.py +81 -0
- data_engine/ui/gui/helpers/lifecycle.py +112 -0
- data_engine/ui/gui/helpers/scroll.py +28 -0
- data_engine/ui/gui/helpers/theming.py +87 -0
- data_engine/ui/gui/icons/dark_light.svg +12 -0
- data_engine/ui/gui/icons/documentation.svg +1 -0
- data_engine/ui/gui/icons/failed.svg +3 -0
- data_engine/ui/gui/icons/group.svg +4 -0
- data_engine/ui/gui/icons/home.svg +2 -0
- data_engine/ui/gui/icons/manual.svg +2 -0
- data_engine/ui/gui/icons/poll.svg +2 -0
- data_engine/ui/gui/icons/schedule.svg +4 -0
- data_engine/ui/gui/icons/settings.svg +2 -0
- data_engine/ui/gui/icons/started.svg +3 -0
- data_engine/ui/gui/icons/success.svg +3 -0
- data_engine/ui/gui/icons/view-log.svg +3 -0
- data_engine/ui/gui/icons.py +50 -0
- data_engine/ui/gui/launcher.py +48 -0
- data_engine/ui/gui/presenters/__init__.py +72 -0
- data_engine/ui/gui/presenters/docs.py +140 -0
- data_engine/ui/gui/presenters/logs.py +58 -0
- data_engine/ui/gui/presenters/runtime_projection.py +29 -0
- data_engine/ui/gui/presenters/sidebar.py +88 -0
- data_engine/ui/gui/presenters/steps.py +148 -0
- data_engine/ui/gui/presenters/workspace.py +39 -0
- data_engine/ui/gui/presenters/workspace_binding.py +75 -0
- data_engine/ui/gui/presenters/workspace_settings.py +182 -0
- data_engine/ui/gui/preview_models.py +37 -0
- data_engine/ui/gui/render_support.py +241 -0
- data_engine/ui/gui/rendering/__init__.py +12 -0
- data_engine/ui/gui/rendering/artifacts.py +95 -0
- data_engine/ui/gui/rendering/icons.py +50 -0
- data_engine/ui/gui/runtime.py +47 -0
- data_engine/ui/gui/state_support.py +193 -0
- data_engine/ui/gui/support.py +214 -0
- data_engine/ui/gui/surface.py +209 -0
- data_engine/ui/gui/theme.py +720 -0
- data_engine/ui/gui/widgets/__init__.py +34 -0
- data_engine/ui/gui/widgets/config.py +41 -0
- data_engine/ui/gui/widgets/logs.py +62 -0
- data_engine/ui/gui/widgets/panels.py +507 -0
- data_engine/ui/gui/widgets/sidebar.py +130 -0
- data_engine/ui/gui/widgets/steps.py +84 -0
- data_engine/ui/tui/__init__.py +5 -0
- data_engine/ui/tui/app.py +222 -0
- data_engine/ui/tui/bootstrap.py +475 -0
- data_engine/ui/tui/bootstrapper.py +117 -0
- data_engine/ui/tui/controllers/__init__.py +6 -0
- data_engine/ui/tui/controllers/flows.py +349 -0
- data_engine/ui/tui/controllers/runtime.py +167 -0
- data_engine/ui/tui/runtime.py +34 -0
- data_engine/ui/tui/state_support.py +141 -0
- data_engine/ui/tui/support.py +63 -0
- data_engine/ui/tui/theme.py +204 -0
- data_engine/ui/tui/widgets.py +123 -0
- data_engine/views/__init__.py +109 -0
- data_engine/views/actions.py +80 -0
- data_engine/views/artifacts.py +58 -0
- data_engine/views/flow_display.py +69 -0
- data_engine/views/logs.py +54 -0
- data_engine/views/models.py +96 -0
- data_engine/views/presentation.py +133 -0
- data_engine/views/runs.py +62 -0
- data_engine/views/state.py +39 -0
- data_engine/views/status.py +13 -0
- data_engine/views/text.py +109 -0
- py_data_engine-0.1.0.dist-info/METADATA +330 -0
- py_data_engine-0.1.0.dist-info/RECORD +200 -0
- py_data_engine-0.1.0.dist-info/WHEEL +5 -0
- py_data_engine-0.1.0.dist-info/entry_points.txt +2 -0
- py_data_engine-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Shared theme resolution services."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Mapping
|
|
6
|
+
|
|
7
|
+
from data_engine.platform.theme import (
|
|
8
|
+
DEFAULT_THEME,
|
|
9
|
+
THEMES,
|
|
10
|
+
ThemePalette,
|
|
11
|
+
resolve_theme_name,
|
|
12
|
+
system_theme_name,
|
|
13
|
+
theme_button_text,
|
|
14
|
+
toggle_theme_name,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ThemeService:
|
|
19
|
+
"""Thin injectable wrapper around shared theme state decisions."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
*,
|
|
24
|
+
themes: Mapping[str, ThemePalette] = THEMES,
|
|
25
|
+
default_theme_name: str = DEFAULT_THEME,
|
|
26
|
+
resolve_theme_name_func: Callable[[str], str] = resolve_theme_name,
|
|
27
|
+
system_theme_name_func: Callable[[], str] = system_theme_name,
|
|
28
|
+
toggle_theme_name_func: Callable[[str], str] = toggle_theme_name,
|
|
29
|
+
theme_button_text_func: Callable[[str], str] = theme_button_text,
|
|
30
|
+
) -> None:
|
|
31
|
+
self._themes = themes
|
|
32
|
+
self.default_theme_name = default_theme_name
|
|
33
|
+
self._resolve_theme_name = resolve_theme_name_func
|
|
34
|
+
self._system_theme_name = system_theme_name_func
|
|
35
|
+
self._toggle_theme_name = toggle_theme_name_func
|
|
36
|
+
self._theme_button_text = theme_button_text_func
|
|
37
|
+
|
|
38
|
+
def resolve_name(self, theme_name: str = DEFAULT_THEME) -> str:
|
|
39
|
+
"""Resolve one explicit or system-bound theme name."""
|
|
40
|
+
return self._resolve_theme_name(theme_name)
|
|
41
|
+
|
|
42
|
+
def system_name(self) -> str:
|
|
43
|
+
"""Return the host-system theme name."""
|
|
44
|
+
return self._system_theme_name()
|
|
45
|
+
|
|
46
|
+
def toggle_name(self, theme_name: str) -> str:
|
|
47
|
+
"""Return the opposite theme name."""
|
|
48
|
+
return self._toggle_theme_name(theme_name)
|
|
49
|
+
|
|
50
|
+
def button_text(self, theme_name: str) -> str:
|
|
51
|
+
"""Return the user-facing theme toggle text."""
|
|
52
|
+
return self._theme_button_text(theme_name)
|
|
53
|
+
|
|
54
|
+
def palette(self, theme_name: str = DEFAULT_THEME) -> ThemePalette:
|
|
55
|
+
"""Return the resolved semantic palette."""
|
|
56
|
+
return self._themes[self.resolve_name(theme_name)]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
__all__ = ["ThemeService"]
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Workspace provisioning helpers shared by CLI and GUI surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from data_engine.platform.workspace_models import (
|
|
10
|
+
DATA_ENGINE_WORKSPACE_COLLECTION_ROOT_ENV_VAR,
|
|
11
|
+
WORKSPACE_FLOW_HELPERS_DIR_NAME,
|
|
12
|
+
WorkspacePaths,
|
|
13
|
+
validate_workspace_id,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def checkout_source_dir(app_root: Path) -> Path | None:
|
|
18
|
+
"""Return the repo-local source directory when app_root points at a checkout."""
|
|
19
|
+
src_dir = app_root / "src"
|
|
20
|
+
return src_dir if src_dir.is_dir() else None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def checkout_tests_dir(app_root: Path) -> Path | None:
|
|
24
|
+
"""Return the repo-local tests directory when app_root points at a checkout."""
|
|
25
|
+
tests_dir = app_root / "tests"
|
|
26
|
+
return tests_dir if tests_dir.is_dir() else None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def workspace_vscode_settings(
|
|
30
|
+
workspace_root: Path,
|
|
31
|
+
*,
|
|
32
|
+
app_root: Path,
|
|
33
|
+
interpreter_path: Path | None = None,
|
|
34
|
+
) -> dict[str, object]:
|
|
35
|
+
"""Return VS Code settings for one workspace root."""
|
|
36
|
+
del interpreter_path
|
|
37
|
+
workspace_id = validate_workspace_id(workspace_root.name)
|
|
38
|
+
terminal_env = {
|
|
39
|
+
"DATA_ENGINE_APP_ROOT": str(app_root),
|
|
40
|
+
"DATA_ENGINE_WORKSPACE_ROOT": str(workspace_root),
|
|
41
|
+
"DATA_ENGINE_WORKSPACE_ID": workspace_id,
|
|
42
|
+
}
|
|
43
|
+
settings: dict[str, object] = {
|
|
44
|
+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv",
|
|
45
|
+
"files.exclude": {".workspace_state": True},
|
|
46
|
+
"search.exclude": {".workspace_state": True},
|
|
47
|
+
"terminal.integrated.env.linux": terminal_env,
|
|
48
|
+
"terminal.integrated.env.osx": terminal_env,
|
|
49
|
+
"terminal.integrated.env.windows": terminal_env,
|
|
50
|
+
}
|
|
51
|
+
src_dir = checkout_source_dir(app_root)
|
|
52
|
+
if src_dir is not None:
|
|
53
|
+
settings["python.analysis.extraPaths"] = [str(src_dir)]
|
|
54
|
+
tests_dir = checkout_tests_dir(app_root)
|
|
55
|
+
if tests_dir is not None:
|
|
56
|
+
settings["python.testing.pytestEnabled"] = True
|
|
57
|
+
settings["python.testing.pytestArgs"] = [str(tests_dir)]
|
|
58
|
+
return settings
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def collection_vscode_settings(
|
|
62
|
+
collection_root: Path,
|
|
63
|
+
*,
|
|
64
|
+
app_root: Path,
|
|
65
|
+
interpreter_path: Path | None = None,
|
|
66
|
+
) -> dict[str, object]:
|
|
67
|
+
"""Return VS Code settings for one workspace collection root."""
|
|
68
|
+
del interpreter_path
|
|
69
|
+
terminal_env = {
|
|
70
|
+
"DATA_ENGINE_APP_ROOT": str(app_root),
|
|
71
|
+
DATA_ENGINE_WORKSPACE_COLLECTION_ROOT_ENV_VAR: str(collection_root),
|
|
72
|
+
}
|
|
73
|
+
settings: dict[str, object] = {
|
|
74
|
+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv",
|
|
75
|
+
"files.exclude": {"**/.workspace_state": True},
|
|
76
|
+
"search.exclude": {"**/.workspace_state": True},
|
|
77
|
+
"terminal.integrated.env.linux": terminal_env,
|
|
78
|
+
"terminal.integrated.env.osx": terminal_env,
|
|
79
|
+
"terminal.integrated.env.windows": terminal_env,
|
|
80
|
+
}
|
|
81
|
+
src_dir = checkout_source_dir(app_root)
|
|
82
|
+
if src_dir is not None:
|
|
83
|
+
settings["python.analysis.extraPaths"] = [str(src_dir)]
|
|
84
|
+
tests_dir = checkout_tests_dir(app_root)
|
|
85
|
+
if tests_dir is not None:
|
|
86
|
+
settings["python.testing.pytestEnabled"] = True
|
|
87
|
+
settings["python.testing.pytestArgs"] = [str(tests_dir)]
|
|
88
|
+
return settings
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def write_workspace_vscode_settings(
|
|
92
|
+
workspace_root: Path,
|
|
93
|
+
*,
|
|
94
|
+
app_root: Path,
|
|
95
|
+
interpreter_path: Path | None = None,
|
|
96
|
+
overwrite: bool = False,
|
|
97
|
+
) -> Path | None:
|
|
98
|
+
"""Write workspace-local VS Code settings unless an existing file should be preserved."""
|
|
99
|
+
settings_path = workspace_root / ".vscode" / "settings.json"
|
|
100
|
+
if settings_path.exists() and not overwrite:
|
|
101
|
+
return None
|
|
102
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
103
|
+
settings_path.write_text(
|
|
104
|
+
json.dumps(
|
|
105
|
+
workspace_vscode_settings(
|
|
106
|
+
workspace_root,
|
|
107
|
+
app_root=app_root,
|
|
108
|
+
interpreter_path=interpreter_path,
|
|
109
|
+
),
|
|
110
|
+
indent=2,
|
|
111
|
+
)
|
|
112
|
+
+ "\n",
|
|
113
|
+
encoding="utf-8",
|
|
114
|
+
)
|
|
115
|
+
return settings_path
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def write_collection_vscode_settings(
|
|
119
|
+
collection_root: Path,
|
|
120
|
+
*,
|
|
121
|
+
app_root: Path,
|
|
122
|
+
interpreter_path: Path | None = None,
|
|
123
|
+
overwrite: bool = False,
|
|
124
|
+
) -> Path | None:
|
|
125
|
+
"""Write collection-root VS Code settings unless an existing file should be preserved."""
|
|
126
|
+
settings_path = collection_root / ".vscode" / "settings.json"
|
|
127
|
+
if settings_path.exists() and not overwrite:
|
|
128
|
+
return None
|
|
129
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
settings_path.write_text(
|
|
131
|
+
json.dumps(
|
|
132
|
+
collection_vscode_settings(
|
|
133
|
+
collection_root,
|
|
134
|
+
app_root=app_root,
|
|
135
|
+
interpreter_path=interpreter_path,
|
|
136
|
+
),
|
|
137
|
+
indent=2,
|
|
138
|
+
)
|
|
139
|
+
+ "\n",
|
|
140
|
+
encoding="utf-8",
|
|
141
|
+
)
|
|
142
|
+
return settings_path
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True)
|
|
146
|
+
class WorkspaceProvisioningResult:
|
|
147
|
+
"""Describe which workspace assets were created during provisioning."""
|
|
148
|
+
|
|
149
|
+
workspace_root: Path
|
|
150
|
+
created_paths: tuple[Path, ...]
|
|
151
|
+
preserved_paths: tuple[Path, ...]
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def created_anything(self) -> bool:
|
|
155
|
+
"""Return whether provisioning created any new files or directories."""
|
|
156
|
+
return bool(self.created_paths)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class WorkspaceProvisioningService:
|
|
160
|
+
"""Own safe workspace-folder provisioning for operator surfaces."""
|
|
161
|
+
|
|
162
|
+
def provision_workspace(
|
|
163
|
+
self,
|
|
164
|
+
workspace_paths: WorkspacePaths,
|
|
165
|
+
*,
|
|
166
|
+
interpreter_path: Path | None = None,
|
|
167
|
+
) -> WorkspaceProvisioningResult:
|
|
168
|
+
"""Provision missing authored-workspace folders without overwriting existing content."""
|
|
169
|
+
created_paths: list[Path] = []
|
|
170
|
+
preserved_paths: list[Path] = []
|
|
171
|
+
workspace_root = workspace_paths.workspace_root
|
|
172
|
+
if workspace_root.exists() and not workspace_root.is_dir():
|
|
173
|
+
raise ValueError(f"Workspace path is not a directory: {workspace_root}")
|
|
174
|
+
if not workspace_root.exists():
|
|
175
|
+
workspace_root.mkdir(parents=True, exist_ok=True)
|
|
176
|
+
created_paths.append(workspace_root)
|
|
177
|
+
collection_settings_path = write_collection_vscode_settings(
|
|
178
|
+
workspace_paths.workspace_collection_root,
|
|
179
|
+
app_root=workspace_paths.app_root,
|
|
180
|
+
interpreter_path=interpreter_path,
|
|
181
|
+
overwrite=False,
|
|
182
|
+
)
|
|
183
|
+
if collection_settings_path is None:
|
|
184
|
+
preserved_paths.append(workspace_paths.workspace_collection_root / ".vscode" / "settings.json")
|
|
185
|
+
else:
|
|
186
|
+
created_paths.append(collection_settings_path)
|
|
187
|
+
for directory in (
|
|
188
|
+
workspace_paths.flow_modules_dir,
|
|
189
|
+
workspace_paths.flow_modules_dir / WORKSPACE_FLOW_HELPERS_DIR_NAME,
|
|
190
|
+
workspace_paths.config_dir,
|
|
191
|
+
workspace_paths.databases_dir,
|
|
192
|
+
):
|
|
193
|
+
if directory.exists():
|
|
194
|
+
preserved_paths.append(directory)
|
|
195
|
+
continue
|
|
196
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
197
|
+
created_paths.append(directory)
|
|
198
|
+
settings_path = write_workspace_vscode_settings(
|
|
199
|
+
workspace_root,
|
|
200
|
+
app_root=workspace_paths.app_root,
|
|
201
|
+
interpreter_path=interpreter_path,
|
|
202
|
+
overwrite=False,
|
|
203
|
+
)
|
|
204
|
+
if settings_path is None:
|
|
205
|
+
preserved_paths.append(workspace_root / ".vscode" / "settings.json")
|
|
206
|
+
else:
|
|
207
|
+
created_paths.append(settings_path)
|
|
208
|
+
return WorkspaceProvisioningResult(
|
|
209
|
+
workspace_root=workspace_root,
|
|
210
|
+
created_paths=tuple(created_paths),
|
|
211
|
+
preserved_paths=tuple(preserved_paths),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
__all__ = [
|
|
216
|
+
"WorkspaceProvisioningResult",
|
|
217
|
+
"WorkspaceProvisioningService",
|
|
218
|
+
"collection_vscode_settings",
|
|
219
|
+
"checkout_source_dir",
|
|
220
|
+
"checkout_tests_dir",
|
|
221
|
+
"write_collection_vscode_settings",
|
|
222
|
+
"workspace_vscode_settings",
|
|
223
|
+
"write_workspace_vscode_settings",
|
|
224
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Workspace path and discovery services."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from data_engine.platform.workspace_models import DiscoveredWorkspace, WorkspacePaths
|
|
9
|
+
from data_engine.platform.workspace_policy import AppStatePolicy, RuntimeLayoutPolicy, WorkspaceDiscoveryPolicy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WorkspaceService:
|
|
13
|
+
"""Own workspace discovery and path resolution through explicit collaborators."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
*,
|
|
18
|
+
app_state_policy: AppStatePolicy | None = None,
|
|
19
|
+
discovery_policy: WorkspaceDiscoveryPolicy | None = None,
|
|
20
|
+
runtime_layout_policy: RuntimeLayoutPolicy | None = None,
|
|
21
|
+
discover_workspaces_func: Callable[..., tuple[DiscoveredWorkspace, ...]] | None = None,
|
|
22
|
+
resolve_workspace_paths_func: Callable[..., WorkspacePaths] | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
self._app_state_policy = app_state_policy or AppStatePolicy()
|
|
25
|
+
self._discovery_policy = discovery_policy or WorkspaceDiscoveryPolicy(app_state_policy=self._app_state_policy)
|
|
26
|
+
self._runtime_layout_policy = runtime_layout_policy or RuntimeLayoutPolicy(
|
|
27
|
+
app_state_policy=self._app_state_policy,
|
|
28
|
+
discovery_policy=self._discovery_policy,
|
|
29
|
+
)
|
|
30
|
+
self._discover_workspaces = discover_workspaces_func
|
|
31
|
+
self._resolve_workspace_paths = resolve_workspace_paths_func
|
|
32
|
+
|
|
33
|
+
def discover(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
app_root: Path | None = None,
|
|
37
|
+
workspace_collection_root: Path | None = None,
|
|
38
|
+
) -> tuple[DiscoveredWorkspace, ...]:
|
|
39
|
+
"""Return discoverable workspaces for the current app and collection roots."""
|
|
40
|
+
if self._discover_workspaces is None:
|
|
41
|
+
return self._discovery_policy.discover(
|
|
42
|
+
app_root=app_root,
|
|
43
|
+
workspace_collection_root=workspace_collection_root,
|
|
44
|
+
)
|
|
45
|
+
return self._discover_workspaces(
|
|
46
|
+
app_root=app_root,
|
|
47
|
+
workspace_collection_root=workspace_collection_root,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def resolve_paths(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
workspace_id: str | None = None,
|
|
54
|
+
workspace_root: Path | None = None,
|
|
55
|
+
data_root: Path | None = None,
|
|
56
|
+
workspace_collection_root: Path | None = None,
|
|
57
|
+
) -> WorkspacePaths:
|
|
58
|
+
"""Resolve one workspace path set with the current override-aware rules."""
|
|
59
|
+
if self._resolve_workspace_paths is None:
|
|
60
|
+
return self._runtime_layout_policy.resolve_paths(
|
|
61
|
+
workspace_id=workspace_id,
|
|
62
|
+
workspace_root=workspace_root,
|
|
63
|
+
data_root=data_root,
|
|
64
|
+
workspace_collection_root=workspace_collection_root,
|
|
65
|
+
)
|
|
66
|
+
return self._resolve_workspace_paths(
|
|
67
|
+
workspace_id=workspace_id,
|
|
68
|
+
workspace_root=workspace_root,
|
|
69
|
+
data_root=data_root,
|
|
70
|
+
workspace_collection_root=workspace_collection_root,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__all__ = ["WorkspaceService"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""CLI surface package."""
|
|
2
|
+
|
|
3
|
+
from data_engine.ui.cli.app import (
|
|
4
|
+
CliDependencies,
|
|
5
|
+
CliDependencyFactories,
|
|
6
|
+
build_default_cli_dependencies,
|
|
7
|
+
build_parser,
|
|
8
|
+
default_cli_dependency_factories,
|
|
9
|
+
main,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"CliDependencies",
|
|
14
|
+
"CliDependencyFactories",
|
|
15
|
+
"build_default_cli_dependencies",
|
|
16
|
+
"build_parser",
|
|
17
|
+
"default_cli_dependency_factories",
|
|
18
|
+
"main",
|
|
19
|
+
]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Operator-focused CLI surface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from data_engine.authoring.model import FlowValidationError
|
|
12
|
+
from data_engine.platform.workspace_models import (
|
|
13
|
+
DATA_ENGINE_APP_ROOT_ENV_VAR,
|
|
14
|
+
DATA_ENGINE_WORKSPACE_ROOT_ENV_VAR,
|
|
15
|
+
InvalidWorkspaceIdError,
|
|
16
|
+
machine_id_text,
|
|
17
|
+
)
|
|
18
|
+
from data_engine.ui.cli import commands_doctor as _commands_doctor
|
|
19
|
+
from data_engine.ui.cli import commands_run as _commands_run
|
|
20
|
+
from data_engine.ui.cli.commands_start import (
|
|
21
|
+
launch_desktop_ui as _launch_desktop_ui,
|
|
22
|
+
launch_terminal_ui as _launch_terminal_ui,
|
|
23
|
+
preferred_gui_python_executable as _preferred_gui_python_executable,
|
|
24
|
+
start_gui_subprocess as _start_gui_subprocess,
|
|
25
|
+
start_surface as _start_surface,
|
|
26
|
+
)
|
|
27
|
+
from data_engine.ui.cli.commands_workspace import (
|
|
28
|
+
create_command as _create_command,
|
|
29
|
+
workspace_vscode_settings as _workspace_vscode_settings,
|
|
30
|
+
)
|
|
31
|
+
from data_engine.ui.cli.dependencies import (
|
|
32
|
+
CliDependencies,
|
|
33
|
+
CliDependencyFactories,
|
|
34
|
+
build_default_cli_dependencies,
|
|
35
|
+
default_cli_dependency_factories,
|
|
36
|
+
)
|
|
37
|
+
from data_engine.ui.cli.parser import _HelpFormatter, build_parser
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _default_cli_dependencies() -> CliDependencies:
|
|
41
|
+
return build_default_cli_dependencies()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main(argv: list[str] | None = None, *, dependencies: CliDependencies | None = None) -> int:
|
|
45
|
+
parser = build_parser()
|
|
46
|
+
args = parser.parse_args(argv)
|
|
47
|
+
dependencies = dependencies or _default_cli_dependencies()
|
|
48
|
+
_apply_environment(args)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
if args.command == "start":
|
|
52
|
+
return _start_surface(args.start_command)
|
|
53
|
+
if args.command == "create":
|
|
54
|
+
return _create_command(args, dependencies=dependencies)
|
|
55
|
+
if args.command == "run":
|
|
56
|
+
return _run_command(args, dependencies=dependencies)
|
|
57
|
+
if args.command == "doctor":
|
|
58
|
+
return _doctor_command(args, dependencies=dependencies)
|
|
59
|
+
except (FlowValidationError, InvalidWorkspaceIdError) as exc:
|
|
60
|
+
print(str(exc), file=sys.stderr)
|
|
61
|
+
return 2
|
|
62
|
+
except KeyboardInterrupt:
|
|
63
|
+
print("Stopped.", file=sys.stderr)
|
|
64
|
+
return 130
|
|
65
|
+
parser.error(f"Unknown command: {args.command}")
|
|
66
|
+
return 2
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _apply_environment(args: argparse.Namespace) -> None:
|
|
70
|
+
if args.app_root is not None:
|
|
71
|
+
os.environ[DATA_ENGINE_APP_ROOT_ENV_VAR] = str(args.app_root.expanduser().resolve())
|
|
72
|
+
else:
|
|
73
|
+
inferred = _infer_project_root_from_cwd(Path.cwd())
|
|
74
|
+
if inferred is not None:
|
|
75
|
+
os.environ.setdefault(DATA_ENGINE_APP_ROOT_ENV_VAR, str(inferred))
|
|
76
|
+
if args.workspace is not None:
|
|
77
|
+
os.environ[DATA_ENGINE_WORKSPACE_ROOT_ENV_VAR] = str(args.workspace.expanduser().resolve())
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _infer_project_root_from_cwd(cwd: Path) -> Path | None:
|
|
81
|
+
candidate = cwd.expanduser().resolve()
|
|
82
|
+
if (candidate / "pyproject.toml").is_file() and (candidate / "src" / "data_engine").is_dir():
|
|
83
|
+
return candidate
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _run_command(args: argparse.Namespace, *, dependencies: CliDependencies) -> int:
|
|
88
|
+
if args.run_command == "tests":
|
|
89
|
+
return _run_tests(slice_name=args.slice, list_slices=args.list_slices, dependencies=dependencies)
|
|
90
|
+
raise FlowValidationError(f"Unknown run command: {args.run_command}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _test_slice_args(slice_name: str, *, app_root: Path) -> tuple[str, ...]:
|
|
94
|
+
return _commands_run.test_slice_args(slice_name, app_root=app_root)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _run_tests(*, slice_name: str, list_slices: bool, dependencies: CliDependencies) -> int:
|
|
98
|
+
app_root = dependencies.app_state_policy.load_settings().app_root
|
|
99
|
+
return _commands_run.run_tests(slice_name=slice_name, list_slices=list_slices, app_root=app_root)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _doctor(*, dependencies: CliDependencies) -> int:
|
|
103
|
+
settings = dependencies.app_state_policy.load_settings()
|
|
104
|
+
paths = dependencies.workspace_service.resolve_paths()
|
|
105
|
+
return _commands_doctor.doctor(settings=settings, paths=paths)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _doctor_command(args: argparse.Namespace, *, dependencies: CliDependencies) -> int:
|
|
109
|
+
if getattr(args, "doctor_command", None) == "daemons":
|
|
110
|
+
return _doctor_daemons(dependencies=dependencies)
|
|
111
|
+
return _doctor(dependencies=dependencies)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _run_process_listing():
|
|
115
|
+
return _commands_doctor.run_process_listing()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _classify_process_kind(command: str) -> str | None:
|
|
119
|
+
return _commands_doctor.classify_process_kind(command)
|
|
120
|
+
|
|
121
|
+
def _doctor_daemons(*, dependencies: CliDependencies) -> int:
|
|
122
|
+
settings = dependencies.app_state_policy.load_settings()
|
|
123
|
+
return _commands_doctor.doctor_daemons(
|
|
124
|
+
settings=settings,
|
|
125
|
+
workspace_service=dependencies.workspace_service,
|
|
126
|
+
process_listing_func=_run_process_listing,
|
|
127
|
+
classify_process_kind_func=_classify_process_kind,
|
|
128
|
+
read_lease_metadata_func=dependencies.shared_state_service.read_lease_metadata,
|
|
129
|
+
lease_is_stale_func=lambda paths, stale_after_seconds: dependencies.shared_state_service.lease_is_stale(
|
|
130
|
+
paths,
|
|
131
|
+
stale_after_seconds=stale_after_seconds,
|
|
132
|
+
),
|
|
133
|
+
machine_id_text_func=machine_id_text,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = [
|
|
138
|
+
"CliDependencies",
|
|
139
|
+
"CliDependencyFactories",
|
|
140
|
+
"_HelpFormatter",
|
|
141
|
+
"_apply_environment",
|
|
142
|
+
"_classify_process_kind",
|
|
143
|
+
"_doctor",
|
|
144
|
+
"_doctor_command",
|
|
145
|
+
"_doctor_daemons",
|
|
146
|
+
"_infer_project_root_from_cwd",
|
|
147
|
+
"_launch_desktop_ui",
|
|
148
|
+
"_launch_terminal_ui",
|
|
149
|
+
"_preferred_gui_python_executable",
|
|
150
|
+
"_run_command",
|
|
151
|
+
"_run_process_listing",
|
|
152
|
+
"_run_tests",
|
|
153
|
+
"_start_gui_subprocess",
|
|
154
|
+
"_start_surface",
|
|
155
|
+
"_test_slice_args",
|
|
156
|
+
"_workspace_vscode_settings",
|
|
157
|
+
"build_default_cli_dependencies",
|
|
158
|
+
"build_parser",
|
|
159
|
+
"default_cli_dependency_factories",
|
|
160
|
+
"main",
|
|
161
|
+
]
|