Skip to content

Commit

Permalink
chore(py-script-engine): add new function to read function parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
ashleycaselli committed Jan 9, 2024
1 parent 97de168 commit 7294660
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 35 deletions.
30 changes: 10 additions & 20 deletions src/main/java/org/topbraid/shacl/py/GraalPyScriptEngine.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.topbraid.shacl.py;

import org.apache.jena.query.QuerySolution;
import org.apache.jena.rdf.model.Resource;
import org.graalvm.polyglot.Context;
import org.topbraid.shacl.py.model.PyTermFactory;

import javax.script.ScriptException;

Expand All @@ -15,12 +14,19 @@ public class GraalPyScriptEngine extends PyScriptEngineImpl {

public GraalPyScriptEngine() {
initEngine();
// TODO implement
context.getPolyglotBindings().putMember("PyTermFactory", new PyTermFactory());
context.eval("python",
"""
import polyglot\s
py_tf = polyglot.import_value('PyTermFactory')
""");
context.eval("python", ARGS_FUNCTION);
}

@Override
public void initEngine() {
this.context = Context.newBuilder()
.allowAllAccess(true)
.option("engine.WarnInterpreterOnly", "false")
.build();
if (this.context == null) {
Expand All @@ -33,33 +39,17 @@ public Object eval(String expr) throws ScriptException {
return context.eval("python", expr);
}

@Override
public void executeLibraries(Resource exec) throws Exception {
// TODO implement
}

@Override
public void executeScriptFromURL(String url) throws Exception {
// TODO implement
}

@Override
public Object get(String varName) {
return context.getBindings("python").getMember(varName);
}

@Override
public Object invokeFunction(String functionName, QuerySolution bindings) throws ScriptException, NoSuchMethodException {
return null;
// TODO implement
}

public final Context getContext() {
return context;
}

@Override
public Object invokeFunctionOrdered(String functionName, Object[] args) throws ScriptException, NoSuchMethodException {
public Object invokeFunctionOrdered(String functionName, Object[] args) {
return context.getBindings("python").getMember(functionName).execute(args);
}

Expand Down
106 changes: 106 additions & 0 deletions src/main/java/org/topbraid/shacl/py/PyGraph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Licensed 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.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*/
package org.topbraid.shacl.py;

import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.topbraid.jenax.util.ExceptionUtil;
import org.topbraid.shacl.js.ScriptEngine;
import org.topbraid.shacl.py.model.PyFactory;
import org.topbraid.shacl.py.model.PyTriple;
import org.topbraid.shacl.util.FailureLog;

import java.util.HashSet;
import java.util.Set;

public class PyGraph {

protected ScriptEngine engine;
private Graph graph;
private Set<PyTripleIterator> openIterators = new HashSet<>();

public PyGraph(Graph graph, ScriptEngine engine) {
this.engine = engine;
this.graph = graph;
}

public void close() {
if (!openIterators.isEmpty()) {
FailureLog.get().logWarning("Python graph session ended but " + openIterators.size() + " iterators have not been closed. Make sure to close them programmatically to avoid resource leaks and locking problems.");
}
closeIterators();
}

public void closeIterators() {
for (PyTripleIterator stream : openIterators) {
stream.closeIterator();
}
openIterators.clear();
}

public Graph getGraph() {
return graph;
}

public PyTripleIterator find(Object subjectSOM, Object predicateSOM, Object objectSOM) {
Node subject = PyFactory.getNode(subjectSOM);
Node predicate = PyFactory.getNode(predicateSOM);
Node object = PyFactory.getNode(objectSOM);
ExtendedIterator<Triple> it = getGraph().find(subject, predicate, object);
PyTripleIterator pyit = new PyTripleIterator(it);
openIterators.add(pyit);
return pyit;
}

public Object query() {
try {
return engine.invokeFunctionOrdered("RDFQuery", new Object[]{this});
} catch (Exception ex) {
throw ExceptionUtil.throwUnchecked(ex);
}
}

public class PyTripleIterator {

private ExtendedIterator<Triple> it;

PyTripleIterator(ExtendedIterator<Triple> it) {
this.it = it;
}

public void close() {
closeIterator();
openIterators.remove(this);
}

void closeIterator() {
it.close();
}

public PyTriple next() {
if (it.hasNext()) {
Triple triple = it.next();
return PyFactory.asPyTriple(triple);
} else {
close();
return null;
}
}
}
}
24 changes: 9 additions & 15 deletions src/main/java/org/topbraid/shacl/py/PyScriptEngineImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,15 @@ public abstract class PyScriptEngineImpl implements ScriptEngine {
protected final static String ARGS_FUNCTION_NAME = "get_args";

protected final static String ARGS_FUNCTION = "def " + ARGS_FUNCTION_NAME + "(func_str): \n" +
"\t start_idx = func_str.find('(') + 1\n" +
"\t end_idx = func_str.find(')', start_idx)\n" +
"\t if start_idx != -1 and end_idx != -1:\n" +
"\t\t params_str = func_str[start_idx:end_idx]\n" +
"\t\t args_list = [arg.strip() for arg in params_str.split(',')]\n" +
"\t\t return args_list\n" +
"\t else:\n" +
"\t start_idx = func_str.find('(') + 1 \n" +
"\t end_idx = func_str.find(')', start_idx) \n" +
"\t if start_idx != -1 and end_idx != -1: \n" +
"\t\t params_str = func_str[start_idx:end_idx] \n" +
"\t\t args_list = [arg.strip() for arg in params_str.split(',')] \n" +
"\t\t return args_list \n" +
"\t else: \n" +
"\t\t return []";

// Copied from https://davidwalsh.name/javascript-arguments
protected final static String ARGS_FUNCTION_2 =
"def " + ARGS_FUNCTION_NAME + "(funcString): \n" +
"\t func_code = func.__code__ \n" +
"\t args_list = func_code.co_varnames[:func_code.co_argcount]\n" +
"\t return args_list\n";

public static final String DASH_PY = "http://datashapes.org/py/dash.py";

public static final String RDFQUERY_PY = "http://datashapes.org/py/rdfquery.py";
Expand Down Expand Up @@ -114,6 +107,7 @@ public Object invokeFunction(String functionName, QuerySolution bindings) throws
public abstract void put(String varName, Object value);

protected Reader createScriptReader(String url) throws Exception {
// TODO replace dash.js and rdfquery.js with python versions
if (DASH_PY.equals(url)) {
return new InputStreamReader(GraalPyScriptEngine.class.getResourceAsStream("/js/dash.js"));
} else if (RDFQUERY_PY.equals(url)) {
Expand All @@ -133,7 +127,6 @@ private List<String> getFunctionParameters(String functionName) throws ScriptExc
throw new ScriptException("Cannot find Python function \"" + functionName + "\"");
}
try {
//String funcString = what.toString();
String funcString = ((Value) what).getSourceLocation().getCharacters().toString();
Object result = this.invokeFunctionOrdered(ARGS_FUNCTION_NAME, Collections.singletonList(funcString).toArray());
List<String> results = ScriptEngineUtil.asArray(result).stream().map(Object::toString).collect(Collectors.toList());
Expand All @@ -143,4 +136,5 @@ private List<String> getFunctionParameters(String functionName) throws ScriptExc
throw new ScriptException(ex);
}
}

}

0 comments on commit 7294660

Please sign in to comment.