In this article

A settings patch must preserve what the user asked to change. An omitted field keeps its value, false can disable a feature, zero can select midnight, and null can clear a nullable field. This illustrative Python example reproduces a filter that loses those actions, then verifies a repair with explicit state and error checks.

Describe the update as an action on existing state

A user disables email alerts, chooses midnight for a digest, and clears a saved signature. The request succeeds, but the settings look unchanged. Before asking an assistant to fix the form, capture the submitted object and the resulting server state. This is an original practice scenario. Its central question is whether the update preserves each requested action, not whether a particular UI framework handles forms correctly.

Use a small contract: omission keeps the current setting; email_alerts accepts only booleans; digest_hour accepts ordinary integers from zero through 23; signature accepts text or null. Null clears the signature, while empty text remains an empty string. Null is invalid for the other two fields. Reject unknown names and invalid values without changing either input. Assume the current settings already satisfy this contract.

    Reproduce the loss immediately after JSON decoding

    The broken function removes values that test false before merging the patch. Starting with alerts enabled, hour nine, and a signature of Thanks, the three requested changes all disappear. Run the snippet below after the broken helper: it prints the original values and then deliberately fails. Python's JSON decoder maps false to False and null to None; decoding has preserved the values at this point. Python: JSON decoding and type conversion

    The filter is where the information is lost. False, numeric zero, None, and empty strings all test false in Python. Python: truth values and dictionary operations One condition therefore removes several values that have different meanings in the update contract. The problem is not that JSON failed to distinguish them. Inspect the value before and after the filter rather than diagnosing from the final screen alone.

    Broken filter and a reproduction with a literal expected update
    Codepython
    def apply_settings_broken(current, patch):
        return {**current, **{key: value for key, value in patch.items() if value}}
    
    import json
    
    current = {"email_alerts": True, "digest_hour": 9, "signature": "Thanks"}
    patch = json.loads('{"email_alerts":false,"digest_hour":0,"signature":null}')
    actual = apply_settings_broken(current, patch)
    print(actual)
    assert actual == {"email_alerts": False, "digest_hour": 0, "signature": None}
    

      Challenge a patch that fixes only false and zero

      An assistant may suggest replacing the truth test with value is not None. That keeps false and zero, but it still discards the explicit request to clear the signature. It may look fixed if you retest only the alert toggle. Compare all three actions against literal expected state. An empty patch and a patch containing signature: null must produce different results when a signature already exists.

      A focused prompt is: Explain why the current filter loses valid changes. Preserve omission, explicit null, false, zero, and empty text according to the field contract. Reject unknown keys and invalid types without mutating either input. Show a test that a non-null filter would fail. This asks the assistant to account for the missing distinction instead of merely swapping one familiar condition for another.

        Validate the fields that are present, then apply them

        The repaired helper iterates over the patch's actual entries. It validates every supplied field before constructing the returned dictionary. No filtering is needed: omission is already represented by a key that is not present. A supplied signature with None reaches the result and clears it; an omitted signature retains the existing value. This contract belongs to this example. The word patch alone does not establish these semantics for every API.

        The exact integer check prevents booleans from becoming hours. Python's Boolean type is an integer subclass. Python: truth values and dictionary operations Rejecting rather than coercing also prevents text such as false or zero from silently becoming a different action. Keep the parser, field validation, and business meaning separate in your explanation. A useful repair preserves the client's intent rather than guessing what malformed values were meant to say.

        Repaired settings transformation with field-specific validation
        Codepython
        def apply_settings(current, patch):
            if type(patch) is not dict:
                raise TypeError("patch must be an object")
            allowed = {"email_alerts", "digest_hour", "signature"}
            if set(patch) - allowed:
                raise ValueError("unknown setting")
            for key, value in patch.items():
                if key == "email_alerts" and type(value) is not bool:
                    raise TypeError("email_alerts must be a boolean")
                if key == "digest_hour":
                    if type(value) is not int:
                        raise TypeError("digest_hour must be an integer")
                    if not 0 <= value <= 23:
                        raise ValueError("digest_hour must be between 0 and 23")
                if key == "signature" and value is not None and type(value) is not str:
                    raise TypeError("signature must be text or null")
            return {**current, **patch}

          Test values, field presence, and rejected patches separately

          Save the repaired helper and the following tests as settings_test.py, then run python settings_test.py -v. Eight test methods passed on Python 3.14.4 when preparing this article. The first case starts from JSON text, so it exercises the same false and null conversion used in the reproduction. The expectations name the exact resulting fields; they do not repeat the patch algorithm. Python: JSON decoding and type conversion

          Other cases distinguish empty text from clearing, preserve omitted fields, reject null on nonnullable fields, and reject invalid hours and types. The final case supplies a valid alert change followed by an invalid hour and checks both inputs afterward. unittest provides subtests and assertions for equality, identity, null, and exceptions, allowing these separate observations to remain visible. Python: unittest assertions and subtests

          Eight test methods for update meaning and unchanged inputs
          Codepython
          import json
          import unittest
          
          class SettingsTests(unittest.TestCase):
              def setUp(self):
                  self.current = {"email_alerts": True, "digest_hour": 9, "signature": "Thanks"}
          
              def test_false_zero_and_null_survive_json(self):
                  patch = json.loads('{"email_alerts":false,"digest_hour":0,"signature":null}')
                  self.assertEqual(apply_settings(self.current, patch),
                                   {"email_alerts": False, "digest_hour": 0, "signature": None})
                  self.assertEqual(self.current,
                                   {"email_alerts": True, "digest_hour": 9, "signature": "Thanks"})
                  self.assertEqual(patch,
                                   {"email_alerts": False, "digest_hour": 0, "signature": None})
          
              def test_omission_keeps_existing_values(self):
                  updated = apply_settings(self.current, {})
                  self.assertEqual(updated, self.current)
                  self.assertIsNot(updated, self.current)
                  self.assertEqual(apply_settings(self.current, {"digest_hour": 23}),
                                   {"email_alerts": True, "digest_hour": 23, "signature": "Thanks"})
          
              def test_empty_text_is_distinct_from_null(self):
                  self.assertEqual(apply_settings(self.current, {"signature": ""})["signature"], "")
                  self.assertIsNone(apply_settings(self.current, {"signature": None})["signature"])
          
              def test_null_is_rejected_for_nonnullable_fields(self):
                  for key in ("email_alerts", "digest_hour"):
                      with self.subTest(key=key), self.assertRaises(TypeError):
                          apply_settings(self.current, {key: None})
          
              def test_wrong_types_are_not_coerced(self):
                  cases = [{"email_alerts": 0}, {"email_alerts": "false"},
                           {"digest_hour": False}, {"digest_hour": 1.0},
                           {"digest_hour": "0"}, {"signature": 0}]
                  for patch in cases:
                      with self.subTest(patch=patch), self.assertRaises(TypeError):
                          apply_settings(self.current, patch)
          
              def test_out_of_range_hours(self):
                  for value in (-1, 24):
                      with self.subTest(value=value), self.assertRaises(ValueError):
                          apply_settings(self.current, {"digest_hour": value})
          
              def test_unknown_keys_and_nonobjects(self):
                  with self.assertRaises(ValueError):
                      apply_settings(self.current, {"digest_huor": 0})
                  for patch in ([], None, "settings"):
                      with self.subTest(patch=patch), self.assertRaises(TypeError):
                          apply_settings(self.current, patch)
          
              def test_invalid_later_field_leaves_inputs_unchanged(self):
                  patch = {"email_alerts": False, "digest_hour": 24}
                  with self.assertRaises(ValueError):
                      apply_settings(self.current, patch)
                  self.assertEqual(self.current,
                                   {"email_alerts": True, "digest_hour": 9, "signature": "Thanks"})
                  self.assertEqual(patch, {"email_alerts": False, "digest_hour": 24})
          
          
          if __name__ == "__main__":
              unittest.main()

            Restore the shortcuts and watch the relevant tests fail

            After the corrected suite passes, keep validation but change the returned merge to include only truthy patch values. The combined false/zero/null case and the empty-text case fail. Next filter only None: two methods still fail, because the combined case must clear the signature and the empty-text method also checks an explicit clear. Both experiments were executed while preparing this article. They expose different incomplete repairs without changing the intended answers.

            The failures do not prove that every aspect of the helper is covered. They show that these tests can distinguish the intended behavior from two plausible suggestions. If the assistant tries to change the expected signature back to Thanks, return to the contract and the captured request. A green result obtained by removing a required user action is evidence of a changed test, not a completed repair.

              Check the surrounding path before declaring the UI repaired

              Restore the actual request handler after the isolated example works. Check whether the client omits unchanged fields, whether its serializer preserves explicit null, and whether the server rejects invalid patches with the established error response. Then inspect persistence and the response used to refresh the screen. A correct helper cannot recover a clearing instruction that an earlier layer already discarded.

              Returning a new dictionary avoids modifying these flat inputs; it does not create a database transaction or resolve concurrent updates. A real service may need version checks or another write-coordination policy. Nested settings also require a defined merge rule rather than an accidental shallow replacement. List those as separate integration questions. Keep the claim proportionate: the executed example verifies field semantics and local input preservation.

                Common questions about null, omission, and settings patches

                Should null always delete a field? No. Here it clears signature to None and is rejected for the other fields. Another API may assign a different meaning. Read its public contract before using a generic merge helper or describing the request as a standard patch format.

                Can I use get to decide whether a field was supplied? A lookup that returns None for both absence and explicit null cannot distinguish those two actions by itself. Check key membership or use a distinct missing-value sentinel when the update logic needs that distinction. This example iterates the supplied entries directly.

                Why test an empty string as well as null? They are different accepted signature values here. A truthiness filter loses both, while a non-null filter loses only the clear. The expected result should express the product's chosen behavior, not whichever condition is shortest.

                  Sources & editorial notes

                  This article was authored with AI using primary-source research and an automated technical review. It has not been reviewed by a human. Code examples are illustrative; run and verify them in your own environment.

                  1. Python: JSON decoding and type conversion

                    Confirms JSON-to-Python decoding for objects, booleans, numeric values, and null. Checked October 24, 2026; the application-specific patch rules are illustrative.

                  2. Python: truth values and dictionary operations

                    Supports the falsey-value explanation, dictionary operations, and Boolean/integer distinction. Checked October 24, 2026.

                  3. Python: unittest assertions and subtests

                    Documents the unittest assertions and subtests used to verify accepted updates, rejected values, and preserved inputs. Checked October 24, 2026.

                  Put the reading into practice

                  Work through an existing repository, review AI suggestions, and test the decisions behind your changes.

                  Explore practice problems