#!/usr/bin/env python
# -*- coding: utf-8 -*-
# "THE WISKEY-WARE LICENSE":
# <utn_kdd@googlegroups.com> wrote this file. As long as you retain this notice
# you can do whatever you want with this stuff. If we meet some day, and you
# think this stuff is worth it, you can buy us a WISKEY in return.
#===============================================================================
# DOCS
#===============================================================================
"""Main logic behind QBJ.
"""
#===============================================================================
# IMPORTS
#===============================================================================
import sys
import json
import traceback
try:
import cStringIO as StringIO
except ImportError:
import StringIO
from yatel import typeconv
from yatel.qbj import functions, schema
#===============================================================================
# CLASS QBJ RESOLVER
#===============================================================================
[docs]class QBJResolver(object):
"""Resolver of QBJ calls.
Parameters
----------
function : dict
Keys of ``function``:
- ``name`` function to be called.
- ``args`` positional arguments for function ``name``.
- ``kwargs`` named arguments for function ``name``.
For further detail on functions arguments see :py:mod:`yatel.qbj.functions`
context : :py:class:`yatel.db.YatelNetwork`
Network to execute functions on.
"""
def __init__ (self, function, context):
self.function = function
self.context = context
def _argument_resolver(self, arg):
atype = arg["type"]
value = None
if "function" in arg:
function = arg["function"]
resolver = QBJResolver(function, self.context)
value = resolver.resolve()
else:
value = arg["value"]
return typeconv.parse({"type": atype, "value": value})
[docs] def resolve(self):
"""Responsible for putting together the call to ``function`` with the
respective arguments, and return its result.
"""
name = self.function["name"]
args = []
for arg in self.function.get("args", ()):
result = self._argument_resolver(arg)
args.append(result)
kwargs = {}
for kw, arg in self.function.get("kwargs", {}).items():
result = self._argument_resolver(arg)
kwargs[kw] = result
if "nw" in kwargs:
return functions.execute(name, *args, **kwargs)
return functions.execute(name, self.context, *args, **kwargs)
#===============================================================================
# CLASS CORE
#===============================================================================
[docs]class QBJEngine(object):
"""Responsible of storing context for QBJ queries, and executes the
functions required on it.
Parameters
----------
nw : :py:class:`yatel.db.YatelNetwork`
Network to be used with the query.
"""
def __init__(self, nw):
self.context = nw
[docs] def execute(self, querydict, stacktrace=False):
"""Takes the query in ``querydict`` and executes it after validation of
it's structure.
Parameters
----------
querydict : dict
Dictionary with query in QBJ format.
stacktrace : bool or False
True if you want a stacktrace to be generated.
Returns
-------
dict
Result of the query.
"""
query_id = None
function = None
error = False
stack_trace = None
error_msg = ""
result = None
try:
schema.validate(querydict)
query_id = querydict["id"]
function = querydict["function"]
main_resolver = QBJResolver(function, self.context)
result = typeconv.simplifier(main_resolver.resolve())
except Exception as err:
if not query_id and isinstance(querydict, dict):
query_id = querydict.get("id")
error = True
error_msg = unicode(err)
if stacktrace:
stack_trace = u"\n".join(
traceback.format_exception(*sys.exc_info())
)
return {
"id": query_id, "error": error, "stack_trace": stack_trace,
"error_msg": error_msg, "result": result
}
#===============================================================================
# MAIN
#===============================================================================
if __name__ == "__main__":
print(__doc__)