diff --git a/LICENSE b/LICENSE index 896fe276618ec00739abe3ee570fd069f267f451..589b3f2e856196abb86a6642856688b7c349f03c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,13 @@ -The MIT License (MIT) +Copyright [2017] [David M. Brown] -Copyright (c) 2015 David Michael Brown +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 3b24f867bb30688bf94e848dc0436cda72d644bc..cd603cdb00c8b697d916c22d40458c0ffd279a4e 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,38 @@ -# aiogremin is no longer maintained. Please use Goblin: https://github.com/ZEROFAIL/goblin +# [aiogremlin 3.2.4](https://pypi.python.org/pypi/aiogremlin/3.2.4) +[Official Documentation](http://aiogremlin.readthedocs.org/en/latest/) -# [aiogremlin 0.1.3](https://pypi.python.org/pypi/aiogremlin/0.0.11) +`aiogremlin` is a port of the official `Gremlin-Python` designed for integration with +event loop based asynchronous Python networking libraries, including `asyncio`, +`aiohttp`, `tornado`, and `curio`. It uses the `async/await` syntax introduced +in PEP 492, and is therefore Python 3.5+ only. -## [Official Documentation](http://aiogremlin.readthedocs.org/en/latest/) +`aiogremlin` tries to follow `Gremlin-Python` as closely as possible both in terms +of API and implementation. It is regularly rebased against the official Apache Git +repository, and will be released according to the TinkerPop release schedule. -`aiogremlin` is a **Python 3** driver for the the [Tinkerpop 3 Gremlin Server](http://tinkerpop.incubator.apache.org/docs/3.0.0.M9-incubating/#gremlin-server). This module is built on [Asyncio](https://docs.python.org/3/library/asyncio.html) and [aiohttp](http://aiohttp.readthedocs.org/en/v0.15.3/index.html) `aiogremlin` is currently in **alpha** mode, but all major functionality has test coverage. - - -## Getting started - -Since Python 3.4 is not the default version on many systems, it's nice to create a virtualenv that uses Python 3.4 by default. Then use pip to install `aiogremlin`. Using virtualenvwrapper on Ubuntu 14.04: - -```bash -$ mkvirtualenv -p /usr/bin/python3.4 aiogremlin -$ pip install aiogremlin -``` - -Fire up the Gremlin Server: - -```bash -$ ./bin/gremlin-server.sh -``` - -The `GremlinClient` communicates asynchronously with the Gremlin Server using websockets. The majority of `GremlinClient` methods are an `asyncio.coroutine`, so you will also need to use `asyncio`: - -```python ->>> import asyncio ->>> from aiogremlin import GremlinClient -``` - -The Gremlin Server responds with messages in chunks, `GremlinClient.submit` submits a script to the server, and returns a `GremlinResponse` object. This object provides the methods: `get` and the property `stream`. `get` collects all of the response messages and returns them as a Python list. `stream` returns an object of the type `GremlinResponseStream` that implements a method `read`. This allows you to read the response without loading all of the messages into memory. - -Note that the GremlinClient constructor and the create_client function take [keyword only arguments](https://www.python.org/dev/peps/pep-3102/) only! +Note that this *NOT* an official Apache project component, it is a +*THIRD PARTY PACKAGE!* +## Getting Started ```python ->>> loop = asyncio.get_event_loop() ->>> gc = GremlinClient(url='ws://localhost:8182/', loop=loop) # Default url +import asyncio +from aiogremlin import DriverRemoteConnection, Graph -# Use get. ->>> @asyncio.coroutine -... def get(gc): -... resp = yield from gc.submit("x + x", bindings={"x": 4}) -... result = yield from resp.get() -... return result ->>> result = loop.run_until_complete(get(gc)) ->>> result -[Message(status_code=200, data=[8], message={}, metadata='')] +loop = asyncio.get_event_loop() ->>> resp = result[0] ->>> resp.status_code -200 ->>> resp # Named tuple. -Message(status_code=200, data=[8], message={}, metadata='') +async def go(loop): + remote_connection = await DriverRemoteConnection.open( + 'ws://localhost:8182/gremlin', 'g') + g = Graph().traversal().withRemote(remote_connection) + vertices = await g.V().toList() + return vertices -# Use stream. ->>> @asyncio.coroutine -... def stream(gc): -... resp = yield from gc.submit("x + x", bindings={"x": 1}) -... while True: -... result = yield from resp.stream.read() -... if result is None: -... break -... print(result) ->>> loop.run_until_complete(stream(gc)) -Message(status_code=200, data=[2], message={}, metadata='') ->>> loop.run_until_complete(gc.close()) # Explicitly close client!!! ->>> loop.close() -``` - -For convenience, `aiogremlin` also provides a method `execute`, which is equivalent to calling `yield from submit()` and then `yield from get()` in the same coroutine. - -```python ->>> loop = asyncio.get_event_loop() ->>> gc = GremlinClient(loop=loop) ->>> execute = gc.execute("x + x", bindings={"x": 4}) ->>> result = loop.run_until_complete(execute) ->>> result -[Message(status_code=200, data=[8], message={}, metadata='')] ->>> loop.run_until_complete(gc.close()) # Explicitly close client!!! ->>> loop.close() +vertices = loop.run_until_complete(go(loop)) +print(vertices) +# [v[1], v[2], v[3], v[4], v[5], v[6]] ``` diff --git a/README.txt b/README.txt index 1dd4b7c303908d36550cdc7e365d6851c26b53ac..4f9bc024541295da0db2ecaaa2933c6577530b66 100644 --- a/README.txt +++ b/README.txt @@ -1,8 +1,6 @@ -========================================================= -aiogremlin - Async Python 3 driver for TP3 Gremlin Server -========================================================= - -**alpha** +===================================================== +aiogremlin - Async Python 3.5+ port of Gremlin-Python +===================================================== `Official Documentation`_ diff --git a/aiogremlin/__init__.py b/aiogremlin/__init__.py index 48332a24203b242857bc873f758473eb601176ce..1c6df920bfa85a5a202192a931d73b8ddc7d2988 100644 --- a/aiogremlin/__init__.py +++ b/aiogremlin/__init__.py @@ -1,9 +1,5 @@ from aiogremlin.driver.cluster import Cluster from aiogremlin.remote.driver_remote_connection import DriverRemoteConnection -from aiogremlin.gremlin_python import statics -from aiogremlin.gremlin_python.process import strategies -from aiogremlin.gremlin_python.process.graph_traversal import __ -from aiogremlin.gremlin_python.process.traversal import Binding from aiogremlin.gremlin_python.structure.graph import Graph __version__ = "3.2.4" diff --git a/aiogremlin/driver/client.py b/aiogremlin/driver/client.py index 6bd597fe16108b4bac1bdbbda392b2b0716f6ead..2759d68649b0864dd729b1fbdd4006257bd7e792 100644 --- a/aiogremlin/driver/client.py +++ b/aiogremlin/driver/client.py @@ -14,33 +14,29 @@ class Client: :param aiogremlin.cluster.Cluster cluster: Cluster used by client :param asyncio.BaseEventLoop loop: + :param dict aliases: Optional mapping for aliases. Default is `None` """ - def __init__(self, cluster, loop, *, aliases=None, processor=None, - op=None): + def __init__(self, cluster, loop, *, aliases=None): self._cluster = cluster self._loop = loop if aliases is None: aliases = {} self._aliases = aliases - if processor is None: - processor = '' - self._processor = processor - if op is None: - op = 'eval' - self._op = op @property def aliases(self): + """Read-only property""" return self._aliases @property def message_serializer(self): + """Read-only property""" return self.cluster.config['message_serializer'] @property def cluster(self): """ - Readonly property. + Read-only property. :returns: The instance of :py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` associated with @@ -60,6 +56,11 @@ class Client: """ **coroutine** Submit a script and bindings to the Gremlin Server. + :param message: Can be an instance of + `Message<aiogremlin.gremlin_python.request.RequestMessage>` or + `Bytecode<aiogremlin.gremlin_python.process.traversal.Bytecode>` + or a `str` representing a raw Gremlin script + :param dict bindings: Optional bindings used with raw Grelmin :returns: :py:class:`ResultSet<aiogremlin.driver.resultset.ResultSet>` object """ diff --git a/aiogremlin/driver/cluster.py b/aiogremlin/driver/cluster.py index abaa8af7c6d639ca257feecdac29a02c430f357d..6cc403fb81d0243c1da49a0a6362c4dd125e0b50 100644 --- a/aiogremlin/driver/cluster.py +++ b/aiogremlin/driver/cluster.py @@ -35,6 +35,8 @@ class Cluster: level interface used by the :py:mod:`aiogremlin` module. :param asyncio.BaseEventLoop loop: + :param dict aliases: Optional mapping for aliases. Default is `None` + :param config: Optional cluster configuration passed as kwargs or `dict` """ DEFAULT_CONFIG = { @@ -73,8 +75,10 @@ class Cluster: specified in configuration. :param asyncio.BaseEventLoop loop: + :param dict aliases: Optional mapping for aliases. Default is `None` :param str configfile: Optional configuration file in .json or .yml format + :param config: Optional cluster configuration passed as kwargs or `dict` """ cluster = cls(loop, aliases=aliases, **config) if configfile: @@ -84,12 +88,13 @@ class Cluster: @property def hosts(self): + """Read-only property""" return self._hosts @property def config(self): """ - Readonly property. + Read-only property. :returns: `dict` containing the cluster configuration """ @@ -100,7 +105,7 @@ class Cluster: **coroutine** Get connection from next available host in a round robin fashion. - :returns: :py:class:`Connection<aiogremlin.connection.Connection>` + :returns: :py:class:`Connection<aiogremlin.driver.connection.Connection>` """ if not self._hosts: await self.establish_hosts() @@ -136,6 +141,11 @@ class Cluster: raise exception.ConfigurationError('Unknown config file format') def config_from_yaml(self, filename): + """ + Load configuration from from YAML file. + + :param str filename: Path to the configuration file. + """ with open(filename, 'r') as f: config = yaml.load(f) config = self._process_config_imports(config) @@ -162,6 +172,11 @@ class Cluster: return config def config_from_module(self, module): + """ + Load configuration from Python module. + + :param str filename: Path to the configuration file. + """ if isinstance(module, str): module = importlib.import_module(module) config = dict() @@ -175,7 +190,8 @@ class Cluster: """ **coroutine** Get a connected client. Main API method. - :returns: A connected instance of `Client<aiogremlin.client.Client>` + :returns: A connected instance of + `Client<aiogremlin.driver.client.Client>` """ aliases = aliases or self._aliases if not self._hosts: diff --git a/aiogremlin/driver/connection.py b/aiogremlin/driver/connection.py index ceab16ea6d54f24dab5fd1d8ec585b4bf2423a6c..4ca67c2c3b988217c46604c1d2187d2634bfa726 100644 --- a/aiogremlin/driver/connection.py +++ b/aiogremlin/driver/connection.py @@ -28,15 +28,16 @@ class Connection: :py:meth:`Connection.open<aiogremlin.connection.Connection.open>`. :param str url: url for host Gremlin Server - :param aiohttp.ClientWebSocketResponse ws: open websocket connection + :param aiogremlin.gremlin_python.driver.transport.AbstractBaseTransport transport: + Transport implementation + :param aiogremlin.gremlin_python.driver.protocol.AbstractBaseProtocol protocol: + Protocol implementation :param asyncio.BaseEventLoop loop: - :param aiohttp.ClientSession: Client session used to establish websocket - connections - :param float response_timeout: (optional) `None` by default :param str username: Username for database auth :param str password: Password for database auth :param int max_inflight: Maximum number of unprocessed requests at any one time on the connection + :param float response_timeout: (optional) `None` by default """ def __init__(self, url, transport, protocol, loop, username, password, max_inflight, response_timeout, message_serializer, provider): @@ -73,6 +74,9 @@ class Connection: :param str url: url for host Gremlin Server :param asyncio.BaseEventLoop loop: + :param aiogremlin.gremlin_python.driver.protocol.AbstractBaseProtocol protocol: + Protocol implementation + :param func transport_factory: Factory function for transports :param ssl.SSLContext ssl_context: :param str username: Username for database auth :param str password: Password for database auth @@ -80,6 +84,8 @@ class Connection: :param int max_inflight: Maximum number of unprocessed requests at any one time on the connection :param float response_timeout: (optional) `None` by default + :param message_serializer: Message serializer implementation + :param provider: Graph provider object implementation :returns: :py:class:`Connection<aiogremlin.connection.Connection>` """ @@ -100,7 +106,7 @@ class Connection: @property def closed(self): """ - Check if connection has been closed. + Read-only property. Check if connection has been closed. :returns: `bool` """ @@ -119,10 +125,7 @@ class Connection: """ Submit a script and bindings to the Gremlin Server - :param str processor: Gremlin Server processor argument - :param str op: Gremlin Server op argument - :param args: Keyword arguments for Gremlin Server. Depend on processor - and op. + :param `RequestMessage<aiogremlin.gremlin_python.driver.request.RequestMessage>` message: :returns: :py:class:`ResultSet<aiogremlin.driver.resultset.ResultSet>` object """ @@ -163,4 +166,4 @@ class Connection: async def __aexit__(self, exc_type, exc, tb): await self.close() - self._conn = None + self._transport = None diff --git a/aiogremlin/driver/protocol.py b/aiogremlin/driver/protocol.py index 03d189c127f9a8cc068cab98f98c9e0d61513e41..f82e28834e374c30232248af310e3a1f54cd895b 100644 --- a/aiogremlin/driver/protocol.py +++ b/aiogremlin/driver/protocol.py @@ -24,7 +24,7 @@ Message = collections.namedtuple( class GremlinServerWSProtocol(protocol.AbstractBaseProtocol): - + """Implemenation of the Gremlin Server Websocket protocol""" def __init__(self, message_serializer, username='', password=''): if isinstance(message_serializer, type): message_serializer = message_serializer() diff --git a/aiogremlin/driver/resultset.py b/aiogremlin/driver/resultset.py index a53765d28f3682e6e9b3d97bf84a5925357e28b5..e759bac9280c137795c544b6bd56f1c4450bd11e 100644 --- a/aiogremlin/driver/resultset.py +++ b/aiogremlin/driver/resultset.py @@ -12,6 +12,7 @@ def error_handler(fn): if msg.status_code not in [200, 206]: self.close() raise exception.GremlinServerError( + msg.status_code, "{0}: {1}".format(msg.status_code, msg.message)) msg = msg.data return msg @@ -38,7 +39,7 @@ class ResultSet: def queue_result(self, result): if result is None: - self.done.set() + self.close() self._response_queue.put_nowait(result) @property @@ -68,10 +69,8 @@ class ResultSet: return msg def close(self): - """Close response stream by setting done flag to true.""" self.done.set() self._loop = None - self._response_queue = None @error_handler async def one(self): @@ -79,7 +78,6 @@ class ResultSet: if not self._response_queue.empty(): msg = self._response_queue.get_nowait() elif self.done.is_set(): - self.close() msg = None else: try: diff --git a/aiogremlin/exception.py b/aiogremlin/exception.py index 51f0ea5967de894e2e8fd6e64c936d37cd6bf17b..13d3718590632d71cfe0db54802056feddf9ce9d 100644 --- a/aiogremlin/exception.py +++ b/aiogremlin/exception.py @@ -23,7 +23,11 @@ class ConfigurationError(Exception): class GremlinServerError(Exception): - pass + + def __init__(self, status_code, msg): + super().__init__(msg) + self.status_code = status_code + self.msg = msg class ResponseTimeoutError(Exception): diff --git a/aiogremlin/gremlin_python/__init__.py b/aiogremlin/gremlin_python/__init__.py index 7626550738f12ccd5e18b8c06a8d68aaab882066..41aee8bc82bf4bb98ed95c3c6982612273e92e68 100644 --- a/aiogremlin/gremlin_python/__init__.py +++ b/aiogremlin/gremlin_python/__init__.py @@ -16,5 +16,10 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ''' - +'''THIS FILE HAS BEEN MODIFIED BY DAVID M. BROWN TO SUPPORT PEP 492''' __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' + +from aiogremlin.gremlin_python.statics import * +from aiogremlin.gremlin_python.process.graph_traversal import __ +from aiogremlin.gremlin_python.process.strategies import * +from aiogremlin.gremlin_python.process.traversal import Binding diff --git a/aiogremlin/gremlin_python/process/traversal.py b/aiogremlin/gremlin_python/process/traversal.py index 6e3290fa68ebe5035de29144726e492c6bc1d2e9..b0e98f4e53f2be47db38642253d3807af136e5b9 100644 --- a/aiogremlin/gremlin_python/process/traversal.py +++ b/aiogremlin/gremlin_python/process/traversal.py @@ -75,19 +75,19 @@ class Traversal(object): self.last_traverser = None return temp async def next(self, amount=None): - if amount is None: - return await self.__anext__() - else: - count = 0 - tempList = [] - while count < amount: - count = count + 1 - try: - temp = await self.__anext__() - except StopAsyncIteration: - return tempList - tempList.append(temp) - return tempList + if not amount: + try: + return await self.__anext__() + except StopAsyncIteration: + return + results = [] + for i in range(amount): + try: + result = await self.__anext__() + except StopAsyncIteration: + return results + results.append(result) + return results Barrier = Enum('Barrier', 'normSack') diff --git a/aiogremlin/remote/driver_remote_connection.py b/aiogremlin/remote/driver_remote_connection.py index 60531b4bbf42d38dfc4d8138e59bfbea4ac6b41e..4532da2b6386c44aa4600e9035fbe9e514ca3ca8 100644 --- a/aiogremlin/remote/driver_remote_connection.py +++ b/aiogremlin/remote/driver_remote_connection.py @@ -11,6 +11,15 @@ __author__ = 'David M. Brown (davebshow@gmail.com)' class DriverRemoteConnection: + """ + Remote connection to a Gremlin Server. Do not instantiate directly, + instead use :py:meth:`DriverRemoteConnection.open` or + :py:meth:`DriverRemoteConnection.using` + + :param aiogremlin.driver.client.Client client: + :param asyncio.BaseEventLoop loop: + :param aiogremlin.driver.cluster.Cluster cluster: + """ def __init__(self, client, loop, *, cluster=None): self._client = client @@ -26,15 +35,32 @@ class DriverRemoteConnection: return self._cluster.config @classmethod - async def using(cls, cluster, aliases=None, *, loop=None): + async def using(cls, cluster, aliases=None): + """ + Create a :py:class:`DriverRemoteConnection` using a specific + :py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` + + :param aiogremlin.driver.cluster.Cluster cluster: + :param dict aliases: Optional mapping for aliases. Default is `None`. + Also accepts `str` argument which will be assigned to `g` + """ client = await cluster.connect(aliases=aliases) - if not loop: - loop = asyncio.get_event_loop() + loop = cluster._loop return cls(client, loop) @classmethod async def open(cls, url=None, aliases=None, loop=None, *, graphson_reader=None, graphson_writer=None, **config): + """ + :param str url: Optional url for host Gremlin Server + + :param dict aliases: Optional mapping for aliases. Default is `None`. + Also accepts `str` argument which will be assigned to `g` + :param asyncio.BaseEventLoop loop: + :param graphson_reader: Custom graphson_reader + :param graphson_writer: Custom graphson_writer + :param config: Optional cluster configuration passed as kwargs or `dict` + """ if url: parsed_url = urlparse(url) config.update({ @@ -54,11 +80,24 @@ class DriverRemoteConnection: return cls(client, loop, cluster=cluster) async def close(self): + """ + Close underlying cluster if applicable. If created with + :py:meth:`DriverRemoteConnection.using`, cluster is NOT closed. + """ if self._cluster: await self._cluster.close() async def submit(self, bytecode): + """Submit bytecode to the Gremlin Server""" result_set = await self._client.submit(bytecode) side_effects = RemoteTraversalSideEffects(result_set.request_id, self._client) return RemoteTraversal(result_set, side_effects) + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + await self.close() + self._client = None + self._cluster = None diff --git a/aiogremlin/remote/driver_remote_side_effects.py b/aiogremlin/remote/driver_remote_side_effects.py index 9dc59c7c82e7603f11a15a748e90268ac833eb14..d888b47c4efaacad28f34d66900ea724b625a0ed 100644 --- a/aiogremlin/remote/driver_remote_side_effects.py +++ b/aiogremlin/remote/driver_remote_side_effects.py @@ -18,6 +18,7 @@ class RemoteTraversalSideEffects(traversal.TraversalSideEffects): return await self.get(key) async def keys(self): + """Get side effect keys associated with Traversal""" if not self._closed: message = request.RequestMessage( 'traversal', 'keys', @@ -29,6 +30,7 @@ class RemoteTraversalSideEffects(traversal.TraversalSideEffects): return self._keys async def get(self, key): + """Get side effects associated with a specific key""" if not self._side_effects.get(key): if not self._closed: results = await self._get(key) @@ -47,6 +49,7 @@ class RemoteTraversalSideEffects(traversal.TraversalSideEffects): return await self._aggregate_results(result_set) async def close(self): + """Release side effects""" if not self._closed: message = request.RequestMessage( 'traversal', 'close', diff --git a/changes.txt b/changes.txt deleted file mode 100644 index d7b4cbae6a3c25c0da7210e8d98c2f17fa3781ff..0000000000000000000000000000000000000000 --- a/changes.txt +++ /dev/null @@ -1,9 +0,0 @@ -0.0.1 - 4/2015: Birth! -0.0.2 - 5/1/2015: Added an init_pool method and a create_client constructor. -0.0.3 - 5/2/2015: Using ujson for serialization. -0.0.4 - 5/12/2015: Added support for sessions. -0.0.5 - 5/13/2015: Using EofStream terminator technique to prepare for 3.0.0.M9 -0.0.7 - 5/20/2015: Full integration with aiohttp. Added Session object for HTTP - connection pooling. Added a context manager. -0.0.8 - 5/20/2015: Fixed bug in client constructor logic. Tested different client - connection handling techniques. diff --git a/docs/aiogremlin.driver.aiohttp.rst b/docs/aiogremlin.driver.aiohttp.rst new file mode 100644 index 0000000000000000000000000000000000000000..99302423e404ec1dc68dd9705265ce085eae1750 --- /dev/null +++ b/docs/aiogremlin.driver.aiohttp.rst @@ -0,0 +1,13 @@ +aiogremlin.driver.aiohttp package +================================= + +Submodules +---------- + +aiogremlin.driver.aiohttp.transport module +------------------------------------------ + +.. automodule:: aiogremlin.driver.aiohttp.transport + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.driver.rst b/docs/aiogremlin.driver.rst new file mode 100644 index 0000000000000000000000000000000000000000..cba702eb424315532952a9a7a74a0a4a641e9159 --- /dev/null +++ b/docs/aiogremlin.driver.rst @@ -0,0 +1,76 @@ +aiogremlin.driver package +========================= + +Subpackages +----------- + +.. toctree:: + + aiogremlin.driver.aiohttp + +Submodules +---------- + +aiogremlin.driver.client module +------------------------------- + +.. automodule:: aiogremlin.driver.client + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.cluster module +-------------------------------- + +.. automodule:: aiogremlin.driver.cluster + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.connection module +----------------------------------- + +.. automodule:: aiogremlin.driver.connection + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.pool module +----------------------------- + +.. automodule:: aiogremlin.driver.pool + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.protocol module +--------------------------------- + +.. automodule:: aiogremlin.driver.protocol + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.provider module +--------------------------------- + +.. automodule:: aiogremlin.driver.provider + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.resultset module +---------------------------------- + +.. automodule:: aiogremlin.driver.resultset + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.driver.server module +------------------------------- + +.. automodule:: aiogremlin.driver.server + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.gremlin_python.driver.rst b/docs/aiogremlin.gremlin_python.driver.rst new file mode 100644 index 0000000000000000000000000000000000000000..2eb5595197fe0cd4f613f7b6a1aad82e4376498c --- /dev/null +++ b/docs/aiogremlin.gremlin_python.driver.rst @@ -0,0 +1,45 @@ +aiogremlin.gremlin_python.driver package +======================================== + +Submodules +---------- + +aiogremlin.gremlin_python.driver.protocol module +------------------------------------------------ + +.. automodule:: aiogremlin.gremlin_python.driver.protocol + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.gremlin_python.driver.remote_connection module +--------------------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.driver.remote_connection + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.gremlin_python.driver.request module +----------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.driver.request + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.gremlin_python.driver.serializer module +-------------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.driver.serializer + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.gremlin_python.driver.transport module +------------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.driver.transport + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.gremlin_python.process.rst b/docs/aiogremlin.gremlin_python.process.rst new file mode 100644 index 0000000000000000000000000000000000000000..7cc2fdd25d9c33475cd8b58485c37ff50f69a5e9 --- /dev/null +++ b/docs/aiogremlin.gremlin_python.process.rst @@ -0,0 +1,29 @@ +aiogremlin.gremlin_python.process package +========================================= + +Submodules +---------- + +aiogremlin.gremlin_python.process.graph_traversal module +-------------------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.process.graph_traversal + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.gremlin_python.process.strategies module +--------------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.process.strategies + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.gremlin_python.process.traversal module +-------------------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.process.traversal + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.gremlin_python.rst b/docs/aiogremlin.gremlin_python.rst new file mode 100644 index 0000000000000000000000000000000000000000..7921453e676f4b7728102ffa085eadf77cccef70 --- /dev/null +++ b/docs/aiogremlin.gremlin_python.rst @@ -0,0 +1,22 @@ +aiogremlin.gremlin_python package +================================= + +Subpackages +----------- + +.. toctree:: + + aiogremlin.gremlin_python.driver + aiogremlin.gremlin_python.process + aiogremlin.gremlin_python.structure + +Submodules +---------- + +aiogremlin.gremlin_python.statics module +---------------------------------------- + +.. automodule:: aiogremlin.gremlin_python.statics + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.gremlin_python.structure.io.rst b/docs/aiogremlin.gremlin_python.structure.io.rst new file mode 100644 index 0000000000000000000000000000000000000000..547f898395aca2155da2361056a8ffb74333c9da --- /dev/null +++ b/docs/aiogremlin.gremlin_python.structure.io.rst @@ -0,0 +1,13 @@ +aiogremlin.gremlin_python.structure.io package +============================================== + +Submodules +---------- + +aiogremlin.gremlin_python.structure.io.graphson module +------------------------------------------------------ + +.. automodule:: aiogremlin.gremlin_python.structure.io.graphson + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.gremlin_python.structure.rst b/docs/aiogremlin.gremlin_python.structure.rst new file mode 100644 index 0000000000000000000000000000000000000000..3c12f02a101076205bc235bea5c1599981525dc0 --- /dev/null +++ b/docs/aiogremlin.gremlin_python.structure.rst @@ -0,0 +1,20 @@ +aiogremlin.gremlin_python.structure package +=========================================== + +Subpackages +----------- + +.. toctree:: + + aiogremlin.gremlin_python.structure.io + +Submodules +---------- + +aiogremlin.gremlin_python.structure.graph module +------------------------------------------------ + +.. automodule:: aiogremlin.gremlin_python.structure.graph + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.remote.rst b/docs/aiogremlin.remote.rst new file mode 100644 index 0000000000000000000000000000000000000000..45cd42f3f51f1f3114f4d4d59ab3130684fdf0ee --- /dev/null +++ b/docs/aiogremlin.remote.rst @@ -0,0 +1,21 @@ +aiogremlin.remote package +========================= + +Submodules +---------- + +aiogremlin.remote.driver_remote_connection module +------------------------------------------------- + +.. automodule:: aiogremlin.remote.driver_remote_connection + :members: + :undoc-members: + :show-inheritance: + +aiogremlin.remote.driver_remote_side_effects module +--------------------------------------------------- + +.. automodule:: aiogremlin.remote.driver_remote_side_effects + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/aiogremlin.rst b/docs/aiogremlin.rst index 193331c0067869925f82f4042a456745c72f50b4..e61427b2092f8d4438592a975f96c5a49df4f62c 100644 --- a/docs/aiogremlin.rst +++ b/docs/aiogremlin.rst @@ -1,56 +1,22 @@ -.. _aiogremlin-client-reference: - -Client Reference -================ - aiogremlin package -------------------------- +================== -.. automodule:: aiogremlin - :members: - :undoc-members: - :show-inheritance: +Subpackages +----------- -aiogremlin.client module ------------------------- +.. toctree:: -.. automodule:: aiogremlin.client - :members: - :undoc-members: - :show-inheritance: - -aiogremlin.connector module ---------------------------- + aiogremlin.driver + aiogremlin.gremlin_python + aiogremlin.remote -This module is based on :py:class:`aiohttp.client.ClientSession` and -:py:class:`aiohttp.connector.BaseConnector`. +Submodules +---------- -.. automodule:: aiogremlin.connector - :members: - :undoc-members: - :show-inheritance: - :inherited-members: - -aiogremlin.exceptions module ----------------------------- - -.. automodule:: aiogremlin.exceptions - :members: - :undoc-members: - :show-inheritance: - -aiogremlin.response module --------------------------- - -.. automodule:: aiogremlin.response - :members: - :undoc-members: - :show-inheritance: - -aiogremlin.subprotocol module ------------------------------ +aiogremlin.exception module +--------------------------- -.. automodule:: aiogremlin.subprotocol +.. automodule:: aiogremlin.exception :members: :undoc-members: :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst index 7de713419e272bca278bf8c826367bf9ae07b5fe..a4f20be14fd0930310f91bdad8ccfb7a82e72b37 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,32 +7,35 @@ aiogremlin ========== -aiogremlin is no longer maintained!!! Please use Goblin: http://goblin.readthedocs.io/en/latest/index.html -:py:mod:`aiogremlin` is an asynchronous client for the `TinkerPop 3 Gremlin Server`_ -based on the `asyncio`_ and `aiohttp`_ libraries. +`aiogremlin` is a port of the official `Gremlin-Python` designed for integration with +event loop based asynchronous Python networking libraries, including `asyncio`_, +`aiohttp`_, and `tornado`_. It uses the `async/await` syntax introduced +in `PEP 492`_, and is therefore Python 3.5+ only. + +`aiogremlin` tries to follow `Gremlin-Python` as closely as possible both in terms +of API and implementation. It is regularly rebased against the official Apache Git +repository, and will be released according to the `TinkerPop`_ release schedule. + +Note that this *NOT* an official Apache project component, it is a +*THIRD PARTY PACKAGE!* Releases ======== -The latest release of :py:mod:`aiogremlin` is **0.1.3**. +The latest release of :py:mod:`aiogremlin` is **3.2.4**. Requirements ============ -- Python 3.4 -- TinkerPop 3 Gremlin Server 3.1.0 +- Python 3.5+ +- TinkerPop 3.2.4 -Using Python 2? Checkout `gremlinrestclient`_. Dependencies ============ -- aiohttp 0.18.4 -- aiowebsocketclient 0.0.4 +- aiohttp 1.3.3 +- PyYAML 3.12 -To speed up serialization, you can also install `ujson`_. If not available, -aiogremlin will use the Python standard library :any:`json<json>` module. - -- ujson 1.33 Installation @@ -46,35 +49,37 @@ Getting Started =============== :py:mod:`aiogremlin` has a simple API that is quite easy to use. However, as it relies -heavily on `asyncio`_ and `aiohttp`_, it is helpful to be familar with the -basics of these modules. If you're not, maybe check out the :py:mod:`asyncio` -documentation relating to the :ref:`event loop<asyncio-event-loop>` and the -concept of the :ref:`coroutine<coroutine>`. Also, I would recommend the -documentation relating to :py:mod:`aiohttp`'s -:ref:`websocket client<aiohttp-client-websockets>` and -:ref:`HTTP client<aiohttp-client-reference>` implementations. +heavily on `asyncio`_ and `aiohttp`_, it is helpful to be familiar with the +basics of these modules. + +:py:mod:`aiogremlin` is *very* similar to Gremlin-Python, except it is all async, all the time. Minimal Example --------------- Submit a script to the Gremlin Server:: >>> import asyncio - >>> import aiogremlin - >>> @asyncio.coroutine - ... def go(): - ... resp = yield from aiogremlin.submit("1 + 1") - ... return (yield from resp.get()) + >>> from aiogremlin import DriverRemoteConnection, Graph + >>> loop = asyncio.get_event_loop() - >>> results = loop.run_until_complete(go()) + + >>> async def go(loop): + ... remote_connection = await DriverRemoteConnection.open( + ... 'ws://localhost:8182/gremlin', 'g') + ... g = Graph().traversal().withRemote(remote_connection) + ... vertices = await g.V().toList() + ... return vertices + + >>> results = loop.run_until_complete(go(loop)) >>> results - [Message(status_code=200, data=[2], message={}, metadata='')] + # [v[1], v[2], v[3], v[4], v[5], v[6]] The above example demonstrates how :py:mod:`aiogremlin` uses the :ref:`event loop<asyncio-event-loop>` to drive communication with the Gremlin Server, but the **rest of examples are written as if they were run in a Python interpreter**. In reality, **this isn't possible**, so remember, code *must* -be wrapped in functions and run with the :ref:`event loop<asyncio-event-loop>`. +be wrapped in a coroutine and run with the :ref:`event loop<asyncio-event-loop>`. Contribute ---------- @@ -103,9 +108,9 @@ Indices and tables * :ref:`modindex` * :ref:`search` -.. _Tinkerpop 3 Gremlin Server: http://tinkerpop.incubator.apache.org/ +.. _TinkerPop: http://tinkerpop.apache.org/ .. _`asyncio`: https://docs.python.org/3/library/asyncio.html .. _`aiohttp`: http://aiohttp.readthedocs.org/en/latest/ -.. _`ujson`: https://pypi.python.org/pypi/ujson +.. _`Tornado`: http://www.tornadoweb.org/en/stable/ +.. _`PEP 492`: https://www.python.org/dev/peps/pep-0492/ .. _Github: https://github.com/davebshow/aiogremlin/issues -.. _`gremlinrestclient`: http://gremlinrestclient.readthedocs.org/en/latest/ diff --git a/docs/modules.rst b/docs/modules.rst index ef0aa53dcfaa1d1db9bd7f1d0fb48b5f272ec968..eb1551773da58a1b8d912fda687f3448fdba18bf 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1,7 +1,7 @@ -API -=== +aiogremlin API +============== .. toctree:: - :maxdepth: 2 + :maxdepth: 4 aiogremlin diff --git a/docs/usage.rst b/docs/usage.rst index 28a3e65bf015ba364c54136aa202132e23da89a4..086b23231a90b14ed7903f2c11f8c0b3b4fcf12c 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -1,252 +1,200 @@ -Using aiogremlin -================ +Using :py:mod:`aiogremlin` +========================== -Before you get started, make sure you have the `Gremlin Server`_ up and running. -All of the following example assume a running Gremlin Server version 3.0.0 at -'ws://localhost:8182/'. +Before you get started, make sure you have the Gremlin Server up and running. +All of the following example assume a running Gremlin Server version 3.2.4 at +`ws://localhost:8182/gremlin` using the `conf/gremlin-server-modern-py.yaml` +configuration:: + $ ./bin/gremlin-server.sh conf/gremlin-server-modern-py.yaml -Submitting a script with :py:func:`submit` ------------------------------------------- +Using the Gremlin Language Variant +---------------------------------- -The simplest way to interact with the Gremlin Server is by using the -:py:func:`aiogremlin.client.submit` function:: +:py:mod:`aiogremlin` is used almost exactly like the official Gremlin-Python, +except that all operations are asynchronous. Thus when coding with :py:mod:`aiogremlin` +coroutines and the `async/await` syntax are used in combination with an `asyncio` compatible +event loop implementation (`tornado`, `ZeroMQ`, `uvloop`, etc.). - >>> resp = yield from aiogremlin.submit("x + x", bindings={"x": 2}) +The following examples assume that you are already familiar with `asyncio`, coroutines, +and the event loop. For readability, they strip away these details +to focus on the syntax used by :py:mod:`aiogremlin`. -This returns an instance of :py:class:`aiogremlin.client.GremlinResponse`. This -class provides the interface used to read the underlying response stream. The -easiest way to read the stream is using the -:py:meth:`aiogremlin.client.GremlinResponse.get`:: +To create a traversal source, simply use +:py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>` +combined with :py:class:`Graph<aiogremlin.gremlin_python.structure.graph.Graph>`:: - >>> results = yield from resp.get() + >>> remote_connection = await DriverRemoteConnection.open( + ... 'ws://localhost:8182/gremlin', 'g') + >>> g = Graph().traversal().withRemote(remote_connection) -However, if you are expecting a huge result set from the server, you may want to -read the chunked responses one at a time:: +In :py:mod:`aiogremlin`, a +:py:class:`Traversal<aiogremlin.gremlin_python.process.traversal.Traversal>` +implements the Asynchronous Iterator Protocol as defined +by PEP 492:: - >>> results = [] - >>> while True: - ... msg = yield from resp.stream.read(): - ... if msg is None: - ... break - ... results.append(msg) + >>> async for vertex in g.V(): + ... print(vertex) +Furthermore, it implements several convience methods - :py:meth:`toList`, +:py:meth:`toSet`, and :py:meth:`next`:: -.. function:: submit(gremlin, *, url='ws://localhost:8182/', bindings=None, - lang="gremlin-groovy", rebindings=None, op="eval", - processor="", timeout=None, session=None, loop=None): + >>> vertex_list = await g.V().toList() + >>> vertex_set = await g.V().toSet() + >>> next_vertex = await g.V().next() # returns next result from the stream - :ref:`coroutine<coroutine>` +:py:class:`Traversal<aiogremlin.gremlin_python.process.traversal.Traversal>` +also contains a reference to a +:py:class:`RemoteTraversalSideEffects<aiogremlin.remote.driver_remote_side_effects.RemoteTraversalSideEffects>` +object that can be used to fetch side effects cached by the server (when applicable):: - Submit a script to the Gremlin Server. + >>> t = g.V().aggregate('a') + >>> await t.iterate() # evaluate the traversal + >>> keys = await t.side_effects.keys() + >>> se = await t.side_effects.get('a') + >>> await t.side_effects.close() - :param str gremlin: Gremlin script to submit to server. +Don't forget to close the +:py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>` +when finished:: - :param str url: url for Gremlin Server (optional). 'ws://localhost:8182/' - by default + >>> await remote_connection.close() - :param dict bindings: A mapping of bindings for Gremlin script. - :param str lang: Language of scripts submitted to the server. - "gremlin-groovy" by default +Using :py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>` +----------------------------------------------------------------------------------------------------------- - :param dict rebindings: Rebind ``Graph`` and ``TraversalSource`` - objects to different variable names in the current request +The +:py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>` +object allows you to configure you database connection in one of two ways: - :param str op: Gremlin Server op argument. "eval" by default. +1. Passing configuration values as kwargs or a :py:class:`dict` to the classmethod +:py:meth:`open<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection.open>`:: - :param str processor: Gremlin Server processor argument. "" by default. - - :param float timeout: timeout for websocket read (seconds)(optional). - Values ``0`` or ``None`` mean no timeout - - :param str session: Session id (optional). Typically a uuid - - :param loop: :ref:`event loop<asyncio-event-loop>` If param is ``None``, - `asyncio.get_event_loop` is used for getting default event loop - (optional) - - :param float conn_timeout: timeout for establishing connection (seconds) - (optional). Values ``0`` or ``None`` mean no timeout - - :param username: Username for SASL auth - - :param password: Password for SASL auth - - :returns: :py:class:`aiogremlin.client.GremlinResponse` object - - -Reusing sockets with :py:class:`GremlinClient` ----------------------------------------------- - -To avoid the overhead of repeatedly establishing websocket connections, -``aiogremlin`` provides the class :py:class:`aiogremlin.client.GremlinClient`. -This class uses pooling to reuse websocket connections, and facilitates -concurrent message passing by yielding new websocket connections as needed:: - - >>> client = aiogremlin.GremlinClient() - >>> resp = client.submit("x + x", bindings={"x": 2}) - -For convenience, :py:class:`GremlinClient` provides the method -:py:meth:`aiogremlin.client.GremlinClient.execute`. This is equivalent of calling, -:py:meth:`GremlinClient.submit` and then :py:meth:`GremlinResponse.get`. -Therefore:: - - >>> results = client.execute("x + x", bindings={"x": 2}) - -Is equivalent to:: - - >>> resp = yield from aiogremlin.submit("x + x", bindings={"x": 2}) - >>> results = yield from resp.get() - -:py:class:`GremlinClient` encapsulates :py:class:`aiogremlin.connector.GremlinConnector`. -This class produces the websocket connections used by the client, and handles all -of the connection pooling. It can also handle pools for multiple servers. To do -so, you can share a :py:class:`GremlinConnector` amongst various client that -point to different endpoints:: - - >>> connector = aiogremlin.GremlinConnector() - >>> client1 = aiogremlin.GremlinClient(url=url='ws://localhost:8182/' - ... ws_connector=connector) - >>> client2 = aiogremlin.GremlinClient(url=url='ws://localhost:8080/' - ... ws_connector=connector) - - -Remember, when you are done you must explicitly close the :py:class:`GremlinClient` -using the coroutinemethod :py:meth:`close`:: - - >>> yield from client.close() - - -.. class:: GremlinClient(self, *, url='ws://localhost:8182/', loop=None, - lang="gremlin-groovy", op="eval", processor="", - timeout=None, ws_connector=None) - - Main interface for interacting with the Gremlin Server. - - :param str url: url for Gremlin Server (optional). 'ws://localhost:8182/' - by default - - :param loop: :ref:`event loop<asyncio-event-loop>` If param is ``None``, - `asyncio.get_event_loop` is used for getting default event loop - (optional) - - :param str lang: Language of scripts submitted to the server. - "gremlin-groovy" by default - - :param str op: Gremlin Server op argument. "eval" by default - - :param str processor: Gremlin Server processor argument. "" by default - - :param float timeout: timeout for establishing connection (optional). - Values ``0`` or ``None`` mean no timeout - - :param ws_connector: A class that implements the method :py:meth:`ws_connect`. - Usually an instance of :py:class:`aiogremlin.connector.GremlinConnector` - -.. method:: close() - - :ref:`coroutine<coroutine>` method - - Close client. If client has not been detached from underlying - ws_connector, this coroutinemethod closes the latter as well. - -.. method:: detach() - - Detach client from ws_connector. Client status is switched to closed. - -.. method:: submit(gremlin, *, bindings=None, lang=None, rebindings=None, - op=None, processor=None, binary=True, session=None, - timeout=None) - - :ref:`coroutine<coroutine>` method - - Submit a script to the Gremlin Server. - - :param str gremlin: Gremlin script to submit to server. - - :param str url: url for Gremlin Server (optional). 'ws://localhost:8182/' - by default - - :param dict bindings: A mapping of bindings for Gremlin script. - - :param str lang: Language of scripts submitted to the server. - "gremlin-groovy" by default - - :param dict rebindings: Rebind ``Graph`` and ``TraversalSource`` - objects to different variable names in the current request - - :param str op: Gremlin Server op argument. "eval" by default. - - :param str processor: Gremlin Server processor argument. "" by default. - - :param float timeout: timeout for establishing connection (optional). - Values ``0`` or ``None`` mean no timeout - - :param str session: Session id (optional). Typically a uuid - - :returns: :py:class:`aiogremlin.client.GremlinResponse` object - -.. method:: execute(gremlin, *, bindings=None, lang=None, rebindings=None, - op=None, processor=None, binary=True, session=None, - timeout=None) - - :ref:`coroutine<coroutine>` method - - Submit a script to the Gremlin Server and get a list of the responses. - - :param str gremlin: Gremlin script to submit to server. - - :param str url: url for Gremlin Server (optional). 'ws://localhost:8182/' - by default - - :param dict bindings: A mapping of bindings for Gremlin script. - - :param str lang: Language of scripts submitted to the server. - "gremlin-groovy" by default - - :param dict rebindings: Rebind ``Graph`` and ``TraversalSource`` - objects to different variable names in the current - request - - :param str op: Gremlin Server op argument. "eval" by default. - - :param str processor: Gremlin Server processor argument. "" by default. - - :param float timeout: timeout for establishing connection (optional). - Values ``0`` or ``None`` mean no timeout - - :param str session: Session id (optional). Typically a uuid - - :returns: :py:class:`list` of :py:class:`aiogremlin.subprotocol.Message` - - -Using Gremlin Server sessions with :py:class:`GremlinClientSession`. --------------------------------------------------------------------- - -The Gremlin Server supports sessions to maintain state across server -messages. Although this is not the preffered method, it is quite useful in -certain situations. For convenience, :py:mod:`aiogremlin` provides the class -:py:class:`aiogremlin.client.GremlinClientSession`. It is basically the -same as the :py:class:`GremlinClient`, but it uses sessions by default:: - - >>> client = aiogremlin.GremlinClientSession() - >>> client.session - '533f15fb-dc2e-4768-86c5-5b136b380b65' - >>> client.reset_session() - 'd7bdb0da-d4ec-4609-8ac0-df9713803d43' - -That's basically it! For more info, see the -:ref:`Client Reference Guide<aiogremlin-client-reference>` - - - - - - - - - - -.. _Gremlin Server: http://tinkerpop.incubator.apache.org/ + >>> remote_connection = await DriverRemoteConnection.open( + ... 'ws://localhost:8182/gremlin', 'g', port=9430) + +2. Passing a :py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` object to the +classmethod +:py:meth:`using<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection.using>`:: + + >>> import asyncio + >>> from aiogremlin import Cluster + >>> loop = asyncio.get_event_loop() + >>> cluster = await Cluster.open(loop, port=9430, aliases={'g': 'g'}) + >>> remote_connection = await DriverRemoteConnection.using(cluster) + +In the case that the +:py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>` +is created with +:py:meth:`using<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection.using>`, +it is not necessary to close the +:py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>`, +but the underlying :py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` must be closed:: + + >>> await cluster.close() + +Configuration options are specified in the final section of this document. + +:py:class:`DriverRemoteConnection<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection>` +is also an asynchronous context manager. It can be used as follows:: + + >>> async with remote_connection: + ... g = Graph().traversal().withRemote(remote_connection) + ... # traverse + # remote_connection is closed upon exit + +Taking this one step further, the +:py:meth:`open<aiogremlin.remote.driver_remote_connection.DriverRemoteConnection.open>` +can be awaited in the async context manager statement:: + + >>> async with await DriverRemoteConnection.open() as remote_connection: + ... g = Graph().traversal().withRemote(remote_connection) + ... # traverse + # remote connection is closed upon exit + +Using the :py:mod:`driver<aiogremlin.driver>` Module +---------------------------------------------------- + +:py:mod:`aiogremlin` also includes an asynchronous driver modeled after the +official Gremlin-Python driver implementation. However, instead of using +threads for asynchronous I/O, it uses an :py:mod:`asyncio` based implemenation. + +To submit a raw Gremlin script to the server, use the +:py:class:`Client<aiogremlin.driver.client.Client>`. This class should not +be instantiated directly, instead use a +:py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` object:: + + >>> cluster = await Cluster.open(loop) + >>> client = await cluster.connect() + >>> result_set = await client.submit('g.V().hasLabel(x)', {'x': 'person'}) + +The :py:class:`ResultSet<aiogremlin.driver.resultset.ResultSet>` returned by +:py:meth:`Client<aiogremlin.driver.client.Client.submit>` implements the +async interator protocol:: + + >>> async for v in result_set: + ... print(v) + +It also provides a convenience method +:py:meth:`all<aiogremlin.driver.client.Client.all>` +that aggregates and returns the result of the script in a :py:class:`list`:: + + >>> results = await result_set.all() + +Closing the client will close the underlying cluster:: + + >>> await client.close() + +Configuring the :py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` object +----------------------------------------------------------------------------- + +Configuration options can be set on +:py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` in one of two ways, either +passed as keyword arguments to +:py:meth:`Cluster<aiogremlin.driver.cluster.Cluster.open>`, or stored in a configuration +file and passed to the :py:meth:`open<aiogremlin.driver.cluster.Cluster.open>` +using the kwarg `configfile`. Configuration files can be either YAML or JSON +format. Currently, :py:class:`Cluster<aiogremlin.driver.cluster.Cluster>` +uses the following configuration: + ++-------------------+----------------------------------------------+-------------+ +|Key |Description |Default | ++===================+==============================================+=============+ +|scheme |URI scheme, typically 'ws' or 'wss' for secure|'ws' | +| |websockets | | ++-------------------+----------------------------------------------+-------------+ +|hosts |A list of hosts the cluster will connect to |['localhost']| ++-------------------+----------------------------------------------+-------------+ +|port |The port of the Gremlin Server to connect to, |8182 | +| |same for all hosts | | ++-------------------+----------------------------------------------+-------------+ +|ssl_certfile |File containing ssl certificate |'' | ++-------------------+----------------------------------------------+-------------+ +|ssl_keyfile |File containing ssl key |'' | ++-------------------+----------------------------------------------+-------------+ +|ssl_password |File containing password for ssl keyfile |'' | ++-------------------+----------------------------------------------+-------------+ +|username |Username for Gremlin Server authentication |'' | ++-------------------+----------------------------------------------+-------------+ +|password |Password for Gremlin Server authentication |'' | ++-------------------+----------------------------------------------+-------------+ +|response_timeout |Timeout for reading responses from the stream |`None` | ++-------------------+----------------------------------------------+-------------+ +|max_conns |The maximum number of connections open at any |4 | +| |time to this host | | ++-------------------+----------------------------------------------+-------------+ +|min_conns |The minimum number of connection open at any |1 | +| |time to this host | | ++-------------------+----------------------------------------------+-------------+ +|max_times_acquired |The maximum number of times a single pool |16 | +| |connection can be acquired and shared | | ++-------------------+----------------------------------------------+-------------+ +|max_inflight |The maximum number of unresolved messages |64 | +| |that may be pending on any one connection | | ++-------------------+----------------------------------------------+-------------+ +|message_serializer |String denoting the class used for message |'classpath' | +| |serialization, currently only supports | | +| |basic GraphSONMessageSerializer | | ++-------------------+----------------------------------------------+-------------+ diff --git a/news.txt b/news.txt new file mode 100644 index 0000000000000000000000000000000000000000..61a279c5081f466b5b8455bd8531960fdcde4dd7 --- /dev/null +++ b/news.txt @@ -0,0 +1 @@ +3.2.4 - Async GLV and driver released!!! diff --git a/profiler.py b/profiler.py deleted file mode 100644 index 2559772e05975d4c7974409ced3cbfbac007f786..0000000000000000000000000000000000000000 --- a/profiler.py +++ /dev/null @@ -1,9 +0,0 @@ -import cProfile -import asyncio -import aiogremlin - -loop = asyncio.get_event_loop() -gc = aiogremlin.GremlinClient(loop=loop) - -execute = gc.execute("x + x", bindings={"x": 4}) -cProfile.run('loop.run_until_complete(execute)') diff --git a/requirements.txt b/requirements.txt index 1bf0c3bdecc884909c96899dd474813efb74c957..e746163784527d036e7efb1856d11dab693ea47a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -aiohttp==0.18.4 -aiowebsocketclient==0.0.4 +aiohttp==1.3.3 +PyYAML==3.12 diff --git a/setup.py b/setup.py index a041ad2d86b1fd0f5548815b51140f29d2b2f198..30c4b42e3617594316c5f79b93b5f3d74bf5fc39 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,9 @@ from setuptools import setup setup( name='aiogremlin', - version='3.2.4', + version='3.2.4-rc.1', url='', - license='MIT', + license='Apache', author='davebshow', author_email='davebshow@gmail.com', description='Async Gremlin-Python', @@ -24,13 +24,13 @@ setup( setup_requires=['pytest-runner'], tests_require=['pytest-asyncio', 'pytest', 'mock'], classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', + 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3 :: Only' ] )