Repository: adriank/ObjectPath
Branch: master
Commit: 8472264c57e8
Files: 30
Total size: 98.2 KB
Directory structure:
gitextract_4ckekqd1/
├── .coveragerc
├── .gitignore
├── .travis.yml
├── .vscode/
│ ├── launch.json
│ └── settings.json
├── LICENSE
├── MANIFEST.in
├── README.md
├── README.rst
├── README.rst.original
├── VER
├── build.sh
├── objectpath/
│ ├── __init__.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── interpreter.py
│ │ └── parser.py
│ ├── shell.py
│ └── utils/
│ ├── __init__.py
│ ├── colorify.py
│ ├── debugger.py
│ ├── json_ext.py
│ └── timeutils.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── shell.py
├── testObjectPath.py
└── tests/
├── __init__.py
├── test_ObjectPath.py
└── test_utils.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .coveragerc
================================================
[run]
omit =
*/tests*
*/shell.py
*/debugger.py
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
================================================
FILE: .travis.yml
================================================
language: python
python:
- '2.7'
- '3.8'
- pypy
install:
- pip install -r requirements.txt
- pip install coveralls
script:
- nosetests
- coverage run --source=objectpath setup.py test
deploy:
provider: pypi
user: adriankal
password:
secure: bWTP43XxjAIwC6PMmfjt4/dCBenky+zF7empMgzfmkUq5jCoQjCJm4IUdYIdjCKLYEIU1DaOTp7+rqmIZf2d8wWvGAc4+k3vinV9k8WzycpBM+YgnW2knQ5eko93H0lpNOIrat4J0wvc51JjHfj4uqib6SCTaXmBS/kRHmiRkx8=
on:
tags: true
all_branches: true
repo: adriank/ObjectPath
distributions: "sdist bdist_wheel"
after_success:
- coveralls
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File (Integrated Terminal)",
"type": "python",
"request": "launch",
"program": "/Users/adrian/projects/ObjectPath/shell.py",
"args": ["/Users/adrian/projects/nutrient-db/butter.json"],
"console": "integratedTerminal"
},
{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"port": 5678,
"host": "localhost"
},
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "objectpath",
"console": "integratedTerminal"
},
{
"name": "Python: Django",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"console": "integratedTerminal",
"args": [
"runserver",
"--noreload",
"--nothreading"
],
"django": true
},
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "app.py"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true
},
{
"name": "Python: Current File (External Terminal)",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "externalTerminal"
}
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"python.linting.pylintEnabled": true
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Adrian Kalbarczyk
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, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following 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 MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS 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.
================================================
FILE: MANIFEST.in
================================================
include *.rst
include VER
================================================
FILE: README.md
================================================
ObjectPath
==========
[](https://pypi.python.org/pypi/objectpath/)
<!--[](https://pypi.python.org/pypi/objectpath/)-->
[](https://travis-ci.org/adriank/ObjectPath)
[](https://landscape.io/github/adriank/ObjectPath/master)
[](https://coveralls.io/r/adriank/ObjectPath?branch=master)
The agile NoSQL query language for semi-structured data
-----------------------------------------------
**#Python #NoSQL #Javascript #JSON #nested-array-object**
ObjectPath is a query language similar to XPath or JSONPath, but much more powerful thanks to embedded arithmetic calculations, comparison mechanisms and built-in functions. This makes the language more like SQL in terms of expressiveness, but it works over JSON documents rather than relations. ObjectPath can be considered a full-featured expression language. Besides selector mechanism there is also boolean logic, type system and string concatenation available. On top of that, the language implementations (Python at the moment; Javascript is in beta!) are secure and relatively fast.
More at [ObjectPath site](https://adriank.github.io/ObjectPath/)

ObjectPath makes it easy to find data in big nested JSON documents. It borrows the best parts from E4X, JSONPath, XPath and SQL. ObjectPath is to JSON documents what XPath is to XML. Other examples to ilustrate this kind of relationship are:
| Scope | Language |
|---|---|
| text documents | regular expression |
| XML | XPath |
| HTML | CSS selectors |
| JSON documents | ObjectPath |
Documentation
-------------
[ObjectPath Reference](https://adriank.github.io/ObjectPath/reference.html)
Command line usage
-----
`````sh
$ sudo pip install objectpath
$ objectpath file.json
`````
or
`````sh
$ git clone https://github.com/adriank/ObjectPath.git
$ cd ObjectPath
$ python shell.py file.json
`````
Python usage
----------------
`````sh
$ sudo pip install objectpath
$ python
>>> from objectpath import *
>>> tree=Tree({"a":1})
>>> tree.execute("$.a")
1
>>>
`````
`````sh
$ git clone https://github.com/adriank/ObjectPath.git
$ cd ObjectPath
$ python
>>> from objectpath import *
>>> tree=Tree({"a":1})
>>> tree.execute("$.a")
1
>>>
`````
Contributing & bugs
-------------------
I appreciate all contributions and bugfix requests for ObjectPath, however since I don't code in Python anymore, this library is not maintained as of now. Since I can't fully assure that code contributed by others meets quality standards, I can't accept PRs.
If you feel you could maintain this code, ping me. I'd be more than happy to transfer this repo to a dedicated ObjectPath organization on GitHub and give the ownership to someone with more time for this project than me.
License
-------
**MIT**
================================================
FILE: README.rst
================================================
ObjectPath
==========
|Downloads| |Build Status| |Code Health| |Coverage Status|
The agile NoSQL query language for semi-structured data
-------------------------------------------------------
**#Python #NoSQL #Javascript #JSON #nested-array-object**
ObjectPath is a query language similar to XPath or JSONPath, but much
more powerful thanks to embedded arithmetic calculations, comparison
mechanisms and built-in functions. This makes the language more like SQL
in terms of expressiveness, but it works over JSON documents rather than
relations. ObjectPath can be considered a full-featured expression
language. Besides selector mechanism there is also boolean logic, type
system and string concatenation available. On top of that, the language
implementations (Python at the moment; Javascript is in beta!) are
secure and relatively fast.
More at `ObjectPath site <http://objectpath.org/>`__
.. figure:: http://adriank.github.io/ObjectPath/img/op-colors.png
:alt: ObjectPath img
ObjectPath img
ObjectPath makes it easy to find data in big nested JSON documents. It
borrows the best parts from E4X, JSONPath, XPath and SQL. ObjectPath is
to JSON documents what XPath is to XML. Other examples to ilustrate this
kind of relationship are:
============== ==================
Scope Language
============== ==================
text documents regular expression
XML XPath
HTML CSS selectors
JSON documents ObjectPath
============== ==================
Documentation
-------------
`ObjectPath Reference <http://objectpath.org/reference.html>`__
Command line usage
------------------
.. code:: sh
$ sudo pip install objectpath
$ objectpath file.json
or
.. code:: sh
$ git clone https://github.com/adriank/ObjectPath.git
$ cd ObjectPath
$ python shell.py file.json
Python usage
------------
.. code:: sh
$ sudo pip install objectpath
$ python
>>> from objectpath import *
>>> tree=Tree({"a":1})
>>> tree.execute("$.a")
1
>>>
.. code:: sh
$ git clone https://github.com/adriank/ObjectPath.git
$ cd ObjectPath
$ python
>>> from objectpath import *
>>> tree=Tree({"a":1})
>>> tree.execute("$.a")
1
>>>
Contributing & bugs
-------------------
I appreciate all contributions and bugfix requests for ObjectPath,
however since I don’t code in Python any more, this library is not
maintained as of now. Since I can’t fully assure that code contributed
by others meets quality standards, I can’t accept PRs.
If you feel you could maintain this code, ping me. I’d be more than
happy to transfer this repo to a dedicated ObjectPath organization on
GitHub and give the ownership to someone with more time for this project
than me.
License
-------
**MIT**
.. |Downloads| image:: https://img.shields.io/pypi/dm/objectpath.svg
:target: https://pypi.python.org/pypi/objectpath/
.. |Build Status| image:: https://travis-ci.org/adriank/ObjectPath.svg?branch=master
:target: https://travis-ci.org/adriank/ObjectPath
.. |Code Health| image:: https://landscape.io/github/adriank/ObjectPath/master/landscape.png
:target: https://landscape.io/github/adriank/ObjectPath/master
.. |Coverage Status| image:: https://coveralls.io/repos/adriank/ObjectPath/badge.png?branch=master
:target: https://coveralls.io/r/adriank/ObjectPath?branch=master
================================================
FILE: README.rst.original
================================================
ObjectPath
==========
|Downloads| |Build Status| |Code Health| |Coverage Status|
The agile NoSQL query language for semi-structured data
-------------------------------------------------------
**#Python #NoSQL #Javascript #JSON #nested-array-object**
ObjectPath is a query language similar to XPath or JSONPath, but much
more powerful thanks to embedded arithmetic calculations, comparison
mechanisms and built-in functions. This makes the language more like SQL
in terms of expressiveness, but it works over JSON documents rather than
relations. ObjectPath can be considered a full-featured expression
language. Besides selector mechanism there is also boolean logic, type
system and string concatenation available. On top of that, the language
implementations (Python at the moment; Javascript is in beta!) are
secure and relatively fast.
More at `ObjectPath site <http://objectpath.org/>`__
.. figure:: http://adriank.github.io/ObjectPath/img/op-colors.png
:alt: ObjectPath img
ObjectPath img
ObjectPath makes it easy to find data in big nested JSON documents. It
borrows the best parts from E4X, JSONPath, XPath and SQL. ObjectPath is
to JSON documents what XPath is to XML. Other examples to ilustrate this
kind of relationship are:
============== ==================
Scope Language
============== ==================
text documents regular expression
XML XPath
HTML CSS selectors
JSON documents ObjectPath
============== ==================
Documentation
-------------
`ObjectPath Reference <http://objectpath.org/reference.html>`__
Command line usage
------------------
.. code:: sh
$ sudo pip install objectpath
$ objectpath file.json
or
.. code:: sh
$ git clone https://github.com/adriank/ObjectPath.git
$ cd ObjectPath
$ python shell.py file.json
Python usage
------------
.. code:: sh
$ sudo pip install objectpath
$ python
>>> from objectpath import *
>>> tree=Tree({"a":1})
>>> tree.execute("$.a")
1
>>>
.. code:: sh
$ git clone https://github.com/adriank/ObjectPath.git
$ cd ObjectPath
$ python
>>> from objectpath import *
>>> tree=Tree({"a":1})
>>> tree.execute("$.a")
1
>>>
Contributing & bugs
-------------------
I appreciate all contributions and bugfix requests for ObjectPath,
however since I don’t code in Python any more, this library is not
maintained as of now. Since I can’t fully assure that code contributed
by others meets quality standards, I can’t accept PRs.
If you feel you could maintain this code, ping me. I’d be more than
happy to transfer this repo to a dedicated ObjectPath organization on
GitHub and give the ownership to someone with more time for this project
than me.
License
-------
**MIT**
.. |Downloads| image:: https://img.shields.io/pypi/dm/objectpath.svg
:target: https://pypi.python.org/pypi/objectpath/
.. |Build Status| image:: https://travis-ci.org/adriank/ObjectPath.svg?branch=master
:target: https://travis-ci.org/adriank/ObjectPath
.. |Code Health| image:: https://landscape.io/github/adriank/ObjectPath/master/landscape.png
:target: https://landscape.io/github/adriank/ObjectPath/master
.. |Coverage Status| image:: https://coveralls.io/repos/adriank/ObjectPath/badge.png?branch=master
:target: https://coveralls.io/r/adriank/ObjectPath?branch=master
================================================
FILE: VER
================================================
0.6.2
================================================
FILE: build.sh
================================================
#!/bin/sh
pandoc -f markdown -t rst -o README.rst README.md
mdTable="aaa Scope aaa Language aaa aaa---aaa---aaa aaa text documents aaa regular
expression aaa aaa XML aaa XPath aaa aaa HTML aaa CSS selectors aaa aaa JSON
documents aaa ObjectPath aaa"
rstTable="============== ==================
Scope Language
============== ==================
text documents regular expression
XML XPath
HTML CSS selectors
JSON documents ObjectPath
============== =================="
sed -i "s/\\\|/aaa/g" README.rst
perl -0777 -i.original -pe "s/$mdTable/$rstTable/g" README.rst
python setup.py build
python setup.py sdist bdist_wheel upload
================================================
FILE: objectpath/__init__.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .core.interpreter import *
from .utils import *
================================================
FILE: objectpath/core/__init__.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of ObjectPath released under MIT license.
# Copyright (C) 2010-2014 Adrian Kalbarczyk
from types import GeneratorType as generator
from itertools import chain
SELECTOR_OPS = [
"is", ">", "<", "is not", ">=", "<=", "in", "not in", ":", "and", "or", "matches", "fn"
]
# it must be list because of further concatenations
NUM_TYPES = [int, float]
try:
NUM_TYPES += [long]
except NameError:
pass
STR_TYPES = [str]
try:
STR_TYPES += [unicode]
except NameError:
pass
ITER_TYPES = [list, generator, chain]
try:
ITER_TYPES += [map, filter]
except NameError:
pass
class ProgrammingError(Exception):
pass
class ExecutionError(Exception):
pass
PY_TYPES_MAP = {
"int": "number",
"float": "number",
"str": "string",
"dict": "object",
"list": "array"
}
================================================
FILE: objectpath/core/interpreter.py
================================================
#!/usr/bin/env python
# This file is part of ObjectPath released under MIT license.
# Copyright (C) 2010-2014 Adrian Kalbarczyk
import sys, re
from .parser import parse
from objectpath.core import *
import objectpath.utils.colorify as color # pylint: disable=W0614
from objectpath.utils import flatten, filter_dict, timeutils, skip
from objectpath.utils.json_ext import py2JSON
from objectpath.core import ITER_TYPES, generator, chain
from objectpath.utils.debugger import Debugger
EPSILON = 0.0000000000000001 #this is used in float comparison
EXPR_CACHE = {}
RE_TYPE = type(re.compile(''))
# setting external modules to 0, thus enabling lazy loading. 0 ensures that Pythonic types are never matched.
# this way is efficient because if statement is fast and once loaded these variables are pointing to libraries.
ObjectId = generateID = calendar = escape = escapeDict = unescape = unescapeDict = 0
class Tree(Debugger):
_REGISTERED_FUNCTIONS = {}
@classmethod
def register_function(cls, name, func):
"""
This method is used to add custom functions not catered for by default
:param str name: The name by which the function will be referred to in the expression
:param callable func: The function
:return:
"""
cls._REGISTERED_FUNCTIONS[name] = func
def __init__(self, obj, cfg=None):
if not cfg:
cfg = {}
self.D = cfg.get("debug", False)
self.setObjectGetter(cfg.get("object_getter", None))
self.setData(obj)
self.current = self.node = None
if self.D: super(Tree, self).__init__()
def setData(self, obj):
if type(obj) in ITER_TYPES + [dict]:
self.data = obj
def setObjectGetter(self, object_getter_cb):
if callable(object_getter_cb):
self.object_getter = object_getter_cb
else:
def default_getter(obj, attr):
try:
return obj.__getattribute__(attr)
except AttributeError:
if self.D:
self.end(color.op(".") + " returning '%s'", color.bold(obj))
return obj
self.object_getter = default_getter
def compile(self, expr):
if expr in EXPR_CACHE:
return EXPR_CACHE[expr]
ret = EXPR_CACHE[expr] = parse(expr, self.D)
return ret
def execute(self, expr):
D = self.D
if D: self.start("Tree.execute")
TYPES = [str, int, float, bool, generator, chain]
try:
TYPES += [long]
except NameError:
pass
# TODO change to yield?
def exe(node):
"""
node[0] - operator name
node[1:] - params
"""
types = [
str, timeutils.datetime.time, timeutils.datetime.date,
timeutils.datetime.datetime
]
try:
types += [unicode]
except:
pass
if D: self.start("executing node %s", color.bold(self.cleanOutput(node)))
type_node = type(node)
if node is None or type_node in TYPES:
return node
elif type_node in types:
return node
elif type_node is list:
return (exe(n) for n in node)
elif type_node is dict:
ret = {}
for i in node.items():
ret[exe(i[0])] = exe(i[1])
return ret
op = node[0]
if op == "or":
if D: self.debug("%s or %s", node[1], node[2])
return exe(node[1]) or exe(node[2])
elif op == "and":
if D: self.debug("%s and %s", node[1], node[2])
return exe(node[1]) and exe(node[2])
elif op == "+":
if len(node) > 2:
fst = exe(node[1])
snd = exe(node[2])
if None in (fst, snd):
return fst or snd
typefst = type(fst)
typesnd = type(snd)
if typefst is dict:
try:
fst.update(snd)
except Exception:
if type(snd) is not dict:
raise ProgrammingError(
"Can't add value of type %s to %s" % (
color.bold(
PY_TYPES_MAP.
get(type(snd).__name__,
type(snd).__name__)
), color.bold("object")
)
)
return fst
if typefst is list and typesnd is list:
if D: self.debug("both sides are lists, returning '%s'", fst + snd)
return fst + snd
if typefst in ITER_TYPES or typesnd in ITER_TYPES:
if typefst not in ITER_TYPES:
fst = [fst]
elif typesnd not in ITER_TYPES:
snd = [snd]
if D: self.debug("at least one side is a generator and the other is an iterable, returning chain")
return chain(fst, snd)
if typefst in NUM_TYPES:
try:
return fst + snd
except Exception:
return fst + float(snd)
if typefst in STR_TYPES or typesnd in STR_TYPES:
if D: self.info("doing string comparison '%s' is '%s'", fst, snd)
if sys.version_info[0] < 3:
if typefst is unicode:
fst = fst.encode("utf-8")
if typesnd is unicode:
snd = snd.encode("utf-8")
return str(fst) + str(snd)
try:
timeType = timeutils.datetime.time
if typefst is timeType and typesnd is timeType:
return timeutils.addTimes(fst, snd)
except Exception:
pass
if D: self.debug("standard addition, returning '%s'", fst + snd)
return fst + snd
else:
return exe(node[1])
elif op == "-":
if len(node) > 2:
fst = exe(node[1])
snd = exe(node[2])
try:
return fst - snd
except Exception:
typefst = type(fst)
typesnd = type(snd)
timeType = timeutils.datetime.time
if typefst is timeType and typesnd is timeType:
return timeutils.subTimes(fst, snd)
else:
return -exe(node[1])
elif op == "*":
return exe(node[1])*exe(node[2])
elif op == "%":
return exe(node[1]) % exe(node[2])
elif op == "/":
return exe(node[1])/float(exe(node[2]))
elif op == ">":
if D: self.debug("%s > %s, %s", node[1], node[2], node[1] > node[2])
return exe(node[1]) > exe(node[2])
elif op == "<":
return exe(node[1]) < exe(node[2])
elif op == ">=":
return exe(node[1]) >= exe(node[2])
elif op == "<=":
return exe(node[1]) <= exe(node[2])
# TODO this algorithm produces 3 for 1<2<3 and should be true
# elif op in "<=>=":
# fst=exe(node[1])
# snd=exe(node[2])
# if op==">":
# return fst > snd and snd or False
# elif op=="<":
# return fst < snd and snd or False
# elif op==">=":
# return fst >= snd and snd or False
# elif op=="<=":
# return fst <= snd and snd or False
elif op == "not":
fst = exe(node[1])
if D: self.debug("doing not '%s'", fst)
return not fst
elif op == "in":
fst = exe(node[1])
snd = exe(node[2])
if D: self.debug("doing '%s' in '%s'", node[1], node[2])
if type(fst) in ITER_TYPES and type(snd) in ITER_TYPES:
return any(
x in max(fst, snd, key=len) for x in min(fst, snd, key=len)
)
return exe(node[1]) in exe(node[2])
elif op == "not in":
fst = exe(node[1])
snd = exe(node[2])
if D: self.debug("doing '%s' not in '%s'", node[1], node[2])
if type(fst) in ITER_TYPES and type(snd) in ITER_TYPES:
return not any(
x in max(fst, snd, key=len) for x in min(fst, snd, key=len)
)
return exe(node[1]) not in exe(node[2])
elif op in ("is", "is not"):
if D: self.debug("found operator '%s'", op)
# try:
fst = exe(node[1])
# except Exception as e:
# if D: self.debug("NOT ERROR! Can't execute node[1] '%s', error: '%s'. Falling back to orginal value.",node[1],str(e))
# fst=node[1]
# try:
snd = exe(node[2])
# except Exception as e:
# if D: self.debug("NOT ERROR! Can't execute node[2] '%s', error: '%s'. Falling back to orginal value.",node[2],str(e))
# snd=node[2]
if op == "is" and fst == snd:
return True
# this doesn't work for 3 is not '3'
# if op == "is not" and fst != snd:
# return True
typefst = type(fst)
typesnd = type(snd)
if D: self.debug("type fst: '%s', type snd: '%s'", typefst, typesnd)
if typefst in STR_TYPES:
if D: self.info("doing string comparison '\"%s\" is \"%s\"'", fst, snd)
ret = str(fst) == str(snd)
elif typefst is float or typesnd is float:
if D: self.info("doing float comparison '%s is %s'", fst, snd)
try:
ret = abs(float(fst) - float(snd)) < EPSILON
except:
ret = False
elif typefst is int or typesnd is int:
if D: self.info("doing integer comparison '%s is %s'", fst, snd)
try:
ret = int(fst) == int(snd)
except:
ret = False
elif typefst is list and typesnd is list:
if D: self.info("doing array comparison '%s' is '%s'", fst, snd)
ret = fst == snd
elif typefst is dict and typesnd is dict:
if D: self.info("doing object comparison '%s' is '%s'", fst, snd)
ret = fst == snd
elif fst is None or snd is None:
if fst is None and snd is None:
# this executes only for "is not"
ret = True
else:
ret = (fst or snd) is None
if D: self.info(
"doing None comparison %s is %s = %s", color.bold(fst), color.bold(snd),
color.bold(not not (fst or snd))
)
else:
if D: self.info("can't compare %s and %s. Returning False", self.cleanOutput(fst), self.cleanOutput(snd))
ret = False
# else:
# try:
# global ObjectId
# if not ObjectId:
# from bson.objectid import ObjectId
# if typefst is ObjectId or typesnd is ObjectId:
# if D: self.info("doing MongoDB objectID comparison '%s' is '%s'",fst,snd)
# ret=str(fst)==str(snd)
# else:
# if D: self.info("doing standard comparison '%s' is '%s'",fst,snd)
# ret=fst is snd
# except Exception:
# pass
if op == "is not":
if D: self.info("'is not' found. Returning %s", not ret)
return not ret
else:
if D: self.info("returning %s is %s => %s", color.bold(self.cleanOutput(fst)), color.bold(self.cleanOutput(snd)), color.bold(ret))
return ret
elif op == "re":
return re.compile(exe(node[1]))
elif op == "matches":
fst = exe(node[1])
snd = exe(node[2])
if type(fst) not in STR_TYPES+[RE_TYPE]:
raise Exception("operator " + color.bold("matches") + " expects regexp on the left. Example: 'a.*d' matches 'abcd'")
if type(snd) in ITER_TYPES:
for i in snd:
if not not re.match(fst, i):
return True
return False
else:
# regex matches string
return not not re.match(fst, snd)
# elif op=="(literal)":
# fstLetter=node[1][0]
# if fstLetter is "'":
# return node[1][1:-1]
# elif fstLetter.isdigit:
# return int(node[1])
elif op == "(root)": # this is $
return self.data
# elif op=="(node)":# this is !
# if D: self.debug("returning node %s",self.node)
# return self.node
elif op == "(current)": # this is @
if D: self.debug("returning current node: \n %s", color.bold(self.current))
return self.current
elif op == "name":
return node[1]
elif op == ".":
fst = node[1]
if type(fst) is tuple:
fst = exe(fst)
typefst = type(fst)
if D: self.debug(color.op(".") + " left is '%s'", color.bold(self.cleanOutput(fst)))
# try:
if node[2][0] == "*":
if D:
self.end(
color.op(".") + " returning '%s'",
color.bold(typefst in ITER_TYPES and fst or [fst])
)
return fst # typefst in ITER_TYPES and fst or [fst]
# except:
# pass
snd = exe(node[2])
if D: self.debug(color.op(".") + " right is '%s'", color.bold(snd))
if typefst in ITER_TYPES:
if D: self.debug(
color.op(".") + " filtering %s by %s", color.bold(self.cleanOutput(fst)),
color.bold(snd)
)
if type(snd) in ITER_TYPES:
return filter_dict(fst, list(snd))
else:
# if D: self.debug(list(fst))
return (e[snd] for e in fst if type(e) is dict and snd in e)
try:
if D: self.end(color.op(".") + " returning '%s'", fst.get(snd))
return fst.get(snd)
except Exception:
if isinstance(fst, object):
return self.object_getter(fst, snd)
if D: self.end(color.op(".") + " returning '%s'", color.bold(fst))
return fst
elif op == "..":
fst = flatten(exe(node[1]))
if node[2][0] == "*":
if D: self.debug(color.op("..") + " returning '%s'", color.bold(fst))
return fst
# reduce objects to selected attributes
snd = exe(node[2])
if D: self.debug(
color.op("..") + " finding all %s in %s", color.bold(snd),
color.bold(self.cleanOutput(fst))
)
if type(snd) in ITER_TYPES:
ret = filter_dict(fst, list(snd))
if D: self.debug(color.op("..") + " returning %s", color.bold(ret))
return ret
else:
ret = chain.from_iterable(
type(x) in ITER_TYPES and x or [x]
for x in (e[snd] for e in fst if snd in e)
)
# print list(chain(*(type(x) in ITER_TYPES and x or [x] for x in (e[snd] for e in fst if snd in e))))
if D: self.debug(color.op("..") + " returning %s", color.bold(self.cleanOutput(ret)))
return ret
elif op == "[":
len_node = len(node)
# TODO move it to tree generation phase
if len_node == 1: # empty list
if D: self.debug("returning an empty list")
return []
if len_node == 2: # list - preserved to catch possible event of leaving it as '[' operator
if D: self.debug("doing list mapping")
return [exe(x) for x in node[1]]
if len_node == 3: # selector used []
fst = exe(node[1])
# check against None
if not fst:
return fst
selector = node[2]
if D:
self.debug(
"\n found selector '%s'.\n executing on %s", color.bold(selector),
color.bold(fst)
)
selectorIsTuple = type(selector) is tuple
if selectorIsTuple and selector[0] == "[":
nodeList = []
nodeList_append = nodeList.append
for i in fst:
if D: self.debug("setting self.current to %s", color.bold(i))
self.current = i
nodeList_append(
exe((selector[0], exe(selector[1]), exe(selector[2])))
)
if D: self.debug(
"returning %s objects: %s", color.bold(len(nodeList)),
color.bold(nodeList)
)
return nodeList
if selectorIsTuple and selector[0] == "(current)":
if D:
self.warning(
color.bold("$.*[@]") + " is eqivalent to " +
color.bold("$.*") + "!"
)
return fst
if selectorIsTuple and selector[0] in SELECTOR_OPS:
if D: self.debug(
"found %s operator in selector, %s", color.bold(selector[0]),
color.bold(selector)
)
if type(fst) is dict:
fst = [fst]
# TODO move it to tree building phase
if type(selector[1]) is tuple and selector[1][0] == "name":
selector = (selector[0], selector[1][1], selector[2])
selector0 = selector[0]
selector1 = selector[1]
selector2 = selector[2]
def exeSelector(fst):
for i in fst:
if D:
self.debug("setting self.current to %s", color.bold(i))
self.debug(" s0: %s\n s1: %s\n s2: %s\n Current: %s", selector0, selector1, selector2, i)
self.current = i
if selector0 == "fn":
yield exe(selector)
# elif type(selector1) in STR_TYPES and False:
# if D: self.debug("found string %s", type(i))
# try:
# if exe((selector0,i[selector1],selector2)):
# yield i
# if D: self.debug("appended")
# if D: self.debug("discarded")
# except Exception as e:
# if D: self.debug("discarded, Exception: %s",color.bold(e))
else:
try:
# TODO optimize an event when @ is not used. exe(selector1) can be cached
if exe((selector0, exe(selector1), exe(selector2))):
yield i
if D: self.debug("appended %s", i)
elif D: self.debug("discarded")
except Exception:
if D: self.debug("discarded")
# if D and nodeList: self.debug("returning '%s' objects: '%s'", color.bold(len(nodeList)), color.bold(nodeList))
return exeSelector(fst)
self.current = fst
snd = exe(node[2])
typefst = type(fst)
if typefst in [tuple] + ITER_TYPES + STR_TYPES:
typesnd = type(snd)
# nodes[N]
if typesnd in NUM_TYPES or typesnd is str and snd.isdigit():
n = int(snd)
if D:
self.info(
"getting %sth element from '%s'", color.bold(n),
color.bold(fst)
)
if typefst in (generator, chain):
if n > 0:
return skip(fst, n)
elif n == 0:
return next(fst)
else:
fst = list(fst)
else:
try:
return fst[n]
except (IndexError, TypeError):
return None
# $.*['string']==$.string
if type(snd) in STR_TYPES:
return exe((".", fst, snd))
else:
# $.*[@.string] - bad syntax, but allowed
return snd
else:
try:
if D: self.debug("returning %s", color.bold(fst[snd]))
return fst[snd]
except KeyError:
# CHECK - is it ok to do that or should it be ProgrammingError?
if D: self.debug("returning an empty list")
return []
raise ProgrammingError(
"Wrong usage of " + color.bold("[") + " operator"
)
elif op == "fn":
# Built-in functions
fnName = node[1]
args = None
try:
args = [exe(x) for x in node[2:]]
except IndexError:
if D:
self.debug("NOT ERROR: can't map '%s' with '%s'", node[2:], exe)
# arithmetic
if fnName == "sum":
args = args[0]
if type(args) in NUM_TYPES:
return args
return sum((x for x in args if type(x) in NUM_TYPES))
elif fnName == "max":
args = args[0]
if type(args) in NUM_TYPES:
return args
return max((x for x in args if type(x) in NUM_TYPES))
elif fnName == "min":
args = args[0]
if type(args) in NUM_TYPES:
return args
return min((x for x in args if type(x) in NUM_TYPES))
elif fnName == "avg":
args = args[0]
if type(args) in NUM_TYPES:
return args
if type(args) not in ITER_TYPES:
raise Exception("Argument for avg() is not an array")
else:
args = list(args)
try:
return sum(args)/float(len(args))
except TypeError:
args = [x for x in args if type(x) in NUM_TYPES]
self.warning("Some items in array were ommited")
return sum(args)/float(len(args))
elif fnName == "round":
return round(*args)
# casting
elif fnName == "int":
return int(args[0])
elif fnName == "float":
return float(args[0])
elif fnName == "str":
return str(py2JSON(args[0]))
elif fnName in ("list", "array"):
try:
a = args[0]
except IndexError:
return []
targs = type(a)
if targs is timeutils.datetime.datetime:
return timeutils.date2list(a) + timeutils.time2list(a)
if targs is timeutils.datetime.date:
return timeutils.date2list(a)
if targs is timeutils.datetime.time:
return timeutils.time2list(a)
return list(a)
# string
elif fnName == "upper":
return args[0].upper()
elif fnName == "lower":
return args[0].lower()
elif fnName == "capitalize":
return args[0].capitalize()
elif fnName == "title":
return args[0].title()
elif fnName == "split":
return args[0].split(*args[1:])
elif fnName == "slice":
if args and type(args[1]) not in ITER_TYPES:
raise ExecutionError(
"Wrong usage of slice(STRING, ARRAY). Second argument is not an array but %s."
% color.bold(type(args[1]).__name__)
)
try:
pos = list(args[1])
if type(pos[0]) in ITER_TYPES:
if D: self.debug("run slice() for a list of slicers")
return (args[0][x[0]:x[1]] for x in pos)
return args[0][pos[0]:pos[1]]
except IndexError:
if len(args) != 2:
raise ProgrammingError(
"Wrong usage of slice(STRING, ARRAY). Provided %s argument, should be exactly 2."
% len(args)
)
elif fnName == "escape":
global escape, escapeDict
if not escape:
from objectpath.utils import escape, escapeDict
return escape(args[0], escapeDict)
elif fnName == "unescape":
global unescape, unescapeDict
if not unescape:
from objectpath.utils import unescape, unescapeDict
return unescape(args[0], unescapeDict)
elif fnName == "replace":
if sys.version_info[0] < 3 and type(args[0]) is unicode:
args[0] = args[0].encode("utf8")
return str.replace(args[0], args[1], args[2])
# TODO this should be supported by /regex/
# elif fnName=="REsub":
# return re.sub(args[1],args[2],args[0])
elif fnName == "sort":
if len(args) > 1:
key = args[1]
a = {"key": lambda x: x.get(key, 0)}
else:
a = {}
args = args[0]
if D: self.debug("doing sort on '%s'", args)
try:
return sorted(args, **a)
except TypeError:
return args
elif fnName == "reverse":
args = args[0]
try:
args.reverse()
return args
except TypeError:
return args
elif fnName == "unique":
try:
return list(set(args[0]))
except TypeError:
return args[0]
elif fnName == "map":
return chain.from_iterable(map(lambda x: exe(("fn", args[0], x)), args[1]))
elif fnName in ("count", "len"):
args = args[0]
if args in (True, False, None):
return args
if type(args) in ITER_TYPES:
return len(list(args))
return len(args)
elif fnName == "join":
try:
joiner = args[1]
except Exception:
joiner = ""
try:
return joiner.join(args[0])
except TypeError:
try:
return joiner.join(map(str, args[0]))
except Exception:
return args[0]
# time
elif fnName in ("now", "age", "time", "date", "dateTime"):
if fnName == "now":
return timeutils.now()
if fnName == "date":
return timeutils.date(args)
if fnName == "time":
return timeutils.time(args)
if fnName == "dateTime":
return timeutils.dateTime(args)
# TODO move lang to localize() entirely!
if fnName == "age":
a = {}
if len(args) > 1:
a["reference"] = args[1]
if len(args) > 2:
a["lang"] = args[2]
return list(timeutils.age(args[0], **a))
elif fnName == "toMillis":
args = args[0]
if args.utcoffset() is not None:
args = args - args.utcoffset() # pylint: disable=E1103
global calendar
if not calendar:
import calendar
return int(
calendar.timegm(args.timetuple())*1000 + args.microsecond/1000
)
elif fnName == "localize":
if type(args[0]) is timeutils.datetime.datetime:
return timeutils.UTC2local(*args)
# polygons
elif fnName == "area":
def segments(p):
p = list(map(lambda x: x[0:2], p))
return zip(p, p[1:] + [p[0]])
return 0.5*abs(
sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(args[0]))
)
# misc
elif fnName == "keys":
try:
return list(args[0].keys())
except AttributeError:
raise ExecutionError(
"Argument is not " + color.bold("object") +
" but %s in keys()" % color.bold(type(args[0]).__name__)
)
elif fnName == "values":
try:
return list(args[0].values())
except AttributeError:
raise ExecutionError(
"Argument is not " + color.bold("object") +
" but %s in values()" % color.bold(type(args[0]).__name__)
)
elif fnName == "type":
ret = type(args[0])
if ret in ITER_TYPES:
return "array"
if ret is dict:
return "object"
return ret.__name__
elif fnName in self._REGISTERED_FUNCTIONS:
return self._REGISTERED_FUNCTIONS[fnName](*args)
else:
raise ProgrammingError(
"Function " + color.bold(fnName) + " does not exist."
)
else:
return node
D = self.D
if type(expr) in STR_TYPES:
tree = self.compile(expr)
elif type(expr) not in (tuple, list, dict):
return expr
ret = exe(tree)
if D: self.end("Tree.execute with: %s", color.bold(self.cleanOutput(ret)))
return ret
def __str__(self):
return "TreeObject()"
def __repr__(self):
return self.__str__()
================================================
FILE: objectpath/core/parser.py
================================================
#!/usr/bin/env python
# This file is part of ObjectPath released under MIT license.
# Copyright (C) 2010-2014 Adrian Kalbarczyk
# Code from http://effbot.org/zone/simple-top-down-parsing.htm was used in this file.
# Licence of the code is public domain.
# Relicenced to MIT by Adrian Kalbarczyk and:
# - specialized to work with ObjectPath,
# - optimized
import sys
if sys.version_info[0] >= 3:
from io import StringIO
else:
from cStringIO import StringIO
from objectpath.core import SELECTOR_OPS, NUM_TYPES
symbol_table = {}
token = nextToken = None
# TODO optimization ('-',1) -> -1
# TODO optimization operators should be numbers
TRUE = ["true", "t"]
FALSE = ["false", "f"]
NONE = ["none", "null", "n", "nil"]
class symbol_base(object):
id = None
value = None
fst = snd = third = None
def nud(self):
raise SyntaxError("Syntax error (%r)." % self.id)
def led(self):
raise SyntaxError("Unknown operator (%r)." % self.id)
def getTree(self):
if self.id == "(name)":
val = self.value.lower()
if val in TRUE:
return True
elif val in FALSE:
return False
elif val in NONE:
return None
return (self.id[1:-1], self.value)
elif self.id == "(number)":
return self.value
elif self.id == "(literal)":
fstLetter = self.value[0]
if fstLetter in ["'", "\""]:
return self.value[1:-1]
# elif fstLetter.isdigit():
# try:
# return int(self.value)
# except:
# return float(self.value)
else:
if self.value == "True":
return True
elif self.value == "False":
return False
elif self.value == "None":
return None
ret = [self.id]
ret_append = ret.append
L = (dict, tuple, list)
for i in filter(None, [self.fst, self.snd, self.third]):
if type(i) is str:
ret_append(i)
elif type(i) in L:
t = []
t_append = t.append
if self.id == "{":
ret = {}
for j in list(self.fst.items()):
ret[j[0].getTree()] = j[1].getTree()
return ret
for j in i:
try:
t_append(j.getTree())
except Exception:
t_append(j)
if self.id in ("[", ".", ".."):
ret.append(t)
else:
ret.extend(t)
# ret_append(t)
# return (self.id,ret[1:])
else:
if type(self.fst.value) in NUM_TYPES and self.snd is None:
if self.id == "-":
return -self.fst.value
if self.id == "+":
return self.fst.value
ret_append(i.getTree())
if self.id == "{":
return {}
# if self.id == "[" and self.fst == []:
# return []
if self.id == "(":
# this will produce ("fn","fnName",arg1,arg2,...argN)
# try:
return tuple(["fn", ret[1][1]] + ret[2:])
# except:
# pass
return tuple(ret)
def __repr__(self):
if self.id == "(name)" or self.id == "(literal)":
return "(%s:%s)" % (self.id[1:-1], self.value)
out = [self.id, self.fst, self.snd, self.third]
# out=list(map(str, filter(None, out)))
return "(" + " ".join(out) + ")"
def symbol(ID, bp=0):
try:
s = symbol_table[ID]
except KeyError:
class s(symbol_base):
pass
s.__name__ = "symbol-" + ID # for debugging
s.id = ID
s.value = None
s.lbp = bp
symbol_table[ID] = s
else:
s.lbp = max(bp, s.lbp)
return s
# helpers
def infix(ID, bp):
def led(self, left):
self.fst = left
self.snd = expression(bp)
return self
symbol(ID, bp).led = led
def infix_r(ID, bp):
def led(self, left):
self.fst = left
self.snd = expression(bp - 1)
return self
symbol(ID, bp).led = led
def prefix(ID, bp):
def nud(self):
self.fst = expression(bp)
return self
symbol(ID).nud = nud
def advance(ID=None):
global token
if ID and token.id != ID:
raise SyntaxError("Expected %r, got %s" % (ID, token.id))
token = nextToken()
def method(s):
# decorator
assert issubclass(s, symbol_base)
def bind(fn):
setattr(s, fn.__name__, fn)
return bind
infix_r("or", 30)
infix_r("and", 40)
prefix("not", 50)
infix("in", 60)
infix("not", 60) # not in
infix("is", 60)
infix("matches", 60)
infix("<", 60)
infix("<=", 60)
infix(">", 60)
infix(">=", 60)
# infix(" ", 60); infix("!=", 60); infix("==", 60)
# infix("&", 90)
# infix("<<", 100); infix(">>", 100)
infix("+", 110)
infix("-", 110)
infix("*", 120)
infix("/", 120)
infix("//", 120)
infix("%", 120)
prefix("-", 130)
prefix("+", 130)
#prefix("~", 130)
# infix_r("**", 140)
symbol(".", 150)
symbol("[", 150)
symbol("{", 150)
symbol("(", 150)
# additional behavior
symbol("(name)").nud = lambda self: self
symbol("(literal)").nud = lambda self: self
symbol("(number)").nud = lambda self: self
symbol("(end)")
symbol(")")
# REGEX
infix("|", 0)
infix("^", 0)
infix("?", 0)
infix("\\", 0)
symbol("@")
@method(symbol("@"))
def nud(self): # pylint: disable=E0102
self.id = "(current)"
return self
symbol("!")
@method(symbol("!"))
def nud(self): # pylint: disable=E0102
self.id = "(node)"
return self
# RegEx
@method(symbol("/"))
def nud(self): # pylint: disable=E0102
self.id = "re"
regex = []
if token.id != "/":
self_fst_append = regex.append
while 1:
if token.id == "/":
break
if token.id in ["(name)", "(number)"]:
self_fst_append(str(token.value))
else:
self_fst_append(token.id)
advance()
self.fst = "".join(regex).replace("\\", "\\\\")
advance("/")
return self
@method(symbol("("))
def nud(self): # pylint: disable=E0102,W0613
expr = expression()
advance(")")
return expr
symbol(",")
@method(symbol("."))
def led(self, left): # pylint: disable=E0102
attr = False
if token.id == ".":
self.id = ".."
advance()
if token.id == "@":
attr = True
advance()
if token.id == "(":
advance()
self.fst = left
self.snd = []
if token.id != ")":
self_snd_append = self.snd.append
while 1:
self_snd_append(expression())
if token.id != ",":
break
advance(",")
advance(")")
return self
if token.id not in ["(name)", "*", "(literal)", "("]:
raise SyntaxError("Expected an attribute name.")
self.fst = left
if attr:
token.value = "@" + token.value
self.snd = token
advance()
return self
# handling namespaces; e.g $.a.b.c or $ss.a.b.c
# default storage is the request namespace
symbol("$")
@method(symbol("$"))
def nud(self): # pylint: disable=E0102
global token # pylint: disable=W0602
self.id = "(root)"
if token.id == ".":
self.fst = "rs"
else:
self.fst = token.value
advance()
return self
symbol("]")
@method(symbol("["))
def led(self, left): # pylint: disable=E0102
self.fst = left
self.snd = expression()
advance("]")
return self
symbol(",")
# this is for built-in functions
@method(symbol("("))
def led(self, left): # pylint: disable=E0102
# self.id="fn"
self.fst = left
self.snd = []
if token.id != ")":
self_snd_append = self.snd.append
while 1:
self_snd_append(expression())
if token.id != ",":
break
advance(",")
advance(")")
return self
symbol(":")
symbol("=")
# constants
def constant(ID):
@method(symbol(ID))
def nud(self): # pylint: disable=W0612
self.id = "(literal)"
self.value = ID
return self
constant("None")
constant("True")
constant("False")
# multitoken operators
@method(symbol("not"))
def led(self, left): # pylint: disable=E0102
if token.id != "in":
raise SyntaxError("Invalid syntax")
advance()
self.id = "not in"
self.fst = left
self.snd = expression(60)
return self
@method(symbol("is"))
def led(self, left): # pylint: disable=E0102
if token.id == "not":
advance()
self.id = "is not"
self.fst = left
self.snd = expression(60)
return self
symbol("]")
@method(symbol("["))
def nud(self): # pylint: disable=E0102
self.fst = []
if token.id != "]":
while 1:
if token.id == "]":
break
self.fst.append(expression())
if token.id not in SELECTOR_OPS + [","]:
break
advance(",")
advance("]")
return self
symbol("}")
@method(symbol("{"))
def nud(self): # pylint: disable=E0102
self.fst = {}
if token.id != "}":
while 1:
if token.id == "}":
break
key = expression()
advance(":")
self.fst[key] = expression()
if token.id != ",":
break
advance(",")
advance("}")
return self
import tokenize as tokenizer
type_map = {
tokenizer.NUMBER: "(number)",
tokenizer.STRING: "(literal)",
tokenizer.OP: "(operator)",
tokenizer.NAME: "(name)",
tokenizer.ERRORTOKEN:
"(operator)" #'$' is recognized in python tokenizer as error token!
}
# python tokenizer
def tokenize_python(program):
if sys.version_info[0] < 3:
tokens = tokenizer.generate_tokens(StringIO(program).next)
else:
tokens = tokenizer.generate_tokens(StringIO(program).__next__)
for t in tokens:
# print type_map[t[0]], t[1]
try:
# change this to output python values in correct type
yield type_map[t[0]], t[1]
except KeyError:
if t[0] in [tokenizer.NL, tokenizer.COMMENT, tokenizer.NEWLINE]:
continue
if t[0] == tokenizer.ENDMARKER:
break
else:
raise SyntaxError("Syntax error")
yield "(end)", "(end)"
def tokenize(program):
if isinstance(program, list):
source = program
else:
source = tokenize_python(program)
for ID, value in source:
if ID == "(literal)":
symbol = symbol_table[ID]
s = symbol()
s.value = value
elif ID == "(number)":
symbol = symbol_table[ID]
s = symbol()
try:
s.value = int(value)
except Exception:
s.value = float(value)
elif value == " ":
continue
else:
# name or operator
symbol = symbol_table.get(value)
if symbol:
s = symbol()
elif ID == "(name)":
symbol = symbol_table[ID]
s = symbol()
s.value = value
else:
raise SyntaxError("Unknown operator '%s', '%s'" % (ID, value))
yield s
# parser engine
def expression(rbp=0):
global token
t = token
token = nextToken()
left = t.nud()
while rbp < token.lbp:
t = token
token = nextToken()
left = t.led(left)
return left
def parse(expr, D=False):
if sys.version_info[0] < 3 and type(expr) is unicode:
expr = expr.encode("utf8")
if type(expr) is not str:
return expr
expr = expr.strip()
global token, nextToken
if sys.version_info[0] >= 3:
nextToken = tokenize(expr).__next__
else:
nextToken = tokenize(expr).next
token = nextToken()
r = expression().getTree()
if D:
print("PARSE STAGE")
print(r)
return r
================================================
FILE: objectpath/shell.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of ObjectPath released under MIT license.
# Copyright (C) 2010-2014 Adrian Kalbarczyk
import argparse
import os
import sys
try:
import readline
# this is to prevent various tools from deleting import readline
___x = readline.__doc__
except ImportError:
import pyreadline as readline
from objectpath import Tree, ITER_TYPES
from objectpath.utils.colorify import * # pylint: disable=W0614
from objectpath.utils import json_ext as json
from objectpath.utils.json_ext import printJSON
try:
import pytz
except ImportError:
if os.isatty(sys.stdin.fileno()) and sys.stdout.isatty():
print("WARNING! pytz is not installed. Localized times are not supported.")
def main():
parser = argparse.ArgumentParser(description='Command line options')
parser.add_argument(
'-u', '--url', dest='URL', help='URL containing JSON document.'
)
# parser.add_argument('-xml', dest='xml', help='[EXPERIMENTAL] Expect XML input.',action='store_true')
parser.add_argument(
'-d',
'--debug',
dest='debug',
help='Debbuging on/off.',
action='store_true'
)
parser.add_argument(
'-p',
'--profile',
dest='profile',
help='Profiling on/off.',
action='store_true'
)
parser.add_argument(
'-e',
'--expr',
dest='expr',
help='Expression/query to execute on file, print on stdout and exit.'
)
parser.add_argument('file', metavar='FILE', nargs="?", help='File name')
args = parser.parse_args()
a = {}
expr = args.expr
if not expr:
print(
bold("ObjectPath interactive shell") + "\n" + bold("ctrl+c") +
" to exit, documentation at " +
const("http://adriank.github.io/ObjectPath") + ".\n"
)
if args.debug:
a["debug"] = True
if args.profile:
try:
from guppy import hpy
except:
pass
File = args.file
# if args.xml:
# from utils.xmlextras import xml2tree
src = False
if args.URL:
if sys.version_info[0] >= 3:
from urllib.request import Request, build_opener # pylint: disable=E0611
else:
from urllib2 import Request, build_opener
request = Request(args.URL)
opener = build_opener()
request.add_header('User-Agent', 'ObjectPath/1.0 +http://objectpath.org/')
src = opener.open(request)
elif File:
src = open(File, "r")
if not src:
if not expr:
print(
"JSON document source not specified. Working with an empty object {}."
)
tree = Tree({}, a)
else:
if not expr:
sys.stdout.write(
"Loading JSON document from " + str(args.URL or File) + "..."
)
sys.stdout.flush()
# if args.xml:
# tree=Tree(json.loads(json.dumps(xml2tree(src))),a)
# else:
tree = Tree(json.load(src), a)
if not expr: print(" " + bold("done") + ".")
if expr:
if args.profile:
import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()
try:
ret = tree.execute(expr)
except Exception as e:
print(e.__class__.__name__ + ": " + str(e))
exit(1)
if type(ret) in ITER_TYPES:
ret = list(ret)
print(json.dumps(ret))
if args.profile:
pr.disable()
s = StringIO.StringIO()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())
return
try:
while True:
limitResult = 5
try:
if sys.version >= '3':
r = input(">>> ")
else:
r = raw_input(">>> ")
if r.startswith("all"):
limitResult = -1
r = tree.execute(r[3:].strip())
else:
r = tree.execute(r)
# python 3 raises error here - unicode is not a proper type there
try:
if type(r) is unicode:
r = r.encode("utf8")
except NameError:
pass
print(printJSON(r, length=limitResult))
if args.profile:
h = hpy()
print(h.heap())
except Exception as e:
print(e)
except KeyboardInterrupt:
pass
# new line at the end forces command prompt to apear at left
print(bold("\nbye!"))
if __name__ == "__main__":
main()
================================================
FILE: objectpath/utils/__init__.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of ObjectPath released under MIT license.
# Copyright (C) 2010-2014 Adrian Kalbarczyk
from itertools import islice
from xml.sax.saxutils import escape, unescape
from objectpath.core import NUM_TYPES, generator, chain, ITER_TYPES
escape = escape
unescape = unescape
unescapeDict = {"'": "'", """: "\""}
escapeDict = {"'": "'", "\"": """}
# islice=islice is an optimization
def skip(iterable, n, islice=islice):
try:
return next(islice(iterable, n, None))
except StopIteration:
return None
# raise IndexError("generator index out of range")
def filter_dict(iterable, keys):
"""
filters keys of each element of iterable
$.(a,b) returns all objects from array that have at least one of the keys:
[1,"aa",{"a":2,"c":3},{"c":3},{"a":1,"b":2}].(a,b) -> [{"a":2},{"a":1,"b":2}]
"""
if type(keys) is not list:
keys = [keys]
for i in iterable:
try:
d = {}
for a in keys:
try:
d[a] = i[a]
except KeyError:
pass
if d != {}:
yield d
except Exception:
pass
def flatten(fragment, skip=False):
def rec(frg):
typefrg = type(frg)
if typefrg in ITER_TYPES:
for i in frg:
for j in rec(i):
yield j
elif typefrg is dict:
yield frg
for i in frg.items():
for j in rec(i[1]):
yield j
g = rec(fragment)
if skip:
for i in xrange(skip):
g.next()
for i in g:
yield i
================================================
FILE: objectpath/utils/colorify.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def color(c, s):
return '\033[%sm%s\033[0m' % (c, s)
def bold(s):
return color(1, s)
def op(s):
return color(32, bold(s))
def const(s):
return color(36, bold(s))
def string(s):
return color(33, bold(s))
================================================
FILE: objectpath/utils/debugger.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# move dbgMap outside
import inspect
from objectpath.core import ITER_TYPES
class Debugger(object):
dbg = None
_debugStr = None
dbgfn = None
level = 40
CUT_AFTER = 100
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = START = END = 20
DEBUG = 10
doDebug = False
def __init__(self):
self._debugStr = []
self.dbgfn = self.consolelog
self.dbgMap = {
"debug": self.DEBUG,
"info": self.INFO,
"start": self.START,
"end": self.END,
"warning": self.WARNING,
"error": self.ERROR,
"critical": self.CRITICAL
}
try:
self.doDebug = True
self.level = self.dbgMap["debug"]
self.info("All strings will be cut to %s chatacters." % self.CUT_AFTER)
except (KeyError, TypeError):
pass
def debug(self, *s):
if self.dbgfn and self.level <= self.DEBUG:
self.dbgfn("DEBUG", s)
def info(self, *s):
if self.dbgfn and self.level <= self.INFO:
self.dbgfn("INFO", s)
def start(self, *s):
if self.dbgfn and self.level <= self.INFO:
self.dbgfn("START", s)
def end(self, *s):
if self.dbgfn and self.level <= self.INFO:
self.dbgfn("END", s)
def warning(self, *s):
if self.dbgfn and self.level <= self.WARNING:
self.dbgfn("WARNING", s)
def error(self, *s):
if self.dbgfn and self.level <= self.ERROR:
self.dbgfn("ERROR", s)
def critical(self, *s):
if self.dbgfn and self.level <= self.CRITICAL:
self.dbgfn("CRITICAL", s)
def lineno(self):
"""Returns the current line number in our program."""
return inspect.currentframe().f_back.f_back.f_back.f_lineno
def cleanOutput(self, o):
toOutput = o
if type(toOutput) in ITER_TYPES:
toOutput = list(toOutput)
if type(toOutput) is tuple:
return tuple(map(lambda x: type(x) in ITER_TYPES and list(x) or x, toOutput))
return toOutput
def consolelog(self, lvl, s):
def f(x):
try:
if type(x) is unicode:
x = x.encode("utf8")
except NameError:
pass
if self.CUT_AFTER and type(x) is dict:
s = []
for i in x.items():
s.append("'%s': %s" % (i[0], repr(i[1])[:self.CUT_AFTER]))
if len(s[-1]) > self.CUT_AFTER:
s.append("...\033[0m")
return "{\n\t" + ",\n\t".join(s) + "\n}"
s = str(x).replace("\n", "").replace("\t", "").replace("u'", "'")
if self.CUT_AFTER and len(s) > self.CUT_AFTER:
return s[:self.CUT_AFTER] + "...\033[0m"
else:
return x
if len(s) > 1:
v = tuple(map(f, s[1:]))
self._debugStr.append((lvl, s[0] % v))
print(
lvl + "@" + str(self.lineno()) + " " + (s[0] % v).replace("u'", "'")
)
else:
self._debugStr.append((lvl, s[0]))
print(lvl + "@" + str(self.lineno()) + " " + f(s[0]))
================================================
FILE: objectpath/utils/json_ext.py
================================================
#!/usr/bin/env python
try:
import simplejson as json
except ImportError:
try:
import json
except ImportError:
raise Exception("JSONNotFound")
try:
import ujson as json_fast
except ImportError:
json_fast = json
from types import GeneratorType as generator
from objectpath.core import ITER_TYPES, STR_TYPES, NUM_TYPES
from objectpath.utils.colorify import * # pylint: disable=W0614
load = json_fast.load
def loads(s):
if s.find("u'") != -1:
s = s.replace("u'", "'")
s = s.replace("'", '"')
try:
return json_fast.loads(s) # ,object_hook=object_hook)
except ValueError as e:
raise Exception(str(e) + " " + s)
def default(obj):
if isinstance(obj, generator):
return list(obj)
def dumps(s, default=default, indent=None):
return json.dumps(s, default=default, indent=indent, separators=(',', ':'))
dump = json.dump
def py2JSON(o):
if o is True:
return 'true'
if o is False:
return 'false'
if o is None:
return 'null'
if type(o) in NUM_TYPES:
return o
# TODO - check if that is correct
if type(o) is tuple:
return list(o)
elif type(o) in ITER_TYPES + [list, str]:
return o
try:
return str(o)
except UnicodeEncodeError:
return o.encode("utf8")
except Exception:
return o
LAST_LIST = None
def printJSON(o, length=5, depth=5):
spaces = 2
def plus():
currIndent[0] += 1
def minus():
currIndent[0] -= 1
def out(s):
try:
s = str(s)
except Exception:
pass
if not ret:
ret.append(s)
elif ret[-1][-1] == "\n":
ret.append(currIndent[0]*spaces*" " + s)
else:
ret.append(s)
def rec(o):
if type(o) in ITER_TYPES:
o = list(o)
if currDepth[0] >= depth:
out("<array of " + str(len(o)) + " items>")
return
out("[")
if len(o) > 0:
if len(o) > 1:
out("\n")
plus()
for i in o[0:length]:
rec(i)
out(",\n")
if length == -1:
rec(o[-1])
out(",\n")
if length != -1 and len(o) > length:
out("... (" + str(len(o) - length) + " more items)\n")
else:
ret.pop()
if len(o) > 1:
out("\n")
if len(o) > 1:
minus()
out("]")
elif type(o) is dict:
currDepth[0] += 1
if currDepth[0] > depth:
out("{...}")
return
keys = o.keys()
out("{")
if len(keys) > 0:
if len(keys) > 1:
plus()
out("\n")
for k in o.keys():
out(string('"' + str(k) + '"') + ": ")
rec(o[k])
out(",\n")
ret.pop()
if len(keys) > 1:
minus()
out("\n")
out("}")
else:
if type(o) in NUM_TYPES:
out(const(o))
elif o in [None, False, True]:
out(const(py2JSON(o)))
elif type(o) in STR_TYPES:
out(string('"' + o + '"'))
else:
out(string(o))
currDepth[0] -= 1
currIndent = [0]
currDepth = [0]
ret = []
rec(o)
currIndent[0] = 0
currDepth[0] = 0
return "".join(ret)
================================================
FILE: objectpath/utils/timeutils.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of ObjectPath released under MIT license.
# Copyright (C) 2008-2010 Adrian Kalbarczyk
import datetime
import sys, os
try:
import pytz
TIMEZONE_CACHE = {"UTC": pytz.utc}
except ImportError:
pass
from objectpath.core import STR_TYPES
HOURS_IN_DAY = 24
now = datetime.datetime.now
def round9_10(n):
i = int(n)
if n - i > 0.9:
return i + 1
return i
# TODO its 31 minuta, should be 31 minut - probably done
def age(date, reference=None, lang="en"):
if reference is None:
reference = now()
td = reference - date #TimeDelta
days = float(td.days)
langIsPL = lang == "pl"
if days:
years = round9_10(days/356)
if years:
if langIsPL:
return (years, years == 1 and "rok" or years < 5 and "lata" or "lat")
else:
return (years, years == 1 and "year" or "years")
months = round9_10(days/30)
if months:
if langIsPL:
return (
months, months == 1 and "miesiąc" or 1 < months < 5 and "miesiące"
or "miesięcy"
)
else:
return (months, months == 1 and "month" or "months")
weeks = round9_10(days/7)
if weeks:
if langIsPL:
return (
weeks, weeks == 1 and "tydzień"
or weeks % 10 in [0, 1, 5, 6, 7, 8, 9] and "tygodni" or "tygodnie"
)
else:
return (weeks, weeks == 1 and "week" or "weeks")
days = int(days)
if langIsPL:
return (days, days == 1 and "dzień" or "dni")
else:
return (days, days == 1 and "day" or "days")
seconds = float(td.seconds)
if seconds is not None:
hours = round9_10(seconds/3600)
if hours:
if langIsPL:
return (
hours, hours == 1 and "godzina" or 1 < hours < 5 and "godziny"
or "godzin"
)
else:
return (hours, hours == 1 and "hour" or "hours")
minutes = round9_10(seconds/60)
if minutes:
if langIsPL:
return (
minutes, minutes == 1 and "minuta" or 1 < minutes < 5 and "minuty"
or "minut"
)
else:
return (minutes, minutes == 1 and "minute" or "minutes")
seconds = int(seconds)
if langIsPL:
return (
seconds, seconds == 1 and "sekunda" or 1 < seconds < 5 and "sekundy"
or "sekund"
)
else:
return (seconds, seconds == 1 and "second" or "seconds")
# return (0,"seconds")
def date(d):
if d:
d = d[0]
t = type(d)
if t is datetime.datetime:
return datetime.date(d.year, d.month, d.day)
if t in (tuple, list):
return datetime.date(*d)
return datetime.date.today()
def date2list(d):
return [d.year, d.month, d.day]
def time(d):
if not d or not d[0]:
d = now()
else:
d = d[0]
t = type(d)
if t in (tuple, list):
return datetime.time(*d)
return datetime.time(d.hour, d.minute, d.second, d.microsecond)
def time2list(t):
return [t.hour, t.minute, t.second, t.microsecond]
def addTimes(fst, snd):
l1 = time2list(fst)
l2 = time2list(snd)
t = [l1[0] + l2[0], l1[1] + l2[1], l1[2] + l2[2], l1[3] + l2[3]]
t2 = []
one = 0
ms = t[3]
if ms >= 1000000:
t2.append(ms - 1000000)
one = 1
else:
t2.append(ms)
for i in (t[2], t[1]):
i = i + one
one = 0
if i >= 60:
t2.append(i - 60)
one = 1
# elif i==60:
# t2.append(0)
# one=1
else:
t2.append(i)
hour = t[0] + one
if hour >= HOURS_IN_DAY:
t2.append(hour - HOURS_IN_DAY)
else:
t2.append(hour)
return datetime.time(*reversed(t2))
def subTimes(fst, snd):
l1 = time2list(fst)
l2 = time2list(snd)
t = [l1[0] - l2[0], l1[1] - l2[1], l1[2] - l2[2], l1[3] - l2[3]]
t2 = []
one = 0
ms = t[3]
if ms < 0:
t2.append(1000000 + ms)
one = 1
else:
t2.append(ms)
for i in (t[2], t[1]):
i = i - one
one = 0
if i >= 0:
t2.append(i)
else:
t2.append(60 + i)
one = 1
hour = t[0] - one
if hour < 0:
t2.append(HOURS_IN_DAY + hour)
else:
t2.append(hour)
return datetime.time(*reversed(t2))
def dateTime(arg):
"""
d may be:
- datetime()
- [y,m,d,h[,m[,ms]]]
- [date(),time()]
- [[y,m,d],[h,m,s,ms]]
and permutations of above
"""
l = len(arg)
if l == 1:
dt = arg[0]
typed = type(dt)
if typed is datetime.datetime:
return dt
if typed in (tuple, list) and len(dt) in [5, 6, 7]:
return datetime.datetime(*dt)
if l == 2:
date = time = None
typeArg0 = type(arg[0])
typeArg1 = type(arg[1])
if typeArg0 in STR_TYPES:
return datetime.datetime.strptime(arg[0], arg[1])
if typeArg0 is datetime.date:
d = arg[0]
date = [d.year, d.month, d.day]
if typeArg0 in (tuple, list):
date = arg[0]
if typeArg1 is datetime.time:
t = arg[1]
time = [t.hour, t.minute, t.second, t.microsecond]
if typeArg1 in (tuple, list):
time = arg[1]
return datetime.datetime(*date + time)
# dt - dateTime, tzName is e.g. 'Europe/Warsaw'
def UTC2local(dt, tzName="UTC"):
try:
if tzName in TIMEZONE_CACHE:
tz = TIMEZONE_CACHE[tzName]
else:
tz = TIMEZONE_CACHE[tzName] = pytz.timezone(tzName)
return TIMEZONE_CACHE["UTC"].localize(dt).astimezone(tz)
except Exception:
return dt
================================================
FILE: requirements.txt
================================================
pytz
================================================
FILE: setup.cfg
================================================
[bdist_wheel]
universal = 1
[yapf]
# split_all_comma_separated_values=True
no_spaces_around_selected_binary_operators="*,/"
indent_width=2
dedent_closing_brackets=True
coalesce_brackets=True
blank_lines_around_top_level_definition=1
continuation_indent_width=2
================================================
FILE: setup.py
================================================
import os
from setuptools import setup
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
long_description = (read('README.rst') + '\n' + 'Download\n' '********\n')
setup(
name='objectpath',
version=read('VER').strip(),
description='The agile query language for semi-structured data. #JSON',
long_description=long_description,
url='http://adriank.github.io/ObjectPath',
author='Adrian Kalbarczyk',
author_email='adrian.kalbarczyk@gmail.com',
license='MIT License',
packages=['objectpath', 'objectpath.utils', 'objectpath.core'],
# package_dir={'': 'objectpath'},
keywords="query, tree, JSON, nested structures",
classifiers=[
"Development Status :: 6 - Mature", "Intended Audience :: Developers",
"Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Topic :: Software Development :: Libraries :: Python Modules"
],
install_requires=[
'pytz',
],
zip_safe=True,
entry_points={'console_scripts': ['objectpath = objectpath.shell:main']},
test_suite="tests"
)
================================================
FILE: shell.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from objectpath import shell
shell.main()
================================================
FILE: testObjectPath.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tests import doTests
# starts all test suites
doTests()
================================================
FILE: tests/__init__.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#unit tests for ACR functions
from .test_ObjectPath import op_test
from .test_utils import utils_test
import unittest
def doTests():
print('Started ObjectPath Python implementation testing.\n')
unittest.TextTestRunner(verbosity=2).run(op_test)
unittest.TextTestRunner(verbosity=2).run(utils_test)
================================================
FILE: tests/test_ObjectPath.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from objectpath.core.interpreter import *
from objectpath.core import ProgrammingError, ExecutionError
from random import randint, choice
#from bson.objectid import ObjectId
import sys, unittest, os
sys.setrecursionlimit(20000)
object1 = {
"__lang__": "en",
"test": {
"_id": 1,
"name": "aaa",
"o": {
"_id": 2
},
"l": [{
"_id": 3,
"aaa": "ddd",
"false": 2
}, {
"_id": 4
}]
}
}
object2 = {
"store": {
"book": [{
"id":1,
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}],
"bicycle": {
"color": "red",
"price": 19.95
},
"k": [{
"_id": 4
}]
}
}
object3 = {
"item_1": {
"value": "foo",
"x": 5.6,
"y": 9
},
"item_2": {
"value": "bar",
"x": 5.6,
"y": 9.891
},
"item_3": {
"value": "foobar",
"x": 5.6,
"y": 9.8
}
}
object4 = {
"root": {
"response": {
"200": {
"value": 5,
},
"201": {
"value": 4,
},
"404": {
"value": 0,
}
}
}
}
tree1 = Tree(object1)
tree2 = Tree(object2)
tree3 = Tree(object3)
tree4 = Tree(object4)
def execute_raw(expr):
return tree1.execute(expr)
TYPES = [generator, chain]
if sys.version_info.major > 2:
TYPES += [map]
TYPES = tuple(TYPES)
def execute(expr):
r = tree1.execute(expr)
if isinstance(r, TYPES):
return list(r)
else:
return r
def execute2(expr):
r = tree2.execute(expr)
if isinstance(r, TYPES):
return list(r)
else:
return r
def execute3(expr):
r = tree3.execute(expr)
if isinstance(r, TYPES):
return list(r)
else:
return r
def execute4(expr):
r = tree4.execute(expr)
if isinstance(r, TYPES):
return list(r)
else:
return r
class ObjectPath(unittest.TestCase):
def test_simple_types(self):
self.assertEqual(execute("null"), None)
self.assertEqual(execute("true"), True)
self.assertEqual(execute("false"), False)
self.assertEqual(execute("''"), "")
self.assertEqual(execute('""'), "")
self.assertEqual(execute("2"), 2)
self.assertEqual(execute("2.0"), 2.0)
def test_arrays(self):
self.assertEqual(execute("[]"), [])
self.assertEqual(list(execute("[1,2,3]")), [1, 2, 3])
self.assertEqual(
list(execute("[false,null,true,'',\"\",2,2.0,{}]")),
[False, None, True, '', "", 2, 2.0, {}]
)
def test_objects(self):
self.assertEqual(execute("{}"), {})
self.assertEqual(
execute("{a:1,b:false,c:'string'}"), {
"a": 1,
"b": False,
"c": 'string'
}
)
self.assertEqual(
execute("{'a':1,'b':false,'c':'string'}"), {
"a": 1,
"b": False,
"c": 'string'
}
)
def test_arithm_add(self):
self.assertEqual(execute("2+3"), 5)
self.assertEqual(execute("2+3+4"), 9)
self.assertEqual(execute("++3"), 3)
# null is treated as neutral value
self.assertEqual(execute("null+3"), 3)
self.assertEqual(execute("3+null"), 3)
def test_arithm_sub(self):
self.assertEqual(execute("-1"), -1)
self.assertEqual(execute("2-3"), 2 - 3)
self.assertEqual(execute("2.2-3.4"), 2.2 - 3.4)
self.assertEqual(execute("-+-3"), 3)
self.assertEqual(execute("+-+3"), -3)
def test_arithm_mul(self):
self.assertEqual(execute("2*3*5*6"), 180)
def test_arithm_mod(self):
self.assertEqual(execute("2%3"), 2.0 % 3)
self.assertEqual(execute("2.0%3"), 2.0 % 3)
self.assertEqual(execute("float(2)%3"), float(2) % 3)
def test_arithm_div(self):
self.assertEqual(execute("2/3"), 2.0/3)
self.assertEqual(execute("2.0/3"), 2.0/3)
self.assertEqual(execute("float(2)/3"), float(2)/3)
def test_arithm_group(self):
self.assertEqual(execute("2-3+4+5-7"), 2 - 3 + 4 + 5 - 7)
self.assertEqual(execute("33*2/5-2"), 33*2/5.0 - 2)
self.assertEqual(execute("33-4*5+2/6"), 33 - 4*5 + 2/6.0)
#self.assertEqual(execute("2//3//4//5"), ('//', ('//', ('//', 2, 3), 4), 5))
def test_arithm_parentheses(self):
self.assertEqual(execute("+6"), 6)
self.assertEqual(execute("2+2*2"), 6)
self.assertEqual(execute("2+(2*2)"), 6)
self.assertEqual(execute("(2+2)*2"), 8)
self.assertEqual(execute("(33-4)*5+2/6"), (33 - 4)*5 + 2/6.0)
self.assertEqual(execute("2/3/(4/5)*6"), 2/3.0/(4/5.0)*6)
self.assertEqual(execute("((2+4))+6"), ((2 + 4)) + 6)
def test_logic_negatives(self):
self.assertEqual(execute("not false"), True)
self.assertEqual(execute("not null"), True)
self.assertEqual(execute("not 0"), True)
self.assertEqual(execute("not 0.0"), True)
self.assertEqual(execute("not ''"), True)
self.assertEqual(execute("not []"), True)
self.assertEqual(execute("not {}"), True)
def test_logic_not(self):
self.assertEqual(execute("not false"), True)
self.assertEqual(execute("not not not false"), True)
def test_logic_or(self):
self.assertEqual(execute("1 or 2"), 1)
self.assertEqual(execute("0 or 2"), 2)
self.assertEqual(execute("'a' or 0 or 3"), 'a')
self.assertEqual(
execute("null or false or 0 or 0.0 or '' or [] or {}"), {}
)
def test_logic_and(self):
self.assertEqual(execute("1 and 2"), 2)
self.assertEqual(execute("0 and 2"), 0)
self.assertEqual(execute("'a' and false and 3"), False)
self.assertEqual(
execute("true and 1 and 1.0 and 'foo' and [1] and {a:1}"), {"a": 1}
)
def test_comparison_regex(self):
self.assertIsInstance(execute("/aaa/"), type(re.compile("")))
self.assertEqual(execute("/.*aaa/ matches 'xxxaaaadddd'"), True)
self.assertEqual(execute("'.*aaa' matches 'xxxaaaadddd'"), True)
self.assertEqual(execute("'.*aaa' matches ['xxxaaaadddd', 'xxx']"), True)
def test_comparison_is(self):
self.assertEqual(execute("2 is 2"), True)
self.assertEqual(execute("'2' is 2"), True)
self.assertEqual(execute("2 is '2'"), True)
self.assertEqual(execute("2 is 2.0"), True)
self.assertEqual(execute("0.1+0.2 is 0.3"), True)
self.assertEqual(execute("[] is []"), True)
self.assertEqual(execute("[1] is [1]"), True)
self.assertEqual(execute("{} is {}"), True)
self.assertEqual(execute("{} is []"), False)
self.assertEqual(execute("None is 'aaa'"), False)
self.assertEqual(execute("None is None"), True)
self.assertEqual(execute("{'aaa':1} is {'aaa':1}"), True)
#oid=ObjectId()
#self.assertEqual(execute("ObjectID('"+str(oid)+"') is '"+str(oid)+"'"), True)
def test_comparison_isnot(self):
self.assertEqual(execute("None is not None"), False)
self.assertEqual(execute("None is not 'aaa'"), True)
self.assertEqual(execute("{} is not []"), True)
self.assertEqual(execute("3 is not 6"), True)
self.assertEqual(execute("3 is not '3'"), False)
self.assertEqual(execute("[] is not [1]"), True)
self.assertEqual(execute("[] is not []"), False)
self.assertEqual(execute("{'aaa':2} is not {'bbb':2}"), True)
self.assertEqual(execute("{} is not {}"), False)
def test_membership_in(self):
self.assertEqual(execute("4 in [6,4,3]"), True)
self.assertEqual(execute("4 in {4:true}"), True)
self.assertEqual(execute("[2,3] in [6,4,3]"), True)
def test_membership_notin(self):
self.assertEqual(execute("4 not in []"), True)
self.assertEqual(execute("1 not in {'232':2}"), True)
self.assertEqual(execute("[2,5] not in [6,4,3]"), True)
def test_complex(self):
self.assertEqual(execute("23 is not 56 or 25 is 57"), True)
self.assertEqual(execute("2+3/4-6*7>0 or 10 is not 11 and 14"), 14)
def test_comparison_lt(self):
self.assertEqual(execute("2<3"), True)
self.assertEqual(execute("3<3"), False)
self.assertEqual(execute("2<=2"), True)
self.assertEqual(execute("2<=1"), False)
def test_comparison_gt(self):
self.assertEqual(execute("5>4"), True)
self.assertEqual(execute("5>5"), False)
self.assertEqual(execute("5>=5"), True)
def test_concatenation(self):
self.assertEqual(execute("'a'+'b'+\"c\""), 'abc')
self.assertEqual(execute("'5'+5"), '55')
self.assertEqual(execute("5+'5'"), 10)
self.assertEqual(list(execute("[1,2,4] + [3,5]")), [1, 2, 4, 3, 5])
self.assertEqual(
execute('{"a":1,"b":2} + {"a":2,"c":3}'), {
"a": 2,
"b": 2,
"c": 3
}
)
self.assertRaises(
ProgrammingError, lambda: execute('{"a":1,"b":2} + "sss"')
)
def test_builtin_casting(self):
self.assertEqual(execute("str('foo')"), 'foo')
self.assertEqual(execute("str(1)"), '1')
self.assertEqual(execute("str(1.0)"), '1.0')
self.assertEqual(execute("str(1 is 1)"), 'true')
self.assertEqual(execute("int(1)"), 1)
self.assertEqual(execute("int(1.0)"), 1)
self.assertEqual(execute("int('1')"), 1)
#Python can't handle that
#self.assertEqual(execute("int('1.0')"), 1)
self.assertEqual(execute("float(1.0)"), 1.0)
self.assertEqual(execute("float(1)"), 1.0)
self.assertEqual(execute("float('1')"), 1.0)
self.assertEqual(execute("float('1.0')"), 1.0)
self.assertEqual(execute("array()"), [])
self.assertEqual(execute("array([])"), [])
self.assertEqual(execute("array('abc')"), ['a', 'b', 'c'])
self.assertEqual(
execute("array(dateTime([2011,4,8,12,0]))"), [2011, 4, 8, 12, 0, 0, 0]
)
self.assertEqual(execute("array(date([2011,4,8]))"), [2011, 4, 8])
self.assertEqual(execute("array(time([12,12,30]))"), [12, 12, 30, 0])
def test_builtin_arithmetic(self):
self.assertEqual(execute("sum([1,2,3,4])"), sum([1, 2, 3, 4]))
self.assertEqual(execute("sum([2,3,4,'333',[]])"), 9)
self.assertEqual(execute("sum(1)"), 1)
self.assertEqual(execute("min([1,2,3,4])"), min([1, 2, 3, 4]))
self.assertEqual(execute("min([2,3,4,'333',[]])"), 2)
self.assertEqual(execute("min(1)"), 1)
self.assertEqual(execute("max([1,2,3,4])"), max([1, 2, 3, 4]))
self.assertEqual(execute("max([2,3,4,'333',[]])"), 4)
self.assertEqual(execute("max(1)"), 1)
self.assertEqual(execute("avg([1,2,3,4])"), 2.5)
self.assertEqual(execute("avg([1,3,3,1])"), 2.0)
self.assertEqual(execute("avg([1.1,1.3,1.3,1.1])"), 1.2000000000000002)
self.assertEqual(execute("avg([2,3,4,'333',[]])"), 3)
self.assertEqual(execute("avg(1)"), 1)
self.assertEqual(execute("round(2/3)"), round(2.0/3))
self.assertEqual(execute("round(2/3,3)"), round(2.0/3, 3))
# edge cases
self.assertEqual(execute("avg(1)"), 1)
# should ommit 'sss'
self.assertEqual(execute("avg([1,'sss',3,3,1])"), 2.0)
def test_builtin_string(self):
self.assertEqual(execute("replace('foobar','oob','baz')"), 'fbazar')
self.assertEqual(execute("""escape('<')"""), "&lt;")
self.assertEqual(execute("""escape('<"&>')"""), "<"&>")
self.assertEqual(execute("""unescape('<"&>')"""), "<\"&>")
self.assertEqual(execute("upper('aaa')"), "AAA")
self.assertEqual(execute("lower('AAA')"), "aaa")
self.assertEqual(execute("title('AAA aaa')"), "Aaa Aaa")
self.assertEqual(execute("capitalize('AAA Aaa')"), "Aaa aaa")
self.assertEqual(execute("split('aaa aaa')"), ["aaa", "aaa"])
self.assertEqual(execute("split('aaaxaaa','x')"), ["aaa", "aaa"])
self.assertEqual(execute("join(['aaą','aaę'],'ć')"), "aaąćaaę")
self.assertEqual(execute("join(['aaa','aaa'])"), "aaaaaa")
self.assertEqual(execute("join(['aaa','aaa',3,55])"), "aaaaaa355")
self.assertEqual(execute('slice("Hello world!", [6, 11])'), "world")
self.assertEqual(execute('slice("Hello world!", [6, -1])'), "world")
self.assertEqual(
execute('slice("Hello world!", [[0,5], [6, 11]])'), ["Hello", "world"]
)
self.assertRaises(ProgrammingError, lambda: execute('slice()'))
self.assertRaises(ExecutionError, lambda: execute('slice("", {})'))
self.assertEqual(execute('map(upper, ["a", "b", "c"])'), ["A", "B", "C"])
def test_builtin_arrays(self):
self.assertEqual(execute("sort([1,2,3,4]+[2,4])"), [1, 2, 2, 3, 4, 4])
self.assertEqual(execute("sort($.._id)"), [1, 2, 3, 4])
self.assertEqual(
execute("sort($..l.*, _id)"), [{
'_id': 3,
'aaa': 'ddd',
'false': 2
}, {
'_id': 4
}]
)
self.assertEqual(execute("reverse([1,2,3,4]+[2,4])"), [4, 2, 4, 3, 2, 1])
self.assertEqual(execute("reverse(sort($.._id))"), [4, 3, 2, 1])
self.assertEqual(execute("len([1,2,3,4]+[2,4])"), 6)
self.assertEqual(execute("unique([1,1,3,3])"), [1, 3])
# edge cases
self.assertEqual(execute("len(True)"), True)
self.assertEqual(execute("len('aaa')"), 3)
def test_builtin_time(self):
import datetime
self.assertIsInstance(execute("now()"), datetime.datetime)
self.assertIsInstance(execute("date()"), datetime.date)
self.assertIsInstance(execute("date(now())"), datetime.date)
self.assertIsInstance(execute("date([2001,12,30])"), datetime.date)
self.assertIsInstance(execute("time()"), datetime.time)
self.assertIsInstance(execute("time(now())"), datetime.time)
self.assertIsInstance(execute("time([12,23])"), datetime.time)
self.assertIsInstance(execute("time([12,23,21,777777])"), datetime.time)
self.assertIsInstance(execute("dateTime(now())"), datetime.datetime)
self.assertIsInstance(
execute("dateTime([2001,12,30,12,23])"), datetime.datetime
)
self.assertIsInstance(
execute("dateTime([2001,12,30,12,23,21,777777])"), datetime.datetime
)
self.assertIsInstance(execute('dateTime("1980-05-11 04:22:33", "%Y-%m-%d %H:%M:%S")'), datetime.datetime)
self.assertEqual(str(execute('dateTime("1980-05-11 04:22:33", "%Y-%m-%d %H:%M:%S")')), "1980-05-11 04:22:33")
self.assertEqual(
execute("toMillis(dateTime([2001,12,30,12,23,21,777777]))"),
1009715001777
)
self.assertIsInstance(
execute("dateTime(date(),time())"), datetime.datetime
)
self.assertIsInstance(
execute("dateTime(date(),[12,23])"), datetime.datetime
)
self.assertIsInstance(
execute("dateTime(date(),[12,23,21,777777])"), datetime.datetime
)
self.assertIsInstance(
execute("dateTime([2001,12,30],time())"), datetime.datetime
)
self.assertEqual(
execute("array(time([12,30])-time([8,00]))"), [4, 30, 0, 0]
)
self.assertEqual(
execute("array(time([12,12,12,12])-time([8,8,8,8]))"), [4, 4, 4, 4]
)
self.assertEqual(
execute("array(time([12,12,12,12])-time([1,2,3,4]))"), [11, 10, 9, 8]
)
self.assertEqual(
execute("array(time([12,00])-time([1,10]))"), [10, 50, 0, 0]
)
self.assertEqual(
execute("array(time([1,00])-time([1,10]))"), [23, 50, 0, 0]
)
self.assertEqual(
execute("array(time([0,00])-time([0,0,0,1]))"), [23, 59, 59, 999999]
)
self.assertEqual(
execute("array(time([0,0])+time([1,1,1,1]))"), [1, 1, 1, 1]
)
self.assertEqual(
execute("array(time([0,0])+time([1,2,3,4]))"), [1, 2, 3, 4]
)
self.assertEqual(
execute("array(time([23,59,59,999999])+time([0,0,0,1]))"), [0, 0, 0, 0]
)
# age tests
self.assertEqual(execute("age(now())"), [0, "seconds"])
self.assertEqual(
execute("age(dateTime([2000,1,1,1,1]),dateTime([2001,1,1,1,1]))"),
[1, "year"]
)
self.assertEqual(
execute("age(dateTime([2000,1,1,1,1]),dateTime([2000,2,1,1,1]))"),
[1, "month"]
)
self.assertEqual(
execute("age(dateTime([2000,1,1,1,1]),dateTime([2000,1,2,1,1]))"),
[1, "day"]
)
self.assertEqual(
execute("age(dateTime([2000,1,1,1,1]),dateTime([2000,1,1,2,1]))"),
[1, "hour"]
)
self.assertEqual(
execute("age(dateTime([2000,1,1,1,1]),dateTime([2000,1,1,1,2]))"),
[1, "minute"]
)
self.assertEqual(
execute("age(dateTime([2000,1,1,1,1,1]),dateTime([2000,1,1,1,1,2]))"),
[1, "second"]
)
self.assertEqual(
execute("""array(time([0,0]) - time([0,0,0,999999]))"""),
[23, 59, 59, 1]
)
self.assertEqual(
execute("""array(time([0,0]) + time([0,0,0,999999]))"""),
[0, 0, 0, 999999]
)
def test_localize(self):
pass
#these tests are passing on computers with timezone set to UTC - not the case of TravisCI
#test of non-DST time
#if sys.version < "3":
#self.assertEqual(execute("array(localize(dateTime([2000,1,1,10,10,1,0]),'Europe/Warsaw'))"), [2000,1,1,11,10,1,0])
#test of DST time
#self.assertEqual(execute("array(localize(dateTime([2000,7,1,10,10,1,0]),'Europe/Warsaw'))"), [2000,7,1,12,10,1,0])
def test_builtin_type(self):
self.assertEqual(execute("type([1,2,3,4]+[2,4])"), "array")
self.assertEqual(execute("type({})"), "object")
self.assertEqual(execute("type('')"), "str")
def test_selector_with_empty_result(self):
self.assertEqual(execute("$.missing is None"), True)
self.assertEqual(execute("$.missing is not None"), False)
def test_misc(self):
self.assertEqual(execute(2), 2)
self.assertEqual(execute('{"@aaa":1}.@aaa'), 1)
self.assertEqual(execute('$ss.a'), None)
self.assertEqual(execute("$..*[10]"), None)
self.assertEqual(sorted(execute("keys({'a':1,'b':2})")), ['a', 'b'])
self.assertRaises(ExecutionError, lambda: execute('keys([])'))
self.assertRaises(ProgrammingError, lambda: execute('blah([])'))
def test_optimizations(self):
self.assertEqual(execute("$.*[@]"), execute("$.*"))
self.assertIsInstance(execute_raw("$..*"), generator)
self.assertIsInstance(execute_raw("$..* + $..*"), chain)
self.assertIsInstance(execute_raw("$..* + 2"), chain)
self.assertIsInstance(execute_raw("2 + $..*"), chain)
self.assertEqual(execute("$.._id[0]"), 1)
self.assertEqual(execute("sort($.._id + $.._id)[2]"), 2)
self.assertIsInstance(execute("$.._id[2]"), int)
self.assertEqual(
execute2("$.store.book.(price)[0].price"),
execute2("$.store.book[0].price")
)
class ObjectPath_Paths(unittest.TestCase):
def assertItemsEqual(self, a, b, m=None):
try:
return self.assertCountEqual(a, b, m)
except: pass
return unittest.TestCase.assertItemsEqual(self, a, b, m)
def test_simple_paths(self):
self.assertEqual(execute("$.*"), object1)
self.assertEqual(execute("$.a.b.c"), None)
self.assertEqual(execute("$.a.b.c[0]"), None)
self.assertEqual(execute("$.__lang__"), "en")
self.assertEqual(execute("$.test.o._id"), 2)
self.assertEqual(execute("$.test.l._id"), [3, 4])
self.assertEqual(execute("$.*[test].o._id"), 2)
self.assertEqual(execute("$.*['test'].o._id"), 2)
self.assertEqual(
execute('[1,"aa",{"a":2,"c":3},{"c":3},{"a":1,"b":2}].(a,b)'), [{
"a": 2
}, {
"a": 1,
"b": 2
}]
)
self.assertEqual(
execute2("$.store.book.(price,title)[0]"), {
"price": 8.95,
"title": "Sayings of the Century"
}
)
self.assertEqual(len(execute2("$..*['Lord' in @.title]")), 1)
self.assertEqual(
execute2("$..book.(price,title)"), [{
'price': 8.95,
'title': 'Sayings of the Century'
}, {
'price': 12.99,
'title': 'Sword of Honour'
}, {
'price': 8.99,
'title': 'Moby Dick'
}, {
'price': 22.99,
'title': 'The Lord of the Rings'
}]
)
self.assertEqual(
execute2("sort($..(price,title),'price')"),
[{
'price': 8.95,
'title': 'Sayings of the Century'
}, {
'price': 8.99,
'title': 'Moby Dick'
}, {
'price': 12.99,
'title': 'Sword of Honour'
}, {
'price': 19.95
}, {
'price': 22.99,
'title': 'The Lord of the Rings'
}]
)
self.assertIsInstance(execute("now().year"), int)
def test_complex_paths(self):
self.assertEqual(sorted(execute("$.._id")), [1, 2, 3, 4])
self.assertEqual(execute("$..l"), object1["test"]["l"])
self.assertEqual(execute("$..l.._id"), [3, 4])
self.assertEqual(execute2("$.store.*"), object2["store"])
self.assertEqual(
execute2("$.store.book.author"),
['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']
)
#print()
#print(execute2("$.store.book.(author,aaa)"))
self.assertEqual(
execute2("$.store.book.(author,aaa)"), [{
"author": "Nigel Rees"
}, {
"author": "Evelyn Waugh"
}, {
"author": "Herman Melville"
}, {
"author": "J. R. R. Tolkien"
}]
)
self.assertEqual(
execute2("$.store.book.(author,price)"), [{
'price': 8.95,
'author': 'Nigel Rees'
}, {
'price': 12.99,
'author': 'Evelyn Waugh'
}, {
'price': 8.99,
'author': 'Herman Melville'
}, {
'price': 22.99,
'author': 'J. R. R. Tolkien'
}]
)
self.assertEqual(
execute2("$.store.book.*[author]"),
['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']
)
self.assertEqual(
execute2("$.store.book.*['author']"),
['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']
)
self.assertEqual(execute2("$.store.book"), object2["store"]["book"])
self.assertEqual(
list(execute2("$..author")),
['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']
)
def test_selectors(self):
self.assertEqual(
execute2("$.store.book[@.id is not null]"),
[{
'category': 'reference',
'price': 8.95,
'title': 'Sayings of the Century',
'id': 1,
'author': 'Nigel Rees'
}]
)
self.assertEqual(len(execute2("$.store.book[@.id is null]")), 3)
self.assertEqual(len(execute("$..*[@._id>2]")), 2)
self.assertEqual(execute("$..*[3 in @.l._id]")[0], object1['test'])
self.assertEqual(execute2("$.store..*[4 in @.k._id]")[0], object2['store'])
self.assertEqual(execute("$..*[@._id>1 and @._id<3][0]"), {'_id': 2})
# very bad syntax!!!
self.assertEqual(
sorted(execute2("$.store.book[@.price]")),
sorted([8.95, 12.99, 8.99, 22.99])
)
self.assertEqual(
execute3("$..*[@.x is 5.6 and @.y is 9.891].value"), ['bar']
)
def test_object_list(self):
self.assertItemsEqual(execute3('values($.*).value'), ['foo', 'bar', 'foobar'])
self.assertItemsEqual(execute3('keys($.*)'), ['item_1', 'item_2', 'item_3'])
self.assertItemsEqual(
execute4('map(values, $..root..response).value'), [5, 4, 0]
)
#testcase2=unittest.FunctionTestCase(test_efficiency(2))
testcase1 = unittest.TestLoader().loadTestsFromTestCase(ObjectPath)
testcase2 = unittest.TestLoader().loadTestsFromTestCase(ObjectPath_Paths)
op_test = unittest.TestSuite([testcase1, testcase2])
#utils_interpreter=unittest.TestSuite([testcase2])
================================================
FILE: tests/test_utils.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from objectpath.utils import *
from objectpath.utils.json_ext import *
import sys, unittest, os
sys.setrecursionlimit(20000)
class Utils_test(unittest.TestCase):
def test_Utils_JSON_compat(self):
self.assertEqual(loads("['ddd']"), ['ddd'])
if sys.version_info.major < 3:
self.assertEqual(loads("[u'ddd']"), ['ddd'])
self.assertRaises(Exception, lambda: loads(['ddd}']))
self.assertEqual(dumps(['ddd']), '["ddd"]')
self.assertEqual(py2JSON(False), 'false')
self.assertEqual(py2JSON(None), 'null')
self.assertEqual(py2JSON((2, 3, 4)), [2, 3, 4])
if sys.version_info.major < 3:
self.assertEqual(py2JSON(unicode('')), '')
self.assertEqual(py2JSON(2), 2)
self.assertEqual(
printJSON([1, 2, 3, 4, 5, 6]),
"[\n \x1b[36m\x1b[1m1\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m2\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m3\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m4\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m5\x1b[0m\x1b[0m,\n ... (1 more items)\n]"
)
self.assertEqual(
printJSON([{}, 1]), '[\n {},\n \x1b[36m\x1b[1m1\x1b[0m\x1b[0m\n]'
)
self.assertEqual(
printJSON({
"aaa": 1
}),
'{\x1b[33m\x1b[1m"aaa"\x1b[0m\x1b[0m: \x1b[36m\x1b[1m1\x1b[0m\x1b[0m}'
)
self.assertEqual(
printJSON({
"a": [1, 2, 3]
}),
'{\x1b[33m\x1b[1m"a"\x1b[0m\x1b[0m: [\n \x1b[36m\x1b[1m1\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m2\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m3\x1b[0m\x1b[0m\n]}'
)
self.assertEqual(
printJSON([[1], {
"aa": 2
}]),
'[\n [\x1b[36m\x1b[1m1\x1b[0m\x1b[0m],\n {\x1b[33m\x1b[1m"aa"\x1b[0m\x1b[0m: \x1b[36m\x1b[1m2\x1b[0m\x1b[0m}\n]'
)
self.assertEqual(
printJSON({
"aaa": {
"bbb": {
"ccc": {
"ddd": [1, 2, 3, 4, 5]
}
}
}
}),
'{\x1b[33m\x1b[1m"aaa"\x1b[0m\x1b[0m: {\x1b[33m\x1b[1m"bbb"\x1b[0m\x1b[0m: {\x1b[33m\x1b[1m"ccc"\x1b[0m\x1b[0m: {\x1b[33m\x1b[1m"ddd"\x1b[0m\x1b[0m: [\n \x1b[36m\x1b[1m1\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m2\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m3\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m4\x1b[0m\x1b[0m,\n \x1b[36m\x1b[1m5\x1b[0m\x1b[0m\n]}}}}'
)
if str(sys.version_info.major) + str(sys.version_info.minor) < '33':
self.assertEqual(
printJSON({
"aaa": {
"bbb": {
"ccc": {
"ddd": {
"eee": [1, 2, 3, 4, 5],
"ddd": {}
}
}
}
}
}),
'{\x1b[33m\x1b[1m"aaa"\x1b[0m\x1b[0m: {\x1b[33m\x1b[1m"bbb"\x1b[0m\x1b[0m: {\x1b[33m\x1b[1m"ccc"\x1b[0m\x1b[0m: {\x1b[33m\x1b[1m"ddd"\x1b[0m\x1b[0m: {\n \x1b[33m\x1b[1m"eee"\x1b[0m\x1b[0m: <array of 5 items>,\n \x1b[33m\x1b[1m"ddd"\x1b[0m\x1b[0m: {...}\n}}}}}'
)
testcase1 = unittest.TestLoader().loadTestsFromTestCase(Utils_test)
utils_test = unittest.TestSuite([testcase1])
gitextract_4ckekqd1/
├── .coveragerc
├── .gitignore
├── .travis.yml
├── .vscode/
│ ├── launch.json
│ └── settings.json
├── LICENSE
├── MANIFEST.in
├── README.md
├── README.rst
├── README.rst.original
├── VER
├── build.sh
├── objectpath/
│ ├── __init__.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── interpreter.py
│ │ └── parser.py
│ ├── shell.py
│ └── utils/
│ ├── __init__.py
│ ├── colorify.py
│ ├── debugger.py
│ ├── json_ext.py
│ └── timeutils.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── shell.py
├── testObjectPath.py
└── tests/
├── __init__.py
├── test_ObjectPath.py
└── test_utils.py
SYMBOL INDEX (124 symbols across 13 files)
FILE: objectpath/core/__init__.py
class ProgrammingError (line 35) | class ProgrammingError(Exception):
class ExecutionError (line 38) | class ExecutionError(Exception):
FILE: objectpath/core/interpreter.py
class Tree (line 22) | class Tree(Debugger):
method register_function (line 26) | def register_function(cls, name, func):
method __init__ (line 35) | def __init__(self, obj, cfg=None):
method setData (line 44) | def setData(self, obj):
method setObjectGetter (line 48) | def setObjectGetter(self, object_getter_cb):
method compile (line 63) | def compile(self, expr):
method execute (line 69) | def execute(self, expr):
method __str__ (line 760) | def __str__(self):
method __repr__ (line 763) | def __repr__(self):
FILE: objectpath/core/parser.py
class symbol_base (line 30) | class symbol_base(object):
method nud (line 35) | def nud(self):
method led (line 38) | def led(self):
method getTree (line 41) | def getTree(self):
method __repr__ (line 113) | def __repr__(self):
function symbol (line 120) | def symbol(ID, bp=0):
function infix (line 139) | def infix(ID, bp):
function infix_r (line 147) | def infix_r(ID, bp):
function prefix (line 155) | def prefix(ID, bp):
function advance (line 162) | def advance(ID=None):
function method (line 168) | def method(s):
function nud (line 221) | def nud(self): # pylint: disable=E0102
function nud (line 228) | def nud(self): # pylint: disable=E0102
function nud (line 234) | def nud(self): # pylint: disable=E0102
function nud (line 252) | def nud(self): # pylint: disable=E0102,W0613
function led (line 260) | def led(self, left): # pylint: disable=E0102
function nud (line 295) | def nud(self): # pylint: disable=E0102
function led (line 308) | def led(self, left): # pylint: disable=E0102
function led (line 318) | def led(self, left): # pylint: disable=E0102
function constant (line 337) | def constant(ID):
function led (line 351) | def led(self, left): # pylint: disable=E0102
function led (line 361) | def led(self, left): # pylint: disable=E0102
function nud (line 372) | def nud(self): # pylint: disable=E0102
function nud (line 388) | def nud(self): # pylint: disable=E0102
function tokenize_python (line 414) | def tokenize_python(program):
function tokenize (line 433) | def tokenize(program):
function expression (line 466) | def expression(rbp=0):
function parse (line 477) | def parse(expr, D=False):
FILE: objectpath/shell.py
function main (line 28) | def main():
FILE: objectpath/utils/__init__.py
function skip (line 17) | def skip(iterable, n, islice=islice):
function filter_dict (line 24) | def filter_dict(iterable, keys):
function flatten (line 45) | def flatten(fragment, skip=False):
FILE: objectpath/utils/colorify.py
function color (line 4) | def color(c, s):
function bold (line 7) | def bold(s):
function op (line 10) | def op(s):
function const (line 13) | def const(s):
function string (line 16) | def string(s):
FILE: objectpath/utils/debugger.py
class Debugger (line 8) | class Debugger(object):
method __init__ (line 21) | def __init__(self):
method debug (line 40) | def debug(self, *s):
method info (line 44) | def info(self, *s):
method start (line 48) | def start(self, *s):
method end (line 52) | def end(self, *s):
method warning (line 56) | def warning(self, *s):
method error (line 60) | def error(self, *s):
method critical (line 64) | def critical(self, *s):
method lineno (line 68) | def lineno(self):
method cleanOutput (line 72) | def cleanOutput(self, o):
method consolelog (line 80) | def consolelog(self, lvl, s):
FILE: objectpath/utils/json_ext.py
function loads (line 21) | def loads(s):
function default (line 30) | def default(obj):
function dumps (line 34) | def dumps(s, default=default, indent=None):
function py2JSON (line 39) | def py2JSON(o):
function printJSON (line 62) | def printJSON(o, length=5, depth=5):
FILE: objectpath/utils/timeutils.py
function round9_10 (line 21) | def round9_10(n):
function age (line 29) | def age(date, reference=None, lang="en"):
function date (line 102) | def date(d):
function date2list (line 112) | def date2list(d):
function time (line 115) | def time(d):
function time2list (line 125) | def time2list(t):
function addTimes (line 128) | def addTimes(fst, snd):
function subTimes (line 158) | def subTimes(fst, snd):
function dateTime (line 185) | def dateTime(arg):
function UTC2local (line 221) | def UTC2local(dt, tzName="UTC"):
FILE: setup.py
function read (line 4) | def read(*rnames):
FILE: tests/__init__.py
function doTests (line 10) | def doTests():
FILE: tests/test_ObjectPath.py
function execute_raw (line 108) | def execute_raw(expr):
function execute (line 117) | def execute(expr):
function execute2 (line 124) | def execute2(expr):
function execute3 (line 131) | def execute3(expr):
function execute4 (line 138) | def execute4(expr):
class ObjectPath (line 145) | class ObjectPath(unittest.TestCase):
method test_simple_types (line 147) | def test_simple_types(self):
method test_arrays (line 156) | def test_arrays(self):
method test_objects (line 164) | def test_objects(self):
method test_arithm_add (line 181) | def test_arithm_add(self):
method test_arithm_sub (line 189) | def test_arithm_sub(self):
method test_arithm_mul (line 196) | def test_arithm_mul(self):
method test_arithm_mod (line 199) | def test_arithm_mod(self):
method test_arithm_div (line 204) | def test_arithm_div(self):
method test_arithm_group (line 209) | def test_arithm_group(self):
method test_arithm_parentheses (line 215) | def test_arithm_parentheses(self):
method test_logic_negatives (line 224) | def test_logic_negatives(self):
method test_logic_not (line 233) | def test_logic_not(self):
method test_logic_or (line 237) | def test_logic_or(self):
method test_logic_and (line 245) | def test_logic_and(self):
method test_comparison_regex (line 253) | def test_comparison_regex(self):
method test_comparison_is (line 259) | def test_comparison_is(self):
method test_comparison_isnot (line 275) | def test_comparison_isnot(self):
method test_membership_in (line 286) | def test_membership_in(self):
method test_membership_notin (line 291) | def test_membership_notin(self):
method test_complex (line 296) | def test_complex(self):
method test_comparison_lt (line 300) | def test_comparison_lt(self):
method test_comparison_gt (line 306) | def test_comparison_gt(self):
method test_concatenation (line 311) | def test_concatenation(self):
method test_builtin_casting (line 327) | def test_builtin_casting(self):
method test_builtin_arithmetic (line 350) | def test_builtin_arithmetic(self):
method test_builtin_string (line 372) | def test_builtin_string(self):
method test_builtin_arrays (line 395) | def test_builtin_arrays(self):
method test_builtin_time (line 415) | def test_builtin_time(self):
method test_localize (line 513) | def test_localize(self):
method test_builtin_type (line 522) | def test_builtin_type(self):
method test_selector_with_empty_result (line 527) | def test_selector_with_empty_result(self):
method test_misc (line 531) | def test_misc(self):
method test_optimizations (line 540) | def test_optimizations(self):
class ObjectPath_Paths (line 554) | class ObjectPath_Paths(unittest.TestCase):
method assertItemsEqual (line 555) | def assertItemsEqual(self, a, b, m=None):
method test_simple_paths (line 561) | def test_simple_paths(self):
method test_complex_paths (line 620) | def test_complex_paths(self):
method test_selectors (line 671) | def test_selectors(self):
method test_object_list (line 696) | def test_object_list(self):
FILE: tests/test_utils.py
class Utils_test (line 10) | class Utils_test(unittest.TestCase):
method test_Utils_JSON_compat (line 11) | def test_Utils_JSON_compat(self):
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (108K chars).
[
{
"path": ".coveragerc",
"chars": 50,
"preview": "[run]\nomit =\n\t*/tests*\n\t*/shell.py\n\t*/debugger.py\n"
},
{
"path": ".gitignore",
"chars": 1327,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": ".travis.yml",
"chars": 561,
"preview": "language: python\npython:\n- '2.7'\n- '3.8'\n- pypy\ninstall:\n- pip install -r requirements.txt\n- pip install coveralls\nscrip"
},
{
"path": ".vscode/launch.json",
"chars": 1607,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n //"
},
{
"path": ".vscode/settings.json",
"chars": 42,
"preview": "{\n \"python.linting.pylintEnabled\": true\n}"
},
{
"path": "LICENSE",
"chars": 1074,
"preview": "MIT License\n\nCopyright (c) 2017 Adrian Kalbarczyk\n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "MANIFEST.in",
"chars": 26,
"preview": "include *.rst\ninclude VER\n"
},
{
"path": "README.md",
"chars": 3142,
"preview": "ObjectPath\n==========\n\n[](https://pypi.python.org/pypi/object"
},
{
"path": "README.rst",
"chars": 3342,
"preview": "ObjectPath\n==========\n\n|Downloads| |Build Status| |Code Health| |Coverage Status|\n\nThe agile NoSQL query language for se"
},
{
"path": "README.rst.original",
"chars": 3342,
"preview": "ObjectPath\n==========\n\n|Downloads| |Build Status| |Code Health| |Coverage Status|\n\nThe agile NoSQL query language for se"
},
{
"path": "VER",
"chars": 6,
"preview": "0.6.2\n"
},
{
"path": "build.sh",
"chars": 665,
"preview": "#!/bin/sh\npandoc -f markdown -t rst -o README.rst README.md\nmdTable=\"aaa Scope aaa Language aaa aaa---aaa---aaa aaa text"
},
{
"path": "objectpath/__init__.py",
"chars": 100,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom .core.interpreter import *\nfrom .utils import *\n"
},
{
"path": "objectpath/core/__init__.py",
"chars": 843,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright"
},
{
"path": "objectpath/core/interpreter.py",
"chars": 27825,
"preview": "#!/usr/bin/env python\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Ka"
},
{
"path": "objectpath/core/parser.py",
"chars": 10897,
"preview": "#!/usr/bin/env python\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Ka"
},
{
"path": "objectpath/shell.py",
"chars": 4251,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright"
},
{
"path": "objectpath/utils/__init__.py",
"chars": 1525,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright"
},
{
"path": "objectpath/utils/colorify.py",
"chars": 264,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\ndef color(c, s):\n return '\\033[%sm%s\\033[0m' % (c, s)\n\ndef bold(s):\n re"
},
{
"path": "objectpath/utils/debugger.py",
"chars": 2892,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# move dbgMap outside\nimport inspect\nfrom objectpath.core import ITER_TYP"
},
{
"path": "objectpath/utils/json_ext.py",
"chars": 3109,
"preview": "#!/usr/bin/env python\ntry:\n import simplejson as json\nexcept ImportError:\n try:\n import json\n except ImportError:\n"
},
{
"path": "objectpath/utils/timeutils.py",
"chars": 5336,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright"
},
{
"path": "requirements.txt",
"chars": 5,
"preview": "pytz\n"
},
{
"path": "setup.cfg",
"chars": 261,
"preview": "[bdist_wheel]\nuniversal = 1\n[yapf]\n# split_all_comma_separated_values=True\nno_spaces_around_selected_binary_operators=\"*"
},
{
"path": "setup.py",
"chars": 1118,
"preview": "import os\nfrom setuptools import setup\n\ndef read(*rnames):\n return open(os.path.join(os.path.dirname(__file__), *rnames"
},
{
"path": "shell.py",
"chars": 90,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom objectpath import shell\n\nshell.main()\n"
},
{
"path": "testObjectPath.py",
"chars": 109,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom tests import doTests\n\n# starts all test suites\ndoTests()\n"
},
{
"path": "tests/__init__.py",
"chars": 352,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#unit tests for ACR functions\n\nfrom .test_ObjectPath import op_test\nfrom ."
},
{
"path": "tests/test_ObjectPath.py",
"chars": 23414,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom objectpath.core.interpreter import *\nfrom objectpath.core import Pro"
},
{
"path": "tests/test_utils.py",
"chars": 2974,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom objectpath.utils import *\nfrom objectpath.utils.json_ext import *\nim"
}
]
About this extraction
This page contains the full source code of the adriank/ObjectPath GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (98.2 KB), approximately 29.5k tokens, and a symbol index with 124 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.