Commit 76b471e8 authored by davebshow's avatar davebshow
Browse files

refactored tests, working on API, started real docs

parent e584e3ec
"""Client for the Tinkerpop 3 Gremlin Server."""
import asyncio
import uuid
import aiohttp
......@@ -10,49 +11,84 @@ from aiogremlin.log import logger, INFO
from aiogremlin.connector import GremlinConnector
from aiogremlin.subprotocol import gremlin_response_parser, GremlinWriter
__all__ = ("GremlinClient", "GremlinClientSession")
__all__ = ("submit", "SimpleGremlinClient", "GremlinClient",
"GremlinClientSession")
@asycnio.coroutine
def submit(gremlin, *,
url='ws://localhost:8182/',
bindings=None,
lang="gremlin-groovy",
op="eval",
processor="",
connector=None):
class BaseGremlinClient:
connector = aiohttp.TCPConnector(force_close=True)
def __init__(self, *, lang="gremlin-groovy", op="eval", processor="",
loop=None, verbose=False):
self._lang = lang
self._op = op
self._processor = processor
self._loop = loop or asyncio.get_event_loop()
self._closed = False
if verbose:
logger.setLevel(INFO)
client_session = aiohttp.ClientSession(
connector=connector, ws_response_class=GremlinClientWebSocketResponse)
@property
def loop(self):
return self._loop
gremlin_client = GremlinClient(url=url, connector=client_session)
@property
def op(self):
return self._op
try:
resp = yield from gremlin_client.submit(
gremlin, bindings=bindings, lang=lang, op=op, processor=processor)
@property
def processor(self):
return self._processor
return resp
@property
def lang(self):
return self._lang
finally:
gremlin_client.detach()
client_session.detach()
def submit(self):
raise NotImplementedError
@asyncio.coroutine
def execute(self, gremlin, *, bindings=None, lang=None,
op=None, processor=None, binary=True):
"""
"""
lang = lang or self.lang
op = op or self.op
processor = processor or self.processor
resp = yield from self.submit(gremlin, bindings=bindings, lang=lang,
op=op, processor=processor,
binary=binary)
return (yield from resp.get())
class SimpleGremlinClient:
class SimpleGremlinClient(BaseGremlinClient):
def __init__(self, connection, *, loop=None, verbose=False):
def __init__(self, connection, *, lang="gremlin-groovy", op="eval",
processor="", loop=None, verbose=False):
"""This class is primarily designed to be used in the context
`manager"""
self._loop = loop or asyncio.get_event_loop()
super().__init__(lang=lang, op=op, processor=processor, loop=loop,
verbose=verbose)
self._connection = connection
if verbose:
logger.setLevel(INFO)
@asyncio.coroutine
def close(self):
if self._closed:
return
self._closed = True
try:
yield from self._connection.release()
finally:
self._connection = None
@property
def closed(self):
return (self._closed or self._connection.closed or
self._connection is None)
@asyncio.coroutine
def submit(self, gremlin, *, bindings=None, lang="gremlin-groovy",
op="eval", processor=""):
op="eval", processor="", session=None, binary=True):
"""
"""
writer = GremlinWriter(self._connection)
......@@ -62,36 +98,33 @@ class SimpleGremlinClient:
binary=binary)
return GremlinResponse(self._connection,
pool=self._pool,
session=session,
loop=self._loop)
class GremlinClient:
class GremlinClient(BaseGremlinClient):
def __init__(self, url='ws://localhost:8182/', loop=None,
def __init__(self, *, url='ws://localhost:8182/', loop=None,
protocols=None, lang="gremlin-groovy", op="eval",
processor="", timeout=None, verbose=False,
session=None, connector=None):
processor="", timeout=None, verbose=False, connector=None):
"""
"""
# Maybe getter setter for some of these: url, session, lang, op
self.url = url
self._loop = loop or asyncio.get_event_loop()
self.lang = lang or "gremlin-groovy"
self.op = op or "eval"
self.processor = processor or ""
super().__init__(lang=lang, op=op, processor=processor, loop=loop,
verbose=verbose)
self._url = url
self._timeout = timeout
self._session = session
if verbose:
logger.setLevel(INFO)
self._session = None
if connector is None:
connector = GremlinConnector()
connector = GremlinConnector(loop=self._loop)
self._connector = connector
@property
def loop(self):
return self._loop
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
@property
def closed(self):
......@@ -99,12 +132,9 @@ class GremlinClient:
@asyncio.coroutine
def close(self):
if self._closed:
return
self._closed = True
try:
yield from self._connector.close()
finally:
......@@ -133,51 +163,44 @@ class GremlinClient:
return GremlinResponse(ws, session=self._session, loop=self._loop)
@asyncio.coroutine
def execute(self, gremlin, *, bindings=None, lang=None,
op=None, processor=None, binary=True):
"""
"""
lang = lang or self.lang
op = op or self.op
processor = processor or self.processor
resp = yield from self.submit(gremlin, bindings=bindings, lang=lang,
op=op, processor=processor,
binary=binary)
return (yield from resp.get())
class GremlinClientSession(GremlinClient):
def __init__(self, url='ws://localhost:8182/', loop=None,
def __init__(self, *, url='ws://localhost:8182/', loop=None,
protocols=None, lang="gremlin-groovy", op="eval",
processor="", timeout=None, verbose=False,
session=None, connector=None):
super().__init__(url=url, loop=loop, protocols=protocols, lang=lang,
op=op, processor=processor, timeout=timeout,
processor="session", session=None, timeout=None,
verbose=False, connector=None):
"""
"""
super().__init__(url=url, protocols=protocols, lang=lang, op=op,
processor=processor, loop=loop, timeout=timeout,
verbose=verbose, connector=connector)
if session is None:
session = str(uuid4.uuid4())
session = str(uuid.uuid4())
self._session = session
def set_session(self):
pass
@property
def session(self):
return self._session
@session.setter
def session(self, value):
self._session = value
def change_session(self):
pass
def reset_session(self, session=None):
if session is None:
session = str(uuid.uuid4())
self._session = session
return self._session
class GremlinResponse:
def __init__(self, ws, *, session=None, loop=None):
# Add timeout for read
self._loop = loop or asyncio.get_event_loop()
self._session = session
self._stream = GremlinResponseStream(ws, oop=self._loop)
self._stream = GremlinResponseStream(ws, loop=self._loop)
@property
def stream(self):
......@@ -224,4 +247,38 @@ class GremlinResponseStream:
message = yield from self._stream.read()
except RequestError:
yield from self._ws.release()
raise
return message
@asyncio.coroutine
def submit(gremlin, *,
url='ws://localhost:8182/',
bindings=None,
lang="gremlin-groovy",
op="eval",
processor="",
connector=None,
loop=None):
if loop is None:
loop = asyncio.get_event_loop()
connector = aiohttp.TCPConnector(force_close=True, loop=loop)
client_session = aiohttp.ClientSession(
connector=connector, loop=loop,
ws_response_class=GremlinClientWebSocketResponse)
gremlin_client = GremlinClient(url=url, loop=loop,
connector=client_session)
try:
resp = yield from gremlin_client.submit(
gremlin, bindings=bindings, lang=lang, op=op, processor=processor)
return resp
finally:
gremlin_client.detach()
client_session.detach()
import asyncio
from contextlib import contextmanager
from aiowebsocketclient import WebSocketConnector
from aiogremlin.response import GremlinClientWebSocketResponse
......@@ -11,41 +13,41 @@ __all__ = ("GremlinConnector",)
class GremlinConnector(WebSocketConnector):
def __init__(self, *args, **kwargs):
kwargs["ws_response_class"] = GremlinClientWebSocketResponse
super().__init__(*args, **kwargs)
def create_client(self, *, url='ws://localhost:8182/', loop=None,
protocol=None, lang="gremlin-groovy", op="eval",
processor="", verbose=False):
return GremlinClient(url=url,
loop=loop,
protocol=protocol,
lang=lang,
op=op,
processor=processor,
connector=self
verbose=verbose)
def create_client_session(self, *, url='ws://localhost:8182/', loop=None,
protocol=None, lang="gremlin-groovy", op="eval",
processor="", connector=self, verbose=False):
return GremlinClientSession(url=url,
loop=loop,
protocol=protocol,
lang=lang,
op=op,
processor=processor,
connector=self
verbose=verbose)
# # Something like
# @contextmanager
# @asyncio.coroutine
# def connect(self, url, etc):
# pass
def __init__(self, *, conn_timeout=None, force_close=False, limit=1024,
client_session=None, loop=None):
"""
:param float conn_timeout: timeout for establishing connection
(optional). Values ``0`` or ``None``
mean no timeout
:param bool force_close: close underlying sockets after
releasing connection
:param int limit: limit for total open websocket connections
:param aiohttp.client.ClientSession client_session: Underlying HTTP
session used to
to establish
websocket
connections
:param loop: `event loop`
used for processing HTTP requests.
If param is ``None``, `asyncio.get_event_loop`
is used for getting default event loop.
(optional)
:param ws_response_class: WebSocketResponse class implementation.
``ClientWebSocketResponse`` by default
"""
super().__init__(conn_timeout=conn_timeout, force_close=force_close,
limit=limit, client_session=client_session, loop=loop,
ws_response_class=GremlinClientWebSocketResponse)
@contextmanager
@asyncio.coroutine
def connection(self, url, *,
protocols=(),
timeout=10.0,
autoclose=True,
autoping=True):
ws = yield from self.ws_connect(url='ws://localhost:8182/')
return ConnectionContextManager(ws)
# aioredis style
def __enter__(self):
......@@ -56,5 +58,5 @@ class GremlinConnector(WebSocketConnector):
pass
def __iter__(self):
conn = yield from self.ws_connect(url='ws://localhost:8182/')
return ConnectionContextManager(client)
ws = yield from self.ws_connect(url='ws://localhost:8182/')
return ConnectionContextManager(ws)
from aiogremlin.client import SimpleGremlinClient
class ConnectionContextManager:
__slots__ = ("_conn")
__slots__ = ("_ws")
def __init__(self, conn):
self._conn = conn
self._client = SimpleGremlinClient(conn)
def __init__(self, ws):
self._ws = ws
def __enter__(self):
if self._conn.closed:
if self._ws.closed:
raise RuntimeError("Connection closed unexpectedly.")
return self._client
return self._ws
def __exit__(self, exception_type, exception_value, traceback):
try:
self._conn._close_code = 1000
self._conn._closing = True
self._conn._close()
self._ws._close_code = 1000
self._ws._closing = True
self._ws._do_close()
finally:
self._conn = None
self._client = None
self._ws = None
......@@ -11,7 +11,7 @@ from aiowebsocketclient.connector import ClientWebSocketResponse
from aiogremlin.exceptions import SocketClientError
from aiogremlin.log import INFO, logger
__all__ = ('GremlinClientWebSocketResponse')
__all__ = ('GremlinClientWebSocketResponse',)
class GremlinClientWebSocketResponse(ClientWebSocketResponse):
......
"""Implements the Gremlin Server subprotocol."""
import asyncio
import collections
import uuid
......@@ -10,9 +9,8 @@ except ImportError:
import json
from aiogremlin.exceptions import RequestError, GremlinServerError
from aiogremlin.log import logger
__all__ = ("GremlinWriter",)
__all__ = ("GremlinWriter", "gremlin_response_parser")
Message = collections.namedtuple(
......@@ -86,10 +84,9 @@ class GremlinWriter:
"language": lang
}
}
if processor == "session":
# idk about this autogenerate here with new class
session = session or str(uuid.uuid4())
message["args"]["session"] = session
logger.info(
"Session ID: {}".format(message["args"]["session"]))
if session is None:
if processor == "session":
raise RuntimeError("session processor requires a session id")
else:
message["args"].update({"session": session})
return message
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aiogremlin.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aiogremlin.qhc"
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/aiogremlin"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aiogremlin"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo