Hello community,

here is the log from the commit of package python-jmespath for openSUSE:Factory 
checked in at 2017-04-20 20:49:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jmespath (Old)
 and      /work/SRC/openSUSE:Factory/.python-jmespath.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jmespath"

Thu Apr 20 20:49:26 2017 rev:10 rq:483389 version:0.9.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jmespath/python-jmespath.changes  
2016-08-17 12:05:28.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-jmespath.new/python-jmespath.changes     
2017-04-20 20:49:27.780580041 +0200
@@ -1,0 +2,23 @@
+Wed Mar 29 14:15:59 UTC 2017 - [email protected]
+
+- properly Requires(postun) for %postun section
+
+-------------------------------------------------------------------
+Wed Mar 29 08:16:19 UTC 2017 - [email protected]
+
+- Only BuildRequire unittest2 for python2
+
+-------------------------------------------------------------------
+Thu Mar 16 18:43:59 UTC 2017 - [email protected]
+
+- Switch to single-spec build
+- Update to version 0.9.2
+  + Fix regression when using ordering comparators on strings (issue 124)
+- From 0.9.1
+  + Raise LexerError on invalid numbers (issue 98)
+  + Add support for custom functions (#100) (issue 100)
+  + Fix ZeroDivisionError for built-in function avg() on empty lists (#115)
+    (issue 115)
+  + Properly handle non numerical ordering operators (#117) (issue 117)
+
+-------------------------------------------------------------------
@@ -11,3 +34,2 @@
-  * Add support for `JEP 9 
<http://jmespath.org/proposals/improved-filters.html>`__,
-    which introduces "and" expressions, "unary" expressions, "not" expressions,
-    and "paren" expressions
+  * Add support for JEP 9 which introduces "and" expressions, "unary"
+    expressions, "not" expressions, and "paren" expressions
@@ -15,3 +37 @@
-    (`issue 90 <https://github.com/jmespath/jmespath.py/issues/90>`__,
-     `issue 88 <https://github.com/jmespath/jmespath.py/issues/88>`__,
-     `issue 82 <https://github.com/jmespath/jmespath.py/issues/82>`__)
+    (issue 90, issue 88, issue 82)
@@ -19,3 +39,3 @@
-  * Improve lexing performance (`issue 84 
<https://github.com/jmespath/jmespath.py/pull/84>`__)
-  * Fix parsing error for multiselect lists (`issue 86 
<https://github.com/jmespath/jmespath.py/issues/86>`__)
-  * Fix issue with escaping single quotes in literal strings (`issue 85 
<https://github.com/jmespath/jmespath.py/issues/85>`__)
+  * Improve lexing performance (issue 84)
+  * Fix parsing error for multiselect lists (issue 86)
+  * Fix issue with escaping single quotes in literal strings (issue 85)
@@ -23,2 +43,2 @@
-    ordered dictionaries (`issue 94 
<https://github.com/jmespath/jmespath.py/pull/94>`__)
-  * Add map() function (`issue 95 
<https://github.com/jmespath/jmespath.py/pull/95>`__)
+    ordered dictionaries (issue 94)
+  * Add map() function (issue 95)

Old:
----
  jmespath-0.9.0.tar.gz

New:
----
  jmespath-0.9.2.tar.gz

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

Other differences:
------------------
++++++ python-jmespath.spec ++++++
--- /var/tmp/diff_new_pack.fNcpWs/_old  2017-04-20 20:49:28.308505395 +0200
+++ /var/tmp/diff_new_pack.fNcpWs/_new  2017-04-20 20:49:28.312504829 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-jmespath
 #
-# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,37 +16,37 @@
 #
 
 
-%define baseName jmespath
-
+%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-jmespath
-Version:        0.9.0
+Version:        0.9.2
 Release:        0
 Summary:        Extract elements from JSON document
 License:        MIT
 Group:          Development/Languages/Python
 Url:            https://github.com/boto/jmespath
-Source0:        
https://pypi.python.org/packages/source/j/%{baseName}/%{baseName}-%{version}.tar.gz
+Source:         
https://pypi.io/packages/source/j/jmespath/jmespath-%{version}.tar.gz
+BuildRequires:  %{python_module devel}
+BuildRequires:  %{python_module ply >= 3.4}
+BuildRequires:  %{python_module setuptools}
+BuildRequires:  %{python_module simplejson}
+BuildRequires:  python-rpm-macros
+# Testing
+BuildRequires:  %{python_module nose}
+%ifpython2
+BuildRequires:  python-unittest2
+%endif
 Requires:       python-base
 Requires:       python-ply >= 3.4
+Requires:       python-simplejson
 Requires(post):   update-alternatives
-Requires(preun): update-alternatives
-BuildRequires:  python-devel
-BuildRequires:  python-ply >= 3.4
-BuildRequires:  python-setuptools
-# For testing
-BuildRequires:  python-nose
-%if 0%{?suse_version} && 0%{?suse_version} <= 1110
-BuildRequires:  python-ordereddict
-%endif
-BuildRequires:  python-simplejson
-BuildRequires:  python-unittest2
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
-%if 0%{?suse_version} && 0%{?suse_version} <= 1110
-%{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
-%else
+Requires(postun): update-alternatives
+
+%if 0%{?suse_version} > 1110
 BuildArch:      noarch
 %endif
 
+%python_subpackages
+
 %description
 JMESPath (pronounced "jaymz path") allows you to declaratively specify how
 to extract elements from a JSON document.
@@ -79,37 +79,35 @@
 The expression: foo.*.name will return ["one", "two"].
 
 %prep
-%setup -q -n %{baseName}-%{version}
+%setup -q -n jmespath-%{version}
 
 %build
-python setup.py build
+%python_build
+#pushd build/scripts-%{$python_bin_suffix}
+#mv jp.py jp-%{$python_bin_suffix}
+#popd
+#pushd build/scripts-%{py_ver}
+#mv jp.py jp-%{py_ver}
+#popd
 
 %install
-python setup.py install --prefix=%{_prefix} --root=%{buildroot} 
--install-scripts=%{_bindir}
-
-# Prepare for update-alternatives usage
-mkdir -p %{buildroot}%{_sysconfdir}/alternatives
-mv %{buildroot}%{_bindir}/jp.py %{buildroot}%{_bindir}/jp-%{py_ver}
-ln -s -f %{_sysconfdir}/alternatives/jp %{buildroot}%{_bindir}/jp
+%python_install
+mv %{buildroot}%{_bindir}/jp.py %{buildroot}%{_bindir}/jp
+%python_clone -a %{buildroot}%{_bindir}/jp
 
 %check
-nosetests tests
+%python_expand nosetests-%{$python_bin_suffix} tests
 
 %post
-%_sbindir/update-alternatives --install %{_bindir}/jp jp 
%{_bindir}/jp-%{py_ver} 30
+%python_install_alternative jp
 
-%preun
-if [ $1 -eq 0 ] ; then
-    %_sbindir/update-alternatives --remove jp %{_bindir}/jp-%{py_ver}
-fi
+%postun
+%python_uninstall_alternative jp
 
-%files
+%files %{python_files}
 %defattr(-,root,root,-)
 %doc LICENSE.txt README.rst
-%{_bindir}/jp
-%{_bindir}/jp-%{py_ver}
-%ghost %{_sysconfdir}/alternatives/jp
-%{python_sitelib}/jmespath/
-%{python_sitelib}/%{baseName}-%{version}-py%{py_ver}.egg-info/
+%{python_sitelib}/*
+%python_alternative %{_bindir}/jp
 
 %changelog

++++++ jmespath-0.9.0.tar.gz -> jmespath-0.9.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/PKG-INFO new/jmespath-0.9.2/PKG-INFO
--- old/jmespath-0.9.0/PKG-INFO 2015-10-01 06:12:12.000000000 +0200
+++ new/jmespath-0.9.2/PKG-INFO 2017-03-11 00:52:50.000000000 +0100
@@ -1,11 +1,11 @@
 Metadata-Version: 1.1
 Name: jmespath
-Version: 0.9.0
+Version: 0.9.2
 Summary: JSON Matching Expressions
 Home-page: https://github.com/jmespath/jmespath.py
 Author: James Saryerwinnie
 Author-email: [email protected]
-License: UNKNOWN
+License: MIT
 Description: JMESPath
         ========
         
@@ -18,6 +18,10 @@
            :target: http://travis-ci.org/jmespath/jmespath.py
         
         
+        .. image:: 
https://codecov.io/github/jmespath/jmespath.py/coverage.svg?branch=develop
+            :target: 
https://codecov.io/github/jmespath/jmespath.py?branch=develop
+        
+        
         JMESPath (pronounced "james path") allows you to declaratively specify 
how to
         extract elements from a JSON document.
         
@@ -56,7 +60,9 @@
         
         The ``jmespath.py`` library has two functions
         that operate on python data structures.  You can use ``search``
-        and give it the jmespath expression and the data::
+        and give it the jmespath expression and the data:
+        
+        .. code:: python
         
             >>> import jmespath
             >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
@@ -64,7 +70,9 @@
         
         Similar to the ``re`` module, you can use the ``compile`` function
         to compile the JMESPath expression and use this parsed expression
-        to perform repeated searches::
+        to perform repeated searches:
+        
+        .. code:: python
         
             >>> import jmespath
             >>> expression = jmespath.compile('foo.bar')
@@ -83,7 +91,9 @@
         You can provide an instance of ``jmespath.Options`` to control how
         a JMESPath expression is evaluated.  The most common scenario for
         using an ``Options`` instance is if you want to have ordered output
-        of your dict keys.  To do this you can use either of these options::
+        of your dict keys.  To do this you can use either of these options:
+        
+        .. code:: python
         
             >>> import jmespath
             >>> jmespath.search('{a: a, b: b},
@@ -98,6 +108,85 @@
             ...               
jmespath.Options(dict_cls=collections.OrderedDict))
         
         
+        Custom Functions
+        ~~~~~~~~~~~~~~~~
+        
+        The JMESPath language has numerous
+        `built-in functions
+        <http://jmespath.org/specification.html#built-in-functions>`__, but it 
is
+        also possible to add your own custom functions.  Keep in mind that
+        custom function support in jmespath.py is experimental and the API may
+        change based on feedback.
+        
+        **If you have a custom function that you've found useful, consider 
submitting
+        it to jmespath.site and propose that it be added to the JMESPath 
language.**
+        You can submit proposals
+        `here <https://github.com/jmespath/jmespath.site/issues>`__.
+        
+        To create custom functions:
+        
+        * Create a subclass of ``jmespath.functions.Functions``.
+        * Create a method with the name ``_func_<your function name>``.
+        * Apply the ``jmespath.functions.signature`` decorator that indicates
+          the expected types of the function arguments.
+        * Provide an instance of your subclass in a ``jmespath.Options`` 
object.
+        
+        Below are a few examples:
+        
+        .. code:: python
+        
+            import jmespath
+            from jmespath import functions
+        
+            # 1. Create a subclass of functions.Functions.
+            #    The function.Functions base class has logic
+            #    that introspects all of its methods and automatically
+            #    registers your custom functions in its function table.
+            class CustomFunctions(functions.Functions):
+        
+                # 2 and 3.  Create a function that starts with _func_
+                # and decorate it with @signature which indicates its
+                # expected types.
+                # In this example, we're creating a jmespath function
+                # called "unique_letters" that accepts a single argument
+                # with an expected type "string".
+                @functions.signature({'types': ['string']})
+                def _func_unique_letters(self, s):
+                    # Given a string s, return a sorted
+                    # string of unique letters: 'ccbbadd' ->  'abcd'
+                    return ''.join(sorted(set(s)))
+        
+                # Here's another example.  This is creating
+                # a jmespath function called "my_add" that expects
+                # two arguments, both of which should be of type number.
+                @functions.signature({'types': ['number']}, {'types': 
['number']})
+                def _func_my_add(self, x, y):
+                    return x + y
+        
+            # 4. Provide an instance of your subclass in a Options object.
+            options = jmespath.Options(custom_functions=CustomFunctions())
+        
+            # Provide this value to jmespath.search:
+            # This will print 3
+            print(
+                jmespath.search(
+                    'my_add(`1`, `2`)', {}, options=options)
+            )
+        
+            # This will print "abcd"
+            print(
+                jmespath.search(
+                    'foo.bar | unique_letters(@)',
+                    {'foo': {'bar': 'ccbbadd'}},
+                    options=options)
+            )
+        
+        Again, if you come up with useful functions that you think make
+        sense in the JMESPath language (and make sense to implement in all
+        JMESPath libraries, not just python), please let us know at
+        `jmespath.site <https://github.com/jmespath/jmespath.site/issues>`__.
+        
+        
         Specification
         =============
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/README.rst 
new/jmespath-0.9.2/README.rst
--- old/jmespath-0.9.0/README.rst       2015-09-30 06:37:03.000000000 +0200
+++ new/jmespath-0.9.2/README.rst       2017-03-10 01:49:47.000000000 +0100
@@ -10,6 +10,10 @@
    :target: http://travis-ci.org/jmespath/jmespath.py
 
 
+.. image:: 
https://codecov.io/github/jmespath/jmespath.py/coverage.svg?branch=develop
+    :target: https://codecov.io/github/jmespath/jmespath.py?branch=develop
+
+
 JMESPath (pronounced "james path") allows you to declaratively specify how to
 extract elements from a JSON document.
 
@@ -48,7 +52,9 @@
 
 The ``jmespath.py`` library has two functions
 that operate on python data structures.  You can use ``search``
-and give it the jmespath expression and the data::
+and give it the jmespath expression and the data:
+
+.. code:: python
 
     >>> import jmespath
     >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
@@ -56,7 +62,9 @@
 
 Similar to the ``re`` module, you can use the ``compile`` function
 to compile the JMESPath expression and use this parsed expression
-to perform repeated searches::
+to perform repeated searches:
+
+.. code:: python
 
     >>> import jmespath
     >>> expression = jmespath.compile('foo.bar')
@@ -75,7 +83,9 @@
 You can provide an instance of ``jmespath.Options`` to control how
 a JMESPath expression is evaluated.  The most common scenario for
 using an ``Options`` instance is if you want to have ordered output
-of your dict keys.  To do this you can use either of these options::
+of your dict keys.  To do this you can use either of these options:
+
+.. code:: python
 
     >>> import jmespath
     >>> jmespath.search('{a: a, b: b},
@@ -90,6 +100,85 @@
     ...               jmespath.Options(dict_cls=collections.OrderedDict))
 
 
+Custom Functions
+~~~~~~~~~~~~~~~~
+
+The JMESPath language has numerous
+`built-in functions
+<http://jmespath.org/specification.html#built-in-functions>`__, but it is
+also possible to add your own custom functions.  Keep in mind that
+custom function support in jmespath.py is experimental and the API may
+change based on feedback.
+
+**If you have a custom function that you've found useful, consider submitting
+it to jmespath.site and propose that it be added to the JMESPath language.**
+You can submit proposals
+`here <https://github.com/jmespath/jmespath.site/issues>`__.
+
+To create custom functions:
+
+* Create a subclass of ``jmespath.functions.Functions``.
+* Create a method with the name ``_func_<your function name>``.
+* Apply the ``jmespath.functions.signature`` decorator that indicates
+  the expected types of the function arguments.
+* Provide an instance of your subclass in a ``jmespath.Options`` object.
+
+Below are a few examples:
+
+.. code:: python
+
+    import jmespath
+    from jmespath import functions
+
+    # 1. Create a subclass of functions.Functions.
+    #    The function.Functions base class has logic
+    #    that introspects all of its methods and automatically
+    #    registers your custom functions in its function table.
+    class CustomFunctions(functions.Functions):
+
+        # 2 and 3.  Create a function that starts with _func_
+        # and decorate it with @signature which indicates its
+        # expected types.
+        # In this example, we're creating a jmespath function
+        # called "unique_letters" that accepts a single argument
+        # with an expected type "string".
+        @functions.signature({'types': ['string']})
+        def _func_unique_letters(self, s):
+            # Given a string s, return a sorted
+            # string of unique letters: 'ccbbadd' ->  'abcd'
+            return ''.join(sorted(set(s)))
+
+        # Here's another example.  This is creating
+        # a jmespath function called "my_add" that expects
+        # two arguments, both of which should be of type number.
+        @functions.signature({'types': ['number']}, {'types': ['number']})
+        def _func_my_add(self, x, y):
+            return x + y
+
+    # 4. Provide an instance of your subclass in a Options object.
+    options = jmespath.Options(custom_functions=CustomFunctions())
+
+    # Provide this value to jmespath.search:
+    # This will print 3
+    print(
+        jmespath.search(
+            'my_add(`1`, `2`)', {}, options=options)
+    )
+
+    # This will print "abcd"
+    print(
+        jmespath.search(
+            'foo.bar | unique_letters(@)',
+            {'foo': {'bar': 'ccbbadd'}},
+            options=options)
+    )
+
+Again, if you come up with useful functions that you think make
+sense in the JMESPath language (and make sense to implement in all
+JMESPath libraries, not just python), please let us know at
+`jmespath.site <https://github.com/jmespath/jmespath.site/issues>`__.
+
+
 Specification
 =============
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath/__init__.py 
new/jmespath-0.9.2/jmespath/__init__.py
--- old/jmespath-0.9.0/jmespath/__init__.py     2015-10-01 06:11:26.000000000 
+0200
+++ new/jmespath-0.9.2/jmespath/__init__.py     2017-03-11 00:45:48.000000000 
+0100
@@ -1,7 +1,7 @@
 from jmespath import parser
 from jmespath.visitor import Options
 
-__version__ = '0.9.0'
+__version__ = '0.9.2'
 
 
 def compile(expression):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath/compat.py 
new/jmespath-0.9.2/jmespath/compat.py
--- old/jmespath-0.9.0/jmespath/compat.py       2015-09-23 07:04:51.000000000 
+0200
+++ new/jmespath-0.9.2/jmespath/compat.py       2017-03-10 01:49:47.000000000 
+0100
@@ -3,6 +3,15 @@
 
 PY2 = sys.version_info[0] == 2
 
+
+def with_metaclass(meta, *bases):
+    # Taken from flask/six.
+    class metaclass(meta):
+        def __new__(cls, name, this_bases, d):
+            return meta(name, bases, d)
+    return type.__new__(metaclass, 'temporary_class', (), {})
+
+
 if PY2:
     text_type = unicode
     string_type = basestring
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath/functions.py 
new/jmespath-0.9.2/jmespath/functions.py
--- old/jmespath-0.9.0/jmespath/functions.py    2015-09-30 06:37:03.000000000 
+0200
+++ new/jmespath-0.9.2/jmespath/functions.py    2017-03-10 01:49:47.000000000 
+0100
@@ -1,10 +1,9 @@
 import math
 import json
-import weakref
 
 from jmespath import exceptions
 from jmespath.compat import string_type as STRING_TYPE
-from jmespath.compat import get_methods
+from jmespath.compat import get_methods, with_metaclass
 
 
 # python types -> jmespath types
@@ -35,48 +34,39 @@
 }
 
 
-def populate_function_table(cls):
-    func_table = cls.FUNCTION_TABLE
-    for name, method in get_methods(cls):
-        signature = getattr(method, 'signature', None)
-        if signature is not None:
-            func_table[name[6:]] = {"function": method,
-                                    "signature": signature}
-    return cls
-
-
-def builtin_function(*arguments):
-    def _record_arity(func):
+def signature(*arguments):
+    def _record_signature(func):
         func.signature = arguments
         return func
-    return _record_arity
+    return _record_signature
 
 
-@populate_function_table
-class RuntimeFunctions(object):
-    # The built in functions are automatically populated in the FUNCTION_TABLE
-    # using the @builtin_function decorator on methods defined in this class.
+class FunctionRegistry(type):
+    def __init__(cls, name, bases, attrs):
+        cls._populate_function_table()
+        super(FunctionRegistry, cls).__init__(name, bases, attrs)
+
+    def _populate_function_table(cls):
+        function_table = getattr(cls, 'FUNCTION_TABLE', {})
+        # Any method with a @signature decorator that also
+        # starts with "_func_" is registered as a function.
+        # _func_max_by -> max_by function.
+        for name, method in get_methods(cls):
+            if not name.startswith('_func_'):
+                continue
+            signature = getattr(method, 'signature', None)
+            if signature is not None:
+                function_table[name[6:]] = {
+                    'function': method,
+                    'signature': signature,
+                }
+        cls.FUNCTION_TABLE = function_table
 
-    FUNCTION_TABLE = {
-    }
 
-    def __init__(self):
-        self._interpreter = None
-
-    @property
-    def interpreter(self):
-        if self._interpreter is None:
-            return None
-        else:
-            return self._interpreter()
+class Functions(with_metaclass(FunctionRegistry, object)):
 
-    @interpreter.setter
-    def interpreter(self, value):
-        # A weakref is used because we have
-        # a cyclic reference and we want to allow
-        # for the memory to be properly freed when
-        # the objects are no longer needed.
-        self._interpreter = weakref.ref(value)
+    FUNCTION_TABLE = {
+    }
 
     def call_function(self, function_name, resolved_args):
         try:
@@ -170,28 +160,31 @@
                     raise exceptions.JMESPathTypeError(
                         function_name, element, actual_typename, types)
 
-    @builtin_function({'types': ['number']})
+    @signature({'types': ['number']})
     def _func_abs(self, arg):
         return abs(arg)
 
-    @builtin_function({'types': ['array-number']})
+    @signature({'types': ['array-number']})
     def _func_avg(self, arg):
-        return sum(arg) / float(len(arg))
+        if arg:
+            return sum(arg) / float(len(arg))
+        else:
+            return None
 
-    @builtin_function({'types': [], 'variadic': True})
+    @signature({'types': [], 'variadic': True})
     def _func_not_null(self, *arguments):
         for argument in arguments:
             if argument is not None:
                 return argument
 
-    @builtin_function({'types': []})
+    @signature({'types': []})
     def _func_to_array(self, arg):
         if isinstance(arg, list):
             return arg
         else:
             return [arg]
 
-    @builtin_function({'types': []})
+    @signature({'types': []})
     def _func_to_string(self, arg):
         if isinstance(arg, STRING_TYPE):
             return arg
@@ -199,7 +192,7 @@
             return json.dumps(arg, separators=(',', ':'),
                               default=str)
 
-    @builtin_function({'types': []})
+    @signature({'types': []})
     def _func_to_number(self, arg):
         if isinstance(arg, (list, dict, bool)):
             return None
@@ -216,88 +209,88 @@
             except ValueError:
                 return None
 
-    @builtin_function({'types': ['array', 'string']}, {'types': []})
+    @signature({'types': ['array', 'string']}, {'types': []})
     def _func_contains(self, subject, search):
         return search in subject
 
-    @builtin_function({'types': ['string', 'array', 'object']})
+    @signature({'types': ['string', 'array', 'object']})
     def _func_length(self, arg):
         return len(arg)
 
-    @builtin_function({'types': ['string']}, {'types': ['string']})
+    @signature({'types': ['string']}, {'types': ['string']})
     def _func_ends_with(self, search, suffix):
         return search.endswith(suffix)
 
-    @builtin_function({'types': ['string']}, {'types': ['string']})
+    @signature({'types': ['string']}, {'types': ['string']})
     def _func_starts_with(self, search, suffix):
         return search.startswith(suffix)
 
-    @builtin_function({'types': ['array', 'string']})
+    @signature({'types': ['array', 'string']})
     def _func_reverse(self, arg):
         if isinstance(arg, STRING_TYPE):
             return arg[::-1]
         else:
             return list(reversed(arg))
 
-    @builtin_function({"types": ['number']})
+    @signature({"types": ['number']})
     def _func_ceil(self, arg):
         return math.ceil(arg)
 
-    @builtin_function({"types": ['number']})
+    @signature({"types": ['number']})
     def _func_floor(self, arg):
         return math.floor(arg)
 
-    @builtin_function({"types": ['string']}, {"types": ['array-string']})
+    @signature({"types": ['string']}, {"types": ['array-string']})
     def _func_join(self, separator, array):
         return separator.join(array)
 
-    @builtin_function({'types': ['expref']}, {'types': ['array']})
+    @signature({'types': ['expref']}, {'types': ['array']})
     def _func_map(self, expref, arg):
         result = []
         for element in arg:
-            result.append(self.interpreter.visit(expref.expression, element))
+            result.append(expref.visit(expref.expression, element))
         return result
 
-    @builtin_function({"types": ['array-number', 'array-string']})
+    @signature({"types": ['array-number', 'array-string']})
     def _func_max(self, arg):
         if arg:
             return max(arg)
         else:
             return None
 
-    @builtin_function({"types": ["object"], "variadic": True})
+    @signature({"types": ["object"], "variadic": True})
     def _func_merge(self, *arguments):
         merged = {}
         for arg in arguments:
             merged.update(arg)
         return merged
 
-    @builtin_function({"types": ['array-number', 'array-string']})
+    @signature({"types": ['array-number', 'array-string']})
     def _func_min(self, arg):
         if arg:
             return min(arg)
         else:
             return None
 
-    @builtin_function({"types": ['array-string', 'array-number']})
+    @signature({"types": ['array-string', 'array-number']})
     def _func_sort(self, arg):
         return list(sorted(arg))
 
-    @builtin_function({"types": ['array-number']})
+    @signature({"types": ['array-number']})
     def _func_sum(self, arg):
         return sum(arg)
 
-    @builtin_function({"types": ['object']})
+    @signature({"types": ['object']})
     def _func_keys(self, arg):
         # To be consistent with .values()
         # should we also return the indices of a list?
         return list(arg.keys())
 
-    @builtin_function({"types": ['object']})
+    @signature({"types": ['object']})
     def _func_values(self, arg):
         return list(arg.values())
 
-    @builtin_function({'types': []})
+    @signature({'types': []})
     def _func_type(self, arg):
         if isinstance(arg, STRING_TYPE):
             return "string"
@@ -312,7 +305,7 @@
         elif arg is None:
             return "null"
 
-    @builtin_function({'types': ['array']}, {'types': ['expref']})
+    @signature({'types': ['array']}, {'types': ['expref']})
     def _func_sort_by(self, array, expref):
         if not array:
             return array
@@ -323,34 +316,32 @@
         # that validates that type, which requires that remaining array
         # elements resolve to the same type as the first element.
         required_type = self._convert_to_jmespath_type(
-            type(self.interpreter.visit(expref.expression, array[0])).__name__)
+            type(expref.visit(expref.expression, array[0])).__name__)
         if required_type not in ['number', 'string']:
             raise exceptions.JMESPathTypeError(
                 'sort_by', array[0], required_type, ['string', 'number'])
-        keyfunc = self._create_key_func(expref.expression,
+        keyfunc = self._create_key_func(expref,
                                         [required_type],
                                         'sort_by')
         return list(sorted(array, key=keyfunc))
 
-    @builtin_function({'types': ['array']}, {'types': ['expref']})
+    @signature({'types': ['array']}, {'types': ['expref']})
     def _func_min_by(self, array, expref):
-        keyfunc = self._create_key_func(expref.expression,
+        keyfunc = self._create_key_func(expref,
                                         ['number', 'string'],
                                         'min_by')
         return min(array, key=keyfunc)
 
-    @builtin_function({'types': ['array']}, {'types': ['expref']})
+    @signature({'types': ['array']}, {'types': ['expref']})
     def _func_max_by(self, array, expref):
-        keyfunc = self._create_key_func(expref.expression,
+        keyfunc = self._create_key_func(expref,
                                         ['number', 'string'],
                                         'min_by')
         return max(array, key=keyfunc)
 
-    def _create_key_func(self, expr_node, allowed_types, function_name):
-        interpreter = self.interpreter
-
+    def _create_key_func(self, expref, allowed_types, function_name):
         def keyfunc(x):
-            result = interpreter.visit(expr_node, x)
+            result = expref.visit(expref.expression, x)
             actual_typename = type(result).__name__
             jmespath_type = self._convert_to_jmespath_type(actual_typename)
             # allowed_types is in term of jmespath types, not python types.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath/lexer.py 
new/jmespath-0.9.2/jmespath/lexer.py
--- old/jmespath-0.9.0/jmespath/lexer.py        2015-10-01 06:11:19.000000000 
+0200
+++ new/jmespath-0.9.2/jmespath/lexer.py        2017-03-10 01:49:47.000000000 
+0100
@@ -8,7 +8,6 @@
 class Lexer(object):
     START_IDENTIFIER = set(string.ascii_letters + '_')
     VALID_IDENTIFIER = set(string.ascii_letters + string.digits + '_')
-    START_NUMBER = set(string.digits + '-')
     VALID_NUMBER = set(string.digits)
     WHITESPACE = set(" \t\n\r")
     SIMPLE_TOKENS = {
@@ -63,13 +62,22 @@
                 yield self._match_or_else('&', 'and', 'expref')
             elif self._current == '`':
                 yield self._consume_literal()
-            elif self._current in self.START_NUMBER:
+            elif self._current in self.VALID_NUMBER:
                 start = self._position
-                buff = self._current
-                while self._next() in self.VALID_NUMBER:
-                    buff += self._current
+                buff = self._consume_number()
                 yield {'type': 'number', 'value': int(buff),
                        'start': start, 'end': start + len(buff)}
+            elif self._current == '-':
+                # Negative number.
+                start = self._position
+                buff = self._consume_number()
+                if len(buff) > 1:
+                    yield {'type': 'number', 'value': int(buff),
+                           'start': start, 'end': start + len(buff)}
+                else:
+                    raise LexerError(lexer_position=start,
+                                     lexer_value=buff,
+                                     message="Unknown token '%s'" % buff)
             elif self._current == '"':
                 yield self._consume_quoted_identifier()
             elif self._current == '<':
@@ -79,7 +87,15 @@
             elif self._current == '!':
                 yield self._match_or_else('=', 'ne', 'not')
             elif self._current == '=':
-                yield self._match_or_else('=', 'eq', 'unknown')
+                if self._next() == '=':
+                    yield {'type': 'eq', 'value': '==',
+                        'start': self._position - 1, 'end': self._position}
+                    self._next()
+                else:
+                    raise LexerError(
+                        lexer_position=self._position - 1,
+                        lexer_value='=',
+                        message="Unknown token =")
             else:
                 raise LexerError(lexer_position=self._position,
                                  lexer_value=self._current,
@@ -87,6 +103,13 @@
         yield {'type': 'eof', 'value': '',
                'start': self._length, 'end': self._length}
 
+    def _consume_number(self):
+        start = self._position
+        buff = self._current
+        while self._next() in self.VALID_NUMBER:
+            buff += self._current
+        return buff
+
     def _initialize_for_expression(self, expression):
         if not expression:
             raise EmptyExpressionError()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath/parser.py 
new/jmespath-0.9.2/jmespath/parser.py
--- old/jmespath-0.9.0/jmespath/parser.py       2015-10-01 06:11:19.000000000 
+0200
+++ new/jmespath-0.9.2/jmespath/parser.py       2017-03-10 01:49:47.000000000 
+0100
@@ -39,6 +39,7 @@
         'eof': 0,
         'unquoted_identifier': 0,
         'quoted_identifier': 0,
+        'literal': 0,
         'rbracket': 0,
         'rparen': 0,
         'comma': 0,
@@ -133,9 +134,6 @@
                 current_token = self._current_token()
         return left
 
-    def _token_nud_string_literal(self, token):
-        return ast.literal(token['value'])
-
     def _token_nud_literal(self, token):
         return ast.literal(token['value'])
 
@@ -224,17 +222,16 @@
         while not current_token == 'rbracket' and index < 3:
             if current_token == 'colon':
                 index += 1
+                if index == 3:
+                    self._raise_parse_error_for_token(
+                        self._lookahead_token(0), 'syntax error')
                 self._advance()
             elif current_token == 'number':
                 parts[index] = self._lookahead_token(0)['value']
                 self._advance()
             else:
-                t = self._lookahead_token(0)
-                lex_position = t['start']
-                actual_value = t['value']
-                actual_type = t['type']
-                raise exceptions.ParseError(lex_position, actual_value,
-                                            actual_type, 'syntax error')
+                self._raise_parse_error_for_token(
+                    self._lookahead_token(0), 'syntax error')
             current_token = self._current_token()
         self._match('rbracket')
         return ast.slice(*parts)
@@ -274,6 +271,14 @@
         return ast.and_expression(left, right)
 
     def _token_led_lparen(self, left):
+        if left['type'] != 'field':
+            #  0 - first func arg or closing paren.
+            # -1 - '(' token
+            # -2 - invalid function "name".
+            prev_t = self._lookahead_token(-2)
+            raise exceptions.ParseError(
+                prev_t['start'], prev_t['value'], prev_t['type'],
+                "Invalid function name '%s'" % prev_t['value'])
         name = left['value']
         args = []
         while not self._current_token() == 'rparen':
@@ -396,12 +401,8 @@
             self._match('dot')
             right = self._parse_dot_rhs(binding_power)
         else:
-            t = self._lookahead_token(0)
-            lex_position = t['start']
-            actual_value = t['value']
-            actual_type = t['type']
-            raise exceptions.ParseError(lex_position, actual_value,
-                                        actual_type, 'syntax error')
+            self._raise_parse_error_for_token(self._lookahead_token(0),
+                                              'syntax error')
         return right
 
     def _parse_dot_rhs(self, binding_power):
@@ -427,34 +428,19 @@
             t = self._lookahead_token(0)
             allowed = ['quoted_identifier', 'unquoted_identifier',
                        'lbracket', 'lbrace']
-            lex_position = t['start']
-            actual_value = t['value']
-            actual_type = t['type']
-            raise exceptions.ParseError(
-                lex_position, actual_value, actual_type,
-                "Expecting: %s, got: %s" % (allowed,
-                                            actual_type))
-
-    def _assert_not_token(self, *token_types):
-        if self._current_token() in token_types:
-            t = self._lookahead_token(0)
-            lex_position = t['start']
-            actual_value = t['value']
-            actual_type = t['type']
-            raise exceptions.ParseError(
-                lex_position, actual_value, actual_type,
-                "Token %s not allowed to be: %s" % (actual_type, token_types))
+            msg = (
+                "Expecting: %s, got: %s" % (allowed, t['type'])
+            )
+            self._raise_parse_error_for_token(t, msg)
 
     def _error_nud_token(self, token):
         if token['type'] == 'eof':
             raise exceptions.IncompleteExpressionError(
                 token['start'], token['value'], token['type'])
-        raise exceptions.ParseError(token['start'], token['value'],
-                                    token['type'], 'Invalid token.')
+        self._raise_parse_error_for_token(token, 'invalid token')
 
     def _error_led_token(self, token):
-        raise exceptions.ParseError(token['start'], token['value'],
-                                    token['type'], 'Invalid token')
+        self._raise_parse_error_for_token(token, 'invalid token')
 
     def _match(self, token_type=None):
         # inline'd self._current_token()
@@ -462,33 +448,13 @@
             # inline'd self._advance()
             self._advance()
         else:
-            t = self._lookahead_token(0)
-            lex_position = t['start']
-            actual_value = t['value']
-            actual_type = t['type']
-            if actual_type == 'eof':
-                raise exceptions.IncompleteExpressionError(
-                    lex_position, actual_value, actual_type)
-            else:
-                message = 'Expecting: %s, got: %s' % (token_type,
-                                                      actual_type)
-            raise exceptions.ParseError(
-                lex_position, actual_value, actual_type, message)
+            self._raise_parse_error_maybe_eof(
+                token_type, self._lookahead_token(0))
 
     def _match_multiple_tokens(self, token_types):
         if self._current_token() not in token_types:
-            t = self._lookahead_token(0)
-            lex_position = t['start']
-            actual_value = t['value']
-            actual_type = t['type']
-            if actual_type == 'eof':
-                raise exceptions.IncompleteExpressionError(
-                    lex_position, actual_value, actual_type)
-            else:
-                message = 'Expecting: %s, got: %s' % (token_types,
-                                                      actual_type)
-            raise exceptions.ParseError(
-                lex_position, actual_value, actual_type, message)
+            self._raise_parse_error_maybe_eof(
+                token_types, self._lookahead_token(0))
         self._advance()
 
     def _advance(self):
@@ -503,6 +469,25 @@
     def _lookahead_token(self, number):
         return self._tokens[self._index + number]
 
+    def _raise_parse_error_for_token(self, token, reason):
+        lex_position = token['start']
+        actual_value = token['value']
+        actual_type = token['type']
+        raise exceptions.ParseError(lex_position, actual_value,
+                                    actual_type, reason)
+
+    def _raise_parse_error_maybe_eof(self, expected_type, token):
+        lex_position = token['start']
+        actual_value = token['value']
+        actual_type = token['type']
+        if actual_type == 'eof':
+            raise exceptions.IncompleteExpressionError(
+                lex_position, actual_value, actual_type)
+        message = 'Expecting: %s, got: %s' % (expected_type,
+                                              actual_type)
+        raise exceptions.ParseError(
+            lex_position, actual_value, actual_type, message)
+
     def _free_cache_entries(self):
         for key in random.sample(self._CACHE.keys(), int(self._MAX_SIZE / 2)):
             del self._CACHE[key]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath/visitor.py 
new/jmespath-0.9.2/jmespath/visitor.py
--- old/jmespath-0.9.0/jmespath/visitor.py      2015-10-01 06:11:19.000000000 
+0200
+++ new/jmespath-0.9.2/jmespath/visitor.py      2017-03-11 00:45:41.000000000 
+0100
@@ -1,6 +1,7 @@
 import operator
 
 from jmespath import functions
+from jmespath.compat import string_type
 
 
 def _equals(x, y):
@@ -33,9 +34,30 @@
         return x is True or x is False
 
 
+def _is_comparable(x):
+    # The spec doesn't officially support string types yet,
+    # but enough people are relying on this behavior that
+    # it's been added back.  This should eventually become
+    # part of the official spec.
+    return _is_actual_number(x) or isinstance(x, string_type)
+
+
+def _is_actual_number(x):
+    # We need to handle python's quirkiness with booleans,
+    # specifically:
+    #
+    # >>> isinstance(False, int)
+    # True
+    # >>> isinstance(True, int)
+    # True
+    if x is True or x is False:
+        return False
+    return isinstance(x, (float, int))
+
+
 class Options(object):
     """Options to control how a JMESPath function is evaluated."""
-    def __init__(self, dict_cls):
+    def __init__(self, dict_cls=None, custom_functions=None):
         #: The class to use when creating a dict.  The interpreter
         #  may create dictionaries during the evalution of a JMESPath
         #  expression.  For example, a multi-select hash will
@@ -45,11 +67,16 @@
         #  want to set a collections.OrderedDict so that you can
         #  have predictible key ordering.
         self.dict_cls = dict_cls
+        self.custom_functions = custom_functions
 
 
 class _Expression(object):
-    def __init__(self, expression):
+    def __init__(self, expression, interpreter):
         self.expression = expression
+        self.interpreter = interpreter
+
+    def visit(self, node, *args, **kwargs):
+        return self.interpreter.visit(node, *args, **kwargs)
 
 
 class Visitor(object):
@@ -71,27 +98,28 @@
 
 class TreeInterpreter(Visitor):
     COMPARATOR_FUNC = {
-        'le': operator.le,
+        'eq': _equals,
         'ne': lambda x, y: not _equals(x, y),
         'lt': operator.lt,
-        'lte': operator.le,
-        'eq': _equals,
         'gt': operator.gt,
+        'lte': operator.le,
         'gte': operator.ge
     }
+    _EQUALITY_OPS = ['eq', 'ne']
     MAP_TYPE = dict
 
     def __init__(self, options=None):
         super(TreeInterpreter, self).__init__()
-        self._options = options
         self._dict_cls = self.MAP_TYPE
-        if options is not None and options.dict_cls is not None:
+        if options is None:
+            options = Options()
+        self._options = options
+        if options.dict_cls is not None:
             self._dict_cls = self._options.dict_cls
-        self._functions = functions.RuntimeFunctions()
-        # Note that .interpreter is a property that uses
-        # a weakref so that the cyclic reference can be
-        # properly freed.
-        self._functions.interpreter = self
+        if options.custom_functions is not None:
+            self._functions = self._options.custom_functions
+        else:
+            self._functions = functions.Functions()
 
     def default_visit(self, node, *args, **kwargs):
         raise NotImplementedError(node['type'])
@@ -109,17 +137,30 @@
             return None
 
     def visit_comparator(self, node, value):
+        # Common case: comparator is == or !=
         comparator_func = self.COMPARATOR_FUNC[node['value']]
-        return comparator_func(
-            self.visit(node['children'][0], value),
-            self.visit(node['children'][1], value)
-        )
+        if node['value'] in self._EQUALITY_OPS:
+            return comparator_func(
+                self.visit(node['children'][0], value),
+                self.visit(node['children'][1], value)
+            )
+        else:
+            # Ordering operators are only valid for numbers.
+            # Evaluating any other type with a comparison operator
+            # will yield a None value.
+            left = self.visit(node['children'][0], value)
+            right = self.visit(node['children'][1], value)
+            num_types = (int, float)
+            if not (_is_comparable(left) and
+                    _is_comparable(right)):
+                return None
+            return comparator_func(left, right)
 
     def visit_current(self, node, value):
         return value
 
     def visit_expref(self, node, value):
-        return _Expression(node['children'][0])
+        return _Expression(node['children'][0], self)
 
     def visit_function_expression(self, node, value):
         resolved_args = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath.egg-info/PKG-INFO 
new/jmespath-0.9.2/jmespath.egg-info/PKG-INFO
--- old/jmespath-0.9.0/jmespath.egg-info/PKG-INFO       2015-10-01 
06:12:12.000000000 +0200
+++ new/jmespath-0.9.2/jmespath.egg-info/PKG-INFO       2017-03-11 
00:52:50.000000000 +0100
@@ -1,11 +1,11 @@
 Metadata-Version: 1.1
 Name: jmespath
-Version: 0.9.0
+Version: 0.9.2
 Summary: JSON Matching Expressions
 Home-page: https://github.com/jmespath/jmespath.py
 Author: James Saryerwinnie
 Author-email: [email protected]
-License: UNKNOWN
+License: MIT
 Description: JMESPath
         ========
         
@@ -18,6 +18,10 @@
            :target: http://travis-ci.org/jmespath/jmespath.py
         
         
+        .. image:: 
https://codecov.io/github/jmespath/jmespath.py/coverage.svg?branch=develop
+            :target: 
https://codecov.io/github/jmespath/jmespath.py?branch=develop
+        
+        
         JMESPath (pronounced "james path") allows you to declaratively specify 
how to
         extract elements from a JSON document.
         
@@ -56,7 +60,9 @@
         
         The ``jmespath.py`` library has two functions
         that operate on python data structures.  You can use ``search``
-        and give it the jmespath expression and the data::
+        and give it the jmespath expression and the data:
+        
+        .. code:: python
         
             >>> import jmespath
             >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
@@ -64,7 +70,9 @@
         
         Similar to the ``re`` module, you can use the ``compile`` function
         to compile the JMESPath expression and use this parsed expression
-        to perform repeated searches::
+        to perform repeated searches:
+        
+        .. code:: python
         
             >>> import jmespath
             >>> expression = jmespath.compile('foo.bar')
@@ -83,7 +91,9 @@
         You can provide an instance of ``jmespath.Options`` to control how
         a JMESPath expression is evaluated.  The most common scenario for
         using an ``Options`` instance is if you want to have ordered output
-        of your dict keys.  To do this you can use either of these options::
+        of your dict keys.  To do this you can use either of these options:
+        
+        .. code:: python
         
             >>> import jmespath
             >>> jmespath.search('{a: a, b: b},
@@ -98,6 +108,85 @@
             ...               
jmespath.Options(dict_cls=collections.OrderedDict))
         
         
+        Custom Functions
+        ~~~~~~~~~~~~~~~~
+        
+        The JMESPath language has numerous
+        `built-in functions
+        <http://jmespath.org/specification.html#built-in-functions>`__, but it 
is
+        also possible to add your own custom functions.  Keep in mind that
+        custom function support in jmespath.py is experimental and the API may
+        change based on feedback.
+        
+        **If you have a custom function that you've found useful, consider 
submitting
+        it to jmespath.site and propose that it be added to the JMESPath 
language.**
+        You can submit proposals
+        `here <https://github.com/jmespath/jmespath.site/issues>`__.
+        
+        To create custom functions:
+        
+        * Create a subclass of ``jmespath.functions.Functions``.
+        * Create a method with the name ``_func_<your function name>``.
+        * Apply the ``jmespath.functions.signature`` decorator that indicates
+          the expected types of the function arguments.
+        * Provide an instance of your subclass in a ``jmespath.Options`` 
object.
+        
+        Below are a few examples:
+        
+        .. code:: python
+        
+            import jmespath
+            from jmespath import functions
+        
+            # 1. Create a subclass of functions.Functions.
+            #    The function.Functions base class has logic
+            #    that introspects all of its methods and automatically
+            #    registers your custom functions in its function table.
+            class CustomFunctions(functions.Functions):
+        
+                # 2 and 3.  Create a function that starts with _func_
+                # and decorate it with @signature which indicates its
+                # expected types.
+                # In this example, we're creating a jmespath function
+                # called "unique_letters" that accepts a single argument
+                # with an expected type "string".
+                @functions.signature({'types': ['string']})
+                def _func_unique_letters(self, s):
+                    # Given a string s, return a sorted
+                    # string of unique letters: 'ccbbadd' ->  'abcd'
+                    return ''.join(sorted(set(s)))
+        
+                # Here's another example.  This is creating
+                # a jmespath function called "my_add" that expects
+                # two arguments, both of which should be of type number.
+                @functions.signature({'types': ['number']}, {'types': 
['number']})
+                def _func_my_add(self, x, y):
+                    return x + y
+        
+            # 4. Provide an instance of your subclass in a Options object.
+            options = jmespath.Options(custom_functions=CustomFunctions())
+        
+            # Provide this value to jmespath.search:
+            # This will print 3
+            print(
+                jmespath.search(
+                    'my_add(`1`, `2`)', {}, options=options)
+            )
+        
+            # This will print "abcd"
+            print(
+                jmespath.search(
+                    'foo.bar | unique_letters(@)',
+                    {'foo': {'bar': 'ccbbadd'}},
+                    options=options)
+            )
+        
+        Again, if you come up with useful functions that you think make
+        sense in the JMESPath language (and make sense to implement in all
+        JMESPath libraries, not just python), please let us know at
+        `jmespath.site <https://github.com/jmespath/jmespath.site/issues>`__.
+        
+        
         Specification
         =============
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath.egg-info/SOURCES.txt 
new/jmespath-0.9.2/jmespath.egg-info/SOURCES.txt
--- old/jmespath-0.9.0/jmespath.egg-info/SOURCES.txt    2015-10-01 
06:12:12.000000000 +0200
+++ new/jmespath-0.9.2/jmespath.egg-info/SOURCES.txt    2017-03-11 
00:52:50.000000000 +0100
@@ -15,7 +15,6 @@
 jmespath.egg-info/PKG-INFO
 jmespath.egg-info/SOURCES.txt
 jmespath.egg-info/dependency_links.txt
-jmespath.egg-info/pbr.json
 jmespath.egg-info/top_level.txt
 tests/__init__.py
 tests/test_compliance.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/jmespath.egg-info/pbr.json 
new/jmespath-0.9.2/jmespath.egg-info/pbr.json
--- old/jmespath-0.9.0/jmespath.egg-info/pbr.json       2015-04-21 
08:34:36.000000000 +0200
+++ new/jmespath-0.9.2/jmespath.egg-info/pbr.json       1970-01-01 
01:00:00.000000000 +0100
@@ -1 +0,0 @@
-{"is_release": true, "git_version": "0466cc1"}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/setup.py new/jmespath-0.9.2/setup.py
--- old/jmespath-0.9.0/setup.py 2015-10-01 06:11:26.000000000 +0200
+++ new/jmespath-0.9.2/setup.py 2017-03-11 00:45:48.000000000 +0100
@@ -9,7 +9,7 @@
 
 setup(
     name='jmespath',
-    version='0.9.0',
+    version='0.9.2',
     description='JSON Matching Expressions',
     long_description=io.open('README.rst', encoding='utf-8').read(),
     author='James Saryerwinnie',
@@ -17,6 +17,7 @@
     url='https://github.com/jmespath/jmespath.py',
     scripts=['bin/jp.py'],
     packages=find_packages(exclude=['tests']),
+    license='MIT',
     classifiers=(
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jmespath-0.9.0/tests/test_compliance.py 
new/jmespath-0.9.2/tests/test_compliance.py
--- old/jmespath-0.9.0/tests/test_compliance.py 2015-10-01 06:11:19.000000000 
+0200
+++ new/jmespath-0.9.2/tests/test_compliance.py 2017-03-10 01:49:47.000000000 
+0100
@@ -5,8 +5,7 @@
 
 from nose.tools import assert_equal
 
-import jmespath
-from jmespath.visitor import TreeInterpreter, Options
+from jmespath.visitor import Options
 
 
 TEST_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -19,16 +18,17 @@
 def test_compliance():
     for full_path in _walk_files():
         if full_path.endswith('.json'):
-            for given, expression, result, error in _load_cases(full_path):
-                if error is NOT_SPECIFIED and result is not NOT_SPECIFIED:
-                    yield (_test_expression, given, expression,
-                        result, os.path.basename(full_path))
-                elif result is NOT_SPECIFIED and error is not NOT_SPECIFIED:
-                    yield (_test_error_expression, given, expression,
-                           error, os.path.basename(full_path))
-                else:
-                    parts = (given, expression, result, error)
-                    raise RuntimeError("Invalid test description: %s" % parts)
+            for given, test_type, test_data in load_cases(full_path):
+                t = test_data
+                # Benchmark tests aren't run as part of the normal
+                # test suite, so we only care about 'result' and
+                # 'error' test_types.
+                if test_type == 'result':
+                    yield (_test_expression, given, t['expression'],
+                           t['result'], os.path.basename(full_path))
+                elif test_type == 'error':
+                    yield (_test_error_expression, given, t['expression'],
+                           t['error'], os.path.basename(full_path))
 
 
 def _walk_files():
@@ -47,14 +47,20 @@
                 yield os.path.join(root, filename)
 
 
-def _load_cases(full_path):
+def load_cases(full_path):
     all_test_data = json.load(open(full_path), object_pairs_hook=OrderedDict)
     for test_data in all_test_data:
         given = test_data['given']
         for case in test_data['cases']:
-            yield (given, case['expression'],
-                   case.get('result', NOT_SPECIFIED),
-                   case.get('error', NOT_SPECIFIED))
+            if 'result' in case:
+                test_type = 'result'
+            elif 'error' in case:
+                test_type = 'error'
+            elif 'bench' in case:
+                test_type = 'bench'
+            else:
+                raise RuntimeError("Unknown test type: %s" % json.dumps(case))
+            yield (given, test_type, case)
 
 
 def _test_expression(given, expression, expected, filename):
@@ -85,9 +91,16 @@
     try:
         parsed = jmespath.compile(expression)
         parsed.search(given)
-    except ValueError as e:
+    except ValueError:
         # Test passes, it raised a parse error as expected.
         pass
+    except Exception as e:
+        # Failure because an unexpected exception was raised.
+        error_msg = ("\n\n  (%s) The expression '%s' was suppose to be a "
+                     "syntax error, but it raised an unexpected error:\n\n%s" 
% (
+                         filename, expression, e))
+        error_msg = error_msg.replace(r'\n', '\n')
+        raise AssertionError(error_msg)
     else:
         error_msg = ("\n\n  (%s) The expression '%s' was suppose to be a "
                      "syntax error, but it successfully parsed as:\n\n%s" % (


Reply via email to