Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
A validation spec names a rule per column. Rather than a branch ladder inside the row loop, you build one small function per column up front and run them — which is how a spec-driven validator is written and how it stays readable as the spec grows.
Write build_and_run_validators(spec, rows). It returns the columns it built validators for and every failure it found.
Function to write
build_and_run_validators(spec: dict, rows: list[dict]) -> dictA dict with the `columns` validated, in sorted order, and every `failure` found.
How to approach it
Run the failing case with the starter and read which columns are named.
Sample cases
+ 2 held back until you submit
a row that passes both rules
Both validators pass, which only happens if each is checking its own column.
Input
Argument 1
{
'amount': 'numeric',
'buyer': 'required'
}Argument 2
| amount | buyer |
|---|---|
| 12 | Amir |
Returns
{
'columns': [
'amount',
'buyer'
],
'failures': []
}only the amount is wrong
One failure, and it must be the amount. A validator that is secretly checking buyer reports nothing here.
Input
Argument 1
{
'amount': 'numeric',
'buyer': 'required'
}Argument 2
| amount | buyer |
|---|---|
| 12 | Amir |
Returns
{
'columns': [
'amount',
'buyer'
],
'failures': [
{
'row': 0,
'column': 'amount',
'rule': 'numeric'
}
]
}a row that fails both rules
Two failures from one row, each naming its own column and rule.
Input
Argument 1
{
'amount': 'numeric',
'buyer': 'required'
}Argument 2
| amount | buyer |
|---|---|
| 12 |
Returns
{
'columns': [
'amount',
'buyer'
],
'failures': [
{
'row': 0,
'column': 'amount',
'rule': 'numeric'
},
{
'row': 0,
'column': 'buyer',
'rule': 'required'
}
]
}no rows
The validators are still built and reported, and nothing is run over them.
Input
Argument 1
{
'amount': 'numeric',
'buyer': 'required'
}Argument 2
[] (empty list)
Returns
{
'columns': [
'amount',
'buyer'
],
'failures': []
}Constraints
spec, in sorted column order.required fails on None and on an empty string; numeric requires an int or a float; any other rule passes.True is not a number here. Python says isinstance(True, int) is True, and a boolean in an amount column is a bug rather than a measurement.{'columns': [...], 'failures': [...]} with each failure as {'row': <index>, 'column': ..., 'rule': ...}.Worked example
The spec asks for amount to be numeric and buyer to be required. A row arrives with amount as the string 12 and a perfectly good buyer, Amir. Exactly one rule must fail, and it must be the amount.
The starter reports nothing at all. Its validators close over the loop variables column and rule, which are read when the check runs, not when it was defined — and by then the loop has finished, so both validators are checking buyer for required. Both pass, the row is declared clean, and the label on each check still says amount and buyer, which is what makes it so hard to see.
What this tests
Closure late binding: a closure captures the variable, not the value it had at definition. Building functions in a loop is the standard way to meet it, and the symptom — every generated function behaving like the last — reads as a copy-paste bug rather than as a language rule.
build_and_run_validators(spec: dict, rows: list[dict]) -> dictSubmit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.