Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to pyproject.toml #62

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
53 changes: 37 additions & 16 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,16 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up Python 3.12
uses: actions/setup-python@v5
- name: "Setup Python, Poetry and Dependencies"
uses: dsoftwareinc/setup-python-poetry-action@v1
with:
python-version: 3.12
allow-prereleases: true
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
python-version: "3.12"
poetry-version: "1.8.3"

- name: Run flake8
shell: bash
run: |
flake8
poetry run flake8

test:
needs: [ lint ]
Expand All @@ -40,19 +37,43 @@ jobs:
fail-fast: false
matrix:
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ]
poetry-version: [ "1.8.3" ]

exclude:
- python-version: "3.7"
poetry-version: "1.8.3"
include:
- python-version: "3.7"
poetry-version: "1.5.1"

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5

- name: "Setup Python, Poetry and Dependencies"
uses: dsoftwareinc/setup-python-poetry-action@v1
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install dependencies
poetry-version: ${{ matrix.poetry-version }}

- name: Test
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
poetry install --no-interaction
poetry run python -m unittest

test-with-coverage:
needs: [ test ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: "Setup Python, Poetry and Dependencies"
uses: dsoftwareinc/setup-python-poetry-action@v1
with:
python-version: "3.12"
poetry-version: "1.8.3"

- name: Test
run: |
make coverage
poetry install --no-interaction
poetry run coverage run -m unittest
poetry run coverage report -m
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dist
doc/_build
*.egg-info
.idea
.venv
3 changes: 0 additions & 3 deletions AUTHORS

This file was deleted.

File renamed without changes.
4 changes: 0 additions & 4 deletions MANIFEST.in

This file was deleted.

13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ python-json-pointer
[![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
[![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)


Resolve JSON Pointers in Python
-------------------------------
# Resolve JSON Pointers in Python

Library to resolve JSON Pointers according to
[RFC 6901](http://tools.ietf.org/html/rfc6901)

See source code for examples

* Website: https://github.com/stefankoegl/python-json-pointer
* Repository: https://github.com/stefankoegl/python-json-pointer.git
* Documentation: https://python-json-pointer.readthedocs.org/
* PyPI: https://pypi.python.org/pypi/jsonpointer
* Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer
* Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer

# Installation

Using pip:

```bash
pip install jsonpointer
```
58 changes: 58 additions & 0 deletions jsonpointer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
#
# python-json-pointer - An implementation of the JSON Pointer syntax
# https://github.com/stefankoegl/python-json-pointer
#
# Copyright (c) 2011 Stefan Kögl <[email protected]>
# 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.
#

""" Identify specific nodes in a JSON document (RFC 6901) """
from .jsonpointer import JsonPointerException, JsonPointer, resolve_pointer, set_pointer, EndOfList

try:
from importlib import metadata
except ImportError: # for Python < 3.8
import importlib_metadata as metadata # type: ignore

# Will be parsed by setup.py to determine package metadata
jsonpointer_metadata = metadata.metadata("jsonpointer")
__author__ = jsonpointer_metadata["Author"]
__version__ = metadata.version("jsonpointer")
__website__ = jsonpointer_metadata["Home-page"]
__license__ = jsonpointer_metadata["License"]

__all__ = [
"resolve_pointer",
"set_pointer",
"JsonPointerException",
"JsonPointer",
"EndOfList",
"__author__",
"__version__",
"__website__",
"__license__",
]
98 changes: 30 additions & 68 deletions jsonpointer.py → jsonpointer/jsonpointer.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,3 @@
# -*- coding: utf-8 -*-
#
# python-json-pointer - An implementation of the JSON Pointer syntax
# https://github.com/stefankoegl/python-json-pointer
#
# Copyright (c) 2011 Stefan Kögl <[email protected]>
# 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.
#

""" Identify specific nodes in a JSON document (RFC 6901) """

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

import copy
import re
from collections.abc import Mapping, Sequence
Expand Down Expand Up @@ -73,7 +33,7 @@ def set_pointer(doc, pointer, value, inplace=True):


def resolve_pointer(doc, pointer, default=_nothing):
""" Resolves pointer against doc and returns the referenced object
"""Resolves pointer against doc and returns the referenced object

>>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}, 'a%20b': 1, 'c d': 2}

Expand Down Expand Up @@ -113,7 +73,7 @@ def resolve_pointer(doc, pointer, default=_nothing):


def pairwise(iterable):
""" Transforms a list to a list of tuples of adjacent items
"""Transforms a list to a list of tuples of adjacent items

s -> (s0,s1), (s1,s2), (s2, s3), ...

Expand Down Expand Up @@ -143,29 +103,29 @@ def __init__(self, list_):
self.list_ = list_

def __repr__(self):
return '{cls}({lst})'.format(cls=self.__class__.__name__,
lst=repr(self.list_))
return "{cls}({lst})".format(cls=self.__class__.__name__, lst=repr(self.list_))


class JsonPointer(object):
"""A JSON Pointer that can reference parts of a JSON document"""

# Array indices must not contain:
# leading zeros, signs, spaces, decimals, etc
_RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$')
_RE_INVALID_ESCAPE = re.compile('(~[^01]|~$)')
_RE_ARRAY_INDEX = re.compile("0|[1-9][0-9]*$")
_RE_INVALID_ESCAPE = re.compile("(~[^01]|~$)")

def __init__(self, pointer):

# validate escapes
invalid_escape = self._RE_INVALID_ESCAPE.search(pointer)
if invalid_escape:
raise JsonPointerException('Found invalid escape {}'.format(
invalid_escape.group()))
raise JsonPointerException(
"Found invalid escape {}".format(invalid_escape.group())
)

parts = pointer.split('/')
if parts.pop(0) != '':
raise JsonPointerException('Location must start with /')
parts = pointer.split("/")
if parts.pop(0) != "":
raise JsonPointerException("Location must start with /")

parts = [unescape(part) for part in parts]
self.parts = parts
Expand Down Expand Up @@ -203,15 +163,15 @@ def set(self, doc, value, inplace=True):

if len(self.parts) == 0:
if inplace:
raise JsonPointerException('Cannot set root in place')
raise JsonPointerException("Cannot set root in place")
return value

if not inplace:
doc = copy.deepcopy(doc)

(parent, part) = self.to_last(doc)

if isinstance(parent, Sequence) and part == '-':
if isinstance(parent, Sequence) and part == "-":
parent.append(value)
else:
parent[part] = value
Expand All @@ -227,37 +187,39 @@ def get_part(cls, doc, part):

elif isinstance(doc, Sequence):

if part == '-':
if part == "-":
return part

if not JsonPointer._RE_ARRAY_INDEX.match(str(part)):
raise JsonPointerException("'%s' is not a valid sequence index" % part)

return int(part)

elif hasattr(doc, '__getitem__'):
elif hasattr(doc, "__getitem__"):
# Allow indexing via ducktyping
# if the target has defined __getitem__
return part

else:
raise JsonPointerException("Document '%s' does not support indexing, "
"must be mapping/sequence or support __getitem__" % type(doc))
raise JsonPointerException(
"Document '%s' does not support indexing, "
"must be mapping/sequence or support __getitem__" % type(doc)
)

def get_parts(self):
"""Returns the list of the parts. For example, JsonPointer('/a/b').get_parts() == ['a', 'b']"""

return self.parts

def walk(self, doc, part):
""" Walks one step in doc and returns the referenced part """
"""Walks one step in doc and returns the referenced part"""

part = JsonPointer.get_part(doc, part)

assert hasattr(doc, '__getitem__'), "invalid document type %s" % (type(doc),)
assert hasattr(doc, "__getitem__"), "invalid document type %s" % (type(doc),)

if isinstance(doc, Sequence):
if part == '-':
if part == "-":
return EndOfList(doc)

try:
Expand All @@ -274,15 +236,15 @@ def walk(self, doc, part):
raise JsonPointerException("member '%s' not found in %s" % (part, doc))

def contains(self, ptr):
""" Returns True if self contains the given ptr """
return self.parts[:len(ptr.parts)] == ptr.parts
"""Returns True if self contains the given ptr"""
return self.parts[: len(ptr.parts)] == ptr.parts

def __contains__(self, item):
""" Returns True if self contains the given ptr """
"""Returns True if self contains the given ptr"""
return self.contains(item)

def join(self, suffix):
""" Returns a new JsonPointer with the given suffix append to this ptr """
"""Returns a new JsonPointer with the given suffix append to this ptr"""
if isinstance(suffix, JsonPointer):
suffix_parts = suffix.parts
elif isinstance(suffix, str):
Expand All @@ -304,7 +266,7 @@ def path(self):
>>> ptr = JsonPointer('/~0/0/~1').path == '/~0/0/~1'
"""
parts = [escape(part) for part in self.parts]
return ''.join('/' + part for part in parts)
return "".join("/" + part for part in parts)

def __eq__(self, other):
"""Compares a pointer to another object
Expand Down Expand Up @@ -336,13 +298,13 @@ def from_parts(cls, parts):
True
"""
parts = [escape(str(part)) for part in parts]
ptr = cls(''.join('/' + part for part in parts))
ptr = cls("".join("/" + part for part in parts))
return ptr


def escape(s):
return s.replace('~', '~0').replace('/', '~1')
return s.replace("~", "~0").replace("/", "~1")


def unescape(s):
return s.replace('~1', '/').replace('~0', '~')
return s.replace("~1", "/").replace("~0", "~")
Loading