hud-python 0.4.1__py3-none-any.whl → 0.4.3__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.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- hud/__init__.py +22 -22
- hud/agents/__init__.py +13 -15
- hud/agents/base.py +599 -599
- hud/agents/claude.py +373 -373
- hud/agents/langchain.py +261 -250
- hud/agents/misc/__init__.py +7 -7
- hud/agents/misc/response_agent.py +82 -80
- hud/agents/openai.py +352 -352
- hud/agents/openai_chat_generic.py +154 -154
- hud/agents/tests/__init__.py +1 -1
- hud/agents/tests/test_base.py +742 -742
- hud/agents/tests/test_claude.py +324 -324
- hud/agents/tests/test_client.py +363 -363
- hud/agents/tests/test_openai.py +237 -237
- hud/cli/__init__.py +617 -617
- hud/cli/__main__.py +8 -8
- hud/cli/analyze.py +371 -371
- hud/cli/analyze_metadata.py +230 -230
- hud/cli/build.py +498 -427
- hud/cli/clone.py +185 -185
- hud/cli/cursor.py +92 -92
- hud/cli/debug.py +392 -392
- hud/cli/docker_utils.py +83 -83
- hud/cli/init.py +280 -281
- hud/cli/interactive.py +353 -353
- hud/cli/mcp_server.py +764 -756
- hud/cli/pull.py +330 -336
- hud/cli/push.py +404 -370
- hud/cli/remote_runner.py +311 -311
- hud/cli/runner.py +160 -160
- hud/cli/tests/__init__.py +3 -3
- hud/cli/tests/test_analyze.py +284 -284
- hud/cli/tests/test_cli_init.py +265 -265
- hud/cli/tests/test_cli_main.py +27 -27
- hud/cli/tests/test_clone.py +142 -142
- hud/cli/tests/test_cursor.py +253 -253
- hud/cli/tests/test_debug.py +453 -453
- hud/cli/tests/test_mcp_server.py +139 -139
- hud/cli/tests/test_utils.py +388 -388
- hud/cli/utils.py +263 -263
- hud/clients/README.md +143 -143
- hud/clients/__init__.py +16 -16
- hud/clients/base.py +378 -379
- hud/clients/fastmcp.py +222 -222
- hud/clients/mcp_use.py +298 -278
- hud/clients/tests/__init__.py +1 -1
- hud/clients/tests/test_client_integration.py +111 -111
- hud/clients/tests/test_fastmcp.py +342 -342
- hud/clients/tests/test_protocol.py +188 -188
- hud/clients/utils/__init__.py +1 -1
- hud/clients/utils/retry_transport.py +160 -160
- hud/datasets.py +327 -322
- hud/misc/__init__.py +1 -1
- hud/misc/claude_plays_pokemon.py +292 -292
- hud/otel/__init__.py +35 -35
- hud/otel/collector.py +142 -142
- hud/otel/config.py +164 -164
- hud/otel/context.py +536 -536
- hud/otel/exporters.py +366 -366
- hud/otel/instrumentation.py +97 -97
- hud/otel/processors.py +118 -118
- hud/otel/tests/__init__.py +1 -1
- hud/otel/tests/test_processors.py +197 -197
- hud/server/__init__.py +5 -5
- hud/server/context.py +114 -114
- hud/server/helper/__init__.py +5 -5
- hud/server/low_level.py +132 -132
- hud/server/server.py +170 -166
- hud/server/tests/__init__.py +3 -3
- hud/settings.py +73 -73
- hud/shared/__init__.py +5 -5
- hud/shared/exceptions.py +180 -180
- hud/shared/requests.py +264 -264
- hud/shared/tests/test_exceptions.py +157 -157
- hud/shared/tests/test_requests.py +275 -275
- hud/telemetry/__init__.py +25 -25
- hud/telemetry/instrument.py +379 -379
- hud/telemetry/job.py +309 -309
- hud/telemetry/replay.py +74 -74
- hud/telemetry/trace.py +83 -83
- hud/tools/__init__.py +33 -33
- hud/tools/base.py +365 -365
- hud/tools/bash.py +161 -161
- hud/tools/computer/__init__.py +15 -15
- hud/tools/computer/anthropic.py +437 -437
- hud/tools/computer/hud.py +376 -376
- hud/tools/computer/openai.py +295 -295
- hud/tools/computer/settings.py +82 -82
- hud/tools/edit.py +314 -314
- hud/tools/executors/__init__.py +30 -30
- hud/tools/executors/base.py +539 -539
- hud/tools/executors/pyautogui.py +621 -621
- hud/tools/executors/tests/__init__.py +1 -1
- hud/tools/executors/tests/test_base_executor.py +338 -338
- hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
- hud/tools/executors/xdo.py +511 -511
- hud/tools/playwright.py +412 -412
- hud/tools/tests/__init__.py +3 -3
- hud/tools/tests/test_base.py +282 -282
- hud/tools/tests/test_bash.py +158 -158
- hud/tools/tests/test_bash_extended.py +197 -197
- hud/tools/tests/test_computer.py +425 -425
- hud/tools/tests/test_computer_actions.py +34 -34
- hud/tools/tests/test_edit.py +259 -259
- hud/tools/tests/test_init.py +27 -27
- hud/tools/tests/test_playwright_tool.py +183 -183
- hud/tools/tests/test_tools.py +145 -145
- hud/tools/tests/test_utils.py +156 -156
- hud/tools/types.py +72 -72
- hud/tools/utils.py +50 -50
- hud/types.py +136 -136
- hud/utils/__init__.py +10 -10
- hud/utils/async_utils.py +65 -65
- hud/utils/design.py +236 -168
- hud/utils/mcp.py +55 -55
- hud/utils/progress.py +149 -149
- hud/utils/telemetry.py +66 -66
- hud/utils/tests/test_async_utils.py +173 -173
- hud/utils/tests/test_init.py +17 -17
- hud/utils/tests/test_progress.py +261 -261
- hud/utils/tests/test_telemetry.py +82 -82
- hud/utils/tests/test_version.py +8 -8
- hud/version.py +7 -7
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/METADATA +10 -8
- hud_python-0.4.3.dist-info/RECORD +131 -0
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/licenses/LICENSE +21 -21
- hud/agents/art.py +0 -101
- hud_python-0.4.1.dist-info/RECORD +0 -132
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/WHEEL +0 -0
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,173 +1,173 @@
|
|
|
1
|
-
"""Tests for async utilities."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import asyncio
|
|
6
|
-
import logging
|
|
7
|
-
import threading
|
|
8
|
-
from unittest.mock import patch
|
|
9
|
-
|
|
10
|
-
import pytest
|
|
11
|
-
|
|
12
|
-
from hud.utils.async_utils import fire_and_forget
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class TestFireAndForget:
|
|
16
|
-
"""Test fire_and_forget function."""
|
|
17
|
-
|
|
18
|
-
@pytest.mark.asyncio
|
|
19
|
-
async def test_fire_and_forget_with_running_loop(self, caplog):
|
|
20
|
-
"""Test fire_and_forget when event loop is already running."""
|
|
21
|
-
# Create a simple coroutine that sets a flag
|
|
22
|
-
flag = []
|
|
23
|
-
|
|
24
|
-
async def test_coro():
|
|
25
|
-
flag.append(True)
|
|
26
|
-
|
|
27
|
-
# Call fire_and_forget in async context
|
|
28
|
-
fire_and_forget(test_coro(), description="test task")
|
|
29
|
-
|
|
30
|
-
# Give it a moment to execute
|
|
31
|
-
await asyncio.sleep(0.1)
|
|
32
|
-
|
|
33
|
-
# Check that the coroutine was executed
|
|
34
|
-
assert flag == [True]
|
|
35
|
-
|
|
36
|
-
@pytest.mark.asyncio
|
|
37
|
-
async def test_fire_and_forget_with_exception(self, caplog):
|
|
38
|
-
"""Test fire_and_forget handles exceptions gracefully."""
|
|
39
|
-
|
|
40
|
-
async def failing_coro():
|
|
41
|
-
raise ValueError("Test exception")
|
|
42
|
-
|
|
43
|
-
# This should not raise
|
|
44
|
-
fire_and_forget(failing_coro(), description="failing task")
|
|
45
|
-
|
|
46
|
-
# Give it a moment to execute
|
|
47
|
-
await asyncio.sleep(0.1)
|
|
48
|
-
|
|
49
|
-
# The exception should be handled silently
|
|
50
|
-
|
|
51
|
-
def test_fire_and_forget_no_event_loop(self):
|
|
52
|
-
"""Test fire_and_forget when no event loop is running."""
|
|
53
|
-
# This test runs in sync context
|
|
54
|
-
flag = threading.Event()
|
|
55
|
-
|
|
56
|
-
async def test_coro():
|
|
57
|
-
flag.set()
|
|
58
|
-
|
|
59
|
-
# Call fire_and_forget in sync context
|
|
60
|
-
fire_and_forget(test_coro(), description="sync test")
|
|
61
|
-
|
|
62
|
-
# Wait for the thread to complete
|
|
63
|
-
assert flag.wait(timeout=2.0), "Coroutine did not execute in thread"
|
|
64
|
-
|
|
65
|
-
def test_fire_and_forget_thread_exception(self, caplog):
|
|
66
|
-
"""Test fire_and_forget handles thread exceptions."""
|
|
67
|
-
|
|
68
|
-
async def failing_coro():
|
|
69
|
-
raise ValueError("Thread exception")
|
|
70
|
-
|
|
71
|
-
# Patch the logger to capture the debug call
|
|
72
|
-
from unittest.mock import patch
|
|
73
|
-
|
|
74
|
-
with patch("hud.utils.async_utils.logger") as mock_logger:
|
|
75
|
-
fire_and_forget(failing_coro(), description="thread fail")
|
|
76
|
-
|
|
77
|
-
# Give thread time to execute and log
|
|
78
|
-
import time
|
|
79
|
-
|
|
80
|
-
time.sleep(0.5) # Wait for thread to complete
|
|
81
|
-
|
|
82
|
-
# Check that error was logged with correct format
|
|
83
|
-
mock_logger.debug.assert_called()
|
|
84
|
-
# Get the actual call arguments
|
|
85
|
-
calls = mock_logger.debug.call_args_list
|
|
86
|
-
assert any(
|
|
87
|
-
call[0][0] == "Error in threaded %s: %s"
|
|
88
|
-
and call[0][1] == "thread fail"
|
|
89
|
-
and "Thread exception" in str(call[0][2])
|
|
90
|
-
for call in calls
|
|
91
|
-
), f"Expected log message not found in calls: {calls}"
|
|
92
|
-
|
|
93
|
-
def test_fire_and_forget_interpreter_shutdown(self, caplog):
|
|
94
|
-
"""Test fire_and_forget handles interpreter shutdown gracefully."""
|
|
95
|
-
|
|
96
|
-
async def test_coro():
|
|
97
|
-
pass
|
|
98
|
-
|
|
99
|
-
# Mock the scenario where we get interpreter shutdown error
|
|
100
|
-
with patch("asyncio.get_running_loop") as mock_get_loop:
|
|
101
|
-
mock_get_loop.side_effect = RuntimeError("no running event loop")
|
|
102
|
-
|
|
103
|
-
with patch("threading.Thread") as mock_thread:
|
|
104
|
-
mock_thread.side_effect = RuntimeError(
|
|
105
|
-
"cannot schedule new futures after interpreter shutdown"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
with caplog.at_level(logging.DEBUG):
|
|
109
|
-
# This should not raise or log
|
|
110
|
-
fire_and_forget(test_coro(), description="shutdown test")
|
|
111
|
-
|
|
112
|
-
# No error should be logged for interpreter shutdown
|
|
113
|
-
assert not any(
|
|
114
|
-
"Could not shutdown test" in record.message for record in caplog.records
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
def test_fire_and_forget_other_thread_error(self, caplog):
|
|
118
|
-
"""Test fire_and_forget logs non-shutdown thread errors."""
|
|
119
|
-
|
|
120
|
-
async def test_coro():
|
|
121
|
-
pass
|
|
122
|
-
|
|
123
|
-
# Mock the scenario where we get a different error
|
|
124
|
-
with patch("asyncio.get_running_loop") as mock_get_loop:
|
|
125
|
-
mock_get_loop.side_effect = RuntimeError("no running event loop")
|
|
126
|
-
|
|
127
|
-
with patch("threading.Thread") as mock_thread:
|
|
128
|
-
mock_thread.side_effect = RuntimeError("Some other error")
|
|
129
|
-
|
|
130
|
-
# Patch the logger to capture the debug call
|
|
131
|
-
with patch("hud.utils.async_utils.logger") as mock_logger:
|
|
132
|
-
fire_and_forget(test_coro(), description="error test")
|
|
133
|
-
|
|
134
|
-
# Check that error was logged with correct format
|
|
135
|
-
mock_logger.debug.assert_called_once_with(
|
|
136
|
-
"Could not %s - no event loop available: %s",
|
|
137
|
-
"error test",
|
|
138
|
-
mock_thread.side_effect,
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
@pytest.mark.asyncio
|
|
142
|
-
async def test_fire_and_forget_cancelled_task(self):
|
|
143
|
-
"""Test fire_and_forget handles cancelled tasks."""
|
|
144
|
-
|
|
145
|
-
cancel_event = asyncio.Event()
|
|
146
|
-
|
|
147
|
-
async def long_running_coro():
|
|
148
|
-
await cancel_event.wait()
|
|
149
|
-
|
|
150
|
-
# Get the current loop
|
|
151
|
-
loop = asyncio.get_running_loop()
|
|
152
|
-
|
|
153
|
-
# Patch create_task to capture the task
|
|
154
|
-
created_task = None
|
|
155
|
-
original_create_task = loop.create_task
|
|
156
|
-
|
|
157
|
-
def mock_create_task(coro):
|
|
158
|
-
nonlocal created_task
|
|
159
|
-
created_task = original_create_task(coro)
|
|
160
|
-
return created_task
|
|
161
|
-
|
|
162
|
-
with patch.object(loop, "create_task", side_effect=mock_create_task):
|
|
163
|
-
fire_and_forget(long_running_coro(), description="cancel test")
|
|
164
|
-
|
|
165
|
-
# Give it a moment to start
|
|
166
|
-
await asyncio.sleep(0.01)
|
|
167
|
-
|
|
168
|
-
# Cancel the task
|
|
169
|
-
assert created_task is not None
|
|
170
|
-
created_task.cancel()
|
|
171
|
-
|
|
172
|
-
# This should not raise any exceptions
|
|
173
|
-
await asyncio.sleep(0.01)
|
|
1
|
+
"""Tests for async utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
import threading
|
|
8
|
+
from unittest.mock import patch
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from hud.utils.async_utils import fire_and_forget
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestFireAndForget:
|
|
16
|
+
"""Test fire_and_forget function."""
|
|
17
|
+
|
|
18
|
+
@pytest.mark.asyncio
|
|
19
|
+
async def test_fire_and_forget_with_running_loop(self, caplog):
|
|
20
|
+
"""Test fire_and_forget when event loop is already running."""
|
|
21
|
+
# Create a simple coroutine that sets a flag
|
|
22
|
+
flag = []
|
|
23
|
+
|
|
24
|
+
async def test_coro():
|
|
25
|
+
flag.append(True)
|
|
26
|
+
|
|
27
|
+
# Call fire_and_forget in async context
|
|
28
|
+
fire_and_forget(test_coro(), description="test task")
|
|
29
|
+
|
|
30
|
+
# Give it a moment to execute
|
|
31
|
+
await asyncio.sleep(0.1)
|
|
32
|
+
|
|
33
|
+
# Check that the coroutine was executed
|
|
34
|
+
assert flag == [True]
|
|
35
|
+
|
|
36
|
+
@pytest.mark.asyncio
|
|
37
|
+
async def test_fire_and_forget_with_exception(self, caplog):
|
|
38
|
+
"""Test fire_and_forget handles exceptions gracefully."""
|
|
39
|
+
|
|
40
|
+
async def failing_coro():
|
|
41
|
+
raise ValueError("Test exception")
|
|
42
|
+
|
|
43
|
+
# This should not raise
|
|
44
|
+
fire_and_forget(failing_coro(), description="failing task")
|
|
45
|
+
|
|
46
|
+
# Give it a moment to execute
|
|
47
|
+
await asyncio.sleep(0.1)
|
|
48
|
+
|
|
49
|
+
# The exception should be handled silently
|
|
50
|
+
|
|
51
|
+
def test_fire_and_forget_no_event_loop(self):
|
|
52
|
+
"""Test fire_and_forget when no event loop is running."""
|
|
53
|
+
# This test runs in sync context
|
|
54
|
+
flag = threading.Event()
|
|
55
|
+
|
|
56
|
+
async def test_coro():
|
|
57
|
+
flag.set()
|
|
58
|
+
|
|
59
|
+
# Call fire_and_forget in sync context
|
|
60
|
+
fire_and_forget(test_coro(), description="sync test")
|
|
61
|
+
|
|
62
|
+
# Wait for the thread to complete
|
|
63
|
+
assert flag.wait(timeout=2.0), "Coroutine did not execute in thread"
|
|
64
|
+
|
|
65
|
+
def test_fire_and_forget_thread_exception(self, caplog):
|
|
66
|
+
"""Test fire_and_forget handles thread exceptions."""
|
|
67
|
+
|
|
68
|
+
async def failing_coro():
|
|
69
|
+
raise ValueError("Thread exception")
|
|
70
|
+
|
|
71
|
+
# Patch the logger to capture the debug call
|
|
72
|
+
from unittest.mock import patch
|
|
73
|
+
|
|
74
|
+
with patch("hud.utils.async_utils.logger") as mock_logger:
|
|
75
|
+
fire_and_forget(failing_coro(), description="thread fail")
|
|
76
|
+
|
|
77
|
+
# Give thread time to execute and log
|
|
78
|
+
import time
|
|
79
|
+
|
|
80
|
+
time.sleep(0.5) # Wait for thread to complete
|
|
81
|
+
|
|
82
|
+
# Check that error was logged with correct format
|
|
83
|
+
mock_logger.debug.assert_called()
|
|
84
|
+
# Get the actual call arguments
|
|
85
|
+
calls = mock_logger.debug.call_args_list
|
|
86
|
+
assert any(
|
|
87
|
+
call[0][0] == "Error in threaded %s: %s"
|
|
88
|
+
and call[0][1] == "thread fail"
|
|
89
|
+
and "Thread exception" in str(call[0][2])
|
|
90
|
+
for call in calls
|
|
91
|
+
), f"Expected log message not found in calls: {calls}"
|
|
92
|
+
|
|
93
|
+
def test_fire_and_forget_interpreter_shutdown(self, caplog):
|
|
94
|
+
"""Test fire_and_forget handles interpreter shutdown gracefully."""
|
|
95
|
+
|
|
96
|
+
async def test_coro():
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
# Mock the scenario where we get interpreter shutdown error
|
|
100
|
+
with patch("asyncio.get_running_loop") as mock_get_loop:
|
|
101
|
+
mock_get_loop.side_effect = RuntimeError("no running event loop")
|
|
102
|
+
|
|
103
|
+
with patch("threading.Thread") as mock_thread:
|
|
104
|
+
mock_thread.side_effect = RuntimeError(
|
|
105
|
+
"cannot schedule new futures after interpreter shutdown"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
with caplog.at_level(logging.DEBUG):
|
|
109
|
+
# This should not raise or log
|
|
110
|
+
fire_and_forget(test_coro(), description="shutdown test")
|
|
111
|
+
|
|
112
|
+
# No error should be logged for interpreter shutdown
|
|
113
|
+
assert not any(
|
|
114
|
+
"Could not shutdown test" in record.message for record in caplog.records
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def test_fire_and_forget_other_thread_error(self, caplog):
|
|
118
|
+
"""Test fire_and_forget logs non-shutdown thread errors."""
|
|
119
|
+
|
|
120
|
+
async def test_coro():
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
# Mock the scenario where we get a different error
|
|
124
|
+
with patch("asyncio.get_running_loop") as mock_get_loop:
|
|
125
|
+
mock_get_loop.side_effect = RuntimeError("no running event loop")
|
|
126
|
+
|
|
127
|
+
with patch("threading.Thread") as mock_thread:
|
|
128
|
+
mock_thread.side_effect = RuntimeError("Some other error")
|
|
129
|
+
|
|
130
|
+
# Patch the logger to capture the debug call
|
|
131
|
+
with patch("hud.utils.async_utils.logger") as mock_logger:
|
|
132
|
+
fire_and_forget(test_coro(), description="error test")
|
|
133
|
+
|
|
134
|
+
# Check that error was logged with correct format
|
|
135
|
+
mock_logger.debug.assert_called_once_with(
|
|
136
|
+
"Could not %s - no event loop available: %s",
|
|
137
|
+
"error test",
|
|
138
|
+
mock_thread.side_effect,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
@pytest.mark.asyncio
|
|
142
|
+
async def test_fire_and_forget_cancelled_task(self):
|
|
143
|
+
"""Test fire_and_forget handles cancelled tasks."""
|
|
144
|
+
|
|
145
|
+
cancel_event = asyncio.Event()
|
|
146
|
+
|
|
147
|
+
async def long_running_coro():
|
|
148
|
+
await cancel_event.wait()
|
|
149
|
+
|
|
150
|
+
# Get the current loop
|
|
151
|
+
loop = asyncio.get_running_loop()
|
|
152
|
+
|
|
153
|
+
# Patch create_task to capture the task
|
|
154
|
+
created_task = None
|
|
155
|
+
original_create_task = loop.create_task
|
|
156
|
+
|
|
157
|
+
def mock_create_task(coro):
|
|
158
|
+
nonlocal created_task
|
|
159
|
+
created_task = original_create_task(coro)
|
|
160
|
+
return created_task
|
|
161
|
+
|
|
162
|
+
with patch.object(loop, "create_task", side_effect=mock_create_task):
|
|
163
|
+
fire_and_forget(long_running_coro(), description="cancel test")
|
|
164
|
+
|
|
165
|
+
# Give it a moment to start
|
|
166
|
+
await asyncio.sleep(0.01)
|
|
167
|
+
|
|
168
|
+
# Cancel the task
|
|
169
|
+
assert created_task is not None
|
|
170
|
+
created_task.cancel()
|
|
171
|
+
|
|
172
|
+
# This should not raise any exceptions
|
|
173
|
+
await asyncio.sleep(0.01)
|
hud/utils/tests/test_init.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"""Test utils package imports."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_utils_imports():
|
|
7
|
-
"""Test that utils package can be imported."""
|
|
8
|
-
import hud.utils
|
|
9
|
-
|
|
10
|
-
# Check that the module exists
|
|
11
|
-
assert hud.utils is not None
|
|
12
|
-
|
|
13
|
-
# Try importing submodules
|
|
14
|
-
from hud.utils import progress, telemetry
|
|
15
|
-
|
|
16
|
-
assert progress is not None
|
|
17
|
-
assert telemetry is not None
|
|
1
|
+
"""Test utils package imports."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_utils_imports():
|
|
7
|
+
"""Test that utils package can be imported."""
|
|
8
|
+
import hud.utils
|
|
9
|
+
|
|
10
|
+
# Check that the module exists
|
|
11
|
+
assert hud.utils is not None
|
|
12
|
+
|
|
13
|
+
# Try importing submodules
|
|
14
|
+
from hud.utils import progress, telemetry
|
|
15
|
+
|
|
16
|
+
assert progress is not None
|
|
17
|
+
assert telemetry is not None
|