Actions are meant to be combined with thresholds and they allow you easily write text to the console or execute custom functions. As an example, when testing a column for values that should always be greater than 2 you might want some text emitted to the console when any failing test units are found. To do that, you need to pair a threshold level with an associated action (and that action could take the form of a console message).
How Actions Work
Let’s look at an example on how this works in practice. The following validation plan contains a single step (using col_vals_gt()) where the thresholds= and actions= parameters are set using Thresholds and Actions calls:
import pointblank as pbvalidation_1 = ( pb.Validate(data=pb.load_dataset(dataset="small_table")) .col_vals_gt( columns="c", value=2, thresholds=pb.Thresholds(warning=1, error=5), actions=pb.Actions(warning="WARNING: failing test found.") ) .interrogate())validation_1
WARNING: failing test found.
Pointblank Validation
2025-04-15|14:46:29
Polars
STEP
COLUMNS
VALUES
TBL
EVAL
UNITS
PASS
FAIL
W
E
C
EXT
#AAAAAA
1
col_vals_gt()
c
2
✓
13
10 0.77
3 0.23
●
○
—
The code uses thresholds=pb.Thresholds(warning=1, error=5) to set a ‘warning’ threshold of 1 and an ‘error’ threshold of 5 failing test units. The results part of the validation table shows that:
The FAIL column shows that 3 tests units have failed
The W column (short for ‘warning’) shows a filled gray circle indicating it’s reached its threshold level
The E (‘error’) column shows an open yellow circle indicating it’s below the threshold level
More importantly, the text "WARNING: failing test found." has been emitted. Here it appears above the validation table and that’s because the action is executed eagerly during interrogation (before the report has even been generated).
So, an action is executed for a particular condition (e.g., ‘warning’) within a validation step if these three things are true:
there is a threshold set for that condition (either globally, or as part of that step)
there is an associated action set for the condition (again, either set globally or within the step)
during interrogation, the threshold value for the condition was exceeded by the number or proportion of failing test units
There is a lot of flexibility for setting both thresholds and actions and everything here is considered optional. Put another way, you can set various thresholds and various actions as needed and the interrogation phase will determine whether all the requirements are met for executing an action.
Expressing Actions with a String
There are a few options in how to define the actions:
String: a message to be displayed in the console
Callable: a function to be called
List of Strings/Callables: for execution of multiple messages or functions
The actions are executed at interrogation time when the threshold level assigned to the action is exceeded by the number or proportion of failing test units. When providing a string, it will simply be printed to the console. A callable will also be executed at the time of interrogation. If providing a list of strings or callables, each item in the list will be executed in order. Such a list can contain a mix of strings and callables.
Displaying console messages may be a simple approach, but it is effective. And the strings don’t have to be static, there are templating features that can be useful for constructing strings for a variety of situations. The following placeholders are available for use:
{type}: The validation step type where the action is executed (e.g., ‘col_vals_gt’, etc.)
{level}: The threshold level where the action is executed (‘warning’, ‘error’, or ‘critical’)
{step} or {i}: The step number in the validation workflow where the action is executed
{col} or {column}: The column name where the action is executed
{val} or {value}: An associated value for the validation method
{time}: A datetime value for when the action was executed
Here’s an example where we prepare a console message with a number of value placeholders (action_str) and use it globally at Actions(critical=):
[CRITICAL: COL_VALS_GT]: Step 2 has failed validation. (2025-04-15 14:46:30.116462+00:00)
[CRITICAL: COL_VALS_GE]: Step 3 has failed validation. (2025-04-15 14:46:30.139515+00:00)
Pointblank Validation
2025-04-15|14:46:30
DuckDBWARNING0.05ERROR0.1CRITICAL0.15
STEP
COLUMNS
VALUES
TBL
EVAL
UNITS
PASS
FAIL
W
E
C
EXT
#4CA64C
1
col_vals_regex()
player_id
[A-Z]{12}\d{3}
✓
2000
2000 1.00
0 0.00
○
○
○
—
#FF3300
2
col_vals_gt()
item_revenue
0.1
✓
2000
1440 0.72
560 0.28
●
●
●
—
#FF3300
3
col_vals_ge()
session_duration
15
✓
2000
1686 0.84
314 0.16
●
●
●
—
What we get here are two messages in the console, corresponding to critical failures in steps 2 and 3. The placeholders were replaced with the correct text for the context. Note that some of the resulting text is capitalized (e.g., "CRITICAL", "COL_VALS_GT", etc.) and this is because we capitalized the placeholder text itself. Have a look at the documentation article of Actions for more details on this.
Making Actions with Callables
Aside from strings, any callable can be used as an action value. Here’s an example where we use a custom function as part of an action:
Data quality issue found (2025-04-15 14:46:30.490085).
Pointblank Validation
2025-04-15|14:46:30
DuckDBWARNING0.05ERROR0.1CRITICAL0.15
STEP
COLUMNS
VALUES
TBL
EVAL
UNITS
PASS
FAIL
W
E
C
EXT
#4CA64C
1
col_vals_regex()
player_id
[A-Z]{12}\d{3}
✓
2000
2000 1.00
0 0.00
○
○
○
—
#EBBC14
2
col_vals_gt()
item_revenue
0.05
✓
2000
1701 0.85
299 0.15
●
●
○
—
#FF3300
3
col_vals_gt()
session_duration
15
✓
2000
1675 0.84
325 0.16
●
●
●
—
In this case, the ‘warning’ action is set to call the user’s dq_issue() function. This action is only executed when the ‘warning’ threshold is exceeded in step 3. Because all three thresholds are exceeded in that step, the ‘warning’ action of executing the function occurs (resulting in a message being printed to the console).
This is an example where actions can be defined locally for an individual validation step. The global threshold setting applied to all three validation steps but the step-level action only applied to step 3. You are free to mix and match both threshold and action settings at the global level (i.e., set in the Validate call) or at the step level. The key thing to be aware of is that step-level settings of thresholds and actions take precedence.
Using get_action_metadata() to Access Metadata When Building an Action Callable
To access information about the validation step where an action was triggered, we can call get_action_metadata() in the body of a function to be used within Actions. The dictionary that’s returned by that function allows us to make more generalized actions that could react accordingly to different failure states.
In the following example, we’ll make a function called print_problem() that prints information to the console about the failure state for a validation step. In this case, the action will be applied to any threshold level being exceeded (by using Actions(default=print_problem)). And only the most severe level exceeded per step will execute print_problem() since Actions(highest_only=True) by default.
error (40) for Step 2: Exceedance of failed test units where values in `item_revenue` should have been > `0.05`.
critical (50) for Step 3: Exceedance of failed test units where values in `session_duration` should have been > `15`.
Pointblank Validation
2025-04-15|14:46:30
DuckDBWARNING0.05ERROR0.1CRITICAL0.15
STEP
COLUMNS
VALUES
TBL
EVAL
UNITS
PASS
FAIL
W
E
C
EXT
#4CA64C
1
col_vals_regex()
Expect that values in player_id should match the regular expression: [A-Z]{12}\d{3}.
player_id
[A-Z]{12}\d{3}
✓
2000
2000 1.00
0 0.00
○
○
○
—
#EBBC14
2
col_vals_gt()
Expect that values in item_revenue should be > 0.05.
item_revenue
0.05
✓
2000
1701 0.85
299 0.15
●
●
○
—
#FF3300
3
col_vals_gt()
Expect that values in session_duration should be > 15.
session_duration
15
✓
2000
1675 0.84
325 0.16
●
●
●
—
We end up seeing two messages printed for failures in Steps 2 and 3. And though those steps had more than one threshold exceeded, only the most severe level in each yielded a console message.
Alse note that we set the action in Validate(actions=) so that the action would apply to all validation steps where thresholds are exceeded. This obviated the need to set actions= at every validation step (though you can do this as a local override, even setting actions=None to disable globally set actions).
The metadata dictionary contains the following fields for a given validation step:
step: The step number.
column: The column name.
value: The value being compared (only available in certain validation steps).
type: The assertion type (e.g., "col_vals_gt", etc.).
time: The time the validation step was executed (in ISO format).
level: The severity level ("warning", "error", or "critical").
level_num: The severity level as a numeric value (30, 40, or 50).
autobrief: A localized and brief statement of the expectation for the step.
failure_text: Localized text that explains how the validation step failed.