All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.deephaven.engine.util.PythonScopeJpyImpl Maven / Gradle / Ivy

There is a newer version: 0.36.1
Show newest version
/**
 * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
 */
package io.deephaven.engine.util;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import io.deephaven.UncheckedDeephavenException;
import io.deephaven.configuration.Configuration;
import org.jpy.PyDictWrapper;
import org.jpy.PyLib;
import org.jpy.PyObject;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

public class PythonScopeJpyImpl implements PythonScope {
    private static volatile boolean cacheEnabled =
            Configuration.getInstance().getBooleanForClassWithDefault(PythonScopeJpyImpl.class, "cacheEnabled", false);

    public static void setCacheEnabled(boolean enabled) {
        cacheEnabled = enabled;
    }

    private final PyDictWrapper dict;

    private static final ThreadLocal> threadScopeStack = new ThreadLocal<>();
    private static final Cache conversionCache = CacheBuilder.newBuilder().weakValues().build();

    public static PythonScopeJpyImpl ofMainGlobals() {
        return new PythonScopeJpyImpl(PyLib.getMainGlobals().asDict());
    }

    public PythonScopeJpyImpl(PyDictWrapper dict) {
        this.dict = dict;
    }

    @Override
    public PyDictWrapper currentScope() {
        Deque scopeStack = threadScopeStack.get();
        if (scopeStack == null || scopeStack.isEmpty()) {
            return this.dict;
        } else {
            return scopeStack.peek();
        }
    }

    @Override
    public Optional getValueRaw(String name) {
        // note: we *may* be returning Optional.of(None)
        // None is a valid PyObject, and can be in scope
        return Optional.ofNullable(currentScope().get(name));
    }

    @Override
    public boolean containsKey(String name) {
        return currentScope().containsKey(name);
    }

    @Override
    public String convertStringKey(PyObject key) {
        if (!key.isString()) {
            throw new IllegalArgumentException(
                    "Found non-string key! Expecting only string keys. " + key);
        }
        return key.toString();
    }

    @Override
    public Object convertValue(PyObject value) {
        if (value.isNone()) {
            return value;
        }
        return convert(value);
    }

    /**
     * Converts a pyObject into an appropriate Java object for use outside of JPy.
     * 

* If we're a List, Dictionary, or Callable, then we wrap them in a java object. *

* If it is a primitive (or a wrapped Java object); we convert it to the java object. *

* Otherwise we return the raw PyObject and the user can do with it what they will. * * @param pyObject the JPy wrapped PyObject. * @return a Java object representing the underlying JPy object. */ public static Object convert(PyObject pyObject) { if (!cacheEnabled) { return convertInternal(pyObject, false); } try { final Object cached = conversionCache.get(pyObject, () -> convertInternal(pyObject, true)); return cached instanceof NULL_TOKEN ? null : cached; } catch (ExecutionException err) { throw new UncheckedDeephavenException("Error converting PyObject to Java object", err); } } private static Object convertInternal(PyObject pyObject, boolean fromCache) { Object ret = pyObject; if (pyObject.isList()) { ret = pyObject.asList(); } else if (pyObject.isDict()) { ret = pyObject.asDict(); } else if (pyObject.isCallable()) { ret = new PyCallableWrapperJpyImpl(pyObject); } else if (pyObject.isConvertible()) { ret = pyObject.getObjectValue(); } return ret == null && fromCache ? new NULL_TOKEN() : ret; } public PyDictWrapper mainGlobals() { return dict; } @Override public void pushScope(PyObject pydict) { Deque scopeStack = threadScopeStack.get(); if (scopeStack == null) { scopeStack = new ArrayDeque<>(); threadScopeStack.set(scopeStack); } scopeStack.push(pydict.asDict()); } @Override public void popScope() { Deque scopeStack = threadScopeStack.get(); if (scopeStack == null) { throw new IllegalStateException("The thread scope stack is empty."); } PyDictWrapper pydict = scopeStack.pop(); pydict.close(); } /** * Guava caches are not allowed to hold on to null values. Additionally, we can't use a singleton pattern or else * the weak-value map will never release null values. */ private static class NULL_TOKEN { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy