You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by da...@apache.org on 2017/11/27 17:52:41 UTC
[05/11] tinkerpop git commit: added core GraphSON types UUID, Date,
and Timestamp for GraphSON 3
added core GraphSON types UUID, Date, and Timestamp for GraphSON 3
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/5031ccdb
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/5031ccdb
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/5031ccdb
Branch: refs/heads/master
Commit: 5031ccdbea28b1384c40fd522e9fec25b5b86818
Parents: d6f031f c0078cc
Author: davebshow <da...@gmail.com>
Authored: Wed Nov 15 10:12:22 2017 -0800
Committer: davebshow <da...@gmail.com>
Committed: Wed Nov 15 10:12:22 2017 -0800
----------------------------------------------------------------------
CHANGELOG.asciidoc | 2 +
.../upgrade/release-3.2.x-incubating.asciidoc | 9 ++
docs/src/upgrade/release-3.3.x.asciidoc | 9 ++
.../src/main/jython/gremlin_python/statics.py | 9 ++
.../gremlin_python/structure/io/graphsonV2d0.py | 71 +++++++++++++++-
.../gremlin_python/structure/io/graphsonV3d0.py | 75 ++++++++++++++++-
.../tests/structure/io/test_graphsonV2d0.py | 87 ++++++++++++++++++--
.../tests/structure/io/test_graphsonV3d0.py | 83 ++++++++++++++++++-
8 files changed, 331 insertions(+), 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5031ccdb/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --cc CHANGELOG.asciidoc
index ce65af6,acfa892..49c9a5b
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@@ -246,11 -23,8 +246,13 @@@ image::https://raw.githubusercontent.co
[[release-3-2-7]]
=== TinkerPop 3.2.7 (Release Date: NOT OFFICIALLY RELEASED YET)
+ * Added core GraphSON classes for Gremlin-Python: `UUID`, `Date`, and `Timestamp`.
+* Provided a method to configure detachment options with `EventStrategy`.
+* Fixed a race condition in `TinkerIndex`.
+* Fixed an `ArrayOutOfBoundsException` in `hasId()` for the rare situation when the provided collection is empty.
+* Bump to Netty 4.0.52
+* `TraversalVertexProgram` `profile()` now accounts for worker iteration in `GraphComputer` OLAP.
+ * `TraversalVertexProgram` ``profile()` now accounts for worker iteration in `GraphComputer` OLAP.
* Added a test for self-edges and fixed `Neo4jVertex` to provided repeated self-edges on `BOTH`.
* Better respected permissions on the `plugins.txt` file and prevented writing if marked as read-only.
* Added getters for the lambdas held by `LambdaCollectingBarrierStep`, `LambdaFlatMapStep` and `LambdaSideEffectStep`.
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5031ccdb/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --cc docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 5564eec,9be51f9..8745ce5
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@@ -29,16 -29,15 +29,25 @@@ image::https://raw.githubusercontent.co
Please see the link:https://github.com/apache/tinkerpop/blob/3.2.7/CHANGELOG.asciidoc#release-3-2-7[changelog] for a complete list of all the modifications that are part of this release.
+ ==== Gremlin-Python Core Types
+ With the addition of `UUID`, `Date`, and `Timestamp`, Gremlin-Python now implements serializers for all core GraphSON types. Users
+ that were using other types to represent this data can now use the Python classes `datetime.datetime` and`uuid.UUID` in GLV traversals.
+ Since Python does not support a native `Timestamp` object, Gremlin-Python now offers a dummy class `Timestamp`, which allows
+ users to wrap a float and submit it to the Gremlin Server as a `Timestamp` GraphSON type. `Timestamp` can be found in
+ `gremlin_python.statics`.
+
+ See: link:https://issues.apache.org/jira/browse/TINKERPOP-1807[TINKERPOP-1807]
+
+==== EventStrategy Detachment
+
+`EventStrategy` forced detachment of mutated elements prior to raising them in events. While this was a desired
+outcome, it may not have always fit every use case. For example, a user may have wanted a reference element or the
+actual element itself. As a result, `EventStrategy` has changed to allow it to be constructed with a `detach()`
+option, where it is possible to specify any of the following: `null` for no detachment, `DetachedFactory` for the
+original behavior, and `ReferenceFactory` for detachment that returns reference elements.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1829[TINKERPOP-1829]
+
==== Embedded Remote Connection
As Gremlin Language Variants (GLVs) expand their usage and use of `withRemote()` becomes more common, the need to mock
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5031ccdb/docs/src/upgrade/release-3.3.x.asciidoc
----------------------------------------------------------------------
diff --cc docs/src/upgrade/release-3.3.x.asciidoc
index 80aff8b,0000000..4aaca6a
mode 100644,000000..100644
--- a/docs/src/upgrade/release-3.3.x.asciidoc
+++ b/docs/src/upgrade/release-3.3.x.asciidoc
@@@ -1,688 -1,0 +1,697 @@@
+////
+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.
+////
+
+= TinkerPop 3.3.0
+
+image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/images/gremlin-mozart.png[width=225]
+
+*Gremlin Symphony #40 in G Minor*
+
+== TinkerPop 3.3.1
+
+*Release Date: NOT OFFICIALLY RELEASED YET*
+
+Please see the link:https://github.com/apache/tinkerpop/blob/3.3.1/CHANGELOG.asciidoc#release-3-3-1[changelog] for a complete list of all the modifications that are part of this release.
+
+=== Upgrading for Users
+
++==== Gremlin-Python Core Types
++With the addition of `UUID`, `Date`, and `Timestamp`, Gremlin-Python now implements serializers for all core GraphSON types. Users
++that were using other types to represent this data can now use the Python classes `datetime.datetime` and`uuid.UUID` in GLV traversals.
++Since Python does not support a native `Timestamp` object, Gremlin-Python now offers a dummy class `Timestamp`, which allows
++users to wrap a float and submit it to the Gremlin Server as a `Timestamp` GraphSON type. `Timestamp` can be found in
++`gremlin_python.statics`.
++
++See: link:https://issues.apache.org/jira/browse/TINKERPOP-1807[TINKERPOP-1807]
++
+==== Gremlin Python path()
+
+There was a bug in GraphSON 3.0 serialization that prevented proper handling of results contain `Path` object. As a
+result, traversals that used and returned results from the `path()` step in Python would return unusable results,
+but did not actually cause an exception condition. This problem is now resolved.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1799[TINKERPOP-1799]
+
+==== Added `math()`-step for Scientific Traversal Computing
+
+`GraphTraversal.math(String)` was added. This step provides scientific calculator capabilities to a Gremlin traversal.
+
+[source,groovy]
+----
+gremlin> g.V().as('a').out('knows').as('b').math('a + b').by('age')
+==>56.0
+==>61.0
+gremlin> g.V().as('a').out('created').as('b').
+......1> math('b + a').
+......2> by(both().count().math('_ + 100')).
+......3> by('age')
+==>132.0
+==>133.0
+==>135.0
+==>138.0
+gremlin> g.withSack(1).V(1).repeat(sack(sum).by(constant(1))).times(10).emit().sack().math('sin _')
+==>0.9092974268256817
+==>0.1411200080598672
+==>-0.7568024953079282
+==>-0.9589242746631385
+==>-0.27941549819892586
+==>0.6569865987187891
+==>0.9893582466233818
+==>0.4121184852417566
+==>-0.5440211108893698
+==>-0.9999902065507035
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1632[TINKERPOP-1632]
+
+==== Changed Typing on `from()` and `to()`
+
+The `from()` and `to()` steps of `GraphTraversal` have a `Traversal<E,Vertex>` overload. The `E` has been changed to `?`
+in order to reduce `< >`-based coersion in strongly type Gremlin language variants.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1793[TINKERPOP-1793]
+
+==== addV(traversal) and addE(traversal)
+
+The `GraphTraversal` and `GraphTraversalSource` methods of `addV()` and `addE()` have been extended to support dynamic
+label determination upon element creation. Both these methods can take a `Traversal<?, String>` where the first `String`
+returned by the traversal is used as the label of the respective element.
+
+[source,groovy]
+----
+gremlin> g = TinkerFactory.createModern().traversal()
+==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
+gremlin> g.addV(V().has('name','marko').label()).
+ property('name','stephen')
+==>v[13]
+gremlin> g.V().has('name','stephen').valueMap(true)
+==>[name:[stephen],label:person,id:13]
+gremlin> g.V().has('name','stephen').
+ addE(V().hasLabel('software').inE().label()).
+ to(V().has('name','lop'))
+==>e[15][13-created->3]
+gremlin> g.V().has('name','stephen').outE().valueMap(true)
+==>[label:created,id:15]
+gremlin>
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1793[TINKERPOP-1793]
+
+==== PageRankVertexProgram
+
+There were two major bugs in the way in which PageRank was being calculated in `PageRankVertexProgram`. First, teleportation
+energy was not being distributed correctly amongst the vertices at each round. Second, terminal vertices (i.e. vertices
+with no outgoing edges) did not have their full gathered energy distributed via teleportation.
+
+For users upgrading, note that while the relative rank orders will remain "the same," the actual PageRank values will differ
+from prior TinkerPop versions.
+
+```
+VERTEX iGRAPH TINKERPOP
+marko 0.1119788 0.11375485828040575
+vadas 0.1370267 0.14598540145985406
+lop 0.2665600 0.30472082661863686
+josh 0.1620746 0.14598540145985406
+ripple 0.2103812 0.1757986539008437
+peter 0.1119788 0.11375485828040575
+```
+
+Normalization preserved through computation:
+
+```
+0.11375485828040575 +
+0.14598540145985406 +
+0.30472082661863686 +
+0.14598540145985406 +
+0.1757986539008437 +
+0.11375485828040575
+==>1.00000000000000018
+```
+
+Two other additions to `PageRankVertexProgram` were provided as well.
+
+1. It now calculates the vertex count and thus, no longer requires the user to specify the vertex count.
+2. It now allows the user to leverage an epsilon-based convergence instead of having to specify the number of iterations to execute.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1783[TINKERPOP-1783]
+
+==== IO Defaults
+
+While 3.3.0 released Gryo 3.0 and GraphSON 3.0 and these versions were defaulted in a number of places, it seems that
+some key defaults were missed. Specifically, calls to `Graph.io(graphson())` and `Graph.io(gryo())` were still using
+the old versions. The defaults have now been changed to ensure 3.0 is properly referenced in those cases.
+
+==== Upgrade Neo4j
+
+See Neo4j's link:https://neo4j.com/guides/upgrade/[3.2 Upgrade FAQ] for a complete guide on how to upgrade from the previous 2.3.3 version. Also note that many of the configuration settings have link:https://neo4j.com/developer/kb/manually-migrating-configuration-settings-from-neo4j-2x-to-neo4j-3x/[changed from neo4j 2x to 3x]
+
+In particular, these properties referenced in TinkerPop documentation and configuration were renamed:
+
+[width="100%",cols="2",options="header"]
+|=========================================================
+|Neo4j 2.3 (TinkerPop \<= 3.3.0) |Neo4j 3.2 (TinkerPop 3.3.1)
+|node_auto_indexing |dbms.auto_index.nodes.enabled
+|relationship_auto_indexing |dbms.auto_index.relationships.enabled
+|ha.cluster_server |ha.host.coordination
+|ha.server |ha.host.data
+|=========================================================
+
+
+=== Upgrading for Providers
+
+IMPORTANT: It is recommended that providers also review all the upgrade instructions specified for users. Many of the
+changes there may prove important for the provider's implementation.
+
+==== Graph Database Providers
+
+===== IO Version Check
+
+In the `Graph.io()` method, providers are to bootstrap the `Io` instance returned with their own custom serializers
+typically provided through a custom `IoRegistry` instance. Prior to this change it was not possible to easily determine
+the version of `Io` that was expected (nor was it especially necessary as TinkerPop didn't have breaking format changes
+between versions). As of 3.3.0 however, there could be IO test incompatibilities for some providers who need to
+register a different `IoRegistry` instance depending on the version the user wants.
+
+To allow for that check, the `Io` interface now has the following method:
+
+[source,java]
+----
+public <V> boolean requiresVersion(final V version);
+----
+
+which allows the graph provider to check if a specific `GryoVersion` or `GraphSONVersion` is required. Using that
+information, the provider could then assign the right `IoRegistry` to match that.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1767[TINKERPOP-1767]
+
+
+== TinkerPop 3.3.0
+
+*Release Date: August 21, 2017*
+
+Please see the link:https://github.com/apache/tinkerpop/blob/3.3.0/CHANGELOG.asciidoc#release-3-3-0[changelog] for a complete list of all the modifications that are part of this release.
+
+=== Upgrading for Users
+
+==== Packaged Data Files
+
+TinkerPop has always packaged sample graphs with its zip distributions. As of 3.3.0, the distributions will only
+include Gryo 3.0, GraphSON 3.0 and GraphML (which is unversioned) files. Other versions are not included, but could
+obviously be generated using the IO API directly.
+
+==== GraphTraversal Has-Methods Re-Organized
+
+`GraphTraversal.hasXXX()`, where `XXX` is `Id`, `Label`, `Key`, `Value`, was faulty in that they relied on calling an
+intermediate method for flattening `Object[]` arguments and thus, yielding a non 1-to-1 correspondence between `GraphTraversal`
+and `Bytecode`. This has been remedied. Most users will not notice this change. Perhaps only some users that may use
+Java reflection over `GraphTraversal` might have a simple problem.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1520[TINKERPOP-1520]
+
+==== Changes to IO
+
+===== Gryo 3.0
+
+With Gryo, TinkerPop skips version 2.0 and goes right to 3.0 (to maintain better parity with GraphSON versioning).
+Gryo 3.0 fixes a number of inconsistencies with Gryo 1.0 and hopefully marks a point where Gryo is better versioned
+over time. Gryo 3.0 is not compatible with Gryo 1.0 and is now the default version of Gryo exposed by TinkerPop in
+Gremlin Server and IO.
+
+It isn't hard to switch back to use of Gryo 1.0 if necessary. Here is the approach for writing an entire graph:
+
+[source,java]
+----
+Graph graph = TinkerFactory.createModern();
+GryoMapper mapper = graph.io(IoCore.gryo()).mapper().version(GryoVersion.V1_0).create()
+try (OutputStream os = new FileOutputStream("tinkerpop-modern.json")) {
+ graph.io(IoCore.gryo()).writer().mapper(mapper).create().writeGraph(os, graph)
+}
+
+final Graph newGraph = TinkerGraph.open();
+try (InputStream stream = new FileInputStream("tinkerpop-modern.json")) {
+ newGraph.io(IoCore.gryo()).reader().mapper(mapper).create().readGraph(stream, newGraph);
+}
+----
+
+Gremlin Server configurations don't include Gryo 1.0 by default:
+
+[source,yaml]
+----
+serializers:
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }} # application/vnd.gremlin-v3.0+gryo
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }} # application/vnd.gremlin-v3.0+gryo-stringd
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }} # application/json
+----
+
+but adding an entry as follows will add it back:
+
+[source,yaml]
+----
+serializers:
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0] }} # application/vnd.gremlin-v1.0+gryo
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }} # application/vnd.gremlin-v3.0+gryo
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }} # application/vnd.gremlin-v3.0+gryo-stringd
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }} # application/json
+----
+
+To use Gryo 1.0 with the Java driver, just specify the 1.0 serializer directly:
+
+[source,java]
+----
+GryoMapper.Builder builder = GryoMapper.build().
+ version(GryoVersion.V1_0).
+ addRegistry(TinkerIoRegistryV1d0.instance());
+Cluster cluster = Cluster.build().serializer(GryoMessageSerializerV1d0(builder));
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1698[TINKERPOP-1698]
+
+===== GraphSON 3.0
+
+GraphSON 3.0 finishes what GraphSON 2.0 began by taking the extra step to include the following types: `g:Map`,
+`g:List` and `g:Set`. With these types it is now possible to get expected Gremlin results in GLVs just as one would
+if using Java. This is especially true of the `g:Map` type, which allows non-string keys values, something not allowed
+in regular JSON maps. This allows for common traversals like `g.V().groupCount()` to work, where the traversal groups
+on a `Vertex` or some other complex object.
+
+Note that GraphSON 3.0 does not have an option to be without types. This was a feature of 1.0 and 2.0, but it is no
+longer supported. There is little point to such a feature as we see more movement toward GLVs, which require types,
+and less usage of scripts with custom parsing of results.
+
+Both TinkerGraph and Gremlin Server have been defaulted to work with GraphSON 3.0. For TinkerGraph this means that
+the following commands:
+
+[source,java]
+----
+Graph graph = TinkerFactory.createModern();
+graph.io(IoCore.graphson()).writeGraph("tinkerpop-modern.json");
+
+final Graph newGraph = TinkerGraph.open();
+newGraph.io(IoCore.graphson()).readGraph("tinkerpop-modern.json");
+----
+
+will write and read GraphSON 3.0 format rather than 1.0. To use 1.0 (or 2.0 for that matter) format simply set the
+`version()` on the appropriate builder methods:
+
+[source,java]
+----
+Graph graph = TinkerFactory.createModern();
+GraphSONMapper mapper = graph.io(IoCore.graphson()).mapper().version(GraphSONVersion.V1_0).create()
+try (OutputStream os = new FileOutputStream("tinkerpop-modern.json")) {
+ graph.io(IoCore.graphson()).writer().mapper(mapper).create().writeGraph(os, graph)
+}
+
+final Graph newGraph = TinkerGraph.open();
+try (InputStream stream = new FileInputStream("tinkerpop-modern.json")) {
+ newGraph.io(IoCore.graphson()).reader().mapper(mapper).create().readGraph(stream, newGraph);
+}
+----
+
+For Gremlin Server, this change means that the `application/json` mime type no longer returns GraphSON 1.0 without
+type embedding. Instead, Gremlin Server will return GraphSON 3.0 with partial types enabled (i.e. which is equivalent
+to `application/vnd.gremlin-v3.0+json`). The `serializers` section the sample Gremlin Server YAML files now typically
+look like this:
+
+[source,yaml]
+----
+serializers:
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }} # application/vnd.gremlin-v3.0+gryo
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }} # application/vnd.gremlin-v3.0+gryo-stringd
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0] }} # application/json
+----
+
+It is possible to bring back the original configuration for `application/json` by changing the last entry as follows:
+
+[source,yaml]
+----
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }} # application/vnd.gremlin-v3.0+gryo
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }} # application/vnd.gremlin-v3.0+gryo-stringd
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0] }} # application/json
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1414[TINKERPOP-1414],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1427[TINKERPOP-1427],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1574[TINKERPOP-1574]
+
+==== Graphite and Ganglia
+
+Graphite and Ganglia are no longer packaged with the Gremlin Server distribution. They are considered optional
+dependencies and therefore must be installed manually by the user.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1550[TINKERPOP-1550],
+link:http://tinkerpop.apache.org/docs/3.3.0/reference/#metrics[Reference Documentation - Metrics]
+
+==== SelectStep Defaults to Pop.last
+
+`SelectStep` and `SelectOneStep` (`select()`) are the only `Scoping` steps that default to `Pop.mixed` as their labeled path
+selection criteria. All other steps, like `match()`, `where()` and `dedup()`, use `Pop.last`. In order to better enable optimizations
+around total `Pop.last` traversals, the `select()`-steps now default to `Pop.last`. Most users will not notice a difference as
+it is rare for repeated labels to be used in practice. However, formal backwards compatibility is possible as outlined below.
+
+Assuming that `x` is not a `Pop` argument:
+
+1. Change all `select(x,y,z)` calls to `selectV3d2(x,y,z)` calls.
+2. Change all `select(x,y,z)`-step calls to `select(Pop.mixed,x,y,z)`.
+
+If an explicit `Pop` argument is provided, then no changes are required.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1541[TINKERPOP-1541]
+
+==== OptionalStep and Side-Effects
+
+The `optional()`-step was previously implemented using `ChooseStep`. However, if the optional branch contained side-effects,
+then unexpected behaviors can emerge. Thus, a potential backwards compatibility issue arises if side-effects were being
+used in `optional()`. However, the behavior would be unpredictable so this backwards incompatibility is desirable.
+
+See link:https://issues.apache.org/jira/browse/TINKERPOP-1506[TINKERPOP-1506]
+
+==== Gremlin Console Initialization
+
+It is no longer possible to intialize the Gremlin Console with a script without use of `-e`. In other words, prior
+versions allowed:
+
+[source,text]
+bin/gremlin.sh gremlin.groovy
+
+Such a command must now be written as:
+
+[source,text]
+bin/gremlin.sh -i gremlin.groovy
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1283[TINKERPOP-1283],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1651[TINKERPOP-1651]
+
+==== GraphTraversal valueMap() Signature Updated
+
+`GraphTraversal.valueMap(includeTokens,propertyKeys...)` now returns a `Map<Object,E>` to account for the presence of `T.id` or `T.label` if you pass `true` to it.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1283[TINKERPOP-1483]
+
+==== HADOOP_GREMLIN_LIBS and Spark
+
+The TinkerPop reference documentation has always mentioned that the `gremlin-spark` `/lib` directory needed to be
+added to `HADOOP_GREMLIN_LIBS` environment variable. In reality, that was not truly necessary. With Spark 1.x having
+`gremlin-spark` in `HADOOP_GREMLIN_LIBS` hasn't been a problem, but Spark 2.0 introduces a check for duplicate jars
+on the path which will cause job initialization to fail. As a result, going forward with TinkerPop 3.3.0, the
+`gremlin-spark` `lib` directory should not be included in `HADOOP_GREMLIN_LIBS`.
+
+==== Deprecation Removal
+
+The following deprecated classes, methods or fields have been removed in this version:
+
+* `giraph-gremlin`
+** `org.apache.tinkerpop.gremlin.giraph.groovy.plugin.GiraphGremlinPlugin`
+* `gremlin-console`
+** `org.apache.tinkerpop.gremlin.console.Console(String)`
+** `org.apache.tinkerpop.gremlin.console.ConsoleImportCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.console.plugin.*`
+** `org.apache.tinkerpop.gremlin.console.groovy.plugin.DriverGremlinPlugin`
+** `org.apache.tinkerpop.gremlin.console.groovy.plugin.DriverRemoteAcceptor`
+** `org.apache.tinkerpop.gremlin.console.groovy.plugin.GephiGremlinPlugin`
+** `org.apache.tinkerpop.gremlin.console.groovy.plugin.UtilitiesGremlinPlugin`
+* `gremlin-core`
+** `org.apache.tinkerpop.gremlin.jsr223.CoreGremlinModule`
+** `org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin#INSTANCE`
+** `org.apache.tinkerpop.gremlin.jsr223.GremlinModule`
+** `org.apache.tinkerpop.gremlin.jsr223.SingleGremlinScriptEngineManager#getInstance()`
+** `org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineManager#addModule(GremlinModule)`
+** `org.apache.tinkerpop.gremlin.jsr223.console.PluginAcceptor`
+** `org.apache.tinkerpop.gremlin.process.traversal.TraversalSource.Builder`
+** `org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP(P...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.util.AndP(P...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.util.OrP(P...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.util.TraversalScriptFunction`
+** `org.apache.tinkerpop.gremlin.process.traversal.util.TraversalScriptHelper`
+** `org.apache.tinkerpop.gremlin.process.traversal.Order.keyIncr`
+** `org.apache.tinkerpop.gremlin.process.traversal.Order.valueIncr`
+** `org.apache.tinkerpop.gremlin.process.traversal.Order.keyDecr`
+** `org.apache.tinkerpop.gremlin.process.traversal.Order.valueIncr`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.GraphTraversal.mapKeys()`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.GraphTraversal.mapValues()`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#addV(Object...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#addE(Direction, String, String, Object...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#addOutE(String, String, Object...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#addInV(String, String, Object...)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#selectV3d2()`
+** `org.apache.tinkerpop.gremlin.process.traversal.Bindings()`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource#withBindings(Bindings)`
+** `org.apache.tinkerpop.gremlin.structure.Transaction.submit(Function)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#sack(BiFunction,String)`
+** `org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.LazyBarrierStrategy`
+** `org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects` (various methods)
+** `org.apache.tinkerpop.gremlin.process.computer.traversal.step.VertexComputing#generateComputer(Graph)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#groupV3d0(String)`
+** `org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#groupV3d0()`
+** `org.apache.tinkerpop.gremlin.structure.Graph.Features.VertexPropertyFeatures#supportsAddProperty()`
+** `org.apache.tinkerpop.gremlin.structure.Graph.Features.VertexPropertyFeatures#FEATURE_ADD_PROPERTY`
+** `org.apache.tinkerpop.gremlin.structure.Graph.OptIn#SUITE_GROOVY_PROCESS_STANDARD`
+** `org.apache.tinkerpop.gremlin.structure.Graph.OptIn#SUITE_GROOVY_PROCESS_COMPUTER`
+** `org.apache.tinkerpop.gremlin.structure.Graph.OptIn#SUITE_GROOVY_ENVIRONMENT`
+** `org.apache.tinkerpop.gremlin.structure.Graph.OptIn#SUITE_GROOVY_ENVIRONMENT_INTEGRATE`
+** `org.apache.tinkerpop.gremlin.structure.io.Io.Builder#registry(IoRegistry)`
+** `org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper.Builder#embedTypes(boolean)`
+** `org.apache.tinkerpop.gremlin.structure.Transaction.submit(Function)`
+** `org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge(Object,String,Map,Pair,Pair)`
+** `org.apache.tinkerpop.gremlin.util.CoreImports`
+** `org.apache.tinkerpop.gremlin.util.ScriptEngineCache`
+** `org.apache.tinkerpop.gremlin.process.computer.util.ConfigurationTraversal`
+* `gremlin-driver`
+** `org.apache.tinkerpop.gremlin.driver.Cluster$Builder#reconnectIntialDelay(int)`
+** `org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0(GryoMapper)`
+** `org.apache.tinkerpop.gremlin.driver.ser.AbstractGraphSONMessageSerializerV2d0#TOKEN_USE_MAPPER_FROM_GRAPH`
+** `org.apache.tinkerpop.gremlin.driver.ser.AbstractGryoSONMessageSerializerV2d0#TOKEN_USE_MAPPER_FROM_GRAPH`
+* `gremlin-groovy`
+** `org.apache.tinkerpop.gremlin.groovy.AbstractImportCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.groovy.DefaultImportCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.groovy.EmptyImportCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.groovy.ImportCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.groovy.NoImportCustomizerProvider`
+** `org.apache.tinkerpop.gremlin.groovy.engine.ConcurrentBindings`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor#build(String,List,List,List,Map)`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor#getScriptEngines()`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor#getGlobalBindings()`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.Builder#enabledPlugins(Set)`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.Builder#addEngineSettings(String,List,List,List,Map)`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.Builder#engineSettings(Map)`
+** `org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.Builder#use(List)`
+** `org.apache.tinkerpop.gremlin.groovy.engine.ScriptEngines`
+** `org.apache.tinkerpop.gremlin.groovy.function.*`
+** `org.apache.tinkerpop.gremlin.groovy.plugin.*`
+** `org.apache.tinkerpop.gremlin.groovy.plugin.credential.*`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.DependencyManager`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine(ImportCustomizerProvider)`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine(CompilerCustomizerProvider)`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine#plugins()`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.ScriptExecutor`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.ScriptEnginePluginAcceptor`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.SandboxExtension`
+** `org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.*`
+** `org.apache.tinkerpop.gremlin.groovy.util.DependencyGrabber#deleteDependenciesFromPath(org.apache.tinkerpop.gremlin.groovy.plugin.Artifact)`
+** `org.apache.tinkerpop.gremlin.groovy.util.DependencyGrabber#copyDependenciesToPath(org.apache.tinkerpop.gremlin.groovy.plugin.Artifact)`
+* `gremlin-python`
+** `org.apache.tinkerpop.gremlin.python.jsr223.GremlinJythonScriptEngine#()`
+* `gremlin-server`
+** `org.apache.tinkerpop.gremlin.server.GremlinServer(ServerGremlinExecutor)`
+** `org.apache.tinkerpop.gremlin.server.Settings#plugins`
+** `org.apache.tinkerpop.gremlin.server.auth.AllowAllAuthenticator.newSaslNegotiator()`
+** `org.apache.tinkerpop.gremlin.server.auth.Authenticator.newSaslNegotiator()`
+** `org.apache.tinkerpop.gremlin.server.auth.Krb5Authenticator.newSaslNegotiator()`
+** `org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator.newSaslNegotiator()`
+** `org.apache.tinkerpop.gremlin.server.handler.IteratorHandler`
+** `org.apache.tinkerpop.gremlin.server.handler.NioGremlinResponseEncoder`
+** `org.apache.tinkerpop.gremlin.server.handler.WsGremlinResponseEncoder`
+** `org.apache.tinkerpop.gremlin.server.handler.OpSelectorHandler.errorMeter`
+** `org.apache.tinkerpop.gremlin.server.op.control.*`
+** `org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor.errorMeter`
+** `org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor.validBindingName`
+** `org.apache.tinkerpop.gremlin.server.op.session.Session.kill()`
+** `org.apache.tinkerpop.gremlin.server.op.session.Session.manualkill()`
+* `hadoop-gremlin`
+** `org.apache.tinkerpop.gremlin.hadoop.Constants#GREMLIN_HADOOP_GRAPH_INPUT_FORMAT`
+** `org.apache.tinkerpop.gremlin.hadoop.Constants#GREMLIN_HADOOP_GRAPH_OUTPUT_FORMAT`
+** `org.apache.tinkerpop.gremlin.hadoop.Constants#GREMLIN_HADOOP_GRAPH_INPUT_FORMAT_HAS_EDGES`
+** `org.apache.tinkerpop.gremlin.hadoop.Constants#GREMLIN_HADOOP_GRAPH_OUTPUT_FORMAT_HAS_EDGES`
+** `org.apache.tinkerpop.gremlin.hadoop.Constants#GREMLIN_SPARK_GRAPH_INPUT_RDD`
+** `org.apache.tinkerpop.gremlin.hadoop.Constants#GREMLIN_SPARK_GRAPH_OUTPUT_RDD`
+* `spark-gremlin`
+** `org.apache.tinkerpop.gremlin.spark.groovy.plugin.SparkGremlinPlugin`
+* `tinkergraph-gremlin`
+** `org.apache.tinkerpop.gremlin.tinkergraph.groovy.plugin.TinkerGraphGremlinPlugin`
+** `org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph#CONFIG_*`
+** `org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistry`
+** `org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0#getInstance()`
+** `org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV2d0#getInstance()`
+
+Please see the javadoc deprecation notes or upgrade documentation specific to when the deprecation took place to
+understand how to resolve this breaking change.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-832[TINKERPOP-832],
+link:https://issues.apache.org/jira/browse/TINKERPOP-833[TINKERPOP-833],
+link:https://issues.apache.org/jira/browse/TINKERPOP-834[TINKERPOP-834],
+link:https://issues.apache.org/jira/browse/TINKERPOP-999[TINKERPOP-999],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1010[TINKERPOP-1010],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1028[TINKERPOP-1028],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1040[TINKERPOP-1040],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1046[TINKERPOP-1046],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1049[TINKERPOP-1049],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1142[TINKERPOP-1142],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1169[TINKERPOP-1169],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1171[TINKERPOP-1171],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1275[TINKERPOP-1275],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1283[TINKERPOP-1283],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1289[TINKERPOP-1289],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1291[TINKERPOP-1291],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1420[TINKERPOP-1420],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1421[TINKERPOP-1421],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1465[TINKERPOP-1465],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1481[TINKERPOP-1481],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1526[TINKERPOP-1526],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1603[TINKERPOP-1603],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1612[TINKERPOP-1612],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1622[TINKERPOP-1622],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1651[TINKERPOP-1651],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1694[TINKERPOP-1694],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1700[TINKERPOP-1700],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1706[TINKERPOP-1706],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1721[TINKERPOP-1721],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1719[TINKERPOP-1719],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1720[TINKERPOP-1720],
+link:https://issues.apache.org/jira/browse/TINKERPOP-880[TINKERPOP-880],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1170[TINKERPOP-1170],
+link:https://issues.apache.org/jira/browse/TINKERPOP-1729[TINKERPOP-1729]
+
+==== Gremlin-server.sh and Init Scripts
+
+`gremlin-server.sh` is now also an init script and can no longer be started without parameters. To start it in the
+foreground with defaults like previous usage, please use the `console` parameter. Also, `gremlin-server.sh` will
+continue to start in the foreground when provided a yaml configuration file.
+
+How to install as a service has been added to the link:http://tinkerpop.apache.org/docs/3.3.0/reference/#_as_a_service[Reference Documentation - As A Service].
+
+The switch name has changed for installing dependencies. `-i` has been deprecated and replaced by `install`.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-980[TINKERPOP-980], link:http://tinkerpop.apache.org/docs/3.3.0/reference/#_configuring_2[Reference Documentation - Server Configuration].
+
+==== Removal of useMapperFromGraph
+
+The `userMapperFromGraph` serialization configuration option was used to allow the IO configurations of a specific
+graph to be assigned to a specific serializer. This feature has been removed completely now. Please use the
+`ioRegistries` configuration option to add one or more specific `Graph` serialization capabilities to a serializer.
+
+[source,yaml]
+----
+serializers:
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1d0] }} # application/vnd.gremlin-v1.0+gryo
+----
+
+see: link:https://issues.apache.org/jira/browse/TINKERPOP-1699[TINKERPOP-1699]
+
+==== Gremlin-server.bat
+
+The switch name has changed for installing dependencies. `-i` has been deprecated and replaced by `install`.
+
+==== SparkGraphComputer GryoRegistrator
+
+Historically, `SparkGraphComputer` has used `GryoSerializer` to handle the serialization of objects in Spark. The reason
+this exists is because TinkerPop uses a shaded version of Kryo and thus, couldn't use the standard `KryoSerializer`-model
+provided by Spark. However, a "shim model" was created which allows for the shaded and unshaded versions of Kryo to
+interact with one another. To this end, `KryoSerializer` can now be used with a `GryoRegistrator`. The properties file
+for a `SparkGraphComputer` now looks as follows:
+
+```
+spark.serializer=org.apache.spark.serializer.KryoSerializer
+spark.kryo.registrator=org.apache.tinkerpop.gremlin.spark.structure.io.gryo.GryoRegistrator
+```
+
+If the old `GryoSerializer` model is desired, then the properties file should simply look as before:
+
+```
+spark.serializer=org.apache.tinkerpop.gremlin.spark.structure.io.gryo.GryoSerializer
+```
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1389
+
+==== ScriptInputFormat
+
+The API for the script provided to a `ScriptInputFormat` has changed slightly. The signature for `parse(line, factory)`
+is now simply `parse(line)`. The inclusion of `factory` was deprecated in 3.1.2. Instead of using the {{factory}} to
+get the {{StarGraph}} there is a {{graph}} variable in the glocal context of the script. Simply use that directly in
+the script.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1137[TINKERPOP-1137],
+link:http://tinkerpop.apache.org/docs/3.3.0-SNAPSHOT/reference/#script-io-format[Reference Documentation - Script I/O Format]
+
+=== Upgrading for Providers
+
+IMPORTANT: It is recommended that providers also review all the upgrade instructions specified for users. Many of the
+changes there may prove important for the provider's implementation.
+
+==== Graph System Providers
+
+===== GremlnPlugin
+
+The previously deprecated `GremlinPlugin` system has been removed. The old `GremlinPlugin` interface formerly resided
+in the `org.apache.tinkerpop.gremlin.groovy.plugin` package of `gremlin-groovy`. This interface was replaced by an
+interface of the same name in 3.2.4, which now resides in the `org.apache.tinkerpop.gremlin.jsr223` package in
+`gremlin-core`. Obviously, existing plugins will need to be updated to use this new interface.
+
+The plugin model has changed slightly to be more generic and not specifically bound to Groovy based script engines.
+Under the new model, the plugin simply returns `Customizer` instances that can be applied generically to any
+`ScriptEngine` or specifically to a particular `ScriptEngine`. More details can be found in the
+link:http://tinkerpop.apache.org/docs/x.y.z/dev/provider/#gremlin-plugins[Provider Documentation]
+
+==== Graph Database Providers
+
+===== Test Suite Removal
+
+A number of test suites that were previously deprecated have been removed which should reduce the burden on graph
+providers who are implementing TinkerPop. Test suites related to perfrmance based on `junit-benchmarks` have been
+removed as have the suites in `gremlin-groovy-test` (in fact, this entire module has been removed). Specifically,
+providers should be concerned with breaking changes related to the removal of:
+
+* `StructurePerformanceSuite`
+* `ProcessPerformanceSuite`
+* `GroovyEnvironmentPerformanceSuite`
+* `GroovyProcessStandardSuite`
+* `GroovyProcessComputerSuite`
+* `GroovyEnvironmentSuite`
+* `GroovyEnvironmentIntegrateSuite`
+
+Those graph providers who relied on these tests should simply remove them from their respective test suites. Beware of
+`OptOut` annotations that reference tests in these suites as test failure will occur if those references are not
+removed.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1235[TINKERPOP-1235], link:https://issues.apache.org/jira/browse/TINKERPOP-1612[TINKERPOP-1612]
+
+===== TransactionException
+
+The `AbstractTransaction.TransactionException` class is now just `TransactionException` which extends `RuntimeExcetpion`
+rather than `Exception`. Providers should consider using this exception to wrap their own on calls to
+`Transaction.commit()` or `Transaction.rollback()`. By throwing this exception, the TinkerPop stack can better respond
+to transaction problems and it allows for more common, generalized error handling for users.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1004[TINKERPOP-1004]
+
+==== Driver Providers
+
+===== SASL Byte Array
+
+Gremlin Server no longer supports accepting a byte array for the value passed to the "sasl" parameter in
+authentication messages. It only accepts a Base64 encoded string.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1603[TINKERPOP-1603]
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5031ccdb/gremlin-python/src/main/jython/gremlin_python/statics.py
----------------------------------------------------------------------
diff --cc gremlin-python/src/main/jython/gremlin_python/statics.py
index 28cae14,f98347e..8f1f0f5
--- a/gremlin-python/src/main/jython/gremlin_python/statics.py
+++ b/gremlin-python/src/main/jython/gremlin_python/statics.py
@@@ -39,9 -35,16 +39,18 @@@ else
from types import IntType
from types import LongType
from types import TypeType
+ from types import ListType
+ from types import DictType
+
+ class timestamp(float):
+ """
+ In Python a timestamp is simply a float. This dummy class (similar to long), allows users to wrap a float
+ in a GLV script to make sure the value is serialized as a GraphSON timestamp.
+ """
+ pass
+
+
staticMethods = {}
staticEnums = {}
default_lambda_language = "gremlin-python"
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5031ccdb/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
----------------------------------------------------------------------
diff --cc gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
index eedc4bc,0000000..c2510ce
mode 100644,000000..100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV2d0.py
@@@ -1,403 -1,0 +1,470 @@@
+'''
+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.
+'''
- from aenum import Enum
++import datetime
+import json
- import six
++import time
++import uuid
+from collections import OrderedDict
+
++import six
++from aenum import Enum
++
+from gremlin_python import statics
+from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType
+from gremlin_python.process.traversal import Binding, Bytecode, P, Traversal, Traverser, TraversalStrategy
+from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path
+
+# When we fall back to a superclass's serializer, we iterate over this map.
+# We want that iteration order to be consistent, so we use an OrderedDict,
+# not a dict.
+_serializers = OrderedDict()
+_deserializers = {}
+
+
+class GraphSONTypeType(type):
+ def __new__(mcs, name, bases, dct):
+ cls = super(GraphSONTypeType, mcs).__new__(mcs, name, bases, dct)
+ if not name.startswith('_'):
+ if cls.python_type:
+ _serializers[cls.python_type] = cls
+ if cls.graphson_type:
+ _deserializers[cls.graphson_type] = cls
+ return cls
+
+
+class GraphSONUtil(object):
+ TYPE_KEY = "@type"
+ VALUE_KEY = "@value"
+
+ @classmethod
+ def typedValue(cls, type_name, value, prefix="g"):
+ out = {cls.TYPE_KEY: cls.formatType(prefix, type_name)}
+ if value is not None:
+ out[cls.VALUE_KEY] = value
+ return out
+
+ @classmethod
+ def formatType(cls, prefix, type_name):
+ return "%s:%s" % (prefix, type_name)
+
+
+# Read/Write classes split to follow precedence of the Java API
+class GraphSONWriter(object):
+ def __init__(self, serializer_map=None):
+ """
+ :param serializer_map: map from Python type to serializer instance implementing `dictify`
+ """
+ self.serializers = _serializers.copy()
+ if serializer_map:
+ self.serializers.update(serializer_map)
+
+ def writeObject(self, objectData):
+ # to JSON
+ return json.dumps(self.toDict(objectData), separators=(',', ':'))
+
+ def toDict(self, obj):
+ """
+ Encodes python objects in GraphSON type-tagged dict values
+ """
+ try:
+ return self.serializers[type(obj)].dictify(obj, self)
+ except KeyError:
+ for key, serializer in self.serializers.items():
+ if isinstance(obj, key):
+ return serializer.dictify(obj, self)
+
+ # list and map are treated as normal json objs (could be isolated serializers)
+ if isinstance(obj, (list, set)):
+ return [self.toDict(o) for o in obj]
+ elif isinstance(obj, dict):
+ return dict((self.toDict(k), self.toDict(v)) for k, v in obj.items())
+ else:
+ return obj
+
+
+class GraphSONReader(object):
+ def __init__(self, deserializer_map=None):
+ """
+ :param deserializer_map: map from GraphSON type tag to deserializer instance implementing `objectify`
+ """
+ self.deserializers = _deserializers.copy()
+ if deserializer_map:
+ self.deserializers.update(deserializer_map)
+
+ def readObject(self, jsonData):
+ # from JSON
+ return self.toObject(json.loads(jsonData))
+
+ def toObject(self, obj):
+ """
+ Unpacks GraphSON type-tagged dict values into objects mapped in self.deserializers
+ """
+ if isinstance(obj, dict):
+ try:
+ return self.deserializers[obj[GraphSONUtil.TYPE_KEY]].objectify(obj[GraphSONUtil.VALUE_KEY], self)
+ except KeyError:
+ pass
+ # list and map are treated as normal json objs (could be isolated deserializers)
+ return dict((self.toObject(k), self.toObject(v)) for k, v in obj.items())
+ elif isinstance(obj, list):
+ return [self.toObject(o) for o in obj]
+ else:
+ return obj
+
+
+@six.add_metaclass(GraphSONTypeType)
+class _GraphSONTypeIO(object):
+ python_type = None
+ graphson_type = None
+
+ symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and",
+ "or_": "or", "is_": "is", "not_": "not", "from_": "from",
+ "set_": "set", "list_": "list", "all_": "all"}
+
+ @classmethod
+ def unmangleKeyword(cls, symbol):
+ return cls.symbolMap.get(symbol, symbol)
+
+ def dictify(self, obj, writer):
+ raise NotImplementedError()
+
+ def objectify(self, d, reader):
+ raise NotImplementedError()
+
+
+class _BytecodeSerializer(_GraphSONTypeIO):
+ @classmethod
+ def _dictify_instructions(cls, instructions, writer):
+ out = []
+ for instruction in instructions:
+ inst = [instruction[0]]
+ inst.extend(writer.toDict(arg) for arg in instruction[1:])
+ out.append(inst)
+ return out
+
+ @classmethod
+ def dictify(cls, bytecode, writer):
+ if isinstance(bytecode, Traversal):
+ bytecode = bytecode.bytecode
+ out = {}
+ if bytecode.source_instructions:
+ out["source"] = cls._dictify_instructions(bytecode.source_instructions, writer)
+ if bytecode.step_instructions:
+ out["step"] = cls._dictify_instructions(bytecode.step_instructions, writer)
+ return GraphSONUtil.typedValue("Bytecode", out)
+
+class TraversalSerializer(_BytecodeSerializer):
+ python_type = Traversal
+
+
+class BytecodeSerializer(_BytecodeSerializer):
+ python_type = Bytecode
+
+
+class VertexSerializer(_GraphSONTypeIO):
+ python_type = Vertex
+ graphson_type = "g:Vertex"
+
+ @classmethod
+ def dictify(cls, vertex, writer):
+ return GraphSONUtil.typedValue("Vertex", {"id": writer.toDict(vertex.id),
+ "label": writer.toDict(vertex.label)})
+
+
+class EdgeSerializer(_GraphSONTypeIO):
+ python_type = Edge
+ graphson_type = "g:Edge"
+
+ @classmethod
+ def dictify(cls, edge, writer):
+ return GraphSONUtil.typedValue("Edge", {"id": writer.toDict(edge.id),
+ "outV": writer.toDict(edge.outV.id),
+ "outVLabel": writer.toDict(edge.outV.label),
+ "label": writer.toDict(edge.label),
+ "inV": writer.toDict(edge.inV.id),
+ "inVLabel": writer.toDict(edge.inV.label)})
+
+
+class VertexPropertySerializer(_GraphSONTypeIO):
+ python_type = VertexProperty
+ graphson_type = "g:VertexProperty"
+
+ @classmethod
+ def dictify(cls, vertex_property, writer):
+ return GraphSONUtil.typedValue("VertexProperty", {"id": writer.toDict(vertex_property.id),
+ "label": writer.toDict(vertex_property.label),
+ "value": writer.toDict(vertex_property.value),
+ "vertex": writer.toDict(vertex_property.vertex.id)})
+
+
+class PropertySerializer(_GraphSONTypeIO):
+ python_type = Property
+ graphson_type = "g:Property"
+
+ @classmethod
+ def dictify(cls, property, writer):
+ elementDict = writer.toDict(property.element)
+ if elementDict is not None:
+ valueDict = elementDict["@value"]
+ if "outVLabel" in valueDict:
+ del valueDict["outVLabel"]
+ if "inVLabel" in valueDict:
+ del valueDict["inVLabel"]
+ if "properties" in valueDict:
+ del valueDict["properties"]
+ if "value" in valueDict:
+ del valueDict["value"]
+ return GraphSONUtil.typedValue("Property", {"key": writer.toDict(property.key),
+ "value": writer.toDict(property.value),
+ "element": elementDict})
+
+
+class TraversalStrategySerializer(_GraphSONTypeIO):
+ python_type = TraversalStrategy
+
+ @classmethod
+ def dictify(cls, strategy, writer):
+ return GraphSONUtil.typedValue(strategy.strategy_name, writer.toDict(strategy.configuration))
+
+
+class TraverserIO(_GraphSONTypeIO):
+ python_type = Traverser
+ graphson_type = "g:Traverser"
+
+ @classmethod
+ def dictify(cls, traverser, writer):
+ return GraphSONUtil.typedValue("Traverser", {"value": writer.toDict(traverser.object),
+ "bulk": writer.toDict(traverser.bulk)})
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Traverser(reader.toObject(d["value"]),
+ reader.toObject(d["bulk"]))
+
+
+class EnumSerializer(_GraphSONTypeIO):
+ python_type = Enum
+
+ @classmethod
+ def dictify(cls, enum, _):
+ return GraphSONUtil.typedValue(cls.unmangleKeyword(type(enum).__name__),
+ cls.unmangleKeyword(str(enum.name)))
+
+
+class PSerializer(_GraphSONTypeIO):
+ python_type = P
+
+ @classmethod
+ def dictify(cls, p, writer):
+ out = {"predicate": p.operator,
+ "value": [writer.toDict(p.value), writer.toDict(p.other)] if p.other is not None else
+ writer.toDict(p.value)}
+ return GraphSONUtil.typedValue("P", out)
+
+
+class BindingSerializer(_GraphSONTypeIO):
+ python_type = Binding
+
+ @classmethod
+ def dictify(cls, binding, writer):
+ out = {"key": binding.key,
+ "value": writer.toDict(binding.value)}
+ return GraphSONUtil.typedValue("Binding", out)
+
+
+class LambdaSerializer(_GraphSONTypeIO):
+ python_type = FunctionType
+
+ @classmethod
+ def dictify(cls, lambda_object, writer):
+ lambda_result = lambda_object()
+ script = lambda_result if isinstance(lambda_result, str) else lambda_result[0]
+ language = statics.default_lambda_language if isinstance(lambda_result, str) else lambda_result[1]
+ out = {"script": script,
+ "language": language}
+ if language == "gremlin-jython" or language == "gremlin-python":
+ if not script.strip().startswith("lambda"):
+ script = "lambda " + script
+ out["script"] = script
+ out["arguments"] = six.get_function_code(eval(out["script"])).co_argcount
+ else:
+ out["arguments"] = -1
+ return GraphSONUtil.typedValue("Lambda", out)
+
+
+class TypeSerializer(_GraphSONTypeIO):
+ python_type = TypeType
+
+ @classmethod
+ def dictify(cls, typ, writer):
+ return writer.toDict(typ())
+
+
++class UUIDIO(_GraphSONTypeIO):
++ python_type = uuid.UUID
++ graphson_type = "g:UUID"
++ graphson_base_type = "UUID"
++
++ @classmethod
++ def dictify(cls, obj, writer):
++ return GraphSONUtil.typedValue(cls.graphson_base_type, str(obj))
++
++ @classmethod
++ def objectify(cls, d, reader):
++ return cls.python_type(d)
++
++
++class DateIO(_GraphSONTypeIO):
++ python_type = datetime.datetime
++ graphson_type = "g:Date"
++ graphson_base_type = "Date"
++
++ @classmethod
++ def dictify(cls, obj, writer):
++ # Java timestamp expects miliseconds
++ if six.PY3:
++ pts = obj.timestamp()
++ else:
++ # Hack for legacy Python
++ # Taken from:
++ # https://github.com/jaraco/backports.datetime_timestamp/blob/master/backports/datetime_timestamp/__init__.py
++ pts = time.mktime((obj.year, obj.month, obj.day,
++ obj.hour, obj.minute, obj.second,
++ -1, -1, -1)) + obj.microsecond / 1e6
++
++ # Have to use int because of legacy Python
++ ts = int(round(pts * 1000))
++ return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
++
++ @classmethod
++ def objectify(cls, ts, reader):
++ # Python timestamp expects seconds
++ return datetime.datetime.fromtimestamp(ts / 1000.0)
++
++
++# Based on current implementation, this class must always be declared before FloatIO.
++# Seems pretty fragile for future maintainers. Maybe look into this.
++class TimestampIO(_GraphSONTypeIO):
++ """A timestamp in Python is type float"""
++ python_type = statics.timestamp
++ graphson_type = "g:Timestamp"
++ graphson_base_type = "Timestamp"
++
++ @classmethod
++ def dictify(cls, obj, writer):
++ # Java timestamp expects milliseconds integer
++ # Have to use int because of legacy Python
++ ts = int(round(obj * 1000))
++ return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
++
++ @classmethod
++ def objectify(cls, ts, reader):
++ # Python timestamp expects seconds
++ return cls.python_type(ts / 1000.0)
++
++
+class _NumberIO(_GraphSONTypeIO):
+ @classmethod
+ def dictify(cls, n, writer):
+ if isinstance(n, bool): # because isinstance(False, int) and isinstance(True, int)
+ return n
+ return GraphSONUtil.typedValue(cls.graphson_base_type, n)
+
+ @classmethod
+ def objectify(cls, v, _):
+ return cls.python_type(v)
+
+
+class FloatIO(_NumberIO):
+ python_type = FloatType
+ graphson_type = "g:Float"
+ graphson_base_type = "Float"
+
+
+class DoubleIO(FloatIO):
+ graphson_type = "g:Double"
+ graphson_base_type = "Double"
+
+
+class Int64IO(_NumberIO):
+ python_type = LongType
+ graphson_type = "g:Int64"
+ graphson_base_type = "Int64"
+
+
+class Int32IO(_NumberIO):
+ python_type = IntType
+ graphson_type = "g:Int32"
+ graphson_base_type = "Int32"
+
+ @classmethod
+ def dictify(cls, n, writer):
+ if isinstance(n, bool):
+ return n
+ return GraphSONUtil.typedValue(cls.graphson_base_type, n)
+
+
+class VertexDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Vertex"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Vertex(reader.toObject(d["id"]), d.get("label", "vertex"))
+
+
+class EdgeDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Edge"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Edge(reader.toObject(d["id"]),
+ Vertex(reader.toObject(d["outV"]), d.get("outVLabel", "vertex")),
+ d.get("label", "edge"),
+ Vertex(reader.toObject(d["inV"]), d.get("inVLabel", "vertex")))
+
+
+class VertexPropertyDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:VertexProperty"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ vertex = Vertex(reader.toObject(d.get("vertex"))) if "vertex" in d else None
+ return VertexProperty(reader.toObject(d["id"]),
+ d["label"],
+ reader.toObject(d["value"]),
+ vertex)
+
+
+class PropertyDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Property"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ element = reader.toObject(d["element"]) if "element" in d else None
+ return Property(d["key"], reader.toObject(d["value"]), element)
+
+
+class PathDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Path"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ labels = [set(label) for label in d["labels"]]
+ objects = [reader.toObject(o) for o in d["objects"]]
+ return Path(labels, objects)
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5031ccdb/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
----------------------------------------------------------------------
diff --cc gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
index 6b5144e,0000000..d34e971
mode 100644,000000..100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
@@@ -1,468 -1,0 +1,535 @@@
- '''
++"""
+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.
- '''
- from aenum import Enum
++"""
++import datetime
+import json
- import six
++import time
++import uuid
+from collections import OrderedDict
+
++import six
++from aenum import Enum
++
+from gremlin_python import statics
+from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType, DictType, ListType, SetType
+from gremlin_python.process.traversal import Binding, Bytecode, P, Traversal, Traverser, TraversalStrategy
+from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path
+
+# When we fall back to a superclass's serializer, we iterate over this map.
+# We want that iteration order to be consistent, so we use an OrderedDict,
+# not a dict.
+_serializers = OrderedDict()
+_deserializers = {}
+
+
+class GraphSONTypeType(type):
+ def __new__(mcs, name, bases, dct):
+ cls = super(GraphSONTypeType, mcs).__new__(mcs, name, bases, dct)
+ if not name.startswith('_'):
+ if cls.python_type:
+ _serializers[cls.python_type] = cls
+ if cls.graphson_type:
+ _deserializers[cls.graphson_type] = cls
+ return cls
+
+
+class GraphSONUtil(object):
+ TYPE_KEY = "@type"
+ VALUE_KEY = "@value"
+
+ @classmethod
+ def typedValue(cls, type_name, value, prefix="g"):
+ out = {cls.TYPE_KEY: cls.formatType(prefix, type_name)}
+ if value is not None:
+ out[cls.VALUE_KEY] = value
+ return out
+
+ @classmethod
+ def formatType(cls, prefix, type_name):
+ return "%s:%s" % (prefix, type_name)
+
+
+# Read/Write classes split to follow precedence of the Java API
+class GraphSONWriter(object):
+ def __init__(self, serializer_map=None):
+ """
+ :param serializer_map: map from Python type to serializer instance implementing `dictify`
+ """
+ self.serializers = _serializers.copy()
+ if serializer_map:
+ self.serializers.update(serializer_map)
+
+ def writeObject(self, objectData):
+ # to JSON
+ return json.dumps(self.toDict(objectData), separators=(',', ':'))
+
+ def toDict(self, obj):
+ """
+ Encodes python objects in GraphSON type-tagged dict values
+ """
+ try:
+ return self.serializers[type(obj)].dictify(obj, self)
+ except KeyError:
+ for key, serializer in self.serializers.items():
+ if isinstance(obj, key):
+ return serializer.dictify(obj, self)
+
+ if isinstance(obj, dict):
+ return dict((self.toDict(k), self.toDict(v)) for k, v in obj.items())
+ elif isinstance(obj, set):
+ return set([self.toDict(o) for o in obj])
+ elif isinstance(obj, list):
+ return [self.toDict(o) for o in obj]
+ else:
+ return obj
+
+
+class GraphSONReader(object):
+ def __init__(self, deserializer_map=None):
+ """
+ :param deserializer_map: map from GraphSON type tag to deserializer instance implementing `objectify`
+ """
+ self.deserializers = _deserializers.copy()
+ if deserializer_map:
+ self.deserializers.update(deserializer_map)
+
+ def readObject(self, jsonData):
+ # from JSON
+ return self.toObject(json.loads(jsonData))
+
+ def toObject(self, obj):
+ """
+ Unpacks GraphSON type-tagged dict values into objects mapped in self.deserializers
+ """
+ if isinstance(obj, dict):
+ try:
+ return self.deserializers[obj[GraphSONUtil.TYPE_KEY]].objectify(obj[GraphSONUtil.VALUE_KEY], self)
+ except KeyError:
+ pass
+ return dict((self.toObject(k), self.toObject(v)) for k, v in obj.items())
+ elif isinstance(obj, set):
+ return set([self.toObject(o) for o in obj])
+ elif isinstance(obj, list):
+ return [self.toObject(o) for o in obj]
+ else:
+ return obj
+
+
+@six.add_metaclass(GraphSONTypeType)
+class _GraphSONTypeIO(object):
+ python_type = None
+ graphson_type = None
+
+ symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and",
+ "or_": "or", "is_": "is", "not_": "not", "from_": "from",
+ "set_": "set", "list_": "list", "all_": "all"}
+
+ @classmethod
+ def unmangleKeyword(cls, symbol):
+ return cls.symbolMap.get(symbol, symbol)
+
+ def dictify(self, obj, writer):
+ raise NotImplementedError()
+
+ def objectify(self, d, reader):
+ raise NotImplementedError()
+
+
+class _BytecodeSerializer(_GraphSONTypeIO):
+ @classmethod
+ def _dictify_instructions(cls, instructions, writer):
+ out = []
+ for instruction in instructions:
+ inst = [instruction[0]]
+ inst.extend(writer.toDict(arg) for arg in instruction[1:])
+ out.append(inst)
+ return out
+
+ @classmethod
+ def dictify(cls, bytecode, writer):
+ if isinstance(bytecode, Traversal):
+ bytecode = bytecode.bytecode
+ out = {}
+ if bytecode.source_instructions:
+ out["source"] = cls._dictify_instructions(bytecode.source_instructions, writer)
+ if bytecode.step_instructions:
+ out["step"] = cls._dictify_instructions(bytecode.step_instructions, writer)
+ return GraphSONUtil.typedValue("Bytecode", out)
+
+
+class TraversalSerializer(_BytecodeSerializer):
+ python_type = Traversal
+
+
+class BytecodeSerializer(_BytecodeSerializer):
+ python_type = Bytecode
+
+
+class VertexSerializer(_GraphSONTypeIO):
+ python_type = Vertex
+ graphson_type = "g:Vertex"
+
+ @classmethod
+ def dictify(cls, vertex, writer):
+ return GraphSONUtil.typedValue("Vertex", {"id": writer.toDict(vertex.id),
+ "label": writer.toDict(vertex.label)})
+
+
+class EdgeSerializer(_GraphSONTypeIO):
+ python_type = Edge
+ graphson_type = "g:Edge"
+
+ @classmethod
+ def dictify(cls, edge, writer):
+ return GraphSONUtil.typedValue("Edge", {"id": writer.toDict(edge.id),
+ "outV": writer.toDict(edge.outV.id),
+ "outVLabel": writer.toDict(edge.outV.label),
+ "label": writer.toDict(edge.label),
+ "inV": writer.toDict(edge.inV.id),
+ "inVLabel": writer.toDict(edge.inV.label)})
+
+
+class VertexPropertySerializer(_GraphSONTypeIO):
+ python_type = VertexProperty
+ graphson_type = "g:VertexProperty"
+
+ @classmethod
+ def dictify(cls, vertex_property, writer):
+ return GraphSONUtil.typedValue("VertexProperty", {"id": writer.toDict(vertex_property.id),
+ "label": writer.toDict(vertex_property.label),
+ "value": writer.toDict(vertex_property.value),
+ "vertex": writer.toDict(vertex_property.vertex.id)})
+
+
+class PropertySerializer(_GraphSONTypeIO):
+ python_type = Property
+ graphson_type = "g:Property"
+
+ @classmethod
+ def dictify(cls, property, writer):
+ elementDict = writer.toDict(property.element)
+ if elementDict is not None:
+ valueDict = elementDict["@value"]
+ if "outVLabel" in valueDict:
+ del valueDict["outVLabel"]
+ if "inVLabel" in valueDict:
+ del valueDict["inVLabel"]
+ if "properties" in valueDict:
+ del valueDict["properties"]
+ if "value" in valueDict:
+ del valueDict["value"]
+ return GraphSONUtil.typedValue("Property", {"key": writer.toDict(property.key),
+ "value": writer.toDict(property.value),
+ "element": elementDict})
+
+
+class TraversalStrategySerializer(_GraphSONTypeIO):
+ python_type = TraversalStrategy
+
+ @classmethod
+ def dictify(cls, strategy, writer):
+ configuration = {}
+ for key in strategy.configuration:
+ configuration[key] = writer.toDict(strategy.configuration[key])
+ return GraphSONUtil.typedValue(strategy.strategy_name, configuration)
+
+
+class TraverserIO(_GraphSONTypeIO):
+ python_type = Traverser
+ graphson_type = "g:Traverser"
+
+ @classmethod
+ def dictify(cls, traverser, writer):
+ return GraphSONUtil.typedValue("Traverser", {"value": writer.toDict(traverser.object),
+ "bulk": writer.toDict(traverser.bulk)})
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Traverser(reader.toObject(d["value"]),
+ reader.toObject(d["bulk"]))
+
+
+class EnumSerializer(_GraphSONTypeIO):
+ python_type = Enum
+
+ @classmethod
+ def dictify(cls, enum, _):
+ return GraphSONUtil.typedValue(cls.unmangleKeyword(type(enum).__name__),
+ cls.unmangleKeyword(str(enum.name)))
+
+
+class PSerializer(_GraphSONTypeIO):
+ python_type = P
+
+ @classmethod
+ def dictify(cls, p, writer):
+ out = {"predicate": p.operator,
+ "value": [writer.toDict(p.value), writer.toDict(p.other)] if p.other is not None else
+ writer.toDict(p.value)}
+ return GraphSONUtil.typedValue("P", out)
+
+
+class BindingSerializer(_GraphSONTypeIO):
+ python_type = Binding
+
+ @classmethod
+ def dictify(cls, binding, writer):
+ out = {"key": binding.key,
+ "value": writer.toDict(binding.value)}
+ return GraphSONUtil.typedValue("Binding", out)
+
+
+class LambdaSerializer(_GraphSONTypeIO):
+ python_type = FunctionType
+
+ @classmethod
+ def dictify(cls, lambda_object, writer):
+ lambda_result = lambda_object()
+ script = lambda_result if isinstance(lambda_result, str) else lambda_result[0]
+ language = statics.default_lambda_language if isinstance(lambda_result, str) else lambda_result[1]
+ out = {"script": script,
+ "language": language}
+ if language == "gremlin-jython" or language == "gremlin-python":
+ if not script.strip().startswith("lambda"):
+ script = "lambda " + script
+ out["script"] = script
+ out["arguments"] = six.get_function_code(eval(out["script"])).co_argcount
+ else:
+ out["arguments"] = -1
+ return GraphSONUtil.typedValue("Lambda", out)
+
+
+class TypeSerializer(_GraphSONTypeIO):
+ python_type = TypeType
+
+ @classmethod
+ def dictify(cls, typ, writer):
+ return writer.toDict(typ())
+
+
++class UUIDIO(_GraphSONTypeIO):
++ python_type = uuid.UUID
++ graphson_type = "g:UUID"
++ graphson_base_type = "UUID"
++
++ @classmethod
++ def dictify(cls, obj, writer):
++ return GraphSONUtil.typedValue(cls.graphson_base_type, str(obj))
++
++ @classmethod
++ def objectify(cls, d, reader):
++ return cls.python_type(d)
++
++
++class DateIO(_GraphSONTypeIO):
++ python_type = datetime.datetime
++ graphson_type = "g:Date"
++ graphson_base_type = "Date"
++
++ @classmethod
++ def dictify(cls, obj, writer):
++ # Java timestamp expects miliseconds
++ if six.PY3:
++ pts = obj.timestamp()
++ else:
++ # Hack for legacy Python
++ # Taken from:
++ # https://github.com/jaraco/backports.datetime_timestamp/blob/master/backports/datetime_timestamp/__init__.py
++ pts = time.mktime((obj.year, obj.month, obj.day,
++ obj.hour, obj.minute, obj.second,
++ -1, -1, -1)) + obj.microsecond / 1e6
++
++ # Have to use int because of legacy Python
++ ts = int(round(pts * 1000))
++ return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
++
++ @classmethod
++ def objectify(cls, ts, reader):
++ # Python timestamp expects seconds
++ return datetime.datetime.fromtimestamp(ts / 1000.0)
++
++
++# Based on current implementation, this class must always be declared before FloatIO.
++# Seems pretty fragile for future maintainers. Maybe look into this.
++class TimestampIO(_GraphSONTypeIO):
++ """A timestamp in Python is type float"""
++ python_type = statics.timestamp
++ graphson_type = "g:Timestamp"
++ graphson_base_type = "Timestamp"
++
++ @classmethod
++ def dictify(cls, obj, writer):
++ # Java timestamp expects milliseconds integer
++ # Have to use int because of legacy Python
++ ts = int(round(obj * 1000))
++ return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
++
++ @classmethod
++ def objectify(cls, ts, reader):
++ # Python timestamp expects seconds
++ return cls.python_type(ts / 1000.0)
++
++
+class _NumberIO(_GraphSONTypeIO):
+ @classmethod
+ def dictify(cls, n, writer):
+ if isinstance(n, bool): # because isinstance(False, int) and isinstance(True, int)
+ return n
+ return GraphSONUtil.typedValue(cls.graphson_base_type, n)
+
+ @classmethod
+ def objectify(cls, v, _):
+ return cls.python_type(v)
+
+
+class ListIO(_GraphSONTypeIO):
+ python_type = ListType
+ graphson_type = "g:List"
+
+ @classmethod
+ def dictify(cls, l, writer):
+ new_list = []
+ for obj in l:
+ new_list.append(writer.toDict(obj))
+ return GraphSONUtil.typedValue("List", new_list)
+
+ @classmethod
+ def objectify(cls, l, reader):
+ new_list = []
+ for obj in l:
+ new_list.append(reader.toObject(obj))
+ return new_list
+
+
+class SetIO(_GraphSONTypeIO):
+ python_type = SetType
+ graphson_type = "g:Set"
+
+ @classmethod
+ def dictify(cls, s, writer):
+ new_list = []
+ for obj in s:
+ new_list.append(writer.toDict(obj))
+ return GraphSONUtil.typedValue("Set", new_list)
+
+ @classmethod
+ def objectify(cls, s, reader):
+ new_set = set()
+ for obj in s:
+ new_set.add(reader.toObject(obj))
+ return new_set
+
+
+class MapType(_GraphSONTypeIO):
+ python_type = DictType
+ graphson_type = "g:Map"
+
+ @classmethod
+ def dictify(cls, d, writer):
+ l = []
+ for key in d:
+ l.append(writer.toDict(key))
+ l.append(writer.toDict(d[key]))
+ return GraphSONUtil.typedValue("Map", l)
+
+ @classmethod
+ def objectify(cls, l, reader):
+ new_dict = {}
+ if len(l) > 0:
+ x = 0
+ while x < len(l):
+ new_dict[reader.toObject(l[x])] = reader.toObject(l[x + 1])
+ x = x + 2
+ return new_dict
+
+
+class FloatIO(_NumberIO):
+ python_type = FloatType
+ graphson_type = "g:Float"
+ graphson_base_type = "Float"
+
+
+class DoubleIO(FloatIO):
+ graphson_type = "g:Double"
+ graphson_base_type = "Double"
+
+
+class Int64IO(_NumberIO):
+ python_type = LongType
+ graphson_type = "g:Int64"
+ graphson_base_type = "Int64"
+
+
+class Int32IO(_NumberIO):
+ python_type = IntType
+ graphson_type = "g:Int32"
+ graphson_base_type = "Int32"
+
+ @classmethod
+ def dictify(cls, n, writer):
+ if isinstance(n, bool):
+ return n
+ return GraphSONUtil.typedValue(cls.graphson_base_type, n)
+
+
+class VertexDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Vertex"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Vertex(reader.toObject(d["id"]), d.get("label", "vertex"))
+
+
+class EdgeDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Edge"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Edge(reader.toObject(d["id"]),
+ Vertex(reader.toObject(d["outV"]), d.get("outVLabel", "vertex")),
+ d.get("label", "edge"),
+ Vertex(reader.toObject(d["inV"]), d.get("inVLabel", "vertex")))
+
+
+class VertexPropertyDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:VertexProperty"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ vertex = Vertex(reader.toObject(d.get("vertex"))) if "vertex" in d else None
+ return VertexProperty(reader.toObject(d["id"]),
+ d["label"],
+ reader.toObject(d["value"]),
+ vertex)
+
+
+class PropertyDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Property"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ element = reader.toObject(d["element"]) if "element" in d else None
+ return Property(d["key"], reader.toObject(d["value"]), element)
+
+
+class PathDeserializer(_GraphSONTypeIO):
+ graphson_type = "g:Path"
+
+ @classmethod
+ def objectify(cls, d, reader):
+ return Path(reader.toObject(d["labels"]), reader.toObject(d["objects"]))