Debug Colander Output Bugs: Stray None, Nested Lists, and Empty List Versus Null
StripeSoftware Engineer · Technical ScreenLast updated
A debugging exercise in the Colander validation library, where recorded results contain stray None values, nested lists that should be flat, and an inconsistent mix of empty lists and null. It tests tracing bad output back to the code that produces it, safe list flattening, None handling and choosing one canonical empty value.
You are given a working copy of Colander, an open-source Python library that validates and deserializes data against a declared schema, into which bugs have been planted. Assume the exercise runs the library over a fixed set of inputs, records the results in an output file, and compares that file with the expected results. Three discrepancies are reported:
Nonevalues appear in the recorded results, and the expected results contain none.- Results that should be flat lists of strings are recorded as nested lists, such as
[s1, [s2, [s3, s4], s5], s6, s7]where[s1, s2, s3, s4, s5, s6, s7]is expected. - Equivalent cases are recorded inconsistently: some record an empty list
[]and othersnull. Every such case must produce the same value.
For each discrepancy, find the code in the library that produces it, fix it there, and add a regression test.
Constraints and Clarifications
- The expected results are the specification: do not edit them, and do not post-process the output file.
- Order within a recorded list matters.
- Colander represents "no value" with its own marker,
colander.null, which is distinct fromNone.
Clarifying Questions
- Is
nullin the recorded results Colander'snullmarker, or JSONnullwritten by the recorder? - Which representation is canonical for "nothing to report": the empty list or
null? - How deep can the nesting go, and can
Noneappear inside nested lists? - Can a recorded list legitimately contain other falsy values, such as an empty string?
Part 1 — Stray None values
Find where a None enters a recorded result and remove it at its source.
What This Part Should Cover
- How the
Nonewas traced to the function that produces it - The fix, and why it belongs there rather than in the code that writes the file
- Not discarding legitimate falsy values
Part 2 — Nested lists
Make every result that should be a flat list come out flat, in its original order, however deeply the lists are nested.
What This Part Should Cover
- Where the nesting is created and where it is removed
- A flattening routine that preserves order and handles any depth
- Which types are expanded and which are kept as leaves
Part 3 — [] versus null
Make equivalent cases record the same value when there is nothing to report.
What This Part Should Cover
- The two code paths and the chosen canonical value
- An empty check that cannot match other values by accident
- Side effects of the change on code that already treats
nullspecially
Hints
Hint 1
Walk back from one None: Take a single recorded `None` and find the first function whose return value already contains it. Ask what that function should return when it has nothing to contribute.
Hint 2
Leaves and containers: Decide exactly which values count as containers to expand and which are leaves to keep. Try your rule on a string before you trust it.
Hint 3
Two roads to nothing: Find the two code paths that produce these cases and pick one representation of "nothing" for both to return. Then check what that representation means to the rest of the library.
Follow-up questions
- The nesting reaches thousands of levels. Does your flattening still work?
- A caller needs to tell "an empty list was supplied" apart from "no value". How do you reconcile that with Part 3?
- The same defects come from several methods. Do you fix every producer, or normalize in one place?
- What test would stop all three defects from coming back?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.