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,259 @@
|
|
|
1
|
+
"""Shared theme tokens and host-theme detection across Data Engine surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import subprocess
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class ThemePalette:
|
|
13
|
+
"""One complete application palette."""
|
|
14
|
+
|
|
15
|
+
name: str
|
|
16
|
+
window_bg: str
|
|
17
|
+
app_bg: str
|
|
18
|
+
panel_bg: str
|
|
19
|
+
panel_border: str
|
|
20
|
+
text: str
|
|
21
|
+
muted_text: str
|
|
22
|
+
section_text: str
|
|
23
|
+
accent_text: str
|
|
24
|
+
warning_text: str
|
|
25
|
+
error_text: str
|
|
26
|
+
button_bg: str
|
|
27
|
+
button_hover: str
|
|
28
|
+
button_checked_bg: str
|
|
29
|
+
button_checked_border: str
|
|
30
|
+
button_disabled_bg: str
|
|
31
|
+
button_disabled_border: str
|
|
32
|
+
button_disabled_text: str
|
|
33
|
+
input_bg: str
|
|
34
|
+
input_border: str
|
|
35
|
+
hover_bg: str
|
|
36
|
+
hover_border: str
|
|
37
|
+
selection_bg: str
|
|
38
|
+
selection_text: str
|
|
39
|
+
selection_border: str
|
|
40
|
+
tab_bg: str
|
|
41
|
+
tab_hover_bg: str
|
|
42
|
+
tab_selected_bg: str
|
|
43
|
+
progress_bg: str
|
|
44
|
+
progress_chunk: str
|
|
45
|
+
summary_bg: str
|
|
46
|
+
summary_border: str
|
|
47
|
+
request_control_bg: str
|
|
48
|
+
request_control_border: str
|
|
49
|
+
request_control_hover: str
|
|
50
|
+
engine_start_bg: str
|
|
51
|
+
engine_start_border: str
|
|
52
|
+
engine_start_hover: str
|
|
53
|
+
engine_stop_bg: str
|
|
54
|
+
engine_stop_border: str
|
|
55
|
+
engine_stop_hover: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
GITHUB_DARK = ThemePalette(
|
|
59
|
+
name="dark",
|
|
60
|
+
window_bg="#0d1117",
|
|
61
|
+
app_bg="#0d1117",
|
|
62
|
+
panel_bg="#161b22",
|
|
63
|
+
panel_border="#30363d",
|
|
64
|
+
text="#c9d1d9",
|
|
65
|
+
muted_text="#8b949e",
|
|
66
|
+
section_text="#7d8590",
|
|
67
|
+
accent_text="#2ea043",
|
|
68
|
+
warning_text="#d29922",
|
|
69
|
+
error_text="#f85149",
|
|
70
|
+
button_bg="#21262d",
|
|
71
|
+
button_hover="#30363d",
|
|
72
|
+
button_checked_bg="#1f6feb",
|
|
73
|
+
button_checked_border="#388bfd",
|
|
74
|
+
button_disabled_bg="#161b22",
|
|
75
|
+
button_disabled_border="#30363d",
|
|
76
|
+
button_disabled_text="#6e7681",
|
|
77
|
+
input_bg="#0d1117",
|
|
78
|
+
input_border="#30363d",
|
|
79
|
+
hover_bg="#1b2230",
|
|
80
|
+
hover_border="#3b4556",
|
|
81
|
+
selection_bg="#1f6feb",
|
|
82
|
+
selection_text="#f0f6fc",
|
|
83
|
+
selection_border="#388bfd",
|
|
84
|
+
tab_bg="#161b22",
|
|
85
|
+
tab_hover_bg="#1b2230",
|
|
86
|
+
tab_selected_bg="#21262d",
|
|
87
|
+
progress_bg="#0d1117",
|
|
88
|
+
progress_chunk="#2ea043",
|
|
89
|
+
summary_bg="#21262d",
|
|
90
|
+
summary_border="#30363d",
|
|
91
|
+
request_control_bg="#F04A00",
|
|
92
|
+
request_control_border="#c23c00",
|
|
93
|
+
request_control_hover="#d84300",
|
|
94
|
+
engine_start_bg="#1f883d",
|
|
95
|
+
engine_start_border="#1a7f37",
|
|
96
|
+
engine_start_hover="#1a7f37",
|
|
97
|
+
engine_stop_bg="#cf222e",
|
|
98
|
+
engine_stop_border="#a40e26",
|
|
99
|
+
engine_stop_hover="#a40e26",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
GITHUB_LIGHT = ThemePalette(
|
|
103
|
+
name="light",
|
|
104
|
+
window_bg="#ffffff",
|
|
105
|
+
app_bg="#f6f8fa",
|
|
106
|
+
panel_bg="#ffffff",
|
|
107
|
+
panel_border="#d0d7de",
|
|
108
|
+
text="#1f2328",
|
|
109
|
+
muted_text="#656d76",
|
|
110
|
+
section_text="#57606a",
|
|
111
|
+
accent_text="#1a7f37",
|
|
112
|
+
warning_text="#9a6700",
|
|
113
|
+
error_text="#cf222e",
|
|
114
|
+
button_bg="#f6f8fa",
|
|
115
|
+
button_hover="#eef2f6",
|
|
116
|
+
button_checked_bg="#ddf4ff",
|
|
117
|
+
button_checked_border="#54aeff",
|
|
118
|
+
button_disabled_bg="#f6f8fa",
|
|
119
|
+
button_disabled_border="#d8dee4",
|
|
120
|
+
button_disabled_text="#8c959f",
|
|
121
|
+
input_bg="#ffffff",
|
|
122
|
+
input_border="#d0d7de",
|
|
123
|
+
hover_bg="#f6f8fa",
|
|
124
|
+
hover_border="#c7d2dd",
|
|
125
|
+
selection_bg="#0969da",
|
|
126
|
+
selection_text="#ffffff",
|
|
127
|
+
selection_border="#54aeff",
|
|
128
|
+
tab_bg="#f6f8fa",
|
|
129
|
+
tab_hover_bg="#eef2f6",
|
|
130
|
+
tab_selected_bg="#ffffff",
|
|
131
|
+
progress_bg="#eef2f6",
|
|
132
|
+
progress_chunk="#1a7f37",
|
|
133
|
+
summary_bg="#f6f8fa",
|
|
134
|
+
summary_border="#d0d7de",
|
|
135
|
+
request_control_bg="#F04A00",
|
|
136
|
+
request_control_border="#c23c00",
|
|
137
|
+
request_control_hover="#d84300",
|
|
138
|
+
engine_start_bg="#1f883d",
|
|
139
|
+
engine_start_border="#1a7f37",
|
|
140
|
+
engine_start_hover="#1a7f37",
|
|
141
|
+
engine_stop_bg="#cf222e",
|
|
142
|
+
engine_stop_border="#a40e26",
|
|
143
|
+
engine_stop_hover="#a40e26",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
THEMES = {
|
|
147
|
+
"dark": GITHUB_DARK,
|
|
148
|
+
"light": GITHUB_LIGHT,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
DEFAULT_THEME = "system"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def toggle_theme_name(theme_name: str) -> str:
|
|
155
|
+
"""Return the opposite theme name."""
|
|
156
|
+
return "light" if theme_name == "dark" else "dark"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def theme_button_text(theme_name: str) -> str:
|
|
160
|
+
"""Return the user-facing label for the theme toggle button."""
|
|
161
|
+
return "Switch to Light" if theme_name == "dark" else "Switch to Dark"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _qt_theme_name() -> str | None:
|
|
165
|
+
try:
|
|
166
|
+
from PySide6.QtCore import Qt # type: ignore
|
|
167
|
+
from PySide6.QtGui import QGuiApplication # type: ignore
|
|
168
|
+
except Exception:
|
|
169
|
+
return None
|
|
170
|
+
app = QGuiApplication.instance()
|
|
171
|
+
if app is None:
|
|
172
|
+
return None
|
|
173
|
+
try:
|
|
174
|
+
scheme = app.styleHints().colorScheme()
|
|
175
|
+
except AttributeError:
|
|
176
|
+
return None
|
|
177
|
+
return "dark" if scheme == Qt.ColorScheme.Dark else "light"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _macos_theme_name() -> str | None:
|
|
181
|
+
try:
|
|
182
|
+
result = subprocess.run(
|
|
183
|
+
["defaults", "read", "-g", "AppleInterfaceStyle"],
|
|
184
|
+
check=False,
|
|
185
|
+
capture_output=True,
|
|
186
|
+
text=True,
|
|
187
|
+
timeout=0.5,
|
|
188
|
+
)
|
|
189
|
+
except Exception:
|
|
190
|
+
return None
|
|
191
|
+
return "dark" if "dark" in (result.stdout or "").strip().lower() else "light"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _windows_theme_name() -> str | None:
|
|
195
|
+
try:
|
|
196
|
+
import winreg # type: ignore
|
|
197
|
+
except Exception:
|
|
198
|
+
return None
|
|
199
|
+
try:
|
|
200
|
+
with winreg.OpenKey(
|
|
201
|
+
winreg.HKEY_CURRENT_USER,
|
|
202
|
+
r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
|
|
203
|
+
) as key:
|
|
204
|
+
value, _ = winreg.QueryValueEx(key, "AppsUseLightTheme")
|
|
205
|
+
except Exception:
|
|
206
|
+
return None
|
|
207
|
+
return "light" if int(value) else "dark"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _linux_theme_name() -> str | None:
|
|
211
|
+
gtk_theme = os.environ.get("GTK_THEME", "").lower()
|
|
212
|
+
if ":dark" in gtk_theme or gtk_theme.endswith("-dark"):
|
|
213
|
+
return "dark"
|
|
214
|
+
if gtk_theme:
|
|
215
|
+
return "light"
|
|
216
|
+
colorfgbg = os.environ.get("COLORFGBG", "")
|
|
217
|
+
if colorfgbg:
|
|
218
|
+
try:
|
|
219
|
+
bg = int(colorfgbg.split(";")[-1])
|
|
220
|
+
except ValueError:
|
|
221
|
+
return None
|
|
222
|
+
return "dark" if bg <= 6 else "light"
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def system_theme_name() -> str:
|
|
227
|
+
"""Return the host light/dark theme using shared cross-surface detection."""
|
|
228
|
+
override = os.environ.get("DATA_ENGINE_THEME", "").strip().lower()
|
|
229
|
+
if override in THEMES:
|
|
230
|
+
return override
|
|
231
|
+
qt_theme = _qt_theme_name()
|
|
232
|
+
if qt_theme is not None:
|
|
233
|
+
return qt_theme
|
|
234
|
+
system = platform.system()
|
|
235
|
+
if system == "Darwin":
|
|
236
|
+
return _macos_theme_name() or "dark"
|
|
237
|
+
if system == "Windows":
|
|
238
|
+
return _windows_theme_name() or "dark"
|
|
239
|
+
return _linux_theme_name() or "dark"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def resolve_theme_name(theme_name: str = DEFAULT_THEME) -> str:
|
|
243
|
+
"""Resolve a requested theme name, honoring system-following default behavior."""
|
|
244
|
+
if theme_name == DEFAULT_THEME:
|
|
245
|
+
return system_theme_name()
|
|
246
|
+
return theme_name if theme_name in THEMES else system_theme_name()
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
__all__ = [
|
|
250
|
+
"DEFAULT_THEME",
|
|
251
|
+
"GITHUB_DARK",
|
|
252
|
+
"GITHUB_LIGHT",
|
|
253
|
+
"THEMES",
|
|
254
|
+
"ThemePalette",
|
|
255
|
+
"resolve_theme_name",
|
|
256
|
+
"system_theme_name",
|
|
257
|
+
"theme_button_text",
|
|
258
|
+
"toggle_theme_name",
|
|
259
|
+
]
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Shared workspace path models and pure helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import hashlib
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import socket
|
|
9
|
+
import sys
|
|
10
|
+
import unicodedata
|
|
11
|
+
|
|
12
|
+
from data_engine.platform.identity import APP_INTERNAL_ID, env_var
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _resolve_app_root_path() -> Path:
|
|
16
|
+
"""Resolve the application root in both dev and frozen executable contexts."""
|
|
17
|
+
if getattr(sys, "frozen", False):
|
|
18
|
+
return Path(sys.executable).resolve().parent
|
|
19
|
+
return Path(__file__).resolve().parents[3]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
APP_ROOT_PATH: Path = _resolve_app_root_path()
|
|
23
|
+
DATA_ENGINE_APP_ROOT_ENV_VAR: str = env_var("app_root")
|
|
24
|
+
DATA_ENGINE_WORKSPACE_ROOT_ENV_VAR: str = env_var("workspace_root")
|
|
25
|
+
DATA_ENGINE_WORKSPACE_ID_ENV_VAR: str = env_var("workspace_id")
|
|
26
|
+
DATA_ENGINE_WORKSPACE_COLLECTION_ROOT_ENV_VAR: str = env_var("workspace_collection_root")
|
|
27
|
+
DATA_ENGINE_RUNTIME_ROOT_ENV_VAR: str = env_var("runtime_root")
|
|
28
|
+
DATA_ENGINE_RUNTIME_DB_PATH_ENV_VAR: str = env_var("runtime_db_path")
|
|
29
|
+
WORKSPACE_CONFIG_DIR_NAME: str = "config"
|
|
30
|
+
WORKSPACE_FLOW_MODULES_DIR_NAME: str = "flow_modules"
|
|
31
|
+
WORKSPACE_FLOW_HELPERS_DIR_NAME: str = "flow_helpers"
|
|
32
|
+
WORKSPACE_DATABASES_DIR_NAME: str = "databases"
|
|
33
|
+
WORKSPACE_STATE_DIR_NAME: str = ".workspace_state"
|
|
34
|
+
WORKSPACE_AVAILABLE_MARKERS_DIR_NAME: str = "available"
|
|
35
|
+
WORKSPACE_LEASED_MARKERS_DIR_NAME: str = "leased"
|
|
36
|
+
WORKSPACE_STALE_MARKERS_DIR_NAME: str = "stale"
|
|
37
|
+
WORKSPACE_LEASE_METADATA_DIR_NAME: str = "leases"
|
|
38
|
+
WORKSPACE_CONTROL_REQUESTS_DIR_NAME: str = "control_requests"
|
|
39
|
+
WORKSPACE_SHARED_STATE_DIR_NAME: str = "state"
|
|
40
|
+
WORKSPACE_SHARED_RUNS_DIR_NAME: str = "runs"
|
|
41
|
+
WORKSPACE_SHARED_STEP_RUNS_DIR_NAME: str = "step_runs"
|
|
42
|
+
WORKSPACE_SHARED_LOGS_DIR_NAME: str = "logs"
|
|
43
|
+
WORKSPACE_SHARED_FILE_STATE_DIR_NAME: str = "file_state"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def normalized_path_text(value: Path | str) -> str:
|
|
47
|
+
"""Return a stable forward-slash path string for display and comparisons."""
|
|
48
|
+
return unicodedata.normalize("NFC", str(value).replace("\\", "/"))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def path_display(value: Path | str | None, *, empty: str = "(not set)") -> str:
|
|
52
|
+
"""Render a path value consistently for UI/display use."""
|
|
53
|
+
if value is None:
|
|
54
|
+
return empty
|
|
55
|
+
return normalized_path_text(value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def toml_path_text(value: Path | str) -> str:
|
|
59
|
+
"""Render a path as TOML-safe text without Windows backslash escapes."""
|
|
60
|
+
return normalized_path_text(value)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class InvalidWorkspaceIdError(ValueError):
|
|
64
|
+
"""Raised when a workspace id contains unsafe path components."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def validate_workspace_id(workspace_id: str) -> str:
|
|
68
|
+
"""Return a workspace id that is safe to use in path components."""
|
|
69
|
+
candidate = str(workspace_id)
|
|
70
|
+
if not candidate.strip():
|
|
71
|
+
raise InvalidWorkspaceIdError("Workspace id cannot be empty.")
|
|
72
|
+
if candidate in {".", ".."}:
|
|
73
|
+
raise InvalidWorkspaceIdError(f"Workspace id {candidate!r} is not allowed.")
|
|
74
|
+
if "\x00" in candidate:
|
|
75
|
+
raise InvalidWorkspaceIdError("Workspace id cannot contain NUL bytes.")
|
|
76
|
+
if "/" in candidate or "\\" in candidate:
|
|
77
|
+
raise InvalidWorkspaceIdError(f"Workspace id {candidate!r} must not contain path separators.")
|
|
78
|
+
return candidate
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def local_workspace_namespace(workspace_root: Path | str, workspace_id: str) -> str:
|
|
82
|
+
"""Return the machine-local namespace for one workspace root."""
|
|
83
|
+
workspace_id = validate_workspace_id(workspace_id)
|
|
84
|
+
digest = hashlib.sha1(normalized_path_text(Path(workspace_root).expanduser().resolve()).encode("utf-8")).hexdigest()[:12]
|
|
85
|
+
return f"{workspace_id}_{digest}"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True)
|
|
89
|
+
class WorkspaceSettings:
|
|
90
|
+
"""Machine-local workspace discovery settings."""
|
|
91
|
+
|
|
92
|
+
app_root: Path
|
|
93
|
+
settings_path: Path
|
|
94
|
+
state_root: Path
|
|
95
|
+
runtime_root: Path
|
|
96
|
+
workspace_collection_root: Path | None
|
|
97
|
+
default_selected: str | None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class DiscoveredWorkspace:
|
|
102
|
+
"""One discovered authored workspace."""
|
|
103
|
+
|
|
104
|
+
workspace_id: str
|
|
105
|
+
workspace_root: Path
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class WorkspacePaths:
|
|
110
|
+
"""Resolved authored, shared, and local-artifact paths for one workspace."""
|
|
111
|
+
|
|
112
|
+
app_root: Path
|
|
113
|
+
workspace_collection_root: Path
|
|
114
|
+
workspace_id: str
|
|
115
|
+
workspace_root: Path
|
|
116
|
+
config_dir: Path
|
|
117
|
+
flow_modules_dir: Path
|
|
118
|
+
databases_dir: Path
|
|
119
|
+
workspace_state_dir: Path
|
|
120
|
+
available_markers_dir: Path
|
|
121
|
+
leased_markers_dir: Path
|
|
122
|
+
stale_markers_dir: Path
|
|
123
|
+
lease_metadata_dir: Path
|
|
124
|
+
lease_metadata_path: Path
|
|
125
|
+
control_requests_dir: Path
|
|
126
|
+
control_request_path: Path
|
|
127
|
+
shared_state_dir: Path
|
|
128
|
+
shared_runs_path: Path
|
|
129
|
+
shared_step_runs_path: Path
|
|
130
|
+
shared_logs_path: Path
|
|
131
|
+
shared_file_state_path: Path
|
|
132
|
+
artifacts_dir: Path
|
|
133
|
+
workspace_cache_dir: Path
|
|
134
|
+
compiled_flow_modules_dir: Path
|
|
135
|
+
runtime_state_dir: Path
|
|
136
|
+
runtime_db_path: Path
|
|
137
|
+
daemon_log_path: Path
|
|
138
|
+
documentation_dir: Path
|
|
139
|
+
daemon_endpoint_kind: str
|
|
140
|
+
daemon_endpoint_path: str
|
|
141
|
+
sphinx_source_dir: Path
|
|
142
|
+
workspace_configured: bool = True
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def authored_workspace_is_available(paths: WorkspacePaths) -> bool:
|
|
146
|
+
"""Return whether one authored workspace root is still present and usable."""
|
|
147
|
+
return paths.workspace_configured and paths.workspace_root.is_dir() and paths.flow_modules_dir.is_dir()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def machine_id_text() -> str:
|
|
151
|
+
"""Return a stable local machine identifier for lease metadata."""
|
|
152
|
+
return socket.gethostname()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
__all__ = [
|
|
156
|
+
"APP_INTERNAL_ID",
|
|
157
|
+
"APP_ROOT_PATH",
|
|
158
|
+
"DATA_ENGINE_APP_ROOT_ENV_VAR",
|
|
159
|
+
"DATA_ENGINE_RUNTIME_DB_PATH_ENV_VAR",
|
|
160
|
+
"DATA_ENGINE_RUNTIME_ROOT_ENV_VAR",
|
|
161
|
+
"DATA_ENGINE_WORKSPACE_COLLECTION_ROOT_ENV_VAR",
|
|
162
|
+
"DATA_ENGINE_WORKSPACE_ID_ENV_VAR",
|
|
163
|
+
"DATA_ENGINE_WORKSPACE_ROOT_ENV_VAR",
|
|
164
|
+
"DiscoveredWorkspace",
|
|
165
|
+
"InvalidWorkspaceIdError",
|
|
166
|
+
"WORKSPACE_AVAILABLE_MARKERS_DIR_NAME",
|
|
167
|
+
"WORKSPACE_CONFIG_DIR_NAME",
|
|
168
|
+
"WORKSPACE_CONTROL_REQUESTS_DIR_NAME",
|
|
169
|
+
"WORKSPACE_DATABASES_DIR_NAME",
|
|
170
|
+
"WORKSPACE_FLOW_HELPERS_DIR_NAME",
|
|
171
|
+
"WORKSPACE_FLOW_MODULES_DIR_NAME",
|
|
172
|
+
"WORKSPACE_LEASED_MARKERS_DIR_NAME",
|
|
173
|
+
"WORKSPACE_LEASE_METADATA_DIR_NAME",
|
|
174
|
+
"WORKSPACE_SHARED_FILE_STATE_DIR_NAME",
|
|
175
|
+
"WORKSPACE_SHARED_LOGS_DIR_NAME",
|
|
176
|
+
"WORKSPACE_SHARED_RUNS_DIR_NAME",
|
|
177
|
+
"WORKSPACE_SHARED_STATE_DIR_NAME",
|
|
178
|
+
"WORKSPACE_SHARED_STEP_RUNS_DIR_NAME",
|
|
179
|
+
"WORKSPACE_STALE_MARKERS_DIR_NAME",
|
|
180
|
+
"WORKSPACE_STATE_DIR_NAME",
|
|
181
|
+
"WorkspacePaths",
|
|
182
|
+
"WorkspaceSettings",
|
|
183
|
+
"authored_workspace_is_available",
|
|
184
|
+
"local_workspace_namespace",
|
|
185
|
+
"machine_id_text",
|
|
186
|
+
"normalized_path_text",
|
|
187
|
+
"path_display",
|
|
188
|
+
"toml_path_text",
|
|
189
|
+
"validate_workspace_id",
|
|
190
|
+
]
|