Repository: h2non/jsonpath-ng

Issue:
Deprecation warning due to invalid escape sequences in Python 3.8
Deprecation warnings are raised due to invalid escape sequences in Python 3.8 . Below is a log of the warnings raised during compiling all the python files. Using raw strings or escaping them will fix this issue.

```
find . -iname '*.py'  | xargs -P 4 -I{} python -Walways -m py_compile {}

./jsonpath_ng/ext/string.py:18: DeprecationWarning: invalid escape sequence \(
  SUB = re.compile("sub\(/(.*)/,\s+(.*)\)")
./jsonpath_ng/ext/string.py:19: DeprecationWarning: invalid escape sequence \(
  SPLIT = re.compile("split\((.),\s+(\d+),\s+(\d+|-1)\)")
./tests/test_jsonpath_rw_ext.py:129: DeprecationWarning: invalid escape sequence \c
  ('sort2', dict(string='objects[\cat]',
./tests/test_jsonpath_rw_ext.py:132: DeprecationWarning: invalid escape sequence \c
  ('sort2_indexed', dict(string='objects[\cat][-1].cat',
./tests/test_jsonpath_rw_ext.py:136: DeprecationWarning: invalid escape sequence \c
  ('sort3', dict(string='objects[/cow,\cat]',
./tests/test_jsonpath_rw_ext.py:145: DeprecationWarning: invalid escape sequence \c
  ('sort3_indexed', dict(string='objects[/cow,\cat][0].cat',
```

Discussion:
domdfcoding: 👈 Friendly ping
ciwasaki: seconding the friendly ping!
RoaldO: third friendly ping

Patch:
diff --git a/README.rst b/README.rst
index 593d7b5..03d2dbc 100644
--- a/README.rst
+++ b/README.rst
@@ -203,9 +203,19 @@ Extensions
 |              | - ``$.objects[\\some_field,/other_field]``    |
 +--------------+-----------------------------------------------+
 | filter       | - ``$.objects[?(@some_field > 5)]``           |
-|              | - ``$.objects[?some_field = "foobar")]``      |
-|              | - ``$.objects[?some_field =~ "foobar")]``     |
-|              | - ``$.objects[?some_field > 5 & other < 2)]`` |
+|              | - ``$.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``                              |
diff --git a/jsonpath_ng/ext/string.py b/jsonpath_ng/ext/string.py
index 80ed890..1cd2763 100644
--- a/jsonpath_ng/ext/string.py
+++ b/jsonpath_ng/ext/string.py
@@ -15,9 +15,9 @@ import re
 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 --git a/jsonpath_ng/jsonpath.py b/jsonpath_ng/jsonpath.py
index f4f9d4b..006031c 100644
--- a/jsonpath_ng/jsonpath.py
+++ b/jsonpath_ng/jsonpath.py
@@ -220,6 +220,9 @@ class Root(JSONPath):
     def __eq__(self, other):
         return isinstance(other, Root)
 
+    def __hash__(self):
+        return hash('$')
+
 
 class This(JSONPath):
     """
@@ -244,6 +247,9 @@ class This(JSONPath):
     def __eq__(self, other):
         return isinstance(other, This)
 
+    def __hash__(self):
+        return hash('this')
+
 
 class Child(JSONPath):
     """
@@ -302,6 +308,9 @@ class Child(JSONPath):
     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):
     """
@@ -323,6 +332,9 @@ class Parent(JSONPath):
     def __repr__(self):
         return 'Parent()'
 
+    def __hash__(self):
+        return hash('parent')
+
 
 class Where(JSONPath):
     """
@@ -357,6 +369,9 @@ class Where(JSONPath):
     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
@@ -469,6 +484,9 @@ class Descendants(JSONPath):
     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):
     """
@@ -490,6 +508,12 @@ class Union(JSONPath):
     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.
@@ -511,6 +535,12 @@ class Intersect(JSONPath):
     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):
     """
@@ -596,6 +626,9 @@ class Fields(JSONPath):
     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):
     """
@@ -662,6 +695,9 @@ class Index(JSONPath):
             pad = self.index - len(value) + 1
             value += [{} for __ in range(pad)]
 
+    def __hash__(self):
+        return hash(self.index)
+
 
 class Slice(JSONPath):
     """
@@ -741,6 +777,9 @@ class Slice(JSONPath):
     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_):
     """
diff --git a/requirements.txt b/requirements.txt
index 247ceb7..279c9a4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,3 @@
 ply
-decorator
 six
 setuptools>=18.5
diff --git a/setup.py b/setup.py
index 4ca960d..6793d5f 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ setuptools.setup(
     },
     test_suite='tests',
     install_requires=[
-        'ply', 'decorator', 'six'
+        'ply', 'six'
     ],
     classifiers=[
         'Development Status :: 5 - Production/Stable',


Test patch:
diff --git a/tests/test_jsonpath.py b/tests/test_jsonpath.py
index 60670b1..416a3bc 100644
--- a/tests/test_jsonpath.py
+++ b/tests/test_jsonpath.py
@@ -183,6 +183,7 @@ class TestJsonPath(unittest.TestCase):
 
         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
diff --git a/tests/test_jsonpath_rw_ext.py b/tests/test_jsonpath_rw_ext.py
index 163e1be..8b80ec2 100644
--- a/tests/test_jsonpath_rw_ext.py
+++ b/tests/test_jsonpath_rw_ext.py
@@ -126,14 +126,14 @@ class Testjsonpath_ng_ext(testscenarios.WithScenarios,
                                                  {'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 +142,7 @@ class Testjsonpath_ng_ext(testscenarios.WithScenarios,
                                {'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},