Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Getting Started

This guide introduces the core concepts of dags and shows you how to get started.

Core Concept

dags works by analyzing function signatures to build a dependency graph. If a function has a parameter named x, and another function is named x, then the first function depends on the second.

def x():
    return 1
def y(x):
    return x + 1

y depends on x because its parameter is named x. Visually, we can represent this as:

Basic Usage

Creating a Combined Function

We start by defining individual functions that represent parts of a simple tax model. In a real project, each function might live in its own module — what matters is that dags can discover dependencies from parameter names alone.

import dags


def income():
    return 50000


def tax_rate():
    return 0.3


def tax(income, tax_rate):
    return income * tax_rate


def net_income(income, tax):
    return income - tax


all_functions = {
    "income": income,
    "tax_rate": tax_rate,
    "tax": tax,
    "net_income": net_income,
}

As before, we can visualize the dependencies:

We have collected all four functions into a single dictionary. This is the typical starting point: in a real economic model you might have dozens of functions computing income, taxes, transfers, utility, etc.

Now we use concatenate_functions to combine them into one function that we can call. We pass:

  • functions — the dictionary of all available functions

  • targets — which outputs we actually want (here: net_income and tax)

  • return_type="dict" — return results as a dictionary (the default is a tuple)

combined_no_inputs = dags.concatenate_functions(
    functions=all_functions,
    targets=["net_income", "tax"],
    return_type="dict",
)

Since every input (income, tax_rate) is already provided by a function in the dictionary, the combined function needs no arguments at all:

combined_no_inputs()
{'net_income': 35000.0, 'tax': 15000.0}

Providing External Inputs

In the example above, income and tax_rate were hard-wired as functions that always return the same value. That is rarely useful in practice — typically these values come from data or vary across scenarios.

A more realistic setup is to include only the functions that actually compute something, and leave the raw inputs to be provided when calling the combined function:

tax_and_net_income = {"tax": tax, "net_income": net_income}

When we combine just these two functions, dags notices that income and tax_rate are not produced by any function in the dictionary. It turns them into required arguments of the combined function.

combined_with_inputs = dags.concatenate_functions(
    functions=tax_and_net_income,
    targets=["net_income", "tax"],
    return_type="dict",
)

Calling without arguments raises an InvalidFunctionArgumentsError, telling you exactly which inputs are missing:

combined_with_inputs()
---------------------------------------------------------------------------
InvalidFunctionArgumentsError             Traceback (most recent call last)
Cell In[8], line 1
----> 1 combined_with_inputs()

File ~/checkouts/readthedocs.org/user_builds/dags/checkouts/latest/src/dags/output.py:102, in dict_output.<locals>.decorator_dict_output.<locals>.wrapper_dict_output(*args, **kwargs)
    100 @functools.wraps(func)
    101 def wrapper_dict_output(*args: P.args, **kwargs: P.kwargs) -> dict[str, T]:
--> 102     raw = func(*args, **kwargs)
    103     return dict(zip(keys, raw, strict=True))

File ~/checkouts/readthedocs.org/user_builds/dags/checkouts/latest/src/dags/signature.py:138, in with_signature.<locals>.decorator_with_signature.<locals>.wrapper_with_signature(*args, **kwargs)
    134     _fail_if_duplicated_arguments(present_args, present_kwargs, funcname)
    135     _fail_if_invalid_keyword_arguments(
    136         present_kwargs, valid_kwargs, funcname
    137     )
--> 138     _fail_if_missing_arguments(
    139         present_args, present_kwargs, valid_kwargs, funcname
    140     )
    141 return func(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/dags/checkouts/latest/src/dags/signature.py:219, in _fail_if_missing_arguments(present_args, present_kwargs, required_args, funcname)
    217 missing_str = ", ".join(sorted(missing))
    218 msg = f"{funcname} is missing required argument{s}: {missing_str}"
--> 219 raise InvalidFunctionArgumentsError(msg)

InvalidFunctionArgumentsError: The concatenated function is missing required arguments: income, tax_rate

We provide the missing inputs as keyword arguments:

combined_with_inputs(income=50000, tax_rate=0.3)
{'net_income': 35000.0, 'tax': 15000.0}

Return Types

By default, concatenate_functions returns results as a tuple (in the order of targets). We used return_type="dict" above to get a labeled dictionary instead, which is usually more convenient:

combined_tuple = dags.concatenate_functions(
    functions=all_functions,
    targets=["net_income", "tax"],
)

combined_tuple()
(35000.0, 15000.0)

Inspecting the DAG

Under the hood, dags builds a dependency graph to figure out which functions to run and in what order. You can inspect this graph directly using create_dag, which returns a networkx DiGraph:

dag = dags.create_dag(
    functions=all_functions,
    targets=["net_income"],
)
list(dag.nodes())
['income', 'tax_rate', 'tax', 'net_income']
list(dag.edges())
[('income', 'tax'), ('income', 'net_income'), ('tax_rate', 'tax'), ('tax', 'net_income')]

Finding Dependencies

get_ancestors is useful for understanding which functions (and inputs) contribute to a given target. This can help you debug unexpected results or understand a large model’s structure:

dags.get_ancestors(
    functions=all_functions,
    targets=["net_income"],
)
{'income', 'tax', 'tax_rate'}

Inspecting Functions

dags provides utility functions for working with function signatures. These are handy when you need to programmatically inspect or modify functions.

Getting Function Arguments

get_free_arguments returns the parameter names of a function (excluding those which have been partialed already and those with defaults):

def my_func(a, b, c=1):
    return a + b + c


dags.get_free_arguments(my_func)
['a', 'b', 'c']

Getting Type Annotations

get_annotations returns type annotations:

def my_func(a: int, b: float) -> float:
    return a + b


dags.get_annotations(my_func)
{'return': float, 'a': int, 'b': float}

Renaming Arguments

When integrating functions from different sources — say, a tax module that uses gross_income and a benefits module that uses total_income for the same quantity — you need to align parameter names so dags can connect them. rename_arguments creates a wrapper with renamed parameters:

def original(x, y):
    return x + y


renamed = dags.rename_arguments(original, mapper={"x": "a", "y": "b"})

dags.get_free_arguments(renamed)
['a', 'b']

Next Steps