Skip to content

Commit 48f9adb

Browse files
committed
Merge pull request #19 from kxepal/pylint
Improve code quality
2 parents fc4e0b2 + 0c4ec87 commit 48f9adb

File tree

1 file changed

+77
-64
lines changed

1 file changed

+77
-64
lines changed

jsonpatch.py

Lines changed: 77 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,34 @@
3030
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131
#
3232

33+
""" Apply JSON-Patches (RFC 6902) """
34+
3335
from __future__ import unicode_literals
3436

35-
""" Apply JSON-Patches (RFC 6902) """
37+
import collections
38+
import copy
39+
import functools
40+
import inspect
41+
import json
42+
import sys
43+
44+
from jsonpointer import JsonPointer, JsonPointerException
3645

3746
# Will be parsed by setup.py to determine package metadata
3847
__author__ = 'Stefan Kögl <[email protected]>'
3948
__version__ = '1.3'
4049
__website__ = 'https://github.com/stefankoegl/python-json-patch'
4150
__license__ = 'Modified BSD License'
4251

43-
import copy
44-
import sys
45-
import operator
46-
import collections
47-
48-
import json
49-
50-
import jsonpointer
5152

53+
# pylint: disable=E0611,W0404
5254
if sys.version_info >= (3, 0):
53-
basestring = (bytes, str)
55+
basestring = (bytes, str) # pylint: disable=C0103,W0622
56+
from itertools import zip_longest
57+
else:
58+
from itertools import izip_longest as zip_longest
5459

5560

56-
JsonPointerException = jsonpointer.JsonPointerException
57-
5861
class JsonPatchException(Exception):
5962
"""Base Json Patch exception"""
6063

@@ -67,22 +70,23 @@ class JsonPatchConflict(JsonPatchException):
6770
- etc.
6871
"""
6972

73+
7074
class JsonPatchTestFailed(JsonPatchException, AssertionError):
7175
""" A Test operation failed """
7276

7377

7478
def multidict(ordered_pairs):
7579
"""Convert duplicate keys values to lists."""
7680
# read all values into lists
77-
d = collections.defaultdict(list)
78-
for k, v in ordered_pairs:
79-
d[k].append(v)
81+
mdict = collections.defaultdict(list)
82+
for key, value in ordered_pairs:
83+
mdict[key].append(value)
8084

81-
# unpack lists that have only 1 item
82-
for k, v in d.items():
83-
if len(v) == 1:
84-
d[k] = v[0]
85-
return dict(d)
85+
return dict(
86+
# unpack lists that have only 1 item
87+
(key, values[0] if len(values) == 1 else values)
88+
for key, values in mdict.items()
89+
)
8690

8791

8892
def get_loadjson():
@@ -94,9 +98,6 @@ def get_loadjson():
9498
function with object_pairs_hook set to multidict for Python versions that
9599
support the parameter. """
96100

97-
import inspect
98-
import functools
99-
100101
argspec = inspect.getargspec(json.load)
101102
if 'object_pairs_hook' not in argspec.args:
102103
return json.load
@@ -123,12 +124,14 @@ def apply_patch(doc, patch, in_place=False):
123124
:rtype: dict
124125
125126
>>> doc = {'foo': 'bar'}
126-
>>> other = apply_patch(doc, [{'op': 'add', 'path': '/baz', 'value': 'qux'}])
127+
>>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}]
128+
>>> other = apply_patch(doc, patch)
127129
>>> doc is not other
128130
True
129131
>>> other == {'foo': 'bar', 'baz': 'qux'}
130132
True
131-
>>> apply_patch(doc, [{'op': 'add', 'path': '/baz', 'value': 'qux'}], in_place=True) == {'foo': 'bar', 'baz': 'qux'}
133+
>>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}]
134+
>>> apply_patch(doc, patch, in_place=True) == {'foo': 'bar', 'baz': 'qux'}
132135
True
133136
>>> doc == other
134137
True
@@ -140,6 +143,7 @@ def apply_patch(doc, patch, in_place=False):
140143
patch = JsonPatch(patch)
141144
return patch.apply(doc, in_place)
142145

146+
143147
def make_patch(src, dst):
144148
"""Generates patch by comparing of two document objects. Actually is
145149
a proxy to :meth:`JsonPatch.from_diff` method.
@@ -230,18 +234,16 @@ def __bool__(self):
230234
def __iter__(self):
231235
return iter(self.patch)
232236

233-
234237
def __hash__(self):
235238
return hash(tuple(self._ops))
236239

237-
238240
def __eq__(self, other):
239241
if not isinstance(other, JsonPatch):
240242
return False
243+
return self._ops == other._ops
241244

242-
return len(list(self._ops)) == len(list(other._ops)) and \
243-
all(map(operator.eq, self._ops, other._ops))
244-
245+
def __ne__(self, other):
246+
return not(self == other)
245247

246248
@classmethod
247249
def from_string(cls, patch_str):
@@ -298,7 +300,9 @@ def compare_dict(path, src, dst):
298300
yield operation
299301
for key in dst:
300302
if key not in src:
301-
yield {'op': 'add', 'path': '/'.join(path + [key]), 'value': dst[key]}
303+
yield {'op': 'add',
304+
'path': '/'.join(path + [key]),
305+
'value': dst[key]}
302306

303307
def compare_list(path, src, dst):
304308
lsrc, ldst = len(src), len(dst)
@@ -309,7 +313,9 @@ def compare_list(path, src, dst):
309313
if lsrc < ldst:
310314
for idx in range(lsrc, ldst):
311315
current = path + [str(idx)]
312-
yield {'op': 'add', 'path': '/'.join(current), 'value': dst[idx]}
316+
yield {'op': 'add',
317+
'path': '/'.join(current),
318+
'value': dst[idx]}
313319
elif lsrc > ldst:
314320
for idx in reversed(range(ldst, lsrc)):
315321
yield {'op': 'remove', 'path': '/'.join(path + [str(idx)])}
@@ -322,7 +328,7 @@ def to_string(self):
322328

323329
@property
324330
def _ops(self):
325-
return map(self._get_operation, self.patch)
331+
return tuple(map(self._get_operation, self.patch))
326332

327333
def apply(self, obj, in_place=False):
328334
"""Applies the patch to given object.
@@ -355,36 +361,35 @@ def _get_operation(self, operation):
355361
raise JsonPatchException("Operation must be a string")
356362

357363
if op not in self.operations:
358-
raise JsonPatchException("Unknown operation '%s'" % op)
364+
raise JsonPatchException("Unknown operation {0!r}".format(op))
359365

360366
cls = self.operations[op]
361367
return cls(operation)
362368

363369

364-
365370
class PatchOperation(object):
366371
"""A single operation inside a JSON Patch."""
367372

368373
def __init__(self, operation):
369374
self.location = operation['path']
370-
self.pointer = jsonpointer.JsonPointer(self.location)
375+
self.pointer = JsonPointer(self.location)
371376
self.operation = operation
372377

373378
def apply(self, obj):
374379
"""Abstract method that applies patch operation to specified object."""
375380
raise NotImplementedError('should implement patch operation.')
376381

377-
378382
def __hash__(self):
379383
return hash(frozenset(self.operation.items()))
380384

381-
382385
def __eq__(self, other):
383386
if not isinstance(other, PatchOperation):
384387
return False
385-
386388
return self.operation == other.operation
387389

390+
def __ne__(self, other):
391+
return not(self == other)
392+
388393

389394
class RemoveOperation(PatchOperation):
390395
"""Removes an object property or an array element."""
@@ -406,30 +411,25 @@ def apply(self, obj):
406411
value = self.operation["value"]
407412
subobj, part = self.pointer.to_last(obj)
408413

409-
# type is already checked in to_last(), so we assert here
410-
# for consistency
411-
assert isinstance(subobj, list) or isinstance(subobj, dict), \
412-
"invalid document type %s" (type(doc),)
413-
414414
if isinstance(subobj, list):
415-
416415
if part == '-':
417-
subobj.append(value)
416+
subobj.append(value) # pylint: disable=E1103
418417

419418
elif part > len(subobj) or part < 0:
420419
raise JsonPatchConflict("can't insert outside of list")
421420

422421
else:
423-
subobj.insert(part, value)
422+
subobj.insert(part, value) # pylint: disable=E1103
424423

425424
elif isinstance(subobj, dict):
426425
if part is None:
427-
# we're replacing the root
428-
obj = value
429-
426+
obj = value # we're replacing the root
430427
else:
431428
subobj[part] = value
432429

430+
else:
431+
raise TypeError("invalid document type {0}".format(type(subobj)))
432+
433433
return obj
434434

435435

@@ -440,11 +440,6 @@ def apply(self, obj):
440440
value = self.operation["value"]
441441
subobj, part = self.pointer.to_last(obj)
442442

443-
# type is already checked in to_last(), so we assert here
444-
# for consistency
445-
assert isinstance(subobj, list) or isinstance(subobj, dict), \
446-
"invalid document type %s" (type(doc),)
447-
448443
if part is None:
449444
return value
450445

@@ -454,8 +449,10 @@ def apply(self, obj):
454449

455450
elif isinstance(subobj, dict):
456451
if not part in subobj:
457-
raise JsonPatchConflict("can't replace non-existant object '%s'"
458-
"" % part)
452+
msg = "can't replace non-existent object '{0}'".format(part)
453+
raise JsonPatchConflict(msg)
454+
else:
455+
raise TypeError("invalid document type {0}".format(type(subobj)))
459456

460457
subobj[part] = value
461458
return obj
@@ -465,15 +462,24 @@ class MoveOperation(PatchOperation):
465462
"""Moves an object property or an array element to new location."""
466463

467464
def apply(self, obj):
468-
from_ptr = jsonpointer.JsonPointer(self.operation['from'])
465+
from_ptr = JsonPointer(self.operation['from'])
469466
subobj, part = from_ptr.to_last(obj)
470467
value = subobj[part]
471468

472469
if self.pointer.contains(from_ptr):
473470
raise JsonPatchException('Cannot move values into its own children')
474471

475-
obj = RemoveOperation({'op': 'remove', 'path': self.operation['from']}).apply(obj)
476-
obj = AddOperation({'op': 'add', 'path': self.location, 'value': value}).apply(obj)
472+
obj = RemoveOperation({
473+
'op': 'remove',
474+
'path': self.operation['from']
475+
}).apply(obj)
476+
477+
obj = AddOperation({
478+
'op': 'add',
479+
'path': self.location,
480+
'value': value
481+
}).apply(obj)
482+
477483
return obj
478484

479485

@@ -487,14 +493,15 @@ def apply(self, obj):
487493
val = subobj
488494
else:
489495
val = self.pointer.walk(subobj, part)
490-
491496
except JsonPointerException as ex:
492497
raise JsonPatchTestFailed(str(ex))
493498

494499
if 'value' in self.operation:
495500
value = self.operation['value']
496501
if val != value:
497-
raise JsonPatchTestFailed('%s is not equal to tested value %s (types %s and %s)' % (val, value, type(val), type(value)))
502+
msg = '{0} ({1}) is not equal to tested value {2} ({3})'
503+
raise JsonPatchTestFailed(msg.format(val, type(val),
504+
value, type(value)))
498505

499506
return obj
500507

@@ -503,8 +510,14 @@ class CopyOperation(PatchOperation):
503510
""" Copies an object property or an array element to a new location """
504511

505512
def apply(self, obj):
506-
from_ptr = jsonpointer.JsonPointer(self.operation['from'])
513+
from_ptr = JsonPointer(self.operation['from'])
507514
subobj, part = from_ptr.to_last(obj)
508515
value = copy.deepcopy(subobj[part])
509-
obj = AddOperation({'op': 'add', 'path': self.location, 'value': value}).apply(obj)
516+
517+
obj = AddOperation({
518+
'op': 'add',
519+
'path': self.location,
520+
'value': value
521+
}).apply(obj)
522+
510523
return obj

0 commit comments

Comments
 (0)