Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/strands/tools/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

logger = logging.getLogger(__name__)

_TOOL_MODULE_PREFIX = "_strands_tool_"


def load_tool_from_string(tool_string: str) -> List[AgentTool]:
"""Load tools follows strands supported input string formats.
Expand Down Expand Up @@ -65,7 +67,7 @@ def load_tools_from_file_path(tool_path: str) -> List[AgentTool]:

module = importlib.util.module_from_spec(spec)
# Load, or re-load, the module
sys.modules[module_name] = module
sys.modules[f"{_TOOL_MODULE_PREFIX}{module_name}"] = module
# Execute the module to run any top level code
spec.loader.exec_module(module)

Expand Down Expand Up @@ -200,7 +202,7 @@ def load_python_tools(tool_path: str, tool_name: str) -> List[AgentTool]:
raise ImportError(f"No loader available for {tool_name}")

module = importlib.util.module_from_spec(spec)
sys.modules[tool_name] = module
sys.modules[f"{_TOOL_MODULE_PREFIX}{tool_name}"] = module
spec.loader.exec_module(module)

# Collect function-based tools decorated with @tool
Expand Down
29 changes: 28 additions & 1 deletion tests/strands/tools/test_loader.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import os
import re
import sys
import tempfile
import textwrap

import pytest

from strands.tools.decorator import DecoratedFunctionTool
from strands.tools.loader import ToolLoader, load_tools_from_file_path
from strands.tools.loader import _TOOL_MODULE_PREFIX, ToolLoader, load_tools_from_file_path
from strands.tools.tools import PythonAgentTool


Expand Down Expand Up @@ -317,3 +318,29 @@ def test_load_tools_from_file_path_module_spec_missing():
with tempfile.NamedTemporaryFile() as f:
with pytest.raises(ImportError, match=f"Could not create spec for {os.path.basename(f.name)}"):
load_tools_from_file_path(f.name)


def test_tool_module_prefix_prevents_collision():
"""Test that tool modules are loaded with prefix to prevent sys.modules collisions."""
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
f.write(
textwrap.dedent("""
import strands

@strands.tools.tool
def test_tool():
return "test"
""")
)
f.flush()

# Load the tool
tools = load_tools_from_file_path(f.name)

# Check that module is in sys.modules with prefix
module_name = os.path.basename(f.name).split(".")[0]
prefixed_name = f"{_TOOL_MODULE_PREFIX}{module_name}"

assert prefixed_name in sys.modules
assert len(tools) == 1
assert tools[0].tool_name == "test_tool"
Loading