Max Demers
04/15/2026, 5:15 PMMax Demers
04/15/2026, 5:23 PMoptional flag (or maybe allow_missing_target) would change the raised exception here to a skip (empty node list) instead.
The handling would look something like this:
from_.parquet(target="some_target", path=some_path, allow_missing_target=True)Stefan Krawczyk
04/16/2026, 1:25 AMfrom_.parquet here? or in this instance, it's not used?Stefan Krawczyk
04/16/2026, 1:26 AMElijah Ben Izzy
04/16/2026, 2:00 AMMax Demers
04/16/2026, 3:28 PMrequired_inputs:
input1: parquet_path1
input2: parquet_path2
optional_inputs:
input3: parquet_path3
this config is being applied to multiple dags.
dag_a:
def processed_data(input1, input2) -> pd.DataFrame:
...
def processed_optional(input3) -> pd.DataFrame:
....
def next_step(processed_data, processed_optional) -> pd.Dataframe:
....
dag_b is a "slim" version, and doesn't actually reference dag_input3, but uses the same inputs otherwise:
def processed_data(input1, input2) -> pd.DataFrame:
...
def next_step(processed_data) -> pd.Dataframe:
....
When we run these dags, we have
builder.with_module(dag_module)
materializers = [from_.parquet(target=target, path=path) for target, path in load_standard_materializers()]
builder.with_materializers(*materializers)
dr = builder.build()
This logic works fine if we load dag_a, but will result in build errors if we run dag_b:
dr = builder.build()
^^^^^^^^^^^^^^^
File "hamilton/driver.py", line 2189, in build
return Driver(
^^^^^^^
File "hamilton/driver.py", line 492, in __init__
raise e
File "hamilton/driver.py", line 476, in __init__
self.graph = materialization.modify_graph(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "hamilton/io/materialization.py", line 486, in modify_graph
loader_nodes.extend(loader.generate_nodes(fn_graph))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "hamilton/io/materialization.py", line 166, in generate_nodes
raise ValueError(
ValueError: Could not find node with name: input3 in function graph. Available nodes: ['input1', 'input2', Ellipsis]
At runtime, without building the dag first or doing module introspection, I'm not sure if there is an easy way to check that we don't use input3 and omit it from our materializers. Defining a unused input3 in the dag would also load unnecessary data.Max Demers
04/16/2026, 3:42 PMoptional ExtractorFactory flag that returns an empty list if there isn't a matching node target. This allows us to skip unused extractors.Stefan Krawczyk
04/17/2026, 6:18 AMimport inspect
all_params = set()
for name, fn in inspect.getmembers(dag_module, inspect.isfunction):
all_params.update(inspect.signature(fn).parameters.keys())
materializers = [from_.parquet(target=t, path=p) for t, p in load_standard_materializers() if t in all_params]
Regarding the feature contribution: Yes, we'd welcome a PR for an optional flag on from_ extractors. The change is in ExtractorFactory in hamilton/io/materialization.py — adding an optional parameter that makes generate_nodes return an empty list instead of raising when the target isn't in the graph. It's a clean, backward-compatible addition. If you open an issue we can discuss naming (optional vs allow_missing_target) — I slightly prefer optional for brevity since the context makes it clear what's optional. from_.parquet(target="input3", path=some_path, optional=True)Max Demers
04/17/2026, 1:46 PMMax Demers
04/19/2026, 9:53 PMElijah Ben Izzy
04/21/2026, 1:52 AMElijah Ben Izzy
04/21/2026, 3:11 AMMax Demers
04/21/2026, 7:00 PM