lyceum-cli 1.0.25__py3-none-any.whl → 1.0.27__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.
- lyceum/external/auth/login.py +18 -18
- lyceum/external/compute/execution/docker.py +4 -2
- lyceum/external/compute/execution/docker_compose.py +263 -0
- lyceum/external/compute/execution/notebook.py +0 -2
- lyceum/external/compute/execution/python.py +2 -1
- lyceum/external/compute/inference/batch.py +8 -10
- lyceum/external/vms/instances.py +301 -0
- lyceum/external/vms/management.py +383 -0
- lyceum/main.py +3 -0
- lyceum/shared/config.py +19 -24
- lyceum/shared/display.py +12 -31
- lyceum/shared/streaming.py +17 -45
- {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/METADATA +1 -1
- lyceum_cli-1.0.27.dist-info/RECORD +34 -0
- {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/WHEEL +1 -1
- {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/top_level.txt +0 -1
- lyceum/external/compute/execution/docker_config.py +0 -123
- lyceum/external/storage/files.py +0 -273
- lyceum_cli-1.0.25.dist-info/RECORD +0 -46
- tests/__init__.py +0 -1
- tests/conftest.py +0 -200
- tests/unit/__init__.py +0 -1
- tests/unit/external/__init__.py +0 -1
- tests/unit/external/compute/__init__.py +0 -1
- tests/unit/external/compute/execution/__init__.py +0 -1
- tests/unit/external/compute/execution/test_data.py +0 -33
- tests/unit/external/compute/execution/test_dependency_resolver.py +0 -257
- tests/unit/external/compute/execution/test_python_helpers.py +0 -406
- tests/unit/external/compute/execution/test_python_run.py +0 -289
- tests/unit/shared/__init__.py +0 -1
- tests/unit/shared/test_config.py +0 -341
- tests/unit/shared/test_streaming.py +0 -259
- /lyceum/external/{storage → vms}/__init__.py +0 -0
- {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/entry_points.txt +0 -0
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
"""Tests for streaming utilities."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from unittest.mock import MagicMock, patch
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
|
|
8
|
-
from lyceum.shared.streaming import (
|
|
9
|
-
StatusLine,
|
|
10
|
-
check_execution_status,
|
|
11
|
-
normalize_newlines,
|
|
12
|
-
stream_execution_output,
|
|
13
|
-
strip_ansi_codes,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TestStripAnsiCodes:
|
|
18
|
-
"""Tests for strip_ansi_codes function."""
|
|
19
|
-
|
|
20
|
-
def test_removes_color_codes(self):
|
|
21
|
-
text = "\x1b[31mRed text\x1b[0m"
|
|
22
|
-
assert strip_ansi_codes(text) == "Red text"
|
|
23
|
-
|
|
24
|
-
def test_removes_multiple_color_codes(self):
|
|
25
|
-
text = "\x1b[1m\x1b[31mBold Red\x1b[0m Normal"
|
|
26
|
-
assert strip_ansi_codes(text) == "Bold Red Normal"
|
|
27
|
-
|
|
28
|
-
def test_preserves_plain_text(self):
|
|
29
|
-
text = "Plain text without codes"
|
|
30
|
-
assert strip_ansi_codes(text) == text
|
|
31
|
-
|
|
32
|
-
def test_handles_empty_string(self):
|
|
33
|
-
assert strip_ansi_codes("") == ""
|
|
34
|
-
|
|
35
|
-
def test_handles_nested_codes(self):
|
|
36
|
-
text = "\x1b[38;5;196mExtended color\x1b[0m"
|
|
37
|
-
assert strip_ansi_codes(text) == "Extended color"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class TestNormalizeNewlines:
|
|
41
|
-
"""Tests for normalize_newlines function."""
|
|
42
|
-
|
|
43
|
-
def test_collapses_double_newlines(self):
|
|
44
|
-
assert normalize_newlines("a\n\nb") == "a\nb"
|
|
45
|
-
|
|
46
|
-
def test_collapses_triple_newlines(self):
|
|
47
|
-
assert normalize_newlines("a\n\n\nb") == "a\nb"
|
|
48
|
-
|
|
49
|
-
def test_preserves_single_newlines(self):
|
|
50
|
-
assert normalize_newlines("a\nb\nc") == "a\nb\nc"
|
|
51
|
-
|
|
52
|
-
def test_handles_mixed_newlines(self):
|
|
53
|
-
text = "a\nb\n\n\nc\nd"
|
|
54
|
-
assert normalize_newlines(text) == "a\nb\nc\nd"
|
|
55
|
-
|
|
56
|
-
def test_handles_empty_string(self):
|
|
57
|
-
assert normalize_newlines("") == ""
|
|
58
|
-
|
|
59
|
-
def test_handles_only_newlines(self):
|
|
60
|
-
assert normalize_newlines("\n\n\n") == "\n"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class TestStatusLine:
|
|
64
|
-
"""Tests for StatusLine class."""
|
|
65
|
-
|
|
66
|
-
def test_start_stop_lifecycle(self):
|
|
67
|
-
status = StatusLine()
|
|
68
|
-
status.start()
|
|
69
|
-
assert status._live is not None
|
|
70
|
-
status.stop()
|
|
71
|
-
assert status._live is None
|
|
72
|
-
|
|
73
|
-
def test_multiple_stops_no_error(self):
|
|
74
|
-
status = StatusLine()
|
|
75
|
-
status.start()
|
|
76
|
-
status.stop()
|
|
77
|
-
status.stop() # Should not raise
|
|
78
|
-
|
|
79
|
-
def test_context_manager(self):
|
|
80
|
-
with StatusLine() as status:
|
|
81
|
-
assert status._live is not None
|
|
82
|
-
assert status._live is None
|
|
83
|
-
|
|
84
|
-
def test_update_message(self):
|
|
85
|
-
status = StatusLine()
|
|
86
|
-
status.start()
|
|
87
|
-
status.update("Loading...") # Should not raise
|
|
88
|
-
assert status._current_status == "Loading..."
|
|
89
|
-
status.stop()
|
|
90
|
-
|
|
91
|
-
def test_update_without_start(self):
|
|
92
|
-
status = StatusLine()
|
|
93
|
-
status.update("Test") # Should not raise even without start
|
|
94
|
-
assert status._current_status == "Test"
|
|
95
|
-
|
|
96
|
-
def test_stop_without_start(self):
|
|
97
|
-
status = StatusLine()
|
|
98
|
-
status.stop() # Should not raise
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class TestStreamExecutionOutput:
|
|
102
|
-
"""Tests for stream_execution_output function."""
|
|
103
|
-
|
|
104
|
-
@patch("lyceum.shared.streaming.httpx.stream")
|
|
105
|
-
@patch("lyceum.shared.streaming.config")
|
|
106
|
-
def test_success_with_output(
|
|
107
|
-
self,
|
|
108
|
-
mock_config,
|
|
109
|
-
mock_httpx_stream,
|
|
110
|
-
setup_httpx_stream,
|
|
111
|
-
mock_execution_id,
|
|
112
|
-
):
|
|
113
|
-
from tests.unit.external.compute.execution.test_data import (
|
|
114
|
-
SAMPLE_SSE_JOB_FINISHED,
|
|
115
|
-
SAMPLE_SSE_OUTPUT_EVENT,
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
mock_config.api_key = "test-key"
|
|
119
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
120
|
-
mock_httpx_stream.return_value = setup_httpx_stream(
|
|
121
|
-
events=[SAMPLE_SSE_OUTPUT_EVENT, SAMPLE_SSE_JOB_FINISHED]
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
result = stream_execution_output(mock_execution_id, "http://stream.url")
|
|
125
|
-
assert result is True
|
|
126
|
-
|
|
127
|
-
@patch("lyceum.shared.streaming.httpx.stream")
|
|
128
|
-
@patch("lyceum.shared.streaming.config")
|
|
129
|
-
def test_failure_with_nonzero_exit(
|
|
130
|
-
self,
|
|
131
|
-
mock_config,
|
|
132
|
-
mock_httpx_stream,
|
|
133
|
-
setup_httpx_stream,
|
|
134
|
-
mock_execution_id,
|
|
135
|
-
):
|
|
136
|
-
from tests.unit.external.compute.execution.test_data import SAMPLE_SSE_JOB_FAILED
|
|
137
|
-
|
|
138
|
-
mock_config.api_key = "test-key"
|
|
139
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
140
|
-
mock_httpx_stream.return_value = setup_httpx_stream(events=[SAMPLE_SSE_JOB_FAILED])
|
|
141
|
-
|
|
142
|
-
result = stream_execution_output(mock_execution_id, "http://stream.url")
|
|
143
|
-
assert result is False
|
|
144
|
-
|
|
145
|
-
@patch("lyceum.shared.streaming.httpx.stream")
|
|
146
|
-
@patch("lyceum.shared.streaming.config")
|
|
147
|
-
def test_http_error_response(
|
|
148
|
-
self,
|
|
149
|
-
mock_config,
|
|
150
|
-
mock_httpx_stream,
|
|
151
|
-
mock_execution_id,
|
|
152
|
-
):
|
|
153
|
-
mock_config.api_key = "test-key"
|
|
154
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
155
|
-
|
|
156
|
-
mock_response = MagicMock()
|
|
157
|
-
mock_response.status_code = 500
|
|
158
|
-
mock_context = MagicMock()
|
|
159
|
-
mock_context.__enter__.return_value = mock_response
|
|
160
|
-
mock_context.__exit__.return_value = None
|
|
161
|
-
mock_httpx_stream.return_value = mock_context
|
|
162
|
-
|
|
163
|
-
result = stream_execution_output(mock_execution_id, "http://stream.url")
|
|
164
|
-
assert result is False
|
|
165
|
-
|
|
166
|
-
@patch("lyceum.shared.streaming.httpx.stream")
|
|
167
|
-
@patch("lyceum.shared.streaming.config")
|
|
168
|
-
def test_uses_default_url_when_not_provided(
|
|
169
|
-
self,
|
|
170
|
-
mock_config,
|
|
171
|
-
mock_httpx_stream,
|
|
172
|
-
setup_httpx_stream,
|
|
173
|
-
mock_execution_id,
|
|
174
|
-
):
|
|
175
|
-
from tests.unit.external.compute.execution.test_data import SAMPLE_SSE_JOB_FINISHED
|
|
176
|
-
|
|
177
|
-
mock_config.api_key = "test-key"
|
|
178
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
179
|
-
mock_httpx_stream.return_value = setup_httpx_stream(events=[SAMPLE_SSE_JOB_FINISHED])
|
|
180
|
-
|
|
181
|
-
stream_execution_output(mock_execution_id)
|
|
182
|
-
|
|
183
|
-
# Check that the default URL was constructed
|
|
184
|
-
call_args = mock_httpx_stream.call_args
|
|
185
|
-
assert mock_execution_id in call_args[0][1]
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
class TestCheckExecutionStatus:
|
|
189
|
-
"""Tests for check_execution_status function."""
|
|
190
|
-
|
|
191
|
-
@patch("lyceum.shared.streaming.httpx.get")
|
|
192
|
-
@patch("lyceum.shared.streaming.config")
|
|
193
|
-
def test_completed_status(self, mock_config, mock_httpx_get, mock_execution_id):
|
|
194
|
-
mock_config.api_key = "test-key"
|
|
195
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
196
|
-
|
|
197
|
-
mock_response = MagicMock()
|
|
198
|
-
mock_response.status_code = 200
|
|
199
|
-
mock_response.json.return_value = {"status": "completed"}
|
|
200
|
-
mock_httpx_get.return_value = mock_response
|
|
201
|
-
|
|
202
|
-
result = check_execution_status(mock_execution_id)
|
|
203
|
-
assert result is True
|
|
204
|
-
|
|
205
|
-
@patch("lyceum.shared.streaming.httpx.get")
|
|
206
|
-
@patch("lyceum.shared.streaming.config")
|
|
207
|
-
def test_failed_user_status(self, mock_config, mock_httpx_get, mock_execution_id):
|
|
208
|
-
mock_config.api_key = "test-key"
|
|
209
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
210
|
-
|
|
211
|
-
mock_response = MagicMock()
|
|
212
|
-
mock_response.status_code = 200
|
|
213
|
-
mock_response.json.return_value = {"status": "failed_user", "errors": "Error msg"}
|
|
214
|
-
mock_httpx_get.return_value = mock_response
|
|
215
|
-
|
|
216
|
-
result = check_execution_status(mock_execution_id)
|
|
217
|
-
assert result is False
|
|
218
|
-
|
|
219
|
-
@patch("lyceum.shared.streaming.httpx.get")
|
|
220
|
-
@patch("lyceum.shared.streaming.config")
|
|
221
|
-
def test_failed_system_status(self, mock_config, mock_httpx_get, mock_execution_id):
|
|
222
|
-
mock_config.api_key = "test-key"
|
|
223
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
224
|
-
|
|
225
|
-
mock_response = MagicMock()
|
|
226
|
-
mock_response.status_code = 200
|
|
227
|
-
mock_response.json.return_value = {"status": "failed_system"}
|
|
228
|
-
mock_httpx_get.return_value = mock_response
|
|
229
|
-
|
|
230
|
-
result = check_execution_status(mock_execution_id)
|
|
231
|
-
assert result is False
|
|
232
|
-
|
|
233
|
-
@patch("lyceum.shared.streaming.httpx.get")
|
|
234
|
-
@patch("lyceum.shared.streaming.config")
|
|
235
|
-
def test_timeout_status(self, mock_config, mock_httpx_get, mock_execution_id):
|
|
236
|
-
mock_config.api_key = "test-key"
|
|
237
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
238
|
-
|
|
239
|
-
mock_response = MagicMock()
|
|
240
|
-
mock_response.status_code = 200
|
|
241
|
-
mock_response.json.return_value = {"status": "timeout"}
|
|
242
|
-
mock_httpx_get.return_value = mock_response
|
|
243
|
-
|
|
244
|
-
result = check_execution_status(mock_execution_id)
|
|
245
|
-
assert result is False
|
|
246
|
-
|
|
247
|
-
@patch("lyceum.shared.streaming.httpx.get")
|
|
248
|
-
@patch("lyceum.shared.streaming.config")
|
|
249
|
-
def test_cancelled_status(self, mock_config, mock_httpx_get, mock_execution_id):
|
|
250
|
-
mock_config.api_key = "test-key"
|
|
251
|
-
mock_config.base_url = "https://api.lyceum.dev"
|
|
252
|
-
|
|
253
|
-
mock_response = MagicMock()
|
|
254
|
-
mock_response.status_code = 200
|
|
255
|
-
mock_response.json.return_value = {"status": "cancelled"}
|
|
256
|
-
mock_httpx_get.return_value = mock_response
|
|
257
|
-
|
|
258
|
-
result = check_execution_status(mock_execution_id)
|
|
259
|
-
assert result is False
|
|
File without changes
|
|
File without changes
|