blob: 86473c40e0f5d7c8fcf111a086a2436bb5a16883 [file] [log] [blame]
/*
* 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.
*/
package org.apache.tinkerpop.gremlin.python.jsr223;
import org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin;
import org.apache.tinkerpop.gremlin.jsr223.CoreImports;
import org.apache.tinkerpop.gremlin.jsr223.Customizer;
import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory;
import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.python.jsr223.PyScriptEngine;
import org.python.jsr223.PyScriptEngineFactory;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import java.io.Reader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @deprecated As of release 3.3.10, not replaced - see TINKERPOP-2317
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
@Deprecated
public class GremlinJythonScriptEngine implements GremlinScriptEngine {
private final PyScriptEngine pyScriptEngine;
public GremlinJythonScriptEngine(final Customizer... customizers) {
this.pyScriptEngine = (PyScriptEngine) new PyScriptEngineFactory().getScriptEngine();
final List<Customizer> listOfCustomizers = new ArrayList<>(Arrays.asList(customizers));
// always need this plugin for a scriptengine to be "Gremlin-enabled"
CoreGremlinPlugin.instance().getCustomizers("gremlin-jython").ifPresent(c -> listOfCustomizers.addAll(Arrays.asList(c)));
final List<ImportCustomizer> importCustomizers = listOfCustomizers.stream()
.filter(p -> p instanceof ImportCustomizer)
.map(p -> (ImportCustomizer) p)
.collect(Collectors.toList());
try {
for (ImportCustomizer ic : importCustomizers) {
for (Class<?> c : ic.getClassImports()) {
if (null == c.getDeclaringClass())
this.pyScriptEngine.eval("from " + c.getPackage().getName() + " import " + c.getSimpleName());
else
this.pyScriptEngine.eval("from " + c.getPackage().getName() + "." + c.getDeclaringClass().getSimpleName() + " import " + c.getSimpleName());
}
for (Method m : ic.getMethodImports()) {
this.pyScriptEngine.eval(SymbolHelper.toPython(m.getName()) + " = " + m.getDeclaringClass().getSimpleName() + "." + m.getName());
}
// enums need to import after methods for some reason or else label comes in as a PyReflectedFunction
for (Enum e : ic.getEnumImports()) {
this.pyScriptEngine.eval(SymbolHelper.toPython(e.name()) + " = " + e.getDeclaringClass().getSimpleName() + "." + e.name());
}
}
loadSugar();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
@Override
public Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings, final String traversalSource) throws ScriptException {
// these validations occur before merging in bytecode bindings which will override existing ones. need to
// extract the named traversalsource prior to that happening so that bytecode bindings can share the same
// namespace as global bindings (e.g. traversalsources and graphs).
if (traversalSource.equals(HIDDEN_G))
throw new IllegalArgumentException("The traversalSource cannot have the name " + HIDDEN_G + " - it is reserved");
if (!bindings.containsKey(traversalSource))
throw new IllegalArgumentException("The bindings available to the ScriptEngine do not contain a traversalSource named: " + traversalSource);
final Object b = bindings.get(traversalSource);
if (!(b instanceof TraversalSource))
throw new IllegalArgumentException(traversalSource + " is of type " + b.getClass().getSimpleName() + " and is not an instance of TraversalSource");
final Bindings inner = new SimpleBindings();
inner.putAll(bindings);
inner.putAll(bytecode.getBindings());
inner.put(HIDDEN_G, b);
return (Traversal.Admin) this.eval(JythonTranslator.of(HIDDEN_G).translate(bytecode), inner);
}
@Override
public Object eval(final String script, final ScriptContext context) throws ScriptException {
return this.pyScriptEngine.eval(script, context);
}
@Override
public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
return this.pyScriptEngine.eval(reader, context);
}
@Override
public Object eval(final String script) throws ScriptException {
return this.pyScriptEngine.eval(script);
}
@Override
public Object eval(final Reader reader) throws ScriptException {
return this.pyScriptEngine.eval(reader);
}
@Override
public Object eval(final String script, final Bindings n) throws ScriptException {
this.pyScriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(n); // TODO: groovy and jython act different
return this.pyScriptEngine.eval(script);
}
@Override
public Object eval(final Reader reader, final Bindings n) throws ScriptException {
this.pyScriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(n); // TODO: groovy and jython act different
return this.pyScriptEngine.eval(reader);
}
@Override
public void put(final String key, final Object value) {
this.pyScriptEngine.put(key, value);
}
@Override
public Object get(final String key) {
return this.pyScriptEngine.get(key);
}
@Override
public Bindings getBindings(final int scope) {
return this.pyScriptEngine.getBindings(scope);
}
@Override
public void setBindings(final Bindings bindings, final int scope) {
this.pyScriptEngine.setBindings(bindings, scope);
}
@Override
public Bindings createBindings() {
return this.pyScriptEngine.createBindings();
}
@Override
public ScriptContext getContext() {
return this.pyScriptEngine.getContext();
}
@Override
public void setContext(final ScriptContext context) {
this.pyScriptEngine.setContext(context);
}
@Override
public GremlinScriptEngineFactory getFactory() {
return new GremlinJythonScriptEngineFactory();
}
private void loadSugar() throws ScriptException {
// add sugar methods
this.pyScriptEngine.eval("def getitem_bypass(self, index):\n" +
" if isinstance(index,int):\n return self.range(index,index+1)\n" +
" elif isinstance(index,slice):\n return self.range(index.start,index.stop)\n" +
" 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)\n");
this.pyScriptEngine.eval("\n" +
"from java.lang import Long\n" +
"import org.apache.tinkerpop.gremlin.util.function.Lambda\n" + // todo: remove or remove imported subclass names? (choose)
"from org.apache.tinkerpop.gremlin.util.function.Lambda import AbstractLambda\n" +
"from org.apache.tinkerpop.gremlin.util.function.Lambda import UnknownArgLambda\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 JythonUnknownArgLambda(UnknownArgLambda):\n" +
" def __init__(self,func,script='none',lang='gremlin-jython'):\n" +
" UnknownArgLambda.__init__(self, script, lang, -1)\n" +
" self.func = func\n" +
" def __repr__(self):\n" +
" return self.getLambdaScript()\n\n" +
"class JythonZeroArgLambda(ZeroArgLambda):\n" +
" def __init__(self,func,script='none',lang='gremlin-jython'):\n" +
" ZeroArgLambda.__init__(self, script, lang)\n" +
" self.func = func\n" +
" def __repr__(self):\n" +
" return self.getLambdaScript()\n" +
" def get(self):\n" +
" return self.func()\n\n" +
"class JythonOneArgLambda(OneArgLambda):\n" +
" def __init__(self,func,script='none',lang='gremlin-jython'):\n" +
" OneArgLambda.__init__(self, script, lang)\n" +
" self.func = func\n" +
" def __repr__(self):\n" +
" return self.getLambdaScript()\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,script='none',lang='gremlin-jython'):\n" +
" TwoArgLambda.__init__(self, script, lang)\n" +
" self.func = func\n" +
" def __repr__(self):\n" +
" return self.getLambdaScript()\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"
);
}
}