mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Fix: Add meaningful error message when graphviz binary is not installed (#826)
* Initial plan * Fix: Add meaningful error message for missing graphviz binary Co-authored-by: ekzhu <320302+ekzhu@users.noreply.github.com> * Fix CI error: Make graphviz test conditional to avoid import errors Co-authored-by: ekzhu <320302+ekzhu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ekzhu <320302+ekzhu@users.noreply.github.com> Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
367a3275d1
commit
965918b883
@@ -89,25 +89,33 @@ class WorkflowViz:
|
||||
dot_content = self.to_digraph()
|
||||
source = graphviz.Source(dot_content)
|
||||
|
||||
if filename:
|
||||
# Save to specified file
|
||||
output_path = Path(filename)
|
||||
if output_path.suffix and output_path.suffix[1:] != format:
|
||||
raise ValueError(f"File extension {output_path.suffix} doesn't match format {format}")
|
||||
try:
|
||||
if filename:
|
||||
# Save to specified file
|
||||
output_path = Path(filename)
|
||||
if output_path.suffix and output_path.suffix[1:] != format:
|
||||
raise ValueError(f"File extension {output_path.suffix} doesn't match format {format}")
|
||||
|
||||
# Remove extension if present since graphviz.render() adds it
|
||||
base_name = str(output_path.with_suffix(""))
|
||||
source.render(base_name, format=format, cleanup=True)
|
||||
|
||||
# Return the actual filename with extension
|
||||
return f"{base_name}.{format}"
|
||||
# Create temporary file
|
||||
with tempfile.NamedTemporaryFile(suffix=f".{format}", delete=False) as temp_file:
|
||||
temp_path = Path(temp_file.name)
|
||||
base_name = str(temp_path.with_suffix(""))
|
||||
|
||||
# Remove extension if present since graphviz.render() adds it
|
||||
base_name = str(output_path.with_suffix(""))
|
||||
source.render(base_name, format=format, cleanup=True)
|
||||
|
||||
# Return the actual filename with extension
|
||||
return f"{base_name}.{format}"
|
||||
# Create temporary file
|
||||
with tempfile.NamedTemporaryFile(suffix=f".{format}", delete=False) as temp_file:
|
||||
temp_path = Path(temp_file.name)
|
||||
base_name = str(temp_path.with_suffix(""))
|
||||
|
||||
source.render(base_name, format=format, cleanup=True)
|
||||
return f"{base_name}.{format}"
|
||||
except graphviz.backend.execute.ExecutableNotFound as e:
|
||||
raise ImportError(
|
||||
"The graphviz executables are not found. The graphviz Python package is installed, but the "
|
||||
"graphviz executables (dot, neato, etc.) are not available on your system's PATH. "
|
||||
"Install graphviz executables: sudo apt-get install graphviz on Debian/Ubuntu, "
|
||||
"brew install graphviz on macOS, or download from https://graphviz.org/download/ for other platforms."
|
||||
) from e
|
||||
|
||||
def save_svg(self, filename: str) -> str:
|
||||
"""Convenience method to save as SVG.
|
||||
|
||||
@@ -186,6 +186,34 @@ def test_workflow_viz_unsupported_format():
|
||||
viz.export(format="invalid") # type: ignore
|
||||
|
||||
|
||||
def test_workflow_viz_graphviz_binary_not_found():
|
||||
"""Test that missing graphviz binary raises ImportError with helpful message."""
|
||||
import unittest.mock
|
||||
|
||||
# Skip test if graphviz package is not available
|
||||
pytest.importorskip("graphviz")
|
||||
|
||||
executor1 = MockExecutor(id="executor1")
|
||||
executor2 = MockExecutor(id="executor2")
|
||||
|
||||
workflow = WorkflowBuilder().add_edge(executor1, executor2).set_start_executor(executor1).build()
|
||||
viz = WorkflowViz(workflow)
|
||||
|
||||
# Mock graphviz.Source.render to raise ExecutableNotFound
|
||||
with unittest.mock.patch("graphviz.Source") as mock_source_class:
|
||||
mock_source = unittest.mock.MagicMock()
|
||||
mock_source_class.return_value = mock_source
|
||||
|
||||
# Import the ExecutableNotFound exception for the test
|
||||
from graphviz.backend.execute import ExecutableNotFound
|
||||
|
||||
mock_source.render.side_effect = ExecutableNotFound("failed to execute PosixPath('dot')")
|
||||
|
||||
# Test that the proper ImportError is raised with helpful message
|
||||
with pytest.raises(ImportError, match="The graphviz executables are not found"):
|
||||
viz.export(format="svg")
|
||||
|
||||
|
||||
def test_workflow_viz_conditional_edge():
|
||||
"""Test that conditional edges are rendered dashed with a label."""
|
||||
start = MockExecutor(id="start")
|
||||
|
||||
Reference in New Issue
Block a user