Source code for odata_query.roundtrip

import sys
from typing import Type

if sys.version_info >= (3, 8):
    from typing import Protocol
else:
    from typing_extensions import Protocol

from odata_query import ast, visitor


class LiteralValNode(Protocol):
    """:meta private:"""

    val: str


PRECEDENCE = {
    ast.Attribute: 10,
    ast.Call: 10,
    ast.Not: 9,
    ast.USub: 9,
    ast.Mult: 8,
    ast.Div: 8,
    ast.Mod: 8,
    ast.Add: 7,
    ast.Sub: 7,
    ast.Gt: 6,
    ast.GtE: 6,
    ast.Lt: 6,
    ast.LtE: 6,
    ast.Eq: 5,
    ast.NotEq: 5,
    ast.And: 4,
    ast.Or: 3,
}


[docs]class AstToODataVisitor(visitor.NodeVisitor): """ :class:`NodeVisitor` that transforms an :term:`AST` back into an OData query string. """ def visit_Identifier(self, node: ast.Identifier) -> str: """:meta private:""" if node.namespace: prefix = ".".join(node.namespace) return prefix + "." + node.name return node.name def visit_Attribute(self, node: ast.Attribute) -> str: """:meta private:""" return self.visit(node.owner) + "/" + node.attr def visit_Null(self, node: ast.Null) -> str: """:meta private:""" return "null" def visit_String(self, node: ast.String) -> str: """:meta private:""" return "'" + node.val + "'" def visit_Duration(self, node: ast.Duration) -> str: """:meta private:""" return "duration'" + node.val + "'" def _visit_Literal(self, node: LiteralValNode) -> str: """:meta private:""" return node.val visit_Integer = _visit_Literal visit_Float = _visit_Literal visit_Boolean = _visit_Literal visit_Date = _visit_Literal visit_Time = _visit_Literal visit_DateTime = _visit_Literal visit_GUID = _visit_Literal def visit_List(self, node: ast.List) -> str: """:meta private:""" return "(" + ", ".join(self.visit(v) for v in node.val) + ")" def visit_Add(self, node: ast.Add) -> str: """:meta private:""" return "add" def visit_Sub(self, node: ast.Sub) -> str: """:meta private:""" return "sub" def visit_Mult(self, node: ast.Mult) -> str: """:meta private:""" return "mul" def visit_Div(self, node: ast.Div) -> str: """:meta private:""" return "div" def visit_Mod(self, node: ast.Mod) -> str: """:meta private:""" return "mod" def visit_BinOp(self, node: ast.BinOp) -> str: """:meta private:""" left = self._visit_and_paren_if_precedence_lower(node.left, type(node.op)) right = self._visit_and_paren_if_precedence_lower(node.right, type(node.op)) return left + " " + self.visit(node.op) + " " + right def visit_Eq(self, node: ast.Eq) -> str: """:meta private:""" return "eq" def visit_NotEq(self, node: ast.NotEq) -> str: """:meta private:""" return "ne" def visit_Lt(self, node: ast.Lt) -> str: """:meta private:""" return "lt" def visit_LtE(self, node: ast.LtE) -> str: """:meta private:""" return "le" def visit_Gt(self, node: ast.Gt) -> str: """:meta private:""" return "gt" def visit_GtE(self, node: ast.GtE) -> str: """:meta private:""" return "ge" def visit_In(self, node: ast.In) -> str: """:meta private:""" return "in" def visit_Compare(self, node: ast.Compare) -> str: """:meta private:""" left = self._visit_and_paren_if_precedence_lower( node.left, type(node.comparator) ) right = self._visit_and_paren_if_precedence_lower( node.right, type(node.comparator) ) return left + " " + self.visit(node.comparator) + " " + right def visit_And(self, node: ast.And) -> str: """:meta private:""" return "and" def visit_Or(self, node: ast.Or) -> str: """:meta private:""" return "or" def visit_BoolOp(self, node: ast.BoolOp) -> str: """:meta private:""" left = self._visit_and_paren_if_precedence_lower(node.left, type(node.op)) right = self._visit_and_paren_if_precedence_lower(node.right, type(node.op)) return left + " " + self.visit(node.op) + " " + right def visit_Not(self, node: ast.Not) -> str: """:meta private:""" return "not" def visit_USub(self, node: ast.USub) -> str: """:meta private:""" return "-" def visit_UnaryOp(self, node: ast.UnaryOp) -> str: """:meta private:""" operand = self._visit_and_paren_if_precedence_lower(node.operand, type(node.op)) return self.visit(node.op) + " " + operand def visit_Call(self, node: ast.Call) -> str: """:meta private:""" return ( self.visit(node.func) + "(" + ", ".join(self.visit(n) for n in node.args) + ")" ) def visit_Any(self, node: ast.Any) -> str: """:meta private:""" return "any" def visit_All(self, node: ast.All) -> str: """:meta private:""" return "all" def visit_Lambda(self, node: ast.Lambda) -> str: """:meta private:""" return self.visit(node.identifier) + ": " + self.visit(node.expression) def visit_CollectionLambda(self, node: ast.CollectionLambda) -> str: """:meta private:""" return ( self.visit(node.owner) + "/" + self.visit(node.operator) + "(" + (self.visit(node.lambda_) if node.lambda_ else "") + ")" ) def _visit_and_paren_if_precedence_lower( self, node: ast._Node, precedence: Type[ast._Node] ) -> str: """ Transform `node` by visiting it, then wrap the result in parentheses if the expressions precedence is lower than that of `precedence`. :meta private: """ res = self.visit(node) if hasattr(node, "op"): node_op = type(node.op) # type: ignore elif hasattr(node, "comparator"): node_op = type(node.comparator) # type: ignore else: node_op = type(node) node_prec = PRECEDENCE.get(node_op, 100) check_prec = PRECEDENCE.get(precedence, 100) if node_prec < check_prec: res = "(" + res + ")" return res