moru 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.
- moru/__init__.py +174 -0
- moru/api/__init__.py +164 -0
- moru/api/client/__init__.py +8 -0
- moru/api/client/api/__init__.py +1 -0
- moru/api/client/api/sandboxes/__init__.py +1 -0
- moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
- moru/api/client/api/sandboxes/get_sandboxes.py +176 -0
- moru/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
- moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
- moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
- moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
- moru/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
- moru/api/client/api/sandboxes/post_sandboxes.py +172 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +165 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
- moru/api/client/api/templates/__init__.py +1 -0
- moru/api/client/api/templates/delete_templates_template_id.py +157 -0
- moru/api/client/api/templates/get_templates.py +172 -0
- moru/api/client/api/templates/get_templates_template_id.py +195 -0
- moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +217 -0
- moru/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
- moru/api/client/api/templates/patch_templates_template_id.py +183 -0
- moru/api/client/api/templates/post_templates.py +172 -0
- moru/api/client/api/templates/post_templates_template_id.py +181 -0
- moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
- moru/api/client/api/templates/post_v2_templates.py +172 -0
- moru/api/client/api/templates/post_v3_templates.py +172 -0
- moru/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
- moru/api/client/client.py +286 -0
- moru/api/client/errors.py +16 -0
- moru/api/client/models/__init__.py +123 -0
- moru/api/client/models/aws_registry.py +85 -0
- moru/api/client/models/aws_registry_type.py +8 -0
- moru/api/client/models/build_log_entry.py +89 -0
- moru/api/client/models/build_status_reason.py +95 -0
- moru/api/client/models/connect_sandbox.py +59 -0
- moru/api/client/models/created_access_token.py +100 -0
- moru/api/client/models/created_team_api_key.py +166 -0
- moru/api/client/models/disk_metrics.py +91 -0
- moru/api/client/models/error.py +67 -0
- moru/api/client/models/gcp_registry.py +69 -0
- moru/api/client/models/gcp_registry_type.py +8 -0
- moru/api/client/models/general_registry.py +77 -0
- moru/api/client/models/general_registry_type.py +8 -0
- moru/api/client/models/identifier_masking_details.py +83 -0
- moru/api/client/models/listed_sandbox.py +154 -0
- moru/api/client/models/log_level.py +11 -0
- moru/api/client/models/max_team_metric.py +78 -0
- moru/api/client/models/mcp_type_0.py +44 -0
- moru/api/client/models/new_access_token.py +59 -0
- moru/api/client/models/new_sandbox.py +172 -0
- moru/api/client/models/new_team_api_key.py +59 -0
- moru/api/client/models/node.py +155 -0
- moru/api/client/models/node_detail.py +165 -0
- moru/api/client/models/node_metrics.py +122 -0
- moru/api/client/models/node_status.py +11 -0
- moru/api/client/models/node_status_change.py +79 -0
- moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
- moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
- moru/api/client/models/resumed_sandbox.py +68 -0
- moru/api/client/models/sandbox.py +145 -0
- moru/api/client/models/sandbox_detail.py +183 -0
- moru/api/client/models/sandbox_log.py +70 -0
- moru/api/client/models/sandbox_log_entry.py +93 -0
- moru/api/client/models/sandbox_log_entry_fields.py +44 -0
- moru/api/client/models/sandbox_logs.py +91 -0
- moru/api/client/models/sandbox_metric.py +118 -0
- moru/api/client/models/sandbox_network_config.py +92 -0
- moru/api/client/models/sandbox_state.py +9 -0
- moru/api/client/models/sandboxes_with_metrics.py +59 -0
- moru/api/client/models/team.py +83 -0
- moru/api/client/models/team_api_key.py +158 -0
- moru/api/client/models/team_metric.py +86 -0
- moru/api/client/models/team_user.py +68 -0
- moru/api/client/models/template.py +217 -0
- moru/api/client/models/template_build.py +139 -0
- moru/api/client/models/template_build_file_upload.py +70 -0
- moru/api/client/models/template_build_info.py +126 -0
- moru/api/client/models/template_build_request.py +115 -0
- moru/api/client/models/template_build_request_v2.py +88 -0
- moru/api/client/models/template_build_request_v3.py +88 -0
- moru/api/client/models/template_build_start_v2.py +184 -0
- moru/api/client/models/template_build_status.py +11 -0
- moru/api/client/models/template_legacy.py +207 -0
- moru/api/client/models/template_request_response_v3.py +83 -0
- moru/api/client/models/template_step.py +91 -0
- moru/api/client/models/template_update_request.py +59 -0
- moru/api/client/models/template_with_builds.py +148 -0
- moru/api/client/models/update_team_api_key.py +59 -0
- moru/api/client/py.typed +1 -0
- moru/api/client/types.py +54 -0
- moru/api/client_async/__init__.py +50 -0
- moru/api/client_sync/__init__.py +52 -0
- moru/api/metadata.py +14 -0
- moru/connection_config.py +217 -0
- moru/envd/api.py +59 -0
- moru/envd/filesystem/filesystem_connect.py +193 -0
- moru/envd/filesystem/filesystem_pb2.py +76 -0
- moru/envd/filesystem/filesystem_pb2.pyi +233 -0
- moru/envd/process/process_connect.py +155 -0
- moru/envd/process/process_pb2.py +92 -0
- moru/envd/process/process_pb2.pyi +304 -0
- moru/envd/rpc.py +61 -0
- moru/envd/versions.py +6 -0
- moru/exceptions.py +95 -0
- moru/sandbox/commands/command_handle.py +69 -0
- moru/sandbox/commands/main.py +39 -0
- moru/sandbox/filesystem/filesystem.py +94 -0
- moru/sandbox/filesystem/watch_handle.py +60 -0
- moru/sandbox/main.py +210 -0
- moru/sandbox/mcp.py +1120 -0
- moru/sandbox/network.py +8 -0
- moru/sandbox/sandbox_api.py +210 -0
- moru/sandbox/signature.py +45 -0
- moru/sandbox/utils.py +34 -0
- moru/sandbox_async/commands/command.py +336 -0
- moru/sandbox_async/commands/command_handle.py +196 -0
- moru/sandbox_async/commands/pty.py +240 -0
- moru/sandbox_async/filesystem/filesystem.py +531 -0
- moru/sandbox_async/filesystem/watch_handle.py +62 -0
- moru/sandbox_async/main.py +734 -0
- moru/sandbox_async/paginator.py +69 -0
- moru/sandbox_async/sandbox_api.py +325 -0
- moru/sandbox_async/utils.py +7 -0
- moru/sandbox_sync/commands/command.py +328 -0
- moru/sandbox_sync/commands/command_handle.py +150 -0
- moru/sandbox_sync/commands/pty.py +230 -0
- moru/sandbox_sync/filesystem/filesystem.py +518 -0
- moru/sandbox_sync/filesystem/watch_handle.py +69 -0
- moru/sandbox_sync/main.py +726 -0
- moru/sandbox_sync/paginator.py +69 -0
- moru/sandbox_sync/sandbox_api.py +308 -0
- moru/template/consts.py +30 -0
- moru/template/dockerfile_parser.py +275 -0
- moru/template/logger.py +232 -0
- moru/template/main.py +1360 -0
- moru/template/readycmd.py +138 -0
- moru/template/types.py +105 -0
- moru/template/utils.py +320 -0
- moru/template_async/build_api.py +202 -0
- moru/template_async/main.py +366 -0
- moru/template_sync/build_api.py +199 -0
- moru/template_sync/main.py +371 -0
- moru-0.1.0.dist-info/METADATA +63 -0
- moru-0.1.0.dist-info/RECORD +152 -0
- moru-0.1.0.dist-info/WHEEL +4 -0
- moru-0.1.0.dist-info/licenses/LICENSE +9 -0
- moru_connect/__init__.py +1 -0
- moru_connect/client.py +493 -0
moru/template/logger.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional, TypedDict, Callable, Dict, Literal
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.style import Style
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
12
|
+
from moru.template.utils import strip_ansi_escape_codes
|
|
13
|
+
|
|
14
|
+
"""Log entry severity levels."""
|
|
15
|
+
LogEntryLevel = Literal["debug", "info", "warn", "error"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class LogEntry:
|
|
20
|
+
"""
|
|
21
|
+
Represents a single log entry from the template build process.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
timestamp: datetime
|
|
25
|
+
level: LogEntryLevel
|
|
26
|
+
message: str
|
|
27
|
+
|
|
28
|
+
def __post_init__(self):
|
|
29
|
+
self.message = strip_ansi_escape_codes(self.message)
|
|
30
|
+
|
|
31
|
+
def __str__(self) -> str:
|
|
32
|
+
return f"[{self.timestamp.isoformat()}] [{self.level}] {self.message}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class LogEntryStart(LogEntry):
|
|
37
|
+
"""
|
|
38
|
+
Special log entry indicating the start of a build process.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
level: LogEntryLevel = field(default="debug", init=False)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class LogEntryEnd(LogEntry):
|
|
46
|
+
"""
|
|
47
|
+
Special log entry indicating the end of a build process.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
level: LogEntryLevel = field(default="debug", init=False)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
Interval in milliseconds for updating the build timer display.
|
|
55
|
+
"""
|
|
56
|
+
TIMER_UPDATE_INTERVAL_MS = 150
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
Default minimum log level to display.
|
|
60
|
+
"""
|
|
61
|
+
DEFAULT_LEVEL: LogEntryLevel = "info"
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
Colored labels for each log level.
|
|
65
|
+
"""
|
|
66
|
+
levels: Dict[LogEntryLevel, tuple[str, Style]] = {
|
|
67
|
+
"error": ("ERROR", Style(color="red")),
|
|
68
|
+
"warn": ("WARN ", Style(color="#FF4400")),
|
|
69
|
+
"info": ("INFO ", Style(color="#FF8800")),
|
|
70
|
+
"debug": ("DEBUG", Style(color="bright_black")),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
Numeric ordering of log levels for comparison (lower = less severe).
|
|
75
|
+
"""
|
|
76
|
+
level_order = {
|
|
77
|
+
"debug": 0,
|
|
78
|
+
"info": 1,
|
|
79
|
+
"warn": 2,
|
|
80
|
+
"error": 3,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def set_interval(func, interval):
|
|
85
|
+
"""
|
|
86
|
+
Returns a stop function that can be called to cancel the interval.
|
|
87
|
+
|
|
88
|
+
Similar to JavaScript's setInterval.
|
|
89
|
+
|
|
90
|
+
:param func: Function to execute at each interval
|
|
91
|
+
:param interval: Interval duration in **seconds**
|
|
92
|
+
|
|
93
|
+
:return: Stop function that can be called to cancel the interval
|
|
94
|
+
"""
|
|
95
|
+
stopped = threading.Event()
|
|
96
|
+
|
|
97
|
+
def loop():
|
|
98
|
+
while not stopped.is_set():
|
|
99
|
+
if stopped.wait(interval): # wait returns True if stopped
|
|
100
|
+
break
|
|
101
|
+
if not stopped.is_set(): # Double-check before executing
|
|
102
|
+
func()
|
|
103
|
+
|
|
104
|
+
threading.Thread(target=loop, daemon=True).start()
|
|
105
|
+
return stopped.set # Return the stop function
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class DefaultBuildLoggerInitialState(TypedDict):
|
|
109
|
+
start_time: float
|
|
110
|
+
animation_frame: int
|
|
111
|
+
timer: Optional[Callable[[], None]]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class DefaultBuildLogger:
|
|
115
|
+
__console = Console()
|
|
116
|
+
|
|
117
|
+
__min_level: LogEntryLevel
|
|
118
|
+
__state: DefaultBuildLoggerInitialState
|
|
119
|
+
|
|
120
|
+
def __init__(self, min_level: Optional[LogEntryLevel] = None):
|
|
121
|
+
self.__min_level = min_level if min_level is not None else DEFAULT_LEVEL
|
|
122
|
+
self.__reset_initial_state()
|
|
123
|
+
|
|
124
|
+
def logger(self, log):
|
|
125
|
+
if isinstance(log, LogEntryStart):
|
|
126
|
+
self.__start_timer()
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
if isinstance(log, LogEntryEnd):
|
|
130
|
+
if self.__state["timer"] is not None:
|
|
131
|
+
self.__state["timer"]()
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
# Filter by minimum level
|
|
135
|
+
if level_order[log.level] < level_order[self.__min_level]:
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
formatted_line = self.__format_log_line(log)
|
|
139
|
+
self.__console.print(formatted_line)
|
|
140
|
+
|
|
141
|
+
# Redraw the timer line
|
|
142
|
+
self.__update_timer()
|
|
143
|
+
|
|
144
|
+
def __reset_initial_state(self, timer: Optional[Callable[[], None]] = None):
|
|
145
|
+
self.__state = {
|
|
146
|
+
"start_time": time.time(),
|
|
147
|
+
"animation_frame": 0,
|
|
148
|
+
"timer": timer,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
def __format_timer_line(self) -> str:
|
|
152
|
+
elapsed_seconds = time.time() - self.__state["start_time"]
|
|
153
|
+
return f"{elapsed_seconds:.1f}s"
|
|
154
|
+
|
|
155
|
+
def __animate_status(self) -> str:
|
|
156
|
+
frames = ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"]
|
|
157
|
+
idx = self.__state["animation_frame"] % len(frames)
|
|
158
|
+
return frames[idx]
|
|
159
|
+
|
|
160
|
+
def __format_log_line(self, line: LogEntry) -> Text:
|
|
161
|
+
timer = self.__format_timer_line().ljust(5)
|
|
162
|
+
timestamp = line.timestamp.strftime("%H:%M:%S")
|
|
163
|
+
level_text, level_style = levels.get(line.level, levels[DEFAULT_LEVEL])
|
|
164
|
+
|
|
165
|
+
# Build a rich Text object
|
|
166
|
+
text = Text.assemble(
|
|
167
|
+
timer,
|
|
168
|
+
" | ",
|
|
169
|
+
(timestamp, "dim"),
|
|
170
|
+
" ",
|
|
171
|
+
(level_text, level_style),
|
|
172
|
+
" ",
|
|
173
|
+
line.message,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return text
|
|
177
|
+
|
|
178
|
+
def __start_timer(self):
|
|
179
|
+
if not sys.stdout.isatty():
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
# Start the timer interval
|
|
183
|
+
stop_timer = set_interval(
|
|
184
|
+
self.__update_timer, TIMER_UPDATE_INTERVAL_MS / 1000.0
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
self.__reset_initial_state(stop_timer)
|
|
188
|
+
|
|
189
|
+
# Initial timer display
|
|
190
|
+
self.__update_timer()
|
|
191
|
+
|
|
192
|
+
def __update_timer(self):
|
|
193
|
+
if not sys.stdout.isatty():
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
self.__state["animation_frame"] += 1
|
|
197
|
+
jumping_squares = self.__animate_status()
|
|
198
|
+
|
|
199
|
+
timer_text = Text.assemble(
|
|
200
|
+
jumping_squares, " Building ", self.__format_timer_line()
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Print with carriage return
|
|
204
|
+
self.__console.print(timer_text, end="\r")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def default_build_logger(
|
|
208
|
+
min_level: Optional[LogEntryLevel] = None,
|
|
209
|
+
) -> Callable[[LogEntry], None]:
|
|
210
|
+
"""
|
|
211
|
+
Create a default build logger with animated timer display.
|
|
212
|
+
|
|
213
|
+
:param min_level: Minimum log level to display (default: 'info')
|
|
214
|
+
|
|
215
|
+
:return: Logger function that accepts LogEntry instances
|
|
216
|
+
|
|
217
|
+
Example
|
|
218
|
+
```python
|
|
219
|
+
from moru import Template, default_build_logger
|
|
220
|
+
|
|
221
|
+
template = Template().from_python_image()
|
|
222
|
+
|
|
223
|
+
# Use with build - implementation would be in build_async module
|
|
224
|
+
# await Template.build(template,
|
|
225
|
+
# alias='my-template',
|
|
226
|
+
# on_build_logs=default_build_logger(min_level='debug')
|
|
227
|
+
# )
|
|
228
|
+
```
|
|
229
|
+
"""
|
|
230
|
+
build_logger = DefaultBuildLogger(min_level)
|
|
231
|
+
|
|
232
|
+
return build_logger.logger
|