package org.python.pydev.editor.codecompletion.shell; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.python.copiedfromeclipsesrc.JDTNotAvailableException; import org.python.pydev.core.IInterpreterInfo; import org.python.pydev.core.IInterpreterManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.PythonNatureWithoutProjectException; import org.python.pydev.core.log.Log; import org.python.pydev.editor.codecompletion.revisited.ModulesManager; import org.python.pydev.logging.DebugSettings; import org.python.pydev.plugin.PydevPlugin; public class ShellsContainer { /** * Reference to 'global python shells' * * this works as follows: * we have the interpreter as that the shell is related to as the 1st key * * and then we have the id with the shell type that points to the actual shell * * @see #MAIN_THREAD_SHELL * @see #OTHER_THREADS_SHELL */ private static Map<String, Map<Integer, AbstractShell>> shells = new HashMap<String, Map<Integer, AbstractShell>>(); /** * simple stop of a shell (it may be later restarted) */ public static void stopServerShell(IInterpreterInfo interpreter, int id) { synchronized (shells) { Map<Integer, AbstractShell> typeToShell = getTypeToShellFromId(interpreter); AbstractShell pythonShell = typeToShell.get(id); if (pythonShell != null) { try { pythonShell.endIt(); } catch (Exception e) { // ignore... we are ending it anyway... } } typeToShell.remove(id); //there's no exception if it was not there in the 1st place... } } /** * Stops all registered shells (should only be called at plugin shutdown). */ public static void shutdownAllShells() { synchronized (shells) { if (DebugSettings.DEBUG_CODE_COMPLETION) { Log.toLogFile("Shutting down all shells (for good)...", AbstractShell.class); } for (Iterator<Map<Integer, AbstractShell>> iter = shells.values().iterator(); iter.hasNext();) { AbstractShell.finishedForGood = true; //we may no longer restart shells Map<Integer, AbstractShell> rel = iter.next(); if (rel != null) { for (Iterator<AbstractShell> iter2 = rel.values().iterator(); iter2.hasNext();) { AbstractShell element = iter2.next(); if (element != null) { try { element.shutdown(); //shutdown } catch (Exception e) { Log.log(e); //let's log it... this should not happen } } } } } shells.clear(); } } /** * Restarts all the shells and clears any related cache. * * @return an error message if some exception happens in this process (an empty string means all went smoothly). */ public static String restartAllShells() { String ret = ""; synchronized (shells) { try { if (DebugSettings.DEBUG_CODE_COMPLETION) { Log.toLogFile("Restarting all shells and clearing caches...", AbstractShell.class); } for (Map<Integer, AbstractShell> val : shells.values()) { for (AbstractShell val2 : val.values()) { if (val2 != null) { val2.endIt(); } } IInterpreterManager[] interpreterManagers = PydevPlugin.getAllInterpreterManagers(); for (IInterpreterManager iInterpreterManager : interpreterManagers) { if (iInterpreterManager == null) { continue; //Should happen only on testing... } try { iInterpreterManager.clearCaches(); } catch (Exception e) { Log.log(e); ret += e.getMessage() + "\n"; } } //Clear the global modules cache! ModulesManager.clearCache(); } } catch (Exception e) { Log.log(e); ret += e.getMessage() + "\n"; } } return ret; } /** * @param interpreter the interpreter whose shell we want. * @return a map with the type of the shell mapping to the shell itself */ private static Map<Integer, AbstractShell> getTypeToShellFromId(IInterpreterInfo interpreter) { synchronized (shells) { Map<Integer, AbstractShell> typeToShell = shells.get(interpreter.getExecutableOrJar()); if (typeToShell == null) { typeToShell = new HashMap<Integer, AbstractShell>(); shells.put(interpreter.getExecutableOrJar(), typeToShell); } return typeToShell; } } /** * register a shell and give it an id * * @param nature the nature (which has the information on the interpreter we want to used) * @param id the shell id * @see #MAIN_THREAD_SHELL * @see #OTHER_THREADS_SHELL * * @param shell the shell to register */ public static void putServerShell(IPythonNature nature, int id, AbstractShell shell) { synchronized (shells) { try { Map<Integer, AbstractShell> typeToShell = getTypeToShellFromId(nature.getProjectInterpreter()); typeToShell.put(id, shell); } catch (Exception e) { throw new RuntimeException(e); } } } public static AbstractShell getServerShell(IPythonNature nature, int id) throws IOException, JDTNotAvailableException, CoreException, MisconfigurationException, PythonNatureWithoutProjectException { return getServerShell(nature.getProjectInterpreter(), nature.getInterpreterType(), id); } /** * @param interpreter the interpreter that should create the shell * * @param relatedTo identifies to which kind of interpreter the shell should be related. * @see org.python.pydev.core.IPythonNature#INTERPRETER_TYPE_PYTHON * @see org.python.pydev.core.IPythonNature#INTERPRETER_TYPE_JYTHON * * @param a given id for the shell * @see #MAIN_THREAD_SHELL * @see #OTHER_THREADS_SHELL * * @return the shell with the given id related to some nature * * @throws CoreException * @throws IOException * @throws MisconfigurationException */ private static AbstractShell getServerShell(IInterpreterInfo interpreter, int relatedTo, int id) throws IOException, JDTNotAvailableException, CoreException, MisconfigurationException { AbstractShell pythonShell = null; synchronized (shells) { if (DebugSettings.DEBUG_CODE_COMPLETION) { Log.toLogFile("Synchronizing on shells...", AbstractShell.class); } if (DebugSettings.DEBUG_CODE_COMPLETION) { String flavor; switch (relatedTo) { case IPythonNature.INTERPRETER_TYPE_JYTHON: flavor = "Jython"; break; case IPythonNature.INTERPRETER_TYPE_IRONPYTHON: flavor = "IronPython"; break; default: flavor = "Python"; } ; Log.toLogFile( "Getting shell related to:" + flavor + " id:" + id + " interpreter: " + interpreter.getExecutableOrJar(), AbstractShell.class); } Map<Integer, AbstractShell> typeToShell = getTypeToShellFromId(interpreter); pythonShell = typeToShell.get(id); if (pythonShell == null) { if (DebugSettings.DEBUG_CODE_COMPLETION) { Log.toLogFile("pythonShell == null", AbstractShell.class); } if (relatedTo == IPythonNature.INTERPRETER_TYPE_PYTHON) { pythonShell = new PythonShell(); } else if (relatedTo == IPythonNature.INTERPRETER_TYPE_JYTHON) { pythonShell = new JythonShell(); } else if (relatedTo == IPythonNature.INTERPRETER_TYPE_IRONPYTHON) { pythonShell = new IronpythonShell(); } else { throw new RuntimeException("unknown related id"); } if (DebugSettings.DEBUG_CODE_COMPLETION) { Log.toLogFile("pythonShell.startIt()", AbstractShell.class); Log.addLogLevel(); } pythonShell.startIt(interpreter); //first start it if (DebugSettings.DEBUG_CODE_COMPLETION) { Log.remLogLevel(); Log.toLogFile("Finished pythonShell.startIt()", AbstractShell.class); } //then make it accessible typeToShell.put(id, pythonShell); } } return pythonShell; } }