jsonpatch 1.36__py3-none-any.whl → 1.38__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsonpatch
3
- Version: 1.36
3
+ Version: 1.38
4
4
  Summary: Apply JSON-Patches (RFC 6902)
5
5
  Home-page: https://github.com/stefankoegl/python-json-patch
6
6
  Author: Stefan Kögl
@@ -0,0 +1,9 @@
1
+ jsonpatch.py,sha256=CUWLdFCK8nBgO4KWsRNy40sjHouzmGx_C30WE0xT2VU,44186
2
+ jsonpatch_cli.py,sha256=IRfXFLpY4qF6QRGSZA227tIZo7ly_RXjImOrMvz8Lag,6043
3
+ jsonpatch-1.38.dist-info/licenses/AUTHORS,sha256=hOtEnBGbfDegKjzMDEl5vuVJyczbI2PlYE2ho4LjGNQ,140
4
+ jsonpatch-1.38.dist-info/licenses/LICENSE,sha256=Q5PrZzP6HjZ_QXabOC9PG-e_Da6x4kPtkrP12Lr9I28,1481
5
+ jsonpatch-1.38.dist-info/METADATA,sha256=-P36hnnP8bn-Bhjj2jRAVl5xmz7XHEaA_kkz-DeAk60,3672
6
+ jsonpatch-1.38.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
7
+ jsonpatch-1.38.dist-info/entry_points.txt,sha256=aY9ormBVRZHHDaAeMcdqTBwZ-ax7GvBTSQ33I75xtZM,98
8
+ jsonpatch-1.38.dist-info/top_level.txt,sha256=efUF341HUk8WcQKnFt8ZLKEKNrm1kFT76Flr38Fjno0,24
9
+ jsonpatch-1.38.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
jsonpatch.py CHANGED
@@ -37,7 +37,8 @@ import collections
37
37
  import copy
38
38
  import functools
39
39
  import json
40
- from collections.abc import Mapping, MutableMapping, MutableSequence, Sequence
40
+ from collections.abc import (Mapping, MutableMapping, MutableSequence,
41
+ Sequence)
41
42
  from difflib import SequenceMatcher
42
43
  from types import MappingProxyType
43
44
 
@@ -54,7 +55,7 @@ _EXACT_MATCH_LIMIT = 500 ** 2
54
55
 
55
56
  # Will be parsed by setup.py to determine package metadata
56
57
  __author__ = 'Stefan Kögl <stefan@skoegl.net>'
57
- __version__ = '1.36'
58
+ __version__ = '1.38'
58
59
  __website__ = 'https://github.com/stefankoegl/python-json-patch'
59
60
  __license__ = 'BSD-3-Clause'
60
61
 
@@ -100,13 +101,13 @@ _jsonloads = functools.partial(json.loads, object_pairs_hook=multidict)
100
101
 
101
102
 
102
103
  def apply_patch(doc, patch, in_place=False, pointer_cls=JsonPointer):
103
- """Apply list of patches to specified json document.
104
+ """Apply patch operations to the specified JSON document.
104
105
 
105
- :param doc: Document object.
106
- :type doc: dict
106
+ :param doc: JSON document, including arrays and scalar values.
107
+ :type doc: object
107
108
 
108
- :param patch: JSON patch as list of dicts or raw JSON-encoded string.
109
- :type patch: list or str
109
+ :param patch: Iterable of operation dicts or a raw JSON-encoded string.
110
+ :type patch: iterable or str
110
111
 
111
112
  :param in_place: While ``True`` patch will modify target document.
112
113
  By default patch will be applied to document copy.
@@ -115,8 +116,8 @@ def apply_patch(doc, patch, in_place=False, pointer_cls=JsonPointer):
115
116
  :param pointer_cls: JSON pointer class to use.
116
117
  :type pointer_cls: Type[JsonPointer]
117
118
 
118
- :return: Patched document object.
119
- :rtype: dict
119
+ :return: Resulting document, which may have a different type from ``doc``.
120
+ :rtype: object
120
121
 
121
122
  >>> doc = {'foo': 'bar'}
122
123
  >>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}]
@@ -145,11 +146,11 @@ def make_patch(src, dst, pointer_cls=JsonPointer):
145
146
  transforms `src` into `dst`, so it has to be applied to `src` (or to a
146
147
  document equal to it).
147
148
 
148
- :param src: Data source document object.
149
- :type src: dict
149
+ :param src: Source JSON document, including arrays and scalar values.
150
+ :type src: object
150
151
 
151
- :param dst: Data target document object.
152
- :type dst: dict
152
+ :param dst: Target JSON document, including arrays and scalar values.
153
+ :type dst: object
153
154
 
154
155
  :param pointer_cls: JSON pointer class to use.
155
156
  :type pointer_cls: Type[JsonPointer]
@@ -384,7 +385,7 @@ class MoveOperation(PatchOperation):
384
385
 
385
386
  @property
386
387
  def from_path(self):
387
- from_ptr = self.pointer_cls(self.operation['from'])
388
+ from_ptr = self._from_pointer()
388
389
  return '/'.join(from_ptr.parts[:-1])
389
390
 
390
391
  @property
@@ -396,14 +397,14 @@ class MoveOperation(PatchOperation):
396
397
  self.set_from_part(-1, value)
397
398
 
398
399
  def get_from_part(self, index):
399
- from_ptr = self.pointer_cls(self.operation['from'])
400
+ from_ptr = self._from_pointer()
400
401
  try:
401
402
  return int(from_ptr.parts[index])
402
403
  except ValueError:
403
404
  return from_ptr.parts[index]
404
405
 
405
406
  def set_from_part(self, index, value):
406
- from_ptr = self.pointer_cls(self.operation['from'])
407
+ from_ptr = self._from_pointer()
407
408
  from_ptr.parts[index] = str(value)
408
409
  self.operation['from'] = from_ptr.path
409
410
 
@@ -457,7 +458,11 @@ class CopyOperation(PatchOperation):
457
458
 
458
459
 
459
460
  class JsonPatch(object):
460
- """A JSON Patch is a list of Patch Operations.
461
+ """A JSON Patch is an iterable of Patch Operations.
462
+
463
+ Accepts sequences, iterators, generators, and other iterables of operation
464
+ dictionaries. Non-sequence inputs are materialized before validation so
465
+ the resulting patch can be applied repeatedly.
461
466
 
462
467
  >>> patch = JsonPatch([
463
468
  ... {'op': 'add', 'path': '/foo', 'value': 'bar'},
@@ -515,9 +520,15 @@ class JsonPatch(object):
515
520
  })
516
521
 
517
522
  def __init__(self, patch, pointer_cls=JsonPointer):
518
- self.patch = patch
519
523
  self.pointer_cls = pointer_cls
520
524
 
525
+ if isinstance(patch, (str, bytes, Mapping)):
526
+ raise InvalidJsonPatch("Document is expected to be sequence of operations")
527
+ try:
528
+ self.patch = patch if isinstance(patch, Sequence) else list(patch)
529
+ except TypeError:
530
+ raise InvalidJsonPatch("Document is expected to be sequence of operations")
531
+
521
532
  # Verify that the structure of the patch document
522
533
  # is correct by retrieving each patch element.
523
534
  # Much of the validation is done in the initializer
@@ -591,11 +602,11 @@ class JsonPatch(object):
591
602
  one. The resulting patch transforms `src` into `dst`, so it has to be
592
603
  applied to `src` (or to a document equal to it).
593
604
 
594
- :param src: Data source document object.
595
- :type src: dict
605
+ :param src: Source JSON document, including arrays and scalar values.
606
+ :type src: object
596
607
 
597
- :param dst: Data target document object.
598
- :type dst: dict
608
+ :param dst: Target JSON document, including arrays and scalar values.
609
+ :type dst: object
599
610
 
600
611
  :param optimization: Generate move operations for matching values.
601
612
  Set to False to use only add, remove and replace.
@@ -636,14 +647,16 @@ class JsonPatch(object):
636
647
  def apply(self, obj, in_place=False):
637
648
  """Applies the patch to a given object.
638
649
 
639
- :param obj: Document object.
640
- :type obj: dict
650
+ :param obj: JSON document, including arrays and scalar values.
651
+ :type obj: object
641
652
 
642
653
  :param in_place: Tweaks the way how patch would be applied - directly to
643
654
  specified `obj` or to its copy.
644
655
  :type in_place: bool
645
656
 
646
- :return: Modified `obj`.
657
+ :return: Resulting document. Use this return value even with
658
+ ``in_place=True``, because a root replacement returns a new
659
+ value rather than modifying the original container.
647
660
  """
648
661
 
649
662
  if not in_place:
jsonpatch_cli.py CHANGED
@@ -12,8 +12,8 @@ import argparse
12
12
 
13
13
  jsondiff_parser = argparse.ArgumentParser(
14
14
  prog='jsondiff', description='Diff two JSON files')
15
- jsondiff_parser.add_argument('FILE1', type=argparse.FileType('r'))
16
- jsondiff_parser.add_argument('FILE2', type=argparse.FileType('r'))
15
+ jsondiff_parser.add_argument('FILE1', type=argparse.FileType('r', encoding='utf-8'))
16
+ jsondiff_parser.add_argument('FILE2', type=argparse.FileType('r', encoding='utf-8'))
17
17
  jsondiff_parser.add_argument('--indent', type=int, default=None,
18
18
  help='Indent output by n spaces')
19
19
  jsondiff_parser.add_argument('-u', '--preserve-unicode', action='store_true',
@@ -43,9 +43,9 @@ def diff_files():
43
43
  jsonpatch_parser = argparse.ArgumentParser(
44
44
  prog='jsonpatch',
45
45
  description='Apply a JSON patch on a JSON file')
46
- jsonpatch_parser.add_argument('ORIGINAL', type=argparse.FileType('r'),
46
+ jsonpatch_parser.add_argument('ORIGINAL', type=argparse.FileType('r', encoding='utf-8'),
47
47
  help='Original file')
48
- jsonpatch_parser.add_argument('PATCH', type=argparse.FileType('r'),
48
+ jsonpatch_parser.add_argument('PATCH', type=argparse.FileType('r', encoding='utf-8'),
49
49
  nargs='?', default=sys.stdin,
50
50
  help='Patch file (read from stdin if omitted)')
51
51
  jsonpatch_parser.add_argument('--indent', type=int, default=None,
@@ -78,6 +78,7 @@ def patch_files():
78
78
  args.ORIGINAL.close()
79
79
  dirname = os.path.abspath(os.path.dirname(args.ORIGINAL.name))
80
80
 
81
+ fd = None
81
82
  try:
82
83
  # Attempt to replace the file atomically. We do this by
83
84
  # creating a temporary file in the same directory as the
@@ -86,17 +87,28 @@ def patch_files():
86
87
  # because atomic renames do not work across mount points.)
87
88
 
88
89
  fd, pathname = tempfile.mkstemp(dir=dirname)
89
- fp = os.fdopen(fd, 'w')
90
+ fp = os.fdopen(fd, 'w', encoding='utf-8')
90
91
  atomic = True
91
92
 
92
93
  except OSError:
94
+ # mkstemp may have succeeded before fdopen failed. Neither the
95
+ # descriptor nor the temporary file belongs to the fallback.
96
+ if fd is not None:
97
+ try:
98
+ os.close(fd)
99
+ except OSError:
100
+ pass
101
+ try:
102
+ os.unlink(pathname)
103
+ except OSError:
104
+ pass
93
105
  # We failed to create the temporary file for an atomic
94
106
  # replace, so fall back to non-atomic mode by backing up
95
107
  # the original (if desired) and writing a new file.
96
108
 
97
109
  if args.backup:
98
110
  os.rename(args.ORIGINAL.name, args.ORIGINAL.name + '.orig')
99
- fp = open(args.ORIGINAL.name, 'w')
111
+ fp = open(args.ORIGINAL.name, 'w', encoding='utf-8')
100
112
  atomic = False
101
113
 
102
114
  else:
@@ -108,15 +120,26 @@ def patch_files():
108
120
  # By this point we have some sort of file object we can write the
109
121
  # modified JSON to.
110
122
 
111
- json.dump(result, fp, indent=args.indent, ensure_ascii=not(args.preserve_unicode))
112
- fp.write('\n')
123
+ try:
124
+ json.dump(result, fp, indent=args.indent, ensure_ascii=not(args.preserve_unicode))
125
+ fp.write('\n')
126
+ if args.in_place:
127
+ fp.close()
128
+ except BaseException:
129
+ if args.in_place:
130
+ # Cleanup must not replace the original output error.
131
+ try:
132
+ fp.close()
133
+ except OSError:
134
+ pass
135
+ if atomic:
136
+ try:
137
+ os.unlink(pathname)
138
+ except OSError:
139
+ pass
140
+ raise
113
141
 
114
142
  if args.in_place:
115
- # Close the new file. If we aren't replacing atomically, this
116
- # is our last step, since everything else is already in place.
117
-
118
- fp.close()
119
-
120
143
  if atomic:
121
144
  try:
122
145
  # Complete the atomic replace by linking the original
@@ -1,9 +0,0 @@
1
- jsonpatch.py,sha256=zlmSJbQUudEtW-I7qw-sit7E5KyEMm_MMYFDEx2JyDo,43250
2
- jsonpatch_cli.py,sha256=-jUMm0QA5Z13bf2C3uLfZe7YH-vxqbGeDltZ9zd4K4I,5240
3
- jsonpatch-1.36.dist-info/licenses/AUTHORS,sha256=hOtEnBGbfDegKjzMDEl5vuVJyczbI2PlYE2ho4LjGNQ,140
4
- jsonpatch-1.36.dist-info/licenses/LICENSE,sha256=Q5PrZzP6HjZ_QXabOC9PG-e_Da6x4kPtkrP12Lr9I28,1481
5
- jsonpatch-1.36.dist-info/METADATA,sha256=EAKYkquXqZWRxQB-ejAILD8eEk2l5U-1bk8AcilDNoM,3672
6
- jsonpatch-1.36.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
- jsonpatch-1.36.dist-info/entry_points.txt,sha256=aY9ormBVRZHHDaAeMcdqTBwZ-ax7GvBTSQ33I75xtZM,98
8
- jsonpatch-1.36.dist-info/top_level.txt,sha256=efUF341HUk8WcQKnFt8ZLKEKNrm1kFT76Flr38Fjno0,24
9
- jsonpatch-1.36.dist-info/RECORD,,