Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions doc/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,47 @@ do not roll back changes already made in place:
>>> original
{'value': 2}

To get all-or-nothing behaviour without copying the document, pass
``atomic=True`` together with ``in_place=True``. If an operation fails, the
changes of the preceding operations are undone before the exception is
raised, so the document is either fully patched or left unchanged:

.. code-block:: python

>>> original = {'value': 1}
>>> try:
... jsonpatch.apply_patch(original, patch, in_place=True, atomic=True)
... except jsonpatch.JsonPatchTestFailed:
... print('Test failed')
Test failed
>>> original
{'value': 1}

This only keeps the values that the patch removes or replaces until it has
been applied, so it needs much less memory than ``in_place=False`` for large
documents. The original containers are restored, so references into the
document stay valid. If a patch contains custom operations (see
:class:`jsonpatch.JsonPatch`'s ``operations``), whose changes can't be undone,
the document is copied instead, and its contents are restored from the copy
if an operation fails; nested containers are then replaced by their copies.

Restoring the document exactly, with the same values, the same order of
object members and the same container objects, is guaranteed for documents
made of plain ``dict`` and ``list`` objects, which includes every document
parsed from JSON. Other containers are restored through their own item
operations, which is exact as long as these only change the item they are
given:

* Containers that change when they are merely read, such as a
``collections.defaultdict``, which adds a missing key on lookup, are not
supported: looking up a location, testing a value or reading the ``from``
of a ``move`` or ``copy`` can add members to them that remain after a
rollback.
* The copy that patches with custom operations are restored from only
restores the items of mappings and sequences, not other state that a
custom operation can change, such as the attributes of objects in the
document or the ``maps`` of a ``collections.ChainMap``.


Ignoring Missing Locations
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
4 changes: 3 additions & 1 deletion jsonpatch-stubs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class JsonPatchException(Exception):

class InvalidJsonPatch(JsonPatchException): ...
class JsonPatchConflict(JsonPatchException): ...
class JsonPatchPathNotFound(JsonPatchConflict): ...
class JsonPatchTestFailed(JsonPatchException, AssertionError): ...

def multidict(ordered_pairs: Iterable[tuple[str, Any]]) -> dict[str, Any]: ...
Expand All @@ -43,6 +44,7 @@ def apply_patch(
patch: str | bytes | Iterable[_Operation],
in_place: bool = False,
pointer_cls: type[JsonPointer] = ...,
atomic: bool = False,
) -> Any: ...
def make_patch(
src: Any,
Expand Down Expand Up @@ -118,7 +120,7 @@ class JsonPatch:
generate_test_ops: bool = False,
) -> Self: ...
def to_string(self, dumps: Callable[[Any], str] | None = None) -> str: ...
def apply(self, obj: Any, in_place: bool = False) -> Any: ...
def apply(self, obj: Any, in_place: bool = False, atomic: bool = False) -> Any: ...

# Computes the operations for JsonPatch.from_diff; mostly an implementation
# detail, so its internal methods are typed loosely.
Expand Down
Loading
Loading