Files
ttulttul-ComfyUI-Modal/tests/test_cloud_runtime_logging.py
T
Ken SimpsonandClaude Opus 5.5 cbdaf4a5c7 Emit INFO logs from all project modules inside the Modal container
Only the comfyui_modal_sync_cloud entrypoint logger had a handler in the
container. Flat-imported sibling modules (cloud_durable_invocation,
cloud_session_bridge, ...) fell through to the root WARNING handler, so
their INFO timing lines were dropped. That hid the durable-result step that
accounted for 28-90s per-item delays after execution finished.

Inside the container, give every shipped top-level module an INFO handler
that prints to the live sys.stdout at emit time (snapshot restores can swap
stdout), with propagation off so warnings are not printed twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:01:19 -07:00

100 lines
3.5 KiB
Python

"""Tests for Modal container logging of sibling project modules."""
from __future__ import annotations
import importlib
import io
import logging
from pathlib import Path
import sys
from typing import Any, Iterator
import uuid
import pytest
from conftest import PACKAGE_NAME
@pytest.fixture
def cloud_runtime_logging_module(extension_package: object) -> Any:
"""Return the cloud runtime logging helpers."""
del extension_package
return importlib.import_module(f"{PACKAGE_NAME}.cloud_runtime_logging")
@pytest.fixture
def module_logger_names() -> Iterator[list[str]]:
"""Yield unique logger names and remove their handlers afterwards."""
names = [f"test_cloud_module_{uuid.uuid4().hex}" for _ in range(2)]
yield names
for name in names:
module_logger = logging.getLogger(name)
for handler in list(module_logger.handlers):
module_logger.removeHandler(handler)
module_logger.setLevel(logging.NOTSET)
module_logger.propagate = True
def test_cloud_module_loggers_emit_info_to_current_stdout(
cloud_runtime_logging_module: Any,
module_logger_names: list[str],
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""INFO from sibling modules must reach the stdout that exists at emit time."""
cloud_runtime_logging_module.configure_cloud_module_loggers(module_logger_names)
replaced_stdout = io.StringIO()
monkeypatch.setattr(sys, "stdout", replaced_stdout)
logging.getLogger(module_logger_names[0]).info(
"Finished remote invocation completion in %.3fs", 1.5
)
output = replaced_stdout.getvalue()
assert "Finished remote invocation completion in 1.500s" in output
assert f"INFO [{module_logger_names[0]}]" in output
def test_cloud_module_loggers_are_idempotent_and_isolated(
cloud_runtime_logging_module: Any,
module_logger_names: list[str],
) -> None:
"""Repeat configuration must not duplicate output or leak to the root logger."""
module = cloud_runtime_logging_module
first = module.configure_cloud_module_loggers(module_logger_names)
second = module.configure_cloud_module_loggers(module_logger_names)
assert first == second == tuple(module_logger_names)
handlers = [logging.getLogger(name).handlers for name in module_logger_names]
assert all(len(module_handlers) == 1 for module_handlers in handlers)
assert handlers[0][0] is handlers[1][0]
for name in module_logger_names:
module_logger = logging.getLogger(name)
assert module_logger.level == logging.INFO
assert module_logger.propagate is False
def test_cloud_module_loggers_skip_the_entrypoint_logger(
cloud_runtime_logging_module: Any,
) -> None:
"""The entrypoint logger keeps its own handler and is never double-configured."""
module = cloud_runtime_logging_module
entrypoint_name = module._TARGET_LOGGER.name
configured = module.configure_cloud_module_loggers([entrypoint_name])
assert configured == ()
def test_cloud_project_module_names_lists_flat_modules(
cloud_runtime_logging_module: Any,
tmp_path: Path,
) -> None:
"""Every shipped top-level module except the package marker is configured."""
for file_name in ("cloud_durable_invocation.py", "settings.py", "__init__.py"):
(tmp_path / file_name).write_text("", encoding="utf-8")
(tmp_path / "notes.txt").write_text("", encoding="utf-8")
names = cloud_runtime_logging_module.cloud_project_module_names(tmp_path)
assert names == ("cloud_durable_invocation", "settings")