[
  {
    "path": ".coveragerc",
    "content": "[run]\nomit =\n\t*/tests*\n\t*/shell.py\n\t*/debugger.py\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\npython:\n- '2.7'\n- '3.8'\n- pypy\ninstall:\n- pip install -r requirements.txt\n- pip install coveralls\nscript:\n- nosetests\n- coverage run --source=objectpath setup.py test\ndeploy:\n  provider: pypi\n  user: adriankal\n  password:\n    secure: bWTP43XxjAIwC6PMmfjt4/dCBenky+zF7empMgzfmkUq5jCoQjCJm4IUdYIdjCKLYEIU1DaOTp7+rqmIZf2d8wWvGAc4+k3vinV9k8WzycpBM+YgnW2knQ5eko93H0lpNOIrat4J0wvc51JjHfj4uqib6SCTaXmBS/kRHmiRkx8=\n  on:\n    tags: true\n    all_branches: true\n    repo: adriank/ObjectPath\n  distributions: \"sdist bdist_wheel\"\nafter_success:\n- coveralls\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Python: Current File (Integrated Terminal)\",\n      \"type\": \"python\",\n      \"request\": \"launch\",\n      \"program\": \"/Users/adrian/projects/ObjectPath/shell.py\",\n      \"args\": [\"/Users/adrian/projects/nutrient-db/butter.json\"],\n      \"console\": \"integratedTerminal\"\n    },\n    {\n      \"name\": \"Python: Attach\",\n      \"type\": \"python\",\n      \"request\": \"attach\",\n      \"port\": 5678,\n      \"host\": \"localhost\"\n    },\n    {\n      \"name\": \"Python: Module\",\n      \"type\": \"python\",\n      \"request\": \"launch\",\n      \"module\": \"objectpath\",\n      \"console\": \"integratedTerminal\"\n    },\n    {\n      \"name\": \"Python: Django\",\n      \"type\": \"python\",\n      \"request\": \"launch\",\n      \"program\": \"${workspaceFolder}/manage.py\",\n      \"console\": \"integratedTerminal\",\n      \"args\": [\n        \"runserver\",\n        \"--noreload\",\n        \"--nothreading\"\n      ],\n      \"django\": true\n    },\n    {\n      \"name\": \"Python: Flask\",\n      \"type\": \"python\",\n      \"request\": \"launch\",\n      \"module\": \"flask\",\n      \"env\": {\n        \"FLASK_APP\": \"app.py\"\n      },\n      \"args\": [\n        \"run\",\n        \"--no-debugger\",\n        \"--no-reload\"\n      ],\n      \"jinja\": true\n    },\n    {\n      \"name\": \"Python: Current File (External Terminal)\",\n      \"type\": \"python\",\n      \"request\": \"launch\",\n      \"program\": \"${file}\",\n      \"console\": \"externalTerminal\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"python.linting.pylintEnabled\": true\n}"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Adrian Kalbarczyk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include *.rst\ninclude VER\n"
  },
  {
    "path": "README.md",
    "content": "ObjectPath\n==========\n\n[![Downloads](https://img.shields.io/pypi/dm/objectpath.svg)](https://pypi.python.org/pypi/objectpath/)\n<!--[![License](https://img.shields.io/pypi/l/objectpath.svg)](https://pypi.python.org/pypi/objectpath/)-->\n[![Build Status](https://travis-ci.org/adriank/ObjectPath.svg?branch=master)](https://travis-ci.org/adriank/ObjectPath)\n[![Code Health](https://landscape.io/github/adriank/ObjectPath/master/landscape.png)](https://landscape.io/github/adriank/ObjectPath/master)\n[![Coverage Status](https://coveralls.io/repos/adriank/ObjectPath/badge.png?branch=master)](https://coveralls.io/r/adriank/ObjectPath?branch=master)\n\nThe agile NoSQL query language for semi-structured data\n-----------------------------------------------\n\n**#Python #NoSQL #Javascript #JSON #nested-array-object**\n\nObjectPath 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.\n\nMore at [ObjectPath site](https://adriank.github.io/ObjectPath/)\n\n![ObjectPath img](http://adriank.github.io/ObjectPath/img/op-colors.png)\n\nObjectPath 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:\n\n| Scope  | Language |\n|---|---|\n| text documents  | regular expression  |\n| XML  | XPath  |\n| HTML  | CSS selectors  |\n| JSON documents | ObjectPath |\n\nDocumentation\n-------------\n\n[ObjectPath Reference](https://adriank.github.io/ObjectPath/reference.html)\n\nCommand line usage\n-----\n\n`````sh\n$ sudo pip install objectpath\n$ objectpath file.json\n`````\nor\n`````sh\n$ git clone https://github.com/adriank/ObjectPath.git\n$ cd ObjectPath\n$ python shell.py file.json\n`````\n\nPython usage\n----------------\n\n`````sh\n$ sudo pip install objectpath\n$ python\n>>> from objectpath import *\n>>> tree=Tree({\"a\":1})\n>>> tree.execute(\"$.a\")\n1\n>>>\n`````\n\n`````sh\n$ git clone https://github.com/adriank/ObjectPath.git\n$ cd ObjectPath\n$ python\n>>> from objectpath import *\n>>> tree=Tree({\"a\":1})\n>>> tree.execute(\"$.a\")\n1\n>>>\n`````\n\nContributing & bugs\n-------------------\n\nI 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. \n\nIf 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.\n\nLicense\n-------\n\n**MIT**\n"
  },
  {
    "path": "README.rst",
    "content": "ObjectPath\n==========\n\n|Downloads| |Build Status| |Code Health| |Coverage Status|\n\nThe agile NoSQL query language for semi-structured data\n-------------------------------------------------------\n\n**#Python #NoSQL #Javascript #JSON #nested-array-object**\n\nObjectPath is a query language similar to XPath or JSONPath, but much\nmore powerful thanks to embedded arithmetic calculations, comparison\nmechanisms and built-in functions. This makes the language more like SQL\nin terms of expressiveness, but it works over JSON documents rather than\nrelations. ObjectPath can be considered a full-featured expression\nlanguage. Besides selector mechanism there is also boolean logic, type\nsystem and string concatenation available. On top of that, the language\nimplementations (Python at the moment; Javascript is in beta!) are\nsecure and relatively fast.\n\nMore at `ObjectPath site <http://objectpath.org/>`__\n\n.. figure:: http://adriank.github.io/ObjectPath/img/op-colors.png\n   :alt: ObjectPath img\n\n   ObjectPath img\n\nObjectPath makes it easy to find data in big nested JSON documents. It\nborrows the best parts from E4X, JSONPath, XPath and SQL. ObjectPath is\nto JSON documents what XPath is to XML. Other examples to ilustrate this\nkind of relationship are:\n\n============== ==================\nScope          Language\n============== ==================\ntext documents regular expression\nXML            XPath\nHTML           CSS selectors\nJSON documents ObjectPath\n============== ==================\n\nDocumentation\n-------------\n\n`ObjectPath Reference <http://objectpath.org/reference.html>`__\n\nCommand line usage\n------------------\n\n.. code:: sh\n\n   $ sudo pip install objectpath\n   $ objectpath file.json\n\nor\n\n.. code:: sh\n\n   $ git clone https://github.com/adriank/ObjectPath.git\n   $ cd ObjectPath\n   $ python shell.py file.json\n\nPython usage\n------------\n\n.. code:: sh\n\n   $ sudo pip install objectpath\n   $ python\n   >>> from objectpath import *\n   >>> tree=Tree({\"a\":1})\n   >>> tree.execute(\"$.a\")\n   1\n   >>>\n\n.. code:: sh\n\n   $ git clone https://github.com/adriank/ObjectPath.git\n   $ cd ObjectPath\n   $ python\n   >>> from objectpath import *\n   >>> tree=Tree({\"a\":1})\n   >>> tree.execute(\"$.a\")\n   1\n   >>>\n\nContributing & bugs\n-------------------\n\nI appreciate all contributions and bugfix requests for ObjectPath,\nhowever since I don’t code in Python any more, this library is not\nmaintained as of now. Since I can’t fully assure that code contributed\nby others meets quality standards, I can’t accept PRs.\n\nIf you feel you could maintain this code, ping me. I’d be more than\nhappy to transfer this repo to a dedicated ObjectPath organization on\nGitHub and give the ownership to someone with more time for this project\nthan me.\n\nLicense\n-------\n\n**MIT**\n\n.. |Downloads| image:: https://img.shields.io/pypi/dm/objectpath.svg\n   :target: https://pypi.python.org/pypi/objectpath/\n.. |Build Status| image:: https://travis-ci.org/adriank/ObjectPath.svg?branch=master\n   :target: https://travis-ci.org/adriank/ObjectPath\n.. |Code Health| image:: https://landscape.io/github/adriank/ObjectPath/master/landscape.png\n   :target: https://landscape.io/github/adriank/ObjectPath/master\n.. |Coverage Status| image:: https://coveralls.io/repos/adriank/ObjectPath/badge.png?branch=master\n   :target: https://coveralls.io/r/adriank/ObjectPath?branch=master\n"
  },
  {
    "path": "README.rst.original",
    "content": "ObjectPath\n==========\n\n|Downloads| |Build Status| |Code Health| |Coverage Status|\n\nThe agile NoSQL query language for semi-structured data\n-------------------------------------------------------\n\n**#Python #NoSQL #Javascript #JSON #nested-array-object**\n\nObjectPath is a query language similar to XPath or JSONPath, but much\nmore powerful thanks to embedded arithmetic calculations, comparison\nmechanisms and built-in functions. This makes the language more like SQL\nin terms of expressiveness, but it works over JSON documents rather than\nrelations. ObjectPath can be considered a full-featured expression\nlanguage. Besides selector mechanism there is also boolean logic, type\nsystem and string concatenation available. On top of that, the language\nimplementations (Python at the moment; Javascript is in beta!) are\nsecure and relatively fast.\n\nMore at `ObjectPath site <http://objectpath.org/>`__\n\n.. figure:: http://adriank.github.io/ObjectPath/img/op-colors.png\n   :alt: ObjectPath img\n\n   ObjectPath img\n\nObjectPath makes it easy to find data in big nested JSON documents. It\nborrows the best parts from E4X, JSONPath, XPath and SQL. ObjectPath is\nto JSON documents what XPath is to XML. Other examples to ilustrate this\nkind of relationship are:\n\n============== ==================\nScope          Language\n============== ==================\ntext documents regular expression\nXML            XPath\nHTML           CSS selectors\nJSON documents ObjectPath\n============== ==================\n\nDocumentation\n-------------\n\n`ObjectPath Reference <http://objectpath.org/reference.html>`__\n\nCommand line usage\n------------------\n\n.. code:: sh\n\n   $ sudo pip install objectpath\n   $ objectpath file.json\n\nor\n\n.. code:: sh\n\n   $ git clone https://github.com/adriank/ObjectPath.git\n   $ cd ObjectPath\n   $ python shell.py file.json\n\nPython usage\n------------\n\n.. code:: sh\n\n   $ sudo pip install objectpath\n   $ python\n   >>> from objectpath import *\n   >>> tree=Tree({\"a\":1})\n   >>> tree.execute(\"$.a\")\n   1\n   >>>\n\n.. code:: sh\n\n   $ git clone https://github.com/adriank/ObjectPath.git\n   $ cd ObjectPath\n   $ python\n   >>> from objectpath import *\n   >>> tree=Tree({\"a\":1})\n   >>> tree.execute(\"$.a\")\n   1\n   >>>\n\nContributing & bugs\n-------------------\n\nI appreciate all contributions and bugfix requests for ObjectPath,\nhowever since I don’t code in Python any more, this library is not\nmaintained as of now. Since I can’t fully assure that code contributed\nby others meets quality standards, I can’t accept PRs.\n\nIf you feel you could maintain this code, ping me. I’d be more than\nhappy to transfer this repo to a dedicated ObjectPath organization on\nGitHub and give the ownership to someone with more time for this project\nthan me.\n\nLicense\n-------\n\n**MIT**\n\n.. |Downloads| image:: https://img.shields.io/pypi/dm/objectpath.svg\n   :target: https://pypi.python.org/pypi/objectpath/\n.. |Build Status| image:: https://travis-ci.org/adriank/ObjectPath.svg?branch=master\n   :target: https://travis-ci.org/adriank/ObjectPath\n.. |Code Health| image:: https://landscape.io/github/adriank/ObjectPath/master/landscape.png\n   :target: https://landscape.io/github/adriank/ObjectPath/master\n.. |Coverage Status| image:: https://coveralls.io/repos/adriank/ObjectPath/badge.png?branch=master\n   :target: https://coveralls.io/r/adriank/ObjectPath?branch=master\n"
  },
  {
    "path": "VER",
    "content": "0.6.2\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/sh\npandoc -f markdown -t rst -o README.rst README.md\nmdTable=\"aaa Scope aaa Language aaa aaa---aaa---aaa aaa text documents aaa regular\nexpression aaa aaa XML aaa XPath aaa aaa HTML aaa CSS selectors aaa aaa JSON\ndocuments aaa ObjectPath aaa\"\nrstTable=\"==============  ==================\nScope           Language\n==============  ==================\ntext documents  regular expression\nXML             XPath\nHTML            CSS selectors\nJSON documents  ObjectPath\n==============  ==================\"\nsed -i \"s/\\\\\\|/aaa/g\" README.rst\nperl -0777 -i.original -pe \"s/$mdTable/$rstTable/g\" README.rst\n\npython setup.py build\npython setup.py sdist bdist_wheel upload\n"
  },
  {
    "path": "objectpath/__init__.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom .core.interpreter import *\nfrom .utils import *\n"
  },
  {
    "path": "objectpath/core/__init__.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Kalbarczyk\n\nfrom types import GeneratorType as generator\nfrom itertools import chain\n\nSELECTOR_OPS = [\n  \"is\", \">\", \"<\", \"is not\", \">=\", \"<=\", \"in\", \"not in\", \":\", \"and\", \"or\", \"matches\", \"fn\"\n]\n# it must be list because of further concatenations\nNUM_TYPES = [int, float]\n\ntry:\n  NUM_TYPES += [long]\nexcept NameError:\n  pass\n\nSTR_TYPES = [str]\n\ntry:\n  STR_TYPES += [unicode]\nexcept NameError:\n  pass\n\nITER_TYPES = [list, generator, chain]\n\ntry:\n  ITER_TYPES += [map, filter]\nexcept NameError:\n  pass\n\nclass ProgrammingError(Exception):\n  pass\n\nclass ExecutionError(Exception):\n  pass\n\nPY_TYPES_MAP = {\n  \"int\": \"number\",\n  \"float\": \"number\",\n  \"str\": \"string\",\n  \"dict\": \"object\",\n  \"list\": \"array\"\n}\n"
  },
  {
    "path": "objectpath/core/interpreter.py",
    "content": "#!/usr/bin/env python\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Kalbarczyk\n\nimport sys, re\nfrom .parser import parse\nfrom objectpath.core import *\nimport objectpath.utils.colorify as color  # pylint: disable=W0614\nfrom objectpath.utils import flatten, filter_dict, timeutils, skip\nfrom objectpath.utils.json_ext import py2JSON\nfrom objectpath.core import ITER_TYPES, generator, chain\nfrom objectpath.utils.debugger import Debugger\n\nEPSILON = 0.0000000000000001  #this is used in float comparison\nEXPR_CACHE = {}\nRE_TYPE = type(re.compile(''))\n# setting external modules to 0, thus enabling lazy loading. 0 ensures that Pythonic types are never matched.\n# this way is efficient because if statement is fast and once loaded these variables are pointing to libraries.\nObjectId = generateID = calendar = escape = escapeDict = unescape = unescapeDict = 0\n\nclass Tree(Debugger):\n  _REGISTERED_FUNCTIONS = {}\n\n  @classmethod\n  def register_function(cls, name, func):\n    \"\"\"\n\t\tThis method is used to add custom functions not catered for by default\n\t\t:param str name: The name by which the function will be referred to in the expression\n\t\t:param callable func: The function\n\t\t:return:\n\t\t\"\"\"\n    cls._REGISTERED_FUNCTIONS[name] = func\n\n  def __init__(self, obj, cfg=None):\n    if not cfg:\n      cfg = {}\n    self.D = cfg.get(\"debug\", False)\n    self.setObjectGetter(cfg.get(\"object_getter\", None))\n    self.setData(obj)\n    self.current = self.node = None\n    if self.D: super(Tree, self).__init__()\n\n  def setData(self, obj):\n    if type(obj) in ITER_TYPES + [dict]:\n      self.data = obj\n\n  def setObjectGetter(self, object_getter_cb):\n    if callable(object_getter_cb):\n      self.object_getter = object_getter_cb\n    else:\n\n      def default_getter(obj, attr):\n        try:\n          return obj.__getattribute__(attr)\n        except AttributeError:\n          if self.D:\n            self.end(color.op(\".\") + \" returning '%s'\", color.bold(obj))\n          return obj\n\n      self.object_getter = default_getter\n\n  def compile(self, expr):\n    if expr in EXPR_CACHE:\n      return EXPR_CACHE[expr]\n    ret = EXPR_CACHE[expr] = parse(expr, self.D)\n    return ret\n\n  def execute(self, expr):\n    D = self.D\n    if D: self.start(\"Tree.execute\")\n    TYPES = [str, int, float, bool, generator, chain]\n    try:\n      TYPES += [long]\n    except NameError:\n      pass\n\n    # TODO change to yield?\n    def exe(node):\n      \"\"\"\n\t\t\t\tnode[0] - operator name\n\t\t\t\tnode[1:] - params\n\t\t\t\"\"\"\n      types = [\n          str, timeutils.datetime.time, timeutils.datetime.date,\n          timeutils.datetime.datetime\n      ]\n      try:\n        types += [unicode]\n      except:\n        pass\n      if D: self.start(\"executing node %s\", color.bold(self.cleanOutput(node)))\n      type_node = type(node)\n      if node is None or type_node in TYPES:\n        return node\n      elif type_node in types:\n        return node\n      elif type_node is list:\n        return (exe(n) for n in node)\n      elif type_node is dict:\n        ret = {}\n        for i in node.items():\n          ret[exe(i[0])] = exe(i[1])\n        return ret\n      op = node[0]\n      if op == \"or\":\n        if D: self.debug(\"%s or %s\", node[1], node[2])\n        return exe(node[1]) or exe(node[2])\n      elif op == \"and\":\n        if D: self.debug(\"%s and %s\", node[1], node[2])\n        return exe(node[1]) and exe(node[2])\n      elif op == \"+\":\n        if len(node) > 2:\n          fst = exe(node[1])\n          snd = exe(node[2])\n          if None in (fst, snd):\n            return fst or snd\n          typefst = type(fst)\n          typesnd = type(snd)\n          if typefst is dict:\n            try:\n              fst.update(snd)\n            except Exception:\n              if type(snd) is not dict:\n                raise ProgrammingError(\n                    \"Can't add value of type %s to %s\" % (\n                        color.bold(\n                            PY_TYPES_MAP.\n                            get(type(snd).__name__,\n                                type(snd).__name__)\n                        ), color.bold(\"object\")\n                    )\n                )\n            return fst\n          if typefst is list and typesnd is list:\n            if D: self.debug(\"both sides are lists, returning '%s'\", fst + snd)\n            return fst + snd\n          if typefst in ITER_TYPES or typesnd in ITER_TYPES:\n            if typefst not in ITER_TYPES:\n              fst = [fst]\n            elif typesnd not in ITER_TYPES:\n              snd = [snd]\n            if D: self.debug(\"at least one side is a generator and the other is an iterable, returning chain\")\n            return chain(fst, snd)\n          if typefst in NUM_TYPES:\n            try:\n              return fst + snd\n            except Exception:\n              return fst + float(snd)\n          if typefst in STR_TYPES or typesnd in STR_TYPES:\n            if D: self.info(\"doing string comparison '%s' is '%s'\", fst, snd)\n            if sys.version_info[0] < 3:\n              if typefst is unicode:\n                fst = fst.encode(\"utf-8\")\n              if typesnd is unicode:\n                snd = snd.encode(\"utf-8\")\n            return str(fst) + str(snd)\n          try:\n            timeType = timeutils.datetime.time\n            if typefst is timeType and typesnd is timeType:\n              return timeutils.addTimes(fst, snd)\n          except Exception:\n            pass\n          if D: self.debug(\"standard addition, returning '%s'\", fst + snd)\n          return fst + snd\n        else:\n          return exe(node[1])\n      elif op == \"-\":\n        if len(node) > 2:\n          fst = exe(node[1])\n          snd = exe(node[2])\n          try:\n            return fst - snd\n          except Exception:\n            typefst = type(fst)\n            typesnd = type(snd)\n            timeType = timeutils.datetime.time\n            if typefst is timeType and typesnd is timeType:\n              return timeutils.subTimes(fst, snd)\n        else:\n          return -exe(node[1])\n      elif op == \"*\":\n        return exe(node[1])*exe(node[2])\n      elif op == \"%\":\n        return exe(node[1]) % exe(node[2])\n      elif op == \"/\":\n        return exe(node[1])/float(exe(node[2]))\n      elif op == \">\":\n        if D: self.debug(\"%s > %s, %s\", node[1], node[2], node[1] > node[2])\n        return exe(node[1]) > exe(node[2])\n      elif op == \"<\":\n        return exe(node[1]) < exe(node[2])\n      elif op == \">=\":\n        return exe(node[1]) >= exe(node[2])\n      elif op == \"<=\":\n        return exe(node[1]) <= exe(node[2])\n      # TODO this algorithm produces 3 for 1<2<3 and should be true\n      # elif op in \"<=>=\":\n      # \tfst=exe(node[1])\n      # \tsnd=exe(node[2])\n      # \tif op==\">\":\n      # \t\treturn fst > snd and snd or False\n      # \telif op==\"<\":\n      # \t\treturn fst < snd and snd or False\n      # \telif op==\">=\":\n      # \t\treturn fst >= snd and snd or False\n      # \telif op==\"<=\":\n      # \t\treturn fst <= snd and snd or False\n      elif op == \"not\":\n        fst = exe(node[1])\n        if D: self.debug(\"doing not '%s'\", fst)\n        return not fst\n      elif op == \"in\":\n        fst = exe(node[1])\n        snd = exe(node[2])\n        if D: self.debug(\"doing '%s' in '%s'\", node[1], node[2])\n        if type(fst) in ITER_TYPES and type(snd) in ITER_TYPES:\n          return any(\n              x in max(fst, snd, key=len) for x in min(fst, snd, key=len)\n          )\n        return exe(node[1]) in exe(node[2])\n      elif op == \"not in\":\n        fst = exe(node[1])\n        snd = exe(node[2])\n        if D: self.debug(\"doing '%s' not in '%s'\", node[1], node[2])\n        if type(fst) in ITER_TYPES and type(snd) in ITER_TYPES:\n          return not any(\n              x in max(fst, snd, key=len) for x in min(fst, snd, key=len)\n          )\n        return exe(node[1]) not in exe(node[2])\n      elif op in (\"is\", \"is not\"):\n        if D: self.debug(\"found operator '%s'\", op)\n        # try:\n        fst = exe(node[1])\n        # except Exception as e:\n        # \tif D: self.debug(\"NOT ERROR! Can't execute node[1] '%s', error: '%s'. Falling back to orginal value.\",node[1],str(e))\n        # \tfst=node[1]\n        # try:\n        snd = exe(node[2])\n        # except Exception as e:\n        # \tif D: self.debug(\"NOT ERROR! Can't execute node[2] '%s', error: '%s'. Falling back to orginal value.\",node[2],str(e))\n        # \tsnd=node[2]\n        if op == \"is\" and fst == snd:\n          return True\n\n        # this doesn't work for 3 is not '3'\n        # if op == \"is not\" and fst != snd:\n        # \treturn True\n        typefst = type(fst)\n        typesnd = type(snd)\n        if D: self.debug(\"type fst: '%s', type snd: '%s'\", typefst, typesnd)\n        if typefst in STR_TYPES:\n          if D: self.info(\"doing string comparison '\\\"%s\\\" is \\\"%s\\\"'\", fst, snd)\n          ret = str(fst) == str(snd)\n        elif typefst is float or typesnd is float:\n          if D: self.info(\"doing float comparison '%s is %s'\", fst, snd)\n          try:\n            ret = abs(float(fst) - float(snd)) < EPSILON\n          except:\n            ret = False\n        elif typefst is int or typesnd is int:\n          if D: self.info(\"doing integer comparison '%s is %s'\", fst, snd)\n          try:\n            ret = int(fst) == int(snd)\n          except:\n            ret = False\n        elif typefst is list and typesnd is list:\n          if D: self.info(\"doing array comparison '%s' is '%s'\", fst, snd)\n          ret = fst == snd\n        elif typefst is dict and typesnd is dict:\n          if D: self.info(\"doing object comparison '%s' is '%s'\", fst, snd)\n          ret = fst == snd\n        elif fst is None or snd is None:\n          if fst is None and snd is None:\n            # this executes only for \"is not\"\n            ret = True\n          else:\n            ret = (fst or snd) is None\n            if D: self.info(\n                \"doing None comparison %s is %s = %s\", color.bold(fst), color.bold(snd),\n                color.bold(not not (fst or snd))\n              )\n        else:\n          if D: self.info(\"can't compare %s and %s. Returning False\", self.cleanOutput(fst), self.cleanOutput(snd))\n          ret = False\n        # else:\n        # \ttry:\n        # \t\tglobal ObjectId\n        # \t\tif not ObjectId:\n        # \t\t\tfrom bson.objectid import ObjectId\n        # \t\tif typefst is ObjectId or typesnd is ObjectId:\n        # \t\t\tif D: self.info(\"doing MongoDB objectID comparison '%s' is '%s'\",fst,snd)\n        # \t\t\tret=str(fst)==str(snd)\n        # \t\telse:\n        # \t\t\tif D: self.info(\"doing standard comparison '%s' is '%s'\",fst,snd)\n        # \t\t\tret=fst is snd\n        # \texcept Exception:\n        # \t\tpass\n        if op == \"is not\":\n          if D: self.info(\"'is not' found. Returning %s\", not ret)\n          return not ret\n        else:\n          if D: self.info(\"returning %s is %s => %s\", color.bold(self.cleanOutput(fst)), color.bold(self.cleanOutput(snd)), color.bold(ret))\n          return ret\n      elif op == \"re\":\n        return re.compile(exe(node[1]))\n      elif op == \"matches\":\n        fst = exe(node[1])\n        snd = exe(node[2])\n        if type(fst) not in STR_TYPES+[RE_TYPE]:\n          raise Exception(\"operator \" + color.bold(\"matches\") + \" expects regexp on the left. Example: 'a.*d' matches 'abcd'\")\n        if type(snd) in ITER_TYPES:\n          for i in snd:\n            if not not re.match(fst, i):\n              return True\n          return False\n        else:\n          # regex matches string\n          return not not re.match(fst, snd)\n      # elif op==\"(literal)\":\n      # \tfstLetter=node[1][0]\n      # \tif fstLetter is \"'\":\n      # \t\treturn node[1][1:-1]\n      # \telif fstLetter.isdigit:\n      # \t\treturn int(node[1])\n      elif op == \"(root)\":  # this is $\n        return self.data\n      # elif op==\"(node)\":# this is !\n      # \tif D: self.debug(\"returning node %s\",self.node)\n      # \treturn self.node\n      elif op == \"(current)\":  # this is @\n        if D: self.debug(\"returning current node: \\n  %s\", color.bold(self.current))\n        return self.current\n      elif op == \"name\":\n        return node[1]\n      elif op == \".\":\n        fst = node[1]\n        if type(fst) is tuple:\n          fst = exe(fst)\n        typefst = type(fst)\n        if D: self.debug(color.op(\".\") + \" left is '%s'\", color.bold(self.cleanOutput(fst)))\n        # try:\n        if node[2][0] == \"*\":\n          if D:\n            self.end(\n                color.op(\".\") + \" returning '%s'\",\n                color.bold(typefst in ITER_TYPES and fst or [fst])\n            )\n          return fst  # typefst in ITER_TYPES and fst or [fst]\n        # except:\n        # \tpass\n        snd = exe(node[2])\n        if D: self.debug(color.op(\".\") + \" right is '%s'\", color.bold(snd))\n        if typefst in ITER_TYPES:\n          if D: self.debug(\n                color.op(\".\") + \" filtering %s by %s\", color.bold(self.cleanOutput(fst)),\n                color.bold(snd)\n            )\n          if type(snd) in ITER_TYPES:\n            return filter_dict(fst, list(snd))\n          else:\n            # if D: self.debug(list(fst))\n            return (e[snd] for e in fst if type(e) is dict and snd in e)\n        try:\n          if D: self.end(color.op(\".\") + \" returning '%s'\", fst.get(snd))\n          return fst.get(snd)\n        except Exception:\n          if isinstance(fst, object):\n            return self.object_getter(fst, snd)\n          if D: self.end(color.op(\".\") + \" returning '%s'\", color.bold(fst))\n          return fst\n      elif op == \"..\":\n        fst = flatten(exe(node[1]))\n        if node[2][0] == \"*\":\n          if D: self.debug(color.op(\"..\") + \" returning '%s'\", color.bold(fst))\n          return fst\n        # reduce objects to selected attributes\n        snd = exe(node[2])\n        if D: self.debug(\n              color.op(\"..\") + \" finding all %s in %s\", color.bold(snd),\n              color.bold(self.cleanOutput(fst))\n          )\n        if type(snd) in ITER_TYPES:\n          ret = filter_dict(fst, list(snd))\n          if D: self.debug(color.op(\"..\") + \" returning %s\", color.bold(ret))\n          return ret\n        else:\n          ret = chain.from_iterable(\n              type(x) in ITER_TYPES and x or [x]\n              for x in (e[snd] for e in fst if snd in e)\n          )\n          # 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))))\n          if D: self.debug(color.op(\"..\") + \" returning %s\", color.bold(self.cleanOutput(ret)))\n          return ret\n      elif op == \"[\":\n        len_node = len(node)\n        # TODO move it to tree generation phase\n        if len_node == 1:  # empty list\n          if D: self.debug(\"returning an empty list\")\n          return []\n        if len_node == 2:  # list - preserved to catch possible event of leaving it as '[' operator\n          if D: self.debug(\"doing list mapping\")\n          return [exe(x) for x in node[1]]\n        if len_node == 3:  # selector used []\n          fst = exe(node[1])\n          # check against None\n          if not fst:\n            return fst\n          selector = node[2]\n          if D:\n            self.debug(\n              \"\\n  found selector '%s'.\\n  executing on %s\", color.bold(selector),\n              color.bold(fst)\n            )\n          selectorIsTuple = type(selector) is tuple\n\n          if selectorIsTuple and selector[0] == \"[\":\n            nodeList = []\n            nodeList_append = nodeList.append\n            for i in fst:\n              if D: self.debug(\"setting self.current to %s\", color.bold(i))\n              self.current = i\n              nodeList_append(\n                  exe((selector[0], exe(selector[1]), exe(selector[2])))\n              )\n            if D: self.debug(\n                  \"returning %s objects: %s\", color.bold(len(nodeList)),\n                  color.bold(nodeList)\n              )\n            return nodeList\n\n          if selectorIsTuple and selector[0] == \"(current)\":\n            if D:\n              self.warning(\n                  color.bold(\"$.*[@]\") + \" is eqivalent to \" +\n                  color.bold(\"$.*\") + \"!\"\n              )\n            return fst\n\n          if selectorIsTuple and selector[0] in SELECTOR_OPS:\n            if D: self.debug(\n                  \"found %s operator in selector, %s\", color.bold(selector[0]),\n                  color.bold(selector)\n              )\n            if type(fst) is dict:\n              fst = [fst]\n            # TODO move it to tree building phase\n            if type(selector[1]) is tuple and selector[1][0] == \"name\":\n              selector = (selector[0], selector[1][1], selector[2])\n            selector0 = selector[0]\n            selector1 = selector[1]\n            selector2 = selector[2]\n\n            def exeSelector(fst):\n              for i in fst:\n                if D:\n                  self.debug(\"setting self.current to %s\", color.bold(i))\n                  self.debug(\"  s0: %s\\n  s1: %s\\n  s2: %s\\n  Current: %s\", selector0, selector1, selector2, i)\n                self.current = i\n                if selector0 == \"fn\":\n                  yield exe(selector)\n                # elif type(selector1) in STR_TYPES and False:\n                # \tif D: self.debug(\"found string %s\", type(i))\n                # \ttry:\n                # \t\tif exe((selector0,i[selector1],selector2)):\n                # \t\t\tyield i\n                # \t\t\tif D: self.debug(\"appended\")\n                # \t\tif D: self.debug(\"discarded\")\n                # \texcept Exception as e:\n                # \t\tif D: self.debug(\"discarded, Exception: %s\",color.bold(e))\n                else:\n                  try:\n                    # TODO optimize an event when @ is not used. exe(selector1) can be cached\n                    if exe((selector0, exe(selector1), exe(selector2))):\n                      yield i\n                      if D: self.debug(\"appended %s\", i)\n                    elif D: self.debug(\"discarded\")\n                  except Exception:\n                    if D: self.debug(\"discarded\")\n\n            # if D and nodeList: self.debug(\"returning '%s' objects: '%s'\", color.bold(len(nodeList)), color.bold(nodeList))\n            return exeSelector(fst)\n          self.current = fst\n          snd = exe(node[2])\n          typefst = type(fst)\n          if typefst in [tuple] + ITER_TYPES + STR_TYPES:\n            typesnd = type(snd)\n            # nodes[N]\n            if typesnd in NUM_TYPES or typesnd is str and snd.isdigit():\n              n = int(snd)\n              if D:\n                self.info(\n                    \"getting %sth element from '%s'\", color.bold(n),\n                    color.bold(fst)\n                )\n              if typefst in (generator, chain):\n                if n > 0:\n                  return skip(fst, n)\n                elif n == 0:\n                  return next(fst)\n                else:\n                  fst = list(fst)\n              else:\n                try:\n                  return fst[n]\n                except (IndexError, TypeError):\n                  return None\n            # $.*['string']==$.string\n            if type(snd) in STR_TYPES:\n              return exe((\".\", fst, snd))\n            else:\n              # $.*[@.string] - bad syntax, but allowed\n              return snd\n          else:\n            try:\n              if D: self.debug(\"returning %s\", color.bold(fst[snd]))\n              return fst[snd]\n            except KeyError:\n              # CHECK - is it ok to do that or should it be ProgrammingError?\n              if D: self.debug(\"returning an empty list\")\n              return []\n        raise ProgrammingError(\n            \"Wrong usage of \" + color.bold(\"[\") + \" operator\"\n        )\n      elif op == \"fn\":\n        # Built-in functions\n        fnName = node[1]\n        args = None\n        try:\n          args = [exe(x) for x in node[2:]]\n        except IndexError:\n          if D:\n            self.debug(\"NOT ERROR: can't map '%s' with '%s'\", node[2:], exe)\n        # arithmetic\n        if fnName == \"sum\":\n          args = args[0]\n          if type(args) in NUM_TYPES:\n            return args\n          return sum((x for x in args if type(x) in NUM_TYPES))\n        elif fnName == \"max\":\n          args = args[0]\n          if type(args) in NUM_TYPES:\n            return args\n          return max((x for x in args if type(x) in NUM_TYPES))\n        elif fnName == \"min\":\n          args = args[0]\n          if type(args) in NUM_TYPES:\n            return args\n          return min((x for x in args if type(x) in NUM_TYPES))\n        elif fnName == \"avg\":\n          args = args[0]\n          if type(args) in NUM_TYPES:\n            return args\n          if type(args) not in ITER_TYPES:\n            raise Exception(\"Argument for avg() is not an array\")\n          else:\n            args = list(args)\n          try:\n            return sum(args)/float(len(args))\n          except TypeError:\n            args = [x for x in args if type(x) in NUM_TYPES]\n            self.warning(\"Some items in array were ommited\")\n            return sum(args)/float(len(args))\n        elif fnName == \"round\":\n          return round(*args)\n        # casting\n        elif fnName == \"int\":\n          return int(args[0])\n        elif fnName == \"float\":\n          return float(args[0])\n        elif fnName == \"str\":\n          return str(py2JSON(args[0]))\n        elif fnName in (\"list\", \"array\"):\n          try:\n            a = args[0]\n          except IndexError:\n            return []\n          targs = type(a)\n          if targs is timeutils.datetime.datetime:\n            return timeutils.date2list(a) + timeutils.time2list(a)\n          if targs is timeutils.datetime.date:\n            return timeutils.date2list(a)\n          if targs is timeutils.datetime.time:\n            return timeutils.time2list(a)\n          return list(a)\n        # string\n        elif fnName == \"upper\":\n          return args[0].upper()\n        elif fnName == \"lower\":\n          return args[0].lower()\n        elif fnName == \"capitalize\":\n          return args[0].capitalize()\n        elif fnName == \"title\":\n          return args[0].title()\n        elif fnName == \"split\":\n          return args[0].split(*args[1:])\n        elif fnName == \"slice\":\n          if args and type(args[1]) not in ITER_TYPES:\n            raise ExecutionError(\n                \"Wrong usage of slice(STRING, ARRAY). Second argument is not an array but %s.\"\n                % color.bold(type(args[1]).__name__)\n            )\n          try:\n            pos = list(args[1])\n            if type(pos[0]) in ITER_TYPES:\n              if D: self.debug(\"run slice() for a list of slicers\")\n              return (args[0][x[0]:x[1]] for x in pos)\n            return args[0][pos[0]:pos[1]]\n          except IndexError:\n            if len(args) != 2:\n              raise ProgrammingError(\n                  \"Wrong usage of slice(STRING, ARRAY). Provided %s argument, should be exactly 2.\"\n                  % len(args)\n              )\n        elif fnName == \"escape\":\n          global escape, escapeDict\n          if not escape:\n            from objectpath.utils import escape, escapeDict\n          return escape(args[0], escapeDict)\n        elif fnName == \"unescape\":\n          global unescape, unescapeDict\n          if not unescape:\n            from objectpath.utils import unescape, unescapeDict\n          return unescape(args[0], unescapeDict)\n        elif fnName == \"replace\":\n          if sys.version_info[0] < 3 and type(args[0]) is unicode:\n            args[0] = args[0].encode(\"utf8\")\n          return str.replace(args[0], args[1], args[2])\n        # TODO this should be supported by /regex/\n        # elif fnName==\"REsub\":\n        # \treturn re.sub(args[1],args[2],args[0])\n        elif fnName == \"sort\":\n          if len(args) > 1:\n            key = args[1]\n            a = {\"key\": lambda x: x.get(key, 0)}\n          else:\n            a = {}\n          args = args[0]\n          if D: self.debug(\"doing sort on '%s'\", args)\n          try:\n            return sorted(args, **a)\n          except TypeError:\n            return args\n        elif fnName == \"reverse\":\n          args = args[0]\n          try:\n            args.reverse()\n            return args\n          except TypeError:\n            return args\n        elif fnName == \"unique\":\n          try:\n            return list(set(args[0]))\n          except TypeError:\n            return args[0]\n        elif fnName == \"map\":\n          return chain.from_iterable(map(lambda x: exe((\"fn\", args[0], x)), args[1]))\n        elif fnName in (\"count\", \"len\"):\n          args = args[0]\n          if args in (True, False, None):\n            return args\n          if type(args) in ITER_TYPES:\n            return len(list(args))\n          return len(args)\n        elif fnName == \"join\":\n          try:\n            joiner = args[1]\n          except Exception:\n            joiner = \"\"\n          try:\n            return joiner.join(args[0])\n          except TypeError:\n            try:\n              return joiner.join(map(str, args[0]))\n            except Exception:\n              return args[0]\n        # time\n        elif fnName in (\"now\", \"age\", \"time\", \"date\", \"dateTime\"):\n          if fnName == \"now\":\n            return timeutils.now()\n          if fnName == \"date\":\n            return timeutils.date(args)\n          if fnName == \"time\":\n            return timeutils.time(args)\n          if fnName == \"dateTime\":\n            return timeutils.dateTime(args)\n          # TODO move lang to localize() entirely!\n          if fnName == \"age\":\n            a = {}\n            if len(args) > 1:\n              a[\"reference\"] = args[1]\n            if len(args) > 2:\n              a[\"lang\"] = args[2]\n            return list(timeutils.age(args[0], **a))\n        elif fnName == \"toMillis\":\n          args = args[0]\n          if args.utcoffset() is not None:\n            args = args - args.utcoffset()  # pylint: disable=E1103\n          global calendar\n          if not calendar:\n            import calendar\n          return int(\n              calendar.timegm(args.timetuple())*1000 + args.microsecond/1000\n          )\n        elif fnName == \"localize\":\n          if type(args[0]) is timeutils.datetime.datetime:\n            return timeutils.UTC2local(*args)\n        # polygons\n        elif fnName == \"area\":\n\n          def segments(p):\n            p = list(map(lambda x: x[0:2], p))\n            return zip(p, p[1:] + [p[0]])\n\n          return 0.5*abs(\n              sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(args[0]))\n          )\n        # misc\n        elif fnName == \"keys\":\n          try:\n            return list(args[0].keys())\n          except AttributeError:\n            raise ExecutionError(\n                \"Argument is not \" + color.bold(\"object\") +\n                \" but %s in keys()\" % color.bold(type(args[0]).__name__)\n            )\n        elif fnName == \"values\":\n          try:\n            return list(args[0].values())\n          except AttributeError:\n            raise ExecutionError(\n                \"Argument is not \" + color.bold(\"object\") +\n                \" but %s in values()\" % color.bold(type(args[0]).__name__)\n            )\n        elif fnName == \"type\":\n          ret = type(args[0])\n          if ret in ITER_TYPES:\n            return \"array\"\n          if ret is dict:\n            return \"object\"\n          return ret.__name__\n        elif fnName in self._REGISTERED_FUNCTIONS:\n          return self._REGISTERED_FUNCTIONS[fnName](*args)\n        else:\n          raise ProgrammingError(\n              \"Function \" + color.bold(fnName) + \" does not exist.\"\n          )\n      else:\n        return node\n\n    D = self.D\n    if type(expr) in STR_TYPES:\n      tree = self.compile(expr)\n    elif type(expr) not in (tuple, list, dict):\n      return expr\n    ret = exe(tree)\n    if D: self.end(\"Tree.execute with: %s\", color.bold(self.cleanOutput(ret)))\n    return ret\n\n  def __str__(self):\n    return \"TreeObject()\"\n\n  def __repr__(self):\n    return self.__str__()\n"
  },
  {
    "path": "objectpath/core/parser.py",
    "content": "#!/usr/bin/env python\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Kalbarczyk\n\n# Code from http://effbot.org/zone/simple-top-down-parsing.htm was used in this file.\n# Licence of the code is public domain.\n# Relicenced to MIT by Adrian Kalbarczyk and:\n# - specialized to work with ObjectPath,\n# - optimized\n\nimport sys\n\nif sys.version_info[0] >= 3:\n  from io import StringIO\nelse:\n  from cStringIO import StringIO\n\nfrom objectpath.core import SELECTOR_OPS, NUM_TYPES\n\nsymbol_table = {}\ntoken = nextToken = None\n# TODO optimization ('-',1) -> -1\n# TODO optimization operators should be numbers\n\nTRUE = [\"true\", \"t\"]\nFALSE = [\"false\", \"f\"]\nNONE = [\"none\", \"null\", \"n\", \"nil\"]\n\nclass symbol_base(object):\n  id = None\n  value = None\n  fst = snd = third = None\n\n  def nud(self):\n    raise SyntaxError(\"Syntax error (%r).\" % self.id)\n\n  def led(self):\n    raise SyntaxError(\"Unknown operator (%r).\" % self.id)\n\n  def getTree(self):\n    if self.id == \"(name)\":\n      val = self.value.lower()\n      if val in TRUE:\n        return True\n      elif val in FALSE:\n        return False\n      elif val in NONE:\n        return None\n      return (self.id[1:-1], self.value)\n    elif self.id == \"(number)\":\n      return self.value\n    elif self.id == \"(literal)\":\n      fstLetter = self.value[0]\n      if fstLetter in [\"'\", \"\\\"\"]:\n        return self.value[1:-1]\n      # elif fstLetter.isdigit():\n      # \ttry:\n      # \t\treturn int(self.value)\n      # \texcept:\n      # \t\treturn float(self.value)\n      else:\n        if self.value == \"True\":\n          return True\n        elif self.value == \"False\":\n          return False\n        elif self.value == \"None\":\n          return None\n    ret = [self.id]\n    ret_append = ret.append\n    L = (dict, tuple, list)\n    for i in filter(None, [self.fst, self.snd, self.third]):\n      if type(i) is str:\n        ret_append(i)\n      elif type(i) in L:\n        t = []\n        t_append = t.append\n        if self.id == \"{\":\n          ret = {}\n          for j in list(self.fst.items()):\n            ret[j[0].getTree()] = j[1].getTree()\n          return ret\n        for j in i:\n          try:\n            t_append(j.getTree())\n          except Exception:\n            t_append(j)\n        if self.id in (\"[\", \".\", \"..\"):\n          ret.append(t)\n        else:\n          ret.extend(t)\n        # ret_append(t)\n        # return (self.id,ret[1:])\n      else:\n        if type(self.fst.value) in NUM_TYPES and self.snd is None:\n          if self.id == \"-\":\n            return -self.fst.value\n          if self.id == \"+\":\n            return self.fst.value\n        ret_append(i.getTree())\n    if self.id == \"{\":\n      return {}\n    # if self.id == \"[\" and self.fst == []:\n    # \treturn []\n    if self.id == \"(\":\n      # this will produce (\"fn\",\"fnName\",arg1,arg2,...argN)\n      # try:\n      return tuple([\"fn\", ret[1][1]] + ret[2:])\n    # except:\n    # \tpass\n    return tuple(ret)\n\n  def __repr__(self):\n    if self.id == \"(name)\" or self.id == \"(literal)\":\n      return \"(%s:%s)\" % (self.id[1:-1], self.value)\n    out = [self.id, self.fst, self.snd, self.third]\n    # out=list(map(str, filter(None, out)))\n    return \"(\" + \" \".join(out) + \")\"\n\ndef symbol(ID, bp=0):\n  try:\n    s = symbol_table[ID]\n  except KeyError:\n\n    class s(symbol_base):\n      pass\n\n    s.__name__ = \"symbol-\" + ID  # for debugging\n    s.id = ID\n    s.value = None\n    s.lbp = bp\n    symbol_table[ID] = s\n  else:\n    s.lbp = max(bp, s.lbp)\n  return s\n\n# helpers\n\ndef infix(ID, bp):\n  def led(self, left):\n    self.fst = left\n    self.snd = expression(bp)\n    return self\n\n  symbol(ID, bp).led = led\n\ndef infix_r(ID, bp):\n  def led(self, left):\n    self.fst = left\n    self.snd = expression(bp - 1)\n    return self\n\n  symbol(ID, bp).led = led\n\ndef prefix(ID, bp):\n  def nud(self):\n    self.fst = expression(bp)\n    return self\n\n  symbol(ID).nud = nud\n\ndef advance(ID=None):\n  global token\n  if ID and token.id != ID:\n    raise SyntaxError(\"Expected %r, got %s\" % (ID, token.id))\n  token = nextToken()\n\ndef method(s):\n  # decorator\n  assert issubclass(s, symbol_base)\n\n  def bind(fn):\n    setattr(s, fn.__name__, fn)\n\n  return bind\n\ninfix_r(\"or\", 30)\ninfix_r(\"and\", 40)\nprefix(\"not\", 50)\ninfix(\"in\", 60)\ninfix(\"not\", 60)  # not in\ninfix(\"is\", 60)\ninfix(\"matches\", 60)\ninfix(\"<\", 60)\ninfix(\"<=\", 60)\ninfix(\">\", 60)\ninfix(\">=\", 60)\n# infix(\"\t\", 60); infix(\"!=\", 60); infix(\"==\", 60)\n# infix(\"&\", 90)\n# infix(\"<<\", 100); infix(\">>\", 100)\ninfix(\"+\", 110)\ninfix(\"-\", 110)\ninfix(\"*\", 120)\ninfix(\"/\", 120)\ninfix(\"//\", 120)\ninfix(\"%\", 120)\nprefix(\"-\", 130)\nprefix(\"+\", 130)\n#prefix(\"~\", 130)\n# infix_r(\"**\", 140)\nsymbol(\".\", 150)\nsymbol(\"[\", 150)\nsymbol(\"{\", 150)\nsymbol(\"(\", 150)\n# additional behavior\nsymbol(\"(name)\").nud = lambda self: self\nsymbol(\"(literal)\").nud = lambda self: self\nsymbol(\"(number)\").nud = lambda self: self\nsymbol(\"(end)\")\nsymbol(\")\")\n\n# REGEX\ninfix(\"|\", 0)\ninfix(\"^\", 0)\ninfix(\"?\", 0)\ninfix(\"\\\\\", 0)\n\nsymbol(\"@\")\n\n@method(symbol(\"@\"))\ndef nud(self):  # pylint: disable=E0102\n  self.id = \"(current)\"\n  return self\n\nsymbol(\"!\")\n\n@method(symbol(\"!\"))\ndef nud(self):  # pylint: disable=E0102\n  self.id = \"(node)\"\n  return self\n\n# RegEx\n@method(symbol(\"/\"))\ndef nud(self):  # pylint: disable=E0102\n  self.id = \"re\"\n  regex = []\n  if token.id != \"/\":\n    self_fst_append = regex.append\n    while 1:\n      if token.id == \"/\":\n        break\n      if token.id in [\"(name)\", \"(number)\"]:\n        self_fst_append(str(token.value))\n      else:\n        self_fst_append(token.id)\n      advance()\n  self.fst = \"\".join(regex).replace(\"\\\\\", \"\\\\\\\\\")\n  advance(\"/\")\n  return self\n\n@method(symbol(\"(\"))\ndef nud(self):  # pylint: disable=E0102,W0613\n  expr = expression()\n  advance(\")\")\n  return expr\n\nsymbol(\",\")\n\n@method(symbol(\".\"))\ndef led(self, left):  # pylint: disable=E0102\n  attr = False\n  if token.id == \".\":\n    self.id = \"..\"\n    advance()\n  if token.id == \"@\":\n    attr = True\n    advance()\n  if token.id == \"(\":\n    advance()\n    self.fst = left\n    self.snd = []\n    if token.id != \")\":\n      self_snd_append = self.snd.append\n      while 1:\n        self_snd_append(expression())\n        if token.id != \",\":\n          break\n        advance(\",\")\n    advance(\")\")\n    return self\n  if token.id not in [\"(name)\", \"*\", \"(literal)\", \"(\"]:\n    raise SyntaxError(\"Expected an attribute name.\")\n  self.fst = left\n  if attr:\n    token.value = \"@\" + token.value\n  self.snd = token\n  advance()\n  return self\n\n# handling namespaces; e.g $.a.b.c or $ss.a.b.c\n# default storage is the request namespace\nsymbol(\"$\")\n\n@method(symbol(\"$\"))\ndef nud(self):  # pylint: disable=E0102\n  global token  # pylint: disable=W0602\n  self.id = \"(root)\"\n  if token.id == \".\":\n    self.fst = \"rs\"\n  else:\n    self.fst = token.value\n    advance()\n  return self\n\nsymbol(\"]\")\n\n@method(symbol(\"[\"))\ndef led(self, left):  # pylint: disable=E0102\n  self.fst = left\n  self.snd = expression()\n  advance(\"]\")\n  return self\n\nsymbol(\",\")\n\n# this is for built-in functions\n@method(symbol(\"(\"))\ndef led(self, left):  # pylint: disable=E0102\n  # self.id=\"fn\"\n  self.fst = left\n  self.snd = []\n  if token.id != \")\":\n    self_snd_append = self.snd.append\n    while 1:\n      self_snd_append(expression())\n      if token.id != \",\":\n        break\n      advance(\",\")\n  advance(\")\")\n  return self\n\nsymbol(\":\")\nsymbol(\"=\")\n\n# constants\n\ndef constant(ID):\n  @method(symbol(ID))\n  def nud(self):  # pylint: disable=W0612\n    self.id = \"(literal)\"\n    self.value = ID\n    return self\n\nconstant(\"None\")\nconstant(\"True\")\nconstant(\"False\")\n\n# multitoken operators\n\n@method(symbol(\"not\"))\ndef led(self, left):  # pylint: disable=E0102\n  if token.id != \"in\":\n    raise SyntaxError(\"Invalid syntax\")\n  advance()\n  self.id = \"not in\"\n  self.fst = left\n  self.snd = expression(60)\n  return self\n\n@method(symbol(\"is\"))\ndef led(self, left):  # pylint: disable=E0102\n  if token.id == \"not\":\n    advance()\n    self.id = \"is not\"\n  self.fst = left\n  self.snd = expression(60)\n  return self\n\nsymbol(\"]\")\n\n@method(symbol(\"[\"))\ndef nud(self):  # pylint: disable=E0102\n  self.fst = []\n  if token.id != \"]\":\n    while 1:\n      if token.id == \"]\":\n        break\n      self.fst.append(expression())\n      if token.id not in SELECTOR_OPS + [\",\"]:\n        break\n      advance(\",\")\n  advance(\"]\")\n  return self\n\nsymbol(\"}\")\n\n@method(symbol(\"{\"))\ndef nud(self):  # pylint: disable=E0102\n  self.fst = {}\n  if token.id != \"}\":\n    while 1:\n      if token.id == \"}\":\n        break\n      key = expression()\n      advance(\":\")\n      self.fst[key] = expression()\n      if token.id != \",\":\n        break\n      advance(\",\")\n  advance(\"}\")\n  return self\n\nimport tokenize as tokenizer\ntype_map = {\n    tokenizer.NUMBER: \"(number)\",\n    tokenizer.STRING: \"(literal)\",\n    tokenizer.OP: \"(operator)\",\n    tokenizer.NAME: \"(name)\",\n    tokenizer.ERRORTOKEN:\n    \"(operator)\"  #'$' is recognized in python tokenizer as error token!\n}\n\n# python tokenizer\ndef tokenize_python(program):\n  if sys.version_info[0] < 3:\n    tokens = tokenizer.generate_tokens(StringIO(program).next)\n  else:\n    tokens = tokenizer.generate_tokens(StringIO(program).__next__)\n  for t in tokens:\n    # print type_map[t[0]], t[1]\n    try:\n      # change this to output python values in correct type\n      yield type_map[t[0]], t[1]\n    except KeyError:\n      if t[0] in [tokenizer.NL, tokenizer.COMMENT, tokenizer.NEWLINE]:\n        continue\n      if t[0] == tokenizer.ENDMARKER:\n        break\n      else:\n        raise SyntaxError(\"Syntax error\")\n  yield \"(end)\", \"(end)\"\n\ndef tokenize(program):\n  if isinstance(program, list):\n    source = program\n  else:\n    source = tokenize_python(program)\n  for ID, value in source:\n    if ID == \"(literal)\":\n      symbol = symbol_table[ID]\n      s = symbol()\n      s.value = value\n    elif ID == \"(number)\":\n      symbol = symbol_table[ID]\n      s = symbol()\n      try:\n        s.value = int(value)\n      except Exception:\n        s.value = float(value)\n    elif value == \" \":\n      continue\n    else:\n      # name or operator\n      symbol = symbol_table.get(value)\n      if symbol:\n        s = symbol()\n      elif ID == \"(name)\":\n        symbol = symbol_table[ID]\n        s = symbol()\n        s.value = value\n      else:\n        raise SyntaxError(\"Unknown operator '%s', '%s'\" % (ID, value))\n    yield s\n\n# parser engine\ndef expression(rbp=0):\n  global token\n  t = token\n  token = nextToken()\n  left = t.nud()\n  while rbp < token.lbp:\n    t = token\n    token = nextToken()\n    left = t.led(left)\n  return left\n\ndef parse(expr, D=False):\n  if sys.version_info[0] < 3 and type(expr) is unicode:\n    expr = expr.encode(\"utf8\")\n  if type(expr) is not str:\n    return expr\n  expr = expr.strip()\n  global token, nextToken\n  if sys.version_info[0] >= 3:\n    nextToken = tokenize(expr).__next__\n  else:\n    nextToken = tokenize(expr).next\n  token = nextToken()\n  r = expression().getTree()\n  if D:\n    print(\"PARSE STAGE\")\n    print(r)\n  return r\n"
  },
  {
    "path": "objectpath/shell.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Kalbarczyk\n\nimport argparse\nimport os\nimport sys\ntry:\n  import readline\n  # this is to prevent various tools from deleting import readline\n  ___x = readline.__doc__\nexcept ImportError:\n  import pyreadline as readline\n\nfrom objectpath import Tree, ITER_TYPES\nfrom objectpath.utils.colorify import *  # pylint: disable=W0614\nfrom objectpath.utils import json_ext as json\nfrom objectpath.utils.json_ext import printJSON\n\ntry:\n  import pytz\nexcept ImportError:\n  if os.isatty(sys.stdin.fileno()) and sys.stdout.isatty():\n    print(\"WARNING! pytz is not installed. Localized times are not supported.\")\n\ndef main():\n  parser = argparse.ArgumentParser(description='Command line options')\n  parser.add_argument(\n      '-u', '--url', dest='URL', help='URL containing JSON document.'\n  )\n  # parser.add_argument('-xml', dest='xml', help='[EXPERIMENTAL] Expect XML input.',action='store_true')\n  parser.add_argument(\n      '-d',\n      '--debug',\n      dest='debug',\n      help='Debbuging on/off.',\n      action='store_true'\n  )\n  parser.add_argument(\n      '-p',\n      '--profile',\n      dest='profile',\n      help='Profiling on/off.',\n      action='store_true'\n  )\n  parser.add_argument(\n      '-e',\n      '--expr',\n      dest='expr',\n      help='Expression/query to execute on file, print on stdout and exit.'\n  )\n  parser.add_argument('file', metavar='FILE', nargs=\"?\", help='File name')\n\n  args = parser.parse_args()\n  a = {}\n  expr = args.expr\n\n  if not expr:\n    print(\n        bold(\"ObjectPath interactive shell\") + \"\\n\" + bold(\"ctrl+c\") +\n        \" to exit, documentation at \" +\n        const(\"http://adriank.github.io/ObjectPath\") + \".\\n\"\n    )\n\n  if args.debug:\n    a[\"debug\"] = True\n  if args.profile:\n    try:\n      from guppy import hpy\n    except:\n      pass\n  File = args.file\n  # if args.xml:\n  # \tfrom utils.xmlextras import xml2tree\n  src = False\n\n  if args.URL:\n    if sys.version_info[0] >= 3:\n      from urllib.request import Request, build_opener  # pylint: disable=E0611\n    else:\n      from urllib2 import Request, build_opener\n    request = Request(args.URL)\n    opener = build_opener()\n    request.add_header('User-Agent', 'ObjectPath/1.0 +http://objectpath.org/')\n    src = opener.open(request)\n  elif File:\n    src = open(File, \"r\")\n\n  if not src:\n    if not expr:\n      print(\n          \"JSON document source not specified. Working with an empty object {}.\"\n      )\n    tree = Tree({}, a)\n  else:\n    if not expr:\n      sys.stdout.write(\n          \"Loading JSON document from \" + str(args.URL or File) + \"...\"\n      )\n    sys.stdout.flush()\n    # if args.xml:\n    # \ttree=Tree(json.loads(json.dumps(xml2tree(src))),a)\n    # else:\n    tree = Tree(json.load(src), a)\n    if not expr: print(\" \" + bold(\"done\") + \".\")\n\n  if expr:\n    if args.profile:\n      import cProfile, pstats, StringIO\n      pr = cProfile.Profile()\n      pr.enable()\n    try:\n      ret = tree.execute(expr)\n    except Exception as e:\n      print(e.__class__.__name__ + \": \" + str(e))\n      exit(1)\n    if type(ret) in ITER_TYPES:\n      ret = list(ret)\n    print(json.dumps(ret))\n    if args.profile:\n      pr.disable()\n      s = StringIO.StringIO()\n      sortby = 'cumulative'\n      ps = pstats.Stats(pr, stream=s).sort_stats(sortby)\n      ps.print_stats()\n      print(s.getvalue())\n    return\n\n  try:\n    while True:\n      limitResult = 5\n      try:\n        if sys.version >= '3':\n          r = input(\">>> \")\n        else:\n          r = raw_input(\">>> \")\n\n        if r.startswith(\"all\"):\n          limitResult = -1\n          r = tree.execute(r[3:].strip())\n        else:\n          r = tree.execute(r)\n\n        # python 3 raises error here - unicode is not a proper type there\n        try:\n          if type(r) is unicode:\n            r = r.encode(\"utf8\")\n        except NameError:\n          pass\n        print(printJSON(r, length=limitResult))\n        if args.profile:\n          h = hpy()\n          print(h.heap())\n      except Exception as e:\n        print(e)\n  except KeyboardInterrupt:\n    pass\n  # new line at the end forces command prompt to apear at left\n  print(bold(\"\\nbye!\"))\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "objectpath/utils/__init__.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2010-2014 Adrian Kalbarczyk\n\nfrom itertools import islice\nfrom xml.sax.saxutils import escape, unescape\nfrom objectpath.core import NUM_TYPES, generator, chain, ITER_TYPES\n\nescape = escape\nunescape = unescape\nunescapeDict = {\"&apos;\": \"'\", \"&quot;\": \"\\\"\"}\nescapeDict = {\"'\": \"&apos;\", \"\\\"\": \"&quot;\"}\n\n# islice=islice is an optimization\ndef skip(iterable, n, islice=islice):\n  try:\n    return next(islice(iterable, n, None))\n  except StopIteration:\n    return None\n    # raise IndexError(\"generator index out of range\")\n\ndef filter_dict(iterable, keys):\n  \"\"\"\n\tfilters keys of each element of iterable\n\t$.(a,b) returns all objects from array that have at least one of the keys:\n\t[1,\"aa\",{\"a\":2,\"c\":3},{\"c\":3},{\"a\":1,\"b\":2}].(a,b) -> [{\"a\":2},{\"a\":1,\"b\":2}]\n\t\"\"\"\n  if type(keys) is not list:\n    keys = [keys]\n  for i in iterable:\n    try:\n      d = {}\n      for a in keys:\n        try:\n          d[a] = i[a]\n        except KeyError:\n          pass\n      if d != {}:\n        yield d\n    except Exception:\n      pass\n\ndef flatten(fragment, skip=False):\n  def rec(frg):\n    typefrg = type(frg)\n    if typefrg in ITER_TYPES:\n      for i in frg:\n        for j in rec(i):\n          yield j\n    elif typefrg is dict:\n      yield frg\n      for i in frg.items():\n        for j in rec(i[1]):\n          yield j\n\n  g = rec(fragment)\n  if skip:\n    for i in xrange(skip):\n      g.next()\n  for i in g:\n    yield i\n"
  },
  {
    "path": "objectpath/utils/colorify.py",
    "content": "#!/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  return color(1, s)\n\ndef op(s):\n  return color(32, bold(s))\n\ndef const(s):\n  return color(36, bold(s))\n\ndef string(s):\n  return color(33, bold(s))\n"
  },
  {
    "path": "objectpath/utils/debugger.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# move dbgMap outside\nimport inspect\nfrom objectpath.core import ITER_TYPES\n\nclass Debugger(object):\n  dbg = None\n  _debugStr = None\n  dbgfn = None\n  level = 40\n  CUT_AFTER = 100\n  CRITICAL = 50\n  ERROR = 40\n  WARNING = 30\n  INFO = START = END = 20\n  DEBUG = 10\n  doDebug = False\n\n  def __init__(self):\n    self._debugStr = []\n    self.dbgfn = self.consolelog\n    self.dbgMap = {\n        \"debug\": self.DEBUG,\n        \"info\": self.INFO,\n        \"start\": self.START,\n        \"end\": self.END,\n        \"warning\": self.WARNING,\n        \"error\": self.ERROR,\n        \"critical\": self.CRITICAL\n    }\n    try:\n      self.doDebug = True\n      self.level = self.dbgMap[\"debug\"]\n      self.info(\"All strings will be cut to %s chatacters.\" % self.CUT_AFTER)\n    except (KeyError, TypeError):\n      pass\n\n  def debug(self, *s):\n    if self.dbgfn and self.level <= self.DEBUG:\n      self.dbgfn(\"DEBUG\", s)\n\n  def info(self, *s):\n    if self.dbgfn and self.level <= self.INFO:\n      self.dbgfn(\"INFO\", s)\n\n  def start(self, *s):\n    if self.dbgfn and self.level <= self.INFO:\n      self.dbgfn(\"START\", s)\n\n  def end(self, *s):\n    if self.dbgfn and self.level <= self.INFO:\n      self.dbgfn(\"END\", s)\n\n  def warning(self, *s):\n    if self.dbgfn and self.level <= self.WARNING:\n      self.dbgfn(\"WARNING\", s)\n\n  def error(self, *s):\n    if self.dbgfn and self.level <= self.ERROR:\n      self.dbgfn(\"ERROR\", s)\n\n  def critical(self, *s):\n    if self.dbgfn and self.level <= self.CRITICAL:\n      self.dbgfn(\"CRITICAL\", s)\n\n  def lineno(self):\n    \"\"\"Returns the current line number in our program.\"\"\"\n    return inspect.currentframe().f_back.f_back.f_back.f_lineno\n\n  def cleanOutput(self, o):\n    toOutput = o\n    if type(toOutput) in ITER_TYPES:\n      toOutput = list(toOutput)\n    if type(toOutput) is tuple:\n      return tuple(map(lambda x: type(x) in ITER_TYPES and list(x) or x, toOutput))\n    return toOutput\n\n  def consolelog(self, lvl, s):\n    def f(x):\n      try:\n        if type(x) is unicode:\n          x = x.encode(\"utf8\")\n      except NameError:\n        pass\n      if self.CUT_AFTER and type(x) is dict:\n        s = []\n        for i in x.items():\n          s.append(\"'%s': %s\" % (i[0], repr(i[1])[:self.CUT_AFTER]))\n          if len(s[-1]) > self.CUT_AFTER:\n            s.append(\"...\\033[0m\")\n        return \"{\\n\\t\" + \",\\n\\t\".join(s) + \"\\n}\"\n      s = str(x).replace(\"\\n\", \"\").replace(\"\\t\", \"\").replace(\"u'\", \"'\")\n      if self.CUT_AFTER and len(s) > self.CUT_AFTER:\n        return s[:self.CUT_AFTER] + \"...\\033[0m\"\n      else:\n        return x\n\n    if len(s) > 1:\n      v = tuple(map(f, s[1:]))\n      self._debugStr.append((lvl, s[0] % v))\n      print(\n        lvl + \"@\" + str(self.lineno()) + \" \" + (s[0] % v).replace(\"u'\", \"'\")\n      )\n    else:\n      self._debugStr.append((lvl, s[0]))\n      print(lvl + \"@\" + str(self.lineno()) + \" \" + f(s[0]))\n"
  },
  {
    "path": "objectpath/utils/json_ext.py",
    "content": "#!/usr/bin/env python\ntry:\n  import simplejson as json\nexcept ImportError:\n  try:\n    import json\n  except ImportError:\n    raise Exception(\"JSONNotFound\")\ntry:\n  import ujson as json_fast\nexcept ImportError:\n  json_fast = json\n\nfrom types import GeneratorType as generator\n\nfrom objectpath.core import ITER_TYPES, STR_TYPES, NUM_TYPES\nfrom objectpath.utils.colorify import *  # pylint: disable=W0614\n\nload = json_fast.load\n\ndef loads(s):\n  if s.find(\"u'\") != -1:\n    s = s.replace(\"u'\", \"'\")\n  s = s.replace(\"'\", '\"')\n  try:\n    return json_fast.loads(s)  # ,object_hook=object_hook)\n  except ValueError as e:\n    raise Exception(str(e) + \" \" + s)\n\ndef default(obj):\n  if isinstance(obj, generator):\n    return list(obj)\n\ndef dumps(s, default=default, indent=None):\n  return json.dumps(s, default=default, indent=indent, separators=(',', ':'))\n\ndump = json.dump\n\ndef py2JSON(o):\n  if o is True:\n    return 'true'\n  if o is False:\n    return 'false'\n  if o is None:\n    return 'null'\n  if type(o) in NUM_TYPES:\n    return o\n  # TODO - check if that is correct\n  if type(o) is tuple:\n    return list(o)\n  elif type(o) in ITER_TYPES + [list, str]:\n    return o\n  try:\n    return str(o)\n  except UnicodeEncodeError:\n    return o.encode(\"utf8\")\n  except Exception:\n    return o\n\nLAST_LIST = None\n\ndef printJSON(o, length=5, depth=5):\n  spaces = 2\n\n  def plus():\n    currIndent[0] += 1\n\n  def minus():\n    currIndent[0] -= 1\n\n  def out(s):\n    try:\n      s = str(s)\n    except Exception:\n      pass\n    if not ret:\n      ret.append(s)\n    elif ret[-1][-1] == \"\\n\":\n      ret.append(currIndent[0]*spaces*\" \" + s)\n    else:\n      ret.append(s)\n\n  def rec(o):\n    if type(o) in ITER_TYPES:\n      o = list(o)\n      if currDepth[0] >= depth:\n        out(\"<array of \" + str(len(o)) + \" items>\")\n        return\n      out(\"[\")\n      if len(o) > 0:\n        if len(o) > 1:\n          out(\"\\n\")\n          plus()\n        for i in o[0:length]:\n          rec(i)\n          out(\",\\n\")\n        if length == -1:\n          rec(o[-1])\n          out(\",\\n\")\n\n        if length != -1 and len(o) > length:\n          out(\"... (\" + str(len(o) - length) + \" more items)\\n\")\n        else:\n          ret.pop()\n          if len(o) > 1:\n            out(\"\\n\")\n        if len(o) > 1:\n          minus()\n      out(\"]\")\n\n    elif type(o) is dict:\n      currDepth[0] += 1\n      if currDepth[0] > depth:\n        out(\"{...}\")\n        return\n      keys = o.keys()\n      out(\"{\")\n      if len(keys) > 0:\n        if len(keys) > 1:\n          plus()\n          out(\"\\n\")\n        for k in o.keys():\n          out(string('\"' + str(k) + '\"') + \": \")\n          rec(o[k])\n          out(\",\\n\")\n        ret.pop()\n        if len(keys) > 1:\n          minus()\n          out(\"\\n\")\n      out(\"}\")\n    else:\n      if type(o) in NUM_TYPES:\n        out(const(o))\n      elif o in [None, False, True]:\n        out(const(py2JSON(o)))\n      elif type(o) in STR_TYPES:\n        out(string('\"' + o + '\"'))\n      else:\n        out(string(o))\n    currDepth[0] -= 1\n\n  currIndent = [0]\n  currDepth = [0]\n  ret = []\n  rec(o)\n  currIndent[0] = 0\n  currDepth[0] = 0\n  return \"\".join(ret)\n"
  },
  {
    "path": "objectpath/utils/timeutils.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# This file is part of ObjectPath released under MIT license.\n# Copyright (C) 2008-2010 Adrian Kalbarczyk\n\nimport datetime\nimport sys, os\ntry:\n  import pytz\n  TIMEZONE_CACHE = {\"UTC\": pytz.utc}\nexcept ImportError:\n  pass\n\nfrom objectpath.core import STR_TYPES\n\nHOURS_IN_DAY = 24\n\nnow = datetime.datetime.now\n\ndef round9_10(n):\n  i = int(n)\n  if n - i > 0.9:\n    return i + 1\n  return i\n\n# TODO its 31 minuta, should be 31 minut - probably done\n\ndef age(date, reference=None, lang=\"en\"):\n  if reference is None:\n    reference = now()\n  td = reference - date  #TimeDelta\n\n  days = float(td.days)\n  langIsPL = lang == \"pl\"\n  if days:\n    years = round9_10(days/356)\n    if years:\n      if langIsPL:\n        return (years, years == 1 and \"rok\" or years < 5 and \"lata\" or \"lat\")\n      else:\n        return (years, years == 1 and \"year\" or \"years\")\n\n    months = round9_10(days/30)\n    if months:\n      if langIsPL:\n        return (\n            months, months == 1 and \"miesiąc\" or 1 < months < 5 and \"miesiące\"\n            or \"miesięcy\"\n        )\n      else:\n        return (months, months == 1 and \"month\" or \"months\")\n\n    weeks = round9_10(days/7)\n    if weeks:\n      if langIsPL:\n        return (\n            weeks, weeks == 1 and \"tydzień\"\n            or weeks % 10 in [0, 1, 5, 6, 7, 8, 9] and \"tygodni\" or \"tygodnie\"\n        )\n      else:\n        return (weeks, weeks == 1 and \"week\" or \"weeks\")\n\n    days = int(days)\n    if langIsPL:\n      return (days, days == 1 and \"dzień\" or \"dni\")\n    else:\n      return (days, days == 1 and \"day\" or \"days\")\n\n  seconds = float(td.seconds)\n  if seconds is not None:\n    hours = round9_10(seconds/3600)\n    if hours:\n      if langIsPL:\n        return (\n            hours, hours == 1 and \"godzina\" or 1 < hours < 5 and \"godziny\"\n            or \"godzin\"\n        )\n      else:\n        return (hours, hours == 1 and \"hour\" or \"hours\")\n\n    minutes = round9_10(seconds/60)\n    if minutes:\n      if langIsPL:\n        return (\n            minutes, minutes == 1 and \"minuta\" or 1 < minutes < 5 and \"minuty\"\n            or \"minut\"\n        )\n      else:\n        return (minutes, minutes == 1 and \"minute\" or \"minutes\")\n\n    seconds = int(seconds)\n    if langIsPL:\n      return (\n          seconds, seconds == 1 and \"sekunda\" or 1 < seconds < 5 and \"sekundy\"\n          or \"sekund\"\n      )\n    else:\n      return (seconds, seconds == 1 and \"second\" or \"seconds\")\n  # return (0,\"seconds\")\n\ndef date(d):\n  if d:\n    d = d[0]\n    t = type(d)\n    if t is datetime.datetime:\n      return datetime.date(d.year, d.month, d.day)\n    if t in (tuple, list):\n      return datetime.date(*d)\n  return datetime.date.today()\n\ndef date2list(d):\n  return [d.year, d.month, d.day]\n\ndef time(d):\n  if not d or not d[0]:\n    d = now()\n  else:\n    d = d[0]\n    t = type(d)\n    if t in (tuple, list):\n      return datetime.time(*d)\n  return datetime.time(d.hour, d.minute, d.second, d.microsecond)\n\ndef time2list(t):\n  return [t.hour, t.minute, t.second, t.microsecond]\n\ndef addTimes(fst, snd):\n  l1 = time2list(fst)\n  l2 = time2list(snd)\n  t = [l1[0] + l2[0], l1[1] + l2[1], l1[2] + l2[2], l1[3] + l2[3]]\n  t2 = []\n  one = 0\n  ms = t[3]\n  if ms >= 1000000:\n    t2.append(ms - 1000000)\n    one = 1\n  else:\n    t2.append(ms)\n  for i in (t[2], t[1]):\n    i = i + one\n    one = 0\n    if i >= 60:\n      t2.append(i - 60)\n      one = 1\n    # elif i==60:\n    # \tt2.append(0)\n    # \tone=1\n    else:\n      t2.append(i)\n  hour = t[0] + one\n  if hour >= HOURS_IN_DAY:\n    t2.append(hour - HOURS_IN_DAY)\n  else:\n    t2.append(hour)\n  return datetime.time(*reversed(t2))\n\ndef subTimes(fst, snd):\n  l1 = time2list(fst)\n  l2 = time2list(snd)\n  t = [l1[0] - l2[0], l1[1] - l2[1], l1[2] - l2[2], l1[3] - l2[3]]\n  t2 = []\n  one = 0\n  ms = t[3]\n  if ms < 0:\n    t2.append(1000000 + ms)\n    one = 1\n  else:\n    t2.append(ms)\n  for i in (t[2], t[1]):\n    i = i - one\n    one = 0\n    if i >= 0:\n      t2.append(i)\n    else:\n      t2.append(60 + i)\n      one = 1\n  hour = t[0] - one\n  if hour < 0:\n    t2.append(HOURS_IN_DAY + hour)\n  else:\n    t2.append(hour)\n  return datetime.time(*reversed(t2))\n\ndef dateTime(arg):\n  \"\"\"\n\td may be:\n\t\t- datetime()\n\t\t- [y,m,d,h[,m[,ms]]]\n\t\t- [date(),time()]\n\t\t- [[y,m,d],[h,m,s,ms]]\n\t\tand permutations of above\n\t\"\"\"\n  l = len(arg)\n  if l == 1:\n    dt = arg[0]\n    typed = type(dt)\n    if typed is datetime.datetime:\n      return dt\n    if typed in (tuple, list) and len(dt) in [5, 6, 7]:\n      return datetime.datetime(*dt)\n  if l == 2:\n    date = time = None\n    typeArg0 = type(arg[0])\n    typeArg1 = type(arg[1])\n    if typeArg0 in STR_TYPES:\n      return datetime.datetime.strptime(arg[0], arg[1])\n    if typeArg0 is datetime.date:\n      d = arg[0]\n      date = [d.year, d.month, d.day]\n    if typeArg0 in (tuple, list):\n      date = arg[0]\n    if typeArg1 is datetime.time:\n      t = arg[1]\n      time = [t.hour, t.minute, t.second, t.microsecond]\n    if typeArg1 in (tuple, list):\n      time = arg[1]\n    return datetime.datetime(*date + time)\n\n# dt - dateTime, tzName is e.g. 'Europe/Warsaw'\ndef UTC2local(dt, tzName=\"UTC\"):\n  try:\n    if tzName in TIMEZONE_CACHE:\n      tz = TIMEZONE_CACHE[tzName]\n    else:\n      tz = TIMEZONE_CACHE[tzName] = pytz.timezone(tzName)\n    return TIMEZONE_CACHE[\"UTC\"].localize(dt).astimezone(tz)\n  except Exception:\n    return dt\n"
  },
  {
    "path": "requirements.txt",
    "content": "pytz\n"
  },
  {
    "path": "setup.cfg",
    "content": "[bdist_wheel]\nuniversal = 1\n[yapf]\n# split_all_comma_separated_values=True\nno_spaces_around_selected_binary_operators=\"*,/\"\nindent_width=2\ndedent_closing_brackets=True\ncoalesce_brackets=True\nblank_lines_around_top_level_definition=1\ncontinuation_indent_width=2\n"
  },
  {
    "path": "setup.py",
    "content": "import os\nfrom setuptools import setup\n\ndef read(*rnames):\n  return open(os.path.join(os.path.dirname(__file__), *rnames)).read()\n\nlong_description = (read('README.rst') + '\\n' + 'Download\\n' '********\\n')\n\nsetup(\n  name='objectpath',\n  version=read('VER').strip(),\n  description='The agile query language for semi-structured data. #JSON',\n  long_description=long_description,\n  url='http://adriank.github.io/ObjectPath',\n  author='Adrian Kalbarczyk',\n  author_email='adrian.kalbarczyk@gmail.com',\n  license='MIT License',\n  packages=['objectpath', 'objectpath.utils', 'objectpath.core'],\n  # package_dir={'': 'objectpath'},\n  keywords=\"query, tree, JSON, nested structures\",\n  classifiers=[\n    \"Development Status :: 6 - Mature\", \"Intended Audience :: Developers\",\n    \"Intended Audience :: Science/Research\", \"License :: OSI Approved :: MIT License\",\n    \"Programming Language :: Python\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\"\n  ],\n  install_requires=[\n    'pytz',\n  ],\n  zip_safe=True,\n  entry_points={'console_scripts': ['objectpath = objectpath.shell:main']},\n  test_suite=\"tests\"\n)\n"
  },
  {
    "path": "shell.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom objectpath import shell\n\nshell.main()\n"
  },
  {
    "path": "testObjectPath.py",
    "content": "#!/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",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#unit tests for ACR functions\n\nfrom .test_ObjectPath import op_test\nfrom .test_utils import utils_test\n\nimport unittest\n\ndef doTests():\n  print('Started ObjectPath Python implementation testing.\\n')\n  unittest.TextTestRunner(verbosity=2).run(op_test)\n  unittest.TextTestRunner(verbosity=2).run(utils_test)\n"
  },
  {
    "path": "tests/test_ObjectPath.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom objectpath.core.interpreter import *\nfrom objectpath.core import ProgrammingError, ExecutionError\nfrom random import randint, choice\n#from bson.objectid import ObjectId\nimport sys, unittest, os\n\nsys.setrecursionlimit(20000)\n\nobject1 = {\n  \"__lang__\": \"en\",\n  \"test\": {\n    \"_id\": 1,\n    \"name\": \"aaa\",\n    \"o\": {\n      \"_id\": 2\n    },\n    \"l\": [{\n      \"_id\": 3,\n      \"aaa\": \"ddd\",\n      \"false\": 2\n    }, {\n      \"_id\": 4\n    }]\n  }\n}\n\nobject2 = {\n  \"store\": {\n    \"book\": [{\n      \"id\":1,\n      \"category\": \"reference\",\n      \"author\": \"Nigel Rees\",\n      \"title\": \"Sayings of the Century\",\n      \"price\": 8.95\n    },\n    {\n      \"category\": \"fiction\",\n      \"author\": \"Evelyn Waugh\",\n      \"title\": \"Sword of Honour\",\n      \"price\": 12.99\n    },\n    {\n      \"category\": \"fiction\",\n      \"author\": \"Herman Melville\",\n      \"title\": \"Moby Dick\",\n      \"isbn\": \"0-553-21311-3\",\n      \"price\": 8.99\n    },\n    {\n      \"category\": \"fiction\",\n      \"author\": \"J. R. R. Tolkien\",\n      \"title\": \"The Lord of the Rings\",\n      \"isbn\": \"0-395-19395-8\",\n      \"price\": 22.99\n    }],\n    \"bicycle\": {\n      \"color\": \"red\",\n      \"price\": 19.95\n    },\n    \"k\": [{\n      \"_id\": 4\n    }]\n  }\n}\n\nobject3 = {\n  \"item_1\": {\n    \"value\": \"foo\",\n    \"x\": 5.6,\n    \"y\": 9\n  },\n  \"item_2\": {\n    \"value\": \"bar\",\n    \"x\": 5.6,\n    \"y\": 9.891\n  },\n  \"item_3\": {\n    \"value\": \"foobar\",\n    \"x\": 5.6,\n    \"y\": 9.8\n  }\n}\n\nobject4 = {\n  \"root\": {\n    \"response\": {\n      \"200\": {\n        \"value\": 5,\n      },\n      \"201\": {\n        \"value\": 4,\n      },\n      \"404\": {\n        \"value\": 0,\n      }\n    }\n  }\n}\n\ntree1 = Tree(object1)\ntree2 = Tree(object2)\ntree3 = Tree(object3)\ntree4 = Tree(object4)\n\ndef execute_raw(expr):\n  return tree1.execute(expr)\n\nTYPES = [generator, chain]\nif sys.version_info.major > 2:\n  TYPES += [map]\n\nTYPES = tuple(TYPES)\n\ndef execute(expr):\n  r = tree1.execute(expr)\n  if isinstance(r, TYPES):\n    return list(r)\n  else:\n    return r\n\ndef execute2(expr):\n  r = tree2.execute(expr)\n  if isinstance(r, TYPES):\n    return list(r)\n  else:\n    return r\n\ndef execute3(expr):\n  r = tree3.execute(expr)\n  if isinstance(r, TYPES):\n    return list(r)\n  else:\n    return r\n\ndef execute4(expr):\n  r = tree4.execute(expr)\n  if isinstance(r, TYPES):\n    return list(r)\n  else:\n    return r\n\nclass ObjectPath(unittest.TestCase):\n\n  def test_simple_types(self):\n    self.assertEqual(execute(\"null\"), None)\n    self.assertEqual(execute(\"true\"), True)\n    self.assertEqual(execute(\"false\"), False)\n    self.assertEqual(execute(\"''\"), \"\")\n    self.assertEqual(execute('\"\"'), \"\")\n    self.assertEqual(execute(\"2\"), 2)\n    self.assertEqual(execute(\"2.0\"), 2.0)\n\n  def test_arrays(self):\n    self.assertEqual(execute(\"[]\"), [])\n    self.assertEqual(list(execute(\"[1,2,3]\")), [1, 2, 3])\n    self.assertEqual(\n      list(execute(\"[false,null,true,'',\\\"\\\",2,2.0,{}]\")),\n      [False, None, True, '', \"\", 2, 2.0, {}]\n    )\n\n  def test_objects(self):\n    self.assertEqual(execute(\"{}\"), {})\n    self.assertEqual(\n      execute(\"{a:1,b:false,c:'string'}\"), {\n        \"a\": 1,\n        \"b\": False,\n        \"c\": 'string'\n      }\n    )\n    self.assertEqual(\n      execute(\"{'a':1,'b':false,'c':'string'}\"), {\n        \"a\": 1,\n        \"b\": False,\n        \"c\": 'string'\n      }\n    )\n\n  def test_arithm_add(self):\n    self.assertEqual(execute(\"2+3\"), 5)\n    self.assertEqual(execute(\"2+3+4\"), 9)\n    self.assertEqual(execute(\"++3\"), 3)\n    # null is treated as neutral value\n    self.assertEqual(execute(\"null+3\"), 3)\n    self.assertEqual(execute(\"3+null\"), 3)\n\n  def test_arithm_sub(self):\n    self.assertEqual(execute(\"-1\"), -1)\n    self.assertEqual(execute(\"2-3\"), 2 - 3)\n    self.assertEqual(execute(\"2.2-3.4\"), 2.2 - 3.4)\n    self.assertEqual(execute(\"-+-3\"), 3)\n    self.assertEqual(execute(\"+-+3\"), -3)\n\n  def test_arithm_mul(self):\n    self.assertEqual(execute(\"2*3*5*6\"), 180)\n\n  def test_arithm_mod(self):\n    self.assertEqual(execute(\"2%3\"), 2.0 % 3)\n    self.assertEqual(execute(\"2.0%3\"), 2.0 % 3)\n    self.assertEqual(execute(\"float(2)%3\"), float(2) % 3)\n\n  def test_arithm_div(self):\n    self.assertEqual(execute(\"2/3\"), 2.0/3)\n    self.assertEqual(execute(\"2.0/3\"), 2.0/3)\n    self.assertEqual(execute(\"float(2)/3\"), float(2)/3)\n\n  def test_arithm_group(self):\n    self.assertEqual(execute(\"2-3+4+5-7\"), 2 - 3 + 4 + 5 - 7)\n    self.assertEqual(execute(\"33*2/5-2\"), 33*2/5.0 - 2)\n    self.assertEqual(execute(\"33-4*5+2/6\"), 33 - 4*5 + 2/6.0)\n    #self.assertEqual(execute(\"2//3//4//5\"), ('//', ('//', ('//', 2, 3), 4), 5))\n\n  def test_arithm_parentheses(self):\n    self.assertEqual(execute(\"+6\"), 6)\n    self.assertEqual(execute(\"2+2*2\"), 6)\n    self.assertEqual(execute(\"2+(2*2)\"), 6)\n    self.assertEqual(execute(\"(2+2)*2\"), 8)\n    self.assertEqual(execute(\"(33-4)*5+2/6\"), (33 - 4)*5 + 2/6.0)\n    self.assertEqual(execute(\"2/3/(4/5)*6\"), 2/3.0/(4/5.0)*6)\n    self.assertEqual(execute(\"((2+4))+6\"), ((2 + 4)) + 6)\n\n  def test_logic_negatives(self):\n    self.assertEqual(execute(\"not false\"), True)\n    self.assertEqual(execute(\"not null\"), True)\n    self.assertEqual(execute(\"not 0\"), True)\n    self.assertEqual(execute(\"not 0.0\"), True)\n    self.assertEqual(execute(\"not ''\"), True)\n    self.assertEqual(execute(\"not []\"), True)\n    self.assertEqual(execute(\"not {}\"), True)\n\n  def test_logic_not(self):\n    self.assertEqual(execute(\"not false\"), True)\n    self.assertEqual(execute(\"not not not false\"), True)\n\n  def test_logic_or(self):\n    self.assertEqual(execute(\"1 or 2\"), 1)\n    self.assertEqual(execute(\"0 or 2\"), 2)\n    self.assertEqual(execute(\"'a' or 0 or 3\"), 'a')\n    self.assertEqual(\n      execute(\"null or false or 0 or 0.0 or '' or [] or {}\"), {}\n    )\n\n  def test_logic_and(self):\n    self.assertEqual(execute(\"1 and 2\"), 2)\n    self.assertEqual(execute(\"0 and 2\"), 0)\n    self.assertEqual(execute(\"'a' and false and 3\"), False)\n    self.assertEqual(\n      execute(\"true and 1 and 1.0 and 'foo' and [1] and {a:1}\"), {\"a\": 1}\n    )\n\n  def test_comparison_regex(self):\n    self.assertIsInstance(execute(\"/aaa/\"), type(re.compile(\"\")))\n    self.assertEqual(execute(\"/.*aaa/ matches 'xxxaaaadddd'\"), True)\n    self.assertEqual(execute(\"'.*aaa' matches 'xxxaaaadddd'\"), True)\n    self.assertEqual(execute(\"'.*aaa' matches ['xxxaaaadddd', 'xxx']\"), True)\n\n  def test_comparison_is(self):\n    self.assertEqual(execute(\"2 is 2\"), True)\n    self.assertEqual(execute(\"'2' is 2\"), True)\n    self.assertEqual(execute(\"2 is '2'\"), True)\n    self.assertEqual(execute(\"2 is 2.0\"), True)\n    self.assertEqual(execute(\"0.1+0.2 is 0.3\"), True)\n    self.assertEqual(execute(\"[] is []\"), True)\n    self.assertEqual(execute(\"[1] is [1]\"), True)\n    self.assertEqual(execute(\"{} is {}\"), True)\n    self.assertEqual(execute(\"{} is []\"), False)\n    self.assertEqual(execute(\"None is 'aaa'\"), False)\n    self.assertEqual(execute(\"None is None\"), True)\n    self.assertEqual(execute(\"{'aaa':1} is {'aaa':1}\"), True)\n    #oid=ObjectId()\n    #self.assertEqual(execute(\"ObjectID('\"+str(oid)+\"') is '\"+str(oid)+\"'\"), True)\n\n  def test_comparison_isnot(self):\n    self.assertEqual(execute(\"None is not None\"), False)\n    self.assertEqual(execute(\"None is not 'aaa'\"), True)\n    self.assertEqual(execute(\"{} is not []\"), True)\n    self.assertEqual(execute(\"3 is not 6\"), True)\n    self.assertEqual(execute(\"3 is not '3'\"), False)\n    self.assertEqual(execute(\"[] is not [1]\"), True)\n    self.assertEqual(execute(\"[] is not []\"), False)\n    self.assertEqual(execute(\"{'aaa':2} is not {'bbb':2}\"), True)\n    self.assertEqual(execute(\"{} is not {}\"), False)\n\n  def test_membership_in(self):\n    self.assertEqual(execute(\"4 in [6,4,3]\"), True)\n    self.assertEqual(execute(\"4 in {4:true}\"), True)\n    self.assertEqual(execute(\"[2,3] in [6,4,3]\"), True)\n\n  def test_membership_notin(self):\n    self.assertEqual(execute(\"4 not in []\"), True)\n    self.assertEqual(execute(\"1 not in {'232':2}\"), True)\n    self.assertEqual(execute(\"[2,5] not in [6,4,3]\"), True)\n\n  def test_complex(self):\n    self.assertEqual(execute(\"23 is not 56 or 25 is 57\"), True)\n    self.assertEqual(execute(\"2+3/4-6*7>0 or 10 is not 11 and 14\"), 14)\n\n  def test_comparison_lt(self):\n    self.assertEqual(execute(\"2<3\"), True)\n    self.assertEqual(execute(\"3<3\"), False)\n    self.assertEqual(execute(\"2<=2\"), True)\n    self.assertEqual(execute(\"2<=1\"), False)\n\n  def test_comparison_gt(self):\n    self.assertEqual(execute(\"5>4\"), True)\n    self.assertEqual(execute(\"5>5\"), False)\n    self.assertEqual(execute(\"5>=5\"), True)\n\n  def test_concatenation(self):\n    self.assertEqual(execute(\"'a'+'b'+\\\"c\\\"\"), 'abc')\n    self.assertEqual(execute(\"'5'+5\"), '55')\n    self.assertEqual(execute(\"5+'5'\"), 10)\n    self.assertEqual(list(execute(\"[1,2,4] + [3,5]\")), [1, 2, 4, 3, 5])\n    self.assertEqual(\n      execute('{\"a\":1,\"b\":2} + {\"a\":2,\"c\":3}'), {\n        \"a\": 2,\n        \"b\": 2,\n        \"c\": 3\n      }\n    )\n    self.assertRaises(\n      ProgrammingError, lambda: execute('{\"a\":1,\"b\":2} + \"sss\"')\n    )\n\n  def test_builtin_casting(self):\n    self.assertEqual(execute(\"str('foo')\"), 'foo')\n    self.assertEqual(execute(\"str(1)\"), '1')\n    self.assertEqual(execute(\"str(1.0)\"), '1.0')\n    self.assertEqual(execute(\"str(1 is 1)\"), 'true')\n    self.assertEqual(execute(\"int(1)\"), 1)\n    self.assertEqual(execute(\"int(1.0)\"), 1)\n    self.assertEqual(execute(\"int('1')\"), 1)\n    #Python can't handle that\n    #self.assertEqual(execute(\"int('1.0')\"), 1)\n    self.assertEqual(execute(\"float(1.0)\"), 1.0)\n    self.assertEqual(execute(\"float(1)\"), 1.0)\n    self.assertEqual(execute(\"float('1')\"), 1.0)\n    self.assertEqual(execute(\"float('1.0')\"), 1.0)\n    self.assertEqual(execute(\"array()\"), [])\n    self.assertEqual(execute(\"array([])\"), [])\n    self.assertEqual(execute(\"array('abc')\"), ['a', 'b', 'c'])\n    self.assertEqual(\n      execute(\"array(dateTime([2011,4,8,12,0]))\"), [2011, 4, 8, 12, 0, 0, 0]\n    )\n    self.assertEqual(execute(\"array(date([2011,4,8]))\"), [2011, 4, 8])\n    self.assertEqual(execute(\"array(time([12,12,30]))\"), [12, 12, 30, 0])\n\n  def test_builtin_arithmetic(self):\n    self.assertEqual(execute(\"sum([1,2,3,4])\"), sum([1, 2, 3, 4]))\n    self.assertEqual(execute(\"sum([2,3,4,'333',[]])\"), 9)\n    self.assertEqual(execute(\"sum(1)\"), 1)\n    self.assertEqual(execute(\"min([1,2,3,4])\"), min([1, 2, 3, 4]))\n    self.assertEqual(execute(\"min([2,3,4,'333',[]])\"), 2)\n    self.assertEqual(execute(\"min(1)\"), 1)\n    self.assertEqual(execute(\"max([1,2,3,4])\"), max([1, 2, 3, 4]))\n    self.assertEqual(execute(\"max([2,3,4,'333',[]])\"), 4)\n    self.assertEqual(execute(\"max(1)\"), 1)\n    self.assertEqual(execute(\"avg([1,2,3,4])\"), 2.5)\n    self.assertEqual(execute(\"avg([1,3,3,1])\"), 2.0)\n    self.assertEqual(execute(\"avg([1.1,1.3,1.3,1.1])\"), 1.2000000000000002)\n    self.assertEqual(execute(\"avg([2,3,4,'333',[]])\"), 3)\n    self.assertEqual(execute(\"avg(1)\"), 1)\n    self.assertEqual(execute(\"round(2/3)\"), round(2.0/3))\n    self.assertEqual(execute(\"round(2/3,3)\"), round(2.0/3, 3))\n    # edge cases\n    self.assertEqual(execute(\"avg(1)\"), 1)\n    # should ommit 'sss'\n    self.assertEqual(execute(\"avg([1,'sss',3,3,1])\"), 2.0)\n\n  def test_builtin_string(self):\n    self.assertEqual(execute(\"replace('foobar','oob','baz')\"), 'fbazar')\n    self.assertEqual(execute(\"\"\"escape('&lt;')\"\"\"), \"&amp;lt;\")\n    self.assertEqual(execute(\"\"\"escape('<\"&>')\"\"\"), \"&lt;&quot;&amp;&gt;\")\n    self.assertEqual(execute(\"\"\"unescape('&lt;&quot;&amp;&gt;')\"\"\"), \"<\\\"&>\")\n    self.assertEqual(execute(\"upper('aaa')\"), \"AAA\")\n    self.assertEqual(execute(\"lower('AAA')\"), \"aaa\")\n    self.assertEqual(execute(\"title('AAA aaa')\"), \"Aaa Aaa\")\n    self.assertEqual(execute(\"capitalize('AAA Aaa')\"), \"Aaa aaa\")\n    self.assertEqual(execute(\"split('aaa aaa')\"), [\"aaa\", \"aaa\"])\n    self.assertEqual(execute(\"split('aaaxaaa','x')\"), [\"aaa\", \"aaa\"])\n    self.assertEqual(execute(\"join(['aaą','aaę'],'ć')\"), \"aaąćaaę\")\n    self.assertEqual(execute(\"join(['aaa','aaa'])\"), \"aaaaaa\")\n    self.assertEqual(execute(\"join(['aaa','aaa',3,55])\"), \"aaaaaa355\")\n    self.assertEqual(execute('slice(\"Hello world!\", [6, 11])'), \"world\")\n    self.assertEqual(execute('slice(\"Hello world!\", [6, -1])'), \"world\")\n    self.assertEqual(\n      execute('slice(\"Hello world!\", [[0,5], [6, 11]])'), [\"Hello\", \"world\"]\n    )\n    self.assertRaises(ProgrammingError, lambda: execute('slice()'))\n    self.assertRaises(ExecutionError, lambda: execute('slice(\"\", {})'))\n    self.assertEqual(execute('map(upper, [\"a\", \"b\", \"c\"])'), [\"A\", \"B\", \"C\"])\n\n  def test_builtin_arrays(self):\n    self.assertEqual(execute(\"sort([1,2,3,4]+[2,4])\"), [1, 2, 2, 3, 4, 4])\n    self.assertEqual(execute(\"sort($.._id)\"), [1, 2, 3, 4])\n    self.assertEqual(\n      execute(\"sort($..l.*, _id)\"), [{\n        '_id': 3,\n        'aaa': 'ddd',\n        'false': 2\n      }, {\n        '_id': 4\n      }]\n    )\n    self.assertEqual(execute(\"reverse([1,2,3,4]+[2,4])\"), [4, 2, 4, 3, 2, 1])\n    self.assertEqual(execute(\"reverse(sort($.._id))\"), [4, 3, 2, 1])\n    self.assertEqual(execute(\"len([1,2,3,4]+[2,4])\"), 6)\n    self.assertEqual(execute(\"unique([1,1,3,3])\"), [1, 3])\n    # edge cases\n    self.assertEqual(execute(\"len(True)\"), True)\n    self.assertEqual(execute(\"len('aaa')\"), 3)\n\n  def test_builtin_time(self):\n    import datetime\n    self.assertIsInstance(execute(\"now()\"), datetime.datetime)\n    self.assertIsInstance(execute(\"date()\"), datetime.date)\n    self.assertIsInstance(execute(\"date(now())\"), datetime.date)\n    self.assertIsInstance(execute(\"date([2001,12,30])\"), datetime.date)\n    self.assertIsInstance(execute(\"time()\"), datetime.time)\n    self.assertIsInstance(execute(\"time(now())\"), datetime.time)\n    self.assertIsInstance(execute(\"time([12,23])\"), datetime.time)\n    self.assertIsInstance(execute(\"time([12,23,21,777777])\"), datetime.time)\n    self.assertIsInstance(execute(\"dateTime(now())\"), datetime.datetime)\n    self.assertIsInstance(\n      execute(\"dateTime([2001,12,30,12,23])\"), datetime.datetime\n    )\n    self.assertIsInstance(\n      execute(\"dateTime([2001,12,30,12,23,21,777777])\"), datetime.datetime\n    )\n    self.assertIsInstance(execute('dateTime(\"1980-05-11 04:22:33\", \"%Y-%m-%d %H:%M:%S\")'), datetime.datetime)\n    self.assertEqual(str(execute('dateTime(\"1980-05-11 04:22:33\", \"%Y-%m-%d %H:%M:%S\")')), \"1980-05-11 04:22:33\")\n\n    self.assertEqual(\n      execute(\"toMillis(dateTime([2001,12,30,12,23,21,777777]))\"),\n      1009715001777\n    )\n    self.assertIsInstance(\n      execute(\"dateTime(date(),time())\"), datetime.datetime\n    )\n    self.assertIsInstance(\n      execute(\"dateTime(date(),[12,23])\"), datetime.datetime\n    )\n    self.assertIsInstance(\n      execute(\"dateTime(date(),[12,23,21,777777])\"), datetime.datetime\n    )\n    self.assertIsInstance(\n      execute(\"dateTime([2001,12,30],time())\"), datetime.datetime\n    )\n    self.assertEqual(\n      execute(\"array(time([12,30])-time([8,00]))\"), [4, 30, 0, 0]\n    )\n    self.assertEqual(\n      execute(\"array(time([12,12,12,12])-time([8,8,8,8]))\"), [4, 4, 4, 4]\n    )\n    self.assertEqual(\n      execute(\"array(time([12,12,12,12])-time([1,2,3,4]))\"), [11, 10, 9, 8]\n    )\n    self.assertEqual(\n      execute(\"array(time([12,00])-time([1,10]))\"), [10, 50, 0, 0]\n    )\n    self.assertEqual(\n      execute(\"array(time([1,00])-time([1,10]))\"), [23, 50, 0, 0]\n    )\n    self.assertEqual(\n      execute(\"array(time([0,00])-time([0,0,0,1]))\"), [23, 59, 59, 999999]\n    )\n    self.assertEqual(\n      execute(\"array(time([0,0])+time([1,1,1,1]))\"), [1, 1, 1, 1]\n    )\n    self.assertEqual(\n      execute(\"array(time([0,0])+time([1,2,3,4]))\"), [1, 2, 3, 4]\n    )\n    self.assertEqual(\n      execute(\"array(time([23,59,59,999999])+time([0,0,0,1]))\"), [0, 0, 0, 0]\n    )\n    # age tests\n    self.assertEqual(execute(\"age(now())\"), [0, \"seconds\"])\n    self.assertEqual(\n      execute(\"age(dateTime([2000,1,1,1,1]),dateTime([2001,1,1,1,1]))\"),\n      [1, \"year\"]\n    )\n    self.assertEqual(\n      execute(\"age(dateTime([2000,1,1,1,1]),dateTime([2000,2,1,1,1]))\"),\n      [1, \"month\"]\n    )\n    self.assertEqual(\n      execute(\"age(dateTime([2000,1,1,1,1]),dateTime([2000,1,2,1,1]))\"),\n      [1, \"day\"]\n    )\n    self.assertEqual(\n      execute(\"age(dateTime([2000,1,1,1,1]),dateTime([2000,1,1,2,1]))\"),\n      [1, \"hour\"]\n    )\n    self.assertEqual(\n      execute(\"age(dateTime([2000,1,1,1,1]),dateTime([2000,1,1,1,2]))\"),\n      [1, \"minute\"]\n    )\n    self.assertEqual(\n      execute(\"age(dateTime([2000,1,1,1,1,1]),dateTime([2000,1,1,1,1,2]))\"),\n      [1, \"second\"]\n    )\n    self.assertEqual(\n      execute(\"\"\"array(time([0,0]) - time([0,0,0,999999]))\"\"\"),\n      [23, 59, 59, 1]\n    )\n    self.assertEqual(\n      execute(\"\"\"array(time([0,0]) + time([0,0,0,999999]))\"\"\"),\n      [0, 0, 0, 999999]\n    )\n\n  def test_localize(self):\n    pass\n    #these tests are passing on computers with timezone set to UTC - not the case of TravisCI\n    #test of non-DST time\n    #if sys.version < \"3\":\n    #self.assertEqual(execute(\"array(localize(dateTime([2000,1,1,10,10,1,0]),'Europe/Warsaw'))\"), [2000,1,1,11,10,1,0])\n    #test of DST time\n    #self.assertEqual(execute(\"array(localize(dateTime([2000,7,1,10,10,1,0]),'Europe/Warsaw'))\"), [2000,7,1,12,10,1,0])\n\n  def test_builtin_type(self):\n    self.assertEqual(execute(\"type([1,2,3,4]+[2,4])\"), \"array\")\n    self.assertEqual(execute(\"type({})\"), \"object\")\n    self.assertEqual(execute(\"type('')\"), \"str\")\n\n  def test_selector_with_empty_result(self):\n    self.assertEqual(execute(\"$.missing is None\"), True)\n    self.assertEqual(execute(\"$.missing is not None\"), False)\n\n  def test_misc(self):\n    self.assertEqual(execute(2), 2)\n    self.assertEqual(execute('{\"@aaa\":1}.@aaa'), 1)\n    self.assertEqual(execute('$ss.a'), None)\n    self.assertEqual(execute(\"$..*[10]\"), None)\n    self.assertEqual(sorted(execute(\"keys({'a':1,'b':2})\")), ['a', 'b'])\n    self.assertRaises(ExecutionError, lambda: execute('keys([])'))\n    self.assertRaises(ProgrammingError, lambda: execute('blah([])'))\n\n  def test_optimizations(self):\n    self.assertEqual(execute(\"$.*[@]\"), execute(\"$.*\"))\n    self.assertIsInstance(execute_raw(\"$..*\"), generator)\n    self.assertIsInstance(execute_raw(\"$..* + $..*\"), chain)\n    self.assertIsInstance(execute_raw(\"$..* + 2\"), chain)\n    self.assertIsInstance(execute_raw(\"2 + $..*\"), chain)\n    self.assertEqual(execute(\"$.._id[0]\"), 1)\n    self.assertEqual(execute(\"sort($.._id + $.._id)[2]\"), 2)\n    self.assertIsInstance(execute(\"$.._id[2]\"), int)\n    self.assertEqual(\n      execute2(\"$.store.book.(price)[0].price\"),\n      execute2(\"$.store.book[0].price\")\n    )\n\nclass ObjectPath_Paths(unittest.TestCase):\n  def assertItemsEqual(self, a, b, m=None):\n    try:\n      return self.assertCountEqual(a, b, m)\n    except: pass\n    return unittest.TestCase.assertItemsEqual(self, a, b, m)\n\n  def test_simple_paths(self):\n    self.assertEqual(execute(\"$.*\"), object1)\n    self.assertEqual(execute(\"$.a.b.c\"), None)\n    self.assertEqual(execute(\"$.a.b.c[0]\"), None)\n    self.assertEqual(execute(\"$.__lang__\"), \"en\")\n    self.assertEqual(execute(\"$.test.o._id\"), 2)\n    self.assertEqual(execute(\"$.test.l._id\"), [3, 4])\n    self.assertEqual(execute(\"$.*[test].o._id\"), 2)\n    self.assertEqual(execute(\"$.*['test'].o._id\"), 2)\n    self.assertEqual(\n      execute('[1,\"aa\",{\"a\":2,\"c\":3},{\"c\":3},{\"a\":1,\"b\":2}].(a,b)'), [{\n        \"a\": 2\n      }, {\n        \"a\": 1,\n        \"b\": 2\n      }]\n    )\n    self.assertEqual(\n      execute2(\"$.store.book.(price,title)[0]\"), {\n        \"price\": 8.95,\n        \"title\": \"Sayings of the Century\"\n      }\n    )\n    self.assertEqual(len(execute2(\"$..*['Lord' in @.title]\")), 1)\n    self.assertEqual(\n      execute2(\"$..book.(price,title)\"), [{\n        'price': 8.95,\n        'title': 'Sayings of the Century'\n      }, {\n        'price': 12.99,\n        'title': 'Sword of Honour'\n      }, {\n        'price': 8.99,\n        'title': 'Moby Dick'\n      }, {\n        'price': 22.99,\n        'title': 'The Lord of the Rings'\n      }]\n    )\n    self.assertEqual(\n      execute2(\"sort($..(price,title),'price')\"),\n      [{\n        'price': 8.95,\n        'title': 'Sayings of the Century'\n      }, {\n        'price': 8.99,\n        'title': 'Moby Dick'\n      }, {\n        'price': 12.99,\n        'title': 'Sword of Honour'\n      }, {\n        'price': 19.95\n      }, {\n        'price': 22.99,\n        'title': 'The Lord of the Rings'\n      }]\n    )\n    self.assertIsInstance(execute(\"now().year\"), int)\n\n  def test_complex_paths(self):\n    self.assertEqual(sorted(execute(\"$.._id\")), [1, 2, 3, 4])\n    self.assertEqual(execute(\"$..l\"), object1[\"test\"][\"l\"])\n    self.assertEqual(execute(\"$..l.._id\"), [3, 4])\n    self.assertEqual(execute2(\"$.store.*\"), object2[\"store\"])\n    self.assertEqual(\n      execute2(\"$.store.book.author\"),\n      ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']\n    )\n    #print()\n    #print(execute2(\"$.store.book.(author,aaa)\"))\n    self.assertEqual(\n      execute2(\"$.store.book.(author,aaa)\"), [{\n        \"author\": \"Nigel Rees\"\n      }, {\n        \"author\": \"Evelyn Waugh\"\n      }, {\n        \"author\": \"Herman Melville\"\n      }, {\n        \"author\": \"J. R. R. Tolkien\"\n      }]\n    )\n    self.assertEqual(\n      execute2(\"$.store.book.(author,price)\"), [{\n        'price': 8.95,\n        'author': 'Nigel Rees'\n      }, {\n        'price': 12.99,\n        'author': 'Evelyn Waugh'\n      }, {\n        'price': 8.99,\n        'author': 'Herman Melville'\n      }, {\n        'price': 22.99,\n        'author': 'J. R. R. Tolkien'\n      }]\n    )\n    self.assertEqual(\n      execute2(\"$.store.book.*[author]\"),\n      ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']\n    )\n    self.assertEqual(\n      execute2(\"$.store.book.*['author']\"),\n      ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']\n    )\n    self.assertEqual(execute2(\"$.store.book\"), object2[\"store\"][\"book\"])\n    self.assertEqual(\n      list(execute2(\"$..author\")),\n      ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']\n    )\n\n  def test_selectors(self):\n    self.assertEqual(\n      execute2(\"$.store.book[@.id is not null]\"),\n      [{\n        'category': 'reference',\n        'price': 8.95,\n        'title': 'Sayings of the Century',\n        'id': 1,\n        'author': 'Nigel Rees'\n      }]\n    )\n    self.assertEqual(len(execute2(\"$.store.book[@.id is null]\")), 3)\n    self.assertEqual(len(execute(\"$..*[@._id>2]\")), 2)\n    self.assertEqual(execute(\"$..*[3 in @.l._id]\")[0], object1['test'])\n    self.assertEqual(execute2(\"$.store..*[4 in @.k._id]\")[0], object2['store'])\n    self.assertEqual(execute(\"$..*[@._id>1 and @._id<3][0]\"), {'_id': 2})\n    # very bad syntax!!!\n    self.assertEqual(\n      sorted(execute2(\"$.store.book[@.price]\")),\n      sorted([8.95, 12.99, 8.99, 22.99])\n    )\n    self.assertEqual(\n      execute3(\"$..*[@.x is 5.6 and @.y is 9.891].value\"), ['bar']\n    )\n\n  def test_object_list(self):\n    self.assertItemsEqual(execute3('values($.*).value'), ['foo', 'bar', 'foobar'])\n    self.assertItemsEqual(execute3('keys($.*)'), ['item_1', 'item_2', 'item_3'])\n    self.assertItemsEqual(\n      execute4('map(values, $..root..response).value'), [5, 4, 0]\n    )\n\n#testcase2=unittest.FunctionTestCase(test_efficiency(2))\ntestcase1 = unittest.TestLoader().loadTestsFromTestCase(ObjectPath)\ntestcase2 = unittest.TestLoader().loadTestsFromTestCase(ObjectPath_Paths)\n\nop_test = unittest.TestSuite([testcase1, testcase2])\n#utils_interpreter=unittest.TestSuite([testcase2])\n"
  },
  {
    "path": "tests/test_utils.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom objectpath.utils import *\nfrom objectpath.utils.json_ext import *\nimport sys, unittest, os\n\nsys.setrecursionlimit(20000)\n\nclass Utils_test(unittest.TestCase):\n  def test_Utils_JSON_compat(self):\n    self.assertEqual(loads(\"['ddd']\"), ['ddd'])\n    if sys.version_info.major < 3:\n      self.assertEqual(loads(\"[u'ddd']\"), ['ddd'])\n    self.assertRaises(Exception, lambda: loads(['ddd}']))\n    self.assertEqual(dumps(['ddd']), '[\"ddd\"]')\n    self.assertEqual(py2JSON(False), 'false')\n    self.assertEqual(py2JSON(None), 'null')\n    self.assertEqual(py2JSON((2, 3, 4)), [2, 3, 4])\n    if sys.version_info.major < 3:\n      self.assertEqual(py2JSON(unicode('')), '')\n    self.assertEqual(py2JSON(2), 2)\n    self.assertEqual(\n      printJSON([1, 2, 3, 4, 5, 6]),\n      \"[\\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]\"\n    )\n    self.assertEqual(\n      printJSON([{}, 1]), '[\\n  {},\\n  \\x1b[36m\\x1b[1m1\\x1b[0m\\x1b[0m\\n]'\n    )\n    self.assertEqual(\n      printJSON({\n        \"aaa\": 1\n      }),\n      '{\\x1b[33m\\x1b[1m\"aaa\"\\x1b[0m\\x1b[0m: \\x1b[36m\\x1b[1m1\\x1b[0m\\x1b[0m}'\n    )\n    self.assertEqual(\n      printJSON({\n        \"a\": [1, 2, 3]\n      }),\n      '{\\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]}'\n    )\n    self.assertEqual(\n      printJSON([[1], {\n        \"aa\": 2\n      }]),\n      '[\\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]'\n    )\n    self.assertEqual(\n      printJSON({\n        \"aaa\": {\n          \"bbb\": {\n            \"ccc\": {\n              \"ddd\": [1, 2, 3, 4, 5]\n            }\n          }\n        }\n      }),\n      '{\\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]}}}}'\n    )\n    if str(sys.version_info.major) + str(sys.version_info.minor) < '33':\n      self.assertEqual(\n        printJSON({\n          \"aaa\": {\n            \"bbb\": {\n              \"ccc\": {\n                \"ddd\": {\n                  \"eee\": [1, 2, 3, 4, 5],\n                  \"ddd\": {}\n                }\n              }\n            }\n          }\n        }),\n        '{\\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}}}}}'\n      )\n\ntestcase1 = unittest.TestLoader().loadTestsFromTestCase(Utils_test)\n\nutils_test = unittest.TestSuite([testcase1])\n"
  }
]