Address maintainer review: reload support, type annotation, test imports

This commit is contained in:
dgenio 2026-03-01 07:49:27 +00:00 committed by Jeremiah Lowin
commit 9d635bc026
3 changed files with 40 additions and 40 deletions

View file

@ -481,8 +481,6 @@ async def run(
ignored_options.append("--port")
if path:
ignored_options.append("--path")
if reload:
ignored_options.append("--reload")
if ignored_options:
logger.warning(
f"Options {', '.join(ignored_options)} are ignored in module mode "
@ -506,6 +504,25 @@ async def run(
if env.build_command(test_cmd) != test_cmd:
env_builder = env.build_command
if reload:
# Build a fastmcp run command for the reload watcher to restart
reload_cmd = ["fastmcp", "run", server_spec, "--module", "--no-reload"]
if log_level:
reload_cmd.extend(["--log-level", log_level])
if no_banner:
reload_cmd.append("--no-banner")
if env_builder is not None:
reload_cmd.append("--skip-env")
if server_args:
reload_cmd.append("--")
reload_cmd.extend(server_args)
if env_builder is not None:
reload_cmd = env_builder(reload_cmd)
await run_module.run_with_reload(
reload_cmd, reload_dirs=reload_dir, is_stdio=True
)
return
run_module.run_module_command(
server_spec,
env_command_builder=env_builder,

View file

@ -8,6 +8,7 @@ import re
import signal
import subprocess
import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any, Literal
@ -259,7 +260,7 @@ async def run_command(
def run_module_command(
module_name: str,
*,
env_command_builder: Any | None = None,
env_command_builder: Callable[[list[str]], list[str]] | None = None,
extra_args: list[str] | None = None,
) -> None:
"""Run a Python module directly using ``python -m <module>``.

View file

@ -1,13 +1,18 @@
import inspect
import json
import subprocess
import sys
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from pydantic import ValidationError
from fastmcp.cli.cli import inspector, run
from fastmcp.cli.run import (
create_mcp_config_server,
is_url,
run_module_command,
)
from fastmcp.client.client import Client
from fastmcp.client.transports import FastMCPTransport
@ -702,10 +707,6 @@ class TestRunModuleCommand:
def test_runs_python_m_module(self):
"""Test that run_module_command invokes python -m <module>."""
from unittest.mock import MagicMock, patch
from fastmcp.cli.run import run_module_command
mock_result = MagicMock()
mock_result.returncode = 0
@ -725,10 +726,6 @@ class TestRunModuleCommand:
def test_forwards_extra_args(self):
"""Test that extra arguments are forwarded after the module name."""
from unittest.mock import MagicMock, patch
from fastmcp.cli.run import run_module_command
mock_result = MagicMock()
mock_result.returncode = 0
@ -746,10 +743,6 @@ class TestRunModuleCommand:
def test_uses_env_command_builder(self):
"""Test that env_command_builder wraps the command."""
from unittest.mock import MagicMock, patch
from fastmcp.cli.run import run_module_command
mock_result = MagicMock()
mock_result.returncode = 0
@ -774,11 +767,6 @@ class TestRunModuleCommand:
def test_exits_with_subprocess_error_code(self):
"""Test that non-zero exit codes from the module are propagated."""
import subprocess
from unittest.mock import patch
from fastmcp.cli.run import run_module_command
with (
patch(
"fastmcp.cli.run.subprocess.run",
@ -792,11 +780,6 @@ class TestRunModuleCommand:
def test_no_env_builder_runs_plain_python(self):
"""Test that without env_command_builder, plain python is used."""
import sys
from unittest.mock import MagicMock, patch
from fastmcp.cli.run import run_module_command
mock_result = MagicMock()
mock_result.returncode = 0
@ -819,8 +802,6 @@ class TestRunModuleMode:
async def test_run_module_mode_requires_server_spec(self):
"""Test that module mode exits with error when server_spec is None."""
from fastmcp.cli.cli import run
with pytest.raises(SystemExit) as exc_info:
await run(None, module=True)
@ -828,10 +809,6 @@ class TestRunModuleMode:
async def test_run_module_mode_warns_ignored_options(self, caplog):
"""Test that ignored options produce a warning in module mode."""
from unittest.mock import MagicMock, patch
from fastmcp.cli.cli import run
mock_result = MagicMock()
mock_result.returncode = 0
@ -846,17 +823,12 @@ class TestRunModuleMode:
transport="sse",
host="0.0.0.0",
port=8080,
reload=True,
)
assert any("ignored in module mode" in r.message for r in caplog.records)
async def test_run_module_mode_delegates_to_run_module_command(self):
"""Test that module mode calls run_module_command with correct args."""
from unittest.mock import MagicMock, patch
from fastmcp.cli.cli import run
mock_result = MagicMock()
mock_result.returncode = 0
@ -872,16 +844,26 @@ class TestRunModuleMode:
assert "-m" in cmd
assert "my_module" in cmd
async def test_run_module_mode_with_reload(self):
"""Test that --reload in module mode delegates to run_with_reload."""
with patch(
"fastmcp.cli.run.run_with_reload", new_callable=AsyncMock
) as mock_reload:
await run("my_module", module=True, reload=True, skip_env=True)
mock_reload.assert_called_once()
cmd = mock_reload.call_args[0][0]
assert "fastmcp" in cmd
assert "--module" in cmd
assert "--no-reload" in cmd
assert "my_module" in cmd
class TestInspectorModuleMode:
"""Test the inspector command's module-mode handling."""
async def test_inspector_module_mode_skips_load_server(self):
"""Test that inspector with module=True skips load_server() and forwards --module."""
from unittest.mock import AsyncMock, MagicMock, patch
from fastmcp.cli.cli import inspector
mock_config = MagicMock()
mock_config.deployment.port = 8080
mock_config.environment.build_command = lambda cmd: cmd