From f80a5ab503fdde56f04e80ff03184f9970bc0c99 Mon Sep 17 00:00:00 2001 From: davebshow <davebshow@gmail.com> Date: Sun, 10 Jul 2016 21:02:12 -0400 Subject: [PATCH] simple async remote graph interface --- goblin/driver/connection.py | 4 ++ goblin/driver/graph.py | 45 +++++++++++++++++++ goblin/gremlin_python/__init__.py | 2 +- goblin/gremlin_python/driver/__init__.py | 4 +- .../driver/rest_remote_connection.py | 4 +- goblin/gremlin_python/process/__init__.py | 34 +++++++------- .../gremlin_python/process/graph_traversal.py | 24 +++++----- .../process/groovy_translator.py | 10 ++--- .../process/jython_translator.py | 18 ++++---- goblin/gremlin_python/process/traversal.py | 2 +- goblin/gremlin_python/structure/__init__.py | 4 +- goblin/gremlin_python/structure/graph.py | 4 +- .../gremlin_python/structure/remote_graph.py | 6 +-- goblin/query.py | 6 +-- setup.py | 3 +- tests/test_driver.py | 16 +++++++ 16 files changed, 126 insertions(+), 60 deletions(-) create mode 100644 goblin/driver/graph.py diff --git a/goblin/driver/connection.py b/goblin/driver/connection.py index 3fbb2ac..468dda6 100644 --- a/goblin/driver/connection.py +++ b/goblin/driver/connection.py @@ -77,6 +77,10 @@ class Connection(AbstractConnection): def force_close(self): return self._force_close + @property + def url(self): + return self._url + async def submit(self, gremlin, *, diff --git a/goblin/driver/graph.py b/goblin/driver/graph.py new file mode 100644 index 0000000..7d6c70e --- /dev/null +++ b/goblin/driver/graph.py @@ -0,0 +1,45 @@ +from goblin.gremlin_python.process.graph_traversal import GraphTraversalSource, GraphTraversal +from goblin.gremlin_python.process.traversal import TraversalStrategy, TraversalStrategies + + +class AsyncGraphTraversal(GraphTraversal): + def __init__(self, graph, traversal_strategies, bytecode): + GraphTraversal.__init__(self, graph, traversal_strategies, bytecode) + + def __repr__(self): + return self.graph.translator.translate(self.bytecode) + + def toList(self): + raise NotImplementedError + + def toSet(self): + raise NotImplementedError + + async def next(self): + resp = await self.traversal_strategies.apply(self) + return resp + + +class AsyncRemoteStrategy(TraversalStrategy): + async def apply(self, traversal): + result = await traversal.graph.remote_connection.submit( + traversal.graph.translator.translate(traversal.bytecode), + bindings=traversal.bindings, + lang=traversal.graph.translator.target_language) + return result + + +class AsyncGraph(object): + def traversal(self): + return GraphTraversalSource(self, self.traversal_strategy, + graph_traversal=AsyncGraphTraversal) + + +class AsyncRemoteGraph(AsyncGraph): + def __init__(self, translator, remote_connection): + self.traversal_strategy = AsyncRemoteStrategy() # A single traversal strategy + self.translator = translator + self.remote_connection = remote_connection + + def __repr__(self): + return "remotegraph[" + self.remote_connection.url + "]" diff --git a/goblin/gremlin_python/__init__.py b/goblin/gremlin_python/__init__.py index cd21e56..518d8e0 100644 --- a/goblin/gremlin_python/__init__.py +++ b/goblin/gremlin_python/__init__.py @@ -16,6 +16,6 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ''' -import statics +from . import statics __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' diff --git a/goblin/gremlin_python/driver/__init__.py b/goblin/gremlin_python/driver/__init__.py index bb4e612..7e1c0f1 100644 --- a/goblin/gremlin_python/driver/__init__.py +++ b/goblin/gremlin_python/driver/__init__.py @@ -16,7 +16,7 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ''' -from remote_connection import RemoteConnection -from rest_remote_connection import RESTRemoteConnection +from .remote_connection import RemoteConnection +from .rest_remote_connection import RESTRemoteConnection __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' diff --git a/goblin/gremlin_python/driver/rest_remote_connection.py b/goblin/gremlin_python/driver/rest_remote_connection.py index b827d0d..b2034fd 100644 --- a/goblin/gremlin_python/driver/rest_remote_connection.py +++ b/goblin/gremlin_python/driver/rest_remote_connection.py @@ -19,8 +19,8 @@ under the License. import json import requests -from gremlin_python.process.traversal import Traverser -from remote_connection import RemoteConnection +from goblin.gremlin_python.process.traversal import Traverser +from .remote_connection import RemoteConnection __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' diff --git a/goblin/gremlin_python/process/__init__.py b/goblin/gremlin_python/process/__init__.py index d84b097..e93356e 100644 --- a/goblin/gremlin_python/process/__init__.py +++ b/goblin/gremlin_python/process/__init__.py @@ -17,22 +17,22 @@ specific language governing permissions and limitations under the License. ''' -from graph_traversal import GraphTraversal -from graph_traversal import GraphTraversalSource -from graph_traversal import __ -from groovy_translator import GroovyTranslator -from jython_translator import JythonTranslator -from traversal import Barrier -from traversal import Bytecode -from traversal import Cardinality -from traversal import Column -from traversal import Direction -from traversal import Operator -from traversal import Order -from traversal import P -from traversal import Pop -from traversal import Scope -from traversal import T -from traversal import Traversal +from .graph_traversal import GraphTraversal +from .graph_traversal import GraphTraversalSource +from .graph_traversal import __ +from .groovy_translator import GroovyTranslator +from .jython_translator import JythonTranslator +from .traversal import Barrier +from .traversal import Bytecode +from .traversal import Cardinality +from .traversal import Column +from .traversal import Direction +from .traversal import Operator +from .traversal import Order +from .traversal import P +from .traversal import Pop +from .traversal import Scope +from .traversal import T +from .traversal import Traversal __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' diff --git a/goblin/gremlin_python/process/graph_traversal.py b/goblin/gremlin_python/process/graph_traversal.py index 2261e83..b5d6a17 100644 --- a/goblin/gremlin_python/process/graph_traversal.py +++ b/goblin/gremlin_python/process/graph_traversal.py @@ -16,20 +16,23 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ''' -from traversal import RawExpression -from traversal import Traversal -from traversal import Bytecode -from gremlin_python import statics +from .traversal import RawExpression +from .traversal import Traversal +from .traversal import Bytecode +from goblin.gremlin_python import statics class GraphTraversalSource(object): - def __init__(self, graph, traversal_strategies, bytecode=Bytecode()): + def __init__(self, graph, traversal_strategies, graph_traversal=None, bytecode=Bytecode()): self.graph = graph self.traversal_strategies = traversal_strategies + if graph_traversal is None: + graph_traversal = GraphTraversal + self.graph_traversal = graph_traversal self.bytecode = bytecode def __repr__(self): return "graphtraversalsource[" + str(self.graph) + "]" def E(self, *args): - traversal = GraphTraversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) + traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) traversal.bytecode.add_step("E", *args) for arg in args: if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str): @@ -38,7 +41,7 @@ class GraphTraversalSource(object): self.bindings.update(arg.bindings) return traversal def V(self, *args): - traversal = GraphTraversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) + traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) traversal.bytecode.add_step("V", *args) for arg in args: if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str): @@ -47,7 +50,7 @@ class GraphTraversalSource(object): self.bindings.update(arg.bindings) return traversal def addV(self, *args): - traversal = GraphTraversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) + traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) traversal.bytecode.add_step("addV", *args) for arg in args: if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str): @@ -56,7 +59,7 @@ class GraphTraversalSource(object): self.bindings.update(arg.bindings) return traversal def inject(self, *args): - traversal = GraphTraversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) + traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode)) traversal.bytecode.add_step("inject", *args) for arg in args: if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str): @@ -1684,6 +1687,3 @@ def where(*args): return __.where(*args) statics.add_static('where', where) - - - diff --git a/goblin/gremlin_python/process/groovy_translator.py b/goblin/gremlin_python/process/groovy_translator.py index a05885d..2c2d79a 100644 --- a/goblin/gremlin_python/process/groovy_translator.py +++ b/goblin/gremlin_python/process/groovy_translator.py @@ -20,11 +20,11 @@ under the License. import sys from aenum import Enum -from traversal import Bytecode -from traversal import P -from traversal import RawExpression -from traversal import SymbolHelper -from traversal import Translator +from .traversal import Bytecode +from .traversal import P +from .traversal import RawExpression +from .traversal import SymbolHelper +from .traversal import Translator if sys.version_info.major > 2: long = int diff --git a/goblin/gremlin_python/process/jython_translator.py b/goblin/gremlin_python/process/jython_translator.py index 0ffcaab..c3169f0 100644 --- a/goblin/gremlin_python/process/jython_translator.py +++ b/goblin/gremlin_python/process/jython_translator.py @@ -21,14 +21,14 @@ import inspect import sys from aenum import Enum -from traversal import Barrier -from traversal import Bytecode -from traversal import Cardinality -from traversal import Column -from traversal import P -from traversal import RawExpression -from traversal import SymbolHelper -from traversal import Translator +from .traversal import Barrier +from .traversal import Bytecode +from .traversal import Cardinality +from .traversal import Column +from .traversal import P +from .traversal import RawExpression +from .traversal import SymbolHelper +from .traversal import Translator if sys.version_info.major > 2: long = int @@ -57,7 +57,7 @@ class JythonTranslator(Translator): if isinstance(arg, str): return "\"" + arg + "\"" elif isinstance(arg, long): - return str(arg) + "L" if arg > 9223372036854775807L else "Long(" + str(arg) + ")" + return str(arg) elif isinstance(arg, Barrier): return "Barrier" + "." + SymbolHelper.toJava(str(arg.name)) elif isinstance(arg, Column): diff --git a/goblin/gremlin_python/process/traversal.py b/goblin/gremlin_python/process/traversal.py index 7558151..c37c440 100644 --- a/goblin/gremlin_python/process/traversal.py +++ b/goblin/gremlin_python/process/traversal.py @@ -18,7 +18,7 @@ under the License. ''' from abc import abstractmethod from aenum import Enum -from gremlin_python import statics +from goblin.gremlin_python import statics class Traversal(object): def __init__(self, graph, traversal_strategies, bytecode): diff --git a/goblin/gremlin_python/structure/__init__.py b/goblin/gremlin_python/structure/__init__.py index fe9ad17..1c69b5c 100644 --- a/goblin/gremlin_python/structure/__init__.py +++ b/goblin/gremlin_python/structure/__init__.py @@ -16,7 +16,7 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ''' -from graph import Graph -from remote_graph import RemoteGraph +from .graph import Graph +from .remote_graph import RemoteGraph __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' diff --git a/goblin/gremlin_python/structure/graph.py b/goblin/gremlin_python/structure/graph.py index 60b2211..82909f1 100644 --- a/goblin/gremlin_python/structure/graph.py +++ b/goblin/gremlin_python/structure/graph.py @@ -19,8 +19,8 @@ under the License. __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' -from gremlin_python.process.graph_traversal import GraphTraversalSource -from gremlin_python.process.traversal import TraversalStrategies +from goblin.gremlin_python.process.graph_traversal import GraphTraversalSource +from goblin.gremlin_python.process.traversal import TraversalStrategies class Graph(object): diff --git a/goblin/gremlin_python/structure/remote_graph.py b/goblin/gremlin_python/structure/remote_graph.py index bc2a11a..19125ea 100644 --- a/goblin/gremlin_python/structure/remote_graph.py +++ b/goblin/gremlin_python/structure/remote_graph.py @@ -19,9 +19,9 @@ under the License. __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' -from graph import Graph -from gremlin_python.process.traversal import TraversalStrategies -from gremlin_python.process.traversal import TraversalStrategy +from .graph import Graph +from goblin.gremlin_python.process.traversal import TraversalStrategies +from goblin.gremlin_python.process.traversal import TraversalStrategy class RemoteGraph(Graph): diff --git a/goblin/query.py b/goblin/query.py index 69b05f8..41ce449 100644 --- a/goblin/query.py +++ b/goblin/query.py @@ -2,7 +2,7 @@ import asyncio import logging -from goblin import gremlin_python +from goblin.gremlin_python import process from goblin import mapper @@ -35,7 +35,7 @@ class QueryResponse: raise StopAsyncIteration -class GoblinTraversal(gremlin_python.PythonGraphTraversal): +class GoblinTraversal(process.GraphTraversal): def __init__(self, translator, query, element_class): super().__init__(translator, remote_connection=None) @@ -57,7 +57,7 @@ class Query: def __init__(self, session, translator, loop): self._session = session self._translator = translator - self._traversal_source = gremlin_python.PythonGraphTraversalSource( + self._traversal_source = process.PythonGraphTraversalSource( self._translator) self._loop = loop self._binding = 0 diff --git a/setup.py b/setup.py index e5d6396..dc6daf4 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,8 @@ setup( author="davebshow", author_email="davebshow@gmail.com", description="Python driver for TP3 Gremlin Server", - packages=["goblin", "goblin.gremlin_python", + packages=["goblin", "goblin.gremlin_python", "goblin.gremlin_python.process", + "goblin.gremlin_python.driver", "goblin.gremlin_python.structure", "goblin.driver", "tests"], install_requires=[ "aenum==1.4.5", diff --git a/tests/test_driver.py b/tests/test_driver.py index 041733c..0b486a8 100644 --- a/tests/test_driver.py +++ b/tests/test_driver.py @@ -2,6 +2,8 @@ import asyncio import unittest from goblin import driver +from goblin.driver import graph +from goblin.gremlin_python import process class TestDriver(unittest.TestCase): @@ -42,3 +44,17 @@ class TestDriver(unittest.TestCase): await connection.close() self.loop.run_until_complete(go()) + + def test_async_graph(self): + + async def go(): + translator = process.GroovyTranslator('g') + connection = await driver.GremlinServer.open( + "http://localhost:8182/", self.loop) + g = graph.AsyncRemoteGraph(translator, connection) + traversal = g.traversal() + resp = await traversal.V().next() + async for msg in resp: + print(msg) + await connection.close() + self.loop.run_until_complete(go()) -- GitLab