Linux ams-business-8.hostwindsdns.com 4.18.0-553.80.1.lve.el8.x86_64 #1 SMP Wed Oct 22 19:29:36 UTC 2025 x86_64
LiteSpeed
Server IP : 192.236.177.161 & Your IP : 216.73.216.154
Domains :
Cant Read [ /etc/named.conf ]
User : ajzdfbpz
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
/
lib /
python3.6 /
site-packages /
Delete
Unzip
Name
Size
Permission
Date
Action
Babel-2.5.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
Jinja2-2.10.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2025-04-01 13:55
Markdown-2.6.11-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:57
OpenSSL
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
PyJWT-1.6.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
PySocks-1.6.8-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:53
S3
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
__pycache__
[ DIR ]
drwxr-xr-x
2026-02-05 20:05
authselect
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
babel
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
beautifulsoup4-4.6.3-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
boto3
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
boto3-1.15.15-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
botocore
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
botocore-1.18.15-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
bs4
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
chardet
[ DIR ]
drwxr-xr-x
2024-02-12 23:53
chardet-3.0.4-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:53
cloud_init-23.4-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2026-01-24 08:06
cloudinit
[ DIR ]
drwxr-xr-x
2026-01-24 08:06
cssselect
[ DIR ]
drwxr-xr-x
2024-02-12 23:53
cssselect-0.9.2-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:53
dateutil
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
decorator-4.2.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:57
dnf
[ DIR ]
drwxr-xr-x
2025-03-13 11:30
dnf-plugins
[ DIR ]
drwxr-xr-x
2025-03-11 09:48
dnfpluginscore
[ DIR ]
drwxr-xr-x
2024-05-31 13:25
file_magic-0.3.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2025-10-10 11:30
html5lib
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
html5lib-0.999999999-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
idna
[ DIR ]
drwxr-xr-x
2024-07-04 11:33
idna-2.5-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-07-04 11:33
iniparse
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
iniparse-0.4-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
iotop
[ DIR ]
drwxr-xr-x
2024-05-31 13:25
isc
[ DIR ]
drwxr-xr-x
2025-11-07 14:21
javapackages
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
javapackages-5.3.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
jinja2
[ DIR ]
drwxr-xr-x
2025-04-01 13:55
jmespath
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
jmespath-0.9.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
jsonpatch-1.21-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
jsonpointer-1.10-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
jsonschema
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
jsonschema-2.6.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
jwcrypto
[ DIR ]
drwxr-xr-x
2024-05-31 13:25
jwcrypto-0.5.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-05-31 13:25
jwt
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
markdown
[ DIR ]
drwxr-xr-x
2024-02-12 23:57
oauthlib
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
oauthlib-2.1.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
passlib
[ DIR ]
drwxr-xr-x
2024-02-13 01:26
passlib-1.7.2-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-13 01:26
pip
[ DIR ]
drwxr-xr-x
2024-05-31 13:24
pip-9.0.3.dist-info
[ DIR ]
drwxr-xr-x
2024-05-31 13:24
pkg_resources
[ DIR ]
drwxr-xr-x
2025-07-17 11:31
ply
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
ply-3.9-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
prettytable-0.7.2-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
procfs
[ DIR ]
drwxr-xr-x
2024-05-31 13:24
pyOpenSSL-19.0.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
pyasn1
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
pyasn1-0.3.7-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
pycparser
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
pycparser-2.14-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
pyparsing-2.1.10.dist-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:53
pyserial-3.1.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-05-31 13:24
python_dateutil-2.6.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
python_linux_procfs-0.7.3-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-05-31 13:24
pytz
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
pytz-2017.2-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
pyudev
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
pyudev-0.21.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
requests
[ DIR ]
drwxr-xr-x
2025-08-07 11:31
requests-2.20.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2025-08-07 11:31
requests_oauthlib
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
requests_oauthlib-1.0.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
rhn
[ DIR ]
drwxr-xr-x
2025-05-29 13:47
rsa
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
rsa-4.9-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
s3cmd-2.4.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:56
s3transfer
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
s3transfer-0.3.4-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
sepolgen
[ DIR ]
drwxr-xr-x
2024-07-04 11:32
sepolicy
[ DIR ]
drwxr-xr-x
2024-07-04 11:32
serial
[ DIR ]
drwxr-xr-x
2024-05-31 13:24
setuptools
[ DIR ]
drwxr-xr-x
2025-07-17 11:31
setuptools-39.2.0.dist-info
[ DIR ]
drwxr-xr-x
2025-07-17 11:31
six-1.11.0.dist-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:51
syspurpose
[ DIR ]
drwxr-xr-x
2025-07-17 11:31
syspurpose-1.28.44-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2025-07-17 11:31
tuned
[ DIR ]
drwxr-xr-x
2025-03-13 11:30
up2date_client
[ DIR ]
drwxr-xr-x
2025-06-27 11:31
urllib3
[ DIR ]
drwxr-xr-x
2026-01-30 12:31
urllib3-1.24.2-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2026-01-30 12:31
virtualenv-15.1.0-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-12-12 14:36
virtualenv_support
[ DIR ]
drwxr-xr-x
2024-12-12 14:36
webencodings
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
webencodings-0.5.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-12 23:54
wheel
[ DIR ]
drwxr-xr-x
2024-02-13 01:26
wheel-0.31.1-py3.6.egg-info
[ DIR ]
drwxr-xr-x
2024-02-13 01:26
IPy-1.00-py3.6.egg-info
13.48
KB
-rw-r--r--
2021-05-05 19:44
IPy.py
59.89
KB
-rw-r--r--
2019-02-28 00:00
_version.py
21
B
-rw-r--r--
2014-08-26 01:11
configobj-5.0.6-py3.6.egg-info
2.96
KB
-rw-r--r--
2019-11-14 17:06
configobj.py
87.53
KB
-rw-r--r--
2014-08-26 01:11
decorator.py
16.18
KB
-rw-r--r--
2018-01-14 10:00
easy_install.py
126
B
-rw-r--r--
2025-07-15 09:13
hwdata.py
7.95
KB
-rw-r--r--
2018-02-12 08:49
iotop-0.6-py3.6.egg-info
348
B
-rw-r--r--
2024-04-06 14:56
isc-2.0-py3.6.egg-info
267
B
-rw-r--r--
2025-11-06 07:45
jsonpatch.py
23.9
KB
-rw-r--r--
2017-12-30 14:06
jsonpointer.py
9.15
KB
-rw-r--r--
2015-10-28 19:06
magic.py
8.66
KB
-rw-r--r--
2025-10-07 06:30
pciutils-2.3.6-py3.6.egg-info
242
B
-rw-r--r--
2019-11-19 06:36
prettytable.py
52.93
KB
-rw-r--r--
2013-04-06 23:44
pyparsing.py
224.48
KB
-rw-r--r--
2019-11-13 10:36
rhnlib-2.8.6-py3.6.egg-info
343
B
-rw-r--r--
2024-09-17 10:10
seobject.py
106.42
KB
-rw-r--r--
2024-07-02 21:08
sepolicy-1.1-py3.6.egg-info
227
B
-rw-r--r--
2024-07-02 21:08
six.py
30.16
KB
-rw-r--r--
2019-11-13 14:45
socks.py
31.52
KB
-rw-r--r--
2017-12-21 04:05
sockshandler.py
2.84
KB
-rw-r--r--
2017-12-21 04:05
validate.py
46.13
KB
-rw-r--r--
2014-08-26 01:11
virtualenv.py
98.96
KB
-rw-r--r--
2024-12-12 07:39
Save
Rename
# -*- coding: utf-8 -*- # # python-json-patch - An implementation of the JSON Patch format # https://github.com/stefankoegl/python-json-patch # # Copyright (c) 2011 Stefan Kögl <stefan@skoegl.net> # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # """ Apply JSON-Patches (RFC 6902) """ from __future__ import unicode_literals import collections import copy import functools import inspect import itertools import json import sys from jsonpointer import JsonPointer, JsonPointerException _ST_ADD = 0 _ST_REMOVE = 1 try: from collections.abc import MutableMapping, MutableSequence except ImportError: from collections import MutableMapping, MutableSequence str = unicode # Will be parsed by setup.py to determine package metadata __author__ = 'Stefan Kögl <stefan@skoegl.net>' __version__ = '1.21' __website__ = 'https://github.com/stefankoegl/python-json-patch' __license__ = 'Modified BSD License' # pylint: disable=E0611,W0404 if sys.version_info >= (3, 0): basestring = (bytes, str) # pylint: disable=C0103,W0622 class JsonPatchException(Exception): """Base Json Patch exception""" class InvalidJsonPatch(JsonPatchException): """ Raised if an invalid JSON Patch is created """ class JsonPatchConflict(JsonPatchException): """Raised if patch could not be applied due to conflict situation such as: - attempt to add object key then it already exists; - attempt to operate with nonexistence object key; - attempt to insert value to array at position beyond of it size; - etc. """ class JsonPatchTestFailed(JsonPatchException, AssertionError): """ A Test operation failed """ def multidict(ordered_pairs): """Convert duplicate keys values to lists.""" # read all values into lists mdict = collections.defaultdict(list) for key, value in ordered_pairs: mdict[key].append(value) return dict( # unpack lists that have only 1 item (key, values[0] if len(values) == 1 else values) for key, values in mdict.items() ) # The "object_pairs_hook" parameter is used to handle duplicate keys when # loading a JSON object. _jsonloads = functools.partial(json.loads, object_pairs_hook=multidict) def apply_patch(doc, patch, in_place=False): """Apply list of patches to specified json document. :param doc: Document object. :type doc: dict :param patch: JSON patch as list of dicts or raw JSON-encoded string. :type patch: list or str :param in_place: While :const:`True` patch will modify target document. By default patch will be applied to document copy. :type in_place: bool :return: Patched document object. :rtype: dict >>> doc = {'foo': 'bar'} >>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}] >>> other = apply_patch(doc, patch) >>> doc is not other True >>> other == {'foo': 'bar', 'baz': 'qux'} True >>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}] >>> apply_patch(doc, patch, in_place=True) == {'foo': 'bar', 'baz': 'qux'} True >>> doc == other True """ if isinstance(patch, basestring): patch = JsonPatch.from_string(patch) else: patch = JsonPatch(patch) return patch.apply(doc, in_place) def make_patch(src, dst): """Generates patch by comparing of two document objects. Actually is a proxy to :meth:`JsonPatch.from_diff` method. :param src: Data source document object. :type src: dict :param dst: Data source document object. :type dst: dict >>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]} >>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]} >>> patch = make_patch(src, dst) >>> new = patch.apply(src) >>> new == dst True """ return JsonPatch.from_diff(src, dst) class JsonPatch(object): """A JSON Patch is a list of Patch Operations. >>> patch = JsonPatch([ ... {'op': 'add', 'path': '/foo', 'value': 'bar'}, ... {'op': 'add', 'path': '/baz', 'value': [1, 2, 3]}, ... {'op': 'remove', 'path': '/baz/1'}, ... {'op': 'test', 'path': '/baz', 'value': [1, 3]}, ... {'op': 'replace', 'path': '/baz/0', 'value': 42}, ... {'op': 'remove', 'path': '/baz/1'}, ... ]) >>> doc = {} >>> result = patch.apply(doc) >>> expected = {'foo': 'bar', 'baz': [42]} >>> result == expected True JsonPatch object is iterable, so you could easily access to each patch statement in loop: >>> lpatch = list(patch) >>> expected = {'op': 'add', 'path': '/foo', 'value': 'bar'} >>> lpatch[0] == expected True >>> lpatch == patch.patch True Also JsonPatch could be converted directly to :class:`bool` if it contains any operation statements: >>> bool(patch) True >>> bool(JsonPatch([])) False This behavior is very handy with :func:`make_patch` to write more readable code: >>> old = {'foo': 'bar', 'numbers': [1, 3, 4, 8]} >>> new = {'baz': 'qux', 'numbers': [1, 4, 7]} >>> patch = make_patch(old, new) >>> if patch: ... # document have changed, do something useful ... patch.apply(old) #doctest: +ELLIPSIS {...} """ def __init__(self, patch): self.patch = patch self.operations = { 'remove': RemoveOperation, 'add': AddOperation, 'replace': ReplaceOperation, 'move': MoveOperation, 'test': TestOperation, 'copy': CopyOperation, } def __str__(self): """str(self) -> self.to_string()""" return self.to_string() def __bool__(self): return bool(self.patch) __nonzero__ = __bool__ def __iter__(self): return iter(self.patch) def __hash__(self): return hash(tuple(self._ops)) def __eq__(self, other): if not isinstance(other, JsonPatch): return False return self._ops == other._ops def __ne__(self, other): return not(self == other) @classmethod def from_string(cls, patch_str): """Creates JsonPatch instance from string source. :param patch_str: JSON patch as raw string. :type patch_str: str :return: :class:`JsonPatch` instance. """ patch = _jsonloads(patch_str) return cls(patch) @classmethod def from_diff(cls, src, dst, optimization=True): """Creates JsonPatch instance based on comparing of two document objects. Json patch would be created for `src` argument against `dst` one. :param src: Data source document object. :type src: dict :param dst: Data source document object. :type dst: dict :return: :class:`JsonPatch` instance. >>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]} >>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]} >>> patch = JsonPatch.from_diff(src, dst) >>> new = patch.apply(src) >>> new == dst True """ builder = DiffBuilder() builder._compare_values('', None, src, dst) ops = list(builder.execute()) return cls(ops) def to_string(self): """Returns patch set as JSON string.""" return json.dumps(self.patch) @property def _ops(self): return tuple(map(self._get_operation, self.patch)) def apply(self, obj, in_place=False): """Applies the patch to given object. :param obj: Document object. :type obj: dict :param in_place: Tweaks way how patch would be applied - directly to specified `obj` or to his copy. :type in_place: bool :return: Modified `obj`. """ if not in_place: obj = copy.deepcopy(obj) for operation in self._ops: obj = operation.apply(obj) return obj def _get_operation(self, operation): if 'op' not in operation: raise InvalidJsonPatch("Operation does not contain 'op' member") op = operation['op'] if not isinstance(op, basestring): raise InvalidJsonPatch("Operation must be a string") if op not in self.operations: raise InvalidJsonPatch("Unknown operation {0!r}".format(op)) cls = self.operations[op] return cls(operation) class PatchOperation(object): """A single operation inside a JSON Patch.""" def __init__(self, operation): self.location = operation['path'] self.pointer = JsonPointer(self.location) self.operation = operation def apply(self, obj): """Abstract method that applies patch operation to specified object.""" raise NotImplementedError('should implement patch operation.') def __hash__(self): return hash(frozenset(self.operation.items())) def __eq__(self, other): if not isinstance(other, PatchOperation): return False return self.operation == other.operation def __ne__(self, other): return not(self == other) @property def path(self): return '/'.join(self.pointer.parts[:-1]) @property def key(self): try: return int(self.pointer.parts[-1]) except ValueError: return self.pointer.parts[-1] @key.setter def key(self, value): self.pointer.parts[-1] = str(value) self.location = self.pointer.path self.operation['path'] = self.location class RemoveOperation(PatchOperation): """Removes an object property or an array element.""" def apply(self, obj): subobj, part = self.pointer.to_last(obj) try: del subobj[part] except (KeyError, IndexError) as ex: msg = "can't remove non-existent object '{0}'".format(part) raise JsonPatchConflict(msg) return obj def _on_undo_remove(self, path, key): if self.path == path: if self.key >= key: self.key += 1 else: key -= 1 return key def _on_undo_add(self, path, key): if self.path == path: if self.key > key: self.key -= 1 else: key -= 1 return key class AddOperation(PatchOperation): """Adds an object property or an array element.""" def apply(self, obj): try: value = self.operation["value"] except KeyError as ex: raise InvalidJsonPatch( "The operation does not contain a 'value' member") subobj, part = self.pointer.to_last(obj) if isinstance(subobj, MutableSequence): if part == '-': subobj.append(value) # pylint: disable=E1103 elif part > len(subobj) or part < 0: raise JsonPatchConflict("can't insert outside of list") else: subobj.insert(part, value) # pylint: disable=E1103 elif isinstance(subobj, MutableMapping): if part is None: obj = value # we're replacing the root else: subobj[part] = value else: raise TypeError("invalid document type {0}".format(type(subobj))) return obj def _on_undo_remove(self, path, key): if self.path == path: if self.key > key: self.key += 1 else: key += 1 return key def _on_undo_add(self, path, key): if self.path == path: if self.key > key: self.key -= 1 else: key += 1 return key class ReplaceOperation(PatchOperation): """Replaces an object property or an array element by new value.""" def apply(self, obj): try: value = self.operation["value"] except KeyError as ex: raise InvalidJsonPatch( "The operation does not contain a 'value' member") subobj, part = self.pointer.to_last(obj) if part is None: return value if isinstance(subobj, MutableSequence): if part > len(subobj) or part < 0: raise JsonPatchConflict("can't replace outside of list") elif isinstance(subobj, MutableMapping): if part not in subobj: msg = "can't replace non-existent object '{0}'".format(part) raise JsonPatchConflict(msg) else: raise TypeError("invalid document type {0}".format(type(subobj))) subobj[part] = value return obj def _on_undo_remove(self, path, key): return key def _on_undo_add(self, path, key): return key class MoveOperation(PatchOperation): """Moves an object property or an array element to new location.""" def apply(self, obj): try: from_ptr = JsonPointer(self.operation['from']) except KeyError as ex: raise InvalidJsonPatch( "The operation does not contain a 'from' member") subobj, part = from_ptr.to_last(obj) try: value = subobj[part] except (KeyError, IndexError) as ex: raise JsonPatchConflict(str(ex)) # If source and target are equal, this is a no-op if self.pointer == from_ptr: return obj if isinstance(subobj, MutableMapping) and \ self.pointer.contains(from_ptr): raise JsonPatchConflict('Cannot move values into its own children') obj = RemoveOperation({ 'op': 'remove', 'path': self.operation['from'] }).apply(obj) obj = AddOperation({ 'op': 'add', 'path': self.location, 'value': value }).apply(obj) return obj @property def from_path(self): from_ptr = JsonPointer(self.operation['from']) return '/'.join(from_ptr.parts[:-1]) @property def from_key(self): from_ptr = JsonPointer(self.operation['from']) try: return int(from_ptr.parts[-1]) except TypeError: return from_ptr.parts[-1] @from_key.setter def from_key(self, value): from_ptr = JsonPointer(self.operation['from']) from_ptr.parts[-1] = str(value) self.operation['from'] = from_ptr.path def _on_undo_remove(self, path, key): if self.from_path == path: if self.from_key >= key: self.from_key += 1 else: key -= 1 if self.path == path: if self.key > key: self.key += 1 else: key += 1 return key def _on_undo_add(self, path, key): if self.from_path == path: if self.from_key > key: self.from_key -= 1 else: key -= 1 if self.path == path: if self.key > key: self.key -= 1 else: key += 1 return key class TestOperation(PatchOperation): """Test value by specified location.""" def apply(self, obj): try: subobj, part = self.pointer.to_last(obj) if part is None: val = subobj else: val = self.pointer.walk(subobj, part) except JsonPointerException as ex: raise JsonPatchTestFailed(str(ex)) try: value = self.operation['value'] except KeyError as ex: raise InvalidJsonPatch( "The operation does not contain a 'value' member") if val != value: msg = '{0} ({1}) is not equal to tested value {2} ({3})' raise JsonPatchTestFailed(msg.format(val, type(val), value, type(value))) return obj class CopyOperation(PatchOperation): """ Copies an object property or an array element to a new location """ def apply(self, obj): try: from_ptr = JsonPointer(self.operation['from']) except KeyError as ex: raise InvalidJsonPatch( "The operation does not contain a 'from' member") subobj, part = from_ptr.to_last(obj) try: value = copy.deepcopy(subobj[part]) except (KeyError, IndexError) as ex: raise JsonPatchConflict(str(ex)) obj = AddOperation({ 'op': 'add', 'path': self.location, 'value': value }).apply(obj) return obj class DiffBuilder(object): def __init__(self): self.index_storage = [{}, {}] self.index_storage2 = [[], []] self.__root = root = [] root[:] = [root, root, None] def store_index(self, value, index, st): try: storage = self.index_storage[st] stored = storage.get(value) if stored is None: storage[value] = [index] else: storage[value].append(index) except TypeError: self.index_storage2[st].append((value, index)) def take_index(self, value, st): try: stored = self.index_storage[st].get(value) if stored: return stored.pop() except TypeError: storage = self.index_storage2[st] for i in range(len(storage)-1, -1, -1): if storage[i][0] == value: return storage.pop(i)[1] def insert(self, op): root = self.__root last = root[0] last[1] = root[0] = [last, root, op] return root[0] def remove(self, index): link_prev, link_next, _ = index link_prev[1] = link_next link_next[0] = link_prev index[:] = [] def iter_from(self, start): root = self.__root curr = start[1] while curr is not root: yield curr[2] curr = curr[1] def __iter__(self): root = self.__root curr = root[1] while curr is not root: yield curr[2] curr = curr[1] def execute(self): root = self.__root curr = root[1] while curr is not root: if curr[1] is not root: op_first, op_second = curr[2], curr[1][2] if op_first.location == op_second.location and \ type(op_first) == RemoveOperation and \ type(op_second) == AddOperation: yield ReplaceOperation({ 'op': 'replace', 'path': op_second.location, 'value': op_second.operation['value'], }).operation curr = curr[1][1] continue yield curr[2].operation curr = curr[1] def _item_added(self, path, key, item): index = self.take_index(item, _ST_REMOVE) if index is not None: op = index[2] if type(op.key) == int: for v in self.iter_from(index): op.key = v._on_undo_remove(op.path, op.key) self.remove(index) if op.location != _path_join(path, key): new_op = MoveOperation({ 'op': 'move', 'from': op.location, 'path': _path_join(path, key), }) self.insert(new_op) else: new_op = AddOperation({ 'op': 'add', 'path': _path_join(path, key), 'value': item, }) new_index = self.insert(new_op) self.store_index(item, new_index, _ST_ADD) def _item_removed(self, path, key, item): new_op = RemoveOperation({ 'op': 'remove', 'path': _path_join(path, key), }) index = self.take_index(item, _ST_ADD) new_index = self.insert(new_op) if index is not None: op = index[2] if type(op.key) == int: for v in self.iter_from(index): op.key = v._on_undo_add(op.path, op.key) self.remove(index) if new_op.location != op.location: new_op = MoveOperation({ 'op': 'move', 'from': new_op.location, 'path': op.location, }) new_index[2] = new_op else: self.remove(new_index) else: self.store_index(item, new_index, _ST_REMOVE) def _item_replaced(self, path, key, item): self.insert(ReplaceOperation({ 'op': 'replace', 'path': _path_join(path, key), 'value': item, })) def _compare_dicts(self, path, src, dst): src_keys = set(src.keys()) dst_keys = set(dst.keys()) added_keys = dst_keys - src_keys removed_keys = src_keys - dst_keys for key in removed_keys: self._item_removed(path, str(key), src[key]) for key in added_keys: self._item_added(path, str(key), dst[key]) for key in src_keys & dst_keys: self._compare_values(path, key, src[key], dst[key]) def _compare_lists(self, path, src, dst): len_src, len_dst = len(src), len(dst) max_len = max(len_src, len_dst) min_len = min(len_src, len_dst) for key in range(max_len): if key < min_len: old, new = src[key], dst[key] if old == new: continue elif isinstance(old, MutableMapping) and \ isinstance(new, MutableMapping): self._compare_dicts(_path_join(path, key), old, new) elif isinstance(old, MutableSequence) and \ isinstance(new, MutableSequence): self._compare_lists(_path_join(path, key), old, new) else: self._item_removed(path, key, old) self._item_added(path, key, new) elif len_src > len_dst: self._item_removed(path, len_dst, src[key]) else: self._item_added(path, key, dst[key]) def _compare_values(self, path, key, src, dst): if src == dst: return elif isinstance(src, MutableMapping) and \ isinstance(dst, MutableMapping): self._compare_dicts(_path_join(path, key), src, dst) elif isinstance(src, MutableSequence) and \ isinstance(dst, MutableSequence): self._compare_lists(_path_join(path, key), src, dst) else: self._item_replaced(path, key, dst) def _path_join(path, key): if key is None: return path return path + '/' + str(key).replace('~', '~0').replace('/', '~1')