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;
}
}