You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/06/27 13:56:15 UTC

tinkerpop git commit: Lambda now breaks up its internal subclasses into ZeroArgLambda, OneArgLambda, and TwoArgLambda. This makes it easy for Jython to extend these classes to wrap Python lambdas as Java8 functions. Finally have PythonJythonTranslatorTe

Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1278 9b99d1395 -> 7dd618595


Lambda now breaks  up its internal subclasses into ZeroArgLambda, OneArgLambda, and TwoArgLambda. This makes it easy for Jython to extend these classes to wrap Python lambdas as Java8 functions. Finally have PythonJythonTranslatorTest passing in full now.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/7dd61859
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/7dd61859
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/7dd61859

Branch: refs/heads/TINKERPOP-1278
Commit: 7dd61859598217a9dd44d0458cfb6a5e5d22d0de
Parents: 9b99d13
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Mon Jun 27 07:56:08 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Mon Jun 27 07:56:08 2016 -0600

----------------------------------------------------------------------
 .../tinkerpop/gremlin/util/function/Lambda.java | 48 ++++++-----
 .../python/GremlinPythonSourceGenerator.groovy  | 59 +++++++------
 .../jsr223/GremlinJythonScriptEngine.java       | 44 ++++++++++
 .../jython/gremlin_python/gremlin_python.py     | 90 ++++++++++----------
 .../jython/gremlin_python/jython_translator.py  | 16 ++--
 .../jython/PythonJythonTranslatorTest.java      | 10 +--
 6 files changed, 162 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7dd61859/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/Lambda.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/Lambda.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/Lambda.java
index cdb4239..75fd1af 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/Lambda.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/Lambda.java
@@ -47,40 +47,44 @@ public interface Lambda extends Serializable {
         }
     }
 
-    public static class SimpleLambda<A, B> extends AbstractLambda implements Function<A, B>, Predicate<A>, Supplier<A>, Consumer<A>, Comparator<A> {
-        public SimpleLambda(final String lambdaSource) {
+    public static class ZeroArgLambda<A> extends AbstractLambda implements Supplier<A> {
+
+        public ZeroArgLambda(final String lambdaSource) {
             super(lambdaSource);
         }
 
         @Override
-        public B apply(final A a) {
+        public A get() {
             return null;
         }
 
-        @Override
-        public boolean test(final A a) {
-            return false;
+    }
+
+    public static class OneArgLambda<A, B> extends AbstractLambda implements Function<A, B>, Predicate<A>, Consumer<A> {
+
+        public OneArgLambda(final String lambdaSource) {
+            super(lambdaSource);
         }
 
         @Override
-        public A get() {
+        public B apply(final A a) {
             return null;
         }
 
         @Override
-        public void accept(final A a) {
-
+        public boolean test(final A a) {
+            return false;
         }
 
         @Override
-        public int compare(final A first, final A second) {
-            return 0;
+        public void accept(final A a) {
+
         }
     }
 
-    public static class ComplexLambda<A, B, C> extends AbstractLambda implements BiFunction<A, B, C> {
+    public static class TwoArgLambda<A, B, C> extends AbstractLambda implements BiFunction<A, B, C>, Comparator<A> {
 
-        public ComplexLambda(final String lambdaSource) {
+        public TwoArgLambda(final String lambdaSource) {
             super(lambdaSource);
         }
 
@@ -88,33 +92,39 @@ public interface Lambda extends Serializable {
         public C apply(final A a, final B b) {
             return null;
         }
+
+
+        @Override
+        public int compare(final A first, final A second) {
+            return 0;
+        }
     }
 
 
     ////
 
     public static <A, B> Function<A, B> function(final String lambdaSource) {
-        return new SimpleLambda<>(lambdaSource);
+        return new OneArgLambda<>(lambdaSource);
     }
 
     public static <A> Predicate<A> predicate(final String lambdaSource) {
-        return new SimpleLambda<>(lambdaSource);
+        return new OneArgLambda<>(lambdaSource);
     }
 
     public static <A> Consumer<A> consumer(final String lambdaSource) {
-        return new SimpleLambda<>(lambdaSource);
+        return new OneArgLambda<>(lambdaSource);
     }
 
     public static <A> Supplier<A> supplier(final String lambdaSource) {
-        return new SimpleLambda<>(lambdaSource);
+        return new ZeroArgLambda<>(lambdaSource);
     }
 
     public static <A> Comparator<A> comparator(final String lambdaSource) {
-        return new SimpleLambda<>(lambdaSource);
+        return new TwoArgLambda<>(lambdaSource);
     }
 
     public static <A, B, C> BiFunction<A, B, C> biFunction(final String lambdaSource) {
-        return new ComplexLambda<>(lambdaSource);
+        return new TwoArgLambda<>(lambdaSource);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7dd61859/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GremlinPythonSourceGenerator.groovy
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GremlinPythonSourceGenerator.groovy b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GremlinPythonSourceGenerator.groovy
index 9099015..cece5df 100644
--- a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GremlinPythonSourceGenerator.groovy
+++ b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GremlinPythonSourceGenerator.groovy
@@ -228,13 +228,14 @@ globalTranslator = None
 ///////////
 // Enums //
 ///////////
-        for (final Class<? extends Enum> enumClass : CoreImports.getClassImports().findAll {
-            Enum.class.isAssignableFrom(it)
-        }.collect()) {
+        for (final Class<? extends Enum> enumClass : CoreImports.getClassImports()
+                .findAll { Enum.class.isAssignableFrom(it) }
+                .sort { a, b -> a.getSimpleName() <=> b.getSimpleName() }
+                .collect()) {
             pythonClass.append("${enumClass.getSimpleName()} = Enum('${enumClass.getSimpleName()}', '");
-            enumClass.getEnumConstants().each { value ->
-                pythonClass.append("${SymbolHelper.toPython(value.name())} ");
-            }
+            enumClass.getEnumConstants()
+                    .sort { a, b -> a.name() <=> b.name() }
+                    .each { value -> pythonClass.append("${SymbolHelper.toPython(value.name())} "); }
             pythonClass.deleteCharAt(pythonClass.length() - 1).append("')\n\n")
             enumClass.getEnumConstants().each { value ->
                 pythonClass.append("statics['${SymbolHelper.toPython(value.name())}'] = ${value.getDeclaringClass().getSimpleName()}.${SymbolHelper.toPython(value.name())}\n");
@@ -243,30 +244,6 @@ globalTranslator = None
         }
         //////////////
 
-        pythonClass.append("""class RawExpression(object):
-   def __init__(self, *args):
-      self.bindings = dict()
-      self.parts = [self._process_arg(arg) for arg in args]
-
-   def _process_arg(self, arg):
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-         self.bindings[arg[0]] = arg[1]
-         return Raw(arg[0])
-      else:
-         return Raw(arg)
-""")
-
-        pythonClass.append("\n")
-        pythonClass.append("""class Raw(object):
-   def __init__(self, value):
-      self.value = value
-
-   def __str__(self):
-      return str(self.value)
-""")
-
-        pythonClass.append("\n")
-
         pythonClass.append("""class P(object):
    def __init__(self, operator, value, other=None):
       self.operator = operator
@@ -306,6 +283,28 @@ globalTranslator = None
         pythonClass.append("\n")
         //////////////
 
+        pythonClass.append("""class RawExpression(object):
+   def __init__(self, *args):
+      self.bindings = dict()
+      self.parts = [self._process_arg(arg) for arg in args]
+
+   def _process_arg(self, arg):
+      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
+         self.bindings[arg[0]] = arg[1]
+         return Raw(arg[0])
+      else:
+         return Raw(arg)
+
+class Raw(object):
+   def __init__(self, value):
+      self.value = value
+
+   def __str__(self):
+      return str(self.value)
+
+""")
+        //////////////
+
         pythonClass.append("statics = OrderedDict(reversed(list(statics.items())))\n")
 
 // save to a python file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7dd61859/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java
index bdf1cc1..ee461bd 100644
--- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java
+++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java
@@ -65,6 +65,50 @@ public class GremlinJythonScriptEngine implements ScriptEngine {
                     "  else:\n    return TypeError('Index must be int or slice')");
             this.pyScriptEngine.eval(GraphTraversal.class.getSimpleName() + ".__getitem__ = getitem_bypass");
             this.pyScriptEngine.eval(GraphTraversal.class.getSimpleName() + ".__getattr__ = lambda self, key: self.values(key)");
+
+            this.pyScriptEngine.eval("\n" +
+                    "import org.apache.tinkerpop.gremlin.util.function.Lambda\n" +
+                    "from org.apache.tinkerpop.gremlin.util.function.Lambda import AbstractLambda\n" +
+                    "from org.apache.tinkerpop.gremlin.util.function.Lambda import ZeroArgLambda\n" +
+                    "from org.apache.tinkerpop.gremlin.util.function.Lambda import OneArgLambda\n" +
+                    "from org.apache.tinkerpop.gremlin.util.function.Lambda import TwoArgLambda\n\n" +
+
+                    "class JythonZeroArgLambda(ZeroArgLambda):\n" +
+                    "  def __init__(self,func):\n" +
+                    "    AbstractLambda.__init__(self, 'nothing')\n" +
+                    "    self.func = func\n" +
+                    "  def __repr__(self):\n" +
+                    "    return 'JythonZeroArgLambda'\n" +
+                    "  def get(self):\n" +
+                    "    return self.func()\n\n" +
+
+                    "class JythonOneArgLambda(OneArgLambda):\n" +
+                    "  def __init__(self,func):\n" +
+                    "    AbstractLambda.__init__(self, 'nothing')\n" +
+                    "    self.func = func\n" +
+                    "  def __repr__(self):\n" +
+                    "    return 'JythonOneArgLambda'\n" +
+                    "  def test(self,a):\n" +
+                    "    return self.func(a)\n" +
+                    "  def apply(self,a):\n" +
+                    "    return self.func(a)\n" +
+                    "  def accept(self,a):\n" +
+                    "    self.func(a)\n" +
+                    "  def compare(self,a,b):\n" +
+                    "    return self.func(a,b)\n\n" +
+
+                    "class JythonTwoArgLambda(TwoArgLambda):\n" +
+                    "  def __init__(self,func):\n" +
+                    "    AbstractLambda.__init__(self, 'nothing')\n" +
+                    "    self.func = func\n" +
+                    "  def __repr__(self):\n" +
+                    "    return 'JythonTwoArgLambda'\n" +
+                    "  def apply(self,a,b):\n" +
+                    "    return self.func(a,b)\n" +
+                    "  def compare(self,a,b):\n" +
+                    "    return self.func(a,b)\n"
+            );
+
         } catch (final ScriptException e) {
             throw new IllegalStateException(e.getMessage(), e);
         }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7dd61859/gremlin-python/src/main/jython/gremlin_python/gremlin_python.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/gremlin_python.py b/gremlin-python/src/main/jython/gremlin_python/gremlin_python.py
index b81ce82..a0bd5e7 100644
--- a/gremlin-python/src/main/jython/gremlin_python/gremlin_python.py
+++ b/gremlin-python/src/main/jython/gremlin_python/gremlin_python.py
@@ -1642,46 +1642,28 @@ def where(*args):
 statics['where'] = where
 
 
-Column = Enum('Column', 'keys values')
+Barrier = Enum('Barrier', 'normSack')
 
-statics['keys'] = Column.keys
-statics['values'] = Column.values
+statics['normSack'] = Barrier.normSack
 
-Scope = Enum('Scope', '_global local')
+Cardinality = Enum('Cardinality', 'list set single')
 
-statics['_global'] = Scope._global
-statics['local'] = Scope.local
+statics['single'] = Cardinality.single
+statics['list'] = Cardinality.list
+statics['set'] = Cardinality.set
 
-T = Enum('T', 'label id key value')
+Column = Enum('Column', 'keys values')
 
-statics['label'] = T.label
-statics['id'] = T.id
-statics['key'] = T.key
-statics['value'] = T.value
+statics['keys'] = Column.keys
+statics['values'] = Column.values
 
-Direction = Enum('Direction', 'OUT IN BOTH')
+Direction = Enum('Direction', 'BOTH IN OUT')
 
 statics['OUT'] = Direction.OUT
 statics['IN'] = Direction.IN
 statics['BOTH'] = Direction.BOTH
 
-Barrier = Enum('Barrier', 'normSack')
-
-statics['normSack'] = Barrier.normSack
-
-Pop = Enum('Pop', 'first last all')
-
-statics['first'] = Pop.first
-statics['last'] = Pop.last
-statics['all'] = Pop.all
-
-Cardinality = Enum('Cardinality', 'single list set')
-
-statics['single'] = Cardinality.single
-statics['list'] = Cardinality.list
-statics['set'] = Cardinality.set
-
-Operator = Enum('Operator', 'sum minus mult div min max assign _and _or addAll sumLong')
+Operator = Enum('Operator', 'addAll _and assign div max min minus mult _or sum sumLong')
 
 statics['sum'] = Operator.sum
 statics['minus'] = Operator.minus
@@ -1695,7 +1677,7 @@ statics['_or'] = Operator._or
 statics['addAll'] = Operator.addAll
 statics['sumLong'] = Operator.sumLong
 
-Order = Enum('Order', 'incr decr keyIncr valueIncr keyDecr valueDecr shuffle')
+Order = Enum('Order', 'decr incr keyDecr keyIncr shuffle valueDecr valueIncr')
 
 statics['incr'] = Order.incr
 statics['decr'] = Order.decr
@@ -1705,24 +1687,23 @@ statics['keyDecr'] = Order.keyDecr
 statics['valueDecr'] = Order.valueDecr
 statics['shuffle'] = Order.shuffle
 
-class RawExpression(object):
-   def __init__(self, *args):
-      self.bindings = dict()
-      self.parts = [self._process_arg(arg) for arg in args]
+Pop = Enum('Pop', 'all first last')
 
-   def _process_arg(self, arg):
-      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
-         self.bindings[arg[0]] = arg[1]
-         return Raw(arg[0])
-      else:
-         return Raw(arg)
+statics['first'] = Pop.first
+statics['last'] = Pop.last
+statics['all'] = Pop.all
 
-class Raw(object):
-   def __init__(self, value):
-      self.value = value
+Scope = Enum('Scope', '_global local')
 
-   def __str__(self):
-      return str(self.value)
+statics['_global'] = Scope._global
+statics['local'] = Scope.local
+
+T = Enum('T', 'id key label value')
+
+statics['label'] = T.label
+statics['id'] = T.id
+statics['key'] = T.key
+statics['value'] = T.value
 
 class P(object):
    def __init__(self, operator, value, other=None):
@@ -1826,4 +1807,23 @@ def without(*args):
 
 statics['without'] = without
 
+class RawExpression(object):
+   def __init__(self, *args):
+      self.bindings = dict()
+      self.parts = [self._process_arg(arg) for arg in args]
+
+   def _process_arg(self, arg):
+      if isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):
+         self.bindings[arg[0]] = arg[1]
+         return Raw(arg[0])
+      else:
+         return Raw(arg)
+
+class Raw(object):
+   def __init__(self, value):
+      self.value = value
+
+   def __str__(self):
+      return str(self.value)
+
 statics = OrderedDict(reversed(list(statics.items())))

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7dd61859/gremlin-python/src/main/jython/gremlin_python/jython_translator.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/jython_translator.py b/gremlin-python/src/main/jython/gremlin_python/jython_translator.py
index 45884a5..e71a2e9 100644
--- a/gremlin-python/src/main/jython/gremlin_python/jython_translator.py
+++ b/gremlin-python/src/main/jython/gremlin_python/jython_translator.py
@@ -17,6 +17,7 @@ specific language governing permissions and limitations
 under the License.
 '''
 
+import inspect
 import sys
 from aenum import Enum
 
@@ -81,12 +82,17 @@ class JythonTranslator(Translator):
             else:
                 return JythonTranslator.stringOrObject(arg.other) + "." + SymbolHelper.toJava(
                     arg.operator) + "(" + JythonTranslator.stringOrObject(arg.value) + ")"
-        elif callable(arg):  # lambdas
-            lambdaString = arg().strip()
-            if lambdaString.startswith("lambda"):
-                return lambdaString
+        elif callable(arg):  # lambda that produces a string that is a lambda
+            argLambdaString = arg().strip()
+            argLength = len(inspect.getargspec(eval(argLambdaString)).args)
+            if argLength == 0:
+                return "JythonZeroArgLambda(" + argLambdaString + ")"
+            elif argLength == 1:
+                return "JythonOneArgLambda(" + argLambdaString + ")"
+            elif argLength == 2:
+                return "JythonTwoArgLambda(" + argLambdaString + ")"
             else:
-                return "lambda: " + lambdaString
+                raise
         elif isinstance(arg, tuple) and 2 == len(arg) and isinstance(arg[0], str):  # bindings
             return arg[0]
         elif isinstance(arg, RawExpression):

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/7dd61859/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/java/translator/jython/PythonJythonTranslatorTest.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/java/translator/jython/PythonJythonTranslatorTest.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/java/translator/jython/PythonJythonTranslatorTest.java
index 7782e21..4d50809 100644
--- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/java/translator/jython/PythonJythonTranslatorTest.java
+++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/java/translator/jython/PythonJythonTranslatorTest.java
@@ -28,7 +28,6 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.util.function.Lambda;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -48,16 +47,15 @@ public class PythonJythonTranslatorTest {
     }
 
     @Test
-    @Ignore("Jython lambdas do not compile to Java8 functions")
     public void shouldSupportStringSupplierLambdas() throws Exception {
         final GraphTraversalSource g = TinkerFactory.createModern().traversal().withTranslator(PythonJythonTranslator.of("g", true));
         GraphTraversal.Admin<Vertex, Integer> t = g.withSideEffect("lengthSum", 0).withSack(1)
                 .V()
-                .filter(Lambda.predicate("lambda : \"lambda x: x.get().label().equals('person')\""))
-                .flatMap(Lambda.<Traverser<Vertex>, Iterator<Vertex>>function("lambda: 'lambda x: x.get().vertices(Direction.OUT)'"))
-                .map(Lambda.<Traverser<Vertex>, Integer>function("lambda: \"lambda x: x.get().value('name').length()\""))
+                .filter(Lambda.predicate("lambda : \"lambda x: x.get().label() == 'person'\""))
+                .flatMap(Lambda.<Traverser<Vertex>, Iterator<Vertex>>function("lambda: 'lambda x: x.get().vertices(OUT)'"))
+                .map(Lambda.<Traverser<Vertex>, Integer>function("lambda: \"lambda x: len(x.get().value('name'))\""))
                 .sideEffect(Lambda.consumer("lambda: \"lambda x : x.sideEffects('lengthSum', x.sideEffects('lengthSum') + x.get())\""))
-                .order().by(Lambda.comparator("lambda: 'lambda a,b : a.compareTo(b)'"))
+                .order().by(Lambda.comparator("lambda: 'lambda a,b : 1 if a > b else 0 if a == b else -1'"))
                 .sack(Lambda.biFunction("lambda: 'lambda a,b : a + b'"))
                 .asAdmin();
         final List<Integer> sacks = new ArrayList<>();