aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/jmespath
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-02-10 16:44:30 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:44:30 +0300
commit2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch)
tree012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/jmespath
parent6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff)
downloadydb-2598ef1d0aee359b4b6d5fdd1758916d5907d04f.tar.gz
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/jmespath')
-rw-r--r--contrib/python/jmespath/.dist-info/METADATA502
-rw-r--r--contrib/python/jmespath/.dist-info/top_level.txt2
-rw-r--r--contrib/python/jmespath/LICENSE.txt40
-rw-r--r--contrib/python/jmespath/README.rst444
-rw-r--r--contrib/python/jmespath/jmespath/__init__.py24
-rw-r--r--contrib/python/jmespath/jmespath/parser.py2
-rw-r--r--contrib/python/jmespath/jmespath/visitor.py6
-rw-r--r--contrib/python/jmespath/tests/__init__.py80
-rw-r--r--contrib/python/jmespath/tests/test_compliance.py204
-rw-r--r--contrib/python/jmespath/tests/test_parser.py736
-rw-r--r--contrib/python/jmespath/tests/ya.make34
-rw-r--r--contrib/python/jmespath/ya.make40
12 files changed, 1057 insertions, 1057 deletions
diff --git a/contrib/python/jmespath/.dist-info/METADATA b/contrib/python/jmespath/.dist-info/METADATA
index 78a973544b..ee91cf2412 100644
--- a/contrib/python/jmespath/.dist-info/METADATA
+++ b/contrib/python/jmespath/.dist-info/METADATA
@@ -1,251 +1,251 @@
-Metadata-Version: 2.0
-Name: jmespath
-Version: 0.10.0
-Summary: JSON Matching Expressions
-Home-page: https://github.com/jmespath/jmespath.py
-Author: James Saryerwinnie
-Author-email: js@jamesls.com
-License: MIT
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
-
-JMESPath
-========
-
-
-.. image:: https://badges.gitter.im/Join Chat.svg
- :target: https://gitter.im/jmespath/chat
-
-
-.. image:: https://travis-ci.org/jmespath/jmespath.py.svg?branch=develop
- :target: https://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.
-
-For example, given this document::
-
- {"foo": {"bar": "baz"}}
-
-The jmespath expression ``foo.bar`` will return "baz".
-
-JMESPath also supports:
-
-Referencing elements in a list. Given the data::
-
- {"foo": {"bar": ["one", "two"]}}
-
-The expression: ``foo.bar[0]`` will return "one".
-You can also reference all the items in a list using the ``*``
-syntax::
-
- {"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}
-
-The expression: ``foo.bar[*].name`` will return ["one", "two"].
-Negative indexing is also supported (-1 refers to the last element
-in the list). Given the data above, the expression
-``foo.bar[-1].name`` will return "two".
-
-The ``*`` can also be used for hash types::
-
- {"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}}
-
-The expression: ``foo.*.name`` will return ["one", "two"].
-
-
-Installation
-============
-
-You can install JMESPath from pypi with:
-
-.. code:: bash
-
- pip install jmespath
-
-
-API
-===
-
-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:
-
-.. code:: python
-
- >>> import jmespath
- >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
- 'baz'
-
-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:
-
-.. code:: python
-
- >>> import jmespath
- >>> expression = jmespath.compile('foo.bar')
- >>> expression.search({'foo': {'bar': 'baz'}})
- 'baz'
- >>> expression.search({'foo': {'bar': 'other'}})
- 'other'
-
-This is useful if you're going to use the same jmespath expression to
-search multiple documents. This avoids having to reparse the
-JMESPath expression each time you search a new document.
-
-Options
--------
-
-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:
-
-.. code:: python
-
- >>> import jmespath
- >>> jmespath.search('{a: a, b: b}',
- ... mydata,
- ... jmespath.Options(dict_cls=collections.OrderedDict))
-
-
- >>> import jmespath
- >>> parsed = jmespath.compile('{a: a, b: b}')
- >>> parsed.search(mydata,
- ... 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
-=============
-
-If you'd like to learn more about the JMESPath language, you can check out
-the `JMESPath tutorial <http://jmespath.org/tutorial.html>`__. Also check
-out the `JMESPath examples page <http://jmespath.org/examples.html>`__ for
-examples of more complex jmespath queries.
-
-The grammar is specified using ABNF, as described in
-`RFC4234 <http://www.ietf.org/rfc/rfc4234.txt>`_.
-You can find the most up to date
-`grammar for JMESPath here <http://jmespath.org/specification.html#grammar>`__.
-
-You can read the full
-`JMESPath specification here <http://jmespath.org/specification.html>`__.
-
-
-Testing
-=======
-
-In addition to the unit tests for the jmespath modules,
-there is a ``tests/compliance`` directory that contains
-.json files with test cases. This allows other implementations
-to verify they are producing the correct output. Each json
-file is grouped by feature.
-
-
-Discuss
-=======
-
-Join us on our `Gitter channel <https://gitter.im/jmespath/chat>`__
-if you want to chat or if you have any questions.
-
-
+Metadata-Version: 2.0
+Name: jmespath
+Version: 0.10.0
+Summary: JSON Matching Expressions
+Home-page: https://github.com/jmespath/jmespath.py
+Author: James Saryerwinnie
+Author-email: js@jamesls.com
+License: MIT
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
+
+JMESPath
+========
+
+
+.. image:: https://badges.gitter.im/Join Chat.svg
+ :target: https://gitter.im/jmespath/chat
+
+
+.. image:: https://travis-ci.org/jmespath/jmespath.py.svg?branch=develop
+ :target: https://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.
+
+For example, given this document::
+
+ {"foo": {"bar": "baz"}}
+
+The jmespath expression ``foo.bar`` will return "baz".
+
+JMESPath also supports:
+
+Referencing elements in a list. Given the data::
+
+ {"foo": {"bar": ["one", "two"]}}
+
+The expression: ``foo.bar[0]`` will return "one".
+You can also reference all the items in a list using the ``*``
+syntax::
+
+ {"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}
+
+The expression: ``foo.bar[*].name`` will return ["one", "two"].
+Negative indexing is also supported (-1 refers to the last element
+in the list). Given the data above, the expression
+``foo.bar[-1].name`` will return "two".
+
+The ``*`` can also be used for hash types::
+
+ {"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}}
+
+The expression: ``foo.*.name`` will return ["one", "two"].
+
+
+Installation
+============
+
+You can install JMESPath from pypi with:
+
+.. code:: bash
+
+ pip install jmespath
+
+
+API
+===
+
+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:
+
+.. code:: python
+
+ >>> import jmespath
+ >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
+ 'baz'
+
+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:
+
+.. code:: python
+
+ >>> import jmespath
+ >>> expression = jmespath.compile('foo.bar')
+ >>> expression.search({'foo': {'bar': 'baz'}})
+ 'baz'
+ >>> expression.search({'foo': {'bar': 'other'}})
+ 'other'
+
+This is useful if you're going to use the same jmespath expression to
+search multiple documents. This avoids having to reparse the
+JMESPath expression each time you search a new document.
+
+Options
+-------
+
+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:
+
+.. code:: python
+
+ >>> import jmespath
+ >>> jmespath.search('{a: a, b: b}',
+ ... mydata,
+ ... jmespath.Options(dict_cls=collections.OrderedDict))
+
+
+ >>> import jmespath
+ >>> parsed = jmespath.compile('{a: a, b: b}')
+ >>> parsed.search(mydata,
+ ... 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
+=============
+
+If you'd like to learn more about the JMESPath language, you can check out
+the `JMESPath tutorial <http://jmespath.org/tutorial.html>`__. Also check
+out the `JMESPath examples page <http://jmespath.org/examples.html>`__ for
+examples of more complex jmespath queries.
+
+The grammar is specified using ABNF, as described in
+`RFC4234 <http://www.ietf.org/rfc/rfc4234.txt>`_.
+You can find the most up to date
+`grammar for JMESPath here <http://jmespath.org/specification.html#grammar>`__.
+
+You can read the full
+`JMESPath specification here <http://jmespath.org/specification.html>`__.
+
+
+Testing
+=======
+
+In addition to the unit tests for the jmespath modules,
+there is a ``tests/compliance`` directory that contains
+.json files with test cases. This allows other implementations
+to verify they are producing the correct output. Each json
+file is grouped by feature.
+
+
+Discuss
+=======
+
+Join us on our `Gitter channel <https://gitter.im/jmespath/chat>`__
+if you want to chat or if you have any questions.
+
+
diff --git a/contrib/python/jmespath/.dist-info/top_level.txt b/contrib/python/jmespath/.dist-info/top_level.txt
index 45c1e038e5..015d274622 100644
--- a/contrib/python/jmespath/.dist-info/top_level.txt
+++ b/contrib/python/jmespath/.dist-info/top_level.txt
@@ -1 +1 @@
-jmespath
+jmespath
diff --git a/contrib/python/jmespath/LICENSE.txt b/contrib/python/jmespath/LICENSE.txt
index aa68928536..e3f59deed7 100644
--- a/contrib/python/jmespath/LICENSE.txt
+++ b/contrib/python/jmespath/LICENSE.txt
@@ -1,20 +1,20 @@
-Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish, dis-
-tribute, sublicense, and/or sell copies of the Software, and to permit
-persons to whom the Software is furnished to do so, subject to the fol-
-lowing conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
-ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
+Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, dis-
+tribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the fol-
+lowing conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/contrib/python/jmespath/README.rst b/contrib/python/jmespath/README.rst
index 530709edee..c49525b833 100644
--- a/contrib/python/jmespath/README.rst
+++ b/contrib/python/jmespath/README.rst
@@ -1,222 +1,222 @@
-JMESPath
-========
-
-
-.. image:: https://badges.gitter.im/Join Chat.svg
- :target: https://gitter.im/jmespath/chat
-
-
-.. image:: https://travis-ci.org/jmespath/jmespath.py.svg?branch=develop
- :target: https://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.
-
-For example, given this document::
-
- {"foo": {"bar": "baz"}}
-
-The jmespath expression ``foo.bar`` will return "baz".
-
-JMESPath also supports:
-
-Referencing elements in a list. Given the data::
-
- {"foo": {"bar": ["one", "two"]}}
-
-The expression: ``foo.bar[0]`` will return "one".
-You can also reference all the items in a list using the ``*``
-syntax::
-
- {"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}
-
-The expression: ``foo.bar[*].name`` will return ["one", "two"].
-Negative indexing is also supported (-1 refers to the last element
-in the list). Given the data above, the expression
-``foo.bar[-1].name`` will return "two".
-
-The ``*`` can also be used for hash types::
-
- {"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}}
-
-The expression: ``foo.*.name`` will return ["one", "two"].
-
-
-Installation
-============
-
-You can install JMESPath from pypi with:
-
-.. code:: bash
-
- pip install jmespath
-
-
-API
-===
-
-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:
-
-.. code:: python
-
- >>> import jmespath
- >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
- 'baz'
-
-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:
-
-.. code:: python
-
- >>> import jmespath
- >>> expression = jmespath.compile('foo.bar')
- >>> expression.search({'foo': {'bar': 'baz'}})
- 'baz'
- >>> expression.search({'foo': {'bar': 'other'}})
- 'other'
-
-This is useful if you're going to use the same jmespath expression to
-search multiple documents. This avoids having to reparse the
-JMESPath expression each time you search a new document.
-
-Options
--------
-
-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:
-
-.. code:: python
-
- >>> import jmespath
- >>> jmespath.search('{a: a, b: b}',
- ... mydata,
- ... jmespath.Options(dict_cls=collections.OrderedDict))
-
-
- >>> import jmespath
- >>> parsed = jmespath.compile('{a: a, b: b}')
- >>> parsed.search(mydata,
- ... 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
-=============
-
-If you'd like to learn more about the JMESPath language, you can check out
-the `JMESPath tutorial <http://jmespath.org/tutorial.html>`__. Also check
-out the `JMESPath examples page <http://jmespath.org/examples.html>`__ for
-examples of more complex jmespath queries.
-
-The grammar is specified using ABNF, as described in
-`RFC4234 <http://www.ietf.org/rfc/rfc4234.txt>`_.
-You can find the most up to date
-`grammar for JMESPath here <http://jmespath.org/specification.html#grammar>`__.
-
-You can read the full
-`JMESPath specification here <http://jmespath.org/specification.html>`__.
-
-
-Testing
-=======
-
-In addition to the unit tests for the jmespath modules,
-there is a ``tests/compliance`` directory that contains
-.json files with test cases. This allows other implementations
-to verify they are producing the correct output. Each json
-file is grouped by feature.
-
-
-Discuss
-=======
-
-Join us on our `Gitter channel <https://gitter.im/jmespath/chat>`__
-if you want to chat or if you have any questions.
+JMESPath
+========
+
+
+.. image:: https://badges.gitter.im/Join Chat.svg
+ :target: https://gitter.im/jmespath/chat
+
+
+.. image:: https://travis-ci.org/jmespath/jmespath.py.svg?branch=develop
+ :target: https://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.
+
+For example, given this document::
+
+ {"foo": {"bar": "baz"}}
+
+The jmespath expression ``foo.bar`` will return "baz".
+
+JMESPath also supports:
+
+Referencing elements in a list. Given the data::
+
+ {"foo": {"bar": ["one", "two"]}}
+
+The expression: ``foo.bar[0]`` will return "one".
+You can also reference all the items in a list using the ``*``
+syntax::
+
+ {"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}
+
+The expression: ``foo.bar[*].name`` will return ["one", "two"].
+Negative indexing is also supported (-1 refers to the last element
+in the list). Given the data above, the expression
+``foo.bar[-1].name`` will return "two".
+
+The ``*`` can also be used for hash types::
+
+ {"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}}
+
+The expression: ``foo.*.name`` will return ["one", "two"].
+
+
+Installation
+============
+
+You can install JMESPath from pypi with:
+
+.. code:: bash
+
+ pip install jmespath
+
+
+API
+===
+
+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:
+
+.. code:: python
+
+ >>> import jmespath
+ >>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
+ 'baz'
+
+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:
+
+.. code:: python
+
+ >>> import jmespath
+ >>> expression = jmespath.compile('foo.bar')
+ >>> expression.search({'foo': {'bar': 'baz'}})
+ 'baz'
+ >>> expression.search({'foo': {'bar': 'other'}})
+ 'other'
+
+This is useful if you're going to use the same jmespath expression to
+search multiple documents. This avoids having to reparse the
+JMESPath expression each time you search a new document.
+
+Options
+-------
+
+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:
+
+.. code:: python
+
+ >>> import jmespath
+ >>> jmespath.search('{a: a, b: b}',
+ ... mydata,
+ ... jmespath.Options(dict_cls=collections.OrderedDict))
+
+
+ >>> import jmespath
+ >>> parsed = jmespath.compile('{a: a, b: b}')
+ >>> parsed.search(mydata,
+ ... 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
+=============
+
+If you'd like to learn more about the JMESPath language, you can check out
+the `JMESPath tutorial <http://jmespath.org/tutorial.html>`__. Also check
+out the `JMESPath examples page <http://jmespath.org/examples.html>`__ for
+examples of more complex jmespath queries.
+
+The grammar is specified using ABNF, as described in
+`RFC4234 <http://www.ietf.org/rfc/rfc4234.txt>`_.
+You can find the most up to date
+`grammar for JMESPath here <http://jmespath.org/specification.html#grammar>`__.
+
+You can read the full
+`JMESPath specification here <http://jmespath.org/specification.html>`__.
+
+
+Testing
+=======
+
+In addition to the unit tests for the jmespath modules,
+there is a ``tests/compliance`` directory that contains
+.json files with test cases. This allows other implementations
+to verify they are producing the correct output. Each json
+file is grouped by feature.
+
+
+Discuss
+=======
+
+Join us on our `Gitter channel <https://gitter.im/jmespath/chat>`__
+if you want to chat or if you have any questions.
diff --git a/contrib/python/jmespath/jmespath/__init__.py b/contrib/python/jmespath/jmespath/__init__.py
index 99482dba8e..c169c8754a 100644
--- a/contrib/python/jmespath/jmespath/__init__.py
+++ b/contrib/python/jmespath/jmespath/__init__.py
@@ -1,20 +1,20 @@
-import warnings
-import sys
+import warnings
+import sys
from jmespath import parser
from jmespath.visitor import Options
-__version__ = '0.10.0'
-
-
-if sys.version_info[:2] <= (2, 6) or ((3, 0) <= sys.version_info[:2] <= (3, 3)):
- python_ver = '.'.join(str(x) for x in sys.version_info[:3])
-
- warnings.warn(
- 'You are using Python {0}, which will no longer be supported in '
- 'version 0.11.0'.format(python_ver),
- DeprecationWarning)
+__version__ = '0.10.0'
+if sys.version_info[:2] <= (2, 6) or ((3, 0) <= sys.version_info[:2] <= (3, 3)):
+ python_ver = '.'.join(str(x) for x in sys.version_info[:3])
+
+ warnings.warn(
+ 'You are using Python {0}, which will no longer be supported in '
+ 'version 0.11.0'.format(python_ver),
+ DeprecationWarning)
+
+
def compile(expression):
return parser.Parser().parse(expression)
diff --git a/contrib/python/jmespath/jmespath/parser.py b/contrib/python/jmespath/jmespath/parser.py
index eeac38fa89..9a8877c668 100644
--- a/contrib/python/jmespath/jmespath/parser.py
+++ b/contrib/python/jmespath/jmespath/parser.py
@@ -490,7 +490,7 @@ class Parser(object):
def _free_cache_entries(self):
for key in random.sample(self._CACHE.keys(), int(self._MAX_SIZE / 2)):
- self._CACHE.pop(key, None)
+ self._CACHE.pop(key, None)
@classmethod
def purge(cls):
diff --git a/contrib/python/jmespath/jmespath/visitor.py b/contrib/python/jmespath/jmespath/visitor.py
index b3e846b761..91cce0e6a5 100644
--- a/contrib/python/jmespath/jmespath/visitor.py
+++ b/contrib/python/jmespath/jmespath/visitor.py
@@ -29,9 +29,9 @@ def _is_special_integer_case(x, y):
# Also need to consider that:
# >>> 0 in [True, False]
# True
- if type(x) is int and (x == 0 or x == 1):
+ if type(x) is int and (x == 0 or x == 1):
return y is True or y is False
- elif type(y) is int and (y == 0 or y == 1):
+ elif type(y) is int and (y == 0 or y == 1):
return x is True or x is False
@@ -257,7 +257,7 @@ class TreeInterpreter(Visitor):
def visit_not_expression(self, node, value):
original_result = self.visit(node['children'][0], value)
- if type(original_result) is int and original_result == 0:
+ if type(original_result) is int and original_result == 0:
# Special case for 0, !0 should be false, not true.
# 0 is not a special cased integer in jmespath.
return False
diff --git a/contrib/python/jmespath/tests/__init__.py b/contrib/python/jmespath/tests/__init__.py
index d86946ccda..7dd0ef61bd 100644
--- a/contrib/python/jmespath/tests/__init__.py
+++ b/contrib/python/jmespath/tests/__init__.py
@@ -1,40 +1,40 @@
-import sys
-from jmespath import ast
-
-
-# The unittest module got a significant overhaul
-# in 2.7, so if we're in 2.6 we can use the backported
-# version unittest2.
-if sys.version_info[:2] == (2, 6):
- import unittest2 as unittest
- import simplejson as json
- from ordereddict import OrderedDict
-else:
- import unittest
- import json
- from collections import OrderedDict
-
-
-# Helper method used to create an s-expression
-# of the AST to make unit test assertions easier.
-# You get a nice string diff on assert failures.
-def as_s_expression(node):
- parts = []
- _as_s_expression(node, parts)
- return ''.join(parts)
-
-
-def _as_s_expression(node, parts):
- parts.append("(%s" % (node.__class__.__name__.lower()))
- if isinstance(node, ast.Field):
- parts.append(" %s" % node.name)
- elif isinstance(node, ast.FunctionExpression):
- parts.append(" %s" % node.name)
- elif isinstance(node, ast.KeyValPair):
- parts.append(" %s" % node.key_name)
- for child in node.children:
- parts.append(" ")
- _as_s_expression(child, parts)
- parts.append(")")
-
-
+import sys
+from jmespath import ast
+
+
+# The unittest module got a significant overhaul
+# in 2.7, so if we're in 2.6 we can use the backported
+# version unittest2.
+if sys.version_info[:2] == (2, 6):
+ import unittest2 as unittest
+ import simplejson as json
+ from ordereddict import OrderedDict
+else:
+ import unittest
+ import json
+ from collections import OrderedDict
+
+
+# Helper method used to create an s-expression
+# of the AST to make unit test assertions easier.
+# You get a nice string diff on assert failures.
+def as_s_expression(node):
+ parts = []
+ _as_s_expression(node, parts)
+ return ''.join(parts)
+
+
+def _as_s_expression(node, parts):
+ parts.append("(%s" % (node.__class__.__name__.lower()))
+ if isinstance(node, ast.Field):
+ parts.append(" %s" % node.name)
+ elif isinstance(node, ast.FunctionExpression):
+ parts.append(" %s" % node.name)
+ elif isinstance(node, ast.KeyValPair):
+ parts.append(" %s" % node.key_name)
+ for child in node.children:
+ parts.append(" ")
+ _as_s_expression(child, parts)
+ parts.append(")")
+
+
diff --git a/contrib/python/jmespath/tests/test_compliance.py b/contrib/python/jmespath/tests/test_compliance.py
index 86e8297027..9b79a8fde9 100644
--- a/contrib/python/jmespath/tests/test_compliance.py
+++ b/contrib/python/jmespath/tests/test_compliance.py
@@ -1,65 +1,65 @@
-import os
+import os
import pytest
-from pprint import pformat
-from . import OrderedDict
-from . import json
-
-from jmespath.visitor import Options
-
-
-TEST_DIR = os.path.dirname(os.path.abspath(__file__))
-COMPLIANCE_DIR = os.path.join(TEST_DIR, 'compliance')
-LEGACY_DIR = os.path.join(TEST_DIR, 'legacy')
-NOT_SPECIFIED = object()
-OPTIONS = Options(dict_cls=OrderedDict)
-
-
+from pprint import pformat
+from . import OrderedDict
+from . import json
+
+from jmespath.visitor import Options
+
+
+TEST_DIR = os.path.dirname(os.path.abspath(__file__))
+COMPLIANCE_DIR = os.path.join(TEST_DIR, 'compliance')
+LEGACY_DIR = os.path.join(TEST_DIR, 'legacy')
+NOT_SPECIFIED = object()
+OPTIONS = Options(dict_cls=OrderedDict)
+
+
def _load_all_cases():
- for full_path in _walk_files():
- if full_path.endswith('.json'):
- 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':
+ for full_path in _walk_files():
+ if full_path.endswith('.json'):
+ 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 (given, t['expression'], t['result'], os.path.basename(full_path))
- elif test_type == 'error':
+ elif test_type == 'error':
yield (given, t['expression'], t['error'], os.path.basename(full_path))
-
-
-def _walk_files():
- # Check for a shortcut when running the tests interactively.
- # If a JMESPATH_TEST is defined, that file is used as the
- # only test to run. Useful when doing feature development.
- single_file = os.environ.get('JMESPATH_TEST')
- if single_file is not None:
- yield os.path.abspath(single_file)
- else:
- for root, dirnames, filenames in os.walk(TEST_DIR):
- for filename in filenames:
- yield os.path.join(root, filename)
- for root, dirnames, filenames in os.walk(LEGACY_DIR):
- for filename in filenames:
- yield os.path.join(root, filename)
-
-
-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']:
- 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 _walk_files():
+ # Check for a shortcut when running the tests interactively.
+ # If a JMESPATH_TEST is defined, that file is used as the
+ # only test to run. Useful when doing feature development.
+ single_file = os.environ.get('JMESPATH_TEST')
+ if single_file is not None:
+ yield os.path.abspath(single_file)
+ else:
+ for root, dirnames, filenames in os.walk(TEST_DIR):
+ for filename in filenames:
+ yield os.path.join(root, filename)
+ for root, dirnames, filenames in os.walk(LEGACY_DIR):
+ for filename in filenames:
+ yield os.path.join(root, filename)
+
+
+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']:
+ 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)
+
+
@pytest.mark.parametrize(
'given,expression,expected,filename',
list(_load_all_cases())
@@ -68,47 +68,47 @@ def test_compliance(given, expression, expected, filename):
_test_expression(given, expression, expected, filename)
-def _test_expression(given, expression, expected, filename):
- import jmespath.parser
- try:
- parsed = jmespath.compile(expression)
- except ValueError as e:
- raise AssertionError(
- 'jmespath expression failed to compile: "%s", error: %s"' %
- (expression, e))
- actual = parsed.search(given, options=OPTIONS)
- expected_repr = json.dumps(expected, indent=4)
- actual_repr = json.dumps(actual, indent=4)
- error_msg = ("\n\n (%s) The expression '%s' was suppose to give:\n%s\n"
- "Instead it matched:\n%s\nparsed as:\n%s\ngiven:\n%s" % (
- filename, expression, expected_repr,
- actual_repr, pformat(parsed.parsed),
- json.dumps(given, indent=4)))
- error_msg = error_msg.replace(r'\n', '\n')
- assert actua == expected, error_msg
-
-
-def _test_error_expression(given, expression, error, filename):
- import jmespath.parser
- if error not in ('syntax', 'invalid-type',
- 'unknown-function', 'invalid-arity', 'invalid-value'):
- raise RuntimeError("Unknown error type '%s'" % error)
- try:
- parsed = jmespath.compile(expression)
- parsed.search(given)
- 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" % (
- filename, expression, pformat(parsed.parsed)))
- error_msg = error_msg.replace(r'\n', '\n')
- raise AssertionError(error_msg)
+def _test_expression(given, expression, expected, filename):
+ import jmespath.parser
+ try:
+ parsed = jmespath.compile(expression)
+ except ValueError as e:
+ raise AssertionError(
+ 'jmespath expression failed to compile: "%s", error: %s"' %
+ (expression, e))
+ actual = parsed.search(given, options=OPTIONS)
+ expected_repr = json.dumps(expected, indent=4)
+ actual_repr = json.dumps(actual, indent=4)
+ error_msg = ("\n\n (%s) The expression '%s' was suppose to give:\n%s\n"
+ "Instead it matched:\n%s\nparsed as:\n%s\ngiven:\n%s" % (
+ filename, expression, expected_repr,
+ actual_repr, pformat(parsed.parsed),
+ json.dumps(given, indent=4)))
+ error_msg = error_msg.replace(r'\n', '\n')
+ assert actua == expected, error_msg
+
+
+def _test_error_expression(given, expression, error, filename):
+ import jmespath.parser
+ if error not in ('syntax', 'invalid-type',
+ 'unknown-function', 'invalid-arity', 'invalid-value'):
+ raise RuntimeError("Unknown error type '%s'" % error)
+ try:
+ parsed = jmespath.compile(expression)
+ parsed.search(given)
+ 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" % (
+ filename, expression, pformat(parsed.parsed)))
+ error_msg = error_msg.replace(r'\n', '\n')
+ raise AssertionError(error_msg)
diff --git a/contrib/python/jmespath/tests/test_parser.py b/contrib/python/jmespath/tests/test_parser.py
index 121b4b79b2..6abe0f42c3 100644
--- a/contrib/python/jmespath/tests/test_parser.py
+++ b/contrib/python/jmespath/tests/test_parser.py
@@ -1,368 +1,368 @@
-#!/usr/bin/env python
-
-import re
-from . import unittest, OrderedDict
-
-from jmespath import parser
-from jmespath import visitor
-from jmespath import ast
-from jmespath import exceptions
-
-
-class TestParser(unittest.TestCase):
- def setUp(self):
- self.parser = parser.Parser()
-
- def assert_parsed_ast(self, expression, expected_ast):
- parsed = self.parser.parse(expression)
- self.assertEqual(parsed.parsed, expected_ast)
-
- def test_parse_empty_string_raises_exception(self):
- with self.assertRaises(exceptions.EmptyExpressionError):
- self.parser.parse('')
-
- def test_field(self):
- self.assert_parsed_ast('foo', ast.field('foo'))
-
- def test_dot_syntax(self):
- self.assert_parsed_ast('foo.bar',
- ast.subexpression([ast.field('foo'),
- ast.field('bar')]))
-
- def test_multiple_dots(self):
- parsed = self.parser.parse('foo.bar.baz')
- self.assertEqual(
- parsed.search({'foo': {'bar': {'baz': 'correct'}}}), 'correct')
-
- def test_index(self):
- parsed = self.parser.parse('foo[1]')
- self.assertEqual(
- parsed.search({'foo': ['zero', 'one', 'two']}),
- 'one')
-
- def test_quoted_subexpression(self):
- self.assert_parsed_ast('"foo"."bar"',
- ast.subexpression([
- ast.field('foo'),
- ast.field('bar')]))
-
- def test_wildcard(self):
- parsed = self.parser.parse('foo[*]')
- self.assertEqual(
- parsed.search({'foo': ['zero', 'one', 'two']}),
- ['zero', 'one', 'two'])
-
- def test_wildcard_with_children(self):
- parsed = self.parser.parse('foo[*].bar')
- self.assertEqual(
- parsed.search({'foo': [{'bar': 'one'}, {'bar': 'two'}]}),
- ['one', 'two'])
-
- def test_or_expression(self):
- parsed = self.parser.parse('foo || bar')
- self.assertEqual(parsed.search({'foo': 'foo'}), 'foo')
- self.assertEqual(parsed.search({'bar': 'bar'}), 'bar')
- self.assertEqual(parsed.search({'foo': 'foo', 'bar': 'bar'}), 'foo')
- self.assertEqual(parsed.search({'bad': 'bad'}), None)
-
- def test_complex_or_expression(self):
- parsed = self.parser.parse('foo.foo || foo.bar')
- self.assertEqual(parsed.search({'foo': {'foo': 'foo'}}), 'foo')
- self.assertEqual(parsed.search({'foo': {'bar': 'bar'}}), 'bar')
- self.assertEqual(parsed.search({'foo': {'baz': 'baz'}}), None)
-
- def test_or_repr(self):
- self.assert_parsed_ast('foo || bar', ast.or_expression(ast.field('foo'),
- ast.field('bar')))
-
- def test_unicode_literals_escaped(self):
- self.assert_parsed_ast(r'`"\u2713"`', ast.literal(u'\u2713'))
-
- def test_multiselect(self):
- parsed = self.parser.parse('foo.{bar: bar,baz: baz}')
- self.assertEqual(
- parsed.search({'foo': {'bar': 'bar', 'baz': 'baz', 'qux': 'qux'}}),
- {'bar': 'bar', 'baz': 'baz'})
-
- def test_multiselect_subexpressions(self):
- parsed = self.parser.parse('foo.{"bar.baz": bar.baz, qux: qux}')
- self.assertEqual(
- parsed.search({'foo': {'bar': {'baz': 'CORRECT'}, 'qux': 'qux'}}),
- {'bar.baz': 'CORRECT', 'qux': 'qux'})
-
- def test_multiselect_with_all_quoted_keys(self):
- parsed = self.parser.parse('foo.{"bar": bar.baz, "qux": qux}')
- result = parsed.search({'foo': {'bar': {'baz': 'CORRECT'}, 'qux': 'qux'}})
- self.assertEqual(result, {"bar": "CORRECT", "qux": "qux"})
-
- def test_function_call_with_and_statement(self):
- self.assert_parsed_ast(
- 'f(@ && @)',
- {'children': [{'children': [{'children': [], 'type': 'current'},
- {'children': [], 'type': 'current'}],
- 'type': 'and_expression'}],
- 'type': 'function_expression',
- 'value': 'f'})
-
-
-class TestErrorMessages(unittest.TestCase):
-
- def setUp(self):
- self.parser = parser.Parser()
-
- def assert_error_message(self, expression, error_message,
- exception=exceptions.ParseError):
- try:
- self.parser.parse(expression)
- except exception as e:
- self.assertEqual(error_message, str(e))
- return
- except Exception as e:
- self.fail(
- "Unexpected error raised (%s: %s) for bad expression: %s" %
- (e.__class__.__name__, e, expression))
- else:
- self.fail(
- "ParseError not raised for bad expression: %s" % expression)
-
- def test_bad_parse(self):
- with self.assertRaises(exceptions.ParseError):
- self.parser.parse('foo]baz')
-
- def test_bad_parse_error_message(self):
- error_message = (
- 'Unexpected token: ]: Parse error at column 3, '
- 'token "]" (RBRACKET), for expression:\n'
- '"foo]baz"\n'
- ' ^')
- self.assert_error_message('foo]baz', error_message)
-
- def test_bad_parse_error_message_with_multiselect(self):
- error_message = (
- 'Invalid jmespath expression: Incomplete expression:\n'
- '"foo.{bar: baz,bar: bar"\n'
- ' ^')
- self.assert_error_message('foo.{bar: baz,bar: bar', error_message)
-
- def test_incomplete_expression_with_missing_paren(self):
- error_message = (
- 'Invalid jmespath expression: Incomplete expression:\n'
- '"length(@,"\n'
- ' ^')
- self.assert_error_message('length(@,', error_message)
-
- def test_bad_lexer_values(self):
- error_message = (
- 'Bad jmespath expression: '
- 'Unclosed " delimiter:\n'
- 'foo."bar\n'
- ' ^')
- self.assert_error_message('foo."bar', error_message,
- exception=exceptions.LexerError)
-
- def test_bad_unicode_string(self):
- # This error message is straight from the JSON parser
- # and pypy has a slightly different error message,
- # so we're not using assert_error_message.
- error_message = re.compile(
- r'Bad jmespath expression: '
- r'Invalid \\uXXXX escape.*\\uAZ12', re.DOTALL)
- with self.assertRaisesRegexp(exceptions.LexerError, error_message):
- self.parser.parse(r'"\uAZ12"')
-
-
-class TestParserWildcards(unittest.TestCase):
- def setUp(self):
- self.parser = parser.Parser()
- self.data = {
- 'foo': [
- {'bar': [{'baz': 'one'}, {'baz': 'two'}]},
- {'bar': [{'baz': 'three'}, {'baz': 'four'}, {'baz': 'five'}]},
- ]
- }
-
- def test_multiple_index_wildcards(self):
- parsed = self.parser.parse('foo[*].bar[*].baz')
- self.assertEqual(parsed.search(self.data),
- [['one', 'two'], ['three', 'four', 'five']])
-
- def test_wildcard_mix_with_indices(self):
- parsed = self.parser.parse('foo[*].bar[0].baz')
- self.assertEqual(parsed.search(self.data),
- ['one', 'three'])
-
- def test_wildcard_mix_last(self):
- parsed = self.parser.parse('foo[0].bar[*].baz')
- self.assertEqual(parsed.search(self.data),
- ['one', 'two'])
-
- def test_indices_out_of_bounds(self):
- parsed = self.parser.parse('foo[*].bar[2].baz')
- self.assertEqual(parsed.search(self.data),
- ['five'])
-
- def test_root_indices(self):
- parsed = self.parser.parse('[0]')
- self.assertEqual(parsed.search(['one', 'two']), 'one')
-
- def test_root_wildcard(self):
- parsed = self.parser.parse('*.foo')
- data = {'top1': {'foo': 'bar'}, 'top2': {'foo': 'baz'},
- 'top3': {'notfoo': 'notfoo'}}
- # Sorted is being used because the order of the keys are not
- # required to be in any specific order.
- self.assertEqual(sorted(parsed.search(data)), sorted(['bar', 'baz']))
- self.assertEqual(sorted(self.parser.parse('*.notfoo').search(data)),
- sorted(['notfoo']))
-
- def test_only_wildcard(self):
- parsed = self.parser.parse('*')
- data = {'foo': 'a', 'bar': 'b', 'baz': 'c'}
- self.assertEqual(sorted(parsed.search(data)), sorted(['a', 'b', 'c']))
-
- def test_escape_sequences(self):
- self.assertEqual(self.parser.parse(r'"foo\tbar"').search(
- {'foo\tbar': 'baz'}), 'baz')
- self.assertEqual(self.parser.parse(r'"foo\nbar"').search(
- {'foo\nbar': 'baz'}), 'baz')
- self.assertEqual(self.parser.parse(r'"foo\bbar"').search(
- {'foo\bbar': 'baz'}), 'baz')
- self.assertEqual(self.parser.parse(r'"foo\fbar"').search(
- {'foo\fbar': 'baz'}), 'baz')
- self.assertEqual(self.parser.parse(r'"foo\rbar"').search(
- {'foo\rbar': 'baz'}), 'baz')
-
- def test_consecutive_escape_sequences(self):
- parsed = self.parser.parse(r'"foo\\nbar"')
- self.assertEqual(parsed.search({'foo\\nbar': 'baz'}), 'baz')
-
- parsed = self.parser.parse(r'"foo\n\t\rbar"')
- self.assertEqual(parsed.search({'foo\n\t\rbar': 'baz'}), 'baz')
-
- def test_escape_sequence_at_end_of_string_not_allowed(self):
- with self.assertRaises(ValueError):
- self.parser.parse('foobar\\')
-
- def test_wildcard_with_multiselect(self):
- parsed = self.parser.parse('foo.*.{a: a, b: b}')
- data = {
- 'foo': {
- 'one': {
- 'a': {'c': 'CORRECT', 'd': 'other'},
- 'b': {'c': 'ALSOCORRECT', 'd': 'other'},
- },
- 'two': {
- 'a': {'c': 'CORRECT', 'd': 'other'},
- 'c': {'c': 'WRONG', 'd': 'other'},
- },
- }
- }
- match = parsed.search(data)
- self.assertEqual(len(match), 2)
- self.assertIn('a', match[0])
- self.assertIn('b', match[0])
- self.assertIn('a', match[1])
- self.assertIn('b', match[1])
-
-
-class TestMergedLists(unittest.TestCase):
- def setUp(self):
- self.parser = parser.Parser()
- self.data = {
- "foo": [
- [["one", "two"], ["three", "four"]],
- [["five", "six"], ["seven", "eight"]],
- [["nine"], ["ten"]]
- ]
- }
-
- def test_merge_with_indices(self):
- parsed = self.parser.parse('foo[][0]')
- match = parsed.search(self.data)
- self.assertEqual(match, ["one", "three", "five", "seven",
- "nine", "ten"])
-
- def test_trailing_merged_operator(self):
- parsed = self.parser.parse('foo[]')
- match = parsed.search(self.data)
- self.assertEqual(
- match,
- [["one", "two"], ["three", "four"],
- ["five", "six"], ["seven", "eight"],
- ["nine"], ["ten"]])
-
-
-class TestParserCaching(unittest.TestCase):
- def test_compile_lots_of_expressions(self):
- # We have to be careful here because this is an implementation detail
- # that should be abstracted from the user, but we need to make sure we
- # exercise the code and that it doesn't blow up.
- p = parser.Parser()
- compiled = []
- compiled2 = []
- for i in range(parser.Parser._MAX_SIZE + 1):
- compiled.append(p.parse('foo%s' % i))
- # Rerun the test and half of these entries should be from the
- # cache but they should still be equal to compiled.
- for i in range(parser.Parser._MAX_SIZE + 1):
- compiled2.append(p.parse('foo%s' % i))
- self.assertEqual(len(compiled), len(compiled2))
- self.assertEqual(
- [expr.parsed for expr in compiled],
- [expr.parsed for expr in compiled2])
-
- def test_cache_purge(self):
- p = parser.Parser()
- first = p.parse('foo')
- cached = p.parse('foo')
- p.purge()
- second = p.parse('foo')
- self.assertEqual(first.parsed,
- second.parsed)
- self.assertEqual(first.parsed,
- cached.parsed)
-
-
-class TestParserAddsExpressionAttribute(unittest.TestCase):
- def test_expression_available_from_parser(self):
- p = parser.Parser()
- parsed = p.parse('foo.bar')
- self.assertEqual(parsed.expression, 'foo.bar')
-
-
-class TestParsedResultAddsOptions(unittest.TestCase):
- def test_can_have_ordered_dict(self):
- p = parser.Parser()
- parsed = p.parse('{a: a, b: b, c: c}')
- options = visitor.Options(dict_cls=OrderedDict)
- result = parsed.search(
- {"c": "c", "b": "b", "a": "a"}, options=options)
- # The order should be 'a', 'b' because we're using an
- # OrderedDict
- self.assertEqual(list(result), ['a', 'b', 'c'])
-
-
-class TestRenderGraphvizFile(unittest.TestCase):
- def test_dot_file_rendered(self):
- p = parser.Parser()
- result = p.parse('foo')
- dot_contents = result._render_dot_file()
- self.assertEqual(dot_contents,
- 'digraph AST {\nfield1 [label="field(foo)"]\n}')
-
- def test_dot_file_subexpr(self):
- p = parser.Parser()
- result = p.parse('foo.bar')
- dot_contents = result._render_dot_file()
- self.assertEqual(
- dot_contents,
- 'digraph AST {\n'
- 'subexpression1 [label="subexpression()"]\n'
- ' subexpression1 -> field2\n'
- 'field2 [label="field(foo)"]\n'
- ' subexpression1 -> field3\n'
- 'field3 [label="field(bar)"]\n}')
-
-
-if __name__ == '__main__':
- unittest.main()
+#!/usr/bin/env python
+
+import re
+from . import unittest, OrderedDict
+
+from jmespath import parser
+from jmespath import visitor
+from jmespath import ast
+from jmespath import exceptions
+
+
+class TestParser(unittest.TestCase):
+ def setUp(self):
+ self.parser = parser.Parser()
+
+ def assert_parsed_ast(self, expression, expected_ast):
+ parsed = self.parser.parse(expression)
+ self.assertEqual(parsed.parsed, expected_ast)
+
+ def test_parse_empty_string_raises_exception(self):
+ with self.assertRaises(exceptions.EmptyExpressionError):
+ self.parser.parse('')
+
+ def test_field(self):
+ self.assert_parsed_ast('foo', ast.field('foo'))
+
+ def test_dot_syntax(self):
+ self.assert_parsed_ast('foo.bar',
+ ast.subexpression([ast.field('foo'),
+ ast.field('bar')]))
+
+ def test_multiple_dots(self):
+ parsed = self.parser.parse('foo.bar.baz')
+ self.assertEqual(
+ parsed.search({'foo': {'bar': {'baz': 'correct'}}}), 'correct')
+
+ def test_index(self):
+ parsed = self.parser.parse('foo[1]')
+ self.assertEqual(
+ parsed.search({'foo': ['zero', 'one', 'two']}),
+ 'one')
+
+ def test_quoted_subexpression(self):
+ self.assert_parsed_ast('"foo"."bar"',
+ ast.subexpression([
+ ast.field('foo'),
+ ast.field('bar')]))
+
+ def test_wildcard(self):
+ parsed = self.parser.parse('foo[*]')
+ self.assertEqual(
+ parsed.search({'foo': ['zero', 'one', 'two']}),
+ ['zero', 'one', 'two'])
+
+ def test_wildcard_with_children(self):
+ parsed = self.parser.parse('foo[*].bar')
+ self.assertEqual(
+ parsed.search({'foo': [{'bar': 'one'}, {'bar': 'two'}]}),
+ ['one', 'two'])
+
+ def test_or_expression(self):
+ parsed = self.parser.parse('foo || bar')
+ self.assertEqual(parsed.search({'foo': 'foo'}), 'foo')
+ self.assertEqual(parsed.search({'bar': 'bar'}), 'bar')
+ self.assertEqual(parsed.search({'foo': 'foo', 'bar': 'bar'}), 'foo')
+ self.assertEqual(parsed.search({'bad': 'bad'}), None)
+
+ def test_complex_or_expression(self):
+ parsed = self.parser.parse('foo.foo || foo.bar')
+ self.assertEqual(parsed.search({'foo': {'foo': 'foo'}}), 'foo')
+ self.assertEqual(parsed.search({'foo': {'bar': 'bar'}}), 'bar')
+ self.assertEqual(parsed.search({'foo': {'baz': 'baz'}}), None)
+
+ def test_or_repr(self):
+ self.assert_parsed_ast('foo || bar', ast.or_expression(ast.field('foo'),
+ ast.field('bar')))
+
+ def test_unicode_literals_escaped(self):
+ self.assert_parsed_ast(r'`"\u2713"`', ast.literal(u'\u2713'))
+
+ def test_multiselect(self):
+ parsed = self.parser.parse('foo.{bar: bar,baz: baz}')
+ self.assertEqual(
+ parsed.search({'foo': {'bar': 'bar', 'baz': 'baz', 'qux': 'qux'}}),
+ {'bar': 'bar', 'baz': 'baz'})
+
+ def test_multiselect_subexpressions(self):
+ parsed = self.parser.parse('foo.{"bar.baz": bar.baz, qux: qux}')
+ self.assertEqual(
+ parsed.search({'foo': {'bar': {'baz': 'CORRECT'}, 'qux': 'qux'}}),
+ {'bar.baz': 'CORRECT', 'qux': 'qux'})
+
+ def test_multiselect_with_all_quoted_keys(self):
+ parsed = self.parser.parse('foo.{"bar": bar.baz, "qux": qux}')
+ result = parsed.search({'foo': {'bar': {'baz': 'CORRECT'}, 'qux': 'qux'}})
+ self.assertEqual(result, {"bar": "CORRECT", "qux": "qux"})
+
+ def test_function_call_with_and_statement(self):
+ self.assert_parsed_ast(
+ 'f(@ && @)',
+ {'children': [{'children': [{'children': [], 'type': 'current'},
+ {'children': [], 'type': 'current'}],
+ 'type': 'and_expression'}],
+ 'type': 'function_expression',
+ 'value': 'f'})
+
+
+class TestErrorMessages(unittest.TestCase):
+
+ def setUp(self):
+ self.parser = parser.Parser()
+
+ def assert_error_message(self, expression, error_message,
+ exception=exceptions.ParseError):
+ try:
+ self.parser.parse(expression)
+ except exception as e:
+ self.assertEqual(error_message, str(e))
+ return
+ except Exception as e:
+ self.fail(
+ "Unexpected error raised (%s: %s) for bad expression: %s" %
+ (e.__class__.__name__, e, expression))
+ else:
+ self.fail(
+ "ParseError not raised for bad expression: %s" % expression)
+
+ def test_bad_parse(self):
+ with self.assertRaises(exceptions.ParseError):
+ self.parser.parse('foo]baz')
+
+ def test_bad_parse_error_message(self):
+ error_message = (
+ 'Unexpected token: ]: Parse error at column 3, '
+ 'token "]" (RBRACKET), for expression:\n'
+ '"foo]baz"\n'
+ ' ^')
+ self.assert_error_message('foo]baz', error_message)
+
+ def test_bad_parse_error_message_with_multiselect(self):
+ error_message = (
+ 'Invalid jmespath expression: Incomplete expression:\n'
+ '"foo.{bar: baz,bar: bar"\n'
+ ' ^')
+ self.assert_error_message('foo.{bar: baz,bar: bar', error_message)
+
+ def test_incomplete_expression_with_missing_paren(self):
+ error_message = (
+ 'Invalid jmespath expression: Incomplete expression:\n'
+ '"length(@,"\n'
+ ' ^')
+ self.assert_error_message('length(@,', error_message)
+
+ def test_bad_lexer_values(self):
+ error_message = (
+ 'Bad jmespath expression: '
+ 'Unclosed " delimiter:\n'
+ 'foo."bar\n'
+ ' ^')
+ self.assert_error_message('foo."bar', error_message,
+ exception=exceptions.LexerError)
+
+ def test_bad_unicode_string(self):
+ # This error message is straight from the JSON parser
+ # and pypy has a slightly different error message,
+ # so we're not using assert_error_message.
+ error_message = re.compile(
+ r'Bad jmespath expression: '
+ r'Invalid \\uXXXX escape.*\\uAZ12', re.DOTALL)
+ with self.assertRaisesRegexp(exceptions.LexerError, error_message):
+ self.parser.parse(r'"\uAZ12"')
+
+
+class TestParserWildcards(unittest.TestCase):
+ def setUp(self):
+ self.parser = parser.Parser()
+ self.data = {
+ 'foo': [
+ {'bar': [{'baz': 'one'}, {'baz': 'two'}]},
+ {'bar': [{'baz': 'three'}, {'baz': 'four'}, {'baz': 'five'}]},
+ ]
+ }
+
+ def test_multiple_index_wildcards(self):
+ parsed = self.parser.parse('foo[*].bar[*].baz')
+ self.assertEqual(parsed.search(self.data),
+ [['one', 'two'], ['three', 'four', 'five']])
+
+ def test_wildcard_mix_with_indices(self):
+ parsed = self.parser.parse('foo[*].bar[0].baz')
+ self.assertEqual(parsed.search(self.data),
+ ['one', 'three'])
+
+ def test_wildcard_mix_last(self):
+ parsed = self.parser.parse('foo[0].bar[*].baz')
+ self.assertEqual(parsed.search(self.data),
+ ['one', 'two'])
+
+ def test_indices_out_of_bounds(self):
+ parsed = self.parser.parse('foo[*].bar[2].baz')
+ self.assertEqual(parsed.search(self.data),
+ ['five'])
+
+ def test_root_indices(self):
+ parsed = self.parser.parse('[0]')
+ self.assertEqual(parsed.search(['one', 'two']), 'one')
+
+ def test_root_wildcard(self):
+ parsed = self.parser.parse('*.foo')
+ data = {'top1': {'foo': 'bar'}, 'top2': {'foo': 'baz'},
+ 'top3': {'notfoo': 'notfoo'}}
+ # Sorted is being used because the order of the keys are not
+ # required to be in any specific order.
+ self.assertEqual(sorted(parsed.search(data)), sorted(['bar', 'baz']))
+ self.assertEqual(sorted(self.parser.parse('*.notfoo').search(data)),
+ sorted(['notfoo']))
+
+ def test_only_wildcard(self):
+ parsed = self.parser.parse('*')
+ data = {'foo': 'a', 'bar': 'b', 'baz': 'c'}
+ self.assertEqual(sorted(parsed.search(data)), sorted(['a', 'b', 'c']))
+
+ def test_escape_sequences(self):
+ self.assertEqual(self.parser.parse(r'"foo\tbar"').search(
+ {'foo\tbar': 'baz'}), 'baz')
+ self.assertEqual(self.parser.parse(r'"foo\nbar"').search(
+ {'foo\nbar': 'baz'}), 'baz')
+ self.assertEqual(self.parser.parse(r'"foo\bbar"').search(
+ {'foo\bbar': 'baz'}), 'baz')
+ self.assertEqual(self.parser.parse(r'"foo\fbar"').search(
+ {'foo\fbar': 'baz'}), 'baz')
+ self.assertEqual(self.parser.parse(r'"foo\rbar"').search(
+ {'foo\rbar': 'baz'}), 'baz')
+
+ def test_consecutive_escape_sequences(self):
+ parsed = self.parser.parse(r'"foo\\nbar"')
+ self.assertEqual(parsed.search({'foo\\nbar': 'baz'}), 'baz')
+
+ parsed = self.parser.parse(r'"foo\n\t\rbar"')
+ self.assertEqual(parsed.search({'foo\n\t\rbar': 'baz'}), 'baz')
+
+ def test_escape_sequence_at_end_of_string_not_allowed(self):
+ with self.assertRaises(ValueError):
+ self.parser.parse('foobar\\')
+
+ def test_wildcard_with_multiselect(self):
+ parsed = self.parser.parse('foo.*.{a: a, b: b}')
+ data = {
+ 'foo': {
+ 'one': {
+ 'a': {'c': 'CORRECT', 'd': 'other'},
+ 'b': {'c': 'ALSOCORRECT', 'd': 'other'},
+ },
+ 'two': {
+ 'a': {'c': 'CORRECT', 'd': 'other'},
+ 'c': {'c': 'WRONG', 'd': 'other'},
+ },
+ }
+ }
+ match = parsed.search(data)
+ self.assertEqual(len(match), 2)
+ self.assertIn('a', match[0])
+ self.assertIn('b', match[0])
+ self.assertIn('a', match[1])
+ self.assertIn('b', match[1])
+
+
+class TestMergedLists(unittest.TestCase):
+ def setUp(self):
+ self.parser = parser.Parser()
+ self.data = {
+ "foo": [
+ [["one", "two"], ["three", "four"]],
+ [["five", "six"], ["seven", "eight"]],
+ [["nine"], ["ten"]]
+ ]
+ }
+
+ def test_merge_with_indices(self):
+ parsed = self.parser.parse('foo[][0]')
+ match = parsed.search(self.data)
+ self.assertEqual(match, ["one", "three", "five", "seven",
+ "nine", "ten"])
+
+ def test_trailing_merged_operator(self):
+ parsed = self.parser.parse('foo[]')
+ match = parsed.search(self.data)
+ self.assertEqual(
+ match,
+ [["one", "two"], ["three", "four"],
+ ["five", "six"], ["seven", "eight"],
+ ["nine"], ["ten"]])
+
+
+class TestParserCaching(unittest.TestCase):
+ def test_compile_lots_of_expressions(self):
+ # We have to be careful here because this is an implementation detail
+ # that should be abstracted from the user, but we need to make sure we
+ # exercise the code and that it doesn't blow up.
+ p = parser.Parser()
+ compiled = []
+ compiled2 = []
+ for i in range(parser.Parser._MAX_SIZE + 1):
+ compiled.append(p.parse('foo%s' % i))
+ # Rerun the test and half of these entries should be from the
+ # cache but they should still be equal to compiled.
+ for i in range(parser.Parser._MAX_SIZE + 1):
+ compiled2.append(p.parse('foo%s' % i))
+ self.assertEqual(len(compiled), len(compiled2))
+ self.assertEqual(
+ [expr.parsed for expr in compiled],
+ [expr.parsed for expr in compiled2])
+
+ def test_cache_purge(self):
+ p = parser.Parser()
+ first = p.parse('foo')
+ cached = p.parse('foo')
+ p.purge()
+ second = p.parse('foo')
+ self.assertEqual(first.parsed,
+ second.parsed)
+ self.assertEqual(first.parsed,
+ cached.parsed)
+
+
+class TestParserAddsExpressionAttribute(unittest.TestCase):
+ def test_expression_available_from_parser(self):
+ p = parser.Parser()
+ parsed = p.parse('foo.bar')
+ self.assertEqual(parsed.expression, 'foo.bar')
+
+
+class TestParsedResultAddsOptions(unittest.TestCase):
+ def test_can_have_ordered_dict(self):
+ p = parser.Parser()
+ parsed = p.parse('{a: a, b: b, c: c}')
+ options = visitor.Options(dict_cls=OrderedDict)
+ result = parsed.search(
+ {"c": "c", "b": "b", "a": "a"}, options=options)
+ # The order should be 'a', 'b' because we're using an
+ # OrderedDict
+ self.assertEqual(list(result), ['a', 'b', 'c'])
+
+
+class TestRenderGraphvizFile(unittest.TestCase):
+ def test_dot_file_rendered(self):
+ p = parser.Parser()
+ result = p.parse('foo')
+ dot_contents = result._render_dot_file()
+ self.assertEqual(dot_contents,
+ 'digraph AST {\nfield1 [label="field(foo)"]\n}')
+
+ def test_dot_file_subexpr(self):
+ p = parser.Parser()
+ result = p.parse('foo.bar')
+ dot_contents = result._render_dot_file()
+ self.assertEqual(
+ dot_contents,
+ 'digraph AST {\n'
+ 'subexpression1 [label="subexpression()"]\n'
+ ' subexpression1 -> field2\n'
+ 'field2 [label="field(foo)"]\n'
+ ' subexpression1 -> field3\n'
+ 'field3 [label="field(bar)"]\n}')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/contrib/python/jmespath/tests/ya.make b/contrib/python/jmespath/tests/ya.make
index c865997f16..46ad0e1bd9 100644
--- a/contrib/python/jmespath/tests/ya.make
+++ b/contrib/python/jmespath/tests/ya.make
@@ -1,17 +1,17 @@
-PY23_TEST()
-
-OWNER(g:python-contrib)
-
-PEERDIR(
- contrib/python/jmespath
-)
-
-TEST_SRCS(
- __init__.py
- test_compliance.py
- test_parser.py
-)
-
-NO_LINT()
-
-END()
+PY23_TEST()
+
+OWNER(g:python-contrib)
+
+PEERDIR(
+ contrib/python/jmespath
+)
+
+TEST_SRCS(
+ __init__.py
+ test_compliance.py
+ test_parser.py
+)
+
+NO_LINT()
+
+END()
diff --git a/contrib/python/jmespath/ya.make b/contrib/python/jmespath/ya.make
index 1ffbd236bc..9943d79f1c 100644
--- a/contrib/python/jmespath/ya.make
+++ b/contrib/python/jmespath/ya.make
@@ -1,33 +1,33 @@
PY23_LIBRARY()
-OWNER(g:python-contrib)
+OWNER(g:python-contrib)
-VERSION(0.10.0)
-
-LICENSE(MIT)
-
-NO_LINT()
+VERSION(0.10.0)
+LICENSE(MIT)
+
+NO_LINT()
+
PY_SRCS(
TOP_LEVEL
- jmespath/__init__.py
- jmespath/ast.py
- jmespath/compat.py
+ jmespath/__init__.py
+ jmespath/ast.py
+ jmespath/compat.py
jmespath/exceptions.py
jmespath/functions.py
jmespath/lexer.py
jmespath/parser.py
- jmespath/visitor.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/jmespath/
- .dist-info/METADATA
- .dist-info/top_level.txt
+ jmespath/visitor.py
)
+RESOURCE_FILES(
+ PREFIX contrib/python/jmespath/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
END()
-
-RECURSE_FOR_TESTS(
- tests
-)
+
+RECURSE_FOR_TESTS(
+ tests
+)