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

org.sikuli.util.JythonHelper Maven / Gradle / Ivy

There is a newer version: 2.0.5
Show newest version
/*
 * Copyright (c) 2010-2016, Sikuli.org, sikulix.com
 * Released under the MIT License.
 *
 */
package org.sikuli.util;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.sikuli.basics.Debug;
import org.sikuli.basics.FileManager;
import org.sikuli.basics.Settings;
import org.sikuli.script.ImagePath;
import org.sikuli.script.RunTime;
import org.sikuli.script.Sikulix;

public class JythonHelper implements JLangHelperInterface {

  static RunTime runTime = RunTime.get();

  //
  private static final String me = "JythonSupport: ";
  private static int lvl = 3;

  public void log(int level, String message, Object... args) {
    Debug.logx(level, me + message, args);
  }

  private void logp(String message, Object... args) {
    Debug.logx(-3, message, args);
  }

  private void logp(int level, String message, Object... args) {
    if (level <= Debug.getDebugLevel()) {
      logp(message, args);
    }
  }

  public void terminate(int retVal, String msg, Object... args) {
    runTime.terminate(retVal, me + msg, args);
  }
  //

  static JythonHelper instance = null;
  static Object interpreter = null;
  List sysPath = new ArrayList();
  List sysArgv = new ArrayList();
  int nPathAdded = 0;
  int nPathSaved = -1;
  static Class[] nc = new Class[0];
  static Class[] nc1 = new Class[1];
  static Class cInterpreter = null;
  static Class cPyException = null;
  static Class cList = null;
  static Class cPy = null;
  static Class cPyFunction = null;
  static Class cPyMethod = null;
  static Class cPyInstance = null;
  static Class cPyObject = null;
  static Class cPyString = null;
  static Method mLen, mGet, mSet, mAdd, mRemove, mClear;
  static Method mGetSystemState, mExec, mExecfile;
  static Field PI_path;

  private JythonHelper() {
  }

  public static JythonHelper get() {
    if (instance == null) {
      instance = new JythonHelper();
      instance.log(lvl, "init: starting");
      try {
        cInterpreter = Class.forName("org.python.util.PythonInterpreter");
      } catch (Exception ex) {
        String sJython = new File(runTime.SikuliJython).getName();
        File fJython = new File(runTime.fSikulixDownloadsGeneric, sJython);
        instance.log(lvl, "trying to use setup downloaded Jython:\n%s", fJython.getAbsolutePath());
        if (fJython.exists()) {
          runTime.addToClasspath(fJython.getAbsolutePath());
        } else {
          instance.log(-1, "Not possible to get a Jython on to the classpath!");
          cInterpreter = null;
        }
      }
      try {
        cInterpreter = Class.forName("org.python.util.PythonInterpreter");
        mGetSystemState = cInterpreter.getMethod("getSystemState", nc);
        mExec = cInterpreter.getMethod("exec", new Class[]{String.class});
        mExecfile = cInterpreter.getMethod("execfile", new Class[]{String.class});
        Constructor PI_new = cInterpreter.getConstructor(nc);
        interpreter = PI_new.newInstance(null);
        cPyException = Class.forName("org.python.core.PyException");
        cList = Class.forName("org.python.core.PyList");
        cPy = Class.forName("org.python.core.Py");
        cPyFunction = Class.forName("org.python.core.PyFunction");
        cPyMethod = Class.forName("org.python.core.PyMethod");
        cPyInstance = Class.forName("org.python.core.PyInstance");
        cPyObject = Class.forName("org.python.core.PyObject");
        cPyString = Class.forName("org.python.core.PyString");
        mLen = cList.getMethod("__len__", nc);
        mClear = cList.getMethod("clear", nc);
        mGet = cList.getMethod("get", new Class[]{int.class});
        mSet = cList.getMethod("set", new Class[]{int.class, Object.class});
        mAdd = cList.getMethod("add", new Class[]{Object.class});
        mRemove = cList.getMethod("remove", new Class[]{int.class});
      } catch (Exception ex) {
        cInterpreter = null;
      }
      instance.log(lvl, "init: success");
    }
    if (cInterpreter == null) {
      instance.runTime.terminate(1, "JythonHelper: no Jython on classpath");
    }
    runTime.isJythonReady = true;
    return instance;
  }

  private void noOp() {
  } // for debugging as breakpoint

  class PyException {

    Object inst = null;
    Field fType = null;
    Field fValue = null;
    Field fTrBack = null;

    public PyException(Object i) {
      inst = i;
      cPyException.cast(inst);
      try {
        fType = cPyException.getField("type");
        fValue = cPyException.getField("value");
        fTrBack = cPyException.getField("traceback");
      } catch (Exception ex) {
        noOp();
      }
    }

    public int isTypeExit() {
      try {
        if (fType.get(inst).toString().contains("SystemExit")) {
          return Integer.parseInt(fValue.get(inst).toString());
        }
      } catch (Exception ex) {
        return -999;
      }
      return -1;
    }
  }

  class PyInstance {

    Object inst = null;
    Method mGetAttr = null;
    Method mInvoke = null;

    public PyInstance(Object i) {
      inst = i;
      cPyInstance.cast(inst);
      try {
        mGetAttr = cPyInstance.getMethod("__getattr__", String.class);
        mInvoke = cPyInstance.getMethod("invoke", String.class, cPyObject);
      } catch (Exception ex) {
        noOp();
      }
    }

    public Object get() {
      return inst;
    }

    Object __getattr__(String mName) {
      if (mGetAttr == null) {
        return null;
      }
      Object method = null;
      try {
        method = mGetAttr.invoke(inst, mName);
      } catch (Exception ex) {
      }
      return method;
    }

    public void invoke(String mName, Object arg) {
      if (mInvoke != null) {
        try {
          mInvoke.invoke(inst, mName, arg);
        } catch (Exception ex) {
          noOp();
        }
      }
    }
  }

  class PyFunction {

    public String __name__;
    Object func = null;
    Method mCall = null;
    Method mCall1 = null;

    public PyFunction(Object f) {
      func = f;
      try {
        cPyFunction.cast(func);
        mCall = cPyFunction.getMethod("__call__");
        mCall1 = cPyFunction.getMethod("__call__", cPyObject);
      } catch (Exception ex) {
        func = null;
      }
      if (func == null) {
        try {
          func = f;
          cPyMethod.cast(func);
          mCall = cPyMethod.getMethod("__call__");
          mCall1 = cPyMethod.getMethod("__call__", cPyObject);
        } catch (Exception ex) {
          func = null;
        }
      }
    }

    void __call__(Object arg) {
      if (mCall1 != null) {
        try {
          mCall1.invoke(func, arg);
        } catch (Exception ex) {
        }
      }
    }

    void __call__() {
      if (mCall != null) {
        try {
          mCall.invoke(func);
        } catch (Exception ex) {
        }
      }
    }
  }

  class Py {

    Method mJava2py = null;

    public Py() {
      try {
        mJava2py = cPy.getMethod("java2py", Object.class);
      } catch (Exception ex) {
        noOp();
      }
    }

    Object java2py(Object arg) {
      if (mJava2py == null) {
        return null;
      }
      Object pyObject = null;
      try {
        pyObject = mJava2py.invoke(null, arg);
      } catch (Exception ex) {
        noOp();
      }
      return pyObject;
    }
  }

  class PyString {

    String aString = "";
    Object pyString = null;

    public PyString(String s) {
      aString = s;
      try {
        pyString = cPyString.getConstructor(String.class).newInstance(aString);
      } catch (Exception ex) {
      }
    }

    public Object get() {
      return pyString;
    }
  }

  public boolean exec(String code) {
    try {
      mExec.invoke(interpreter, code);
    } catch (Exception ex) {
      PyException pex = new PyException(ex.getCause());
      if (pex.isTypeExit() < 0) {
        log(-1, "exec: returns:\n%s", ex.getCause());
      }
      return false;
    }
    return true;
  }

  public int execfile(String fpScript) {
    int retval = -999;
    try {
      mExecfile.invoke(interpreter, fpScript);
    } catch (Exception ex) {
      PyException pex = new PyException(ex.getCause());
      if ((retval = pex.isTypeExit()) < 0) {
        log(-1, "execFile: returns:\n%s", ex.getCause());
      }
    }
    return retval;
  }

//TODO check signature (instance method)
  public boolean checkCallback(Object[] args) {
    PyInstance inst = new PyInstance(args[0]);
    String mName = (String) args[1];
    Object method = inst.__getattr__(mName);
    if (method == null || !method.getClass().getName().contains("PyMethod")) {
      log(-100, "checkCallback: Object: %s, Method not found: %s", inst, mName);
      return false;
    }
    return true;
  }

  public boolean runLoggerCallback(Object[] args) {
    PyInstance inst = new PyInstance(args[0]);
    String mName = (String) args[1];
    String msg = (String) args[2];
    Object method = inst.__getattr__(mName);
    if (method == null || !method.getClass().getName().contains("PyMethod")) {
      log(-100, "runLoggerCallback: Object: %s, Method not found: %s", inst, mName);
      return false;
    }
    try {
      PyString pmsg = new PyString(msg);
      inst.invoke(mName, pmsg.get());
    } catch (Exception ex) {
      log(-100, "runLoggerCallback: invoke: %s", ex.getMessage());
      return false;
    }
    return true;
  }

  @Override
  public boolean runObserveCallback(Object[] args) {
    PyFunction func = new PyFunction(args[0]);
    boolean success = true;
    try {
      func.__call__(new Py().java2py(args[1]));
    } catch (Exception ex) {
//      if (!"".equals(func.__name__)) {
      if (!func.toString().contains("")) {
        log(-1, "runObserveCallback: jython invoke: %s", ex.getMessage());
        return false;
      }
      success = false;
    }
    if (success) {
      return true;
    }
    try {
      func.__call__();
    } catch (Exception ex) {
      log(-1, "runObserveCallback: jython invoke : %s", ex.getMessage());
      return false;
    }
    return true;
  }

  //TODO implement generalized callback
  public boolean runCallback(Object[] args) {
    PyInstance inst = (PyInstance) args[0];
    String mName = (String) args[1];
    Object method = inst.__getattr__(mName);
    if (method == null || !method.getClass().getName().contains("PyMethod")) {
      log(-1, "runCallback: Object: %s, Method not found: %s", inst, mName);
      return false;
    }
    try {
      PyString pmsg = new PyString("not yet supported");
      inst.invoke(mName, pmsg.get());
    } catch (Exception ex) {
      log(-1, "runCallback: invoke: %s", ex.getMessage());
      return false;
    }
    return true;
  }

  public static JythonHelper set(Object ip) {
    JythonHelper.get();
    interpreter = ip;
    return instance;
  }

  public boolean prepareRobot() {
    if (runTime.isRunningFromJar()) {
      File fLibRobot = new File(runTime.fSikulixLib, "robot");
      if (!fLibRobot.exists()) {
        log(-1, "prepareRobot: not available: %s", fLibRobot);
        Sikulix.terminate(1);
      }
      if (!hasSysPath(runTime.fSikulixLib.getAbsolutePath())) {
        insertSysPath(runTime.fSikulixLib);
      }
    }
    if (!hasSysPath(new File(Settings.BundlePath).getParent())) {
      appendSysPath(new File(Settings.BundlePath).getParent());
    }
    exec("import robot");
    return true;
  }

  public String load(String fpJarOrFolder) {
//##
//# loads a Sikuli extension (.jar) from
//#  1. user's sikuli data path
//#  2. bundle path
//#
//def load(jar):
//    def _load(abspath):
//        if os.path.exists(abspath):
//            if not abspath in sys.path:
//                sys.path.append(abspath)
//            return True
//        return False
//
//    if JythonHelper.load(jar):
//        return True
//
//    if _load(jar):
//        return True
//    path = getBundlePath()
//    if path:
//        jarInBundle = os.path.join(path, jar)
//        if _load(jarInBundle):
//            return True
//    path = ExtensionManager.getInstance().getLoadPath(jar)
//    if path and _load(path):
//        return True
//    return False
    log(lvl, "load: to be loaded:\n%s", fpJarOrFolder);
    if (!fpJarOrFolder.endsWith(".jar")) {
      fpJarOrFolder += ".jar";
    }
    String fpBundle = ImagePath.getBundlePath();
    File fJar = new File(FileManager.normalizeAbsolute(fpJarOrFolder, false));
    if (!fJar.exists()) {
      fJar = new File(fpBundle, fpJarOrFolder);
      fJar = new File(FileManager.normalizeAbsolute(fJar.getPath(), false));
      if (!fJar.exists()) {
        fJar = new File(runTime.fSikulixExtensions, fpJarOrFolder);
        if (!fJar.exists()) {
          fJar = new File(runTime.fSikulixLib, fpJarOrFolder);
          if (!fJar.exists()) {
            fJar = null;
          }
        }
      }
    }
    if (fJar != null) {
      if (runTime.addToClasspath(fJar.getPath())) {
        if (!hasSysPath(fJar.getPath())) {
          insertSysPath(fJar);
        }
      } else {
        log(-1, "load: not possible");
      }
    } else {
      log(-1, "load: could not be found - even not in bundle nor in Lib nor in Extensions");
    }
    if (fJar == null) {
      return null;
    }
    return fJar.getAbsolutePath();
  }

  private long lastRun = 0;

  private List importedScripts = new ArrayList();
  String name = "";
  public void reloadImported() {
    if (lastRun > 0) {
      for (File fMod : importedScripts) {
        name = getPyName(fMod);
        if (new File(fMod, name + ".py").lastModified() > lastRun) {
          log(lvl, "reload: %s", fMod);
          get().exec("reload(" + name + ")");
        };
      }
    }
    lastRun = new Date().getTime();
  }

  private String getPyName(File fMod) {
    String ending = ".sikuli";
    String name = fMod.getName();
    if (name.endsWith(ending)) {
      name = name.substring(0, name.length() - ending.length());
    }
    return name;
  }

  public String findModule(String modName, Object packPath, Object sysPath) {
    if (modName.endsWith(".*")) {
      log(lvl + 1, "findModule: %s", modName);
      return null;
    }
    if (packPath != null) {
      log(lvl + 1, "findModule: in pack: %s (%s)", modName, packPath);
      return null;
    }
    int nDot = modName.lastIndexOf(".");
    String modNameFull =  modName;
    if (nDot > -1) {
      modName = modName.substring(nDot + 1);
    }
    String fpBundle = ImagePath.getBundlePath();
    File fParentBundle = null;
    File fModule = null;
    if (fpBundle != null) {
      fParentBundle = new File(fpBundle).getParentFile();
      fModule = existsModule(modName, fParentBundle);
    }
    if (fModule == null) {
      fModule = existsSysPathModule(modName);
      if (fModule == null) {
        return null;
      }
    }
    log(lvl + 1, "findModule: final: %s [%s]", fModule.getName(), fModule.getParent());
    if (fModule.getName().endsWith(".sikuli")) {
      importedScripts.add(fModule);
      return fModule.getAbsolutePath();
    }
    return null;
  }

  public String loadModulePrepare(String modName, String modPath) {
    log(lvl + 1, "loadModulePrepare: %s in %s", modName, modPath);
    int nDot = modName.lastIndexOf(".");
    if (nDot > -1) {
      modName = modName.substring(nDot + 1);
    }
    addSysPath(modPath);
    if (modPath.endsWith(".sikuli")) {
      ImagePath.add(modPath);
    }
    return modName;
  }

  private File existsModule(String mName, File fFolder) {
    if (mName.endsWith(".sikuli") || mName.endsWith(".py")) {
      return null;
    }
    File fSikuli = new File(fFolder, mName + ".sikuli");
    if (fSikuli.exists()) {
      return fSikuli;
    }
    File fPython = new File(fFolder, mName + ".py");
    if (fPython.exists()) {
      return fPython;
    }
    return null;
  }

  public void getSysArgv() {
    sysArgv = new ArrayList();
    if (null == cInterpreter) {
      sysArgv = null;
      return;
    }
    try {
      Object aState = mGetSystemState.invoke(interpreter, (Object[]) null);
      Field fArgv = aState.getClass().getField("argv");
      Object pyArgv = fArgv.get(aState);
      Integer argvLen = (Integer) mLen.invoke(pyArgv, (Object[]) null);
      for (int i = 0; i < argvLen; i++) {
        String entry = (String) mGet.invoke(pyArgv, i);
        log(lvl + 1, "sys.path[%2d] = %s", i, entry);
        sysArgv.add(entry);
      }
    } catch (Exception ex) {
      sysArgv = null;
    }
  }

  public void setSysArgv(String[] args) {
    if (null == cInterpreter || null == sysArgv) {
      return;
    }
    try {
      Object aState = mGetSystemState.invoke(interpreter, (Object[]) null);
      Field fArgv = aState.getClass().getField("argv");
      Object pyArgv = fArgv.get(aState);
      mClear.invoke(pyArgv, null);
      for (String arg : args) {
        mAdd.invoke(pyArgv, arg);
      }
    } catch (Exception ex) {
      sysArgv = null;
    }
  }

  public void getSysPath() {
    sysPath = new ArrayList();
    if (null == cInterpreter) {
      sysPath = null;
      return;
    }
    try {
      Object aState = mGetSystemState.invoke(interpreter, (Object[]) null);
      Field fPath = aState.getClass().getField("path");
      Object pyPath = fPath.get(aState);
      Integer pathLen = (Integer) mLen.invoke(pyPath, (Object[]) null);
      for (int i = 0; i < pathLen; i++) {
        String entry = (String) mGet.invoke(pyPath, i);
        log(lvl + 1, "sys.path[%2d] = %s", i, entry);
        sysPath.add(entry);
      }
    } catch (Exception ex) {
      sysPath = null;
    }
  }

  public void setSysPath() {
    if (null == cInterpreter || null == sysPath) {
      return;
    }
    try {
      Object aState = mGetSystemState.invoke(interpreter, (Object[]) null);
      Field fPath = aState.getClass().getField("path");
      Object pyPath = fPath.get(aState);
      Integer pathLen = (Integer) mLen.invoke(pyPath, (Object[]) null);
      for (int i = 0; i < pathLen && i < sysPath.size(); i++) {
        String entry = sysPath.get(i);
        log(lvl + 1, "sys.path.set[%2d] = %s", i, entry);
        mSet.invoke(pyPath, i, entry);
      }
      if (pathLen < sysPath.size()) {
        for (int i = pathLen; i < sysPath.size(); i++) {
          String entry = sysPath.get(i);
          log(lvl + 1, "sys.path.add[%2d] = %s", i, entry);
          mAdd.invoke(pyPath, entry);
        }
      }
      if (pathLen > sysPath.size()) {
        for (int i = sysPath.size(); i < pathLen; i++) {
          String entry = (String) mGet.invoke(pyPath, i);
          log(lvl + 1, "sys.path.rem[%2d] = %s", i, entry);
          mRemove.invoke(pyPath, i);
        }
      }
    } catch (Exception ex) {
      sysPath = null;
    }
  }

  public void addSitePackages() {
    File fLibFolder = runTime.fSikulixLib;
    File fSitePackages = new File(fLibFolder, "site-packages");
    if (fSitePackages.exists()) {
      addSysPath(fSitePackages);
      if (hasSysPath(fSitePackages.getAbsolutePath())) {
        log(lvl, "added as Jython::sys.path[0]:\n%s", fSitePackages);
      }
      File fSites = new File(fSitePackages, "sites.txt");
      String sSites = "";
      if (fSites.exists()) {
        sSites = FileManager.readFileToString(fSites);
        if (!sSites.isEmpty()) {
          log(lvl, "found Lib/site-packages/sites.txt");
          String[] listSites = sSites.split("\n");
          for (String site : listSites) {
            String path = site.trim();
            if (!path.isEmpty()) {
              appendSysPath(path);
              log(lvl, "adding from Lib/site-packages/sites.txt:\n%s", path);
            }
          }
        }
      }
    }
    String fpBundle = ImagePath.getPath(0);
    if (fpBundle != null) {
      addSysPath(fpBundle);
    }
  }

  public void addSysPath(String fpFolder) {
    if (!hasSysPath(fpFolder)) {
      sysPath.add(0, fpFolder);
      setSysPath();
      nPathAdded++;
    }
  }

  public void appendSysPath(String fpFolder) {
    if (!hasSysPath(fpFolder)) {
      sysPath.add(fpFolder);
      setSysPath();
      nPathAdded++;
    }
  }

  public void putSysPath(String fpFolder, int n) {
    if (n < 1 || n > sysPath.size()) {
      addSysPath(fpFolder);
    } else {
      sysPath.add(n, fpFolder);
      setSysPath();
      nPathAdded++;
    }
  }

  public void addSysPath(File fFolder) {
    addSysPath(fFolder.getAbsolutePath());
  }

  public void insertSysPath(File fFolder) {
    getSysPath();
    sysPath.add((nPathSaved > -1 ? nPathSaved : 0), fFolder.getAbsolutePath());
    setSysPath();
    nPathSaved = -1;
  }

  public void removeSysPath(File fFolder) {
    int n;
    if (-1 < (n = getSysPathEntry(fFolder))) {
      sysPath.remove(n);
      nPathSaved = n;
      setSysPath();
      nPathAdded = nPathAdded == 0 ? 0 : nPathAdded--;
    }
  }

  public boolean hasSysPath(String fpFolder) {
    getSysPath();
    for (String fpPath : sysPath) {
      if (FileManager.pathEquals(fpPath, fpFolder)) {
        return true;
      }
    }
    return false;
  }

  public int getSysPathEntry(File fFolder) {
    getSysPath();
    int n = 0;
    for (String fpPath : sysPath) {
      if (FileManager.pathEquals(fpPath, fFolder.getAbsolutePath())) {
        return n;
      }
      n++;
    }
    return -1;
  }

  public File existsSysPathModule(String modname) {
    getSysPath();
    File fModule = null;
    for (String fpPath : sysPath) {
      fModule = existsModule(modname, new File(fpPath));
      if (null != fModule) {
        break;
      }
    }
    return fModule;
  }

  public File existsSysPathJar(String fpJar) {
    getSysPath();
    File fJar = null;
    for (String fpPath : sysPath) {
      fJar = new File(fpPath, fpJar);
      if (fJar.exists()) {
        break;
      }
      fJar = null;
    }
    return fJar;
  }

  public void showSysPath() {
    if (Debug.is(lvl)) {
      getSysPath();
      log(lvl, "***** Jython sys.path");
      for (int i = 0; i < sysPath.size(); i++) {
        logp(lvl, "%2d: %s", i, sysPath.get(i));
      }
      log(lvl, "***** Jython sys.path end");
    }
  }

  public String getCurrentLine() {
    String trace = "";
    Object frame = null;
    Object back = null;
    try {
      Method mGetFrame = cPy.getMethod("getFrame", nc);
      Class cPyFrame = Class.forName("org.python.core.PyFrame");
      Field fLineno = cPyFrame.getField("f_lineno");
      Field fCode = cPyFrame.getField("f_code");
      Field fBack = cPyFrame.getField("f_back");
      Class cPyBaseCode = Class.forName("org.python.core.PyBaseCode");
      Field fFilename = cPyBaseCode.getField("co_filename");
      frame = mGetFrame.invoke(cPy, (Object[]) null);
      back = fBack.get(frame);
      if (null == back) {
        trace = "Jython: at " + getCurrentLineTraceElement(fLineno, fCode, fFilename, frame);
      } else {
        trace = "Jython traceback - current first:\n"
                + getCurrentLineTraceElement(fLineno, fCode, fFilename, frame);
        while (null != back) {
          String line = getCurrentLineTraceElement(fLineno, fCode, fFilename, back);
          if (! line.startsWith("Region (")) {
            trace += "\n" + line;
          }
          back = fBack.get(back);
        }
      }
    } catch (Exception ex) {
      trace = String.format("Jython: getCurrentLine: INSPECT EXCEPTION: %s", ex);
    }
    return trace;
  }

  private String getCurrentLineTraceElement(Field fLineno, Field fCode, Field fFilename, Object frame) {
    String trace = "";
    try {
      int lineno = fLineno.getInt(frame);
      Object code = fCode.get(frame);
      Object filename = fFilename.get(code);
      String fname = FileManager.getName((String) filename);
      fname = fname.replace(".py", "");
      trace = String.format("%s (%d)", fname, lineno);
    } catch (Exception ex) {
    }
    return trace;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy