Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: Ibc42804275a4757d5bbdbdba20c914778105aa4c
This commit is contained in:
parent
942761ac49
commit
631f779045
@ -1,7 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = jsonpath_rw_ext
|
||||
omit = jsonpath_rw_ext/openstack/*
|
||||
|
||||
[report]
|
||||
ignore-errors = True
|
54
.gitignore
vendored
54
.gitignore
vendored
@ -1,54 +0,0 @@
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
.eggs
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
.venv
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
.*sw?
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=sileht/python-jsonpath-rw-ext.git
|
3
.mailmap
3
.mailmap
@ -1,3 +0,0 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
12
.travis.yml
12
.travis.yml
@ -1,12 +0,0 @@
|
||||
sudo: false
|
||||
language: python
|
||||
install:
|
||||
- pip install tox
|
||||
script:
|
||||
- tox
|
||||
env:
|
||||
- TOXENV=py27
|
||||
- TOXENV=py33
|
||||
- TOXENV=py34
|
||||
- TOXENV=pep8
|
||||
- TOXENV=docs
|
@ -1 +0,0 @@
|
||||
If you would like to contribute to the development, just send github pull requests.
|
@ -1,4 +0,0 @@
|
||||
jsonpath-rw-ext Style Commandments
|
||||
===============================================
|
||||
|
||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
176
LICENSE
176
LICENSE
@ -1,176 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
@ -1,6 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
14
README
Normal file
14
README
Normal file
@ -0,0 +1,14 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
120
README.rst
120
README.rst
@ -1,120 +0,0 @@
|
||||
===============================
|
||||
python-jsonpath-rw-ext
|
||||
===============================
|
||||
|
||||
.. image:: https://travis-ci.org/sileht/python-jsonpath-rw-ext.png?branch=master
|
||||
:target: https://travis-ci.org/sileht/python-jsonpath-rw-ext
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/jsonpath-rw-ext.svg
|
||||
:target: https://pypi.python.org/pypi/jsonpath-rw-ext/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/jsonpath-rw-ext.svg
|
||||
:target: https://pypi.python.org/pypi/jsonpath-rw-ext/
|
||||
:alt: Downloads
|
||||
|
||||
Extensions for JSONPath RW
|
||||
|
||||
jsonpath-rw-ext extends json-path-rw capabilities by adding multiple extensions.
|
||||
'len' that allows one to get the length of a list. 'sorted' that returns a sorted version
|
||||
of a list, 'arithmetic' that permits one to make math operation between elements and
|
||||
'filter' to select only certain elements of a list.
|
||||
|
||||
Each extensions will be proposed `upstream <https://github.com/kennknowles/python-jsonpath-rw>`__
|
||||
and will stay here only if they are refused.
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: https://python-jsonpath-rw-ext.readthedocs.org/en/latest/
|
||||
* Source: http://github.com/sileht/python-jsonpath-rw-ext
|
||||
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
At the command line::
|
||||
|
||||
$ pip install jsonpath-rw-ext
|
||||
|
||||
Or, if you have virtualenvwrapper installed::
|
||||
|
||||
$ mkvirtualenv jsonpath-rw-ext
|
||||
$ pip install jsonpath-rw-ext
|
||||
|
||||
|
||||
To replace the jsonpath_rw parser by this one with::
|
||||
|
||||
import jsonpath_rw_ext
|
||||
jsonpath_rw_ext.parse("$.foo").find(...)
|
||||
|
||||
Or::
|
||||
|
||||
from jsonpath_rw_ext import parser
|
||||
parser.ExtentedJsonPathParser().parse("$.foo").find(...)
|
||||
|
||||
|
||||
The jsonpath classes are not part of the public API, because the name/structure
|
||||
can change when they will be implemented upstream. Only the syntax *shouldn't*
|
||||
change.
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
+--------------+----------------------------------------------+
|
||||
| name | Example |
|
||||
+==============+==============================================+
|
||||
| len | - $.objects.`len` |
|
||||
+--------------+----------------------------------------------+
|
||||
| sub | - $.field.`sub(/foo\\\\+(.*)/, \\\\1)` |
|
||||
+--------------+----------------------------------------------+
|
||||
| split | - $.field.`split(+, 2, -1)` |
|
||||
| | - $.field.`split(sep, segement, maxsplit)` |
|
||||
+--------------+----------------------------------------------+
|
||||
| sorted | - $.objects.`sorted` |
|
||||
| | - $.objects[\\some_field] |
|
||||
| | - $.objects[\\some_field,/other_field] |
|
||||
+--------------+----------------------------------------------+
|
||||
| filter | - $.objects[?(@some_field > 5)] |
|
||||
| | - $.objects[?some_field = "foobar")] |
|
||||
| | - $.objects[?some_field > 5 & other < 2)] |
|
||||
+--------------+----------------------------------------------+
|
||||
| arithmetic | - $.foo + "_" + $.bar |
|
||||
| (-+*/) | - $.foo * 12 |
|
||||
| | - $.objects[*].cow + $.objects[*].cat |
|
||||
+--------------+----------------------------------------------+
|
||||
|
||||
About arithmetic and string
|
||||
---------------------------
|
||||
|
||||
Operations are done with python operators and allows types that python
|
||||
allows, and return [] if the operation can be done due to incompatible types.
|
||||
|
||||
When operators are used, a jsonpath must be be fully defined otherwise
|
||||
jsonpath-rw-ext can't known if the expression is a string or a jsonpath field,
|
||||
in this case it will choice string as type.
|
||||
|
||||
Example with data::
|
||||
|
||||
{
|
||||
'cow': 'foo',
|
||||
'fish': 'bar'
|
||||
}
|
||||
|
||||
| **cow + fish** returns **cowfish**
|
||||
| **$.cow + $.fish** returns **foobar**
|
||||
| **$.cow + "_" + $.fish** returns **foo_bar**
|
||||
| **$.cow + "_" + fish** returns **foo_fish**
|
||||
|
||||
About arithmetic and list
|
||||
-------------------------
|
||||
|
||||
Arithmetic can be used against two lists if they have the same size.
|
||||
|
||||
Example with data::
|
||||
|
||||
{'objects': [
|
||||
{'cow': 2, 'cat': 3},
|
||||
{'cow': 4, 'cat': 6}
|
||||
]}
|
||||
|
||||
| **$.objects[\*].cow + $.objects[\*].cat** returns **[6, 9]**
|
||||
|
@ -1,75 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
#'oslosphinx'
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'jsonpath-rw-ext'
|
||||
copyright = u''
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'Mehdi Abaakouk', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
@ -1,4 +0,0 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
.. include:: ../../CONTRIBUTING.rst
|
@ -1,23 +0,0 @@
|
||||
.. jsonpath-rw-ext documentation master file, created by
|
||||
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to jsonpath-rw-ext's documentation!
|
||||
========================================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
readme
|
||||
contributing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -1 +0,0 @@
|
||||
.. include:: ../../README.rst
|
@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import pbr.version
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'jsonpath_rw_ext').version_string()
|
||||
|
||||
from .parser import parse # noqa
|
@ -1,72 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import operator
|
||||
|
||||
import jsonpath_rw
|
||||
|
||||
OPERATOR_MAP = {
|
||||
'+': operator.add,
|
||||
'-': operator.sub,
|
||||
'*': operator.mul,
|
||||
'/': operator.truediv,
|
||||
}
|
||||
|
||||
|
||||
class Operation(jsonpath_rw.JSONPath):
|
||||
def __init__(self, left, op, right):
|
||||
self.left = left
|
||||
self.op = OPERATOR_MAP[op]
|
||||
self.right = right
|
||||
|
||||
def find(self, datum):
|
||||
result = []
|
||||
if (isinstance(self.left, jsonpath_rw.JSONPath)
|
||||
and isinstance(self.right, jsonpath_rw.JSONPath)):
|
||||
left = self.left.find(datum)
|
||||
right = self.right.find(datum)
|
||||
if left and right and len(left) == len(right):
|
||||
for l, r in zip(left, right):
|
||||
try:
|
||||
result.append(self.op(l.value, r.value))
|
||||
except TypeError:
|
||||
return []
|
||||
else:
|
||||
return []
|
||||
elif isinstance(self.left, jsonpath_rw.JSONPath):
|
||||
left = self.left.find(datum)
|
||||
for l in left:
|
||||
try:
|
||||
result.append(self.op(l.value, self.right))
|
||||
except TypeError:
|
||||
return []
|
||||
elif isinstance(self.right, jsonpath_rw.JSONPath):
|
||||
right = self.right.find(datum)
|
||||
for r in right:
|
||||
try:
|
||||
result.append(self.op(self.left, r.value))
|
||||
except TypeError:
|
||||
return []
|
||||
else:
|
||||
try:
|
||||
result.append(self.op(self.left, self.right))
|
||||
except TypeError:
|
||||
return []
|
||||
return [jsonpath_rw.DatumInContext.wrap(r) for r in result]
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r%s%r)' % (self.__class__.__name__, self.left, self.op,
|
||||
self.right)
|
||||
|
||||
def __str__(self):
|
||||
return '%s%s%s' % (self.left, self.op, self.right)
|
@ -1,111 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import jsonpath_rw
|
||||
import operator
|
||||
from six import moves
|
||||
|
||||
|
||||
OPERATOR_MAP = {
|
||||
'!=': operator.ne,
|
||||
'==': operator.eq,
|
||||
'=': operator.eq,
|
||||
'<=': operator.le,
|
||||
'<': operator.lt,
|
||||
'>=': operator.ge,
|
||||
'>': operator.gt,
|
||||
}
|
||||
|
||||
|
||||
class Filter(jsonpath_rw.JSONPath):
|
||||
"""The JSONQuery filter"""
|
||||
|
||||
def __init__(self, expressions):
|
||||
self.expressions = expressions
|
||||
|
||||
def find(self, datum):
|
||||
if not self.expressions:
|
||||
return datum
|
||||
|
||||
datum = jsonpath_rw.DatumInContext.wrap(datum)
|
||||
if not isinstance(datum.value, list):
|
||||
return []
|
||||
|
||||
return [jsonpath_rw.DatumInContext(datum.value[i],
|
||||
path=jsonpath_rw.Index(i),
|
||||
context=datum)
|
||||
for i in moves.range(0, len(datum.value))
|
||||
if (len(self.expressions) ==
|
||||
len(list(filter(lambda x: x.find(datum.value[i]),
|
||||
self.expressions))))]
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.expressions)
|
||||
|
||||
def __str__(self):
|
||||
return '[?%s]' % self.expressions
|
||||
|
||||
|
||||
class Expression(jsonpath_rw.JSONPath):
|
||||
"""The JSONQuery expression"""
|
||||
|
||||
def __init__(self, target, op, value):
|
||||
self.target = target
|
||||
self.op = op
|
||||
self.value = value
|
||||
|
||||
def find(self, datum):
|
||||
datum = self.target.find(jsonpath_rw.DatumInContext.wrap(datum))
|
||||
|
||||
if not datum:
|
||||
return []
|
||||
if self.op is None:
|
||||
return datum
|
||||
|
||||
found = []
|
||||
for data in datum:
|
||||
value = data.value
|
||||
if isinstance(self.value, int):
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
continue
|
||||
elif isinstance(self.value, bool):
|
||||
try:
|
||||
value = bool(value)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if OPERATOR_MAP[self.op](value, self.value):
|
||||
found.append(data)
|
||||
|
||||
return found
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Filter) and
|
||||
self.target == other.target and
|
||||
self.op == other.op and
|
||||
self.value == other.value)
|
||||
|
||||
def __repr__(self):
|
||||
if self.op is None:
|
||||
return '%s(%r)' % (self.__class__.__name__, self.target)
|
||||
else:
|
||||
return '%s(%r %s %r)' % (self.__class__.__name__,
|
||||
self.target, self.op, self.value)
|
||||
|
||||
def __str__(self):
|
||||
if self.op is None:
|
||||
return '%s' % self.target
|
||||
else:
|
||||
return '%s %s %s' % (self.target, self.op, self.value)
|
@ -1,92 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
import jsonpath_rw
|
||||
|
||||
|
||||
class SortedThis(jsonpath_rw.This):
|
||||
"""The JSONPath referring to the sorted version of the current object.
|
||||
|
||||
Concrete syntax is '`sorted`' or [\\field,/field].
|
||||
"""
|
||||
def __init__(self, expressions=None):
|
||||
self.expressions = expressions
|
||||
|
||||
def _compare(self, left, right):
|
||||
left = jsonpath_rw.DatumInContext.wrap(left)
|
||||
right = jsonpath_rw.DatumInContext.wrap(right)
|
||||
|
||||
for expr in self.expressions:
|
||||
field, reverse = expr
|
||||
l_datum = field.find(left)
|
||||
r_datum = field.find(right)
|
||||
if (not l_datum or not r_datum or
|
||||
len(l_datum) > 1 or len(r_datum) > 1 or
|
||||
l_datum[0].value == r_datum[0].value):
|
||||
# NOTE(sileht): should we do something if the expression
|
||||
# match multiple fields, for now ignore them
|
||||
continue
|
||||
elif l_datum[0].value < r_datum[0].value:
|
||||
return 1 if reverse else -1
|
||||
else:
|
||||
return -1 if reverse else 1
|
||||
return 0
|
||||
|
||||
def find(self, datum):
|
||||
"""Return sorted value of This if list or dict."""
|
||||
if isinstance(datum.value, dict) and self.expressions:
|
||||
return datum
|
||||
|
||||
if isinstance(datum.value, dict) or isinstance(datum.value, list):
|
||||
key = (functools.cmp_to_key(self._compare)
|
||||
if self.expressions else None)
|
||||
return [jsonpath_rw.DatumInContext.wrap(
|
||||
[value for value in sorted(datum.value, key=key)])]
|
||||
return datum
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Len)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.expressions)
|
||||
|
||||
def __str__(self):
|
||||
return '[?%s]' % self.expressions
|
||||
|
||||
|
||||
class Len(jsonpath_rw.JSONPath):
|
||||
"""The JSONPath referring to the len of the current object.
|
||||
|
||||
Concrete syntax is '`len`'.
|
||||
"""
|
||||
|
||||
def find(self, datum):
|
||||
datum = jsonpath_rw.DatumInContext.wrap(datum)
|
||||
try:
|
||||
value = len(datum.value)
|
||||
except TypeError:
|
||||
return []
|
||||
else:
|
||||
return [jsonpath_rw.DatumInContext(value,
|
||||
context=None,
|
||||
path=Len())]
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Len)
|
||||
|
||||
def __str__(self):
|
||||
return '`len`'
|
||||
|
||||
def __repr__(self):
|
||||
return 'Len()'
|
@ -1,91 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import re
|
||||
|
||||
import jsonpath_rw
|
||||
|
||||
|
||||
SUB = re.compile("sub\(/(.*)/,\s+(.*)\)")
|
||||
SPLIT = re.compile("split\((.),\s+(\d+),\s+(\d+|-1)\)")
|
||||
|
||||
|
||||
class DefintionInvalid(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Sub(jsonpath_rw.This):
|
||||
"""Regex subtituor
|
||||
|
||||
Concrete syntax is '`sub(/regex/, repl)`'
|
||||
"""
|
||||
|
||||
def __init__(self, method=None):
|
||||
m = SUB.match(method)
|
||||
if m is None:
|
||||
raise DefintionInvalid("%s is not valid" % method)
|
||||
self.expr = m.group(1).strip()
|
||||
self.repl = m.group(2).strip()
|
||||
self.regex = re.compile(self.expr)
|
||||
self.method = method
|
||||
print("%r" % self)
|
||||
|
||||
def find(self, datum):
|
||||
datum = jsonpath_rw.DatumInContext.wrap(datum)
|
||||
value = self.regex.sub(self.repl, datum.value)
|
||||
if value == datum.value:
|
||||
return []
|
||||
else:
|
||||
return [jsonpath_rw.DatumInContext.wrap(value)]
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Sub) and self.method == other.method)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.method)
|
||||
|
||||
def __str__(self):
|
||||
return '`sub(/%s/, %s)`' % (self.expr, self.repl)
|
||||
|
||||
|
||||
class Split(jsonpath_rw.This):
|
||||
"""String splitter
|
||||
|
||||
Concrete syntax is '`split(char, segment, max_split)`'
|
||||
"""
|
||||
|
||||
def __init__(self, method=None):
|
||||
m = SPLIT.match(method)
|
||||
if m is None:
|
||||
raise DefintionInvalid("%s is not valid" % method)
|
||||
self.char = m.group(1)
|
||||
self.segment = int(m.group(2))
|
||||
self.max_split = int(m.group(3))
|
||||
self.method = method
|
||||
|
||||
def find(self, datum):
|
||||
datum = jsonpath_rw.DatumInContext.wrap(datum)
|
||||
try:
|
||||
value = datum.value.split(self.char, self.max_split)[self.segment]
|
||||
except Exception:
|
||||
return []
|
||||
return [jsonpath_rw.DatumInContext.wrap(value)]
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Sub) and self.method == other.method)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.method)
|
||||
|
||||
def __str__(self):
|
||||
return '`%s`' % self.method
|
@ -1,179 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import jsonpath_rw
|
||||
from jsonpath_rw import lexer
|
||||
from jsonpath_rw import parser
|
||||
|
||||
from jsonpath_rw_ext import _arithmetic
|
||||
from jsonpath_rw_ext import _filter
|
||||
from jsonpath_rw_ext import _iterable
|
||||
from jsonpath_rw_ext import _string
|
||||
|
||||
# NOTE(sileht): This block is very important otherwise py3X tests fail no joke
|
||||
# ply/yacc.py order functions by line, then by module, but in py3 module are
|
||||
# not sortable, so we add this block to not have methods defined at the same
|
||||
# line in jsonpath_rw and jsonpath_rw_ext, yes that really sucks ...
|
||||
# (Need some other lines)
|
||||
# (Need some other lines)
|
||||
# (Need some other lines)
|
||||
|
||||
|
||||
class ExtendedJsonPathLexer(lexer.JsonPathLexer):
|
||||
"""Custom LALR-lexer for JsonPath"""
|
||||
literals = lexer.JsonPathLexer.literals + ['?', '@', '+', '*', '/', '-']
|
||||
tokens = (['BOOL'] +
|
||||
parser.JsonPathLexer.tokens +
|
||||
['FILTER_OP', 'SORT_DIRECTION', 'FLOAT'])
|
||||
|
||||
t_FILTER_OP = r'==?|<=|>=|!=|<|>'
|
||||
|
||||
def t_BOOL(self, t):
|
||||
r'true|false'
|
||||
t.value = True if t.value == 'true' else False
|
||||
return t
|
||||
|
||||
def t_SORT_DIRECTION(self, t):
|
||||
r',?\s*(/|\\)'
|
||||
t.value = t.value[-1]
|
||||
return t
|
||||
|
||||
def t_ID(self, t):
|
||||
r'@?[a-zA-Z_][a-zA-Z0-9_@\-]*'
|
||||
# NOTE(sileht): This fixes the ID expression to be
|
||||
# able to use @ for `This` like any json query
|
||||
t.type = self.reserved_words.get(t.value, 'ID')
|
||||
return t
|
||||
|
||||
def t_FLOAT(self, t):
|
||||
r'-?\d+\.\d+'
|
||||
t.value = float(t.value)
|
||||
return t
|
||||
|
||||
|
||||
class ExtentedJsonPathParser(parser.JsonPathParser):
|
||||
"""Custom LALR-parser for JsonPath"""
|
||||
|
||||
tokens = ExtendedJsonPathLexer.tokens
|
||||
|
||||
def __init__(self, debug=False, lexer_class=None):
|
||||
lexer_class = lexer_class or ExtendedJsonPathLexer
|
||||
super(ExtentedJsonPathParser, self).__init__(debug, lexer_class)
|
||||
|
||||
def p_jsonpath_operator_jsonpath(self, p):
|
||||
"""jsonpath : NUMBER operator NUMBER
|
||||
| FLOAT operator FLOAT
|
||||
| ID operator ID
|
||||
| NUMBER operator jsonpath
|
||||
| FLOAT operator jsonpath
|
||||
| jsonpath operator NUMBER
|
||||
| jsonpath operator FLOAT
|
||||
| jsonpath operator jsonpath
|
||||
"""
|
||||
|
||||
# NOTE(sileht): If we have choice between a field or a string we
|
||||
# always choice string, because field can be full qualified
|
||||
# like $.foo == foo and where string can't.
|
||||
for i in [1, 3]:
|
||||
if (isinstance(p[i], jsonpath_rw.Fields)
|
||||
and len(p[i].fields) == 1):
|
||||
p[i] = p[i].fields[0]
|
||||
|
||||
p[0] = _arithmetic.Operation(p[1], p[2], p[3])
|
||||
|
||||
def p_operator(self, p):
|
||||
"""operator : '+'
|
||||
| '-'
|
||||
| '*'
|
||||
| '/'
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_jsonpath_named_operator(self, p):
|
||||
"jsonpath : NAMED_OPERATOR"
|
||||
if p[1] == 'len':
|
||||
p[0] = _iterable.Len()
|
||||
elif p[1] == 'sorted':
|
||||
p[0] = _iterable.SortedThis()
|
||||
elif p[1].startswith("split("):
|
||||
p[0] = _string.Split(p[1])
|
||||
elif p[1].startswith("sub("):
|
||||
p[0] = _string.Sub(p[1])
|
||||
else:
|
||||
super(ExtentedJsonPathParser, self).p_jsonpath_named_operator(p)
|
||||
|
||||
def p_expression(self, p):
|
||||
"""expression : jsonpath
|
||||
| jsonpath FILTER_OP ID
|
||||
| jsonpath FILTER_OP FLOAT
|
||||
| jsonpath FILTER_OP NUMBER
|
||||
| jsonpath FILTER_OP BOOL
|
||||
"""
|
||||
if len(p) == 2:
|
||||
left, op, right = p[1], None, None
|
||||
else:
|
||||
__, left, op, right = p
|
||||
p[0] = _filter.Expression(left, op, right)
|
||||
|
||||
def p_expressions_expression(self, p):
|
||||
"expressions : expression"
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_expressions_and(self, p):
|
||||
"expressions : expressions '&' expressions"
|
||||
# TODO(sileht): implements '|'
|
||||
p[0] = p[1] + p[3]
|
||||
|
||||
def p_expressions_parens(self, p):
|
||||
"expressions : '(' expressions ')'"
|
||||
p[0] = p[2]
|
||||
|
||||
def p_filter(self, p):
|
||||
"filter : '?' expressions "
|
||||
p[0] = _filter.Filter(p[2])
|
||||
|
||||
def p_jsonpath_filter(self, p):
|
||||
"jsonpath : jsonpath '[' filter ']'"
|
||||
p[0] = jsonpath_rw.Child(p[1], p[3])
|
||||
|
||||
def p_sort(self, p):
|
||||
"sort : SORT_DIRECTION jsonpath"
|
||||
p[0] = (p[2], p[1] != "/")
|
||||
|
||||
def p_sorts_sort(self, p):
|
||||
"sorts : sort"
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_sorts_comma(self, p):
|
||||
"sorts : sorts sorts"
|
||||
p[0] = p[1] + p[2]
|
||||
|
||||
def p_jsonpath_sort(self, p):
|
||||
"jsonpath : jsonpath '[' sorts ']'"
|
||||
sort = _iterable.SortedThis(p[3])
|
||||
p[0] = jsonpath_rw.Child(p[1], sort)
|
||||
|
||||
def p_jsonpath_this(self, p):
|
||||
"jsonpath : '@'"
|
||||
p[0] = jsonpath_rw.This()
|
||||
|
||||
precedence = [
|
||||
('left', '+', '-'),
|
||||
('left', '*', '/'),
|
||||
] + jsonpath_rw.parser.JsonPathParser.precedence + [
|
||||
('nonassoc', 'ID'),
|
||||
]
|
||||
|
||||
|
||||
def parse(path, debug=False):
|
||||
return ExtentedJsonPathParser(debug=debug).parse(path)
|
@ -1,576 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
test_jsonpath_rw_ext
|
||||
----------------------------------
|
||||
|
||||
Tests for `jsonpath_rw_ext` module.
|
||||
"""
|
||||
|
||||
from jsonpath_rw import jsonpath # For setting the global auto_id_field flag
|
||||
from oslotest import base
|
||||
from six import moves
|
||||
import testscenarios
|
||||
|
||||
from jsonpath_rw_ext import parser
|
||||
|
||||
|
||||
class TestJsonpath_rw_ext(testscenarios.WithScenarios,
|
||||
base.BaseTestCase):
|
||||
scenarios = [
|
||||
('sorted_list', dict(string='objects.`sorted`',
|
||||
data={'objects': ['alpha', 'gamma', 'beta']},
|
||||
target=[['alpha', 'beta', 'gamma']])),
|
||||
('sorted_list_indexed', dict(string='objects.`sorted`[1]',
|
||||
data={'objects': [
|
||||
'alpha', 'gamma', 'beta']},
|
||||
target='beta')),
|
||||
('sorted_dict', dict(string='objects.`sorted`',
|
||||
data={'objects': {'cow': 'moo', 'horse': 'neigh',
|
||||
'cat': 'meow'}},
|
||||
target=[['cat', 'cow', 'horse']])),
|
||||
('sorted_dict_indexed', dict(string='objects.`sorted`[0]',
|
||||
data={'objects': {'cow': 'moo',
|
||||
'horse': 'neigh',
|
||||
'cat': 'meow'}},
|
||||
target='cat')),
|
||||
|
||||
('len_list', dict(string='objects.`len`',
|
||||
data={'objects': ['alpha', 'gamma', 'beta']},
|
||||
target=3)),
|
||||
('len_dict', dict(string='objects.`len`',
|
||||
data={'objects': {'cow': 'moo', 'cat': 'neigh'}},
|
||||
target=2)),
|
||||
('len_str', dict(string='objects[0].`len`',
|
||||
data={'objects': ['alpha', 'gamma']},
|
||||
target=5)),
|
||||
|
||||
('filter_exists_syntax1', dict(string='objects[?cow]',
|
||||
data={'objects': [{'cow': 'moo'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow': 'moo'}])),
|
||||
('filter_exists_syntax2', dict(string='objects[?@.cow]',
|
||||
data={'objects': [{'cow': 'moo'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow': 'moo'}])),
|
||||
('filter_exists_syntax3', dict(string='objects[?(@.cow)]',
|
||||
data={'objects': [{'cow': 'moo'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow': 'moo'}])),
|
||||
('filter_exists_syntax4', dict(string='objects[?(@."cow!?cat")]',
|
||||
data={'objects': [{'cow!?cat': 'moo'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow!?cat': 'moo'}])),
|
||||
('filter_eq1', dict(string='objects[?cow="moo"]',
|
||||
data={'objects': [{'cow': 'moo'},
|
||||
{'cow': 'neigh'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow': 'moo'}])),
|
||||
('filter_eq2', dict(string='objects[?(@.["cow"]="moo")]',
|
||||
data={'objects': [{'cow': 'moo'},
|
||||
{'cow': 'neigh'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow': 'moo'}])),
|
||||
('filter_eq3', dict(string='objects[?cow=="moo"]',
|
||||
data={'objects': [{'cow': 'moo'},
|
||||
{'cow': 'neigh'},
|
||||
{'cat': 'neigh'}]},
|
||||
target=[{'cow': 'moo'}])),
|
||||
('filter_gt', dict(string='objects[?cow>5]',
|
||||
data={'objects': [{'cow': 8},
|
||||
{'cow': 7},
|
||||
{'cow': 5},
|
||||
{'cow': 'neigh'}]},
|
||||
target=[{'cow': 8}, {'cow': 7}])),
|
||||
('filter_and', dict(string='objects[?cow>5&cat=2]',
|
||||
data={'objects': [{'cow': 8, 'cat': 2},
|
||||
{'cow': 7, 'cat': 2},
|
||||
{'cow': 2, 'cat': 2},
|
||||
{'cow': 5, 'cat': 3},
|
||||
{'cow': 8, 'cat': 3}]},
|
||||
target=[{'cow': 8, 'cat': 2},
|
||||
{'cow': 7, 'cat': 2}])),
|
||||
('filter_float_gt', dict(
|
||||
string='objects[?confidence>=0.5].prediction',
|
||||
data={
|
||||
'objects': [
|
||||
{'confidence': 0.42,
|
||||
'prediction': 'Good'},
|
||||
{'confidence': 0.58,
|
||||
'prediction': 'Bad'},
|
||||
]
|
||||
},
|
||||
target=['Bad']
|
||||
)),
|
||||
('sort1', dict(string='objects[/cow]',
|
||||
data={'objects': [{'cat': 1, 'cow': 2},
|
||||
{'cat': 2, 'cow': 1},
|
||||
{'cat': 3, 'cow': 3}]},
|
||||
target=[[{'cat': 2, 'cow': 1},
|
||||
{'cat': 1, 'cow': 2},
|
||||
{'cat': 3, 'cow': 3}]])),
|
||||
('sort1_indexed', dict(string='objects[/cow][0].cat',
|
||||
data={'objects': [{'cat': 1, 'cow': 2},
|
||||
{'cat': 2, 'cow': 1},
|
||||
{'cat': 3, 'cow': 3}]},
|
||||
target=2)),
|
||||
('sort2', dict(string='objects[\cat]',
|
||||
data={'objects': [{'cat': 2}, {'cat': 1}, {'cat': 3}]},
|
||||
target=[[{'cat': 3}, {'cat': 2}, {'cat': 1}]])),
|
||||
('sort2_indexed', dict(string='objects[\cat][-1].cat',
|
||||
data={'objects': [{'cat': 2}, {'cat': 1},
|
||||
{'cat': 3}]},
|
||||
target=1)),
|
||||
('sort3', dict(string='objects[/cow,\cat]',
|
||||
data={'objects': [{'cat': 1, 'cow': 2},
|
||||
{'cat': 2, 'cow': 1},
|
||||
{'cat': 3, 'cow': 1},
|
||||
{'cat': 3, 'cow': 3}]},
|
||||
target=[[{'cat': 3, 'cow': 1},
|
||||
{'cat': 2, 'cow': 1},
|
||||
{'cat': 1, 'cow': 2},
|
||||
{'cat': 3, 'cow': 3}]])),
|
||||
('sort3_indexed', dict(string='objects[/cow,\cat][0].cat',
|
||||
data={'objects': [{'cat': 1, 'cow': 2},
|
||||
{'cat': 2, 'cow': 1},
|
||||
{'cat': 3, 'cow': 1},
|
||||
{'cat': 3, 'cow': 3}]},
|
||||
target=3)),
|
||||
('sort4', dict(string='objects[/cat.cow]',
|
||||
data={'objects': [{'cat': {'dog': 1, 'cow': 2}},
|
||||
{'cat': {'dog': 2, 'cow': 1}},
|
||||
{'cat': {'dog': 3, 'cow': 3}}]},
|
||||
target=[[{'cat': {'dog': 2, 'cow': 1}},
|
||||
{'cat': {'dog': 1, 'cow': 2}},
|
||||
{'cat': {'dog': 3, 'cow': 3}}]])),
|
||||
('sort4_indexed', dict(string='objects[/cat.cow][0].cat.dog',
|
||||
data={'objects': [{'cat': {'dog': 1,
|
||||
'cow': 2}},
|
||||
{'cat': {'dog': 2,
|
||||
'cow': 1}},
|
||||
{'cat': {'dog': 3,
|
||||
'cow': 3}}]},
|
||||
target=2)),
|
||||
('sort5_twofields', dict(string='objects[/cat.(cow,bow)]',
|
||||
data={'objects':
|
||||
[{'cat': {'dog': 1, 'bow': 3}},
|
||||
{'cat': {'dog': 2, 'cow': 1}},
|
||||
{'cat': {'dog': 2, 'bow': 2}},
|
||||
{'cat': {'dog': 3, 'cow': 2}}]},
|
||||
target=[[{'cat': {'dog': 2, 'cow': 1}},
|
||||
{'cat': {'dog': 2, 'bow': 2}},
|
||||
{'cat': {'dog': 3, 'cow': 2}},
|
||||
{'cat': {'dog': 1, 'bow': 3}}]])),
|
||||
|
||||
('sort5_indexed', dict(string='objects[/cat.(cow,bow)][0].cat.dog',
|
||||
data={'objects':
|
||||
[{'cat': {'dog': 1, 'bow': 3}},
|
||||
{'cat': {'dog': 2, 'cow': 1}},
|
||||
{'cat': {'dog': 2, 'bow': 2}},
|
||||
{'cat': {'dog': 3, 'cow': 2}}]},
|
||||
target=2)),
|
||||
('arithmetic_number_only', dict(string='3 * 3', data={},
|
||||
target=[9])),
|
||||
|
||||
('arithmetic_mul1', dict(string='$.foo * 10', data={'foo': 4},
|
||||
target=[40])),
|
||||
('arithmetic_mul2', dict(string='10 * $.foo', data={'foo': 4},
|
||||
target=[40])),
|
||||
('arithmetic_mul3', dict(string='$.foo * 10', data={'foo': 4},
|
||||
target=[40])),
|
||||
('arithmetic_mul4', dict(string='$.foo * 3', data={'foo': 'f'},
|
||||
target=['fff'])),
|
||||
('arithmetic_mul5', dict(string='foo * 3', data={'foo': 'f'},
|
||||
target=['foofoofoo'])),
|
||||
('arithmetic_mul6', dict(string='($.foo * 10 * $.foo) + 2',
|
||||
data={'foo': 4}, target=[162])),
|
||||
('arithmetic_mul7', dict(string='$.foo * 10 * $.foo + 2',
|
||||
data={'foo': 4}, target=[240])),
|
||||
|
||||
('arithmetic_str0', dict(string='foo + bar',
|
||||
data={'foo': 'name', "bar": "node"},
|
||||
target=["foobar"])),
|
||||
('arithmetic_str1', dict(string='foo + "_" + bar',
|
||||
data={'foo': 'name', "bar": "node"},
|
||||
target=["foo_bar"])),
|
||||
('arithmetic_str2', dict(string='$.foo + "_" + $.bar',
|
||||
data={'foo': 'name', "bar": "node"},
|
||||
target=["name_node"])),
|
||||
('arithmetic_str3', dict(string='$.foo + $.bar',
|
||||
data={'foo': 'name', "bar": "node"},
|
||||
target=["namenode"])),
|
||||
('arithmetic_str4', dict(string='foo.cow + bar.cow',
|
||||
data={'foo': {'cow': 'name'},
|
||||
"bar": {'cow': "node"}},
|
||||
target=["namenode"])),
|
||||
|
||||
('arithmetic_list1', dict(string='$.objects[*].cow * 2',
|
||||
data={'objects': [{'cow': 1},
|
||||
{'cow': 2},
|
||||
{'cow': 3}]},
|
||||
target=[2, 4, 6])),
|
||||
|
||||
('arithmetic_list2', dict(string='$.objects[*].cow * $.objects[*].cow',
|
||||
data={'objects': [{'cow': 1},
|
||||
{'cow': 2},
|
||||
{'cow': 3}]},
|
||||
target=[1, 4, 9])),
|
||||
|
||||
('arithmetic_list_err1', dict(
|
||||
string='$.objects[*].cow * $.objects2[*].cow',
|
||||
data={'objects': [{'cow': 1}, {'cow': 2}, {'cow': 3}],
|
||||
'objects2': [{'cow': 5}]},
|
||||
target=[])),
|
||||
|
||||
('arithmetic_err1', dict(string='$.objects * "foo"',
|
||||
data={'objects': []}, target=[])),
|
||||
('arithmetic_err2', dict(string='"bar" * "foo"', data={}, target=[])),
|
||||
|
||||
('real_life_example1', dict(
|
||||
string="payload.metrics[?(@.name='cpu.frequency')].value * 100",
|
||||
data={'payload': {'metrics': [
|
||||
{'timestamp': '2013-07-29T06:51:34.472416',
|
||||
'name': 'cpu.frequency',
|
||||
'value': 1600,
|
||||
'source': 'libvirt.LibvirtDriver'},
|
||||
{'timestamp': '2013-07-29T06:51:34.472416',
|
||||
'name': 'cpu.user.time',
|
||||
'value': 17421440000000,
|
||||
'source': 'libvirt.LibvirtDriver'}]}},
|
||||
target=[160000])),
|
||||
|
||||
('real_life_example2', dict(
|
||||
string="payload.(id|(resource.id))",
|
||||
data={'payload': {'id': 'foobar'}},
|
||||
target=['foobar'])),
|
||||
('real_life_example3', dict(
|
||||
string="payload.id|(resource.id)",
|
||||
data={'payload': {'resource':
|
||||
{'id': 'foobar'}}},
|
||||
target=['foobar'])),
|
||||
('real_life_example4', dict(
|
||||
string="payload.id|(resource.id)",
|
||||
data={'payload': {'id': 'yes',
|
||||
'resource': {'id': 'foobar'}}},
|
||||
target=['yes', 'foobar'])),
|
||||
|
||||
('sub1', dict(
|
||||
string="payload.`sub(/(foo\\\\d+)\\\\+(\\\\d+bar)/, \\\\2-\\\\1)`",
|
||||
data={'payload': "foo5+3bar"},
|
||||
target=["3bar-foo5"]
|
||||
)),
|
||||
('sub2', dict(
|
||||
string='payload.`sub(/foo\\\\+bar/, repl)`',
|
||||
data={'payload': "foo+bar"},
|
||||
target=["repl"]
|
||||
)),
|
||||
|
||||
('split1', dict(
|
||||
string='payload.`split(-, 2, -1)`',
|
||||
data={'payload': "foo-bar-cat-bow"},
|
||||
target=["cat"]
|
||||
)),
|
||||
('split2', dict(
|
||||
string='payload.`split(-, 2, 2)`',
|
||||
data={'payload': "foo-bar-cat-bow"},
|
||||
target=["cat-bow"]
|
||||
)),
|
||||
|
||||
('bug-#2-correct', dict(
|
||||
string='foo[?(@.baz==1)]',
|
||||
data={'foo': [{'baz': 1}, {'baz': 2}]},
|
||||
target=[{'baz': 1}],
|
||||
)),
|
||||
|
||||
('bug-#2-wrong', dict(
|
||||
string='foo[*][?(@.baz==1)]',
|
||||
data={'foo': [{'baz': 1}, {'baz': 2}]},
|
||||
target=[],
|
||||
)),
|
||||
|
||||
('boolean-filter-true', dict(
|
||||
string='foo[?flag = true].color',
|
||||
data={'foo': [{"color": "blue", "flag": True},
|
||||
{"color": "green", "flag": False}]},
|
||||
target=['blue']
|
||||
)),
|
||||
|
||||
('boolean-filter-false', dict(
|
||||
string='foo[?flag = false].color',
|
||||
data={'foo': [{"color": "blue", "flag": True},
|
||||
{"color": "green", "flag": False}]},
|
||||
target=['green']
|
||||
)),
|
||||
|
||||
('boolean-filter-other-datatypes-involved', dict(
|
||||
string='foo[?flag = true].color',
|
||||
data={'foo': [{"color": "blue", "flag": True},
|
||||
{"color": "green", "flag": 2},
|
||||
{"color": "red", "flag": "hi"}]},
|
||||
target=['blue']
|
||||
)),
|
||||
|
||||
('boolean-filter-string-true-string-literal', dict(
|
||||
string='foo[?flag = "true"].color',
|
||||
data={'foo': [{"color": "blue", "flag": True},
|
||||
{"color": "green", "flag": "true"}]},
|
||||
target=['green']
|
||||
)),
|
||||
]
|
||||
|
||||
def test_fields_value(self):
|
||||
jsonpath.auto_id_field = None
|
||||
result = parser.parse(self.string, debug=True).find(self.data)
|
||||
if isinstance(self.target, list):
|
||||
self.assertEqual(self.target, [r.value for r in result])
|
||||
elif isinstance(self.target, set):
|
||||
self.assertEqual(self.target, set([r.value for r in result]))
|
||||
elif isinstance(self.target, (int, float)):
|
||||
self.assertEqual(self.target, result[0].value)
|
||||
else:
|
||||
self.assertEqual(self.target, result[0].value)
|
||||
|
||||
# NOTE(sileht): copy of tests/test_jsonpath.py
|
||||
# to ensure we didn't break jsonpath_rw
|
||||
|
||||
|
||||
class TestJsonPath(base.BaseTestCase):
|
||||
"""Tests of the actual jsonpath functionality """
|
||||
|
||||
#
|
||||
# Check that the data value returned is good
|
||||
#
|
||||
def check_cases(self, test_cases):
|
||||
# Note that just manually building an AST would avoid this dep and
|
||||
# isolate the tests, but that would suck a bit
|
||||
# Also, we coerce iterables, etc, into the desired target type
|
||||
|
||||
for string, data, target in test_cases:
|
||||
print('parse("%s").find(%s) =?= %s' % (string, data, target))
|
||||
result = parser.parse(string).find(data)
|
||||
if isinstance(target, list):
|
||||
assert [r.value for r in result] == target
|
||||
elif isinstance(target, set):
|
||||
assert set([r.value for r in result]) == target
|
||||
else:
|
||||
assert result.value == target
|
||||
|
||||
def test_fields_value(self):
|
||||
jsonpath.auto_id_field = None
|
||||
self.check_cases([('foo', {'foo': 'baz'}, ['baz']),
|
||||
('foo,baz', {'foo': 1, 'baz': 2}, [1, 2]),
|
||||
('@foo', {'@foo': 1}, [1]),
|
||||
('*', {'foo': 1, 'baz': 2}, set([1, 2]))])
|
||||
|
||||
jsonpath.auto_id_field = 'id'
|
||||
self.check_cases([('*', {'foo': 1, 'baz': 2}, set([1, 2, '`this`']))])
|
||||
|
||||
def test_root_value(self):
|
||||
jsonpath.auto_id_field = None
|
||||
self.check_cases([
|
||||
('$', {'foo': 'baz'}, [{'foo': 'baz'}]),
|
||||
('foo.$', {'foo': 'baz'}, [{'foo': 'baz'}]),
|
||||
('foo.$.foo', {'foo': 'baz'}, ['baz']),
|
||||
])
|
||||
|
||||
def test_this_value(self):
|
||||
jsonpath.auto_id_field = None
|
||||
self.check_cases([
|
||||
('`this`', {'foo': 'baz'}, [{'foo': 'baz'}]),
|
||||
('foo.`this`', {'foo': 'baz'}, ['baz']),
|
||||
('foo.`this`.baz', {'foo': {'baz': 3}}, [3]),
|
||||
])
|
||||
|
||||
def test_index_value(self):
|
||||
self.check_cases([
|
||||
('[0]', [42], [42]),
|
||||
('[5]', [42], []),
|
||||
('[2]', [34, 65, 29, 59], [29])
|
||||
])
|
||||
|
||||
def test_slice_value(self):
|
||||
self.check_cases([('[*]', [1, 2, 3], [1, 2, 3]),
|
||||
('[*]', moves.range(1, 4), [1, 2, 3]),
|
||||
('[1:]', [1, 2, 3, 4], [2, 3, 4]),
|
||||
('[:2]', [1, 2, 3, 4], [1, 2])])
|
||||
|
||||
# Funky slice hacks
|
||||
self.check_cases([
|
||||
('[*]', 1, [1]), # This is a funky hack
|
||||
('[0:]', 1, [1]), # This is a funky hack
|
||||
('[*]', {'foo': 1}, [{'foo': 1}]), # This is a funky hack
|
||||
('[*].foo', {'foo': 1}, [1]), # This is a funky hack
|
||||
])
|
||||
|
||||
def test_child_value(self):
|
||||
self.check_cases([('foo.baz', {'foo': {'baz': 3}}, [3]),
|
||||
('foo.baz', {'foo': {'baz': [3]}}, [[3]]),
|
||||
('foo.baz.bizzle', {'foo': {'baz': {'bizzle': 5}}},
|
||||
[5])])
|
||||
|
||||
def test_descendants_value(self):
|
||||
self.check_cases([
|
||||
('foo..baz', {'foo': {'baz': 1, 'bing': {'baz': 2}}}, [1, 2]),
|
||||
('foo..baz', {'foo': [{'baz': 1}, {'baz': 2}]}, [1, 2]),
|
||||
])
|
||||
|
||||
def test_parent_value(self):
|
||||
self.check_cases([('foo.baz.`parent`', {'foo': {'baz': 3}},
|
||||
[{'baz': 3}]),
|
||||
('foo.`parent`.foo.baz.`parent`.baz.bizzle',
|
||||
{'foo': {'baz': {'bizzle': 5}}}, [5])])
|
||||
|
||||
def test_hyphen_key(self):
|
||||
# NOTE(sileht): hyphen is now a operator
|
||||
# so to use it has key we must escape it with quote
|
||||
# self.check_cases([('foo.bar-baz', {'foo': {'bar-baz': 3}}, [3]),
|
||||
# ('foo.[bar-baz,blah-blah]',
|
||||
# {'foo': {'bar-baz': 3, 'blah-blah': 5}},
|
||||
# [3, 5])])
|
||||
self.check_cases([('foo."bar-baz"', {'foo': {'bar-baz': 3}}, [3]),
|
||||
('foo.["bar-baz","blah-blah"]',
|
||||
{'foo': {'bar-baz': 3, 'blah-blah': 5}},
|
||||
[3, 5])])
|
||||
# self.assertRaises(lexer.JsonPathLexerError, self.check_cases,
|
||||
# [('foo.-baz', {'foo': {'-baz': 8}}, [8])])
|
||||
|
||||
#
|
||||
# Check that the paths for the data are correct.
|
||||
# FIXME: merge these tests with the above, since the inputs are the same
|
||||
# anyhow
|
||||
#
|
||||
def check_paths(self, test_cases):
|
||||
# Note that just manually building an AST would avoid this dep and
|
||||
# isolate the tests, but that would suck a bit
|
||||
# Also, we coerce iterables, etc, into the desired target type
|
||||
|
||||
for string, data, target in test_cases:
|
||||
print('parse("%s").find(%s).paths =?= %s' % (string, data, target))
|
||||
result = parser.parse(string).find(data)
|
||||
if isinstance(target, list):
|
||||
assert [str(r.full_path) for r in result] == target
|
||||
elif isinstance(target, set):
|
||||
assert set([str(r.full_path) for r in result]) == target
|
||||
else:
|
||||
assert str(result.path) == target
|
||||
|
||||
def test_fields_paths(self):
|
||||
jsonpath.auto_id_field = None
|
||||
self.check_paths([('foo', {'foo': 'baz'}, ['foo']),
|
||||
('foo,baz', {'foo': 1, 'baz': 2}, ['foo', 'baz']),
|
||||
('*', {'foo': 1, 'baz': 2}, set(['foo', 'baz']))])
|
||||
|
||||
jsonpath.auto_id_field = 'id'
|
||||
self.check_paths([('*', {'foo': 1, 'baz': 2},
|
||||
set(['foo', 'baz', 'id']))])
|
||||
|
||||
def test_root_paths(self):
|
||||
jsonpath.auto_id_field = None
|
||||
self.check_paths([
|
||||
('$', {'foo': 'baz'}, ['$']),
|
||||
('foo.$', {'foo': 'baz'}, ['$']),
|
||||
('foo.$.foo', {'foo': 'baz'}, ['foo']),
|
||||
])
|
||||
|
||||
def test_this_paths(self):
|
||||
jsonpath.auto_id_field = None
|
||||
self.check_paths([
|
||||
('`this`', {'foo': 'baz'}, ['`this`']),
|
||||
('foo.`this`', {'foo': 'baz'}, ['foo']),
|
||||
('foo.`this`.baz', {'foo': {'baz': 3}}, ['foo.baz']),
|
||||
])
|
||||
|
||||
def test_index_paths(self):
|
||||
self.check_paths([('[0]', [42], ['[0]']),
|
||||
('[2]', [34, 65, 29, 59], ['[2]'])])
|
||||
|
||||
def test_slice_paths(self):
|
||||
self.check_paths([('[*]', [1, 2, 3], ['[0]', '[1]', '[2]']),
|
||||
('[1:]', [1, 2, 3, 4], ['[1]', '[2]', '[3]'])])
|
||||
|
||||
def test_child_paths(self):
|
||||
self.check_paths([('foo.baz', {'foo': {'baz': 3}}, ['foo.baz']),
|
||||
('foo.baz', {'foo': {'baz': [3]}}, ['foo.baz']),
|
||||
('foo.baz.bizzle', {'foo': {'baz': {'bizzle': 5}}},
|
||||
['foo.baz.bizzle'])])
|
||||
|
||||
def test_descendants_paths(self):
|
||||
self.check_paths([('foo..baz', {'foo': {'baz': 1, 'bing': {'baz': 2}}},
|
||||
['foo.baz', 'foo.bing.baz'])])
|
||||
|
||||
#
|
||||
# Check the "auto_id_field" feature
|
||||
#
|
||||
def test_fields_auto_id(self):
|
||||
jsonpath.auto_id_field = "id"
|
||||
self.check_cases([('foo.id', {'foo': 'baz'}, ['foo']),
|
||||
('foo.id', {'foo': {'id': 'baz'}}, ['baz']),
|
||||
('foo,baz.id', {'foo': 1, 'baz': 2}, ['foo', 'baz']),
|
||||
('*.id',
|
||||
{'foo': {'id': 1},
|
||||
'baz': 2},
|
||||
set(['1', 'baz']))])
|
||||
|
||||
def test_root_auto_id(self):
|
||||
jsonpath.auto_id_field = 'id'
|
||||
self.check_cases([
|
||||
('$.id', {'foo': 'baz'}, ['$']), # This is a wonky case that is
|
||||
# not that interesting
|
||||
('foo.$.id', {'foo': 'baz', 'id': 'bizzle'}, ['bizzle']),
|
||||
('foo.$.baz.id', {'foo': 4, 'baz': 3}, ['baz']),
|
||||
])
|
||||
|
||||
def test_this_auto_id(self):
|
||||
jsonpath.auto_id_field = 'id'
|
||||
self.check_cases([
|
||||
('id', {'foo': 'baz'}, ['`this`']), # This is, again, a wonky case
|
||||
# that is not that interesting
|
||||
('foo.`this`.id', {'foo': 'baz'}, ['foo']),
|
||||
('foo.`this`.baz.id', {'foo': {'baz': 3}}, ['foo.baz']),
|
||||
])
|
||||
|
||||
def test_index_auto_id(self):
|
||||
jsonpath.auto_id_field = "id"
|
||||
self.check_cases([('[0].id', [42], ['[0]']),
|
||||
('[2].id', [34, 65, 29, 59], ['[2]'])])
|
||||
|
||||
def test_slice_auto_id(self):
|
||||
jsonpath.auto_id_field = "id"
|
||||
self.check_cases([('[*].id', [1, 2, 3], ['[0]', '[1]', '[2]']),
|
||||
('[1:].id', [1, 2, 3, 4], ['[1]', '[2]', '[3]'])])
|
||||
|
||||
def test_child_auto_id(self):
|
||||
jsonpath.auto_id_field = "id"
|
||||
self.check_cases([('foo.baz.id', {'foo': {'baz': 3}}, ['foo.baz']),
|
||||
('foo.baz.id', {'foo': {'baz': [3]}}, ['foo.baz']),
|
||||
('foo.baz.id', {'foo': {'id': 'bizzle', 'baz': 3}},
|
||||
['bizzle.baz']),
|
||||
('foo.baz.id', {'foo': {'baz': {'id': 'hi'}}},
|
||||
['foo.hi']),
|
||||
('foo.baz.bizzle.id',
|
||||
{'foo': {'baz': {'bizzle': 5}}},
|
||||
['foo.baz.bizzle'])])
|
||||
|
||||
def test_descendants_auto_id(self):
|
||||
jsonpath.auto_id_field = "id"
|
||||
self.check_cases([('foo..baz.id',
|
||||
{'foo': {
|
||||
'baz': 1,
|
||||
'bing': {
|
||||
'baz': 2
|
||||
}
|
||||
}},
|
||||
['foo.baz',
|
||||
'foo.bing.baz'])])
|
@ -1,6 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from oslo-incubator.git
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=jsonpath_rw_ext
|
40
release.sh
40
release.sh
@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
version=$1
|
||||
[ ! "$version"] && version=$(python setup.py --version | sed 's/\.dev.*//')
|
||||
|
||||
status=$(git status -sz)
|
||||
[ -z "$status" ] || false
|
||||
git checkout master
|
||||
[ "$SKIP_TESTS" ] || tox -epy35,py27,pep8,docs
|
||||
git push
|
||||
git tag -s $version -m "Release version ${version}"
|
||||
git checkout $version
|
||||
git clean -fd
|
||||
pbr_version=$(python setup.py --version)
|
||||
if [ "$version" != "$pbr_version" ]; then
|
||||
echo "something goes wrong pbr version is different from the provided one. ($pbr_version != $version)"
|
||||
exit 1
|
||||
fi
|
||||
python setup.py sdist bdist_wheel
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo "release: jsonpath-rw-ext ${version}"
|
||||
echo
|
||||
echo "SHA1sum: "
|
||||
sha1sum dist/*
|
||||
echo "MD5sum: "
|
||||
md5sum dist/*
|
||||
echo
|
||||
echo "uploading..."
|
||||
echo
|
||||
set -x
|
||||
|
||||
read
|
||||
git push --tags
|
||||
twine upload -r pypi -s dist/jsonpath-rw-ext-${version}.tar.gz dist/jsonpath_rw_ext-${version}-py2.py3-none-any.whl
|
||||
git checkout master
|
@ -1,6 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=1.8
|
||||
jsonpath-rw>=1.2.0
|
33
setup.cfg
33
setup.cfg
@ -1,33 +0,0 @@
|
||||
[metadata]
|
||||
name = jsonpath-rw-ext
|
||||
summary = Extensions for JSONPath RW
|
||||
description-file =
|
||||
README.rst
|
||||
author = Mehdi Abaakouk
|
||||
author-email = sileht@sileht.net
|
||||
home-page = https://github.com/sileht/python-jsonpath-rw-ext
|
||||
classifier =
|
||||
Development Status :: 5 - Production/Stable
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.3
|
||||
Programming Language :: Python :: 3.4
|
||||
|
||||
[files]
|
||||
packages =
|
||||
jsonpath_rw_ext
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
29
setup.py
29
setup.py
@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
@ -1,15 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
hacking>=0.12.0,!=0.13.0,<0.14
|
||||
|
||||
coverage>=3.6
|
||||
discover
|
||||
python-subunit>=0.0.18
|
||||
sphinx>=1.3
|
||||
oslosphinx>=2.5.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=1.4.0
|
36
tox.ini
36
tox.ini
@ -1,36 +0,0 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py33,py34,py27,pypy,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py test --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
# H405 the lexer use docstring and this is not compatible with this PEP
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125,H405
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
Loading…
x
Reference in New Issue
Block a user