Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
jsonpointer.py347 linesDownload Raw Back to site-packages
1# python-json-pointer - An implementation of the JSON Pointer syntax2# https://github.com/stefankoegl/python-json-pointer3#4# Copyright (c) 2011 Stefan Kögl <stefan@skoegl.net>5# All rights reserved.6#7# Redistribution and use in source and binary forms, with or without8# modification, are permitted provided that the following conditions9# are met:10#11# 1. Redistributions of source code must retain the above copyright12# notice, this list of conditions and the following disclaimer.13# 2. Redistributions in binary form must reproduce the above copyright14# notice, this list of conditions and the following disclaimer in the15# documentation and/or other materials provided with the distribution.16# 3. The name of the author may not be used to endorse or promote products17# derived from this software without specific prior written permission.18#19# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR20# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES21# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.22# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,23# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT24# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF28# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29#30 31""" Identify specific nodes in a JSON document (RFC 6901) """32 33# Will be parsed by setup.py to determine package metadata34__author__ = 'Stefan Kögl <stefan@skoegl.net>'35__version__ = '3.1.1'36__website__ = 'https://github.com/stefankoegl/python-json-pointer'37__license__ = 'Modified BSD License'38 39import copy40import re41from collections.abc import Mapping, Sequence42from itertools import tee, chain43 44_nothing = object()45 46 47def set_pointer(doc, pointer, value, inplace=True):48    """Resolves a pointer against doc and sets the value of the target within doc.49 50    With inplace set to true, doc is modified as long as pointer is not the51    root.52 53    >>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}}54 55    >>> set_pointer(obj, '/foo/anArray/0/prop', 55) == \56    {'foo': {'another prop': {'baz': 'A string'}, 'anArray': [{'prop': 55}]}}57    True58 59    >>> set_pointer(obj, '/foo/yet another prop', 'added prop') == \60    {'foo': {'another prop': {'baz': 'A string'}, 'yet another prop': 'added prop', 'anArray': [{'prop': 55}]}}61    True62 63    >>> obj = {'foo': {}}64    >>> set_pointer(obj, '/foo/a%20b', 'x') == \65    {'foo': {'a%20b': 'x' }}66    True67    """68 69    pointer = JsonPointer(pointer)70    return pointer.set(doc, value, inplace)71 72 73def resolve_pointer(doc, pointer, default=_nothing):74    """ Resolves pointer against doc and returns the referenced object75 76    >>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}, 'a%20b': 1, 'c d': 2}77 78    >>> resolve_pointer(obj, '') == obj79    True80 81    >>> resolve_pointer(obj, '/foo') == obj['foo']82    True83 84    >>> resolve_pointer(obj, '/foo/another prop') == obj['foo']['another prop']85    True86 87    >>> resolve_pointer(obj, '/foo/another prop/baz') == obj['foo']['another prop']['baz']88    True89 90    >>> resolve_pointer(obj, '/foo/anArray/0') == obj['foo']['anArray'][0]91    True92 93    >>> resolve_pointer(obj, '/some/path', None) == None94    True95 96    >>> resolve_pointer(obj, '/a b', None) == None97    True98 99    >>> resolve_pointer(obj, '/a%20b') == 1100    True101 102    >>> resolve_pointer(obj, '/c d') == 2103    True104 105    >>> resolve_pointer(obj, '/c%20d', None) == None106    True107    """108 109    pointer = JsonPointer(pointer)110    return pointer.resolve(doc, default)111 112 113def pairwise(iterable):114    """ Transforms a list to a list of tuples of adjacent items115 116    s -> (s0,s1), (s1,s2), (s2, s3), ...117 118    >>> list(pairwise([]))119    []120 121    >>> list(pairwise([1]))122    []123 124    >>> list(pairwise([1, 2, 3, 4]))125    [(1, 2), (2, 3), (3, 4)]126    """127    a, b = tee(iterable)128    for _ in b:129        break130    return zip(a, b)131 132 133class JsonPointerException(Exception):134    pass135 136 137class EndOfList:138    """Result of accessing element "-" of a list"""139 140    def __init__(self, list_):141        self.list_ = list_142 143    def __repr__(self):144        return '{cls}({lst})'.format(cls=self.__class__.__name__,145                                     lst=repr(self.list_))146 147 148class JsonPointer:149    """A JSON Pointer that can reference parts of a JSON document"""150 151    # Array indices must not contain:152    # leading zeros, signs, spaces, decimals, etc153    _RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$')154    _RE_INVALID_ESCAPE = re.compile('(~[^01]|~$)')155 156    def __init__(self, pointer):157 158        # validate escapes159        invalid_escape = self._RE_INVALID_ESCAPE.search(pointer)160        if invalid_escape:161            raise JsonPointerException('Found invalid escape {}'.format(162                invalid_escape.group()))163 164        parts = pointer.split('/')165        if parts.pop(0) != '':166            raise JsonPointerException('Location must start with /')167 168        parts = [unescape(part) for part in parts]169        self.parts = parts170 171    def to_last(self, doc):172        """Resolves ptr until the last step, returns (sub-doc, last-step)"""173 174        if not self.parts:175            return doc, None176 177        for part in self.parts[:-1]:178            doc = self.walk(doc, part)179 180        return doc, JsonPointer.get_part(doc, self.parts[-1])181 182    def resolve(self, doc, default=_nothing):183        """Resolves the pointer against doc and returns the referenced object"""184 185        for part in self.parts:186 187            try:188                doc = self.walk(doc, part)189            except JsonPointerException:190                if default is _nothing:191                    raise192                else:193                    return default194 195        return doc196 197    get = resolve198 199    def set(self, doc, value, inplace=True):200        """Resolve the pointer against the doc and replace the target with value."""201 202        if len(self.parts) == 0:203            if inplace:204                raise JsonPointerException('Cannot set root in place')205            return value206 207        if not inplace:208            doc = copy.deepcopy(doc)209 210        (parent, part) = self.to_last(doc)211 212        if isinstance(parent, Sequence) and part == '-':213            parent.append(value)214        else:215            parent[part] = value216 217        return doc218 219    @classmethod220    def get_part(cls, doc, part):221        """Returns the next step in the correct type"""222 223        if isinstance(doc, Mapping):224            return part225 226        elif isinstance(doc, Sequence):227 228            if part == '-':229                return part230 231            if not JsonPointer._RE_ARRAY_INDEX.fullmatch(str(part)):232                raise JsonPointerException("'%s' is not a valid sequence index" % part)233 234            return int(part)235 236        elif hasattr(doc, '__getitem__'):237            # Allow indexing via ducktyping238            # if the target has defined __getitem__239            return part240 241        else:242            raise JsonPointerException("Document '%s' does not support indexing, "243                                       "must be mapping/sequence or support __getitem__" % type(doc))244 245    def get_parts(self):246        """Returns the list of the parts. For example, JsonPointer('/a/b').get_parts() == ['a', 'b']"""247 248        return self.parts249 250    def walk(self, doc, part):251        """ Walks one step in doc and returns the referenced part """252 253        part = JsonPointer.get_part(doc, part)254 255        assert hasattr(doc, '__getitem__'), "invalid document type %s" % (type(doc),)256 257        if isinstance(doc, Sequence):258            if part == '-':259                return EndOfList(doc)260 261            try:262                return doc[part]263 264            except IndexError:265                raise JsonPointerException("index '%s' is out of bounds" % (part,))266 267        # Else the object is a mapping or supports __getitem__(so assume custom indexing)268        try:269            return doc[part]270 271        except KeyError:272            raise JsonPointerException("member '%s' not found in %s" % (part, doc))273 274    def contains(self, ptr):275        """ Returns True if self contains the given ptr """276        return self.parts[:len(ptr.parts)] == ptr.parts277 278    def __contains__(self, item):279        """ Returns True if self contains the given ptr """280        return self.contains(item)281 282    def join(self, suffix):283        """ Returns a new JsonPointer with the given suffix append to this ptr """284        if isinstance(suffix, JsonPointer):285            suffix_parts = suffix.parts286        elif isinstance(suffix, str):287            suffix_parts = JsonPointer(suffix).parts288        else:289            suffix_parts = suffix290        try:291            return JsonPointer.from_parts(chain(self.parts, suffix_parts))292        except:  # noqa E722293            raise JsonPointerException("Invalid suffix")294 295    def __truediv__(self, suffix):296        return self.join(suffix)297 298    @property299    def path(self):300        """Returns the string representation of the pointer301 302        >>> ptr = JsonPointer('/~0/0/~1').path == '/~0/0/~1'303        """304        parts = [escape(part) for part in self.parts]305        return ''.join('/' + part for part in parts)306 307    def __eq__(self, other):308        """Compares a pointer to another object309 310        Pointers can be compared by comparing their strings (or splitted311        strings), because no two different parts can point to the same312        structure in an object (eg no different number representations)313        """314 315        if not isinstance(other, JsonPointer):316            return False317 318        return self.parts == other.parts319 320    def __hash__(self):321        return hash(tuple(self.parts))322 323    def __str__(self):324        return self.path325 326    def __repr__(self):327        return type(self).__name__ + "(" + repr(self.path) + ")"328 329    @classmethod330    def from_parts(cls, parts):331        """Constructs a JsonPointer from a list of (unescaped) paths332 333        >>> JsonPointer.from_parts(['a', '~', '/', 0]).path == '/a/~0/~1/0'334        True335        """336        parts = [escape(str(part)) for part in parts]337        ptr = cls(''.join('/' + part for part in parts))338        return ptr339 340 341def escape(s):342    return s.replace('~', '~0').replace('/', '~1')343 344 345def unescape(s):346    return s.replace('~1', '/').replace('~0', '~')347 
codekingpro/portable-devtools · Team Ai