Skip to content

fix: update() with in-place callback returning None no longer overwrites field - #238

Open
deepakganesh78 wants to merge 1 commit into
h2non:masterfrom
deepakganesh78:fix/issue163-update-inplace-callback
Open

fix: update() with in-place callback returning None no longer overwrites field#238
deepakganesh78 wants to merge 1 commit into
h2non:masterfrom
deepakganesh78:fix/issue163-update-inplace-callback

Conversation

@deepakganesh78

@deepakganesh78 deepakganesh78 commented Aug 1, 2026

Copy link
Copy Markdown

Fixes #163

Problem

Since commit 7987969 (Fix issue with lambda based updates), Fields._update_base assigns the return value of the callable back to data[field]:

python data[field] = val(data[field], data, field)

This broke callbacks that modify data[field] in-place and return None (the pre-7987969 pattern that issue #163 relies on), because None overwrites the value the callback just set.

Index._update_base had the opposite inconsistency — it called the callable without assigning the return, so return-value callbacks were silently ignored for array indices.

Root cause

Neither Fields._update_base nor Index._update_base handled both callback styles (in-place and return-value).

Fix

Both methods now capture the callable's return value and only assign it if it is not None. If the callable returns None (explicitly or implicitly), the in-place modification is preserved.

⚠️ Behavior change: intentionally setting None via a returning callback

This fix means a callback like lambda orig, data, field: None can no longer be used to deliberately set a field to None via its return value. No existing test or documented behavior relies on this pattern, and it is the only reasonable way to restore compatibility with the in-place callback contract that existed before 7987969 and that issue #163 depends on. Callers who need to set None can still do so inside the callback body: data[field] = None.

For Index._update_base, this is a net improvement — return-value callbacks (e.g. lambda orig, data, idx: orig + 1) now work for array indices, where previously the return value was silently discarded.

Reproduction

`python
from jsonpath_ng import parse

def lowercase_inplace(orig, data, field):
data[field] = data[field].lower()
# returns None

data = {'Data_cat': {'data_entry': [
{'value': 0, 'UPPERCASE': 'UPPERCASE_A'},
{'value': 2, 'UPPERCASE': 'UPPERCASE_B'},
]}}
parse('$..UPPERCASE').update(data, lowercase_inplace)

Before fix: UPPERCASE fields become None

After fix: UPPERCASE fields become lowercase strings

`

Validation

  • 2 new regression tests (in-place and return-value callbacks with $..field doubledot notation)
  • Full test suite: 355 tests passed, 0 failures
  • The only existing lambda update test (lambda x, y, z: x + 1) exercises Fields (not Index) and continues to pass

…tes field (h2non#163)

When a callable passed to update() modifies data in-place and returns None,
Fields._update_base and Index._update_base now preserve the in-place
modification instead of overwriting the field with None.

Both in-place (returning None) and return-value callbacks are supported.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

expression.update() returns none

1 participant