diff --git a/docs/driver.rst b/docs/driver.rst
new file mode 100644
index 0000000000000000000000000000000000000000..81e68906f58f9734034f3972000a3c4d661a3615
--- /dev/null
+++ b/docs/driver.rst
@@ -0,0 +1,96 @@
+Using the Driver
+================
+
+At the its simplest, the driver provides the
+:py:meth:`open<goblin.driver.connection.Connection.open>` coroutine classmethod,
+which returns a :py:class:`Connection<goblin.driver.connection.Connection>` to the
+Gremlin Server::
+
+    >>> import asyncio
+    >>> from goblin import driver
+    >>> loop = asyncio.get_event_loop()
+    >>> conn = await driver.Connection.open('ws://localhost:8182/gremlin', loop)
+
+The :py:class:`Connection<goblin.driver.connection.Connection>` object can be
+used to :py:meth:`submit<goblin.driver.connection.Connection.submit>` messages
+to the Gremlin Server.
+:py:meth:`submit<goblin.driver.connection.Connection.submit>` returns a
+:py:class:`Response<goblin.driver.connection.Response>` object that implements
+the PEP 492 asynchronous iterator protocol::
+
+    >>> resp = await conn.submit(gremlin='1 + 1')
+    >>> async for msg in resp:
+    ...     print(msg)
+    >>> await conn.close()  # conn also implements async context manager interface
+
+Connecting to a :py:class:`Cluster<goblin.driver.cluster.Cluster>`
+------------------------------------------------------------------
+
+To take advantage of the higher level features of the
+:py:mod:`driver<goblin.driver>`, :py:mod:`Goblin` provides the
+:py:class:`Cluster<goblin.driver.cluster.Cluster>` object.
+:py:class:`Cluster<goblin.driver.cluster.Cluster>` is used to create multi-host
+clients that leverage connection pooling and sharing. Its interface is based
+on the TinkerPop Java driver::
+
+    >>> cluster = await driver.Cluster.open()  # opens a cluster with default config
+    >>> client = await cluster.connect()
+    >>> resp = await client.submit(gremlin='1 + 1')  # round robin requests to available hosts
+    >>> async for msg in resp:
+    ...     print(msg)
+    >>> await cluster.close()  # Close all connections to all hosts
+
+And that is it. While :py:class:`Cluster<goblin.driver.cluster.Cluster>`
+is simple to learn and use, it provides a wide variety of configuration options.
+
+Configuring :py:class:`Cluster<goblin.driver.cluster.Cluster>`
+--------------------------------------------------------------
+
+Configuration options can be set on
+:py:class:`Cluster<goblin.driver.cluster.Cluster>` in one of two ways, either
+passed as keyword arguments to
+:py:meth:`open<goblin.driver.cluster.Cluster.open>`, or stored in a configuration
+file and passed to the :py:meth:`open<goblin.driver.cluster.Cluster.open>`
+using the kwarg `configfile`. Configuration files can be either YAML or JSON
+format. Currently, :py:class:`Cluster<goblin.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 GraphSON2MessageSerializer              |             |
++-------------------+----------------------------------------------+-------------+
diff --git a/docs/glv.rst b/docs/glv.rst
new file mode 100644
index 0000000000000000000000000000000000000000..dd50e61aa7e31755f19a2daba444c1c4520b4dac
--- /dev/null
+++ b/docs/glv.rst
@@ -0,0 +1,95 @@
+Using AsyncGraph (GLV)
+======================
+
+:py:mod:`Goblin` provides an asynchronous version of the gremlin-python
+Gremlin Language Variant (GLV) that is bundled with Apache TinkerPop beginning
+with the 3.2.2 release. Traversal are generated using the class
+:py:class:`AsyncGraph<goblin.driver.graph.AsyncGraph>` combined with a remote
+connection class, either :py:class:`Connection<goblin.driver.connection.Connection>` or
+:py:class:`DriverRemoteConnection<goblin.driver.connection.DriverRemoteConnection>`::
+
+    >>> import asyncio
+    >>> from goblin import driver
+
+    >>> loop = asyncio.get_event_loop()
+    >>> remote_conn = loop.run_until_complete(
+    ...     driver.Connection.open(
+    ...         "http://localhost:8182/gremlin", loop))
+    >>> graph = driver.AsyncGraph()
+    >>> g = graph.traversal().withRemote(remote_conn)
+
+Once you have a traversal source, it's all Gremlin...::
+
+    >>> traversal = g.addV('query_language').property('name', 'gremlin')
+
+`traversal` is in an instance of
+:py:class:`AsyncGraphTraversal<goblin.driver.graph.AsyncGraphTraversal>`, which
+implements the Python 3.5 asynchronous iterator protocol::
+
+    >>> async def iterate_traversal(traversal):
+    >>>     async for msg in traversal:
+    >>>         print(msg)
+
+    >>> loop.run_until_complete(iterate_traversal(traversal))
+    # v[0]
+
+:py:class:`AsyncGraphTraversal<goblin.driver.graph.AsyncGraphTraversal>` also
+provides several convenience methods to help iterate over results:
+
+- :py:meth:`next<goblin.driver.graph.AsyncGraphTraversal.next>`
+- :py:meth:`toList<goblin.driver.graph.AsyncGraphTraversal.toList>`
+- :py:meth:`toSet<goblin.driver.graph.AsyncGraphTraversal.toSet>`
+- :py:meth:`oneOrNone<goblin.driver.graph.AsyncGraphTraversal.oneOrNone>`
+
+Notice the mixedCase? Not very pythonic? Well no, but it maintains continuity
+with the Gremlin query language, and that's what the GLV is all about...
+
+Note: Gremlin steps that are reserved words in Python, like `or`, `in`, use a
+a trailing underscore `or_` and `in_`.
+
+The Side Effect Interface
+-------------------------
+
+When using TinkerPop 3.2.2+ with the default
+:py:class:`GraphSON2MessageSerializer<goblin.driver.serializer.GraphSON2MessageSerializer>`,
+:py:mod:`Goblin` provides an asynchronous side effects interface using the
+:py:class:`AsyncRemoteTraversalSideEffects<goblin.driver.graph.AsyncRemoteTraversalSideEffects>`
+class. This allows side effects to be retrieved after executing the traversal::
+
+    >>> traversal = g.V().aggregate('a')
+    >>> results = loop.run_until_complete(traversal.toList())
+    >>> print(results)
+    # [v[0]]
+
+Calling
+:py:meth:`keys<goblin.driver.graph.AsyncRemoteTraversalSideEffects.keys>`
+will then return an asynchronous iterator containing all keys for cached
+side effects:
+
+    >>> async def get_side_effect_keys(traversal):
+    ...     resp = await traversal.side_effects.keys()
+    ...     async for key in resp:
+    ...         print(key)
+
+
+    >>> loop.run_until_complete(get_side_effect_keys(traversal))
+    # 'a'
+
+Then calling
+:py:meth:`get<goblin.driver.graph.AsyncRemoteTraversalSideEffects.get>`
+using a valid key will return the cached side effects::
+
+    >>> async def get_side_effects(traversal):
+    ...     resp = await traversal.side_effects.get('a')
+    ...     async for side_effect in resp:
+    ...         print(side_effect)
+
+
+    >>> loop.run_until_complete(get_side_effects(traversal))
+    # v[0]
+
+And that's it! For more information on Gremlin Language Variants, please
+visit the `Apache TinkerPop GLV Documentation`_.
+
+
+.. _Apache TinkerPop GLV Documentation: http://tinkerpop.apache.org/docs/3.2.2/tutorials/gremlin-language-variants/
diff --git a/docs/goblin.driver.rst b/docs/goblin.driver.rst
index 9f34e04d1e97bba5b794912841e825e629bc820d..776807e89d5fccf56106292e722c4293c8e23851 100644
--- a/docs/goblin.driver.rst
+++ b/docs/goblin.driver.rst
@@ -4,10 +4,18 @@ goblin.driver package
 Submodules
 ----------
 
-goblin.driver.api module
-------------------------
+goblin.driver.client module
+---------------------------
 
-.. automodule:: goblin.driver.api
+.. automodule:: goblin.driver.client
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+goblin.driver.cluster module
+----------------------------
+
+.. automodule:: goblin.driver.cluster
     :members:
     :undoc-members:
     :show-inheritance:
@@ -27,3 +35,36 @@ goblin.driver.graph module
     :members:
     :undoc-members:
     :show-inheritance:
+
+goblin.driver.pool module
+-------------------------
+
+.. automodule:: goblin.driver.pool
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+goblin.driver.serializer module
+-------------------------------
+
+.. automodule:: goblin.driver.serializer
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+goblin.driver.server module
+---------------------------
+
+.. automodule:: goblin.driver.server
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: goblin.driver
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/docs/goblin.rst b/docs/goblin.rst
index ce1353713835fe65807a24dc49d6b69396b48daa..da9f726d77954985d490dfa8e22eb5134fa7d9a9 100644
--- a/docs/goblin.rst
+++ b/docs/goblin.rst
@@ -27,6 +27,14 @@ goblin.app module
     :undoc-members:
     :show-inheritance:
 
+goblin.cardinality module
+-------------------------
+
+.. automodule:: goblin.cardinality
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
 goblin.element module
 ---------------------
 
@@ -43,7 +51,13 @@ goblin.exception module
     :undoc-members:
     :show-inheritance:
 
+goblin.manager module
+---------------------
 
+.. automodule:: goblin.manager
+    :members:
+    :undoc-members:
+    :show-inheritance:
 
 goblin.mapper module
 --------------------
@@ -69,10 +83,10 @@ goblin.session module
     :undoc-members:
     :show-inheritance:
 
-goblin.traversal module
------------------------
+Module contents
+---------------
 
-.. automodule:: goblin.traversal
+.. automodule:: goblin
     :members:
     :undoc-members:
     :show-inheritance:
diff --git a/docs/index.rst b/docs/index.rst
index c02ca08b41e4dd877a61c7be0560cd47d7664494..1704c1732e48d19d3f558566c03999a406e2e75f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -46,9 +46,10 @@ Submit scripts and bindings to the `Gremlin Server`_::
     >>> async def go(loop):
     ...     script = "g.addV('developer').property(k1, v1)"
     ...     bindings = {'k1': 'name', 'v1': 'Leif'}
-    ...     conn = await driver.GremlinServer.open('ws://localhost:8182/', loop)
+    ...     conn = await driver.Connection.open(
+    ...         'ws://localhost:8182/gremlin', loop)
     ...     async with conn:
-    ...         resp = await conn.submit(script, bindings=bindings)
+    ...         resp = await conn.submit(gremlin=script, bindings=bindings)
     ...         async for msg in resp:
     ...             print(msg)
 
@@ -56,29 +57,33 @@ Submit scripts and bindings to the `Gremlin Server`_::
     >>> loop.run_until_complete(go(loop))
     # {'type': 'vertex', 'id': 0, 'label': 'developer', 'properties': {'name': [{'id': 1, 'value': 'Leif'}]}}
 
+For more information on using the driver, see the :doc:`Driver docs</driver>`
 
-**AsyncRemoteGraph**
+**AsyncGraph**
 
 Generate and submit Gremlin traversals in native Python::
 
-    >>> from gremlin_python import process
 
+    >>> remote_conn = loop.run_until_complete(
+    ...     driver.Connection.open(
+    ...         "http://localhost:8182/gremlin", loop))
+    >>> graph = driver.AsyncGraph()
+    >>> g = graph.traversal().withRemote(remote_conn)
 
-    >>> connection = loop.run_until_complete(
-    ...     driver.GremlinServer.open("http://localhost:8182/", loop))
-    >>> translator = process.GroovyTranslator('g')
-    >>> graph = driver.AsyncRemoteGraph(translator, connection)
 
-    >>> async def go(graph):
-    ...     g = graph.traversal()
-    ...     resp = await g.addV('developer').property('name', 'Leif').next()
-    ...     async for msg in resp:
+    >>> async def go(g):
+    ...     traversal = g.addV('developer').property('name', 'Leif')
+    ...     async for msg in traversal:
     ...         print(msg)
-    ...     await graph.close()
+    ...     await remote_conn.close()
 
-    >>> loop.run_until_complete(go(graph))
+
+    >>> loop.run_until_complete(go(g))
     # {'properties': {'name': [{'value': 'Leif', 'id': 3}]}, 'label': 'developer', 'id': 2, 'type': 'vertex'}
 
+For more information on using the :py:class:`goblin.driver.graph.AsyncGraph<AsyncGraph>`,
+see the :doc:`GLV docs</glv>`
+
 
 **OGM**
 
@@ -100,11 +105,11 @@ Define custom vertex/edge classes using the provided base :py:mod:`classes<gobli
 
 Create a :py:class:`Goblin App<goblin.app.Goblin>` and register the element classes::
 
-    >>> from goblin import create_app
+    >>> from goblin import Goblin
 
 
     >>> app = loop.run_until_complete(
-    ...     create_app('ws://localhost:8182/', loop))
+    ...     Goblin.open(loop))
     >>> app.register(Person, Knows)
 
 
@@ -114,20 +119,19 @@ database::
 
     >>> async def go(app):
     ...     session = await app.session()
-    ...     async with session:
-    ...         leif = Person()
-    ...         leif.name = 'Leif'
-    ...         leif.age = 28
-    ...         jon = Person()
-    ...         jon.name = 'Jonathan'
-    ...         works_with = Knows(leif, jon)
-    ...         session.add(leif, jon, works_with)
-    ...         await session.flush()
-    ...         result = await session.g.E(works_with.id).one_or_none()
-    ...         assert result is works_with
-    ...         people = await session.traversal(Person).all()  # element class based traversal source
-    ...         async for person in people:
-    ...             print(person)
+    ...     leif = Person()
+    ...     leif.name = 'Leif'
+    ...     leif.age = 28
+    ...     jon = Person()
+    ...     jon.name = 'Jonathan'
+    ...     works_with = Knows(leif, jon)
+    ...     session.add(leif, jon, works_with)
+    ...     await session.flush()
+    ...     result = await session.g.E(works_with.id).oneOrNone()
+    ...     assert result is works_with
+    ...     people = session.traversal(Person)  # element class based traversal source
+    ...     async for person in people:
+    ...         print(person)
 
 
     >>> loop.run_until_complete(go(app))
@@ -142,12 +146,31 @@ for an element, that element will be updated to reflect these changes.
 For more information on using the OGM, see the :doc:`OGM docs</ogm>`
 
 
+A note about GraphSON message serialization
+-------------------------------------------
+
+The :py:mod:`goblin.driver` provides support for both GraphSON2 and GraphSON1
+out of the box. By default, it uses the
+:py:class:`GraphSON2MessageSerializer<goblin.driver.serializer.GraphSON2MessageSerializer>`.
+Since GraphSON2 was only recently included in the TinkerPop 3.2.2 release,
+:py:mod:`goblin.driver` also ships with
+:py:class:`GraphSONMessageSerializer<goblin.driver.serializer.GraphSONMessageSerializer>`.
+In the near future (when projects like Titan and DSE support the 3.2 Gremlin
+Server line), support for GraphsSON1 will be dropped.
+
+The :py:mod:`goblin<Goblin>` OGM still uses GraphSON1 by default and will do so
+until :py:mod:`goblin.driver` support is dropped. It will then be updated to
+use GraphSON2.
+
+
 Contents:
 
 .. toctree::
    :maxdepth: 4
 
    ogm
+   glv
+   driver
    modules
 
 
diff --git a/docs/modules.rst b/docs/modules.rst
index 7eee4a9125181e7a3461d44fd9750e2cea9fe686..8b7f906082ce89a2a791cbf0277f2ef8ecd04c83 100644
--- a/docs/modules.rst
+++ b/docs/modules.rst
@@ -1,5 +1,5 @@
-Goblin API
-==========
+goblin
+======
 
 .. toctree::
    :maxdepth: 4
diff --git a/docs/ogm.rst b/docs/ogm.rst
index 6729ba87feeab7ac6b72658b3b635d58cc320096..cf1b228eab111ae126ab57dc03fe52a1b220744a 100644
--- a/docs/ogm.rst
+++ b/docs/ogm.rst
@@ -240,10 +240,10 @@ must be wrapped in coroutines and ran using the :py:class:`asyncio.BaseEventLoop
 but, for convenience, they are shown as if they were run in a Python interpreter.
 To use a :py:class:`Session<goblin.session.Session>`, first create a
 :py:class:`Goblin App <goblin.app.Goblin>` using
-:py:func:`create_app<goblin.app.create_app>`, then register the defined element
+:py:meth:`Goblin.open<goblin.app.Goblin.open>`, then register the defined element
 classes::
 
-    >>> app = await goblin.create_app('ws://localhost:8182/', loop)
+    >>> app = await goblin.Goblin.open(loop)
     >>> app.register(Person, City, BornIn)
     >>> session = await app.session()
 
@@ -317,14 +317,21 @@ the value to be specified::
     >>> traversal = session.traversal(Person)
     >>> traversal.has(bindprop(Person, 'name', 'Leifur', binding='v1'))
 
-Finally, to submit a traversal, :py:mod:`Goblin` provides two methods:
-:py:meth:`all` and :py:meth:`one_or_none`. :py:meth:`all` returns all results
-produced by the traversal, while :py:meth:`one_or_none` returns either the last
-result, or in the case that the traversal did not return results, `None`. Remember
-to `await` the traversal when calling these methods::
+Finally, there are a variety of ways to to submit a traversal to the server.
+First of all, all traversals are themselve asynchronous iterators, and using
+them as such will cause a traversal to be sent on the wire:
+
+    >>> async for msg in session.g.V().hasLabel('person'):
+    ...     print(msg)
+
+Furthermore, :py:mod:`Goblin` provides several convenience methods that
+submit a traversal as well as process the results :py:meth:`toList`,
+:py:meth:`toSet` and :py:meth:`oneOrNone`. These methods both submit a script
+to the server and iterate over the results. Remember to `await` the traversal
+when calling these methods::
 
     >>> traversal = session.traversal(Person)
     >>> leif = await traversal.has(
-    ...     bindprop(Person, 'name', 'Leifur', binding='v1')).one_or_none()
+    ...     bindprop(Person, 'name', 'Leifur', binding='v1')).oneOrNone()
 
 And that is pretty much it. We hope you enjoy the :py:mod:`Goblin` OGM.
diff --git a/goblin/__init__.py b/goblin/__init__.py
index f77f95dbe45954f3faa95a77461e1f44efd0d1e8..2127055309e8a5f26dc2443b231e5dc794a1d0a4 100644
--- a/goblin/__init__.py
+++ b/goblin/__init__.py
@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
 
-from goblin.app import create_app, Goblin
+from goblin.app import Goblin
 from goblin.cardinality import Cardinality
 from goblin.element import Vertex, Edge, VertexProperty
 from goblin.properties import Property, String, Integer, Float, Boolean
diff --git a/goblin/app.py b/goblin/app.py
index 83cbf36e402f19ddfa8168ca63e38f937e942d66..c8c56a3453645db9b34c206b6bfa3849fe1fc507 100644
--- a/goblin/app.py
+++ b/goblin/app.py
@@ -27,46 +27,6 @@ from goblin import driver, element, session
 logger = logging.getLogger(__name__)
 
 
-async def create_app(url, loop, get_hashable_id=None, **config):
-    """
-    Constructor function for :py:class:`Goblin`. Connect to database and
-    build a dictionary of relevant vendor implmentation features.
-
-    :param str url: Database url
-    :param asyncio.BaseEventLoop loop: Event loop implementation
-    :param dict config: Config parameters for application
-
-    :returns: :py:class:`Goblin` object
-    """
-
-    features = {}
-    async with await driver.GremlinServer.open(url, loop) as conn:
-        # Propbably just use a parser to parse the whole feature list
-        aliases = config.get('aliases', {})
-        stream = await conn.submit(
-            'graph.features().graph().supportsComputer()', aliases=aliases)
-        msg = await stream.fetch_data()
-        features['computer'] = msg
-        stream = await conn.submit(
-            'graph.features().graph().supportsTransactions()', aliases=aliases)
-        msg = await stream.fetch_data()
-        features['transactions'] = msg
-        stream = await conn.submit(
-            'graph.features().graph().supportsPersistence()', aliases=aliases)
-        msg = await stream.fetch_data()
-        features['persistence'] = msg
-        stream = await conn.submit(
-            'graph.features().graph().supportsConcurrentAccess()', aliases=aliases)
-        msg = await stream.fetch_data()
-        features['concurrent_access'] = msg
-        stream = await conn.submit(
-            'graph.features().graph().supportsThreadedTransactions()', aliases=aliases)
-        msg = await stream.fetch_data()
-        features['threaded_transactions'] = msg
-    return Goblin(url, loop, get_hashable_id=get_hashable_id,
-                  features=features, **config)
-
-
 # Main API classes
 class Goblin:
     """
@@ -80,23 +40,35 @@ class Goblin:
     :param dict config: Config parameters for application
     """
 
-    DEFAULT_CONFIG = {
-        'translator': process.GroovyTranslator('g')
-    }
-
-    def __init__(self, url, loop, *, get_hashable_id=None, features=None,
-                 **config):
-        self._url = url
-        self._loop = loop
-        self._features = features
-        self._config = self.DEFAULT_CONFIG
-        self._config.update(config)
+    def __init__(self, cluster, *, get_hashable_id=None, aliases=None):
+        self._cluster = cluster
+        self._loop = self._cluster._loop
+        self._transactions = None
+        self._cluster = cluster
         self._vertices = collections.defaultdict(
             lambda: element.GenericVertex)
         self._edges = collections.defaultdict(lambda: element.GenericEdge)
         if not get_hashable_id:
             get_hashable_id = lambda x: x
         self._get_hashable_id = get_hashable_id
+        if aliases is None:
+            aliases = {}
+        self._aliases = aliases
+
+    @classmethod
+    async def open(cls, loop, *, get_hashable_id=None, aliases=None, **config):
+        # App currently only supports GraphSON 1
+        cluster = await driver.Cluster.open(
+            loop, aliases=aliases,
+            message_serializer=driver.GraphSONMessageSerializer,
+            **config)
+        app = Goblin(cluster, get_hashable_id=get_hashable_id, aliases=aliases)
+        await app.supports_transactions()
+        return app
+
+    @property
+    def config(self):
+        return self._cluster.config
 
     @property
     def vertices(self):
@@ -108,24 +80,6 @@ class Goblin:
         """Registered edge classes"""
         return self._edges
 
-    @property
-    def features(self):
-        """Vendor specific database implementation features"""
-        return self._features
-
-    def from_file(filepath):
-        """Load config from filepath. Not implemented"""
-        raise NotImplementedError
-
-    def from_obj(obj):
-        """Load config from object. Not implemented"""
-        raise NotImplementedError
-
-    @property
-    def translator(self):
-        """gremlin-python translator class"""
-        return self._config['translator']
-
     @property
     def url(self):
         """Database url"""
@@ -143,7 +97,30 @@ class Goblin:
             if element.__type__ == 'edge':
                 self._edges[element.__label__] = element
 
-    async def session(self, *, use_session=False):
+    def config_from_file(self, filename):
+        """
+        Load configuration from from file.
+
+        :param str filename: Path to the configuration file.
+        """
+        self._cluster.config_from_file(filename)
+
+    def config_from_yaml(self, filename):
+        self._cluster.config_from_yaml(filename)
+
+    def config_from_json(self, filename):
+        """
+        Load configuration from from JSON file.
+
+        :param str filename: Path to the configuration file.
+        """
+        self._cluster.config_from_json(filename)
+
+    def register_from_module(self, modulename):
+        raise NotImplementedError
+
+    async def session(self, *, use_session=False, processor='', op='eval',
+                      aliases=None):
         """
         Create a session object.
 
@@ -151,10 +128,26 @@ class Goblin:
 
         :returns: :py:class:`Session<goblin.session.Session>` object
         """
-        aliases = self._config.get('aliases', None)
-        conn = await driver.GremlinServer.open(self.url, self._loop)
+        conn = await self._cluster.connect(processor=processor, op=op,
+                                           aliases=aliases)
+        transactions = await self.supports_transactions()
         return session.Session(self,
                                conn,
                                self._get_hashable_id,
-                               use_session=use_session,
-                               aliases=aliases)
+                               transactions,
+                               use_session=use_session)
+
+    async def supports_transactions(self):
+        if self._transactions is None:
+            conn = await self._cluster.get_connection()
+            stream = await conn.submit(
+                gremlin='graph.features().graph().supportsTransactions()',
+                aliases=self._aliases)
+            msg = await stream.fetch_data()
+            msg = msg.object
+            stream.close()
+            self._transactions = msg
+        return self._transactions
+
+    async def close(self):
+        await self._cluster.close()
diff --git a/goblin/driver/__init__.py b/goblin/driver/__init__.py
index 0e790b6aa15251126a5e0e7ef5e1e0735f90dfef..f60c40ba52da3f22a2c584e4528530f732588036 100644
--- a/goblin/driver/__init__.py
+++ b/goblin/driver/__init__.py
@@ -15,6 +15,10 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
 
-from goblin.driver.api import GremlinServer
-from goblin.driver.connection import AbstractConnection
-from goblin.driver.graph import AsyncRemoteGraph
+from goblin.driver.cluster import Cluster
+from goblin.driver.client import Client, SessionedClient
+from goblin.driver.connection import AbstractConnection, Connection
+from goblin.driver.graph import AsyncGraph
+from goblin.driver.serializer import (
+    GraphSONMessageSerializer, GraphSON2MessageSerializer)
+from goblin.driver.server import GremlinServer
diff --git a/goblin/driver/api.py b/goblin/driver/api.py
deleted file mode 100644
index 717af379c6ab7382d93c31173ce95073f74498e1..0000000000000000000000000000000000000000
--- a/goblin/driver/api.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2016 ZEROFAIL
-#
-# This file is part of Goblin.
-#
-# Goblin is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Goblin is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
-
-import asyncio
-import aiohttp
-
-from goblin.driver import connection
-
-
-class GremlinServer:
-    """Factory class that generates connections to the Gremlin Server"""
-
-    @classmethod
-    async def open(cls,
-                   url,
-                   loop,
-                   *,
-                   client_session=None,
-                   username=None,
-                   password=None):
-        """
-        Open a connection to the Gremlin Server.
-
-        :param str url: Database url
-        :param asyncio.BaseEventLoop loop: Event loop implementation
-        :param aiohttp.client.ClientSession client_session: Client session
-            used to generate websocket connections.
-        :param str username: Username for server auth
-        :param str password: Password for server auth
-
-        :returns: :py:class:`Connection<goblin.driver.connection.Connection>`
-        """
-        if client_session is None:
-            client_session = aiohttp.ClientSession(loop=loop)
-        ws = await client_session.ws_connect(url)
-        return connection.Connection(url, ws, loop, client_session,
-                                     username=username, password=password)
diff --git a/goblin/driver/client.py b/goblin/driver/client.py
new file mode 100644
index 0000000000000000000000000000000000000000..b00e51f291afaee75b485fab5dacfb1b98a3738d
--- /dev/null
+++ b/goblin/driver/client.py
@@ -0,0 +1,111 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+from goblin import exception
+
+
+class Client:
+    """
+    Client that utilizes a :py:class:`Cluster<goblin.driver.cluster.Cluster>`
+    to access a cluster of Gremlin Server hosts. Issues requests to hosts using
+    a round robin strategy.
+
+    :param goblin.driver.cluster.Cluster cluster: Cluster used by
+        client
+    :param asyncio.BaseEventLoop loop:
+    """
+    def __init__(self, cluster, loop, *, aliases=None, processor=None,
+                 op=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 message_serializer(self):
+        return self.cluster.config['message_serializer']
+
+    @property
+    def cluster(self):
+        """
+        Readonly property.
+
+        :returns: The instance of
+            :py:class:`Cluster<goblin.driver.cluster.Cluster>` associated with
+            client.
+        """
+        return self._cluster
+
+    def alias(self, aliases):
+        client = Client(self._cluster, self._loop,
+                        aliases=aliases)
+        return client
+
+    async def submit(self,
+                     *,
+                     processor=None,
+                     op=None,
+                     **args):
+        """
+        **coroutine** 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.
+        :returns: :py:class:`Response` object
+        """
+        processor = processor or self._processor
+        op = op or self._op
+        # Certain traversal processor ops don't support this arg
+        if not args.get('aliases') and op not in ['keys', 'close',
+                                                  'authentication']:
+            args['aliases'] = self._aliases
+        conn = await self.cluster.get_connection()
+        resp = await conn.submit(
+            processor=processor, op=op, **args)
+        self._loop.create_task(conn.release_task(resp))
+        return resp
+
+
+class SessionedClient(Client):
+
+    def __init__(self, cluster, loop, session, *, aliases=None):
+        super().__init__(cluster, loop, aliases=aliases, processor='session',
+                         op='eval')
+        self._session = session
+
+    @property
+    def session(self):
+        return self._session
+
+    async def submit(self, **args):
+        if not args.get('gremlin', ''):
+            raise exception.ClientError('Session requires a gremlin string')
+        return await super().submit(processor='session', op='eval',
+                                    session=self.session,
+                                    **args)
+
+    async def close(self):
+        raise NotImplementedError
diff --git a/goblin/driver/cluster.py b/goblin/driver/cluster.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e0024a3697a52512632f4bf978900c754610876
--- /dev/null
+++ b/goblin/driver/cluster.py
@@ -0,0 +1,194 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+import asyncio
+import collections
+import configparser
+import json
+import ssl
+
+import yaml
+
+from goblin import driver, exception
+
+
+def my_import(name):
+    components = name.split('.')
+    mod = __import__(components[0])
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+
+class Cluster:
+    """
+    A cluster of Gremlin Server hosts. This object provides the main high
+    level interface used by the :py:mod:`goblin.driver` module.
+
+    :param asyncio.BaseEventLoop loop:
+    """
+
+    DEFAULT_CONFIG = {
+        'scheme': 'ws',
+        'hosts': ['localhost'],
+        'port': 8182,
+        'ssl_certfile': '',
+        'ssl_keyfile': '',
+        'ssl_password': '',
+        'username': '',
+        'password': '',
+        'response_timeout': None,
+        'max_conns': 4,
+        'min_conns': 1,
+        'max_times_acquired': 16,
+        'max_inflight': 64,
+        'message_serializer': 'goblin.driver.GraphSON2MessageSerializer'
+    }
+
+    def __init__(self, loop, aliases=None, **config):
+        self._loop = loop
+        self._config = self._get_message_serializer(dict(self.DEFAULT_CONFIG))
+        self._config.update(config)
+        self._hosts = collections.deque()
+        self._closed = False
+        if aliases is None:
+            aliases = {}
+        self._aliases = aliases
+
+    @classmethod
+    async def open(cls, loop, *, aliases=None, configfile=None, **config):
+        """
+        **coroutine** Open a cluster, connecting to all available hosts as
+        specified in configuration.
+
+        :param asyncio.BaseEventLoop loop:
+        :param str configfile: Optional configuration file in .json or
+            .yml format
+        """
+        cluster = cls(loop, aliases=aliases, **config)
+        if configfile:
+            cluster.config_from_file(configfile)
+        await cluster.establish_hosts()
+        return cluster
+
+    @property
+    def hosts(self):
+        return self._hosts
+
+    @property
+    def config(self):
+        """
+        Readonly property.
+
+        :returns: `dict` containing the cluster configuration
+        """
+        return self._config
+
+    async def get_connection(self):
+        """
+        **coroutine** Get connection from next available host in a round robin
+        fashion.
+
+        :returns: :py:class:`Connection<goblin.driver.connection.Connection>`
+        """
+        if not self._hosts:
+            await self.establish_hosts()
+        host = self._hosts.popleft()
+        conn = await host.get_connection()
+        self._hosts.append(host)
+        return conn
+
+    async def establish_hosts(self):
+        """
+        **coroutine** Connect to all hosts as specified in configuration.
+        """
+        scheme = self._config['scheme']
+        hosts = self._config['hosts']
+        port = self._config['port']
+        for host in hosts:
+            url = '{}://{}:{}/gremlin'.format(scheme, host, port)
+            host = await driver.GremlinServer.open(
+                url, self._loop, **dict(self._config))
+            self._hosts.append(host)
+
+    def config_from_file(self, filename):
+        """
+        Load configuration from from file.
+
+        :param str filename: Path to the configuration file.
+        """
+        if filename.endswith('yml') or filename.endswith('yaml'):
+            self.config_from_yaml(filename)
+        elif filename.endswith('.json'):
+            self.config_from_json(filename)
+        else:
+            raise exception.ConfigurationError('Unknown config file format')
+
+    def config_from_yaml(self, filename):
+        with open(filename, 'r') as f:
+            config = yaml.load(f)
+        config = self._get_message_serializer(config)
+        self._config.update(config)
+
+    def config_from_json(self, filename):
+        """
+        Load configuration from from JSON file.
+
+        :param str filename: Path to the configuration file.
+        """
+        with open(filename, 'r') as f:
+            config = json.load(f)
+        config = self._get_message_serializer(config)
+        self.config.update(config)
+
+    def _get_message_serializer(self, config):
+        message_serializer = config.get('message_serializer', '')
+        if message_serializer:
+            config['message_serializer'] = my_import(message_serializer)
+        return config
+
+    def config_from_module(self, filename):
+        raise NotImplementedError
+
+    async def connect(self, processor=None, op=None, aliases=None,
+                      session=None):
+        """
+        **coroutine** Get a connected client. Main API method.
+
+        :returns: A connected instance of `Client<goblin.driver.client.Client>`
+        """
+        aliases = aliases or self._aliases
+        if not self._hosts:
+            await self.establish_hosts()
+        if session:
+            host = self._hosts.popleft()
+            client = driver.SessionedClient(host, self._loop, session,
+                                            aliases=aliases)
+            self._hosts.append(host)
+        else:
+            client = driver.Client(self, self._loop, processor=processor,
+                                   op=op, aliases=aliases)
+        return client
+
+    async def close(self):
+        """**coroutine** Close cluster and all connected hosts."""
+        waiters = []
+        while self._hosts:
+            host = self._hosts.popleft()
+            waiters.append(host.close())
+        await asyncio.gather(*waiters)
+        self._closed = True
diff --git a/goblin/driver/connection.py b/goblin/driver/connection.py
index 7c783d6e4a1362ef1b5414564516b32909e1469f..1b63c42707e98a6f5db172287039ed28e10fe684 100644
--- a/goblin/driver/connection.py
+++ b/goblin/driver/connection.py
@@ -26,6 +26,7 @@ import uuid
 import aiohttp
 
 from goblin import exception
+from goblin.driver import serializer
 
 
 logger = logging.getLogger(__name__)
@@ -41,7 +42,8 @@ def error_handler(fn):
     async def wrapper(self):
         msg = await fn(self)
         if msg:
-            if msg.status_code not in [200, 206, 204]:
+            if msg.status_code not in [200, 206]:
+                self.close()
                 raise exception.GremlinServerError(
                     "{0}: {1}".format(msg.status_code, msg.message))
             msg = msg.data
@@ -51,10 +53,25 @@ def error_handler(fn):
 
 class Response:
     """Gremlin Server response implementated as an async iterator."""
-    def __init__(self, response_queue, loop):
+    def __init__(self, response_queue, request_id, timeout, loop):
         self._response_queue = response_queue
+        self._request_id = request_id
         self._loop = loop
-        self._done = False
+        self._timeout = timeout
+        self._done = asyncio.Event(loop=self._loop)
+
+    @property
+    def request_id(self):
+        return self._request_id
+
+    @property
+    def done(self):
+        """
+        Readonly property.
+
+        :returns: `asyncio.Event` object
+        """
+        return self._done
 
     async def __aiter__(self):
         return self
@@ -62,18 +79,30 @@ class Response:
     async def __anext__(self):
         msg = await self.fetch_data()
         if msg:
-            return msg
+            return msg.object
         else:
             raise StopAsyncIteration
 
+    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 fetch_data(self):
         """Get a single message from the response stream"""
-        if self._done:
+        if self.done.is_set():
             return None
-        msg = await self._response_queue.get()
+        try:
+            msg = await asyncio.wait_for(self._response_queue.get(),
+                                         timeout=self._timeout,
+                                         loop=self._loop)
+        except asyncio.TimeoutError:
+            self.close()
+            raise exception.ResponseTimeoutError('Response timed out')
         if msg is None:
-            self._done = True
+            self.close()
         return msg
 
 
@@ -92,167 +121,169 @@ class Connection(AbstractConnection):
     """
     Main classd for interacting with the Gremlin Server. Encapsulates a
     websocket connection. Not instantiated directly. Instead use
-    :py:meth:`GremlinServer.open<goblin.driver.api.GremlinServer.open>`.
+    :py:meth:`Connection.open<goblin.driver.connection.Connection.open>`.
+
+    :param str url: url for host Gremlin Server
+    :param aiohttp.ClientWebSocketResponse ws: open websocket connection
+    :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
     """
-    def __init__(self, url, ws, loop, conn_factory, *, username=None,
-                 password=None):
+    def __init__(self, url, ws, loop, client_session, username, password,
+                 max_inflight, response_timeout, message_serializer):
         self._url = url
         self._ws = ws
         self._loop = loop
-        self._conn_factory = conn_factory
+        self._client_session = client_session
+        self._response_timeout = response_timeout
         self._username = username
         self._password = password
         self._closed = False
         self._response_queues = {}
+        self._receive_task = self._loop.create_task(self._receive())
+        self._semaphore = asyncio.Semaphore(value=max_inflight,
+                                            loop=self._loop)
+        if isinstance(message_serializer, type):
+            message_serializer = message_serializer()
+        self._message_serializer = message_serializer
+
+    @classmethod
+    async def open(cls, url, loop, *, ssl_context=None, username='',
+                   password='', max_inflight=64, response_timeout=None,
+                   message_serializer=serializer.GraphSON2MessageSerializer):
+        """
+        **coroutine** Open a connection to the Gremlin Server.
+
+        :param str url: url for host Gremlin Server
+        :param asyncio.BaseEventLoop loop:
+        :param ssl.SSLContext ssl_context:
+        :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
+
+        :returns: :py:class:`Connection<goblin.driver.connection.Connection>`
+        """
+        connector = aiohttp.TCPConnector(ssl_context=ssl_context, loop=loop)
+        client_session = aiohttp.ClientSession(loop=loop, connector=connector)
+        ws = await client_session.ws_connect(url)
+        return cls(url, ws, loop, client_session, username, password,
+                   max_inflight, response_timeout, message_serializer)
 
     @property
-    def response_queues(self):
-        return self._response_queues
+    def message_serializer(self):
+        return self._message_serializer
 
     @property
     def closed(self):
-        return self._closed
+        """
+        Check if connection has been closed.
+
+        :returns: `bool`
+        """
+        return self._closed or self._ws.closed
 
     @property
     def url(self):
+        """
+        Readonly property.
+
+        :returns: str The url association with this connection.
+        """
         return self._url
 
     async def submit(self,
-                    gremlin,
-                    *,
-                    bindings=None,
-                    lang='gremlin-groovy',
-                    aliases=None,
-                    op="eval",
-                    processor="",
-                    session=None,
-                    request_id=None):
+                     *,
+                     processor='',
+                     op='eval',
+                     **args):
         """
         Submit a script and bindings to the Gremlin Server
 
-        :param str gremlin: Gremlin script to submit to server.
-        :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 aliases: 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 str session: Session id (optional). Typically a uuid
-        :param str request_id: Request id (optional). Typically a uuid
-
+        :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.
         :returns: :py:class:`Response` object
         """
-        if aliases is None:
-            aliases = {}
-        if request_id is None:
-            request_id = str(uuid.uuid4())
-        message = self._prepare_message(gremlin,
-                                        bindings,
-                                        lang,
-                                        aliases,
-                                        op,
-                                        processor,
-                                        session,
-                                        request_id)
+        await self._semaphore.acquire()
+        request_id = str(uuid.uuid4())
+        message = self._message_serializer.serialize_message(
+            request_id, processor, op, **args)
         response_queue = asyncio.Queue(loop=self._loop)
-        self.response_queues[request_id] = response_queue
+        self._response_queues[request_id] = response_queue
         if self._ws.closed:
-            self._ws = await self.conn_factory.ws_connect(self.url)
+            self._ws = await self.client_session.ws_connect(self.url)
         self._ws.send_bytes(message)
-        self._loop.create_task(self._receive())
-        return Response(response_queue, self._loop)
+        resp = Response(response_queue, request_id, self._response_timeout, self._loop)
+        self._loop.create_task(self._terminate_response(resp, request_id))
+        return resp
+
+    def _authenticate(self, username, password, session):
+        auth = b''.join([b'\x00', username.encode('utf-8'),
+                         b'\x00', password.encode('utf-8')])
+        request_id = str(uuid.uuid4())
+        args = {'sasl': base64.b64encode(auth).decode()}
+        message = self._message_serializer.serialize_message(
+            request_id, '', 'authentication', **args)
+        self._ws.send_bytes(message, binary=True)
 
     async def close(self):
-        """Close underlying connection and mark as closed."""
+        """**coroutine** Close underlying connection and mark as closed."""
+        self._receive_task.cancel()
         await self._ws.close()
         self._closed = True
-        await self._conn_factory.close()
-
-    def _prepare_message(self, gremlin, bindings, lang, aliases, op,
-                         processor, session, request_id):
-        message = {
-            "requestId": request_id,
-            "op": op,
-            "processor": processor,
-            "args": {
-                "gremlin": gremlin,
-                "bindings": bindings,
-                "language":  lang,
-                "aliases": aliases
-            }
-        }
-        message = self._finalize_message(message, processor, session)
-        return message
-
-    def _authenticate(self, username, password, processor, session):
-        auth = b"".join([b"\x00", username.encode("utf-8"),
-                         b"\x00", password.encode("utf-8")])
-        message = {
-            "requestId": str(uuid.uuid4()),
-            "op": "authentication",
-            "processor": "",
-            "args": {
-                "sasl": base64.b64encode(auth).decode()
-            }
-        }
-        message = self._finalize_message(message, processor, session)
-        self._ws.submit(message, binary=True)
-
-    def _finalize_message(self, message, processor, session):
-        if processor == "session":
-            if session is None:
-                raise RuntimeError("session processor requires a session id")
-            else:
-                message["args"].update({"session": session})
-        message = json.dumps(message)
-        return self._set_message_header(message, "application/json")
-
-    @staticmethod
-    def _set_message_header(message, mime_type):
-        if mime_type == "application/json":
-            mime_len = b"\x10"
-            mime_type = b"application/json"
-        else:
-            raise ValueError("Unknown mime type.")
-        return b"".join([mime_len, mime_type, message.encode("utf-8")])
+        await self._client_session.close()
+
+    async def _terminate_response(self, resp, request_id):
+        await resp.done.wait()
+        del self._response_queues[request_id]
+        self._semaphore.release()
 
     async def _receive(self):
-        data = await self._ws.receive()
-        if data.tp == aiohttp.MsgType.close:
-            await self._ws.close()
-        elif data.tp == aiohttp.MsgType.error:
-            raise data.data
-        elif data.tp == aiohttp.MsgType.closed:
-            pass
-        else:
-            if data.tp == aiohttp.MsgType.binary:
-                data = data.data.decode()
-            elif data.tp == aiohttp.MsgType.text:
-                data = data.strip()
-            message = json.loads(data)
-            request_id = message['requestId']
-            status_code = message['status']['code']
-            data = message["result"]["data"]
-            msg = message["status"]["message"]
-            response_queue = self._response_queues[request_id]
-            if status_code == 407:
-                await self._authenticate(self._username, self._password,
-                                         self._processor, self._session)
-                self._loop.create_task(self._receive())
+        while True:
+            data = await self._ws.receive()
+            if data.tp == aiohttp.MsgType.close:
+                await self._ws.close()
+            elif data.tp == aiohttp.MsgType.error:
+                raise data.data
+            elif data.tp == aiohttp.MsgType.closed:
+                pass
             else:
-                if data:
-                    for result in data:
-                        message = Message(status_code, result, msg)
-                        response_queue.put_nowait(message)
-                else:
-                    message = Message(status_code, data, msg)
-                    response_queue.put_nowait(message)
-                if status_code == 206:
-                    self._loop.create_task(self._receive())
-                else:
+                if data.tp == aiohttp.MsgType.binary:
+                    data = data.data.decode()
+                elif data.tp == aiohttp.MsgType.text:
+                    data = data.strip()
+                message = json.loads(data)
+                request_id = message['requestId']
+                status_code = message['status']['code']
+                data = message['result']['data']
+                msg = message['status']['message']
+                response_queue = self._response_queues[request_id]
+                if status_code == 407:
+                    await self._authenticate(self._username, self._password,
+                                             self._processor)
+                elif status_code == 204:
                     response_queue.put_nowait(None)
-                    del self._response_queues[request_id]
+                else:
+                    if data:
+                        for result in data:
+                            result = self._message_serializer.deserialize_message(result)
+                            message = Message(status_code, result, msg)
+                            response_queue.put_nowait(message)
+                    else:
+                        data = self._message_serializer.deserialize_message(data)
+                        message = Message(status_code, data, msg)
+                        response_queue.put_nowait(message)
+                    if status_code != 206:
+                        response_queue.put_nowait(None)
 
     async def __aenter__(self):
         return self
@@ -260,3 +291,6 @@ class Connection(AbstractConnection):
     async def __aexit__(self, exc_type, exc, tb):
         await self.close()
         self._conn = None
+
+
+DriverRemoteConnection = Connection
diff --git a/goblin/driver/graph.py b/goblin/driver/graph.py
index 4d19a6d7c1808fe7bcaa14af22ae78f81ddde2f1..4720b8ddcb7bf9b1e3d986b9498e27d943130b1c 100644
--- a/goblin/driver/graph.py
+++ b/goblin/driver/graph.py
@@ -17,75 +17,151 @@
 
 """A temporary solution to allow integration with gremlin_python package."""
 
+import functools
+
 from gremlin_python.process.graph_traversal import (
-    GraphTraversalSource, GraphTraversal)
-from gremlin_python.process.traversal import (
-    TraversalStrategy, TraversalStrategies)
+    GraphTraversal, GraphTraversalSource)
+from gremlin_python.process.traversal import TraversalStrategies
+from gremlin_python.driver.remote_connection import (
+    RemoteStrategy, RemoteTraversalSideEffects)
+from gremlin_python.structure.graph import Graph
+from goblin.driver.serializer import GraphSON2MessageSerializer
 
 
-class AsyncGraphTraversal(GraphTraversal):
-    def __init__(self, graph, traversal_strategies, bytecode):
-        GraphTraversal.__init__(self, graph, traversal_strategies, bytecode)
+class AsyncRemoteTraversalSideEffects(RemoteTraversalSideEffects):
 
-    def __repr__(self):
-        return self.graph.translator.translate(self.bytecode)
+    async def keys(self):
+        return await self.keys_lambda()
 
-    def toList(self):
-        raise NotImplementedError
-
-    def toSet(self):
-        raise NotImplementedError
+    async def get(self, key):
+        return await self.value_lambda(sideEffectKey=key)
 
-    async def next(self):
-        resp = await self.traversal_strategies.apply(self)
-        return resp
 
+class AsyncRemoteStrategy(RemoteStrategy):
 
-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)
+        if isinstance(self.remote_connection.message_serializer,
+                      GraphSON2MessageSerializer):
+            processor = 'traversal'
+            op = 'bytecode'
+            side_effects = AsyncRemoteTraversalSideEffects
+        else:
+            processor = ''
+            op = 'eval'
+            side_effects = None
+        if traversal.traversers is None:
+            resp = await self.remote_connection.submit(
+                gremlin=traversal.bytecode, processor=processor, op=op)
+            traversal.traversers = resp
+            if side_effects:
+                keys_lambda = functools.partial(self.remote_connection.submit,
+                                                processor='traversal',
+                                                op='keys',
+                                                sideEffect=resp.request_id)
+                value_lambda = functools.partial(self.remote_connection.submit,
+                                                 processor='traversal',
+                                                 op='gather',
+                                                 sideEffect=resp.request_id)
+                side_effects = side_effects(keys_lambda, value_lambda)
+            traversal.side_effects = side_effects
+
+
+
+class AsyncGraphTraversal(GraphTraversal):
+
+    async def __aiter__(self):
+        return self
+
+    async def __anext__(self):
+        if self.traversers is None:
+            await self._get_traversers()
+        if self.last_traverser is None:
+            self.last_traverser = await self.traversers.fetch_data()
+            if self.last_traverser is None:
+                raise StopAsyncIteration
+        obj = self.last_traverser.object
+        self.last_traverser.bulk = self.last_traverser.bulk - 1
+        if self.last_traverser.bulk <= 0:
+            self.last_traverser = None
+        return obj
+
+    async def _get_traversers(self):
+        for ts in self.traversal_strategies.traversal_strategies:
+            await ts.apply(self)
+
+    async def next(self, amount=None):
+        """
+        **coroutine** Return the next result from the iterator.
+
+        :param int amount: The number of results returned, defaults to None
+            (1 result)
+        """
+        if amount is None:
+            try:
+                return await self.__anext__()
+            except StopAsyncIteration:
+                pass
+        else:
+            count = 0
+            tempList = []
+            while count < amount:
+                count = count + 1
+                try: temp = await self.__anext__()
+                except StopIteration: return tempList
+                tempList.append(temp)
+            return tempList
+
+    async def toList(self):
+        """**coroutine** Submit the travesal, iterate results, return a list"""
+        results = []
+        async for msg in self:
+            results.append(msg)
+        return results
+
+    async def toSet(self):
+        """**coroutine** Submit the travesal, iterate results, return a set"""
+        results = set()
+        async for msg in self:
+            results.add(msg)
+        return results
+
+    async def oneOrNone(self):
+        """
+        **coroutine** Get one or zero results from a traveral. Returns last
+        iterated result.
+        """
+        result = None
+        async for msg in self:
+            result = msg
         return result
 
+    def iterate(self):
+        raise NotImplementedError
 
-class AsyncGraph:
-    def traversal(self):
-        return GraphTraversalSource(self, self.traversal_strategy,
-                                    graph_traversal=self.graph_traversal)
-
-
-class AsyncRemoteGraph(AsyncGraph):
-    """
-    Generate asynchronous gremlin traversals using native Python.
-
-    :param gremlin_python.process.GroovyTranslator translator:
-        gremlin_python translator class, typically
-        :py:class:`GroovyTranslator<gremlin_python.process.GroovyTranslator>`
-    :param goblin.driver.connection connection: underlying remote
-        connection
-    :param gremlin_python.process.GraphTraversal graph_traversal:
-        Custom graph traversal class
-    """
-    def __init__(self, translator, remote_connection, *, graph_traversal=None):
-        self.traversal_strategy = AsyncRemoteStrategy()  # A single traversal strategy
-        self.translator = translator
-        self.remote_connection = remote_connection
-        if graph_traversal is None:
-            graph_traversal = AsyncGraphTraversal
-        self.graph_traversal = graph_traversal
+    def nextTraverser(self):
+        raise NotImplementedError
 
-    def __repr__(self):
-        return "remotegraph[" + self.remote_connection.url + "]"
 
-    async def close(self):
-        """Close underlying remote connection"""
-        await self.remote_connection.close()
-        self.remote_connection = None
+class AsyncGraph(Graph):
+    """Generate asynchronous gremlin traversals using native Python"""
 
-    async def __aenter__(self):
-        return self
+    def traversal(self, *, graph_traversal=None, remote_strategy=None):
+        """
+        Get a traversal source from the Graph
+
+        :param gremlin_python.process.GraphTraversal graph_traversal:
+            Custom graph traversal class
+        :param gremlin_python.driver.remote_connection.RemoteStrategy remote_strategy:
+            Custom remote strategy class
 
-    async def __aexit__(self, exc_type, exc, tb):
-        await self.close()
+        :returns:
+            :py:class:`gremlin_python.process.graph_traversal.GraphTraversalSource`
+        """
+        if graph_traversal is None:
+            graph_traversal = AsyncGraphTraversal
+        if remote_strategy is None:
+            remote_strategy = AsyncRemoteStrategy
+        return GraphTraversalSource(
+            self, TraversalStrategies.global_cache[self.__class__],
+            remote_strategy=remote_strategy,
+            graph_traversal=graph_traversal)
diff --git a/goblin/driver/pool.py b/goblin/driver/pool.py
new file mode 100644
index 0000000000000000000000000000000000000000..3df37dedf0ee5a3194ae2986989eed3db11b445c
--- /dev/null
+++ b/goblin/driver/pool.py
@@ -0,0 +1,224 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+import asyncio
+import collections
+
+import aiohttp
+
+from goblin.driver import connection
+
+
+class PooledConnection:
+    """
+    Wrapper for :py:class:`Connection<goblin.driver.connection.Connection>`
+    that helps manage tomfoolery associated with connection pooling.
+
+    :param goblin.driver.connection.Connection conn:
+    :param goblin.driver.pool.ConnectionPool pool:
+    """
+    def __init__(self, conn, pool):
+        self._conn = conn
+        self._pool = pool
+        self._times_acquired = 0
+
+    @property
+    def times_acquired(self):
+        """
+        Readonly property.
+
+        :returns: int
+        """
+        return self._times_acquired
+
+    def increment_acquired(self):
+        """Increment times acquired attribute by 1"""
+        self._times_acquired += 1
+
+    def decrement_acquired(self):
+        """Decrement times acquired attribute by 1"""
+        self._times_acquired -= 1
+
+    async def submit(self,
+                     *,
+                     processor='',
+                     op='eval',
+                     **args):
+        """
+        **coroutine** 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.
+
+        :returns: :py:class:`Response` object
+        """
+        return await self._conn.submit(processor=processor, op=op, **args)
+
+    async def release_task(self, resp):
+        await resp.done.wait()
+        self.release()
+
+    def release(self):
+        self._pool.release(self)
+
+    async def close(self):
+        """Close underlying connection"""
+        await self._conn.close()
+        self._conn = None
+        self._pool = None
+
+    @property
+    def closed(self):
+        """
+        Readonly property.
+
+        :returns: bool
+        """
+        return self._conn.closed
+
+
+class ConnectionPool:
+    """
+    A pool of connections to a Gremlin Server host.
+
+    :param str url: url for host Gremlin Server
+    :param asyncio.BaseEventLoop loop:
+    :param ssl.SSLContext ssl_context:
+    :param str username: Username for database auth
+    :param str password: Password for database auth
+    :param float response_timeout: (optional) `None` by default
+    :param int max_conns: Maximum number of conns to a host
+    :param int min_connsd: Minimum number of conns to a host
+    :param int max_times_acquired: Maximum number of times a conn can be
+        shared by multiple coroutines (clients)
+    :param int max_inflight: Maximum number of unprocessed requests at any
+        one time on the connection
+    """
+
+    def __init__(self, url, loop, ssl_context, username, password, max_conns,
+                 min_conns, max_times_acquired, max_inflight, response_timeout,
+                 message_serializer):
+        self._url = url
+        self._loop = loop
+        self._ssl_context = ssl_context
+        self._username = username
+        self._password = password
+        self._max_conns = max_conns
+        self._min_conns = min_conns
+        self._max_times_acquired = max_times_acquired
+        self._max_inflight = max_inflight
+        self._response_timeout = response_timeout
+        self._message_serializer = message_serializer
+        self._condition = asyncio.Condition(loop=self._loop)
+        self._available = collections.deque()
+        self._acquired = collections.deque()
+
+    @property
+    def url(self):
+        """
+        Readonly property.
+
+        :returns: str
+        """
+        return self._url
+
+    async def init_pool(self):
+        """**coroutine** Open minumum number of connections to host"""
+        for i in range(self._min_conns):
+            conn = await self._get_connection(self._username,
+                                              self._password,
+                                              self._max_inflight,
+                                              self._response_timeout,
+                                              self._message_serializer)
+            self._available.append(conn)
+
+    def release(self, conn):
+        """
+        Release connection back to pool after use.
+
+        :param PooledConnection conn:
+        """
+        if conn.closed:
+            self._acquired.remove(conn)
+        else:
+            conn.decrement_acquired()
+            if not conn.times_acquired:
+                self._acquired.remove(conn)
+                self._available.append(conn)
+        self._loop.create_task(self._notify())
+
+    async def _notify(self):
+        async with self._condition:
+            self._condition.notify()
+
+    async def acquire(self, username=None, password=None, max_inflight=None,
+                      response_timeout=None, message_serializer=None):
+        """**coroutine** Acquire a new connection from the pool."""
+        username = username or self._username
+        password = password or self._password
+        response_timeout = response_timeout or self._response_timeout
+        max_inflight = max_inflight or self._max_inflight
+        message_serializer = message_serializer or self._message_serializer
+        async with self._condition:
+            while True:
+                while self._available:
+                    conn = self._available.popleft()
+                    if not conn.closed:
+                        conn.increment_acquired()
+                        self._acquired.append(conn)
+                        return conn
+                if len(self._acquired) < self._max_conns:
+                    conn = await self._get_connection(username, password,
+                                                      max_inflight,
+                                                      response_timeout,
+                                                      message_serializer)
+                    conn.increment_acquired()
+                    self._acquired.append(conn)
+                    return conn
+                else:
+                    for x in range(len(self._acquired)):
+                        conn = self._acquired.popleft()
+                        if conn.times_acquired < self._max_times_acquired:
+                            conn.increment_acquired()
+                            self._acquired.append(conn)
+                            return conn
+                        self._acquired.append(conn)
+                    else:
+                        await self._condition.wait()
+
+    async def close(self):
+        """**coroutine** Close connection pool."""
+        waiters = []
+        while self._available:
+            conn = self._available.popleft()
+            waiters.append(conn.close())
+        while self._acquired:
+            conn = self._acquired.popleft()
+            waiters.append(conn.close())
+        await asyncio.gather(*waiters)
+
+    async def _get_connection(self, username, password, max_inflight,
+                              response_timeout, message_serializer):
+        conn = await connection.Connection.open(
+            self._url, self._loop, ssl_context=self._ssl_context,
+            username=username, password=password,
+            response_timeout=response_timeout,
+            message_serializer=message_serializer)
+        conn = PooledConnection(conn, self)
+        return conn
diff --git a/goblin/driver/serializer.py b/goblin/driver/serializer.py
new file mode 100644
index 0000000000000000000000000000000000000000..d39a759e6aafd6f94d17641d04528fc79ac49c3c
--- /dev/null
+++ b/goblin/driver/serializer.py
@@ -0,0 +1,148 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+import json
+
+from gremlin_python.process.traversal import Bytecode, Traverser
+from gremlin_python.process.translator import GroovyTranslator
+from gremlin_python.structure.io.graphson import GraphSONWriter, GraphSONReader
+
+
+class Processor:
+    """Base class for OpProcessor serialization system."""
+
+    def get_op(self, op):
+        op = getattr(self, op, None)
+        if not op:
+            raise Exception("Processor does not support op")
+        return op
+
+
+class GraphSONMessageSerializer:
+    """Message serializer for GraphSONv1"""
+    # processors and ops
+    class standard(Processor):
+
+        def authentication(self, args):
+            return args
+
+        def eval(self, args):
+            gremlin = args['gremlin']
+            if isinstance(gremlin, Bytecode):
+                translator = GroovyTranslator('g')
+                args['gremlin'] = translator.translate(gremlin)
+                args['bindings'] = gremlin.bindings
+            return args
+
+
+    class session(standard):
+        pass
+
+
+    def get_processor(self, processor):
+        processor = getattr(self, processor, None)
+        if not processor:
+            raise Exception("Unknown processor")
+        return processor()
+
+    def serialize_message(self, request_id, processor, op, **args):
+        if not processor:
+            processor_obj = self.get_processor('standard')
+        else:
+            processor_obj = self.get_processor(processor)
+        op_method = processor_obj.get_op(op)
+        args = op_method(args)
+        message = self.build_message(request_id, processor, op, args)
+        return message
+
+    def build_message(self, request_id, processor, op, args):
+        message = {
+            'requestId': request_id,
+            'processor': processor,
+            'op': op,
+            'args': args
+        }
+        return self.finalize_message(message,  b'\x10', b'application/json')
+
+    def finalize_message(self, message, mime_len, mime_type):
+        message = json.dumps(message)
+        message = b''.join([mime_len, mime_type, message.encode('utf-8')])
+        return message
+
+    def deserialize_message(self, message):
+        return Traverser(message)
+
+
+class GraphSON2MessageSerializer(GraphSONMessageSerializer):
+    """Message serializer for GraphSONv2"""
+
+    class session(GraphSONMessageSerializer.session):
+
+        def close(self, args):
+            return args
+
+
+    class traversal(Processor):
+
+        def authentication(self, args):
+            return args
+
+        def bytecode(self, args):
+            gremlin = args['gremlin']
+            args['gremlin'] = GraphSONWriter.writeObject(gremlin)
+            aliases = args.get('aliases', '')
+            if not aliases:
+                aliases = {'g': 'g'}
+            args['aliases'] = aliases
+            return args
+
+        def close(self, args):
+            return self.keys(args)
+
+        def gather(self, args):
+            side_effect = args['sideEffect']
+            args['sideEffect'] = {'@type': 'g:UUID', '@value': side_effect}
+            aliases = args.get('aliases', '')
+            if not aliases:
+                aliases = {'g': 'g'}
+            args['aliases'] = aliases
+            return args
+
+        def keys(self, args):
+            side_effect = args['sideEffect']
+            args['sideEffect'] = {'@type': 'g:UUID', '@value': side_effect}
+            return args
+
+    def build_message(self, request_id, processor, op, args):
+        message = {
+            'requestId': {'@type': 'g:UUID', '@value': request_id},
+            'processor': processor,
+            'op': op,
+            'args': args
+        }
+        return self.finalize_message(message, b"\x21",
+                                     b"application/vnd.gremlin-v2.0+json")
+
+    def deserialize_message(self, message):
+        if isinstance(message, dict):
+            if message.get('@type', '') == 'g:Traverser':
+                obj = GraphSONReader._objectify(message)
+            else:
+                obj = Traverser(message.get('@value', message))
+        else:
+            obj = Traverser(message)
+        return obj
diff --git a/goblin/driver/server.py b/goblin/driver/server.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ba5a24b1fd0763b11cbbdfbad2b2e7a57f1425e
--- /dev/null
+++ b/goblin/driver/server.py
@@ -0,0 +1,113 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+from goblin.driver import pool
+
+
+class GremlinServer:
+    """
+    Class that wraps a connection pool. Currently doesn't do much, but may
+    be useful in the future....
+
+    :param pool.ConnectionPool pool:
+    """
+
+    def __init__(self, url, loop, **config):
+        self._pool = None
+        self._url = url
+        self._loop = loop
+        self._response_timeout = config['response_timeout']
+        self._username = config['username']
+        self._password = config['password']
+        self._max_times_acquired = config['max_times_acquired']
+        self._max_conns = config['max_conns']
+        self._min_conns = config['min_conns']
+        self._max_inflight = config['max_inflight']
+        self._message_serializer = config['message_serializer']
+        scheme = config['scheme']
+        if scheme in ['https', 'wss']:
+            certfile = config['ssl_certfile']
+            keyfile = config['ssl_keyfile']
+            ssl_password = config['ssl_password']
+            ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+            ssl_context.load_cert_chain(
+                certfile, keyfile=keyfile, password=ssl_password)
+            self._ssl_context = ssl_context
+        else:
+            self._ssl_context = None
+
+    @property
+    def url(self):
+        return self._url
+
+    @property
+    def pool(self):
+        """
+        Readonly property.
+
+        :returns: :py:class:`ConnectionPool<goblin.driver.pool.ConnectionPool>`
+        """
+        if self._pool:
+            return self._pool
+
+    async def close(self):
+        """**coroutine** Close underlying connection pool."""
+        if self._pool:
+            await self._pool.close()
+            self._pool = None
+
+    async def get_connection(self):
+        """**coroutine** Acquire a connection from the pool."""
+        try:
+            conn = await self._pool.acquire()
+        except AttributeError:
+            raise Exception("Please initialize pool")
+        return conn
+
+    async def initialize(self):
+        conn_pool = pool.ConnectionPool(
+            self._url, self._loop, self._ssl_context, self._username,
+            self._password, self._max_conns, self._min_conns,
+            self._max_times_acquired, self._max_inflight,
+            self._response_timeout, self._message_serializer)
+        await conn_pool.init_pool()
+        self._pool = conn_pool
+
+    @classmethod
+    async def open(cls, url, loop, **config):
+        """
+        **coroutine** Establish connection pool and host to Gremlin Server.
+
+        :param str url: url for host Gremlin Server
+        :param asyncio.BaseEventLoop loop:
+        :param ssl.SSLContext ssl_context:
+        :param str username: Username for database auth
+        :param str password: Password for database auth
+        :param float response_timeout: (optional) `None` by default
+        :param int max_conns: Maximum number of conns to a host
+        :param int min_connsd: Minimum number of conns to a host
+        :param int max_times_acquired: Maximum number of times a conn can be
+            shared by multiple coroutines (clients)
+        :param int max_inflight: Maximum number of unprocessed requests at any
+            one time on the connection
+
+        :returns: :py:class:`GremlinServer`
+        """
+
+        host = cls(url, loop, **config)
+        await host.initialize()
+        return host
diff --git a/goblin/exception.py b/goblin/exception.py
index 642b7df97cfea30cee2854683f780661d4be43cc..203102b9e8db4217ac6903fc63ccb9492402b011 100644
--- a/goblin/exception.py
+++ b/goblin/exception.py
@@ -15,6 +15,11 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
 
+
+class ClientError(Exception):
+    pass
+
+
 class MappingError(Exception):
     pass
 
@@ -27,5 +32,13 @@ class ElementError(Exception):
     pass
 
 
+class ConfigurationError(Exception):
+    pass
+
+
 class GremlinServerError(Exception):
     pass
+
+
+class ResponseTimeoutError(Exception):
+    pass
diff --git a/goblin/session.py b/goblin/session.py
index 744aa80c80f576d6300ac3e9dd02568b6af13bb7..6c32ce583f7484d8415bda49273b9a1f75beb9aa 100644
--- a/goblin/session.py
+++ b/goblin/session.py
@@ -22,14 +22,77 @@ import collections
 import logging
 import weakref
 
-from goblin import exception, mapper, traversal
+from goblin import cardinality, exception, mapper
 from goblin.driver import connection, graph
 from goblin.element import GenericVertex
 
+from gremlin_python.driver.remote_connection import RemoteStrategy
+from gremlin_python.process.traversal import Cardinality, Traverser
+
+
 
 logger = logging.getLogger(__name__)
 
 
+def bindprop(element_class, ogm_name, val, *, binding=None):
+    """
+    Helper function for binding ogm properties/values to corresponding db
+    properties/values for traversals.
+
+    :param goblin.element.Element element_class: User defined element class
+    :param str ogm_name: Name of property as defined in the ogm
+    :param val: The property value
+    :param str binding: The binding for val (optional)
+
+    :returns: tuple object ('db_property_name', ('binding(if passed)', val))
+    """
+    db_name = getattr(element_class, ogm_name, ogm_name)
+    _, data_type = element_class.__mapping__.ogm_properties[ogm_name]
+    val = data_type.to_db(val)
+    if binding:
+        val = (binding, val)
+    return db_name, val
+
+
+class TraversalResponse:
+    """Asynchronous iterator that encapsulates a traversal response queue"""
+    def __init__(self, response_queue, request_id):
+        self._queue = response_queue
+        self._request_id = request_id
+        self._done = False
+
+    @property
+    def request_id(self):
+        return self._request_id
+
+    async def __aiter__(self):
+        return self
+
+    async def __anext__(self):
+        if self._done:
+            return
+        msg = await self.fetch_data()
+        if msg:
+            return msg
+        else:
+            self._done = True
+            raise StopAsyncIteration
+
+    async def fetch_data(self):
+        return await self._queue.get()
+
+
+class GoblinAsyncRemoteStrategy(RemoteStrategy):
+
+    async def apply(self, traversal):
+
+        if traversal.traversers is None:
+            resp = await self.remote_connection.submit(
+                gremlin=traversal.bytecode, processor='', op='eval')
+            traversal.traversers = resp
+            traversal.side_effects = None
+
+
 class Session(connection.AbstractConnection):
     """
     Provides the main API for interacting with the database. Does not
@@ -41,20 +104,24 @@ class Session(connection.AbstractConnection):
     :param bool use_session: Support for Gremlin Server session. Not implemented
     """
 
-    def __init__(self, app, conn, get_hashable_id, *, use_session=False,
-                 aliases=None):
+    def __init__(self, app, conn, get_hashable_id, transactions, *,
+                 use_session=False):
         self._app = app
         self._conn = conn
         self._loop = self._app._loop
         self._use_session = False
-        self._aliases = aliases or dict()
         self._pending = collections.deque()
         self._current = weakref.WeakValueDictionary()
         self._get_hashable_id = get_hashable_id
-        remote_graph = graph.AsyncRemoteGraph(
-            self._app.translator, self,
-            graph_traversal=traversal.GoblinTraversal)
-        self._traversal_factory = traversal.TraversalFactory(remote_graph)
+        self._graph = graph.AsyncGraph()
+
+    @property
+    def graph(self):
+        return self._graph
+
+    @property
+    def message_serializer(self):
+        return self.conn.message_serializer
 
     @property
     def app(self):
@@ -64,10 +131,6 @@ class Session(connection.AbstractConnection):
     def conn(self):
         return self._conn
 
-    @property
-    def traversal_factory(self):
-        return self._traversal_factory
-
     @property
     def current(self):
         return self._current
@@ -76,14 +139,12 @@ class Session(connection.AbstractConnection):
         return self
 
     async def __aexit__(self, exc_type, exc, tb):
-        await self.close()
+        self.close()
 
-    async def close(self):
+    def close(self):
         """
-        Close the underlying db connection and disconnect session from Goblin
-        application.
         """
-        await self.conn.close()
+        self._conn = None
         self._app = None
 
     # Traversal API
@@ -96,44 +157,59 @@ class Session(connection.AbstractConnection):
             :py:class:`goblin.gremlin_python.process.GraphTraversalSource`
             object
         """
-        return self.traversal_factory.traversal()
+        return self.traversal()
 
-    def traversal(self, element_class):
+    @property
+    def _g(self):
+        """
+        Traversal source for internal use. Uses undelying conn. Doesn't
+        trigger complex deserailization.
         """
-        Get a traversal spawned from an element class.
+        return self.graph.traversal(
+            graph_traversal=graph.AsyncGraphTraversal,
+            remote_strategy=GoblinAsyncRemoteStrategy).withRemote(self.conn)
 
-        :param :goblin.element.Element element_class: Element class
-            used to spawn traversal.
+    def traversal(self, element_class=None):
+        """
+        Generate a traversal using a user defined element class as a
+        starting point.
 
-        :returns: :py:class:`GoblinTraversal<goblin.traversal.GoblinTraversal>`
-            object
+        :param goblin.element.Element element_class: An optional element
+            class that will dictate the element type (vertex/edge) as well as
+            the label for the traversal source
+
+        :returns: :py:class:`AsyncGraphTraversal`
         """
-        return self.traversal_factory.traversal(element_class=element_class)
+        traversal = self.graph.traversal(
+            graph_traversal=graph.AsyncGraphTraversal,
+            remote_strategy=GoblinAsyncRemoteStrategy).withRemote(self)
+        if element_class:
+            label = element_class.__mapping__.label
+            if element_class.__type__ == 'vertex':
+                traversal = traversal.V()
+            if element_class.__type__ == 'edge':
+                traversal = traversal.E()
+            traversal = traversal.hasLabel(label)
+        return traversal
 
     async def submit(self,
-                    gremlin,
-                    *,
-                    bindings=None,
-                    lang='gremlin-groovy'):
+                     **args):
         """
         Submit a query to the Gremiln Server.
 
         :param str gremlin: Gremlin script to submit to server.
         :param dict bindings: A mapping of bindings for Gremlin script.
-        :param str lang: Language of scripts submitted to the server.
-            "gremlin-groovy" by default
 
         :returns:
             :py:class:`TraversalResponse<goblin.traversal.TraversalResponse>`
             object
         """
         await self.flush()
-        async_iter = await self.conn.submit(
-            gremlin, bindings=bindings, lang=lang, aliases=self._aliases)
+        async_iter = await self.conn.submit(**args)
         response_queue = asyncio.Queue(loop=self._loop)
         self._loop.create_task(
             self._receive(async_iter, response_queue))
-        return traversal.TraversalResponse(response_queue)
+        return TraversalResponse(response_queue, async_iter.request_id)
 
     async def _receive(self, async_iter, response_queue):
         async for result in async_iter:
@@ -155,15 +231,16 @@ class Session(connection.AbstractConnection):
                         current.source = GenericVertex()
                         current.target = GenericVertex()
                 element = current.__mapping__.mapper_func(result, current)
-                return element
+                return Traverser(element, 1)
             else:
                 for key in result:
                     result[key] = self._deserialize_result(result[key])
-                return result
+                return Traverser(result, 1)
         elif isinstance(result, list):
-            return [self._deserialize_result(item) for item in result]
+            result = [self._deserialize_result(item) for item in result]
+            return Traverser(result, 1)
         else:
-            return result
+            return Traverser(result, 1)
 
     # Creation API
     def add(self, *elements):
@@ -190,7 +267,7 @@ class Session(connection.AbstractConnection):
 
         :param goblin.element.Vertex vertex: Vertex to be removed
         """
-        traversal = self.traversal_factory.remove_vertex(vertex)
+        traversal = self._g.V(vertex.id).drop()
         result = await self._simple_traversal(traversal, vertex)
         hashable_id = self._get_hashable_id(vertex.id)
         vertex = self.current.pop(hashable_id)
@@ -203,7 +280,7 @@ class Session(connection.AbstractConnection):
 
         :param goblin.element.Edge edge: Element to be removed
         """
-        traversal = self.traversal_factory.remove_edge(edge)
+        traversal = self._g.E(edge.id).drop()
         result = await self._simple_traversal(traversal, edge)
         hashable_id = self._get_hashable_id(edge.id)
         edge = self.current.pop(hashable_id)
@@ -270,8 +347,7 @@ class Session(connection.AbstractConnection):
 
         :returns: :py:class:`Vertex<goblin.element.Vertex>` | None
         """
-        return await self.traversal_factory.get_vertex_by_id(
-            vertex).one_or_none()
+        return await self.g.V(vertex.id).oneOrNone()
 
     async def get_edge(self, edge):
         """
@@ -281,8 +357,7 @@ class Session(connection.AbstractConnection):
 
         :returns: :py:class:`Edge<goblin.element.Edge>` | None
         """
-        return await self.traversal_factory.get_edge_by_id(
-            edge).one_or_none()
+        return await self.g.E(edge.id).oneOrNone()
 
     async def update_vertex(self, vertex):
         """
@@ -294,7 +369,7 @@ class Session(connection.AbstractConnection):
         """
         props = mapper.map_props_to_db(vertex, vertex.__mapping__)
         # vert_props = mapper.map_vert_props_to_db
-        traversal = self.g.V(vertex.id)
+        traversal = self._g.V(vertex.id)
         return await self._update_vertex_properties(vertex, traversal, props)
 
     async def update_edge(self, edge):
@@ -306,7 +381,7 @@ class Session(connection.AbstractConnection):
         :returns: :py:class:`Edge<goblin.element.Edge>` object
         """
         props = mapper.map_props_to_db(edge, edge.__mapping__)
-        traversal = self.g.E(edge.id)
+        traversal = self._g.E(edge.id)
         return await self._update_edge_properties(edge, traversal, props)
 
     # Transaction support
@@ -314,13 +389,10 @@ class Session(connection.AbstractConnection):
         """Not implemented"""
         raise NotImplementedError
 
-    def _wrap_in_tx(self):
-        raise NotImplementedError
-
     async def commit(self):
         """Not implemented"""
         await self.flush()
-        if self.engine._features['transactions'] and self._use_session():
+        if self.transactions and self._use_session():
             await self.tx()
         raise NotImplementedError
 
@@ -329,13 +401,10 @@ class Session(connection.AbstractConnection):
 
     # *metodos especiales privados for creation API
     async def _simple_traversal(self, traversal, element):
-        stream = await self.conn.submit(
-            repr(traversal), bindings=traversal.bindings,
-            aliases=self._aliases)
-        msg = await stream.fetch_data()
+        msg = await traversal.oneOrNone()
         if msg:
             msg = element.__mapping__.mapper_func(msg, element)
-            return msg
+        return msg
 
     async def _save_element(self,
                             elem,
@@ -352,62 +421,57 @@ class Session(connection.AbstractConnection):
             result = await create_func(elem)
         return result
 
-    async def _add_vertex(self, elem):
+    async def _add_vertex(self, vertex):
         """Convenience function for generating crud traversals."""
-        props = mapper.map_props_to_db(elem, elem.__mapping__)
-        traversal = self.g.addV(elem.__mapping__.label)
-        traversal, _, metaprops = self.traversal_factory.add_properties(
-            traversal, props)
-        result = await self._simple_traversal(traversal, elem)
+        props = mapper.map_props_to_db(vertex, vertex.__mapping__)
+        traversal = self._g.addV(vertex.__mapping__.label)
+        traversal, _, metaprops = self._add_properties(traversal, props)
+        result = await self._simple_traversal(traversal, vertex)
         if metaprops:
             await self._add_metaprops(result, metaprops)
-            traversal = self.traversal_factory.get_vertex_by_id(elem)
-            result = await self._simple_traversal(traversal, elem)
+            traversal = self._g.V(vertex.id)
+            result = await self._simple_traversal(traversal, vertex)
         return result
 
-    async def _add_edge(self, elem):
+    async def _add_edge(self, edge):
         """Convenience function for generating crud traversals."""
-        props = mapper.map_props_to_db(elem, elem.__mapping__)
-        traversal = self.g.V(elem.source.id)
-        traversal = traversal.addE(elem.__mapping__._label)
+        props = mapper.map_props_to_db(edge, edge.__mapping__)
+        traversal = self._g.V(edge.source.id)
+        traversal = traversal.addE(edge.__mapping__._label)
         traversal = traversal.to(
-            self.g.V(elem.target.id))
-        traversal, _, _ = self.traversal_factory.add_properties(
+            self._g.V(edge.target.id))
+        traversal, _, _ = self._add_properties(
             traversal, props)
-        return await self._simple_traversal(traversal, elem)
+        return await self._simple_traversal(traversal, edge)
 
     async def _check_vertex(self, vertex):
         """Used to check for existence, does not update session vertex"""
-        traversal = self.g.V(vertex.id)
-        stream = await self.conn.submit(repr(traversal), aliases=self._aliases)
-        return await stream.fetch_data()
+        msg = await self._g.V(vertex.id).oneOrNone()
+        return msg
 
     async def _check_edge(self, edge):
         """Used to check for existence, does not update session edge"""
-        traversal = self.g.E(edge.id)
-        stream = await self.conn.submit(repr(traversal), aliases=self._aliases)
-        return await stream.fetch_data()
+        msg = await self._g.E(edge.id).oneOrNone()
+        return msg
 
     async def _update_vertex_properties(self, vertex, traversal, props):
-        traversal, removals, metaprops = self.traversal_factory.add_properties(
-            traversal, props)
+        traversal, removals, metaprops = self._add_properties(traversal, props)
         for k in removals:
-            await self.g.V(vertex.id).properties(k).drop().one_or_none()
+            await self._g.V(vertex.id).properties(k).drop().oneOrNone()
         result = await self._simple_traversal(traversal, vertex)
         if metaprops:
             removals = await self._add_metaprops(result, metaprops)
             for db_name, key, value in removals:
-                await self.g.V(vertex.id).properties(
-                    db_name).has(key, value).drop().one_or_none()
-            traversal = self.traversal_factory.get_vertex_by_id(vertex)
+                await self._g.V(vertex.id).properties(
+                    db_name).has(key, value).drop().oneOrNone()
+            traversal = self._g.V(vertex.id)
             result = await self._simple_traversal(traversal, vertex)
         return result
 
     async def _update_edge_properties(self, edge, traversal, props):
-        traversal, removals, _ = self.traversal_factory.add_properties(
-            traversal, props)
+        traversal, removals, _ = self._add_properties(traversal, props)
         for k in removals:
-            await self.g.E(edge.id).properties(k).drop().one_or_none()
+            await self._g.E(edge.id).properties(k).drop().oneOrNone()
         return await self._simple_traversal(traversal, edge)
 
     async def _add_metaprops(self, result, metaprops):
@@ -416,12 +480,35 @@ class Session(connection.AbstractConnection):
             db_name, (binding, value), metaprops = metaprop
             for key, val in metaprops.items():
                 if val:
-                    traversal = self.g.V(result.id).properties(
+                    traversal = self._g.V(result.id).properties(
                         db_name).hasValue(value).property(key, val)
-                    stream = await self.conn.submit(
-                        repr(traversal), bindings=traversal.bindings,
-                        aliases=self._aliases)
-                    await stream.fetch_data()
+                    await traversal.oneOrNone()
                 else:
                     potential_removals.append((db_name, key, value))
         return potential_removals
+
+    def _add_properties(self, traversal, props):
+        binding = 0
+        potential_removals = []
+        potential_metaprops = []
+        for card, db_name, val, metaprops in props:
+            if val:
+                key = ('k' + str(binding), db_name)
+                val = ('v' + str(binding), val)
+                if card:
+                    # Maybe use a dict here as a translator
+                    if card == cardinality.Cardinality.list:
+                        card = Cardinality.list
+                    elif card == cardinality.Cardinality.set:
+                        card = Cardinality.set
+                    else:
+                        card = Cardinality.single
+                    traversal = traversal.property(card, key, val)
+                else:
+                    traversal = traversal.property(key, val)
+                binding += 1
+                if metaprops:
+                    potential_metaprops.append((db_name, val, metaprops))
+            else:
+                potential_removals.append(db_name)
+        return traversal, potential_removals, potential_metaprops
diff --git a/goblin/traversal.py b/goblin/traversal.py
deleted file mode 100644
index f516a65c55276e12163257628e20ba97a8d39c62..0000000000000000000000000000000000000000
--- a/goblin/traversal.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# Copyright 2016 ZEROFAIL
-#
-# This file is part of Goblin.
-#
-# Goblin is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Goblin is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
-
-"""Query API and helpers"""
-
-import asyncio
-import functools
-import logging
-
-from goblin import cardinality, element, mapper
-from goblin.driver import connection, graph
-from gremlin_python import process
-
-
-logger = logging.getLogger(__name__)
-
-
-def bindprop(element_class, ogm_name, val, *, binding=None):
-    """
-    Helper function for binding ogm properties/values to corresponding db
-    properties/values for traversals.
-
-    :param goblin.element.Element element_class: User defined element class
-    :param str ogm_name: Name of property as defined in the ogm
-    :param val: The property value
-    :param str binding: The binding for val (optional)
-
-    :returns: tuple object ('db_property_name', ('binding(if passed)', val))
-    """
-    db_name = getattr(element_class, ogm_name, ogm_name)
-    _, data_type = element_class.__mapping__.ogm_properties[ogm_name]
-    val = data_type.to_db(val)
-    if binding:
-        val = (binding, val)
-    return db_name, val
-
-
-class TraversalResponse:
-    """Asynchronous iterator that encapsulates a traversal response queue"""
-    def __init__(self, response_queue):
-        self._queue = response_queue
-        self._done = False
-
-    async def __aiter__(self):
-        return self
-
-    async def __anext__(self):
-        if self._done:
-            return
-        msg = await self._queue.get()
-        if msg:
-            return msg
-        else:
-            self._done = True
-            raise StopAsyncIteration
-
-
-# This is all until we figure out GLV integration...
-class GoblinTraversal(graph.AsyncGraphTraversal):
-
-    async def all(self):
-        """
-        Get all results from traversal.
-
-        :returns: :py:class:`TraversalResponse` object
-        """
-        return await self.next()
-
-    async def one_or_none(self):
-        """
-        Get one or zero results from a traveral.
-
-        :returns: :py:class:`Element<goblin.element.Element>` object
-        """
-        result = None
-        async for msg in await self.next():
-            result = msg
-        return result
-
-
-class TraversalFactory:
-    """Helper that wraps a AsyncRemoteGraph"""
-    def __init__(self, graph):
-        self._graph = graph
-
-    @property
-    def graph(self):
-        return self._graph
-
-    def traversal(self, *, element_class=None):
-        """
-        Generate a traversal using a user defined element class as a
-        starting point.
-
-        :param goblin.element.Element element_class: An optional element
-            class that will dictate the element type (vertex/edge) as well as
-            the label for the traversal source
-
-        :returns: :py:class:`GoblinTraversal`
-        """
-        traversal = self.graph.traversal()
-        if element_class:
-            label = element_class.__mapping__.label
-            traversal = self._graph.traversal()
-            if element_class.__type__ == 'vertex':
-                traversal = traversal.V()
-            if element_class.__type__ == 'edge':
-                traversal = traversal.E()
-            traversal = traversal.hasLabel(label)
-        return traversal
-
-    def remove_vertex(self, elem):
-        """Convenience function for generating crud traversals."""
-        return self.traversal().V(elem.id).drop()
-
-    def remove_edge(self, elem):
-        """Convenience function for generating crud traversals."""
-        return self.traversal().E(elem.id).drop()
-
-    def get_vertex_by_id(self, elem):
-        """Convenience function for generating crud traversals."""
-        return self.traversal().V(elem.id)
-
-    def get_edge_by_id(self, elem):
-        """Convenience function for generating crud traversals."""
-        return self.traversal().E(elem.id)
-
-    def add_properties(self, traversal, props):
-        binding = 0
-        potential_removals = []
-        potential_metaprops = []
-        for card, db_name, val, metaprops in props:
-            if val:
-                key = ('k' + str(binding), db_name)
-                val = ('v' + str(binding), val)
-                if card:
-                    # Maybe use a dict here as a translator
-                    if card == cardinality.Cardinality.list:
-                        card = process.Cardinality.list
-                    elif card == cardinality.Cardinality.set:
-                        card = process.Cardinality.set
-                    else:
-                        card = process.Cardinality.single
-                    traversal = traversal.property(card, key, val)
-                else:
-                    traversal = traversal.property(key, val)
-                binding += 1
-                if metaprops:
-                    potential_metaprops.append((db_name, val, metaprops))
-            else:
-                potential_removals.append(db_name)
-        return traversal, potential_removals, potential_metaprops
diff --git a/gremlin_python/__init__.py b/gremlin_python/__init__.py
index 518d8e008b256688b90e792343fd0cf1e7bb26e2..7626550738f12ccd5e18b8c06a8d68aaab882066 100644
--- a/gremlin_python/__init__.py
+++ b/gremlin_python/__init__.py
@@ -16,6 +16,5 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-from . import statics
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
diff --git a/gremlin_python/driver/__init__.py b/gremlin_python/driver/__init__.py
index 7e1c0f1a812f96353a3b4b12daf4db66b2ce4a51..7626550738f12ccd5e18b8c06a8d68aaab882066 100644
--- a/gremlin_python/driver/__init__.py
+++ b/gremlin_python/driver/__init__.py
@@ -16,7 +16,5 @@ 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
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
diff --git a/gremlin_python/driver/remote_connection.py b/gremlin_python/driver/remote_connection.py
index 1651b92ad7809e01fd6fecf06371edacd3a7b937..452fcaf5f6e5c6915bcdeafaa28851911002a46e 100644
--- a/gremlin_python/driver/remote_connection.py
+++ b/gremlin_python/driver/remote_connection.py
@@ -16,16 +16,64 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-from abc import abstractmethod
+import abc
+import six
+
+from ..process.traversal import Traversal
+from ..process.traversal import TraversalStrategy
+from ..process.traversal import TraversalSideEffects
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 
 
+@six.add_metaclass(abc.ABCMeta)
 class RemoteConnection(object):
-    def __init__(self, url):
-        self.url = url
+    def __init__(self, url, traversal_source):
+        self._url = url
+        self._traversal_source = traversal_source
+
+    @property
+    def url(self):
+        return self._url
+
+    @property
+    def traversal_source(self):
+        return self._traversal_source
+
+    @abc.abstractmethod
+    def submit(self, bytecode):
+        print("sending " + bytecode + " to GremlinServer...")
+        return RemoteTraversal(iter([]), TraversalSideEffects())
+
+    def __repr__(self):
+        return "remoteconnection[" + self._url + "," + self._traversal_source + "]"
+
+
+class RemoteTraversal(Traversal):
+    def __init__(self, traversers, side_effects):
+        Traversal.__init__(self, None, None, None)
+        self.traversers = traversers
+        self.side_effects = side_effects
+
+
+class RemoteTraversalSideEffects(TraversalSideEffects):
+    def __init__(self, keys_lambda, value_lambda):
+        self.keys_lambda = keys_lambda
+        self.value_lambda = value_lambda
+
+    def keys(self):
+        return self.keys_lambda()
+
+    def get(self, key):
+        return self.value_lambda(key)
+
+
+class RemoteStrategy(TraversalStrategy):
+    def __init__(self, remote_connection):
+        self.remote_connection = remote_connection
 
-    @abstractmethod
-    def submit(self, target_language, script, bindings):
-        print "sending " + script + " to GremlinServer..."
-        return iter([])
+    def apply(self, traversal):
+        if traversal.traversers is None:
+            remote_traversal = self.remote_connection.submit(traversal.bytecode)
+            # traversal.side_effects = remote_traversal.side_effects
+            traversal.traversers = remote_traversal#.traversers
diff --git a/gremlin_python/process/__init__.py b/gremlin_python/process/__init__.py
index e93356e95724d6eaab0892313c6744643294229b..7626550738f12ccd5e18b8c06a8d68aaab882066 100644
--- a/gremlin_python/process/__init__.py
+++ b/gremlin_python/process/__init__.py
@@ -17,22 +17,4 @@ 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
-
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
diff --git a/gremlin_python/process/graph_traversal.py b/gremlin_python/process/graph_traversal.py
index 37f9deb66a251c8d75ab2e316501ff6755f184c9..9010f603076efc69e6ad08950fa7fbda890ed9b6 100644
--- a/gremlin_python/process/graph_traversal.py
+++ b/gremlin_python/process/graph_traversal.py
@@ -16,933 +16,402 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-from .traversal import RawExpression
+import sys
 from .traversal import Traversal
+from .traversal import TraversalStrategies
 from .traversal import Bytecode
-from gremlin_python import statics
+from ..driver.remote_connection import RemoteStrategy
+from .. import statics
 
 class GraphTraversalSource(object):
-  def __init__(self, graph, traversal_strategies, graph_traversal=None, bytecode=Bytecode()):
+  def __init__(self, graph, traversal_strategies, bytecode=None,
+               graph_traversal=None, remote_strategy=None):
     self.graph = graph
     self.traversal_strategies = traversal_strategies
     if graph_traversal is None:
         graph_traversal = GraphTraversal
     self.graph_traversal = graph_traversal
+    if remote_strategy is None:
+        remote_strategy = RemoteStrategy
+    self.remote_strategy = remote_strategy
+    if bytecode is None:
+      bytecode = Bytecode()
     self.bytecode = bytecode
   def __repr__(self):
     return "graphtraversalsource[" + str(self.graph) + "]"
-  def E(self, *args):
-    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):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return traversal
-  def V(self, *args):
-    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):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return traversal
-  def addV(self, *args):
-    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):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return traversal
-  def inject(self, *args):
-    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):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return traversal
   def withBulk(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withBulk", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
   def withComputer(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withComputer", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
   def withPath(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withPath", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
   def withSack(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withSack", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
   def withSideEffect(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withSideEffect", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
   def withStrategies(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withStrategies", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return source
-  def withTranslator(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
-    source.bytecode.add_source("withTranslator", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
   def withoutStrategies(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
     source.bytecode.add_source("withoutStrategies", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return source
+  def withRemote(self, remote_connection):
+    source = GraphTraversalSource(
+        self.graph, TraversalStrategies(self.traversal_strategies),
+        Bytecode(self.bytecode), self.graph_traversal, self.remote_strategy)
+    source.traversal_strategies.add_strategies([self.remote_strategy(remote_connection)])
+    return source
+  def withBindings(self, bindings):
+    return self
+  def E(self, *args):
+    traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    traversal.bytecode.add_step("E", *args)
+    return traversal
+  def V(self, *args):
+    traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    traversal.bytecode.add_step("V", *args)
+    return traversal
+  def addV(self, *args):
+    traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    traversal.bytecode.add_step("addV", *args)
+    return traversal
+  def inject(self, *args):
+    traversal = self.graph_traversal(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    traversal.bytecode.add_step("inject", *args)
+    return traversal
 
 
 class GraphTraversal(Traversal):
   def __init__(self, graph, traversal_strategies, bytecode):
     Traversal.__init__(self, graph, traversal_strategies, bytecode)
+  def __getitem__(self, index):
+    if isinstance(index, int):
+        return self.range(index, index + 1)
+    elif isinstance(index, slice):
+        return self.range(0 if index.start is None else index.start, sys.maxint if index.stop is None else index.stop)
+    else:
+        raise TypeError("Index must be int or slice")
+  def __getattr__(self, key):
+    return self.values(key)
   def V(self, *args):
     self.bytecode.add_step("V", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _and(self, *args):
-    self.bytecode.add_step("_and", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _as(self, *args):
-    self.bytecode.add_step("_as", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _from(self, *args):
-    self.bytecode.add_step("_from", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _in(self, *args):
-    self.bytecode.add_step("_in", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _is(self, *args):
-    self.bytecode.add_step("_is", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _not(self, *args):
-    self.bytecode.add_step("_not", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def _or(self, *args):
-    self.bytecode.add_step("_or", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def addE(self, *args):
     self.bytecode.add_step("addE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def addInE(self, *args):
     self.bytecode.add_step("addInE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def addOutE(self, *args):
     self.bytecode.add_step("addOutE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def addV(self, *args):
     self.bytecode.add_step("addV", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def aggregate(self, *args):
     self.bytecode.add_step("aggregate", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def asAdmin(self, *args):
-    self.bytecode.add_step("asAdmin", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
+    return self
+  def and_(self, *args):
+    self.bytecode.add_step("and", *args)
+    return self
+  def as_(self, *args):
+    self.bytecode.add_step("as", *args)
     return self
   def barrier(self, *args):
     self.bytecode.add_step("barrier", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def both(self, *args):
     self.bytecode.add_step("both", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def bothE(self, *args):
     self.bytecode.add_step("bothE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def bothV(self, *args):
     self.bytecode.add_step("bothV", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def branch(self, *args):
     self.bytecode.add_step("branch", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def by(self, *args):
     self.bytecode.add_step("by", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def cap(self, *args):
     self.bytecode.add_step("cap", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def choose(self, *args):
     self.bytecode.add_step("choose", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def coalesce(self, *args):
     self.bytecode.add_step("coalesce", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def coin(self, *args):
     self.bytecode.add_step("coin", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def constant(self, *args):
     self.bytecode.add_step("constant", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def count(self, *args):
     self.bytecode.add_step("count", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def cyclicPath(self, *args):
     self.bytecode.add_step("cyclicPath", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def dedup(self, *args):
     self.bytecode.add_step("dedup", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def drop(self, *args):
     self.bytecode.add_step("drop", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def emit(self, *args):
     self.bytecode.add_step("emit", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def filter(self, *args):
     self.bytecode.add_step("filter", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def flatMap(self, *args):
     self.bytecode.add_step("flatMap", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def fold(self, *args):
     self.bytecode.add_step("fold", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
+    return self
+  def from_(self, *args):
+    self.bytecode.add_step("from", *args)
     return self
   def group(self, *args):
     self.bytecode.add_step("group", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def groupCount(self, *args):
     self.bytecode.add_step("groupCount", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def groupV3d0(self, *args):
     self.bytecode.add_step("groupV3d0", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def has(self, *args):
     self.bytecode.add_step("has", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def hasId(self, *args):
     self.bytecode.add_step("hasId", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def hasKey(self, *args):
     self.bytecode.add_step("hasKey", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def hasLabel(self, *args):
     self.bytecode.add_step("hasLabel", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def hasNot(self, *args):
     self.bytecode.add_step("hasNot", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def hasValue(self, *args):
     self.bytecode.add_step("hasValue", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def id(self, *args):
     self.bytecode.add_step("id", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def identity(self, *args):
     self.bytecode.add_step("identity", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def inE(self, *args):
     self.bytecode.add_step("inE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def inV(self, *args):
     self.bytecode.add_step("inV", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
+    return self
+  def in_(self, *args):
+    self.bytecode.add_step("in", *args)
     return self
   def inject(self, *args):
     self.bytecode.add_step("inject", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
-    return self
-  def iterate(self, *args):
-    self.bytecode.add_step("iterate", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
+    return self
+  def is_(self, *args):
+    self.bytecode.add_step("is", *args)
     return self
   def key(self, *args):
     self.bytecode.add_step("key", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def label(self, *args):
     self.bytecode.add_step("label", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def limit(self, *args):
     self.bytecode.add_step("limit", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def local(self, *args):
     self.bytecode.add_step("local", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def loops(self, *args):
     self.bytecode.add_step("loops", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def map(self, *args):
     self.bytecode.add_step("map", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def mapKeys(self, *args):
     self.bytecode.add_step("mapKeys", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def mapValues(self, *args):
     self.bytecode.add_step("mapValues", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def match(self, *args):
     self.bytecode.add_step("match", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def max(self, *args):
     self.bytecode.add_step("max", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def mean(self, *args):
     self.bytecode.add_step("mean", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def min(self, *args):
     self.bytecode.add_step("min", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
+    return self
+  def not_(self, *args):
+    self.bytecode.add_step("not", *args)
     return self
   def option(self, *args):
     self.bytecode.add_step("option", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def optional(self, *args):
     self.bytecode.add_step("optional", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
+    return self
+  def or_(self, *args):
+    self.bytecode.add_step("or", *args)
     return self
   def order(self, *args):
     self.bytecode.add_step("order", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def otherV(self, *args):
     self.bytecode.add_step("otherV", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def out(self, *args):
     self.bytecode.add_step("out", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def outE(self, *args):
     self.bytecode.add_step("outE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def outV(self, *args):
     self.bytecode.add_step("outV", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def pageRank(self, *args):
     self.bytecode.add_step("pageRank", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def path(self, *args):
     self.bytecode.add_step("path", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def peerPressure(self, *args):
     self.bytecode.add_step("peerPressure", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def profile(self, *args):
     self.bytecode.add_step("profile", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def program(self, *args):
     self.bytecode.add_step("program", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def project(self, *args):
     self.bytecode.add_step("project", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def properties(self, *args):
     self.bytecode.add_step("properties", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def property(self, *args):
     self.bytecode.add_step("property", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def propertyMap(self, *args):
     self.bytecode.add_step("propertyMap", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def range(self, *args):
     self.bytecode.add_step("range", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def repeat(self, *args):
     self.bytecode.add_step("repeat", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def sack(self, *args):
     self.bytecode.add_step("sack", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def sample(self, *args):
     self.bytecode.add_step("sample", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def select(self, *args):
     self.bytecode.add_step("select", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def sideEffect(self, *args):
     self.bytecode.add_step("sideEffect", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def simplePath(self, *args):
     self.bytecode.add_step("simplePath", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def store(self, *args):
     self.bytecode.add_step("store", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def subgraph(self, *args):
     self.bytecode.add_step("subgraph", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def sum(self, *args):
     self.bytecode.add_step("sum", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def tail(self, *args):
     self.bytecode.add_step("tail", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def timeLimit(self, *args):
     self.bytecode.add_step("timeLimit", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def times(self, *args):
     self.bytecode.add_step("times", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def to(self, *args):
     self.bytecode.add_step("to", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def toE(self, *args):
     self.bytecode.add_step("toE", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def toV(self, *args):
     self.bytecode.add_step("toV", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def tree(self, *args):
     self.bytecode.add_step("tree", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def unfold(self, *args):
     self.bytecode.add_step("unfold", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def union(self, *args):
     self.bytecode.add_step("union", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def until(self, *args):
     self.bytecode.add_step("until", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def value(self, *args):
     self.bytecode.add_step("value", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def valueMap(self, *args):
     self.bytecode.add_step("valueMap", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def values(self, *args):
     self.bytecode.add_step("values", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
   def where(self, *args):
     self.bytecode.add_step("where", *args)
-    for arg in args:
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-        self.bindings[arg[0]] = arg[1]
-      elif isinstance(arg, RawExpression):
-        self.bindings.update(arg.bindings)
     return self
 
 
@@ -954,24 +423,6 @@ class __(object):
   def __(*args):
     return GraphTraversal(None, None, Bytecode()).__(*args)
   @staticmethod
-  def _and(*args):
-    return GraphTraversal(None, None, Bytecode())._and(*args)
-  @staticmethod
-  def _as(*args):
-    return GraphTraversal(None, None, Bytecode())._as(*args)
-  @staticmethod
-  def _in(*args):
-    return GraphTraversal(None, None, Bytecode())._in(*args)
-  @staticmethod
-  def _is(*args):
-    return GraphTraversal(None, None, Bytecode())._is(*args)
-  @staticmethod
-  def _not(*args):
-    return GraphTraversal(None, None, Bytecode())._not(*args)
-  @staticmethod
-  def _or(*args):
-    return GraphTraversal(None, None, Bytecode())._or(*args)
-  @staticmethod
   def addE(*args):
     return GraphTraversal(None, None, Bytecode()).addE(*args)
   @staticmethod
@@ -987,6 +438,12 @@ class __(object):
   def aggregate(*args):
     return GraphTraversal(None, None, Bytecode()).aggregate(*args)
   @staticmethod
+  def and_(*args):
+    return GraphTraversal(None, None, Bytecode()).and_(*args)
+  @staticmethod
+  def as_(*args):
+    return GraphTraversal(None, None, Bytecode()).as_(*args)
+  @staticmethod
   def barrier(*args):
     return GraphTraversal(None, None, Bytecode()).barrier(*args)
   @staticmethod
@@ -1080,9 +537,15 @@ class __(object):
   def inV(*args):
     return GraphTraversal(None, None, Bytecode()).inV(*args)
   @staticmethod
+  def in_(*args):
+    return GraphTraversal(None, None, Bytecode()).in_(*args)
+  @staticmethod
   def inject(*args):
     return GraphTraversal(None, None, Bytecode()).inject(*args)
   @staticmethod
+  def is_(*args):
+    return GraphTraversal(None, None, Bytecode()).is_(*args)
+  @staticmethod
   def key(*args):
     return GraphTraversal(None, None, Bytecode()).key(*args)
   @staticmethod
@@ -1119,9 +582,15 @@ class __(object):
   def min(*args):
     return GraphTraversal(None, None, Bytecode()).min(*args)
   @staticmethod
+  def not_(*args):
+    return GraphTraversal(None, None, Bytecode()).not_(*args)
+  @staticmethod
   def optional(*args):
     return GraphTraversal(None, None, Bytecode()).optional(*args)
   @staticmethod
+  def or_(*args):
+    return GraphTraversal(None, None, Bytecode()).or_(*args)
+  @staticmethod
   def order(*args):
     return GraphTraversal(None, None, Bytecode()).order(*args)
   @staticmethod
@@ -1233,36 +702,6 @@ def V(*args):
 
 statics.add_static('V', V)
 
-def _and(*args):
-      return __._and(*args)
-
-statics.add_static('_and', _and)
-
-def _as(*args):
-      return __._as(*args)
-
-statics.add_static('_as', _as)
-
-def _in(*args):
-      return __._in(*args)
-
-statics.add_static('_in', _in)
-
-def _is(*args):
-      return __._is(*args)
-
-statics.add_static('_is', _is)
-
-def _not(*args):
-      return __._not(*args)
-
-statics.add_static('_not', _not)
-
-def _or(*args):
-      return __._or(*args)
-
-statics.add_static('_or', _or)
-
 def addE(*args):
       return __.addE(*args)
 
@@ -1288,6 +727,16 @@ def aggregate(*args):
 
 statics.add_static('aggregate', aggregate)
 
+def and_(*args):
+      return __.and_(*args)
+
+statics.add_static('and_', and_)
+
+def as_(*args):
+      return __.as_(*args)
+
+statics.add_static('as_', as_)
+
 def barrier(*args):
       return __.barrier(*args)
 
@@ -1443,11 +892,21 @@ def inV(*args):
 
 statics.add_static('inV', inV)
 
+def in_(*args):
+      return __.in_(*args)
+
+statics.add_static('in_', in_)
+
 def inject(*args):
       return __.inject(*args)
 
 statics.add_static('inject', inject)
 
+def is_(*args):
+      return __.is_(*args)
+
+statics.add_static('is_', is_)
+
 def key(*args):
       return __.key(*args)
 
@@ -1508,11 +967,21 @@ def min(*args):
 
 statics.add_static('min', min)
 
+def not_(*args):
+      return __.not_(*args)
+
+statics.add_static('not_', not_)
+
 def optional(*args):
       return __.optional(*args)
 
 statics.add_static('optional', optional)
 
+def or_(*args):
+      return __.or_(*args)
+
+statics.add_static('or_', or_)
+
 def order(*args):
       return __.order(*args)
 
diff --git a/gremlin_python/process/jython_translator.py b/gremlin_python/process/jython_translator.py
deleted file mode 100644
index c3169f07c71a0fb0e5a35a33ef2c60bf66e7b5e6..0000000000000000000000000000000000000000
--- a/gremlin_python/process/jython_translator.py
+++ /dev/null
@@ -1,102 +0,0 @@
-'''
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you 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
-
-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.
-'''
-
-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
-
-if sys.version_info.major > 2:
-    long = int
-
-__author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
-
-
-class JythonTranslator(Translator):
-    def __init__(self, traversal_source, anonymous_traversal="__", target_language="gremlin-jython"):
-        Translator.__init__(self, traversal_source, anonymous_traversal, target_language)
-
-    def translate(self, bytecode):
-        return self.__internalTranslate(self.traversal_source, bytecode)
-
-    def __internalTranslate(self, start, bytecode):
-        traversal_script = start
-        for instruction in bytecode.source_instructions:
-            traversal_script = traversal_script + "." + SymbolHelper.toJava(
-                instruction[0]) + "(" + self.stringify(*instruction[1]) + ")"
-        for instruction in bytecode.step_instructions:
-            traversal_script = traversal_script + "." + SymbolHelper.toJava(
-                instruction[0]) + "(" + self.stringify(*instruction[1]) + ")"
-        return traversal_script
-
-    def stringOrObject(self, arg):
-        if isinstance(arg, str):
-            return "\"" + arg + "\""
-        elif isinstance(arg, long):
-            return str(arg)
-        elif isinstance(arg, Barrier):
-            return "Barrier" + "." + SymbolHelper.toJava(str(arg.name))
-        elif isinstance(arg, Column):
-            return "Column.valueOf('" + SymbolHelper.toJava(str(arg.name)) + "')"
-        elif isinstance(arg, Cardinality):
-            return "Cardinality" + "." + SymbolHelper.toJava(str(arg.name))
-        elif isinstance(arg, Enum):  # Order, Direction, Scope, T, etc.
-            return SymbolHelper.toJava(type(arg).__name__) + "." + SymbolHelper.toJava(str(arg.name))
-        elif isinstance(arg, P):
-            if arg.other is None:
-                return "P." + SymbolHelper.toJava(arg.operator) + "(" + self.stringOrObject(
-                    arg.value) + ")"
-            else:
-                return self.stringOrObject(arg.other) + "." + SymbolHelper.toJava(
-                    arg.operator) + "(" + self.stringOrObject(arg.value) + ")"
-        elif isinstance(arg, Bytecode):
-            return self.__internalTranslate(self.anonymous_traversal, arg)
-        elif callable(arg):  # lambda that produces a string that is a lambda
-            argLambdaString = arg().strip()
-            argLength = len(inspect.getargspec(eval(argLambdaString)).args)
-            if argLength == 0:
-                return "JythonZeroArgLambda(" + argLambdaString + ")"
-            elif argLength == 1:
-                return "JythonOneArgLambda(" + argLambdaString + ")"
-            elif argLength == 2:
-                return "JythonTwoArgLambda(" + argLambdaString + ")"
-            else:
-                raise
-        elif isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):  # bindings
-            return arg[0]
-        elif isinstance(arg, RawExpression):
-            return "".join(self.stringOrObject(i) for i in arg.parts)
-        else:
-            return str(arg)
-
-    def stringify(self, *args):
-        if len(args) == 0:
-            return ""
-        elif len(args) == 1:
-            return self.stringOrObject(args[0])
-        else:
-            return ", ".join(self.stringOrObject(i) for i in args)
diff --git a/gremlin_python/process/groovy_translator.py b/gremlin_python/process/translator.py
similarity index 60%
rename from gremlin_python/process/groovy_translator.py
rename to gremlin_python/process/translator.py
index 2c2d79a860da80b70bb427df6351793e06c8df45..ac1ebe73cb7e02db5cc00c07927fcbd164a85b63 100644
--- a/gremlin_python/process/groovy_translator.py
+++ b/gremlin_python/process/translator.py
@@ -17,19 +17,68 @@ specific language governing permissions and limitations
 under the License.
 '''
 
-import sys
+# Translate bytecode. Used for backwards compatiblitity
+
+from abc import abstractmethod
 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 gremlin_python.process.traversal import P, Bytecode, Binding
+
+
+class RawExpression(object):
+   def __init__(self, *args):
+      self.bindings = dict()
+      self.parts = [self._process_arg(arg) for arg in args]
+
+   def _process_arg(self, arg):
+      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
+         self.bindings[arg[0]] = arg[1]
+         return Raw(arg[0])
+      else:
+         return Raw(arg)
+
+class Raw(object):
+   def __init__(self, value):
+      self.value = value
+
+   def __str__(self):
+      return str(self.value)
+
+
+TO_JAVA_MAP = {"_global": "global", "_as": "as", "_in": "in", "_and": "and",
+               "_or": "or", "_is": "is", "_not": "not", "_from": "from",
+               "Cardinality": "VertexProperty.Cardinality", "Barrier": "SackFunctions.Barrier"}
 
-if sys.version_info.major > 2:
-    long = int
 
-__author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
+class Translator(object):
+    def __init__(self, traversal_source, anonymous_traversal, target_language):
+        self.traversal_source = traversal_source
+        self.anonymous_traversal = anonymous_traversal
+        self.target_language = target_language
+
+    @abstractmethod
+    def translate(self, bytecode):
+        return
+
+    @abstractmethod
+    def __repr__(self):
+        return "translator[" + self.traversal_source + ":" + self.target_language + "]"
+
+
+class SymbolHelper(object):
+    @staticmethod
+    def toJava(symbol):
+        if (symbol in TO_JAVA_MAP):
+            return TO_JAVA_MAP[symbol]
+        else:
+            return symbol
+
+    @staticmethod
+    def mapEnum(enum):
+        if (enum in enumMap):
+            return enumMap[enum]
+        else:
+            return enum
 
 
 class GroovyTranslator(Translator):
@@ -37,16 +86,16 @@ class GroovyTranslator(Translator):
         Translator.__init__(self, traversal_source, anonymous_traversal, target_language)
 
     def translate(self, bytecode):
-        return self.__internalTranslate(self.traversal_source, bytecode)
+        return self._internalTranslate(self.traversal_source, bytecode)
 
-    def __internalTranslate(self, start, bytecode):
+    def _internalTranslate(self, start, bytecode):
         traversal_script = start
         for instruction in bytecode.source_instructions:
             traversal_script = traversal_script + "." + SymbolHelper.toJava(
-                instruction[0]) + "(" + self.stringify(*instruction[1]) + ")"
+                instruction[0]) + "(" + self.stringify(*instruction[1:]) + ")"
         for instruction in bytecode.step_instructions:
             traversal_script = traversal_script + "." + SymbolHelper.toJava(
-                instruction[0]) + "(" + self.stringify(*instruction[1]) + ")"
+                instruction[0]) + "(" + self.stringify(*instruction[1:]) + ")"
         return traversal_script
 
     def stringOrObject(self, arg):
@@ -54,7 +103,7 @@ class GroovyTranslator(Translator):
             return "\"" + arg + "\""
         elif isinstance(arg, bool):
             return str(arg).lower()
-        elif isinstance(arg, long):
+        elif isinstance(arg, int):
             return str(arg) + "L"
         elif isinstance(arg, float):
             return str(arg) + "f"
@@ -67,8 +116,10 @@ class GroovyTranslator(Translator):
             else:
                 return self.stringOrObject(arg.other) + "." + SymbolHelper.toJava(
                     arg.operator) + "(" + self.stringOrObject(arg.value) + ")"
+        elif isinstance(arg, Binding):
+            return arg.key
         elif isinstance(arg, Bytecode):
-            return self.__internalTranslate(self.anonymous_traversal, arg)
+            return self._internalTranslate(self.anonymous_traversal, arg)
         elif callable(arg):  # closures
             lambdaString = arg().strip()
             if lambdaString.startswith("{"):
diff --git a/gremlin_python/process/traversal.py b/gremlin_python/process/traversal.py
index 7558151f79f9ca7d837bc11e0c650c810eb52a19..cbba42d8bfd8d16817cf2ac7ca74658262184cde 100644
--- a/gremlin_python/process/traversal.py
+++ b/gremlin_python/process/traversal.py
@@ -16,53 +16,50 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-from abc import abstractmethod
+import abc
+import six
 from aenum import Enum
-from gremlin_python import statics
+from .. import statics
 
 class Traversal(object):
     def __init__(self, graph, traversal_strategies, bytecode):
         self.graph = graph
         self.traversal_strategies = traversal_strategies
         self.bytecode = bytecode
-        self.results = None
+        self.side_effects = TraversalSideEffects()
+        self.traversers = None
         self.last_traverser = None
-        self.bindings = {}
-
     def __repr__(self):
-        return self.graph.translator.translate(self.bytecode)
-
-    def __getitem__(self, index):
-        if isinstance(index, int):
-            return self.range(index, index + 1)
-        elif isinstance(index, slice):
-            return self.range(index.start, index.stop)
-        else:
-            raise TypeError("Index must be int or slice")
-
-    def __getattr__(self, key):
-        return self.values(key)
-
+        return str(self.bytecode)
     def __iter__(self):
         return self
-
     def __next__(self):
-        if self.results is None:
+        if self.traversers is None:
             self.traversal_strategies.apply_strategies(self)
         if self.last_traverser is None:
-            self.last_traverser = next(self.results)
+            self.last_traverser = next(self.traversers)
         object = self.last_traverser.object
         self.last_traverser.bulk = self.last_traverser.bulk - 1
         if self.last_traverser.bulk <= 0:
             self.last_traverser = None
         return object
-
     def toList(self):
         return list(iter(self))
-
     def toSet(self):
         return set(iter(self))
-
+    def iterate(self):
+        while True:
+            try: self.nextTraverser()
+            except StopIteration: return self
+    def nextTraverser(self):
+        if self.traversers is None:
+            self.traversal_strategies.apply_strategies(self)
+        if self.last_traverser is None:
+            return next(self.traversers)
+        else:
+            temp = self.last_traverser
+            self.last_traverser = None
+            return temp
     def next(self, amount=None):
         if amount is None:
             return self.__next__()
@@ -76,29 +73,25 @@ class Traversal(object):
                 tempList.append(temp)
             return tempList
 
-Barrier = Enum('Barrier', 'normSack')
 
+Barrier = Enum('Barrier', 'normSack')
 statics.add_static('normSack', Barrier.normSack)
 
 Cardinality = Enum('Cardinality', 'list set single')
-
 statics.add_static('single', Cardinality.single)
 statics.add_static('list', Cardinality.list)
 statics.add_static('set', Cardinality.set)
 
 Column = Enum('Column', 'keys values')
-
 statics.add_static('keys', Column.keys)
 statics.add_static('values', Column.values)
 
 Direction = Enum('Direction', 'BOTH IN OUT')
-
 statics.add_static('OUT', Direction.OUT)
 statics.add_static('IN', Direction.IN)
 statics.add_static('BOTH', Direction.BOTH)
 
-Operator = Enum('Operator', 'addAll _and assign div max min minus mult _or sum sumLong')
-
+Operator = Enum('Operator', 'addAll and_ assign div max min minus mult or_ sum sumLong')
 statics.add_static('sum', Operator.sum)
 statics.add_static('minus', Operator.minus)
 statics.add_static('mult', Operator.mult)
@@ -106,13 +99,12 @@ statics.add_static('div', Operator.div)
 statics.add_static('min', Operator.min)
 statics.add_static('max', Operator.max)
 statics.add_static('assign', Operator.assign)
-statics.add_static('_and', Operator._and)
-statics.add_static('_or', Operator._or)
+statics.add_static('and_', Operator.and_)
+statics.add_static('or_', Operator.or_)
 statics.add_static('addAll', Operator.addAll)
 statics.add_static('sumLong', Operator.sumLong)
 
 Order = Enum('Order', 'decr incr keyDecr keyIncr shuffle valueDecr valueIncr')
-
 statics.add_static('incr', Order.incr)
 statics.add_static('decr', Order.decr)
 statics.add_static('keyIncr', Order.keyIncr)
@@ -121,19 +113,16 @@ statics.add_static('keyDecr', Order.keyDecr)
 statics.add_static('valueDecr', Order.valueDecr)
 statics.add_static('shuffle', Order.shuffle)
 
-Pop = Enum('Pop', 'all first last')
-
+Pop = Enum('Pop', 'all_ first last')
 statics.add_static('first', Pop.first)
 statics.add_static('last', Pop.last)
-statics.add_static('all', Pop.all)
+statics.add_static('all_', Pop.all_)
 
-Scope = Enum('Scope', '_global local')
-
-statics.add_static('_global', Scope._global)
+Scope = Enum('Scope', 'global_ local')
+statics.add_static('global_', Scope.global_)
 statics.add_static('local', Scope.local)
 
 T = Enum('T', 'id key label value')
-
 statics.add_static('label', T.label)
 statics.add_static('id', T.id)
 statics.add_static('key', T.key)
@@ -145,9 +134,6 @@ class P(object):
       self.value = value
       self.other = other
    @staticmethod
-   def _not(*args):
-      return P("not", *args)
-   @staticmethod
    def between(*args):
       return P("between", *args)
    @staticmethod
@@ -172,6 +158,9 @@ class P(object):
    def neq(*args):
       return P("neq", *args)
    @staticmethod
+   def not_(*args):
+      return P("not", *args)
+   @staticmethod
    def outside(*args):
       return P("outside", *args)
    @staticmethod
@@ -184,106 +173,94 @@ class P(object):
    def without(*args):
       return P("without", *args)
    def _and(self, arg):
-      return P("_and", arg, self)
+      return P("and", arg, self)
    def _or(self, arg):
-      return P("_or", arg, self)
-
-def _not(*args):
-      return P._not(*args)
-
-statics.add_static('_not',_not)
+      return P("or", arg, self)
+   def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.operator == other.operator and self.value == other.value and self.other == other.other
+   def __repr__(self):
+      return self.operator + "(" + str(self.value) + ")" if self.other is None else self.operator + "(" + str(self.value) + "," + str(self.other) + ")"
 
 def between(*args):
       return P.between(*args)
-
 statics.add_static('between',between)
 
 def eq(*args):
       return P.eq(*args)
-
 statics.add_static('eq',eq)
 
 def gt(*args):
       return P.gt(*args)
-
 statics.add_static('gt',gt)
 
 def gte(*args):
       return P.gte(*args)
-
 statics.add_static('gte',gte)
 
 def inside(*args):
       return P.inside(*args)
-
 statics.add_static('inside',inside)
 
 def lt(*args):
       return P.lt(*args)
-
 statics.add_static('lt',lt)
 
 def lte(*args):
       return P.lte(*args)
-
 statics.add_static('lte',lte)
 
 def neq(*args):
       return P.neq(*args)
-
 statics.add_static('neq',neq)
 
+def not_(*args):
+      return P.not_(*args)
+statics.add_static('not_',not_)
+
 def outside(*args):
       return P.outside(*args)
-
 statics.add_static('outside',outside)
 
 def test(*args):
       return P.test(*args)
-
 statics.add_static('test',test)
 
 def within(*args):
       return P.within(*args)
-
 statics.add_static('within',within)
 
 def without(*args):
       return P.without(*args)
-
 statics.add_static('without',without)
 
 
-class RawExpression(object):
-   def __init__(self, *args):
-      self.bindings = dict()
-      self.parts = [self._process_arg(arg) for arg in args]
-
-   def _process_arg(self, arg):
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-         self.bindings[arg[0]] = arg[1]
-         return Raw(arg[0])
-      else:
-         return Raw(arg)
-
-class Raw(object):
-   def __init__(self, value):
-      self.value = value
-
-   def __str__(self):
-      return str(self.value)
-
 
 '''
 TRAVERSER
 '''
 
 class Traverser(object):
-    def __init__(self, object, bulk):
+    def __init__(self, object, bulk=1):
         self.object = object
         self.bulk = bulk
     def __repr__(self):
         return str(self.object)
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.object == other.object
+
+'''
+TRAVERSAL SIDE-EFFECTS
+'''
+
+class TraversalSideEffects(object):
+    def keys(self):
+        return set()
+    def get(self, key):
+        raise KeyError(key)
+    def __getitem__(self, key):
+        return self.get(key)
+    def __repr__(self):
+        return "sideEffects[size:" + str(len(self.keys())) + "]"
 
 '''
 TRAVERSAL STRATEGIES
@@ -291,87 +268,68 @@ TRAVERSAL STRATEGIES
 
 class TraversalStrategies(object):
     global_cache = {}
-
-    def __init__(self, traversal_strategies):
-        self.traversal_strategies = traversal_strategies
-        return
-
+    def __init__(self, traversal_strategies=None):
+        self.traversal_strategies = traversal_strategies.traversal_strategies if traversal_strategies is not None else []
+    def add_strategies(self, traversal_strategies):
+        self.traversal_strategies = self.traversal_strategies + traversal_strategies
     def apply_strategies(self, traversal):
         for traversal_strategy in self.traversal_strategies:
             traversal_strategy.apply(traversal)
-        return
 
 
+@six.add_metaclass(abc.ABCMeta)
 class TraversalStrategy(object):
-    @abstractmethod
+    @abc.abstractmethod
     def apply(self, traversal):
         return
 
 '''
-BYTECODE AND TRANSLATOR
+BYTECODE
 '''
 
 class Bytecode(object):
     def __init__(self, bytecode=None):
         self.source_instructions = []
         self.step_instructions = []
+        self.bindings = {}
         if bytecode is not None:
             self.source_instructions = list(bytecode.source_instructions)
             self.step_instructions = list(bytecode.step_instructions)
-
     def add_source(self, source_name, *args):
-        newArgs = ()
+        instruction = [source_name]
         for arg in args:
-            newArgs = newArgs + (Bytecode.__convertArgument(arg),)
-        self.source_instructions.append((source_name, newArgs))
-        return
-
+            instruction.append(self._convertArgument(arg))
+        self.source_instructions.append(instruction)
     def add_step(self, step_name, *args):
-        newArgs = ()
+        instruction = [step_name]
         for arg in args:
-            newArgs = newArgs + (Bytecode.__convertArgument(arg),)
-        self.step_instructions.append((step_name, newArgs))
-        return
-
-    @staticmethod
-    def __convertArgument(arg):
+            instruction.append(self._convertArgument(arg))
+        self.step_instructions.append(instruction)
+    def _convertArgument(self,arg):
         if isinstance(arg, Traversal):
+            self.bindings.update(arg.bytecode.bindings)
             return arg.bytecode
+        elif isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
+            self.bindings[arg[0]] = arg[1]
+            return Binding(arg[0],arg[1])
         else:
             return arg
-
-
-TO_JAVA_MAP = {"_global": "global", "_as": "as", "_in": "in", "_and": "and",
-               "_or": "or", "_is": "is", "_not": "not", "_from": "from",
-               "Cardinality": "VertexProperty.Cardinality", "Barrier": "SackFunctions.Barrier"}
-
-
-class Translator(object):
-    def __init__(self, traversal_source, anonymous_traversal, target_language):
-        self.traversal_source = traversal_source
-        self.anonymous_traversal = anonymous_traversal
-        self.target_language = target_language
-
-    @abstractmethod
-    def translate(self, bytecode):
-        return
-
-    @abstractmethod
     def __repr__(self):
-        return "translator[" + self.traversal_source + ":" + self.target_language + "]"
+        return (str(self.source_instructions) if len(self.source_instructions) > 0 else "") + \
+               (str(self.step_instructions) if len(self.step_instructions) > 0 else "")
 
 
-class SymbolHelper(object):
-    @staticmethod
-    def toJava(symbol):
-        if (symbol in TO_JAVA_MAP):
-            return TO_JAVA_MAP[symbol]
-        else:
-            return symbol
+'''
+BINDINGS
+'''
 
-    @staticmethod
-    def mapEnum(enum):
-        if (enum in enumMap):
-            return enumMap[enum]
-        else:
-            return enum
+class Bindings(object):
+    def of(self,key,value):
+        if not isinstance(key, str):
+            raise TypeError("Key must be str")
+        return (key,value)
+
+class Binding(object):
+    def __init__(self,key,value):
+        self.key = key
+        self.value = value
diff --git a/gremlin_python/statics.py b/gremlin_python/statics.py
index 12cbb702c22b2366eaf0679fc32b941f46eba030..293ff93a6200ac7084fea76ffae509d45bc4d6d1 100644
--- a/gremlin_python/statics.py
+++ b/gremlin_python/statics.py
@@ -20,6 +20,7 @@ from aenum import Enum
 
 staticMethods = {}
 staticEnums = {}
+default_lambda_language = "gremlin-python"
 
 
 def add_static(key, value):
diff --git a/gremlin_python/structure/__init__.py b/gremlin_python/structure/__init__.py
index 1c69b5c0e00ef94926514351d5f58116fc30f5c6..7626550738f12ccd5e18b8c06a8d68aaab882066 100644
--- a/gremlin_python/structure/__init__.py
+++ b/gremlin_python/structure/__init__.py
@@ -16,7 +16,5 @@ 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
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
diff --git a/gremlin_python/structure/graph.py b/gremlin_python/structure/graph.py
index 60b22113b57b5ac40156ad0a4adaaeded39b6586..22403b653b715b2b8b3c4b3f113e3c74864d9156 100644
--- a/gremlin_python/structure/graph.py
+++ b/gremlin_python/structure/graph.py
@@ -24,5 +24,67 @@ from gremlin_python.process.traversal import TraversalStrategies
 
 
 class Graph(object):
+    def __init__(self):
+        if self.__class__ not in TraversalStrategies.global_cache:
+            TraversalStrategies.global_cache[self.__class__] = TraversalStrategies()
+
     def traversal(self):
         return GraphTraversalSource(self, TraversalStrategies.global_cache[self.__class__])
+
+    def __repr__(self):
+        return "graph[empty]"
+
+
+class Element(object):
+    def __init__(self, id, label):
+        self.id = id
+        self.label = label
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.id == other.id
+
+    def __hash__(self):
+        return hash(self.id)
+
+
+class Vertex(Element):
+    def __init__(self, id, label="vertex"):
+        Element.__init__(self, id, label)
+
+    def __repr__(self):
+        return "v[" + str(self.id) + "]"
+
+
+class Edge(Element):
+    def __init__(self, id, outV, label, inV):
+        Element.__init__(self, id, label)
+        self.outV = outV
+        self.inV = inV
+
+    def __repr__(self):
+        return "e[" + str(self.id) + "][" + str(self.outV.id) + "-" + self.label + "->" + str(self.inV.id) + "]"
+
+
+class VertexProperty(Element):
+    def __init__(self, id, label, value):
+        Element.__init__(self, id, label)
+        self.value = value
+        self.key = self.label
+
+    def __repr__(self):
+        return "vp[" + str(self.label) + "->" + str(self.value)[0:20] + "]"
+
+
+class Property(object):
+    def __init__(self, key, value):
+        self.key = key
+        self.value = value
+
+    def __repr__(self):
+        return "p[" + str(self.key) + "->" + str(self.value)[0:20] + "]"
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.key == other.key and self.value == other.value
+
+    def __hash__(self):
+        return hash(self.key) + hash(self.value)
diff --git a/gremlin_python/driver/rest_remote_connection.py b/gremlin_python/structure/io/__init__.py
similarity index 51%
rename from gremlin_python/driver/rest_remote_connection.py
rename to gremlin_python/structure/io/__init__.py
index 6d967f8268efeb7749209879d6ae86a5e9bbd1f3..7626550738f12ccd5e18b8c06a8d68aaab882066 100644
--- a/gremlin_python/driver/rest_remote_connection.py
+++ b/gremlin_python/structure/io/__init__.py
@@ -16,28 +16,5 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-import json
-import requests
-
-from gremlin_python.process.traversal import Traverser
-from .remote_connection import RemoteConnection
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
-
-
-class RESTRemoteConnection(RemoteConnection):
-    def __init__(self, url):
-        RemoteConnection.__init__(self, url)
-
-    def __repr__(self):
-        return "RESTRemoteConnection[" + self.url + "]"
-
-    def submit(self, target_language, script, bindings):
-        response = requests.post(self.url, data=json.dumps(
-            {"gremlin": script, "language": target_language, "bindings": bindings}))
-        if response.status_code != requests.codes.ok:
-            raise BaseException(response.text)
-        results = []
-        for x in response.json()['result']['data']:
-            results.append(Traverser(x, 1))
-        return iter(results)
diff --git a/gremlin_python/structure/io/graphson.py b/gremlin_python/structure/io/graphson.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed3114554061a772f5396f4e2dad55dbecaccb57
--- /dev/null
+++ b/gremlin_python/structure/io/graphson.py
@@ -0,0 +1,292 @@
+'''
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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
+
+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.
+'''
+
+__author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
+
+import json
+from abc import abstractmethod
+from aenum import Enum
+from types import FunctionType
+
+from gremlin_python import statics
+from gremlin_python.process.traversal import Binding
+from gremlin_python.process.traversal import Bytecode
+from gremlin_python.process.traversal import P
+from gremlin_python.process.traversal import Traversal
+from gremlin_python.process.traversal import Traverser
+from gremlin_python.structure.graph import Edge
+from gremlin_python.structure.graph import Property
+from gremlin_python.structure.graph import Vertex
+from gremlin_python.structure.graph import VertexProperty
+
+
+class long(int): pass
+FloatType = float
+IntType = int
+LongType = long
+
+
+
+class GraphSONWriter(object):
+    @staticmethod
+    def _dictify(object):
+        for key in serializers:
+            if isinstance(object, key):
+                return serializers[key]._dictify(object)
+        # list and map are treated as normal json objects (could be isolated serializers)
+        if isinstance(object, list):
+            newList = []
+            for item in object:
+                newList.append(GraphSONWriter._dictify(item))
+            return newList
+        elif isinstance(object, dict):
+            newDict = {}
+            for key in object:
+                newDict[GraphSONWriter._dictify(key)] = GraphSONWriter._dictify(object[key])
+            return newDict
+        else:
+            return object
+
+    @staticmethod
+    def writeObject(objectData):
+        return json.dumps(GraphSONWriter._dictify(objectData), separators=(',', ':'))
+
+
+class GraphSONReader(object):
+    @staticmethod
+    def _objectify(object):
+        if isinstance(object, dict):
+            if _SymbolHelper._TYPE in object:
+                type = object[_SymbolHelper._TYPE]
+                if type in deserializers:
+                    return deserializers[type]._objectify(object)
+                    # list and map are treated as normal json objects (could be isolated deserializers)
+            newDict = {}
+            for key in object:
+                newDict[GraphSONReader._objectify(key)] = GraphSONReader._objectify(object[key])
+            return newDict
+        elif isinstance(object, list):
+            newList = []
+            for item in object:
+                newList.append(GraphSONReader._objectify(item))
+            return newList
+        else:
+            return object
+
+    @staticmethod
+    def readObject(jsonData):
+        return GraphSONReader._objectify(json.loads(jsonData))
+
+
+'''
+SERIALIZERS
+'''
+
+
+class GraphSONSerializer(object):
+    @abstractmethod
+    def _dictify(self, object):
+        return object
+
+
+class BytecodeSerializer(GraphSONSerializer):
+    def _dictify(self, bytecode):
+        if isinstance(bytecode, Traversal):
+            bytecode = bytecode.bytecode
+        dict = {}
+        sources = []
+        for instruction in bytecode.source_instructions:
+            inst = []
+            inst.append(instruction[0])
+            for arg in instruction[1:]:
+                inst.append(GraphSONWriter._dictify(arg))
+            sources.append(inst)
+        steps = []
+        for instruction in bytecode.step_instructions:
+            inst = []
+            inst.append(instruction[0])
+            for arg in instruction[1:]:
+                inst.append(GraphSONWriter._dictify(arg))
+            steps.append(inst)
+        if len(sources) > 0:
+            dict["source"] = sources
+        if len(steps) > 0:
+            dict["step"] = steps
+        return _SymbolHelper.objectify("Bytecode", dict)
+
+
+class TraverserSerializer(GraphSONSerializer):
+    def _dictify(self, traverser):
+        return _SymbolHelper.objectify("Traverser", {"value": GraphSONWriter._dictify(traverser.object),
+                                                     "bulk": GraphSONWriter._dictify(traverser.bulk)})
+
+
+class EnumSerializer(GraphSONSerializer):
+    def _dictify(self, enum):
+        return _SymbolHelper.objectify(_SymbolHelper.toGremlin(type(enum).__name__),
+                                       _SymbolHelper.toGremlin(str(enum.name)))
+
+
+class PSerializer(GraphSONSerializer):
+    def _dictify(self, p):
+        dict = {}
+        dict["predicate"] = p.operator
+        if p.other is None:
+            dict["value"] = GraphSONWriter._dictify(p.value)
+        else:
+            dict["value"] = [GraphSONWriter._dictify(p.value), GraphSONWriter._dictify(p.other)]
+        return _SymbolHelper.objectify("P", dict)
+
+
+class BindingSerializer(GraphSONSerializer):
+    def _dictify(self, binding):
+        dict = {}
+        dict["key"] = binding.key
+        dict["value"] = GraphSONWriter._dictify(binding.value)
+        return _SymbolHelper.objectify("Binding", dict)
+
+
+class LambdaSerializer(GraphSONSerializer):
+    def _dictify(self, lambdaObject):
+        lambdaResult = lambdaObject()
+        dict = {}
+        script = lambdaResult if isinstance(lambdaResult, str) else lambdaResult[0]
+        language = statics.default_lambda_language if isinstance(lambdaResult, str) else lambdaResult[1]
+        dict["script"] = script
+        dict["language"] = language
+        if language == "gremlin-jython" or language == "gremlin-python":
+            if not script.strip().startswith("lambda"):
+                script = "lambda " + script
+                dict["script"] = script
+            dict["arguments"] = eval(dict["script"]).func_code.co_argcount
+        else:
+            dict["arguments"] = -1
+        return _SymbolHelper.objectify("Lambda", dict)
+
+
+class NumberSerializer(GraphSONSerializer):
+    def _dictify(self, number):
+        if isinstance(number, bool):  # python thinks that 0/1 integers are booleans
+            return number
+        elif isinstance(number, int):
+            return _SymbolHelper.objectify("Int64", number)
+        elif isinstance(number, float):
+            return _SymbolHelper.objectify("Float", number)
+        else:
+            return number
+
+
+'''
+DESERIALIZERS
+'''
+
+
+class GraphSONDeserializer(object):
+    @abstractmethod
+    def _objectify(self, dict):
+        return dict
+
+
+class TraverserDeserializer(GraphSONDeserializer):
+    def _objectify(self, dict):
+        return Traverser(GraphSONReader._objectify(dict[_SymbolHelper._VALUE]["value"]),
+                         GraphSONReader._objectify(dict[_SymbolHelper._VALUE]["bulk"]))
+
+
+class NumberDeserializer(GraphSONDeserializer):
+    def _objectify(self, dict):
+        type = dict[_SymbolHelper._TYPE]
+        value = dict[_SymbolHelper._VALUE]
+        if type == "g:Int32":
+            return int(value)
+        elif type == "g:Int64":
+            return long(value)
+        else:
+            return float(value)
+
+
+class VertexDeserializer(GraphSONDeserializer):
+    def _objectify(self, dict):
+        value = dict[_SymbolHelper._VALUE]
+        return Vertex(GraphSONReader._objectify(value["id"]), value["label"] if "label" in value else "")
+
+
+class EdgeDeserializer(GraphSONDeserializer):
+    def _objectify(self, dict):
+        value = dict[_SymbolHelper._VALUE]
+        return Edge(GraphSONReader._objectify(value["id"]),
+                    Vertex(GraphSONReader._objectify(value["outV"]), ""),
+                    value["label"] if "label" in value else "vertex",
+                    Vertex(GraphSONReader._objectify(value["inV"]), ""))
+
+
+class VertexPropertyDeserializer(GraphSONDeserializer):
+    def _objectify(self, dict):
+        value = dict[_SymbolHelper._VALUE]
+        return VertexProperty(GraphSONReader._objectify(value["id"]), value["label"],
+                              GraphSONReader._objectify(value["value"]))
+
+
+class PropertyDeserializer(GraphSONDeserializer):
+    def _objectify(self, dict):
+        value = dict[_SymbolHelper._VALUE]
+        return Property(value["key"], GraphSONReader._objectify(value["value"]))
+
+
+class _SymbolHelper(object):
+    symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and",
+                 "or_": "or", "is_": "is", "not_": "not", "from_": "from",
+                 "set_": "set", "list_": "list", "all_": "all"}
+
+    _TYPE = "@type"
+    _VALUE = "@value"
+
+    @staticmethod
+    def toGremlin(symbol):
+        return _SymbolHelper.symbolMap[symbol] if symbol in _SymbolHelper.symbolMap else symbol
+
+    @staticmethod
+    def objectify(type, value, prefix="g"):
+        return {_SymbolHelper._TYPE: prefix + ":" + type, _SymbolHelper._VALUE: value}
+
+
+serializers = {
+    Traversal: BytecodeSerializer(),
+    Traverser: TraverserSerializer(),
+    Bytecode: BytecodeSerializer(),
+    Binding: BindingSerializer(),
+    P: PSerializer(),
+    Enum: EnumSerializer(),
+    FunctionType: LambdaSerializer(),
+    LongType: NumberSerializer(),
+    IntType: NumberSerializer(),
+    FloatType: NumberSerializer()
+}
+
+deserializers = {
+    "g:Traverser": TraverserDeserializer(),
+    "g:Int32": NumberDeserializer(),
+    "g:Int64": NumberDeserializer(),
+    "g:Float": NumberDeserializer(),
+    "g:Double": NumberDeserializer(),
+    "g:Vertex": VertexDeserializer(),
+    "g:Edge": EdgeDeserializer(),
+    "g:VertexProperty": VertexPropertyDeserializer(),
+    "g:Property": PropertyDeserializer()
+}
diff --git a/gremlin_python/structure/remote_graph.py b/gremlin_python/structure/remote_graph.py
deleted file mode 100644
index 778895b24a4438477a328b383bedc900935eae5e..0000000000000000000000000000000000000000
--- a/gremlin_python/structure/remote_graph.py
+++ /dev/null
@@ -1,47 +0,0 @@
-'''
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you 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
-
-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.
-'''
-
-__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
-
-
-class RemoteGraph(Graph):
-    def __init__(self, translator, remote_connection):
-        TraversalStrategies.global_cache[self.__class__] = TraversalStrategies([RemoteStrategy()])
-        self.translator = translator
-        self.remote_connection = remote_connection
-
-    def __repr__(self):
-        return "remotegraph[" + self.remote_connection.url + "]"
-
-
-class RemoteStrategy(TraversalStrategy):
-    def apply(self, traversal):
-        if not (traversal.graph.__class__.__name__ == "RemoteGraph"):
-            raise BaseException(
-                "RemoteStrategy can only be used with a RemoteGraph: " + traversal.graph.__class__.__name__)
-        if traversal.results is None:
-            traversal.results = traversal.graph.remote_connection.submit(
-                traversal.graph.translator.target_language,  # script engine
-                traversal.graph.translator.translate(traversal.bytecode),  # script
-                traversal.bindings)  # bindings
-        return
diff --git a/requirements.txt b/requirements.txt
index e11f6e8a34eff7f8e5228f7d9e6653723bdf71f9..1caa67af2382c38fc052e781ef9c0d02d9f1491a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,8 +10,12 @@ inflection==0.3.1
 Jinja2==2.8
 MarkupSafe==0.23
 multidict==1.2.0
+PyYAML==3.12
 Pygments==2.1.3
 pytz==2016.6.1
+pytest==3.0.2
+pytest-asyncio==0.5.0
+pytest-runner==2.9
 six==1.10.0
 snowballstemmer==1.2.1
 Sphinx==1.4.5
diff --git a/setup.cfg b/setup.cfg
index b7e478982ccf9ab1963c74e1084dfccb6e42c583..cfcbfea95cdd0b95dd262f7a44e723e8df284400 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,5 @@
 [aliases]
 test=pytest
+
+[tool:pytest]
+norecursedirs = '.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg' lib lib64
diff --git a/setup.py b/setup.py
index 84655e153b7b4fe2a6fa7ee56c32e385fdc199d5..db6494c410e87ce07d9db3d40c77fcec84c5f8aa 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@ from setuptools import setup
 
 setup(
     name="goblin",
-    version="1.0.0a3",
+    version="1.0.0b1",
     url="",
     license="AGPL",
     author="davebshow",
@@ -15,7 +15,8 @@ setup(
     install_requires=[
         "aenum==1.4.5",
         "aiohttp==0.22.1",
-        "inflection==0.3.1"
+        "inflection==0.3.1",
+        "PyYAML==3.12"
     ],
     test_suite="tests",
     setup_requires=['pytest-runner'],
diff --git a/tests/config/config.json b/tests/config/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..12e1ba733bbb8350713d31ba59a34faf33cdc508
--- /dev/null
+++ b/tests/config/config.json
@@ -0,0 +1,13 @@
+{
+   "ssl_password":"",
+   "port":8182,
+   "ssl_certfile":"",
+   "scheme":"wss",
+   "hosts":[
+      "localhost"
+   ],
+   "username":"dave",
+   "password":"mypass",
+   "ssl_keyfile":"",
+   "message_serializer":"goblin.driver.GraphSONMessageSerializer"
+}
diff --git a/tests/config/config.yml b/tests/config/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a02338a20c434836141d16582ba2d99196f9f663
--- /dev/null
+++ b/tests/config/config.yml
@@ -0,0 +1,4 @@
+scheme: 'wss'
+hosts: ['localhost']
+port: 8183
+message_serializer: 'goblin.driver.GraphSONMessageSerializer'
diff --git a/tests/conftest.py b/tests/conftest.py
index 288d745ccfa4f7e83067eb3c885ff03454275031..9468d106aa32532b57e9ab5fbf4f35d06daeff12 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -14,12 +14,18 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
-
+import asyncio
 import pytest
-from goblin import create_app, driver, element, properties, Cardinality
+from goblin import Goblin, driver, element, properties, Cardinality
+from goblin.driver import pool, serializer
 from gremlin_python import process
 
 
+def pytest_generate_tests(metafunc):
+    if 'cluster' in metafunc.fixturenames:
+        metafunc.parametrize("cluster", ['c1', 'c2'], indirect=True)
+
+
 class HistoricalName(element.VertexProperty):
     notes = properties.Property(properties.String)
     year = properties.Property(properties.Integer)  # this is dumb but handy
@@ -43,7 +49,6 @@ class Place(element.Vertex):
         properties.Integer, card=Cardinality.set)
 
 
-
 class Knows(element.Edge):
     __label__ = 'knows'
     notes = properties.Property(properties.String, default='N/A')
@@ -60,34 +65,50 @@ def gremlin_server():
 
 @pytest.fixture
 def unused_server_url(unused_tcp_port):
-    return 'http://localhost:{}/'.format(unused_tcp_port)
+    return 'http://localhost:{}/gremlin'.format(unused_tcp_port)
 
 
 @pytest.fixture
-def connection(gremlin_server, event_loop):
+def connection(event_loop):
     conn = event_loop.run_until_complete(
-        gremlin_server.open("http://localhost:8182/", event_loop))
+        driver.Connection.open(
+            "http://localhost:8182/gremlin", event_loop,
+            message_serializer=serializer.GraphSONMessageSerializer))
     return conn
 
 
 @pytest.fixture
-def remote_graph(connection):
-     translator = process.GroovyTranslator('g')
-     return driver.AsyncRemoteGraph(translator, connection)
+def connection_pool(event_loop):
+    return pool.ConnectionPool(
+        "http://localhost:8182/gremlin", event_loop, None, '', '', 4, 1, 16,
+        64, None, serializer.GraphSONMessageSerializer)
 
 
 @pytest.fixture
-def app(event_loop):
-    app = event_loop.run_until_complete(
-        create_app("http://localhost:8182/", event_loop))
-    app.register(Person, Place, Knows, LivesIn)
-    return app
+def cluster(request, event_loop):
+    if request.param == 'c1':
+        cluster = driver.Cluster(
+            event_loop,
+            message_serializer=serializer.GraphSONMessageSerializer)
+    elif request.param == 'c2':
+        cluster = driver.Cluster(
+            event_loop,
+            message_serializer=serializer.GraphSON2MessageSerializer)
+    return cluster
 
 
 @pytest.fixture
-def session(event_loop, app):
-    session = event_loop.run_until_complete(app.session())
-    return session
+def remote_graph():
+     return driver.AsyncGraph()
+
+
+@pytest.fixture
+def app(request, event_loop):
+    app = event_loop.run_until_complete(
+        Goblin.open(event_loop, aliases={'g': 'g'}))
+
+    app.register(Person, Place, Knows, LivesIn)
+    return app
 
 
 # Instance fixtures
@@ -142,6 +163,11 @@ def place_name():
 
 
 # Class fixtures
+@pytest.fixture
+def cluster_class(event_loop):
+    return driver.Cluster
+
+
 @pytest.fixture
 def string_class():
     return properties.String
diff --git a/tests/test_app.py b/tests/test_app.py
index cf2e11d9747a292a8faf953e410f95a512a13bf4..89067f1a3dcdab29121562b75350b88ece3cc8d5 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -15,29 +15,40 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
 
+import pytest
+
 from goblin import element
+from goblin.driver import serializer
 from gremlin_python import process
 
 
-def test_registry(app, person, place, knows, lives_in):
+@pytest.mark.asyncio
+async def test_registry(app, person, place, knows, lives_in):
     assert len(app.vertices) ==  2
     assert len(app.edges) == 2
     assert person.__class__ == app.vertices['person']
     assert place.__class__ == app.vertices['place']
     assert knows.__class__ == app.edges['knows']
     assert lives_in.__class__ == app.edges['lives_in']
+    await app.close()
 
 
-def test_registry_defaults(app):
+@pytest.mark.asyncio
+async def test_registry_defaults(app):
     vertex = app.vertices['unregistered']
     assert isinstance(vertex(), element.Vertex)
     edge = app.edges['unregistered']
     assert isinstance(edge(), element.Edge)
+    await app.close()
 
-
-def test_features(app):
-    assert app._features
+@pytest.mark.asyncio
+async def test_transaction_discovery(app):
+    assert app._transactions is not None
+    await app.close()
 
 
-def test_translator(app):
-    assert isinstance(app.translator, process.GroovyTranslator)
+@pytest.mark.asyncio
+async def test_aliases(app):
+    session = await app.session()
+    assert session._conn._aliases == {'g': 'g'}
+    await app.close()
diff --git a/tests/test_client.py b/tests/test_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..889ffe6fad09cb1350544992f7402ddedf08b74a
--- /dev/null
+++ b/tests/test_client.py
@@ -0,0 +1,64 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+import asyncio
+import uuid
+
+import pytest
+
+from goblin.driver.server import GremlinServer
+
+
+@pytest.mark.asyncio
+async def test_client_auto_release(cluster):
+    client = await cluster.connect()
+    resp = await client.submit(gremlin="1 + 1")
+    async for msg in resp:
+        pass
+    await asyncio.sleep(0)
+    host = cluster._hosts.popleft()
+    assert len(host._pool._available) == 1
+    await host.close()
+
+
+@pytest.mark.asyncio
+async def test_alias(cluster):
+    client = await cluster.connect()
+    aliased_client = client.alias({"g": "g1"})
+    assert aliased_client._aliases == {"g": "g1"}
+    assert aliased_client._cluster is client._cluster
+    assert aliased_client._loop is client._loop
+    await cluster.close()
+
+
+@pytest.mark.asyncio
+async def test_sessioned_client(cluster):
+    session = str(uuid.uuid4())
+    client = await cluster.connect(session=session)
+    assert isinstance(client.cluster, GremlinServer)
+    resp = await client.submit(gremlin="v = g.addV('person').property('name', 'joe').next(); v")
+    async for msg in resp:
+        try:
+            assert msg['properties']['name'][0]['value'] == 'joe'
+        except KeyError:
+            assert msg['properties']['name'][0]['@value']['value'] == 'joe'
+
+    resp = await client.submit(gremlin="g.V(v.id).values('name')")
+
+    async for msg in resp:
+        assert msg == 'joe'
+    await cluster.close()
diff --git a/tests/test_config.py b/tests/test_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..72d4bcf4189a5c03e87f25ab43bb6fc543d1ae9c
--- /dev/null
+++ b/tests/test_config.py
@@ -0,0 +1,128 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+import os
+
+import pytest
+
+from goblin import driver, exception
+
+
+dirname = os.path.dirname(os.path.dirname(__file__))
+
+
+def test_cluster_default_config(cluster):
+    assert cluster.config['scheme'] == 'ws'
+    assert cluster.config['hosts'] == ['localhost']
+    assert cluster.config['port'] == 8182
+    assert cluster.config['ssl_certfile'] == ''
+    assert cluster.config['ssl_keyfile'] == ''
+    assert cluster.config['ssl_password'] == ''
+    assert cluster.config['username'] == ''
+    assert cluster.config['password'] == ''
+
+
+@pytest.mark.asyncio
+async def test_app_default_config(app):
+    assert app.config['scheme'] == 'ws'
+    assert app.config['hosts'] == ['localhost']
+    assert app.config['port'] == 8182
+    assert app.config['ssl_certfile'] == ''
+    assert app.config['ssl_keyfile'] == ''
+    assert app.config['ssl_password'] == ''
+    assert app.config['username'] == ''
+    assert app.config['password'] == ''
+    assert issubclass(app.config['message_serializer'],
+                      driver.GraphSONMessageSerializer)
+    await app.close()
+
+
+def test_cluster_custom_config(event_loop, cluster_class):
+    cluster = cluster_class(event_loop, username='dave', password='mypass',
+                            hosts=['127.0.0.1'])
+    assert cluster.config['scheme'] == 'ws'
+    assert cluster.config['hosts'] == ['127.0.0.1']
+    assert cluster.config['port'] == 8182
+    assert cluster.config['ssl_certfile'] == ''
+    assert cluster.config['ssl_keyfile'] == ''
+    assert cluster.config['ssl_password'] == ''
+    assert cluster.config['username'] == 'dave'
+    assert cluster.config['password'] == 'mypass'
+    assert issubclass(cluster.config['message_serializer'],
+                      driver.GraphSONMessageSerializer)
+
+
+def test_cluster_config_from_json(event_loop, cluster_class):
+    cluster = cluster_class(event_loop)
+    cluster.config_from_file(dirname + '/tests/config/config.json')
+    assert cluster.config['scheme'] == 'wss'
+    assert cluster.config['hosts'] == ['localhost']
+    assert cluster.config['port'] == 8182
+    assert cluster.config['ssl_certfile'] == ''
+    assert cluster.config['ssl_keyfile'] == ''
+    assert cluster.config['ssl_password'] == ''
+    assert cluster.config['username'] == 'dave'
+    assert cluster.config['password'] == 'mypass'
+
+    assert issubclass(cluster.config['message_serializer'],
+                      driver.GraphSONMessageSerializer)
+
+
+def test_cluster_config_from_yaml(event_loop, cluster_class):
+    cluster = cluster_class(event_loop)
+    cluster.config_from_file(dirname + '/tests/config/config.yml')
+    assert cluster.config['scheme'] == 'wss'
+    assert cluster.config['hosts'] == ['localhost']
+    assert cluster.config['port'] == 8183
+    assert cluster.config['ssl_certfile'] == ''
+    assert cluster.config['ssl_keyfile'] == ''
+    assert cluster.config['ssl_password'] == ''
+    assert cluster.config['username'] == ''
+    assert cluster.config['password'] == ''
+    assert issubclass(cluster.config['message_serializer'],
+                      driver.GraphSONMessageSerializer)
+
+@pytest.mark.asyncio
+async def test_app_config_from_json(app):
+    app.config_from_file(dirname + '/tests/config/config.json')
+    assert app.config['scheme'] == 'wss'
+    assert app.config['hosts'] == ['localhost']
+    assert app.config['port'] == 8182
+    assert app.config['ssl_certfile'] == ''
+    assert app.config['ssl_keyfile'] == ''
+    assert app.config['ssl_password'] == ''
+    assert app.config['username'] == 'dave'
+    assert app.config['password'] == 'mypass'
+
+    assert issubclass(app.config['message_serializer'],
+                      driver.GraphSONMessageSerializer)
+    await app.close()
+
+
+@pytest.mark.asyncio
+async def test_app_config_from_yaml(app):
+    app.config_from_file(dirname + '/tests/config/config.yml')
+    assert app.config['scheme'] == 'wss'
+    assert app.config['hosts'] == ['localhost']
+    assert app.config['port'] == 8183
+    assert app.config['ssl_certfile'] == ''
+    assert app.config['ssl_keyfile'] == ''
+    assert app.config['ssl_password'] == ''
+    assert app.config['username'] == ''
+    assert app.config['password'] == ''
+    assert issubclass(app.config['message_serializer'],
+                      driver.GraphSONMessageSerializer)
+    await app.close()
diff --git a/tests/test_driver.py b/tests/test_connection.py
similarity index 73%
rename from tests/test_driver.py
rename to tests/test_connection.py
index d62b1f132daecfd6e15df15426da751bbf76ebd3..5ed44f641decd3aa836381796bed489c6ffb7fa1 100644
--- a/tests/test_driver.py
+++ b/tests/test_connection.py
@@ -14,7 +14,7 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
-
+import asyncio
 import pytest
 
 from goblin import exception
@@ -40,7 +40,7 @@ async def test_conn_context_manager(connection):
 @pytest.mark.asyncio
 async def test_submit(connection):
     async with connection:
-        stream = await connection.submit("1 + 1")
+        stream = await connection.submit(gremlin="1 + 1")
         results = []
         async for msg in stream:
             results.append(msg)
@@ -52,7 +52,8 @@ async def test_submit(connection):
 async def test_204_empty_stream(connection):
     resp = False
     async with connection:
-        stream = await connection.submit('g.V().has("unlikely", "even less likely")')
+        stream = await connection.submit(
+            gremlin='g.V().has("unlikely", "even less likely")')
         async for msg in stream:
             resp = True
     assert not resp
@@ -61,7 +62,7 @@ async def test_204_empty_stream(connection):
 @pytest.mark.asyncio
 async def test_server_error(connection):
     async with connection:
-        stream = await connection.submit('g. V jla;sdf')
+        stream = await connection.submit(gremlin='g. V jla;sdf')
         with pytest.raises(exception.GremlinServerError):
             async for msg in stream:
                 pass
@@ -70,15 +71,16 @@ async def test_server_error(connection):
 @pytest.mark.asyncio
 async def test_cant_connect(event_loop, gremlin_server, unused_server_url):
     with pytest.raises(Exception):
-        await gremlin_server.open(unused_server_url, event_loop)
+        await gremlin_server.get_connection(unused_server_url, event_loop)
 
 
 @pytest.mark.asyncio
 async def test_resp_queue_removed_from_conn(connection):
     async with connection:
-        stream = await connection.submit("1 + 1")
+        stream = await connection.submit(gremlin="1 + 1")
         async for msg in stream:
             pass
+        await asyncio.sleep(0)
         assert stream._response_queue not in list(
             connection._response_queues.values())
 
@@ -86,7 +88,16 @@ async def test_resp_queue_removed_from_conn(connection):
 @pytest.mark.asyncio
 async def test_stream_done(connection):
     async with connection:
-        stream = await connection.submit("1 + 1")
+        stream = await connection.submit(gremlin="1 + 1")
         async for msg in stream:
             pass
-        assert stream._done
+        assert stream.done
+
+@pytest.mark.asyncio
+async def test_connection_response_timeout(connection):
+    async with connection:
+        connection._response_timeout = 0.0000001
+        with pytest.raises(exception.ResponseTimeoutError):
+            stream = await connection.submit(gremlin="1 + 1")
+            async for msg in stream:
+                pass
diff --git a/tests/test_connection_protocol.py b/tests/test_connection_protocol.py
new file mode 100644
index 0000000000000000000000000000000000000000..227cdec02c2580772ca3a5a246b6196ad4491f5d
--- /dev/null
+++ b/tests/test_connection_protocol.py
@@ -0,0 +1,137 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+import asyncio
+import uuid
+import pytest
+
+from goblin import exception
+from goblin.driver import serializer
+
+
+@pytest.mark.asyncio
+async def test_eval(remote_graph, connection):
+    async with connection:
+        connection._message_serializer = serializer.GraphSON2MessageSerializer()
+        g = remote_graph.traversal()
+        traversal = "g.addV('person').property('name', 'leifur')"
+        resp = await connection.submit(
+            processor='', op='eval', gremlin=traversal, scriptEvalTimeout=1)
+
+        async for msg in resp:
+            assert msg['label'] == 'person'
+
+
+@pytest.mark.asyncio
+async def test_bytecode(remote_graph, connection):
+    async with connection:
+        connection._message_serializer = serializer.GraphSON2MessageSerializer()
+        g = remote_graph.traversal()
+        traversal = g.addV('person').property('name', 'leifur')
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        async for msg in resp:
+            vid = msg.id
+        traversal = g.V(vid).label()
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        async for msg in resp:
+            assert msg == 'person'
+        traversal = g.V(vid).name
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        async for msg in resp:
+            assert msg == 'leifur'
+
+
+@pytest.mark.asyncio
+async def test_side_effects(remote_graph, connection):
+    async with connection:
+        connection._message_serializer = serializer.GraphSON2MessageSerializer()
+        g = remote_graph.traversal()
+        # Add some nodes
+        traversal = g.addV('person').property('name', 'leifur')
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        async for msg in resp:
+            pass
+        traversal = g.addV('person').property('name', 'dave')
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        async for msg in resp:
+            pass
+        traversal = g.addV('person').property('name', 'jonathan')
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        async for msg in resp:
+            pass
+
+        # # Make a query
+        traversal = g.V().aggregate('a').aggregate('b')
+        resp = await connection.submit(
+            processor='traversal', op='bytecode', gremlin=traversal.bytecode)
+        request_id = resp.request_id
+        async for msg in resp:
+            pass
+        resp = await connection.submit(processor='traversal', op='keys',
+                                       sideEffect=request_id)
+        keys = []
+        async for msg in resp:
+            keys.append(msg)
+        assert keys == ['a', 'b']
+
+        resp = await connection.submit(processor='traversal', op='gather',
+                                       sideEffect=request_id,
+                                       sideEffectKey='a')
+        side_effects = []
+        async for msg in resp:
+            side_effects.append(msg)
+        assert side_effects
+
+        # Close isn't implmented yet
+        # resp = await connection.submit(processor='traversal', op='close',
+        #                                sideEffect=request_id)
+        # async for msg in resp:
+        #     print(msg)
+
+
+@pytest.mark.asyncio
+async def test_session(connection):
+    async with connection:
+        connection._message_serializer = serializer.GraphSON2MessageSerializer()
+        session = str(uuid.uuid4())
+        resp = await connection.submit(
+            gremlin="v = g.addV('person').property('name', 'unused_name').next(); v",
+            processor='session',
+            op='eval',
+            session=session)
+        async for msg in resp:
+            assert msg['label'] == 'person'
+        resp = await connection.submit(
+            gremlin="v.values('name')",
+            processor='session',
+            op='eval',
+            session=session)
+        async for msg in resp:
+            assert msg == 'unused_name'
+        # Close isnt' implemented yet
+        # resp = await connection.submit(
+        #     processor='session',
+        #     op='close',
+        #     session=session)
+        # async for msg in resp:
+        #     print(msg)
diff --git a/tests/test_graph.py b/tests/test_graph.py
index 415f6f9a37b887b8884b7d838400d268ab9d7322..c4a9ec7f71df191045641217ced28904475602a1 100644
--- a/tests/test_graph.py
+++ b/tests/test_graph.py
@@ -17,40 +17,64 @@
 
 import pytest
 
-from gremlin_python import process
-
-
-@pytest.mark.asyncio
-async def test_close_graph(remote_graph):
-    remote_connection = remote_graph.remote_connection
-    await remote_graph.close()
-    assert remote_connection.closed
-
+from goblin.driver import serializer
 
-@pytest.mark.asyncio
-async def test_conn_context_manager(remote_graph):
-    remote_connection = remote_graph.remote_connection
-    async with remote_graph:
-        assert not remote_graph.remote_connection.closed
-    assert remote_connection.closed
+from gremlin_python import process
 
 
 @pytest.mark.asyncio
-async def test_generate_traversal(remote_graph):
-    async with remote_graph:
-        traversal = remote_graph.traversal().V().hasLabel(('v1', 'person'))
-        assert isinstance(traversal, process.GraphTraversal)
-        assert traversal.bindings['v1'] == 'person'
+async def test_generate_traversal(remote_graph, connection):
+    async with connection:
+        g = remote_graph.traversal().withRemote(connection)
+        traversal = g.V().hasLabel(('v1', 'person'))
+        assert isinstance(traversal, process.graph_traversal.GraphTraversal)
+        assert traversal.bytecode.bindings['v1'] == 'person'
 
 
 @pytest.mark.asyncio
-async def test_submit_traversal(remote_graph):
-    async with remote_graph:
-        g = remote_graph.traversal()
-        resp = await g.addV('person').property('name', 'leifur').next()
-        leif = await resp.fetch_data()
+async def test_submit_traversal(remote_graph, connection):
+    async with connection:
+        g = remote_graph.traversal().withRemote(connection)
+        resp = g.addV('person').property('name', 'leifur')
+        leif = await resp.next()
+        resp.traversers.close()
         assert leif['properties']['name'][0]['value'] == 'leifur'
         assert leif['label'] == 'person'
-        resp = await g.V(leif['id']).drop().next()
-        none = await resp.fetch_data()
+        resp = g.V(leif['id']).drop()
+        none = await resp.next()
         assert none is None
+
+
+@pytest.mark.asyncio
+async def test_side_effects(remote_graph, connection):
+    async with connection:
+        connection._message_serializer = serializer.GraphSON2MessageSerializer()
+        g = remote_graph.traversal().withRemote(connection)
+        # create some nodes
+        resp = g.addV('person').property('name', 'leifur')
+        leif = await resp.next()
+        resp.traversers.close()
+        resp = g.addV('person').property('name', 'dave')
+        dave = await resp.next()
+        resp.traversers.close()
+        resp = g.addV('person').property('name', 'jon')
+        jonthan = await resp.next()
+        resp.traversers.close()
+        traversal = g.V().aggregate('a').aggregate('b')
+        async for msg in traversal:
+            pass
+        keys = []
+        resp = await traversal.side_effects.keys()
+        async for msg in resp:
+            keys.append(msg)
+        assert keys == ['a', 'b']
+        side_effects = []
+        resp = await traversal.side_effects.get('a')
+        async for msg in resp:
+            side_effects.append(msg)
+        assert side_effects
+        side_effects = []
+        resp = await traversal.side_effects.get('b')
+        async for msg in resp:
+            side_effects.append(msg)
+        assert side_effects
diff --git a/tests/test_pool.py b/tests/test_pool.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f9e65b7738492189a1638431a38b97b4953b510
--- /dev/null
+++ b/tests/test_pool.py
@@ -0,0 +1,105 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
+import asyncio
+import pytest
+
+
+@pytest.mark.asyncio
+async def test_pool_init(connection_pool):
+    await connection_pool.init_pool()
+    assert len(connection_pool._available) == 1
+    await connection_pool.close()
+
+
+@pytest.mark.asyncio
+async def test_acquire_release(connection_pool):
+    conn = await connection_pool.acquire()
+    assert not len(connection_pool._available)
+    assert len(connection_pool._acquired) == 1
+    assert conn.times_acquired == 1
+    connection_pool.release(conn)
+    assert len(connection_pool._available) == 1
+    assert not len(connection_pool._acquired)
+    assert not conn.times_acquired
+    await connection_pool.close()
+
+
+@pytest.mark.asyncio
+async def test_acquire_multiple(connection_pool):
+    conn1 = await connection_pool.acquire()
+    conn2 = await connection_pool.acquire()
+    assert not conn1 is conn2
+    assert len(connection_pool._acquired) == 2
+    await connection_pool.close()
+
+
+@pytest.mark.asyncio
+async def test_share(connection_pool):
+    connection_pool._max_conns = 1
+    conn1 = await connection_pool.acquire()
+    conn2 = await connection_pool.acquire()
+    assert conn1 is conn2
+    assert conn1.times_acquired == 2
+    await connection_pool.close()
+
+
+@pytest.mark.asyncio
+async def test_acquire_multiple_and_share(connection_pool):
+    connection_pool._max_conns = 2
+    connection_pool._max_times_acquired = 2
+    conn1 = await connection_pool.acquire()
+    conn2 = await connection_pool.acquire()
+    assert not conn1 is conn2
+    conn3 = await connection_pool.acquire()
+    conn4 = await connection_pool.acquire()
+    assert not conn3 is conn4
+    assert conn3 is conn1
+    assert conn4 is conn2
+    await connection_pool.close()
+
+
+@pytest.mark.asyncio
+async def test_max_acquired(connection_pool):
+    connection_pool._max_conns = 2
+    connection_pool._max_times_acquired = 2
+    conn1 = await connection_pool.acquire()
+    conn2 = await connection_pool.acquire()
+    conn3 = await connection_pool.acquire()
+    conn4 = await connection_pool.acquire()
+    with pytest.raises(asyncio.TimeoutError):
+        await asyncio.wait_for(connection_pool.acquire(), timeout=0.1)
+    await connection_pool.close()
+
+
+@pytest.mark.asyncio
+async def test_release_notify(connection_pool):
+    connection_pool._max_conns = 2
+    connection_pool._max_times_acquired = 2
+    conn1 = await connection_pool.acquire()
+    conn2 = await connection_pool.acquire()
+    conn3 = await connection_pool.acquire()
+    conn4 = await connection_pool.acquire()
+
+    async def release(conn):
+        conn.release()
+
+    results = await asyncio.gather(
+        *[connection_pool.acquire(), release(conn4)])
+    conn4 = results[0]
+    assert conn4 is conn2
+    await connection_pool.close()
diff --git a/tests/test_properties.py b/tests/test_properties.py
index 3a5c498cbfc09cbb5006c59e618036e4613523b9..d2aad7d5cddb0af84d8193d568463ef52345b1f6 100644
--- a/tests/test_properties.py
+++ b/tests/test_properties.py
@@ -57,7 +57,7 @@ def test_setattr_validation(person):
 def test_set_id_throws(person):
     with pytest.raises(exception.ElementError):
         person.id = 1
-        
+
 
 def test_id_class_attr_throws(person_class):
     with pytest.raises(exception.ElementError):
diff --git a/tests/test_session.py b/tests/test_session.py
index c29ff0c6b8a263da287376919c078dd5f4d6482f..2200eb3db0a2423229b398b6c9a63f82c864363b 100644
--- a/tests/test_session.py
+++ b/tests/test_session.py
@@ -20,328 +20,348 @@
 import pytest
 
 from goblin import element
-from goblin.traversal import bindprop
+from goblin.session import bindprop
+from gremlin_python.process.translator import GroovyTranslator
 
 from gremlin_python.process.graph_traversal import __
 
 
-@pytest.mark.asyncio
-async def test_session_close(session):
-    assert not session.conn.closed
-    await session.close()
-    assert session.conn.closed
-
-
-@pytest.mark.asyncio
-async def test_session_ctxt_mngr(session):
-    async with session:
-        assert not session.conn.closed
-    assert session.conn.closed
+def test_bindprop(person_class):
+    db_val, (binding, val) = bindprop(person_class, 'name', 'dave', binding='n1')
+    assert db_val == 'name'
+    assert binding == 'n1'
+    assert val == 'dave'
 
 
 class TestCreationApi:
 
     @pytest.mark.asyncio
-    async def test_create_vertex(self, session, person_class):
-        async with session:
-            jon = person_class()
-            jon.name = 'jonathan'
-            jon.age = 38
-            leif = person_class()
-            leif.name = 'leifur'
-            leif.age = 28
-            session.add(jon, leif)
-            assert not hasattr(jon, 'id')
-            assert not hasattr(leif, 'id')
-            await session.flush()
-            assert hasattr(jon, 'id')
-            assert session.current[jon.id] is jon
-            assert jon.name == 'jonathan'
-            assert jon.age == 38
-            assert hasattr(leif, 'id')
-            assert session.current[leif.id] is leif
-            assert leif.name == 'leifur'
-            assert leif.age == 28
+    async def test_create_vertex(self, app, person_class):
+        session = await app.session()
+        jon = person_class()
+        jon.name = 'jonathan'
+        jon.age = 38
+        leif = person_class()
+        leif.name = 'leifur'
+        leif.age = 28
+        session.add(jon, leif)
+        assert not hasattr(jon, 'id')
+        assert not hasattr(leif, 'id')
+        await session.flush()
+        assert hasattr(jon, 'id')
+        assert session.current[jon.id] is jon
+        assert jon.name == 'jonathan'
+        assert jon.age == 38
+        assert hasattr(leif, 'id')
+        assert session.current[leif.id] is leif
+        assert leif.name == 'leifur'
+        assert leif.age == 28
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_create_edge(self, session, person_class, place_class,
+    async def test_create_edge(self, app, person_class, place_class,
                                lives_in_class):
-        async with session:
-            jon = person_class()
-            jon.name = 'jonathan'
-            jon.age = 38
-            montreal = place_class()
-            montreal.name = 'Montreal'
-            lives_in = lives_in_class(jon, montreal)
-            session.add(jon, montreal, lives_in)
-            await session.flush()
-            assert hasattr(lives_in, 'id')
-            assert session.current[lives_in.id] is lives_in
-            assert lives_in.source is jon
-            assert lives_in.target is montreal
-            assert lives_in.source.__label__ == 'person'
-            assert lives_in.target.__label__ == 'place'
+        session = await app.session()
+        jon = person_class()
+        jon.name = 'jonathan'
+        jon.age = 38
+        montreal = place_class()
+        montreal.name = 'Montreal'
+        lives_in = lives_in_class(jon, montreal)
+        session.add(jon, montreal, lives_in)
+        await session.flush()
+        assert hasattr(lives_in, 'id')
+        assert session.current[lives_in.id] is lives_in
+        assert lives_in.source is jon
+        assert lives_in.target is montreal
+        assert lives_in.source.__label__ == 'person'
+        assert lives_in.target.__label__ == 'place'
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_create_edge_no_source(self, session, lives_in, person):
-        async with session:
-            lives_in.source = person
-            with pytest.raises(Exception):
-                await session.save(lives_in)
+    async def test_create_edge_no_source(self, app, lives_in, person):
+        session = await app.session()
+        lives_in.source = person
+        with pytest.raises(Exception):
+            await session.save(lives_in)
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_create_edge_no_target(self, session, lives_in, place):
-        async with session:
-            lives_in.target = place
-            with pytest.raises(Exception):
-                await session.save(lives_in)
+    async def test_create_edge_no_target(self, app, lives_in, place):
+        session = await app.session()
+        lives_in.target = place
+        with pytest.raises(Exception):
+            await session.save(lives_in)
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_create_edge_no_source_target(self, session, lives_in):
-        async with session:
-            with pytest.raises(Exception):
-                await session.save(lives_in)
+    async def test_create_edge_no_source_target(self, app, lives_in):
+        session = await app.session()
+        with pytest.raises(Exception):
+            await session.save(lives_in)
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_get_vertex(self, session, person_class):
-        async with session:
-            jon = person_class()
-            jon.name = 'jonathan'
-            jon.age = 38
-            await session.save(jon)
-            jid = jon.id
-            result = await session.get_vertex(jon)
-            assert result.id == jid
-            assert result is jon
+    async def test_get_vertex(self, app, person_class):
+        session = await app.session()
+        jon = person_class()
+        jon.name = 'jonathan'
+        jon.age = 38
+        await session.save(jon)
+        jid = jon.id
+        result = await session.get_vertex(jon)
+        assert result.id == jid
+        assert result is jon
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_get_edge(self, session, person_class, place_class,
+    async def test_get_edge(self, app, person_class, place_class,
                             lives_in_class):
-        async with session:
-            jon = person_class()
-            jon.name = 'jonathan'
-            jon.age = 38
-            montreal = place_class()
-            montreal.name = 'Montreal'
-            lives_in = lives_in_class(jon, montreal)
-            session.add(jon, montreal, lives_in)
-            await session.flush()
-            lid = lives_in.id
-            result = await session.get_edge(lives_in)
-            assert result.id == lid
-            assert result is lives_in
+        session = await app.session()
+        jon = person_class()
+        jon.name = 'jonathan'
+        jon.age = 38
+        montreal = place_class()
+        montreal.name = 'Montreal'
+        lives_in = lives_in_class(jon, montreal)
+        session.add(jon, montreal, lives_in)
+        await session.flush()
+        lid = lives_in.id
+        result = await session.get_edge(lives_in)
+        assert result.id == lid
+        assert result is lives_in
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_get_vertex_doesnt_exist(self, session, person):
-        async with session:
-            person._id = 1000000000000000000000000000000000000000000000
-            result = await session.get_vertex(person)
-            assert not result
+    async def test_get_vertex_doesnt_exist(self, app, person):
+        session = await app.session()
+        person._id = 1000000000000000000000000000000000000000000000
+        result = await session.get_vertex(person)
+        assert not result
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_get_edge_doesnt_exist(self, session, knows, person_class):
-        async with session:
-            jon = person_class()
-            leif = person_class()
-            works_with = knows
-            works_with.source = jon
-            works_with.target = leif
-            works_with._id = 1000000000000000000000000000000000000000000000
-            result = await session.get_edge(works_with)
-            assert not result
+    async def test_get_edge_doesnt_exist(self, app, knows, person_class):
+        session = await app.session()
+        jon = person_class()
+        leif = person_class()
+        works_with = knows
+        works_with.source = jon
+        works_with.target = leif
+        works_with._id = 1000000000000000000000000000000000000000000000
+        result = await session.get_edge(works_with)
+        assert not result
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_remove_vertex(self, session, person):
-        async with session:
-            person.name = 'dave'
-            person.age = 35
-            await session.save(person)
-            result = await session.g.V(person.id).one_or_none()
-            assert result is person
-            rid = result.id
-            await session.remove_vertex(person)
-            result = await session.g.V(rid).one_or_none()
-            assert not result
-
+    async def test_remove_vertex(self, app, person):
+        session = await app.session()
+        person.name = 'dave'
+        person.age = 35
+        await session.save(person)
+        result = await session.g.V(person.id).oneOrNone()
+        assert result is person
+        rid = result.id
+        await session.remove_vertex(person)
+        result = await session.g.V(rid).oneOrNone()
+        assert not result
+        await app.close()
+#
     @pytest.mark.asyncio
-    async def test_remove_edge(self, session, person_class, place_class,
+    async def test_remove_edge(self, app, person_class, place_class,
                                lives_in_class):
-        async with session:
-            jon = person_class()
-            jon.name = 'jonathan'
-            jon.age = 38
-            montreal = place_class()
-            montreal.name = 'Montreal'
-            lives_in = lives_in_class(jon, montreal)
-            session.add(jon, montreal, lives_in)
-            await session.flush()
-            result = await session.g.E(lives_in.id).one_or_none()
-            assert result is lives_in
-            rid = result.id
-            await session.remove_edge(lives_in)
-            result = await session.g.E(rid).one_or_none()
-            assert not result
+        session = await app.session()
+        jon = person_class()
+        jon.name = 'jonathan'
+        jon.age = 38
+        montreal = place_class()
+        montreal.name = 'Montreal'
+        lives_in = lives_in_class(jon, montreal)
+        session.add(jon, montreal, lives_in)
+        await session.flush()
+        result = await session.g.E(lives_in.id).oneOrNone()
+        assert result is lives_in
+        rid = result.id
+        await session.remove_edge(lives_in)
+        result = await session.g.E(rid).oneOrNone()
+        assert not result
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_update_vertex(self, session, person):
-        async with session:
-            person.name = 'dave'
-            person.age = 35
-            result = await session.save(person)
-            assert result.name == 'dave'
-            assert result.age == 35
-            person.name = 'david'
-            person.age = None
-            result = await session.save(person)
-            assert result is person
-            assert result.name == 'david'
-            assert not result.age
+    async def test_update_vertex(self, app, person):
+        session = await app.session()
+        person.name = 'dave'
+        person.age = 35
+        result = await session.save(person)
+        assert result.name == 'dave'
+        assert result.age == 35
+        person.name = 'david'
+        person.age = None
+        result = await session.save(person)
+        assert result is person
+        assert result.name == 'david'
+        assert not result.age
+        await app.close()
 
     @pytest.mark.asyncio
-    async def test_update_edge(self, session, person_class, knows):
-        async with session:
-            dave = person_class()
-            leif = person_class()
-            knows.source = dave
-            knows.target = leif
-            knows.notes = 'online'
-            session.add(dave, leif)
-            await session.flush()
-            result = await session.save(knows)
-            assert knows.notes == 'online'
-            knows.notes = None
-            result = await session.save(knows)
-            assert result is knows
-            assert not result.notes
+    async def test_update_edge(self, app, person_class, knows):
+        session = await app.session()
+        dave = person_class()
+        leif = person_class()
+        knows.source = dave
+        knows.target = leif
+        knows.notes = 'online'
+        session.add(dave, leif)
+        await session.flush()
+        result = await session.save(knows)
+        assert knows.notes == 'online'
+        knows.notes = None
+        result = await session.save(knows)
+        assert result is knows
+        assert not result.notes
+        await app.close()
 
 
 class TestTraversalApi:
 
     @pytest.mark.asyncio
-    async def test_traversal_source_generation(self, session, person_class,
+    async def test_traversal_source_generation(self, app, person_class,
                                                knows_class):
-        async with session:
-            traversal = session.traversal(person_class)
-            assert repr(traversal) == 'g.V().hasLabel("person")'
-            traversal = session.traversal(knows_class)
-            assert repr(traversal) == 'g.E().hasLabel("knows")'
+        session = await app.session()
+        traversal = session.traversal(person_class)
+        translator = GroovyTranslator('g')
+        assert translator.translate(traversal.bytecode) == 'g.V().hasLabel("person")'
+        traversal = session.traversal(knows_class)
+        assert translator.translate(traversal.bytecode) == 'g.E().hasLabel("knows")'
+        await app.close()
 
 
     @pytest.mark.asyncio
-    async def test_all(self, session, person_class):
-        async with session:
-            dave = person_class()
-            leif = person_class()
-            jon = person_class()
-            session.add(dave, leif, jon)
-            await session.flush()
-            resp = await session.traversal(person_class).all()
-            results = []
-            async for msg in resp:
-                assert isinstance(msg, person_class)
-                results.append(msg)
-            assert len(results) > 2
-
-    @pytest.mark.asyncio
-    async def test_one_or_none_one(self, session, person_class):
-        async with session:
-            dave = person_class()
-            leif = person_class()
-            jon = person_class()
-            session.add(dave, leif, jon)
-            await session.flush()
-            resp = await session.traversal(person_class).one_or_none()
-            assert isinstance(resp, person_class)
-
-    @pytest.mark.asyncio
-    async def test_traversal_bindprop(self, session, person_class):
-        async with session:
-            itziri = person_class()
-            itziri.name = 'itziri'
-            result1 = await session.save(itziri)
-            bound_name = bindprop(person_class, 'name', 'itziri', binding='v1')
-            p1 = await session.traversal(person_class).has(
-                *bound_name).one_or_none()
-
-    @pytest.mark.asyncio
-    async def test_one_or_none_none(self, session):
-        async with session:
-            none = await session.g.V().hasLabel(
-                'a very unlikey label').one_or_none()
-            assert not none
-
-    @pytest.mark.asyncio
-    async def test_vertex_deserialization(self, session, person_class):
-        async with session:
-            resp = await session.g.addV('person').property(
-                person_class.name, 'leif').property('place_of_birth', 'detroit').one_or_none()
-            assert isinstance(resp, person_class)
-            assert resp.name == 'leif'
-            assert resp.place_of_birth == 'detroit'
-
-    @pytest.mark.asyncio
-    async def test_edge_desialization(self, session, knows_class):
-        async with session:
-            p1 = await session.g.addV('person').one_or_none()
-            p2 = await session.g.addV('person').one_or_none()
-            e1 = await session.g.V(p1.id).addE('knows').to(
-                session.g.V(p2.id)).property(
-                    knows_class.notes, 'somehow').property(
-                    'how_long', 1).one_or_none()
-            assert isinstance(e1, knows_class)
-            assert e1.notes == 'somehow'
-            assert e1.how_long == 1
-
-    @pytest.mark.asyncio
-    async def test_unregistered_vertex_deserialization(self, session):
-        async with session:
-            dave = await session.g.addV(
-                'unregistered').property('name', 'dave').one_or_none()
-            assert isinstance(dave, element.GenericVertex)
-            assert dave.name == 'dave'
-            assert dave.__label__ == 'unregistered'
-
-    @pytest.mark.asyncio
-    async def test_unregistered_edge_desialization(self, session):
-        async with session:
-            p1 = await session.g.addV('person').one_or_none()
-            p2 = await session.g.addV('person').one_or_none()
-            e1 = await session.g.V(p1.id).addE('unregistered').to(
-                session.g.V(p2.id)).property('how_long', 1).one_or_none()
-            assert isinstance(e1, element.GenericEdge)
-            assert e1.how_long == 1
-            assert e1.__label__ == 'unregistered'
-
-    @pytest.mark.asyncio
-    async def test_property_deserialization(self, session):
-        async with session:
-            p1 = await session.g.addV('person').property(
-                'name', 'leif').one_or_none()
-            name = await session.g.V(p1.id).properties('name').one_or_none()
-            assert name['value'] == 'leif'
-            assert name['label'] == 'name'
-
-    @pytest.mark.asyncio
-    async def test_non_element_deserialization(self, session):
-        async with session:
-            p1 = await session.g.addV('person').property(
-                'name', 'leif').one_or_none()
-            one = await session.g.V(p1.id).count().one_or_none()
-            assert one == 1
-
-    @pytest.mark.asyncio
-    async def test_deserialize_nested_map(self, session, person_class):
-        async with session:
-            await session.g.addV('person').property(
-                person_class.name, 'leif').property('place_of_birth', 'detroit').one_or_none()
-
-            await session.g.addV('person').property(person_class.name, 'David').property(
-                person_class.nicknames, 'davebshow').property(
-                person_class.nicknames, 'Dave').one_or_none()
-
-            resp = await (session.g.V().hasLabel('person')._as('x').valueMap()._as('y')
-                          .select('x', 'y').fold().one_or_none())
-
-            for item in resp:
-                assert isinstance(item['x'], person_class)
-                assert isinstance(item['y'], dict)
+    async def test_all(self, app, person_class):
+        session = await app.session()
+        dave = person_class()
+        leif = person_class()
+        jon = person_class()
+        session.add(dave, leif, jon)
+        await session.flush()
+        resp = session.traversal(person_class)
+        results = []
+        async for msg in resp:
+            assert isinstance(msg, person_class)
+            results.append(msg)
+        assert len(results) > 2
+        await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_oneOrNone_one(self, app, person_class):
+    #     session = await app.session()
+    #     dave = person_class()
+    #     leif = person_class()
+    #     jon = person_class()
+    #     session.add(dave, leif, jon)
+    #     await session.flush()
+    #     resp = await session.traversal(person_class).oneOrNone()
+    #     assert isinstance(resp, person_class)
+    #     await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_traversal_bindprop(self, app, person_class):
+    #     session = await app.session()
+    #     itziri = person_class()
+    #     itziri.name = 'itziri'
+    #     result1 = await session.save(itziri)
+    #     bound_name = bindprop(person_class, 'name', 'itziri', binding='v1')
+    #     p1 = await session.traversal(person_class).has(
+    #     *bound_name).oneOrNone()
+    #     await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_oneOrNone_none(self, app):
+    #     session = await app.session()
+    #     none = await session.g.V().hasLabel(
+    #         'a very unlikey label').oneOrNone()
+    #     assert not none
+    #     await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_vertex_deserialization(self, app, person_class):
+    #     session = await app.session()
+    #     resp = await session.g.addV('person').property(
+    #         person_class.name, 'leif').property('place_of_birth', 'detroit').oneOrNone()
+    #     assert isinstance(resp, person_class)
+    #     assert resp.name == 'leif'
+    #     assert resp.place_of_birth == 'detroit'
+    #     await app.close()
+    #
+    # @pytest.mark.asyncio
+    # async def test_edge_desialization(self, app, knows_class):
+    #     session = await app.session()
+    #     p1 = await session.g.addV('person').oneOrNone()
+    #     p2 = await session.g.addV('person').oneOrNone()
+    #     e1 = await session.g.V(p1.id).addE('knows').to(
+    #     session.g.V(p2.id)).property(
+    #         knows_class.notes, 'somehow').property(
+    #         'how_long', 1).oneOrNone()
+    #     assert isinstance(e1, knows_class)
+    #     assert e1.notes == 'somehow'
+    #     assert e1.how_long == 1
+    #     await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_unregistered_vertex_deserialization(self, app):
+    #     session = await app.session()
+    #     dave = await session.g.addV(
+    #         'unregistered').property('name', 'dave').oneOrNone()
+    #     assert isinstance(dave, element.GenericVertex)
+    #     assert dave.name == 'dave'
+    #     assert dave.__label__ == 'unregistered'
+    #     await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_unregistered_edge_desialization(self, app):
+    #     session = await app.session()
+    #     p1 = await session.g.addV('person').oneOrNone()
+    #     p2 = await session.g.addV('person').oneOrNone()
+    #     e1 = await session.g.V(p1.id).addE('unregistered').to(
+    #     session.g.V(p2.id)).property('how_long', 1).oneOrNone()
+    #     assert isinstance(e1, element.GenericEdge)
+    #     assert e1.how_long == 1
+    #     assert e1.__label__ == 'unregistered'
+    #     await app.close()
+
+    # @pytest.mark.asyncio
+    # async def test_property_deserialization(self, app):
+    #     session = await app.session()
+    #     p1 = await session.g.addV('person').property(
+    #     'name', 'leif').oneOrNone()
+    #     name = await session.g.V(p1.id).properties('name').oneOrNone()
+    #     assert name['value'] == 'leif'
+    #     assert name['label'] == 'name'
+    #     await app.close()
+#
+#     @pytest.mark.asyncio
+#     async def test_non_element_deserialization(self, app):
+#         session = await app.session()
+#         p1 = await session.g.addV('person').property(
+#         'name', 'leif').oneOrNone()
+#         one = await session.g.V(p1.id).count().oneOrNone()
+#         assert one == 1
+#         await app.close()
+#
+#
+#     @pytest.mark.asyncio
+#     async def test_deserialize_nested_map(self, session, person_class):
+#         async with session:
+#             await session.g.addV('person').property(
+#                 person_class.name, 'leif').property('place_of_birth', 'detroit').oneOrNone()
+#
+#             await session.g.addV('person').property(person_class.name, 'David').property(
+#                 person_class.nicknames, 'davebshow').property(
+#                 person_class.nicknames, 'Dave').oneOrNone()
+#
+#             resp = await (session.g.V().hasLabel('person')._as('x').valueMap()._as('y')
+#                           .select('x', 'y').fold().oneOrNone())
+#
+#             for item in resp:
+#                 assert isinstance(item['x'], person_class)
+#                 assert isinstance(item['y'], dict)
diff --git a/tests/test_traversal.py b/tests/test_traversal.py
deleted file mode 100644
index 999821c264c3ffda6585bd5a77dcc43a64388b99..0000000000000000000000000000000000000000
--- a/tests/test_traversal.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2016 ZEROFAIL
-#
-# This file is part of Goblin.
-#
-# Goblin is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Goblin is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
-
-from goblin.traversal import bindprop
-
-
-def test_bindprop(person_class):
-    db_val, (binding, val) = bindprop(person_class, 'name', 'dave', binding='n1')
-    assert db_val == 'name'
-    assert binding == 'n1'
-    assert val == 'dave'
diff --git a/tests/test_vertex_properties_functional.py b/tests/test_vertex_properties_functional.py
index b488bff4f3288b01aff83f2343dc0a71f0f99618..ae9c9997ca3bacfc40268b5df930dc9fade5de9d 100644
--- a/tests/test_vertex_properties_functional.py
+++ b/tests/test_vertex_properties_functional.py
@@ -1,119 +1,139 @@
+# Copyright 2016 ZEROFAIL
+#
+# This file is part of Goblin.
+#
+# Goblin is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Goblin is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.
+
 import pytest
 
 
 @pytest.mark.asyncio
-async def test_add_update_property(session, person):
-    async with session:
-        person.birthplace = 'Iowa City'
-        result = await session.save(person)
-        assert result.birthplace.value == 'Iowa City'
-        person.birthplace = 'unknown'
-        result = await session.save(person)
-        assert result.birthplace.value == 'unknown'
-        person.birthplace = None
-        result = await session.save(person)
-        assert not result.birthplace
+async def test_add_update_property(app, person):
+    session = await app.session()
+    person.birthplace = 'Iowa City'
+    result = await session.save(person)
+    assert result.birthplace.value == 'Iowa City'
+    person.birthplace = 'unknown'
+    result = await session.save(person)
+    assert result.birthplace.value == 'unknown'
+    person.birthplace = None
+    result = await session.save(person)
+    assert not result.birthplace
+    await app.close()
 
 
 @pytest.mark.asyncio
-async def test_add_update_list_card_property(session, person):
-    async with session:
-        person.nicknames = ['db', 'dirtydb']
-        result = await session.save(person)
-        assert [v.value for v in result.nicknames] == ['db', 'dirtydb']
-        person.nicknames.append('davebshow')
-        result = await session.save(person)
-        assert [v.value for v in result.nicknames] == [
-            'db', 'dirtydb', 'davebshow']
-        person.nicknames = []
-        result = await session.save(person)
-        assert not result.nicknames
-        person.nicknames = ['none']
-        result = await session.save(person)
-        assert result.nicknames('none').value == 'none'
-        person.nicknames = None
-        result = await session.save(person)
-        assert not result.nicknames
+async def test_add_update_list_card_property(app, person):
+    session = await app.session()
+    person.nicknames = ['db', 'dirtydb']
+    result = await session.save(person)
+    assert [v.value for v in result.nicknames] == ['db', 'dirtydb']
+    person.nicknames.append('davebshow')
+    result = await session.save(person)
+    assert [v.value for v in result.nicknames] == [
+        'db', 'dirtydb', 'davebshow']
+    person.nicknames = []
+    result = await session.save(person)
+    assert not result.nicknames
+    person.nicknames = ['none']
+    result = await session.save(person)
+    assert result.nicknames('none').value == 'none'
+    person.nicknames = None
+    result = await session.save(person)
+    assert not result.nicknames
+    await app.close()
 
 
 @pytest.mark.asyncio
-async def test_add_update_set_card_property(session, place):
-    async with session:
-        place.important_numbers = set([1, 2])
-        result = await session.save(place)
-        assert {v.value for v in result.important_numbers} == {1, 2}
-        place.important_numbers = set([3, 4])
-        result = await session.save(place)
-        assert {v.value for v in result.important_numbers} == {3, 4}
-        place.important_numbers.add(5)
-        result = await session.save(place)
-        assert {v.value for v in result.important_numbers} == {3, 4, 5}
-        place.important_numbers = set()
-        result = await session.save(place)
-        assert not result.important_numbers
-        place.important_numbers = set([1, 2])
-        result = await session.save(place)
-        assert place.important_numbers(1).value == 1
-        place.important_numbers = None
-        result = await session.save(place)
-        assert not result.important_numbers
+async def test_add_update_set_card_property(app, place):
+    session = await app.session()
+    place.important_numbers = set([1, 2])
+    result = await session.save(place)
+    assert {v.value for v in result.important_numbers} == {1, 2}
+    place.important_numbers = set([3, 4])
+    result = await session.save(place)
+    assert {v.value for v in result.important_numbers} == {3, 4}
+    place.important_numbers.add(5)
+    result = await session.save(place)
+    assert {v.value for v in result.important_numbers} == {3, 4, 5}
+    place.important_numbers = set()
+    result = await session.save(place)
+    assert not result.important_numbers
+    place.important_numbers = set([1, 2])
+    result = await session.save(place)
+    assert place.important_numbers(1).value == 1
+    place.important_numbers = None
+    result = await session.save(place)
+    assert not result.important_numbers
+    await app.close()
 
 
 @pytest.mark.asyncio
-async def test_add_update_metas(session, place):
-    async with session:
-        place.historical_name = ['Detroit']
-        place.historical_name('Detroit').notes = 'rock city'
-        place.historical_name('Detroit').year = 1900
-        result = await session.save(place)
-        assert result.historical_name('Detroit').notes == 'rock city'
-        assert result.historical_name('Detroit').year == 1900
-
-        place.historical_name('Detroit').notes = 'comeback city'
-        place.historical_name('Detroit').year = 2016
-        result = await session.save(place)
-        assert result.historical_name('Detroit').notes == 'comeback city'
-        assert result.historical_name('Detroit').year == 2016
-
-        place.historical_name('Detroit').notes = None
-        place.historical_name('Detroit').year = None
-        result = await session.save(place)
-        assert not result.historical_name('Detroit').notes
-        assert not result.historical_name('Detroit').year
-
-
+async def test_add_update_metas(app, place):
+    session = await app.session()
+    place.historical_name = ['Detroit']
+    place.historical_name('Detroit').notes = 'rock city'
+    place.historical_name('Detroit').year = 1900
+    result = await session.save(place)
+    assert result.historical_name('Detroit').notes == 'rock city'
+    assert result.historical_name('Detroit').year == 1900
+
+    place.historical_name('Detroit').notes = 'comeback city'
+    place.historical_name('Detroit').year = 2016
+    result = await session.save(place)
+    assert result.historical_name('Detroit').notes == 'comeback city'
+    assert result.historical_name('Detroit').year == 2016
+
+    place.historical_name('Detroit').notes = None
+    place.historical_name('Detroit').year = None
+    result = await session.save(place)
+    assert not result.historical_name('Detroit').notes
+    assert not result.historical_name('Detroit').year
+    await app.close()
 
 
 @pytest.mark.asyncio
-async def test_add_update_metas_list_card(session, place):
-    async with session:
-        place.historical_name = ['Hispania', 'Al-Andalus']
-        place.historical_name('Hispania').notes = 'romans'
-        place.historical_name('Hispania').year = 200
-        place.historical_name('Al-Andalus').notes = 'muslims'
-        place.historical_name('Al-Andalus').year = 700
-        result = await session.save(place)
-        assert result.historical_name('Hispania').notes == 'romans'
-        assert result.historical_name('Hispania').year == 200
-        assert result.historical_name('Al-Andalus').notes == 'muslims'
-        assert result.historical_name('Al-Andalus').year == 700
-
-        place.historical_name('Hispania').notes = 'really old'
-        place.historical_name('Hispania').year = 200
-        place.historical_name('Al-Andalus').notes = 'less old'
-        place.historical_name('Al-Andalus').year = 700
-        result = await session.save(place)
-        assert result.historical_name('Hispania').notes == 'really old'
-        assert result.historical_name('Hispania').year == 200
-        assert result.historical_name('Al-Andalus').notes == 'less old'
-        assert result.historical_name('Al-Andalus').year == 700
-
-        place.historical_name('Hispania').notes = None
-        place.historical_name('Hispania').year = None
-        place.historical_name('Al-Andalus').notes = None
-        place.historical_name('Al-Andalus').year = None
-        result = await session.save(place)
-        assert not result.historical_name('Hispania').notes
-        assert not result.historical_name('Hispania').year
-        assert not result.historical_name('Al-Andalus').notes
-        assert not result.historical_name('Al-Andalus').year
+async def test_add_update_metas_list_card(app, place):
+    session = await app.session()
+    place.historical_name = ['Hispania', 'Al-Andalus']
+    place.historical_name('Hispania').notes = 'romans'
+    place.historical_name('Hispania').year = 200
+    place.historical_name('Al-Andalus').notes = 'muslims'
+    place.historical_name('Al-Andalus').year = 700
+    result = await session.save(place)
+    assert result.historical_name('Hispania').notes == 'romans'
+    assert result.historical_name('Hispania').year == 200
+    assert result.historical_name('Al-Andalus').notes == 'muslims'
+    assert result.historical_name('Al-Andalus').year == 700
+
+    place.historical_name('Hispania').notes = 'really old'
+    place.historical_name('Hispania').year = 200
+    place.historical_name('Al-Andalus').notes = 'less old'
+    place.historical_name('Al-Andalus').year = 700
+    result = await session.save(place)
+    assert result.historical_name('Hispania').notes == 'really old'
+    assert result.historical_name('Hispania').year == 200
+    assert result.historical_name('Al-Andalus').notes == 'less old'
+    assert result.historical_name('Al-Andalus').year == 700
+
+    place.historical_name('Hispania').notes = None
+    place.historical_name('Hispania').year = None
+    place.historical_name('Al-Andalus').notes = None
+    place.historical_name('Al-Andalus').year = None
+    result = await session.save(place)
+    assert not result.historical_name('Hispania').notes
+    assert not result.historical_name('Hispania').year
+    assert not result.historical_name('Al-Andalus').notes
+    assert not result.historical_name('Al-Andalus').year
+    await app.close()