Barry Digby
03/16/2026, 4:13 PMBarry Digby
03/16/2026, 4:14 PM# site_etl.py
import pandas as pd
from hamilton.function_modifiers import config
def filter(product_df: pd.DataFrame, site: str) -> pd.DataFrame:
return product_df[product_df['Site'] == site]
@config.when(sort_table=True)
def sort_table__true(filter: pd.DataFrame) -> pd.DataFrame:
sort_df = filter.sort_values(by=['Date', 'Batch'], ascending=[True, True])
return sort_df
@config.when(sort_table=False)
def sort_table__false(filter: pd.DataFrame) -> pd.DataFrame:
return filter
@config.when(remove_duplicates=True)
def remove_table_dups__true(sort_table: pd.DataFrame, site: str) -> dict[str, pd.DataFrame]:
rm_dup_df = sort_table.drop_duplicates(['Batch'])
return {site: rm_dup_df}
@config.when(remove_duplicates=False)
def remove_table_dups__false(sort_table: pd.DataFrame, site: str) -> dict[str, pd.DataFrame]:
return {site: sort_table}
# subdag_etl.py
import site_etl
import pandas as pd
from hamilton.function_modifiers import subdag, source
@subdag(
site_etl,
inputs={'product_df': source('product_df')}
)
def etl_pipeline(remove_table_dups: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
return remove_table_dups
# main.py
import subdag_etl
import pandas as pd
from hamilton import driver
data = [
("2021-01-31", "LT100", "US"),
("2022-02-21", "LT100", "US"),
("2022-01-02", "MT300", "US"),
("2020-04-14", "MT300", "US"),
("2020-09-10", "PX099", "CA"),
("2021-10-14", "PX099", "CA"),
("2014-12-10", "PX099", "CA")
]
product_df = pd.DataFrame(data, columns=['Date', 'Batch', 'Site'])
product_df['Date'] = pd.to_datetime(product_df['Date'], format="%Y-%m-%d").dt.normalize()
config = [
{
'site': 'US',
'sort_table': True,
'remove_duplicates': True
},
{
'site': 'CA',
'sort_table': False,
'remove_duplicates': False
}
]
for row in config:
dr = (
driver.Builder()
.with_modules(subdag_etl)
.with_config(config=row)
.build()
)
result = dr.execute(['etl_pipeline'], inputs={'product_df': product_df})
print(result['etl_pipeline'])
{'US': Date Batch Site
3 2020-04-14 MT300 US
0 2021-01-31 LT100 US}
{'CA': Date Batch Site
4 2020-09-10 PX099 CA
5 2021-10-14 PX099 CA
6 2014-12-10 PX099 CA}Barry Digby
03/16/2026, 4:24 PMyield() and Parallelizable), 3) Run n functions in parallel and 4) Collect
If I adopted the same strategy for the above, I would be removing the subdag_etl.py file and thus the @subdag() decorator entirely.
Is there an elegant way to go about this?Stefan Krawczyk
03/16/2026, 6:11 PM```data = [
("2021-01-31", "LT100", "US"),
("2022-02-21", "LT100", "US"),
("2022-01-02", "MT300", "US"),
("2020-04-14", "MT300", "US"),
("2020-09-10", "PX099", "CA"),
("2021-10-14", "PX099", "CA"),
("2014-12-10", "PX099", "CA")
]```?
Stefan Krawczyk
03/16/2026, 6:12 PMParallelizable is basically a way to "book end" a DAG. So you can place
@subdag(
site_etl,
inputs={'product_df': source('product_df')}
)
def etl_pipeline(remove_table_dups: pd.DataFrame) -> pd.DataFrame:
return remove_table_dups
in the middle of parallelizable & collect, and you should get what you want?Stefan Krawczyk
03/16/2026, 6:26 PMBarry Digby
03/17/2026, 12:35 PMmain.py I iterate over the config parameters, passing them to .with_config() and execute the subdag sequentially in a for-loop.
Works fine but I would like to run this in parallel - somewhere along the line I need either .with_config() or config={} to accept a Parallelizable version of the config parameters.
I think this deviates from the examples linked - they seem to be running input sources in parallel. In my case, product_df remains fixed.
# main.py --
# remove the for loop, pass config as dr.execute input for Parallelizable
dr = (
driver.Builder()
.enable_dynamic_execution(allow_experimental_mode=True)
.with_remote_executor(executors.MultiThreadingExecutor(max_tasks=4))
.with_modules(subdag_etl)
.build()
)
result = dr.execute(['etl_pipeline'], inputs={'product_df': product_df, 'config': config})
# subdag_etl.py --
def parse_config(config: list) -> Parallelizable[dict]:
for row in config:
yield row
@subdag(
site_etl,
inputs={'product_df': source('product_df')},
config={?}
)
def etl_pipeline(remove_table_dups: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
return remove_table_dups
def collect_results(etl_pipeline: Collect[dict[str, pd.DataFrame]]) -> dict:
return etl_pipeline
I'm unsure how to pass the Parallelizable[dict] to the @subdag(config=...) - hope this makes sense, thank you for taking the time to reviewStefan Krawczyk
03/17/2026, 3:04 PMStefan Krawczyk
03/17/2026, 3:04 PMStefan Krawczyk
03/17/2026, 3:05 PMStefan Krawczyk
03/17/2026, 3:05 PMBarry Digby
03/18/2026, 9:36 AM