fix: restore Python 3.12 startup and release 2.0.1

This commit is contained in:
chenmingxuan committed 2026-10-22 19:22:13 +08:00
1 parent bf6247703d
commit b584dae80a
19 files changed
+223 -20

No files matched your search

+15 -2
View File
@@ -15,6 +15,7 @@
"""Unit tests for Artemis System Diagnostics & Readiness Engine."""
import asyncio
from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock
import pytest
@@ -173,7 +174,13 @@ async def test_dashboard_reuses_recent_confirmed_state_on_one_timeout(monkeypatc
@pytest.mark.asyncio
async def test_readiness_engine_coalesces_concurrent_full_scans():
@pytest.mark.parametrize("same_clock_tick", [False, True])
async def test_readiness_engine_coalesces_concurrent_full_scans(same_clock_tick, monkeypatch):
if same_clock_tick:
monkeypatch.setattr(
"artemis.core.diagnostics.engine.time",
SimpleNamespace(monotonic=lambda: 100.0, time=lambda: 100.0),
)
engine = ReadinessEngine()
result = ProbeResult(
id="test_probe",
@@ -200,7 +207,13 @@ async def test_readiness_engine_coalesces_concurrent_full_scans():
@pytest.mark.asyncio
async def test_readiness_engine_reuses_cache_until_forced():
@pytest.mark.parametrize("same_clock_tick", [False, True])
async def test_readiness_engine_reuses_cache_until_forced(same_clock_tick, monkeypatch):
if same_clock_tick:
monkeypatch.setattr(
"artemis.core.diagnostics.engine.time",
SimpleNamespace(monotonic=lambda: 100.0, time=lambda: 100.0),
)
engine = ReadinessEngine()
result = ProbeResult(
id="test_probe",
+57
View File
@@ -0,0 +1,57 @@
from mcp.types import ToolAnnotations
import pytest
from artemis.mcp.tool_server import ArtemisFastMCP
@pytest.mark.asyncio
@pytest.mark.parametrize("registration", ["decorator", "add_tool"])
@pytest.mark.parametrize(
("docstring", "expected"),
[
("Summary.\n\n Details.\n Nested.\n ", "Summary.\n\nDetails.\n Nested.\n"),
("Summary.\n\nDetails.\n Nested.\n", "Summary.\n\nDetails.\n Nested.\n"),
("One line.", "One line."),
],
)
async def test_tool_descriptions_match_across_docstring_formats(registration, docstring, expected):
server = ArtemisFastMCP("description-test")
def probe(count: int = 1) -> str:
return str(count)
probe.__doc__ = docstring
if registration == "decorator":
assert server.tool()(probe) is probe
else:
server.add_tool(probe)
(tool,) = await server.list_tools()
assert tool.description == expected
assert tool.inputSchema["properties"]["count"]["type"] == "integer"
assert probe.__doc__ == docstring
@pytest.mark.asyncio
async def test_explicit_tool_metadata_is_preserved():
server = ArtemisFastMCP("description-test")
def probe() -> str:
"""The fallback docstring."""
return "ok"
description = "Explicit description.\n Keep this indentation.\n"
server.add_tool(
probe,
"renamed_probe",
"Probe title",
description,
annotations=ToolAnnotations(readOnlyHint=True),
meta={"source": "test"},
structured_output=False,
)
(tool,) = await server.list_tools()
assert tool.name == "renamed_probe"
assert tool.title == "Probe title"
assert tool.description == description
assert tool.annotations.readOnlyHint is True
assert tool.meta == {"source": "test"}
+61
View File
@@ -0,0 +1,61 @@
"""Cold imports and runtime annotations must work on the installer's Python."""
from importlib import import_module
import inspect
from pathlib import Path
import subprocess
import sys
from textwrap import dedent
from typing import get_type_hints
import pytest
@pytest.mark.parametrize(
("module_name", "class_name"),
[
("artemis.exploration.store", "ExplorationStore"),
("artemis.exploration.demonstrations", "Demonstrations"),
("apps.admin_console.services.favorites", "FavoriteService"),
],
)
def test_startup_service_annotations_do_not_shadow_builtin_collections(module_name, class_name):
service = getattr(import_module(module_name), class_name)
# Python 3.14 defers these errors until annotations are read. Evaluating them
# also detects the import failure seen immediately on Python 3.12 and 3.13.
assert get_type_hints(service.list)["return"] == list[dict]
for method in vars(service).values():
if inspect.isfunction(method):
get_type_hints(method)
def test_ui_server_cold_import_serves_empty_workspace(tmp_path, monkeypatch):
monkeypatch.setenv("ARTEMIS_APP_DIR", str(tmp_path / "app"))
monkeypatch.setenv("ARTEMIS_TRACES_DIR", str(tmp_path / "traces"))
code = dedent("""
from fastapi.testclient import TestClient
from apps.admin_console.server import app
# No lifespan: validate startup imports and HTTP routes without device
# discovery, background workers, or a running user's service.
client = TestClient(app, base_url="http://localhost")
try:
for path in ("/api/automation/favorites", "/api/explorations"):
response = client.get(path)
assert response.status_code == 200, response.text
assert response.json() == [], response.text
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert "/api/explorations" in response.json()["paths"]
finally:
client.close()
""")
result = subprocess.run(
[sys.executable, "-X", "utf8", "-c", code],
cwd=Path(__file__).resolve().parents[2],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert result.returncode == 0, result.stdout + result.stderr