Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-jsonpath-ng for 
openSUSE:Factory checked in at 2023-09-20 13:27:06
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jsonpath-ng (Old)
 and      /work/SRC/openSUSE:Factory/.python-jsonpath-ng.new.16627 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jsonpath-ng"

Wed Sep 20 13:27:06 2023 rev:3 rq:1112029 version:1.6.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jsonpath-ng/python-jsonpath-ng.changes    
2021-11-03 17:27:18.205371269 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-jsonpath-ng.new.16627/python-jsonpath-ng.changes
 2023-09-20 13:29:00.450253981 +0200
@@ -1,0 +2,43 @@
+Fri Sep 15 08:51:59 UTC 2023 - [email protected]
+
+- version update to 1.6.0
+  1.6.0 / 2023-09-13
+  *  Enclose field names containing literals in quotes
+  *  Add note about extensions
+  *  Remove documentation status link
+  *  Update supported versions in setup.py
+  *  Add LICENSE file
+  *  Code cleanup
+  *  Remove dependency on six
+  *  Update build status badge
+  *  (origin/github-actions, github-actions) Remove testscenarios dependency
+  *  Remove pytest version constraints
+  *  Add testing with GitHub actions
+  *  Escape back slashes in tests to avoid DeprecationWarning.
+  *  Use raw strings for regular expressions to avoid DeprecationWarning.
+  *  refactor(package): remove dependency for decorator
+  *  Merge pull request #128 from michaelmior/hashable
+  *  (origin/hashable, hashable) Make path instances hashable
+  *  Merge pull request #122 from snopoke/snopoke-patch-1
+  *  Add more detail to filter docs.
+  *  remove incorrect parenthesis in filter examples
+  *  Merge pull request #119 from snopoke/patch-1
+  *  add 'sub' line with function param names
+  *  readme formatting fixes
+  *  chore(history): update
+  *  Update __init__.py
+  1.5.3 / 2021-07-05
+  * Update __init__.py
+  * Update setup.py
+  * Merge pull request #72 from kaapstorm/find_or_create
+  * Tests
+  * Add `update_or_create()` method
+  * Merge pull request #68 from kaapstorm/example_tests
+  * Merge pull request #70 from kaapstorm/exceptions
+  * Add/fix `__eq__()`
+  * Add tests based on Stefan Goessner's examples
+  * Tests
+  * Allow callers to catch JSONPathErrors
+- six and decorator are not required
+
+-------------------------------------------------------------------

Old:
----
  jsonpath-ng-1.5.2.tar.gz

New:
----
  jsonpath-ng-1.6.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-jsonpath-ng.spec ++++++
--- /var/tmp/diff_new_pack.MLnPK8/_old  2023-09-20 13:29:01.614295682 +0200
+++ /var/tmp/diff_new_pack.MLnPK8/_new  2023-09-20 13:29:01.614295682 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-jsonpath-ng
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,9 +16,8 @@
 #
 
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-jsonpath-ng
-Version:        1.5.2
+Version:        1.6.0
 Release:        0
 Summary:        JSONPath for Python
 License:        Apache-2.0
@@ -31,13 +30,13 @@
 Requires:       python-decorator
 Requires:       python-ply
 Requires:       python-six
+Requires(post): update-alternatives
+Requires(postun):update-alternatives
 BuildArch:      noarch
 # SECTION test requirements
-BuildRequires:  %{python_module decorator}
 BuildRequires:  %{python_module ply}
 #
 BuildRequires:  %{python_module pytest}
-BuildRequires:  %{python_module six}
 BuildRequires:  python3-oslotest
 # /SECTION
 %python_subpackages

++++++ jsonpath-ng-1.5.2.tar.gz -> jsonpath-ng-1.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/.github/workflows/ci.yml 
new/jsonpath-ng-1.6.0/.github/workflows/ci.yml
--- old/jsonpath-ng-1.5.2/.github/workflows/ci.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/jsonpath-ng-1.6.0/.github/workflows/ci.yml      2023-09-13 
21:46:25.000000000 +0200
@@ -0,0 +1,28 @@
+name: CI
+on:
+  push:
+    branches:
+      - '*'
+    tags:
+      - 'v*'
+  pull_request:
+    branches:
+      - main
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
+    steps:
+    - uses: actions/checkout@v3
+    - uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install -r requirements.txt
+        pip install -r requirements-dev.txt
+    - name: Run tests
+      run: make test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/History.md 
new/jsonpath-ng-1.6.0/History.md
--- old/jsonpath-ng-1.5.2/History.md    2020-09-07 15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/History.md    2023-09-13 21:46:25.000000000 +0200
@@ -1,3 +1,44 @@
+1.6.0 / 2023-09-13
+===================
+  *  Enclose field names containing literals in quotes
+  *  Add note about extensions
+  *  Remove documentation status link
+  *  Update supported versions in setup.py
+  *  Add LICENSE file
+  *  Code cleanup
+  *  Remove dependency on six
+  *  Update build status badge
+  *  (origin/github-actions, github-actions) Remove testscenarios dependency
+  *  Remove pytest version constraints
+  *  Add testing with GitHub actions
+  *  Escape back slashes in tests to avoid DeprecationWarning.
+  *  Use raw strings for regular expressions to avoid DeprecationWarning.
+  *  refactor(package): remove dependency for decorator
+  *  Merge pull request #128 from michaelmior/hashable
+  *  (origin/hashable, hashable) Make path instances hashable
+  *  Merge pull request #122 from snopoke/snopoke-patch-1
+  *  Add more detail to filter docs.
+  *  remove incorrect parenthesis in filter examples
+  *  Merge pull request #119 from snopoke/patch-1
+  *  add 'sub' line with function param names
+  *  readme formatting fixes
+  *  chore(history): update
+  *  Update __init__.py
+
+1.5.3 / 2021-07-05
+==================
+
+  * Update __init__.py
+  * Update setup.py
+  * Merge pull request #72 from kaapstorm/find_or_create
+  * Tests
+  * Add `update_or_create()` method
+  * Merge pull request #68 from kaapstorm/example_tests
+  * Merge pull request #70 from kaapstorm/exceptions
+  * Add/fix `__eq__()`
+  * Add tests based on Stefan Goessner's examples
+  * Tests
+  * Allow callers to catch JSONPathErrors
 
 v1.5.2 / 2020-09-07
 ===================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/MANIFEST.in 
new/jsonpath-ng-1.6.0/MANIFEST.in
--- old/jsonpath-ng-1.5.2/MANIFEST.in   2020-09-07 15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/MANIFEST.in   2023-09-13 21:46:25.000000000 +0200
@@ -1 +1,2 @@
 recursive-include tests *.json *.py
+include LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/README.rst 
new/jsonpath-ng-1.6.0/README.rst
--- old/jsonpath-ng-1.5.2/README.rst    2020-09-07 15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/README.rst    2023-09-13 21:46:25.000000000 +0200
@@ -187,29 +187,42 @@
 Extensions
 ----------
 
-+--------------+----------------------------------------------+
-| name         | Example                                      |
-+==============+==============================================+
-| len          | - $.objects.`len`                            |
-+--------------+----------------------------------------------+
-| sub          | - $.field.`sub(/foo\\\\+(.*)/, \\\\1)`       |
-+--------------+----------------------------------------------+
-| split        | - $.field.`split(+, 2, -1)`                  |
-|              | - $.field.`split(sep, segement, maxsplit)`   |
-+--------------+----------------------------------------------+
-| sorted       | - $.objects.`sorted`                         |
-|              | - $.objects[\\some_field]                    |
-|              | - $.objects[\\some_field,/other_field]       |
-+--------------+----------------------------------------------+
-| filter       | - $.objects[?(@some_field > 5)]              |
-|              | - $.objects[?some_field = "foobar")]         |
-|              | - $.objects[?some_field =~ "foobar")]        |
-|              | - $.objects[?some_field > 5 & other < 2)]    |
-+--------------+----------------------------------------------+
-| arithmetic   | - $.foo + "_" + $.bar                        |
-| (-+*/)       | - $.foo * 12                                 |
-|              | - $.objects[*].cow + $.objects[*].cat        |
-+--------------+----------------------------------------------+
+To use the extensions below you must import from `jsonpath_ng.ext`.
+
++--------------+-----------------------------------------------+
+| name         | Example                                       |
++==============+===============================================+
+| len          | - ``$.objects.`len```                         |
++--------------+-----------------------------------------------+
+| sub          | - ``$.field.`sub(/foo\\\\+(.*)/, \\\\1)```    |
+|              | - ``$.field.`sub(/regex/, replacement)```     |
++--------------+-----------------------------------------------+
+| split        | - ``$.field.`split(+, 2, -1)```               |
+|              | - ``$.field.`split(sep, segement, maxsplit)```|
++--------------+-----------------------------------------------+
+| sorted       | - ``$.objects.`sorted```                      |
+|              | - ``$.objects[\\some_field]``                 |
+|              | - ``$.objects[\\some_field,/other_field]``    |
++--------------+-----------------------------------------------+
+| filter       | - ``$.objects[?(@some_field > 5)]``           |
+|              | - ``$.objects[?some_field = "foobar"]``       |
+|              | - ``$.objects[?some_field =~ "foobar"]``      |
+|              | - ``$.objects[?some_field > 5 & other < 2]``  |
+|              |                                               |
+|              | Supported operators:                          |
+|              | - Equality: ==, =, !=                         |
+|              | - Comparison: >, >=, <, <=                    |
+|              | - Regex match: =~                             |
+|              |                                               |
+|              | Combine multiple criteria with '&'.           |
+|              |                                               |
+|              | Properties can only be compared to static     |
+|              | values.                                       |
++--------------+-----------------------------------------------+
+| arithmetic   | - ``$.foo + "_" + $.bar``                     |
+| (-+*/)       | - ``$.foo * 12``                              |
+|              | - ``$.objects[*].cow + $.objects[*].cat``     |
++--------------+-----------------------------------------------+
 
 About arithmetic and string
 ---------------------------
@@ -228,10 +241,10 @@
         'fish': 'bar'
     }
 
-| **cow + fish** returns **cowfish**
-| **$.cow + $.fish** returns **foobar**
-| **$.cow + "_" + $.fish** returns **foo_bar**
-| **$.cow + "_" + fish** returns **foo_fish**
+| ``cow + fish`` returns ``cowfish``
+| ``$.cow + $.fish`` returns ``foobar``
+| ``$.cow + "_" + $.fish`` returns ``foo_bar``
+| ``$.cow + "_" + fish`` returns ``foo_fish``
 
 About arithmetic and list
 -------------------------
@@ -245,7 +258,7 @@
         {'cow': 4, 'cat': 6}
     ]}
 
-| **$.objects[\*].cow + $.objects[\*].cat** returns **[6, 9]**
+| ``$.objects[\*].cow + $.objects[\*].cat`` returns ``[6, 9]``
 
 More to explore
 ---------------
@@ -322,9 +335,7 @@
 
 .. |PyPi downloads| image:: https://pypip.in/d/jsonpath-ng/badge.png
    :target: https://pypi.python.org/pypi/jsonpath-ng
-.. |Build Status| image:: 
https://travis-ci.org/h2non/jsonpath-ng.svg?branch=master
-   :target: https://travis-ci.org/h2non/jsonpath-ng
+.. |Build Status| image:: 
https://github.com/h2non/jsonpath-ng/actions/workflows/ci.yml/badge.svg
+   :target: https://github.com/h2non/jsonpath-ng/actions/workflows/ci.yml
 .. |PyPI| image:: 
https://img.shields.io/pypi/v/jsonpath-ng.svg?maxAge=2592000?style=flat-square
    :target: https://pypi.python.org/pypi/jsonpath-ng
-.. |Documentation Status| image:: 
https://img.shields.io/badge/docs-latest-green.svg?style=flat
-   :target: http://jsonpath-ng.readthedocs.io/en/latest/?badge=latest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/__init__.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/__init__.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/__init__.py       2020-09-07 
15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/__init__.py       2023-09-13 
21:46:25.000000000 +0200
@@ -3,4 +3,4 @@
 
 
 # Current package version
-__version__ = '1.5.2'
+__version__ = '1.6.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/exceptions.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/exceptions.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/exceptions.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/exceptions.py     2023-09-13 
21:46:25.000000000 +0200
@@ -0,0 +1,10 @@
+class JSONPathError(Exception):
+    pass
+
+
+class JsonPathLexerError(JSONPathError):
+    pass
+
+
+class JsonPathParserError(JSONPathError):
+    pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/ext/filter.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/ext/filter.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/ext/filter.py     2020-09-07 
15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/ext/filter.py     2023-09-13 
21:46:25.000000000 +0200
@@ -13,7 +13,6 @@
 
 import operator
 import re
-from six import moves
 
 from .. import JSONPath, DatumInContext, Index
 
@@ -49,7 +48,7 @@
             return []
 
         return [DatumInContext(datum.value[i], path=Index(i), context=datum)
-                for i in moves.range(0, len(datum.value))
+                for i in range(0, len(datum.value))
                 if (len(self.expressions) ==
                     len(list(filter(lambda x: x.find(datum.value[i]),
                                     self.expressions))))]
@@ -71,6 +70,10 @@
     def __str__(self):
         return '[?%s]' % self.expressions
 
+    def __eq__(self, other):
+        return (isinstance(other, Filter)
+                and self.expressions == other.expressions)
+
 
 class Expression(JSONPath):
     """The JSONQuery expression"""
@@ -108,7 +111,7 @@
         return found
 
     def __eq__(self, other):
-        return (isinstance(other, Filter) and
+        return (isinstance(other, Expression) and
                 self.target == other.target and
                 self.op == other.op and
                 self.value == other.value)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/ext/string.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/ext/string.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/ext/string.py     2020-09-07 
15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/ext/string.py     2023-09-13 
21:46:25.000000000 +0200
@@ -15,9 +15,9 @@
 from .. import DatumInContext, This
 
 
-SUB = re.compile("sub\(/(.*)/,\s+(.*)\)")
-SPLIT = re.compile("split\((.),\s+(\d+),\s+(\d+|-1)\)")
-STR = re.compile("str\(\)")
+SUB = re.compile(r"sub\(/(.*)/,\s+(.*)\)")
+SPLIT = re.compile(r"split\((.),\s+(\d+),\s+(\d+|-1)\)")
+STR = re.compile(r"str\(\)")
 
 
 class DefintionInvalid(Exception):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/jsonpath.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/jsonpath.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/jsonpath.py       2020-09-07 
15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/jsonpath.py       2023-09-13 
21:46:25.000000000 +0200
@@ -1,8 +1,9 @@
-from __future__ import unicode_literals, print_function, absolute_import, 
division, generators, nested_scopes
+from __future__ import (absolute_import, division, generators, nested_scopes,
+                        print_function, unicode_literals)
+
 import logging
-import six
-from six.moves import xrange
 from itertools import *  # noqa
+from jsonpath_ng.lexer import JsonPathLexer
 
 # Get logger name
 logger = logging.getLogger(__name__)
@@ -11,6 +12,9 @@
 # ... could be a kwarg pervasively but uses are rare and simple today
 auto_id_field = None
 
+NOT_SET = object()
+LIST_KEY = object()
+
 
 class JSONPath(object):
     """
@@ -27,6 +31,9 @@
         """
         raise NotImplementedError()
 
+    def find_or_create(self, data):
+        return self.find(data)
+
     def update(self, data, val):
         """
         Returns `data` with the specified path replaced by `val`. Only updates
@@ -35,6 +42,9 @@
 
         raise NotImplementedError()
 
+    def update_or_create(self, data, val):
+        return self.update(data, val)
+
     def filter(self, fn, data):
         """
         Returns `data` with the specified path filtering nodes according
@@ -210,6 +220,9 @@
     def __eq__(self, other):
         return isinstance(other, Root)
 
+    def __hash__(self):
+        return hash('$')
+
 
 class This(JSONPath):
     """
@@ -234,6 +247,9 @@
     def __eq__(self, other):
         return isinstance(other, This)
 
+    def __hash__(self):
+        return hash('this')
+
 
 class Child(JSONPath):
     """
@@ -261,6 +277,23 @@
             self.right.update(datum.value, val)
         return data
 
+    def find_or_create(self, datum):
+        datum = DatumInContext.wrap(datum)
+        submatches = []
+        for subdata in self.left.find_or_create(datum):
+            if isinstance(subdata, AutoIdForDatum):
+                # Extra special case: auto ids do not have children,
+                # so cut it off right now rather than auto id the auto id
+                continue
+            for submatch in self.right.find_or_create(subdata):
+                submatches.append(submatch)
+        return submatches
+
+    def update_or_create(self, data, val):
+        for datum in self.left.find_or_create(data):
+            self.right.update_or_create(datum.value, val)
+        return _clean_list_keys(data)
+
     def filter(self, fn, data):
         for datum in self.left.find(data):
             self.right.filter(fn, datum.value)
@@ -275,6 +308,9 @@
     def __repr__(self):
         return '%s(%r, %r)' % (self.__class__.__name__, self.left, self.right)
 
+    def __hash__(self):
+        return hash((self.left, self.right))
+
 
 class Parent(JSONPath):
     """
@@ -296,6 +332,9 @@
     def __repr__(self):
         return 'Parent()'
 
+    def __hash__(self):
+        return hash('parent')
+
 
 class Where(JSONPath):
     """
@@ -330,6 +369,9 @@
     def __eq__(self, other):
         return isinstance(other, Where) and other.left == self.left and 
other.right == self.right
 
+    def __hash__(self):
+        return hash((self.left, self.right))
+
 class Descendants(JSONPath):
     """
     JSONPath that matches first the left expression then any descendant
@@ -439,6 +481,13 @@
     def __eq__(self, other):
         return isinstance(other, Descendants) and self.left == other.left and 
self.right == other.right
 
+    def __repr__(self):
+        return '%s(%r, %r)' % (self.__class__.__name__, self.left, self.right)
+
+    def __hash__(self):
+        return hash((self.left, self.right))
+
+
 class Union(JSONPath):
     """
     JSONPath that returns the union of the results of each match.
@@ -459,6 +508,12 @@
     def find(self, data):
         return self.left.find(data) + self.right.find(data)
 
+    def __eq__(self, other):
+        return isinstance(other, Union) and self.left == other.left and 
self.right == other.right
+
+    def __hash__(self):
+        return hash((self.left, self.right))
+
 class Intersect(JSONPath):
     """
     JSONPath for bits that match *both* patterns.
@@ -480,6 +535,12 @@
     def find(self, data):
         raise NotImplementedError()
 
+    def __eq__(self, other):
+        return isinstance(other, Intersect) and self.left == other.left and 
self.right == other.right
+
+    def __hash__(self):
+        return hash((self.left, self.right))
+
 
 class Fields(JSONPath):
     """
@@ -493,15 +554,20 @@
     def __init__(self, *fields):
         self.fields = fields
 
-    def get_field_datum(self, datum, field):
+    @staticmethod
+    def get_field_datum(datum, field, create):
         if field == auto_id_field:
             return AutoIdForDatum(datum)
-        else:
-            try:
-                field_value = datum.value[field] # Do NOT use `val.get(field)` 
since that confuses None as a value and None due to `get`
-                return DatumInContext(value=field_value, path=Fields(field), 
context=datum)
-            except (TypeError, KeyError, AttributeError):
-                return None
+        try:
+            field_value = datum.value.get(field, NOT_SET)
+            if field_value is NOT_SET:
+                if create:
+                    datum.value[field] = field_value = {}
+                else:
+                    return None
+            return DatumInContext(field_value, path=Fields(field), 
context=datum)
+        except (TypeError, AttributeError):
+            return None
 
     def reified_fields(self, datum):
         if '*' not in self.fields:
@@ -514,15 +580,28 @@
                 return ()
 
     def find(self, datum):
-        datum  = DatumInContext.wrap(datum)
+        return self._find_base(datum, create=False)
+
+    def find_or_create(self, datum):
+        return self._find_base(datum, create=True)
 
-        return  [field_datum
-                 for field_datum in [self.get_field_datum(datum, field) for 
field in self.reified_fields(datum)]
-                 if field_datum is not None]
+    def _find_base(self, datum, create):
+        datum = DatumInContext.wrap(datum)
+        field_data = [self.get_field_datum(datum, field, create)
+                      for field in self.reified_fields(datum)]
+        return [fd for fd in field_data if fd is not None]
 
     def update(self, data, val):
+        return self._update_base(data, val, create=False)
+
+    def update_or_create(self, data, val):
+        return self._update_base(data, val, create=True)
+
+    def _update_base(self, data, val, create):
         if data is not None:
             for field in self.reified_fields(DatumInContext.wrap(data)):
+                if field not in data and create:
+                    data[field] = {}
                 if field in data:
                     if hasattr(val, '__call__'):
                         val(data[field], data, field)
@@ -539,7 +618,14 @@
         return data
 
     def __str__(self):
-        return ','.join(map(str, self.fields))
+        # If any JsonPathLexer.literals are included in field name need quotes
+        # This avoids unnecessary quotes to keep strings short.
+        # Test each field whether it contains a literal and only then add 
quotes
+        # The test loops over all literals, could possibly optimize to short 
circuit if one found
+        fields_as_str = ("'" + str(f) + "'" if any([l in f for l in 
JsonPathLexer.literals]) else
+                         str(f) for f in self.fields)
+        return ','.join(fields_as_str)
+
 
     def __repr__(self):
         return '%s(%s)' % (self.__class__.__name__, ','.join(map(repr, 
self.fields)))
@@ -547,6 +633,9 @@
     def __eq__(self, other):
         return isinstance(other, Fields) and tuple(self.fields) == 
tuple(other.fields)
 
+    def __hash__(self):
+        return hash(tuple(self.fields))
+
 
 class Index(JSONPath):
     """
@@ -561,14 +650,33 @@
         self.index = index
 
     def find(self, datum):
-        datum = DatumInContext.wrap(datum)
+        return self._find_base(datum, create=False)
+
+    def find_or_create(self, datum):
+        return self._find_base(datum, create=True)
 
+    def _find_base(self, datum, create):
+        datum = DatumInContext.wrap(datum)
+        if create:
+            if datum.value == {}:
+                datum.value = _create_list_key(datum.value)
+            self._pad_value(datum.value)
         if datum.value and len(datum.value) > self.index:
             return [DatumInContext(datum.value[self.index], path=self, 
context=datum)]
         else:
             return []
 
     def update(self, data, val):
+        return self._update_base(data, val, create=False)
+
+    def update_or_create(self, data, val):
+        return self._update_base(data, val, create=True)
+
+    def _update_base(self, data, val, create):
+        if create:
+            if data == {}:
+                data = _create_list_key(data)
+            self._pad_value(data)
         if hasattr(val, '__call__'):
             val.__call__(data[self.index], data, self.index)
         elif len(data) > self.index:
@@ -586,6 +694,17 @@
     def __str__(self):
         return '[%i]' % self.index
 
+    def __repr__(self):
+        return '%s(index=%r)' % (self.__class__.__name__, self.index)
+
+    def _pad_value(self, value):
+        if len(value) <= self.index:
+            pad = self.index - len(value) + 1
+            value += [{} for __ in range(pad)]
+
+    def __hash__(self):
+        return hash(self.index)
+
 
 class Slice(JSONPath):
     """
@@ -624,13 +743,13 @@
             return []
         # Here's the hack. If it is a dictionary or some kind of constant,
         # put it in a single-element list
-        if (isinstance(datum.value, dict) or isinstance(datum.value, 
six.integer_types) or isinstance(datum.value, six.string_types)):
+        if (isinstance(datum.value, dict) or isinstance(datum.value, int) or 
isinstance(datum.value, str)):
             return self.find(DatumInContext([datum.value], path=datum.path, 
context=datum.context))
 
         # Some iterators do not support slicing but we can still
         # at least work for '*'
-        if self.start == None and self.end == None and self.step == None:
-            return [DatumInContext(datum.value[i], path=Index(i), 
context=datum) for i in xrange(0, len(datum.value))]
+        if self.start is None and self.end is None and self.step is None:
+            return [DatumInContext(datum.value[i], path=Index(i), 
context=datum) for i in range(0, len(datum.value))]
         else:
             return [DatumInContext(datum.value[i], path=Index(i), 
context=datum) for i in range(0, 
len(datum.value))[self.start:self.end:self.step]]
 
@@ -652,7 +771,7 @@
         return data
 
     def __str__(self):
-        if self.start == None and self.end == None and self.step == None:
+        if self.start is None and self.end is None and self.step is None:
             return '[*]'
         else:
             return '[%s%s%s]' % (self.start or '',
@@ -664,3 +783,35 @@
 
     def __eq__(self, other):
         return isinstance(other, Slice) and other.start == self.start and 
self.end == other.end and other.step == self.step
+
+    def __hash__(self):
+        return hash((self.start, self.end, self.step))
+
+
+def _create_list_key(dict_):
+    """
+    Adds a list to a dictionary by reference and returns the list.
+
+    See `_clean_list_keys()`
+    """
+    dict_[LIST_KEY] = new_list = [{}]
+    return new_list
+
+
+def _clean_list_keys(dict_):
+    """
+    Replace {LIST_KEY: ['foo', 'bar']} with ['foo', 'bar'].
+
+    >>> _clean_list_keys({LIST_KEY: ['foo', 'bar']})
+    ['foo', 'bar']
+
+    """
+    for key, value in dict_.items():
+        if isinstance(value, dict):
+            dict_[key] = _clean_list_keys(value)
+        elif isinstance(value, list):
+            dict_[key] = [_clean_list_keys(v) if isinstance(v, dict) else v
+                          for v in value]
+    if LIST_KEY in dict_:
+        return dict_[LIST_KEY]
+    return dict_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/lexer.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/lexer.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/lexer.py  2020-09-07 15:37:49.000000000 
+0200
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/lexer.py  2023-09-13 21:46:25.000000000 
+0200
@@ -4,11 +4,9 @@
 
 import ply.lex
 
-logger = logging.getLogger(__name__)
-
+from jsonpath_ng.exceptions import JsonPathLexerError
 
-class JsonPathLexerError(Exception):
-    pass
+logger = logging.getLogger(__name__)
 
 
 class JsonPathLexer(object):
@@ -18,7 +16,7 @@
 
     def __init__(self, debug=False):
         self.debug = debug
-        if self.__doc__ == None:
+        if self.__doc__ is None:
             raise JsonPathLexerError('Docstrings have been removed! By design 
of PLY, jsonpath-rw requires docstrings. You must not use PYTHONOPTIMIZE=2 or 
python -OO.')
 
     def tokenize(self, string):
@@ -33,7 +31,8 @@
 
         while True:
             t = new_lexer.token()
-            if t is None: break
+            if t is None:
+                break
             t.col = t.lexpos - new_lexer.latest_newline
             yield t
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/jsonpath_ng/parser.py 
new/jsonpath-ng-1.6.0/jsonpath_ng/parser.py
--- old/jsonpath-ng-1.5.2/jsonpath_ng/parser.py 2020-09-07 15:37:49.000000000 
+0200
+++ new/jsonpath-ng-1.6.0/jsonpath_ng/parser.py 2023-09-13 21:46:25.000000000 
+0200
@@ -1,18 +1,27 @@
-from __future__ import print_function, absolute_import, division, generators, 
nested_scopes
+from __future__ import (
+    print_function,
+    absolute_import,
+    division,
+    generators,
+    nested_scopes,
+)
+import logging
 import sys
 import os.path
-import logging
 
 import ply.yacc
 
+from jsonpath_ng.exceptions import JsonPathParserError
 from jsonpath_ng.jsonpath import *
 from jsonpath_ng.lexer import JsonPathLexer
 
 logger = logging.getLogger(__name__)
 
+
 def parse(string):
     return JsonPathParser().parse(string)
 
+
 class JsonPathParser(object):
     '''
     An LALR-parser for JsonPath
@@ -21,8 +30,12 @@
     tokens = JsonPathLexer.tokens
 
     def __init__(self, debug=False, lexer_class=None):
-        if self.__doc__ == None:
-            raise Exception('Docstrings have been removed! By design of PLY, 
jsonpath-rw requires docstrings. You must not use PYTHONOPTIMIZE=2 or python 
-OO.')
+        if self.__doc__ is None:
+            raise JsonPathParserError(
+                'Docstrings have been removed! By design of PLY, '
+                'jsonpath-rw requires docstrings. You must not use '
+                'PYTHONOPTIMIZE=2 or python -OO.'
+            )
 
         self.debug = debug
         self.lexer_class = lexer_class or JsonPathLexer # Crufty but works 
around statefulness in PLY
@@ -43,7 +56,8 @@
 
         parsing_table_module = '_'.join([module_name, start_symbol, 
'parsetab'])
 
-        # And we regenerate the parse table every time; it doesn't actually 
take that long!
+        # And we regenerate the parse table every time;
+        # it doesn't actually take that long!
         new_parser = ply.yacc.yacc(module=self,
                                    debug=self.debug,
                                    tabmodule = parsing_table_module,
@@ -66,7 +80,8 @@
     ]
 
     def p_error(self, t):
-        raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, 
t.col, t.value, t.type))
+        raise JsonPathParserError('Parse error at %s:%s near token %s (%s)'
+                                  % (t.lineno, t.col, t.value, t.type))
 
     def p_jsonpath_binop(self, p):
         """jsonpath : jsonpath '.' jsonpath
@@ -98,7 +113,8 @@
         elif p[1] == 'parent':
             p[0] = Parent()
         else:
-            raise Exception('Unknown named operator `%s` at %s:%s' % (p[1], 
p.lineno(1), p.lexpos(1)))
+            raise JsonPathParserError('Unknown named operator `%s` at %s:%s'
+                                      % (p[1], p.lineno(1), p.lexpos(1)))
 
     def p_jsonpath_root(self, p):
         "jsonpath : '$'"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/requirements-dev.txt 
new/jsonpath-ng-1.6.0/requirements-dev.txt
--- old/jsonpath-ng-1.5.2/requirements-dev.txt  2020-09-07 15:37:49.000000000 
+0200
+++ new/jsonpath-ng-1.6.0/requirements-dev.txt  2023-09-13 21:46:25.000000000 
+0200
@@ -1,7 +1,6 @@
 oslotest
-pytest~=3.0.5
+pytest
 flake8
-testscenarios
 coverage
 coveralls
-pytest-cov~=2.3.1
+pytest-cov
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/requirements.txt 
new/jsonpath-ng-1.6.0/requirements.txt
--- old/jsonpath-ng-1.5.2/requirements.txt      2020-09-07 15:37:49.000000000 
+0200
+++ new/jsonpath-ng-1.6.0/requirements.txt      2023-09-13 21:46:25.000000000 
+0200
@@ -1,4 +1,2 @@
 ply
-decorator
-six
 setuptools>=18.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/setup.py 
new/jsonpath-ng-1.6.0/setup.py
--- old/jsonpath-ng-1.5.2/setup.py      2020-09-07 15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/setup.py      2023-09-13 21:46:25.000000000 +0200
@@ -4,9 +4,9 @@
 
 setuptools.setup(
     name='jsonpath-ng',
-    version='1.5.2',
+    version='1.6.0',
     description=(
-        'A final implementation of JSONPath for Python that aims to be ' 
+        'A final implementation of JSONPath for Python that aims to be '
         'standard compliant, including arithmetic and binary comparison '
         'operators and providing clear AST for metaprogramming.'
     ),
@@ -23,17 +23,17 @@
     },
     test_suite='tests',
     install_requires=[
-        'ply', 'decorator', 'six'
+        'ply'
     ],
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Apache Software License',
-        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.5',
-        'Programming Language :: Python :: 3.6',
-       'Programming Language :: Python :: 3.7',
-       'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11'
     ],
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/tests/test_create.py 
new/jsonpath-ng-1.6.0/tests/test_create.py
--- old/jsonpath-ng-1.5.2/tests/test_create.py  1970-01-01 01:00:00.000000000 
+0100
+++ new/jsonpath-ng-1.6.0/tests/test_create.py  2023-09-13 21:46:25.000000000 
+0200
@@ -0,0 +1,177 @@
+import doctest
+from collections import namedtuple
+
+import pytest
+
+import jsonpath_ng
+from jsonpath_ng.ext import parse
+
+Params = namedtuple('Params', 'string initial_data insert_val target')
+
+
[email protected]('string, initial_data, insert_val, target', [
+
+    Params(string='$.foo',
+           initial_data={},
+           insert_val=42,
+           target={'foo': 42}),
+
+    Params(string='$.foo.bar',
+           initial_data={},
+           insert_val=42,
+           target={'foo': {'bar': 42}}),
+
+    Params(string='$.foo[0]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [42]}),
+
+    Params(string='$.foo[1]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [{}, 42]}),
+
+    Params(string='$.foo[0].bar',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [{'bar': 42}]}),
+
+    Params(string='$.foo[1].bar',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [{}, {'bar': 42}]}),
+
+    Params(string='$.foo[0][0]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [[42]]}),
+
+    Params(string='$.foo[1][1]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [{}, [{}, 42]]}),
+
+    Params(string='foo[0]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [42]}),
+
+    Params(string='foo[1]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [{}, 42]}),
+
+    Params(string='foo',
+           initial_data={},
+           insert_val=42,
+           target={'foo': 42}),
+
+    # Initial data can be a list if we expect a list back
+    Params(string='[0]',
+           initial_data=[],
+           insert_val=42,
+           target=[42]),
+
+    Params(string='[1]',
+           initial_data=[],
+           insert_val=42,
+           target=[{}, 42]),
+
+    # Converts initial data to a list if necessary
+    Params(string='[0]',
+           initial_data={},
+           insert_val=42,
+           target=[42]),
+
+    Params(string='[1]',
+           initial_data={},
+           insert_val=42,
+           target=[{}, 42]),
+
+    Params(string='foo[?bar="baz"].qux',
+           initial_data={'foo': [
+               {'bar': 'baz'},
+               {'bar': 'bizzle'},
+           ]},
+           insert_val=42,
+           target={'foo': [
+               {'bar': 'baz', 'qux': 42},
+               {'bar': 'bizzle'}
+           ]}),
+])
+def test_update_or_create(string, initial_data, insert_val, target):
+    jsonpath = parse(string)
+    result = jsonpath.update_or_create(initial_data, insert_val)
+    assert result == target
+
+
[email protected]('string, initial_data, insert_val, target', [
+    # Slice not supported
+    Params(string='foo[0:1]',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [42, 42]}),
+    # result is {'foo': {}}
+
+    # Filter does not create items to meet criteria
+    Params(string='foo[?bar="baz"].qux',
+           initial_data={},
+           insert_val=42,
+           target={'foo': [{'bar': 'baz', 'qux': 42}]}),
+    # result is {'foo': {}}
+
+    # Does not convert initial data to a dictionary
+    Params(string='foo',
+           initial_data=[],
+           insert_val=42,
+           target={'foo': 42}),
+    # raises TypeError
+
+])
[email protected]
+def test_unsupported_classes(string, initial_data, insert_val, target):
+    jsonpath = parse(string)
+    result = jsonpath.update_or_create(initial_data, insert_val)
+    assert result == target
+
+
[email protected]('string, initial_data, insert_val, target', [
+
+    Params(string='$.name[0].text',
+           initial_data={},
+           insert_val='Sir Michael',
+           target={'name': [{'text': 'Sir Michael'}]}),
+
+    Params(string='$.name[0].given[0]',
+           initial_data={'name': [{'text': 'Sir Michael'}]},
+           insert_val='Michael',
+           target={'name': [{'text': 'Sir Michael',
+                             'given': ['Michael']}]}),
+
+    Params(string='$.name[0].prefix[0]',
+           initial_data={'name': [{'text': 'Sir Michael',
+                                   'given': ['Michael']}]},
+           insert_val='Sir',
+           target={'name': [{'text': 'Sir Michael',
+                             'given': ['Michael'],
+                             'prefix': ['Sir']}]}),
+
+    Params(string='$.birthDate',
+           initial_data={'name': [{'text': 'Sir Michael',
+                                   'given': ['Michael'],
+                                   'prefix': ['Sir']}]},
+           insert_val='1943-05-05',
+           target={'name': [{'text': 'Sir Michael',
+                             'given': ['Michael'],
+                             'prefix': ['Sir']}],
+                   'birthDate': '1943-05-05'}),
+])
+def test_build_doc(string, initial_data, insert_val, target):
+    jsonpath = parse(string)
+    result = jsonpath.update_or_create(initial_data, insert_val)
+    assert result == target
+
+
+def test_doctests():
+    results = doctest.testmod(jsonpath_ng)
+    assert results.failed == 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/tests/test_examples.py 
new/jsonpath-ng-1.6.0/tests/test_examples.py
--- old/jsonpath-ng-1.5.2/tests/test_examples.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/jsonpath-ng-1.6.0/tests/test_examples.py        2023-09-13 
21:46:25.000000000 +0200
@@ -0,0 +1,74 @@
+import pytest
+
+from jsonpath_ng.ext.filter import Filter, Expression
+from jsonpath_ng.ext import parse
+from jsonpath_ng.jsonpath import *
+
+
[email protected]('string, parsed', [
+    # The authors of all books in the store
+    ("$.store.book[*].author",
+     Child(Child(Child(Child(Root(), Fields('store')), Fields('book')),
+                 Slice()), Fields('author'))),
+
+    # All authors
+    ("$..author", Descendants(Root(), Fields('author'))),
+
+    # All things in the store
+    ("$.store.*", Child(Child(Root(), Fields('store')), Fields('*'))),
+
+    # The price of everything in the store
+    ("$.store..price",
+     Descendants(Child(Root(), Fields('store')), Fields('price'))),
+
+    # The third book
+    ("$..book[2]",
+     Child(Descendants(Root(), Fields('book')),Index(2))),
+
+    # The last book in order
+    # ("$..book[(@.length-1)]",  # Not implemented
+    #  Child(Descendants(Root(), Fields('book')), Slice(start=-1))),
+    ("$..book[-1:]",
+     Child(Descendants(Root(), Fields('book')), Slice(start=-1))),
+
+    # The first two books
+    # ("$..book[0,1]",  # Not implemented
+    #  Child(Descendants(Root(), Fields('book')), Slice(end=2))),
+    ("$..book[:2]",
+     Child(Descendants(Root(), Fields('book')), Slice(end=2))),
+
+    # Filter all books with ISBN number
+    ("$..book[?(@.isbn)]",
+     Child(Descendants(Root(), Fields('book')),
+           Filter([Expression(Child(This(), Fields('isbn')), None, None)]))),
+
+    # Filter all books cheaper than 10
+    ("$..book[?(@.price<10)]",
+     Child(Descendants(Root(), Fields('book')),
+           Filter([Expression(Child(This(), Fields('price')), '<', 10)]))),
+
+    # All members of JSON structure
+    ("$..*", Descendants(Root(), Fields('*'))),
+])
+def test_goessner_examples(string, parsed):
+    """
+    Test Stefan Goessner's `examples`_
+
+    .. _examples: https://goessner.net/articles/JsonPath/index.html#e3
+    """
+    assert parse(string, debug=True) == parsed
+
+
[email protected]('string, parsed', [
+    # Navigate objects
+    ("$.store.book[0].title",
+     Child(Child(Child(Child(Root(), Fields('store')), Fields('book')),
+                 Index(0)), Fields('title'))),
+
+    # Navigate dictionaries
+    ("$['store']['book'][0]['title']",
+     Child(Child(Child(Child(Root(), Fields('store')), Fields('book')),
+                 Index(0)), Fields('title'))),
+])
+def test_obj_v_dict(string, parsed):
+    assert parse(string, debug=True) == parsed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/tests/test_exceptions.py 
new/jsonpath-ng-1.6.0/tests/test_exceptions.py
--- old/jsonpath-ng-1.5.2/tests/test_exceptions.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/jsonpath-ng-1.6.0/tests/test_exceptions.py      2023-09-13 
21:46:25.000000000 +0200
@@ -0,0 +1,20 @@
+import pytest
+
+from jsonpath_ng import parse as rw_parse
+from jsonpath_ng.exceptions import JSONPathError, JsonPathParserError
+from jsonpath_ng.ext import parse as ext_parse
+
+
+def test_rw_exception_class():
+    with pytest.raises(JSONPathError):
+        rw_parse('foo.bar.`grandparent`.baz')
+
+
+def test_rw_exception_subclass():
+    with pytest.raises(JsonPathParserError):
+        rw_parse('foo.bar.`grandparent`.baz')
+
+
+def test_ext_exception_subclass():
+    with pytest.raises(JsonPathParserError):
+        ext_parse('foo.bar.`grandparent`.baz')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/tests/test_jsonpath.py 
new/jsonpath-ng-1.6.0/tests/test_jsonpath.py
--- old/jsonpath-ng-1.5.2/tests/test_jsonpath.py        2020-09-07 
15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/tests/test_jsonpath.py        2023-09-13 
21:46:25.000000000 +0200
@@ -136,7 +136,7 @@
 
     def test_slice_value(self):
         self.check_cases([('[*]', [1, 2, 3], [1, 2, 3]),
-                          ('[*]', xrange(1, 4), [1, 2, 3]),
+                          ('[*]', range(1, 4), [1, 2, 3]),
                           ('[1:]', [1, 2, 3, 4], [2, 3, 4]),
                           ('[:2]', [1, 2, 3, 4], [1, 2])])
 
@@ -183,6 +183,7 @@
 
         for string, data, target in test_cases:
             print('parse("%s").find(%s).paths =?= %s' % (string, data, target))
+            assert hash(parse(string)) == hash(parse(string))
             result = parse(string).find(data)
             if isinstance(target, list):
                 assert [str(r.full_path) for r in result] == target
@@ -232,6 +233,8 @@
     def test_descendants_paths(self):
         self.check_paths([('foo..baz', {'foo': {'baz': 1, 'bing': {'baz': 
2}}}, ['foo.baz', 'foo.bing.baz'] )])
 
+    def test_literals_in_field_names(self):
+        self.check_paths([("A.'a.c'", {'A' : {'a.c': 'd'}}, ["A.'a.c'"])])
 
     #
     # Check the "auto_id_field" feature
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jsonpath-ng-1.5.2/tests/test_jsonpath_rw_ext.py 
new/jsonpath-ng-1.6.0/tests/test_jsonpath_rw_ext.py
--- old/jsonpath-ng-1.5.2/tests/test_jsonpath_rw_ext.py 2020-09-07 
15:37:49.000000000 +0200
+++ new/jsonpath-ng-1.6.0/tests/test_jsonpath_rw_ext.py 2023-09-13 
21:46:25.000000000 +0200
@@ -21,14 +21,23 @@
 
 from jsonpath_ng import jsonpath  # For setting the global auto_id_field flag
 from oslotest import base
-from six import moves
-import testscenarios
 
 from jsonpath_ng.ext import parser
 
 
-class Testjsonpath_ng_ext(testscenarios.WithScenarios,
-                          base.BaseTestCase):
+# Example from 
https://docs.pytest.org/en/7.1.x/example/parametrize.html#a-quick-port-of-testscenarios
+def pytest_generate_tests(metafunc):
+    idlist = []
+    argvalues = []
+    for scenario in metafunc.cls.scenarios:
+        idlist.append(scenario[0])
+        items = scenario[1].items()
+        argnames = [x[0] for x in items]
+        argvalues.append([x[1] for x in items])
+    metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
+
+
+class Testjsonpath_ng_ext:
     scenarios = [
         ('sorted_list', dict(string='objects.`sorted`',
                              data={'objects': ['alpha', 'gamma', 'beta']},
@@ -126,14 +135,14 @@
                                                  {'cat': 2, 'cow': 1},
                                                  {'cat': 3, 'cow': 3}]},
                                target=2)),
-        ('sort2', dict(string='objects[\cat]',
+        ('sort2', dict(string='objects[\\cat]',
                        data={'objects': [{'cat': 2}, {'cat': 1}, {'cat': 3}]},
                        target=[[{'cat': 3}, {'cat': 2}, {'cat': 1}]])),
-        ('sort2_indexed', dict(string='objects[\cat][-1].cat',
+        ('sort2_indexed', dict(string='objects[\\cat][-1].cat',
                                data={'objects': [{'cat': 2}, {'cat': 1},
                                                  {'cat': 3}]},
                                target=1)),
-        ('sort3', dict(string='objects[/cow,\cat]',
+        ('sort3', dict(string='objects[/cow,\\cat]',
                        data={'objects': [{'cat': 1, 'cow': 2},
                                          {'cat': 2, 'cow': 1},
                                          {'cat': 3, 'cow': 1},
@@ -142,7 +151,7 @@
                                {'cat': 2, 'cow': 1},
                                {'cat': 1, 'cow': 2},
                                {'cat': 3, 'cow': 3}]])),
-        ('sort3_indexed', dict(string='objects[/cow,\cat][0].cat',
+        ('sort3_indexed', dict(string='objects[/cow,\\cat][0].cat',
                                data={'objects': [{'cat': 1, 'cow': 2},
                                                  {'cat': 2, 'cow': 1},
                                                  {'cat': 3, 'cow': 1},
@@ -334,17 +343,17 @@
         )),
     ]
 
-    def test_fields_value(self):
+    def test_fields_value(self, string, data, target):
         jsonpath.auto_id_field = None
-        result = parser.parse(self.string, debug=True).find(self.data)
-        if isinstance(self.target, list):
-            self.assertEqual(self.target, [r.value for r in result])
-        elif isinstance(self.target, set):
-            self.assertEqual(self.target, set([r.value for r in result]))
-        elif isinstance(self.target, (int, float)):
-            self.assertEqual(self.target, result[0].value)
+        result = parser.parse(string, debug=True).find(data)
+        if isinstance(target, list):
+            assert target == [r.value for r in result]
+        elif isinstance(target, set):
+            assert target == set([r.value for r in result])
+        elif isinstance(target, (int, float)):
+            assert target == result[0].value
         else:
-            self.assertEqual(self.target, result[0].value)
+            assert target == result[0].value
 
 # NOTE(sileht): copy of tests/test_jsonpath.py
 # to ensure we didn't break jsonpath_ng
@@ -406,7 +415,7 @@
 
     def test_slice_value(self):
         self.check_cases([('[*]', [1, 2, 3], [1, 2, 3]),
-                          ('[*]', moves.range(1, 4), [1, 2, 3]),
+                          ('[*]', range(1, 4), [1, 2, 3]),
                           ('[1:]', [1, 2, 3, 4], [2, 3, 4]),
                           ('[:2]', [1, 2, 3, 4], [1, 2])])
 

Reply via email to