-
Notifications
You must be signed in to change notification settings - Fork 174
Description
is_submodule() in hamilton/graph_utils.py (line 24) uses a substring match (in) instead of a proper prefix match, causing imported functions to be incorrectly included as DAG nodes when the user module's name is a substring of the imported function's module path.
Current behavior
is_submodule() uses parent.__name__ in child.__name__ (substring check). When a user module's name appears anywhere within an imported function's fully-qualified module path, find_functions() incorrectly treats that imported function as part of the user's DAG.
For example, a module named modifiers that imports source and value from hamilton.function_modifiers produces 3 nodes instead of 1:
my_func (from modifiers)
source (from hamilton.function_modifiers.dependencies)
value (from hamilton.function_modifiers.dependencies)
This happens because "modifiers" in "hamilton.function_modifiers.dependencies" evaluates to True.
The buggy code at hamilton/graph_utils.py:24:
def is_submodule(child: ModuleType, parent: ModuleType):
return parent.__name__ in child.__name__Stack Traces
N/A — no exception is raised; the bug silently adds spurious nodes.
Screenshots
N/A
Steps to replicate behavior
- Create a user module named
modifiers.py:from hamilton.function_modifiers import source, value def my_func(input_data: int) -> int: return input_data * 2
- Inspect what
find_functionsdiscovers:import modifiers from hamilton.graph_utils import find_functions functions = find_functions(modifiers) for name, fn in functions: print(f'{name} (from {fn.__module__})')
- Observe that
sourceandvalueare incorrectly included as DAG nodes.
This affects any user module whose name is a substring of an imported function's module path (e.g. function, ton, ilton, etc.).
Library & System Information
- Python version: 3.10+
- Hamilton version: 1.89.0 (current main branch)
- OS: all (bug is in pure Python logic)
Expected behavior
Only my_func should appear as a DAG node. Imported functions like source and value should be excluded because hamilton.function_modifiers.dependencies is not a submodule of modifiers.
A correct implementation would use a prefix check instead of a substring check:
def is_submodule(child: ModuleType, parent: ModuleType):
return child.__name__ == parent.__name__ or child.__name__.startswith(parent.__name__ + ".")Additional context
The bug exists because in tests for substring containment, not hierarchical module relationships. The fix is a one-line change replacing the in operator with a startswith prefix check plus an equality check for the same-module case.