Like `._modify`, but for removal.
(self, keypath: Tuple[str, ...], key: str)
| 1130 | self.merge() |
| 1131 | |
| 1132 | def _remove(self, keypath: Tuple[str, ...], key: str) -> None: |
| 1133 | """ |
| 1134 | Like `._modify`, but for removal. |
| 1135 | """ |
| 1136 | # NOTE: because deletions are processed in merge() last, we do not need |
| 1137 | # to remove things from _modifications on removal; but we *do* do the |
| 1138 | # inverse - remove from _deletions on modification. |
| 1139 | # TODO: may be sane to push this step up to callers? |
| 1140 | data = self._deletions |
| 1141 | keypath_list = list(keypath) |
| 1142 | while keypath_list: |
| 1143 | subkey = keypath_list.pop(0) |
| 1144 | if subkey in data: |
| 1145 | data = data[subkey] |
| 1146 | # If we encounter None, it means something higher up than our |
| 1147 | # requested keypath is already marked as deleted; so we don't |
| 1148 | # have to do anything or go further. |
| 1149 | if data is None: |
| 1150 | return |
| 1151 | # Otherwise it's presumably another dict, so keep looping... |
| 1152 | else: |
| 1153 | # Key not found -> nobody's marked anything along this part of |
| 1154 | # the path for deletion, so we'll start building it out. |
| 1155 | data[subkey] = {} |
| 1156 | # Then prep for next iteration |
| 1157 | data = data[subkey] |
| 1158 | # Exited loop -> data must be the leafmost dict, so we can now set our |
| 1159 | # deleted key to None |
| 1160 | data[key] = None |
| 1161 | self.merge() |
| 1162 | |
| 1163 | |
| 1164 | class AmbiguousMergeError(ValueError): |
no test coverage detected