You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/06/04 17:35:17 UTC

[tinkerpop] branch TINKERPOP-2361 created (now 80a40bb)

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a change to branch TINKERPOP-2361
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


      at 80a40bb  TINKERPOP-2361 java

This branch includes the following new commits:

     new 80a40bb  TINKERPOP-2361 java

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[tinkerpop] 01/01: TINKERPOP-2361 java

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2361
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 80a40bb491b8a8a5d74745cc19e4464e5f8c7bf2
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Fri May 29 09:52:08 2020 -0400

    TINKERPOP-2361 java
---
 CHANGELOG.asciidoc                                 |  1 +
 docs/src/upgrade/release-3.5.x.asciidoc            | 26 ++++++++++++++++++++++
 .../gremlin/process/traversal/Bytecode.java        |  9 ++++++--
 .../process/traversal/step/TraversalParent.java    |  5 +++++
 .../gremlin/process/traversal/TraversalTest.java   | 15 +++++++++++++
 gremlin-dotnet/glv/GraphTraversal.template         | 16 +++++++++++--
 .../src/Gremlin.Net/Process/Traversal/Bytecode.cs  |  5 +++++
 .../Process/Traversal/DefaultTraversal.cs          |  5 +++++
 .../Process/Traversal/GraphTraversal.cs            | 16 +++++++++++--
 .../Gremlin.Net/Process/Traversal/ITraversal.cs    |  5 +++++
 .../Process/Traversal/GraphTraversalSourceTests.cs | 11 +++++++++
 .../IO/GraphSON/BytecodeGraphSONSerializerTests.cs |  1 +
 .../gremlin-javascript/lib/process/bytecode.js     |  8 ++++++-
 .../gremlin-javascript/test/unit/traversal-test.js | 20 +++++++++++++++--
 gremlin-python/glv/TraversalSource.template        |  3 +++
 .../python/gremlin_python/process/traversal.py     |  3 +++
 .../main/python/tests/process/test_traversal.py    | 22 +++++++++++++-----
 .../tinkergraph/structure/TinkerGraphPlayTest.java |  4 ++--
 18 files changed, 158 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 0b93aeb..9275b78 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -37,6 +37,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Refactored `MapStep` to move its logic to `ScalarMapStep` so that the old behavior could be preserved while allow other implementations to have more flexibility.
 * Modified TinkerGraph to support `null` property values and can be configured to disable that feature.
 * Modified `null` handling in mutations to be consistent for a new `Vertex` as well as update to an existing one.
+* Enforced use of anonymous child traversals.
 * Removed support for Python 2.x in gremlinpython.
 * Upgraded to Apache Commons Configuration2.
 * Renamed `StoreStep` to `AggregateLocalStep`.
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index f1b2ee3..56b7757 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -29,6 +29,32 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.5.0/CHANGELOG.asc
 
 === Upgrading for Users
 
+==== Anonymous Child Traversals
+
+TinkerPop conventions for child traversals is to spawn them anonymously from `__`, therefore:
+
+[source,groovy]
+g.addV('person').addE('self').to(__.V(1))
+
+or more succinctly via static import as:
+
+[source,groovy]
+g.addV('person').addE('self').to(V(1))
+
+Some users have chosen to instead write the above as:
+
+[source,groovy]
+g.addV('person').addE('self').to(g.V(1))
+
+which spawns a child traversal from a `GraphTraversalSource`. When spawned this way, a traversal is bound to a "source"
+and therefore is not anonymous. While the above code worked, it is important that there be less ways to do things
+with Gremlin so as to avoid confusion in examples, documentations and mailing list answers.
+
+As of 3.5.0, attempting to use a traversal spawned from a "source" will result in an exception. Users will need to
+modify their code if they use the unconventional syntax.
+
+link:https://issues.apache.org/jira/browse/TINKERPOP-2361[TINKERPOP-2361]
+
 ==== Gryo Usage
 
 Since the first release of TinkerPop 3.x, Gryo has been the default serialization format for Gremlin Server and
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
index 43e615e..ffe6390 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
@@ -291,9 +291,14 @@ public final class Bytecode implements Cloneable, Serializable {
                 return new Binding<>(variable, convertArgument(argument, false));
         }
         //
-        if (argument instanceof Traversal)
+        if (argument instanceof Traversal) {
+            // prevent use of "g" to spawn child traversals
+            if (((Traversal) argument).asAdmin().getTraversalSource().isPresent())
+                throw new IllegalStateException(String.format(
+                        "The child traversal of %s was not spawned anonymously - use the __ class rather than a TraversalSource to construct the child traversal", argument));
+
             return ((Traversal) argument).asAdmin().getBytecode();
-        else if (argument instanceof Map) {
+        } else if (argument instanceof Map) {
             final Map<Object, Object> map = new LinkedHashMap<>(((Map) argument).size());
             for (final Map.Entry<?, ?> entry : ((Map<?, ?>) argument).entrySet()) {
                 map.put(convertArgument(entry.getKey(), true), convertArgument(entry.getValue(), true));
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
index 695d297..867b5fe 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
@@ -76,9 +76,14 @@ public interface TraversalParent extends AutoCloseable {
         return (Step<?, ?>) this;
     }
 
+    /**
+     * Assigns {@code this} as the parent to the child and merges/sets the side-effects. Implementations should
+     * validate that the child is spawned anonymously.
+     */
     public default <S, E> Traversal.Admin<S, E> integrateChild(final Traversal.Admin<?, ?> childTraversal) {
         if (null == childTraversal)
             return null;
+
         childTraversal.setParent(this);
         childTraversal.getSideEffects().mergeInto(this.asStep().getTraversal().getSideEffects());
         childTraversal.setSideEffects(this.asStep().getTraversal().getSideEffects());
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java
index 80d5024..90eab14 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java
@@ -19,10 +19,13 @@
 package org.apache.tinkerpop.gremlin.process.traversal;
 
 import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -35,11 +38,13 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsCollectionContaining.hasItems;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -128,6 +133,16 @@ public class TraversalTest {
         assertThat(t.isClosed(), is(true));
     }
 
+    @Test
+    public void shouldOnlyAllowAnonymousChildren() {
+        final GraphTraversalSource g = traversal().withGraph(EmptyGraph.instance());
+        g.V(1).addE("self").to(__.V(1));
+        try {
+            g.V(1).addE("self").to(g.V(1));
+            fail("Should not allow child traversals spawned from 'g'");
+        } catch (IllegalStateException ignored) {}
+    }
+
     private static class MockCloseStep<E> extends MockStep<E> implements AutoCloseable {
         private boolean closed = false;
 
diff --git a/gremlin-dotnet/glv/GraphTraversal.template b/gremlin-dotnet/glv/GraphTraversal.template
index 430968d..4dad324 100644
--- a/gremlin-dotnet/glv/GraphTraversal.template
+++ b/gremlin-dotnet/glv/GraphTraversal.template
@@ -37,7 +37,7 @@ namespace Gremlin.Net.Process.Traversal
         ///     Initializes a new instance of the <see cref="GraphTraversal{SType, EType}" /> class.
         /// </summary>
         public GraphTraversal()
-            : this(new List<ITraversalStrategy>(), new Bytecode())
+            : this(new List<ITraversalStrategy>(), new Bytecode(), true)
         {
         }
 
@@ -47,9 +47,21 @@ namespace Gremlin.Net.Process.Traversal
         /// <param name="traversalStrategies">The traversal strategies to be used by this graph traversal at evaluation time.</param>
         /// <param name="bytecode">The <see cref="Bytecode" /> associated with the construction of this graph traversal.</param>
         public GraphTraversal(ICollection<ITraversalStrategy> traversalStrategies, Bytecode bytecode)
+            : this(traversalStrategies, bytecode, false)
+        {
+        }
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="GraphTraversal{SType, EType}" /> class.
+        /// </summary>
+        /// <param name="traversalStrategies">The traversal strategies to be used by this graph traversal at evaluation time.</param>
+        /// <param name="bytecode">The <see cref="Bytecode" /> associated with the construction of this graph traversal.</param>
+        /// <param name="anonymous">Set to true if spawned from an anonymous traversal source and false otherwise.</param>
+        private GraphTraversal(ICollection<ITraversalStrategy> traversalStrategies, Bytecode bytecode, bool anonymous)
         {
             TraversalStrategies = traversalStrategies;
             Bytecode = bytecode;
+            IsAnonymous = anonymous;
         }
 
         private static GraphTraversal<S2, E2> Wrap<S2, E2>(GraphTraversal<S, E> traversal)
@@ -59,7 +71,7 @@ namespace Gremlin.Net.Process.Traversal
                 return traversal as GraphTraversal<S2, E2>;
             }
             // New wrapper
-            return new GraphTraversal<S2, E2>(traversal.TraversalStrategies, traversal.Bytecode);
+            return new GraphTraversal<S2, E2>(traversal.TraversalStrategies, traversal.Bytecode, traversal.IsAnonymous);
         }
 
 <% graphStepMethods.each { method -> %>
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
index 490e4e4..fc3011e 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
@@ -121,6 +121,11 @@ namespace Gremlin.Net.Process.Traversal
             {
                 return null;
             }
+
+            if (argument is ITraversal traversal && !traversal.IsAnonymous)
+                throw new ArgumentException(
+                    $"The child traversal of {traversal.Bytecode} was not spawned anonymously - use the __ class rather than a TraversalSource to construct the child traversal");
+                
             
             if (IsDictionaryType(argument.GetType()))
             {
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/DefaultTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/DefaultTraversal.cs
index 3124715..38e031c 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/DefaultTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/DefaultTraversal.cs
@@ -40,6 +40,11 @@ namespace Gremlin.Net.Process.Traversal
         ///     Gets the <see cref="Traversal.Bytecode" /> representation of this traversal.
         /// </summary>
         public Bytecode Bytecode { get; protected set; }
+        
+        /// <summary>
+        ///     Determines if this traversal was spawned anonymously or not.
+        /// </summary>
+        public bool IsAnonymous { get; protected set; }
 
         /// <summary>
         ///     Gets or sets the <see cref="Traverser" />'s of this traversal that hold the results of the traversal.
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
index 7394fe8..ed5f1d4 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs
@@ -37,7 +37,7 @@ namespace Gremlin.Net.Process.Traversal
         ///     Initializes a new instance of the <see cref="GraphTraversal{SType, EType}" /> class.
         /// </summary>
         public GraphTraversal()
-            : this(new List<ITraversalStrategy>(), new Bytecode())
+            : this(new List<ITraversalStrategy>(), new Bytecode(), true)
         {
         }
 
@@ -47,9 +47,21 @@ namespace Gremlin.Net.Process.Traversal
         /// <param name="traversalStrategies">The traversal strategies to be used by this graph traversal at evaluation time.</param>
         /// <param name="bytecode">The <see cref="Bytecode" /> associated with the construction of this graph traversal.</param>
         public GraphTraversal(ICollection<ITraversalStrategy> traversalStrategies, Bytecode bytecode)
+            : this(traversalStrategies, bytecode, false)
+        {
+        }
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="GraphTraversal{SType, EType}" /> class.
+        /// </summary>
+        /// <param name="traversalStrategies">The traversal strategies to be used by this graph traversal at evaluation time.</param>
+        /// <param name="bytecode">The <see cref="Bytecode" /> associated with the construction of this graph traversal.</param>
+        /// <param name="anonymous">Set to true if spawned from an anonymous traversal source and false otherwise.</param>
+        private GraphTraversal(ICollection<ITraversalStrategy> traversalStrategies, Bytecode bytecode, bool anonymous)
         {
             TraversalStrategies = traversalStrategies;
             Bytecode = bytecode;
+            IsAnonymous = anonymous;
         }
 
         private static GraphTraversal<S2, E2> Wrap<S2, E2>(GraphTraversal<S, E> traversal)
@@ -59,7 +71,7 @@ namespace Gremlin.Net.Process.Traversal
                 return traversal as GraphTraversal<S2, E2>;
             }
             // New wrapper
-            return new GraphTraversal<S2, E2>(traversal.TraversalStrategies, traversal.Bytecode);
+            return new GraphTraversal<S2, E2>(traversal.TraversalStrategies, traversal.Bytecode, traversal.IsAnonymous);
         }
 
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ITraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ITraversal.cs
index 39c88d1..95cbb74 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ITraversal.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ITraversal.cs
@@ -38,6 +38,11 @@ namespace Gremlin.Net.Process.Traversal
         ///     Gets the <see cref="Bytecode" /> representation of this traversal.
         /// </summary>
         Bytecode Bytecode { get; }
+        
+        /// <summary>
+        ///     Determines if this traversal was spawned anonymously or not.
+        /// </summary>
+        bool IsAnonymous { get;  }
 
         /// <summary>
         ///     Gets or sets the <see cref="Traverser" />'s of this traversal that hold the results of the traversal.
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GraphTraversalSourceTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GraphTraversalSourceTests.cs
index b3c285f..2cc39a6 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GraphTraversalSourceTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GraphTraversalSourceTests.cs
@@ -21,6 +21,7 @@
 
 #endregion
 
+using System;
 using Gremlin.Net.Process.Traversal;
 using Xunit;
 
@@ -83,5 +84,15 @@ namespace Gremlin.Net.UnitTest.Process.Traversal
             Assert.Equal(5, clone.Bytecode.StepInstructions.Count);
             Assert.Equal(4, cloneClone.Bytecode.StepInstructions.Count);
         }
+        
+        [Fact]
+        public void ShouldOnlyAllowChildTraversalsThatAreAnonymous()
+        {
+            var g = AnonymousTraversalSource.Traversal();
+
+            g.V(0).AddE("self").To(__.V(1));
+
+            Assert.Throws<ArgumentException>(() => g.V(0).AddE("self").To(g.V(1)));
+        }
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs
index 9b39391..29a434f 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs
@@ -162,6 +162,7 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
         public TestTraversal(Bytecode bytecode)
         {
             Bytecode = bytecode;
+            IsAnonymous = true;
         }
     }
 }
\ No newline at end of file
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/bytecode.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/bytecode.js
index e7fdd40..4c0e94c 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/bytecode.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/bytecode.js
@@ -22,6 +22,8 @@
  */
 'use strict';
 
+const { Traversal } = require('./traversal');
+
 class Bytecode {
   /**
    * Creates a new instance of Bytecode
@@ -76,7 +78,11 @@ class Bytecode {
     const instruction = new Array(length);
     instruction[0] = name;
     for (let i = 1; i < length; i++) {
-      instruction[i] = values[i - 1];
+      const val = values[i - 1];
+      if (val instanceof Traversal && val.graph != null)
+        throw new Error("The child traversal of ${val} was not spawned anonymously - use " +
+            "the __ class rather than a TraversalSource to construct the child traversal");
+      instruction[i] = val;
     }
     return instruction;
   }
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/traversal-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/traversal-test.js
index acdcdd4..22ebf17 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/traversal-test.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/traversal-test.js
@@ -25,7 +25,10 @@
 const assert = require('assert');
 const expect = require('chai').expect;
 const graph = require('../../lib/structure/graph');
+const anon = require('../../lib/process/anonymous-traversal');
 const t = require('../../lib/process/traversal');
+const gt = require('../../lib/process/graph-traversal');
+const V = gt.statics.V;
 const Bytecode = require('../../lib/process/bytecode');
 const TraversalStrategies = require('../../lib/process/traversal-strategy').TraversalStrategies;
 
@@ -33,7 +36,7 @@ describe('Traversal', function () {
 
   describe('#getByteCode()', function () {
     it('should add steps for with a string parameter', function () {
-      const g = new graph.Graph().traversal();
+      const g = anon.traversal().withGraph(new graph.Graph());
       const bytecode = g.V().out('created').getBytecode();
       assert.ok(bytecode);
       assert.strictEqual(bytecode.sourceInstructions.length, 0);
@@ -44,7 +47,7 @@ describe('Traversal', function () {
     });
 
     it('should add steps with an enum value', function () {
-      const g = new graph.Graph().traversal();
+      const g = anon.traversal().withGraph(new graph.Graph());
       const bytecode = g.V().order().by('age', t.order.desc).getBytecode();
       assert.ok(bytecode);
       assert.strictEqual(bytecode.sourceInstructions.length, 0);
@@ -237,4 +240,17 @@ describe('Traversal', function () {
       });
     });
   });
+
+  describe("build", function() {
+    it('should only allow anonymous child traversals', function() {
+      const g = anon.traversal().withGraph(new graph.Graph());
+      assert.doesNotThrow(function() {
+        g.V(0).addE("self").to(V(1))
+      });
+
+      assert.throws(function() {
+        g.V(0).addE("self").to(g.V(1))
+      });
+    })
+  });
 });
\ No newline at end of file
diff --git a/gremlin-python/glv/TraversalSource.template b/gremlin-python/glv/TraversalSource.template
index 5c94284..b12dd82 100644
--- a/gremlin-python/glv/TraversalSource.template
+++ b/gremlin-python/glv/TraversalSource.template
@@ -345,6 +345,9 @@ class Bytecode(object):
 
     def __convertArgument(self,arg):
         if isinstance(arg, Traversal):
+            if arg.graph is not None:
+                raise TypeError(f"The child traversal of {arg} was not spawned anonymously - use the __ "
+                                "class rather than a TraversalSource to construct the child traversal")
             self.bindings.update(arg.bytecode.bindings)
             return arg.bytecode
         elif isinstance(arg, dict):
diff --git a/gremlin-python/src/main/python/gremlin_python/process/traversal.py b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
index 161de69..45b60e9 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
@@ -627,6 +627,9 @@ class Bytecode(object):
 
     def __convertArgument(self,arg):
         if isinstance(arg, Traversal):
+            if arg.graph is not None:
+                raise TypeError(f"The child traversal of {arg} was not spawned anonymously - use the __ "
+                                "class rather than a TraversalSource to construct the child traversal")
             self.bindings.update(arg.bytecode.bindings)
             return arg.bytecode
         elif isinstance(arg, dict):
diff --git a/gremlin-python/src/main/python/tests/process/test_traversal.py b/gremlin-python/src/main/python/tests/process/test_traversal.py
index 5b9b619..653c56a 100644
--- a/gremlin-python/src/main/python/tests/process/test_traversal.py
+++ b/gremlin-python/src/main/python/tests/process/test_traversal.py
@@ -53,19 +53,19 @@ class TestTraversal(object):
         assert 1 == len(bytecode.step_instructions[1])
         assert 2 == len(bytecode.step_instructions[2])
         ##
-        bytecode = g.V(Bindings.of('a', [1,2,3])) \
-            .out(Bindings.of('b','created')) \
-            .where(__.in_(Bindings.of('c','created'), Bindings.of('d','knows')) \
-            .count().is_(Bindings.of('e',P.gt(2)))).bytecode
+        bytecode = g.V(Bindings.of('a', [1, 2, 3])) \
+            .out(Bindings.of('b', 'created')) \
+            .where(__.in_(Bindings.of('c', 'created'), Bindings.of('d', 'knows')) \
+            .count().is_(Bindings.of('e', P.gt(2)))).bytecode
         assert 5 == len(bytecode.bindings.keys())
         assert [1,2,3] == bytecode.bindings['a']
         assert 'created' == bytecode.bindings['b']
         assert 'created' == bytecode.bindings['c']
         assert 'knows' == bytecode.bindings['d']
         assert P.gt(2) == bytecode.bindings['e']
-        assert Binding('b','created') == bytecode.step_instructions[1][1]
+        assert Binding('b', 'created') == bytecode.step_instructions[1][1]
         assert 'binding[b=created]' == str(bytecode.step_instructions[1][1])
-        assert isinstance(hash(bytecode.step_instructions[1][1]),int)
+        assert isinstance(hash(bytecode.step_instructions[1][1]), int)
 
     def test_P(self):
         # verify that the order of operations is respected
@@ -104,4 +104,14 @@ class TestTraversal(object):
         assert 5 == len(clone.bytecode.step_instructions)
         assert 4 == len(cloneClone.bytecode.step_instructions)
 
+    def test_enforce_anonymous_child_traversal(self):
+        g = traversal().withGraph(Graph())
+        g.V(0).addE("self").to(__.V(1))
+
+        try:
+            g.V(0).addE("self").to(g.V(1))
+            assert false
+        except TypeError:
+            pass
+
 
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
index f4a0a0a..0791b16 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.computer.Computer;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ConsoleMutationListener;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
@@ -269,10 +270,9 @@ public class TinkerGraphPlayTest {
     }
 
     @Test
-    @Ignore
     public void testBugs() {
         GraphTraversalSource g = TinkerFactory.createModern().traversal();
-
+        Object o1 = g.V().map(__.V(1));
         System.out.println(g.V().as("a").both().as("b").dedup("a", "b").by(T.label).select("a", "b").explain());
         System.out.println(g.V().as("a").both().as("b").dedup("a", "b").by(T.label).select("a", "b").toList());