Skip to content

fix: from_json() preserves falsy data instead of collapsing to None - #246

Open
patchwright wants to merge 2 commits into
caesar0301:masterfrom
patchwright:fix/from-json-falsy-data
Open

fix: from_json() preserves falsy data instead of collapsing to None#246
patchwright wants to merge 2 commits into
caesar0301:masterfrom
patchwright:fix/from-json-falsy-data

Conversation

@patchwright

Copy link
Copy Markdown

Problem

Tree.from_json() collapses every falsy-but-valid node data value (0, False, "", [], {}) to None, so a round-trip through to_json / from_json silently drops it.

>>> from treelib import Tree
>>> t = Tree(); t.create_node("root", "root", data=0)
>>> Tree.from_json(t.to_json(with_data=True)).all_nodes()[0].data
None      # expected 0

Confirmed on master HEAD fce5fa3.

Root cause

from_json reads each node's data as node_info.get("data") or None (treelib/tree.py:251). dict.get() already returns None for a missing key, so the or None only converts legitimate falsy values to None. The method was added in #244 (merged 2026-07-23).

Fix

Drop the redundant or None:

-                node_data = node_info.get("data") or None
+                node_data = node_info.get("data")

One line, no API change. Missing key still maps to None; a present value is returned as-is.

How to test

Added test_from_json_preserves_falsy_data, next to the to_json tests. It fails on master (AssertionError: None != 0) and passes with this change. Full suite: 211 passed.

before: from_json data = None
after:  from_json data = 0

Notes

The identical or None on the id line just above is latent: to_json() does not currently emit an "id" field, so get("id") is already None. Left untouched to keep this change to the one observed bug.

Backward compatibility

No breaking changes. Callers that stored falsy data and round-tripped it now get the correct value back instead of None.


Assisted-by: Claude (code generation, reviewed and tested locally)

@patchwright
patchwright requested a review from liamlundy as a code owner July 25, 2026 13:51
patchwright added a commit to patchwright/wildlint that referenced this pull request Jul 25, 2026
Distilled from caesar0301/treelib#246 (from_json collapsed falsy data
like 0/""/[] to None via node_info.get("data") or None). dict.get()
already returns None for a missing key, so a trailing or None is
redundant and collapses legitimate falsy values. DEFAULT tier; the
legitimate d.get(k) or fallback idiom is intentionally not flagged.

Precision: 3 findings across the 6-package corpus (all django),
comparable to WL005 -- not a FP explosion; DEFAULT tier validated.
Dogfood: catches both or-None sites in treelib/tree.py @ master HEAD;
0 findings on wildlint's own source.

Tracked in the corpus gate (RULES + baseline). Bumps 0.8.2 -> 0.8.3.
@cmoliverio

Copy link
Copy Markdown

Hey patchwright. One of the implicit decisions I made when working on from_json() was that the the data is always to be a dict. However, I did not consider this use case where a user might use the data to store a value directly, and I think it's a valid interpretation. This is a good find. 👍

The only thing I'm wondering is if you can add a test for the other falsy values, as it only tests 0. I think it'd be prudent to consider all of these edge cases explicitly.

@liamlundy

Copy link
Copy Markdown
Collaborator

Hey patchwright. One of the implicit decisions I made when working on from_json() was that the the data is always to be a dict. However, I did not consider this use case where a user might use the data to store a value directly, and I think it's a valid interpretation. This is a good find. 👍

The only thing I'm wondering is if you can add a test for the other falsy values, as it only tests 0. I think it'd be prudent to consider all of these edge cases explicitly.

Thanks for finding and fixing this @patchwright! I agree with @cmoliverio here. Can you add unit test for some other falsy values and I'll get this merged

@cmoliverio

cmoliverio commented Jul 30, 2026

Copy link
Copy Markdown

I'm going to go ahead and cherry pick this commit and add the other tests. I want to confirm that the other falsy cases round-trip correctly myself.

@patchwright

Copy link
Copy Markdown
Author

Thanks both. I pushed the extra cases.

test_from_json_preserves_falsy_data now covers 0, 0.0, False, "", [] and {}. I checked each one against the unpatched from_json first: all six collapse to None there, and all six round-trip with the fix. I also added test_from_json_preserves_none_and_truthy_data so that None staying None and ordinary truthy data are pinned down too.

One detail worth flagging: the falsy test asserts on type() as well as equality. assertEqual(0, False) and assertEqual(0, 0.0) both pass in Python, so equality alone would not catch a value coming back as the wrong type.

On the round-trip question, the serialisation side was already fine. to_dict sets node_dict["data"] = node.data unconditionally when with_data=True, so the falsy values were always written to the JSON correctly. Only the or None on the read side dropped them.

Full suite is 212 passed, and make lint and make format-check are clean. If you already started the cherry-pick, take whichever is less work.

@cmoliverio

Copy link
Copy Markdown

Can this or #248 get merged? They are good tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants