Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

user module deleter #40

Closed
wants to merge 2 commits into from

2 participants

@jonahkichwacoders

(One of many requests, all together at https://github.com/jonahkichwacoders/PyDev/tree/fabioz_all_requests if it is useful)

Pull in UMD from Spyder licensed with Spyder License Agreement (MIT license)

Spyder is http://code.google.com/p/spyderlib/
UMD is the User Module Deleter

To fully enable UMD we need to call the new method runfile instead of
execfile. EvaluateActionSetter is updated to call runfile.

@jonahkichwacoders jonahkichwacoders Pull in UMD from Spyder licensed with Spyder License Agreement (MIT L…
…icense)MIT license)

Spyder is http://code.google.com/p/spyderlib/
UMD is the User Module Deleter

To fully enable UMD we need to call the new method runfile instead of
execfile. EvaluateActionSetter is updated to call runfile.
690b039
@jonahkichwacoders jonahkichwacoders Merge branch 'development' into pullrequest/user_module_deleter 55550c0
@fabioz

Humm, I think that the sitecustomize is not the best place for this... that way it'll be added on all the runs a user does (even on regular python/jython/ironpython launches). I think that the UMD should only be loaded (along with those classes) if it's actually on the interactive console, so, it should probably be on its own module (i.e.: pydev_umd.py in org.python.pydev\pysrc) and should be added the same way that we provide an execfile on py3k (which is in org.python.pydev\pysrc\pydevconsole.py -- search for execfile there).

@jonahkichwacoders

OK, I will look at moving the code to pydev_umd.py.

@fabioz
Owner

Closing for now (waiting for changes commented before to merge pull request).

@fabioz fabioz closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 26, 2013
  1. @jonahkichwacoders

    Pull in UMD from Spyder licensed with Spyder License Agreement (MIT L…

    jonahkichwacoders authored
    …icense)MIT license)
    
    Spyder is http://code.google.com/p/spyderlib/
    UMD is the User Module Deleter
    
    To fully enable UMD we need to call the new method runfile instead of
    execfile. EvaluateActionSetter is updated to call runfile.
  2. @jonahkichwacoders
This page is out of date. Refresh to see the latest.
View
6 plugins/com.python.pydev/src/com/python/pydev/interactiveconsole/EvaluateActionSetter.java
@@ -116,7 +116,7 @@ private static void sendCommandToConsole(PySelection selection, ScriptConsole co
}
/**
- * Gets the command to send to the console (either the selected text or an execfile with the editor).
+ * Gets the command to send to the console (either the selected text or a runfile with the editor).
*/
private static String getCommandToSend(PyEdit edit, PySelection selection) {
String cmd = null;
@@ -125,11 +125,11 @@ private static String getCommandToSend(PyEdit edit, PySelection selection) {
if (code.length() != 0) {
cmd = code + "\n";
} else {
- //no code available: do an execfile in the current context
+ //no code available: do a runfile in the current context
File editorFile = edit.getEditorFile();
if (editorFile != null) {
- cmd = PythonSnippetUtils.getExecfileCommand(editorFile);
+ cmd = PythonSnippetUtils.getRunfileCommand(editorFile);
}
}
return cmd;
View
6 plugins/org.python.pydev.debug/plugin.xml
@@ -672,6 +672,12 @@
class="org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs"
id="org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs">
</page>
+ <page
+ category="org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs"
+ class="org.python.pydev.debug.newconsole.prefs.InteractiveConsoleUMDPrefs"
+ id="org.python.pydev.debug.newconsole.prefs.UMD"
+ name="User Module Deleter (UMD)">
+ </page>
</extension>
<extension
point="org.python.pydev.pydev_hover">
View
9 plugins/org.python.pydev.debug/src_console/org/python/pydev/debug/newconsole/PydevConsoleConstants.java
@@ -61,6 +61,15 @@
public static final String INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR = "INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR";
public static final boolean DEFAULT_INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR = true;
+ public static final String INTERACTIVE_CONSOLE_UMD_ENABLED = "INTERACTIVE_CONSOLE_UMD_ENABLED";
+ public static final boolean DEFAULT_INTERACTIVE_CONSOLE_UMD_ENABLED = true;
+
+ public static final String INTERACTIVE_CONSOLE_UMD_VERBOSE = "INTERACTIVE_CONSOLE_UMD_VERBOSE";
+ public static final boolean DEFAULT_INTERACTIVE_CONSOLE_UMD_VERBOSE = true;
+
+ public static final String INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST = "INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST";
+ public static final String DEFAULT_INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST = "guidata,guiqwt";
+
public static final int CONSOLE_TIMEOUT = 500;
}
View
9 ...python.pydev.debug/src_console/org/python/pydev/debug/newconsole/PydevConsolePreferencesInitializer.java
@@ -60,6 +60,15 @@ public void initializeDefaultPreferences() {
node.putBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR,
PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR);
+
+ node.putBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_ENABLED,
+ PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_UMD_ENABLED);
+
+ node.putBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_VERBOSE,
+ PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_UMD_VERBOSE);
+
+ node.put(PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST,
+ PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST);
}
}
View
13 plugins/org.python.pydev.debug/src_console/org/python/pydev/debug/newconsole/env/PydevIProcessFactory.java
@@ -38,6 +38,7 @@
import org.python.pydev.debug.model.PyStackFrame;
import org.python.pydev.debug.newconsole.PydevConsoleConstants;
import org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs;
+import org.python.pydev.debug.newconsole.prefs.InteractiveConsoleUMDPrefs;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.runners.SimpleIronpythonRunner;
@@ -250,6 +251,18 @@ public PydevConsoleLaunchInfo createLaunch(IInterpreterManager interpreterManage
} else {
String[] env = SimpleRunner.createEnvWithPythonpath(pythonpathEnv, interpreter.getExecutableOrJar(),
interpreterManager, nature);
+ // Add in UMD settings
+ String[] s = new String[env.length + 3];
+ System.arraycopy(env, 0, s, 0, env.length);
+
+ s[s.length - 3] = "PYDEV_UMD_ENABLED="
+ + Boolean.toString(InteractiveConsoleUMDPrefs.isUMDEnabled());
+ s[s.length - 2] = "PYDEV_UMD_NAMELIST="
+ + InteractiveConsoleUMDPrefs.getUMDExcludeModules();
+ s[s.length - 1] = "PYDEV_UMD_VERBOSE="
+ + Boolean.toString(InteractiveConsoleUMDPrefs.isUMDVerbose());
+ env = s;
+
process = SimpleRunner.createProcess(commandLine, env, null);
}
View
119 ...g.python.pydev.debug/src_console/org/python/pydev/debug/newconsole/prefs/InteractiveConsoleUMDPrefs.java
@@ -0,0 +1,119 @@
+package org.python.pydev.debug.newconsole.prefs;
+
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.ListEditor;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.python.pydev.core.docutils.StringUtils;
+import org.python.pydev.debug.core.PydevDebugPlugin;
+import org.python.pydev.debug.newconsole.PydevConsoleConstants;
+import org.python.pydev.utils.LabelFieldEditor;
+
+public class InteractiveConsoleUMDPrefs extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ public static final String PREFERENCES_ID = "org.python.pydev.debug.newconsole.prefs.InteractiveConsoleUMDPrefs";
+
+ public InteractiveConsoleUMDPrefs() {
+ super(FLAT);
+ }
+
+ public void init(IWorkbench workbench) {
+ setDescription("PyDev User Module Deleter (UMD) preferences.\n\n" +
+ "UMD forces Python to reload modules which were " +
+ "imported when executing a script in the " +
+ "external console with the 'runfile' function.");
+ setPreferenceStore(PydevDebugPlugin.getDefault().getPreferenceStore());
+ }
+
+ @Override
+ protected void createFieldEditors() {
+ Composite p = getFieldEditorParent();
+
+ addField(new BooleanFieldEditor(PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_ENABLED,
+ "Enable UMD", p));
+ addField(new LabelFieldEditor("LabelFieldEditor", "", p));
+
+ addField(new BooleanFieldEditor(PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_VERBOSE,
+ "Show reloaded modules list", p));
+ addField(new LabelFieldEditor("LabelFieldEditor", "", p));
+
+ addField(new ListEditor(PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST,
+ "UMD Excluded Modules:", p) {
+
+ @Override
+ protected String createList(String[] items) {
+ return StringUtils.join(",", items);
+ }
+
+ @Override
+ protected String[] parseString(String stringList) {
+ return stringList.split(",");
+ }
+
+ @Override
+ protected String getNewInputObject() {
+ InputDialog d = new InputDialog(getShell(), "New Excluded Module",
+ "Add the module you want to exclude.", "",
+ new IInputValidator() {
+ public String isValid(String newText) {
+ if (newText.indexOf(',') != -1) {
+ return "The input cannot have a comma";
+ }
+ return null;
+ }
+ });
+
+ if (d.open() == InputDialog.OK) {
+ return d.getValue();
+ }
+ return null;
+ }
+
+ @Override
+ protected void doFillIntoGrid(Composite parent, int numColumns) {
+ super.doFillIntoGrid(parent, numColumns);
+ List listControl = getListControl(parent);
+ GridData layoutData = (GridData) listControl.getLayoutData();
+ layoutData.heightHint = 300;
+ }
+ });
+
+ }
+
+ public static boolean isUMDEnabled() {
+ PydevDebugPlugin plugin = PydevDebugPlugin.getDefault();
+ if (plugin != null) {
+ return plugin.getPreferenceStore().getBoolean(
+ PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_ENABLED);
+ } else {
+ return PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_UMD_ENABLED;
+ }
+ }
+
+ public static boolean isUMDVerbose() {
+ PydevDebugPlugin plugin = PydevDebugPlugin.getDefault();
+ if (plugin != null) {
+ return plugin.getPreferenceStore().getBoolean(
+ PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_VERBOSE);
+ } else {
+ return PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_UMD_VERBOSE;
+ }
+ }
+
+ public static String getUMDExcludeModules() {
+ PydevDebugPlugin plugin = PydevDebugPlugin.getDefault();
+ if (plugin != null) {
+ return plugin.getPreferenceStore().getString(
+ PydevConsoleConstants.INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST);
+ } else {
+ return PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_UMD_EXCLUDE_MODULE_LIST;
+ }
+ }
+
+}
View
16 ...eractive_console/src/org/python/pydev/shared_interactive_console/console/codegen/PythonSnippetUtils.java
@@ -4,6 +4,7 @@
import org.eclipse.ui.ide.ResourceUtil;
import org.python.pydev.shared_core.string.FastStringBuffer;
+import org.python.pydev.shared_core.string.StringUtils;
/**
* A collection of utilities to help create snippets of Python Code
@@ -24,10 +25,23 @@ public static String getExecfileCommand(File file) {
.append(")\n").toString();
}
+ /**
+ * Creates a "runfile" command for the argument file, escaping the name
+ * file as needed.
+ *
+ * @param file
+ * file to runfile
+ * @return Python snippet
+ */
+ public static String getRunfileCommand(File file) {
+ return StringUtils.format("runfile(%s)\n", getSingleQuotedString(file.toString()));
+ }
+
private static String getQuotedString(char quote, String string) {
// Implemented using SimpleRunner.getArgumentsAsStr as a starting point
- if (string == null || string.length() == 0)
+ if (string == null || string.length() == 0) {
return "" + quote + quote;
+ }
FastStringBuffer buf = new FastStringBuffer();
buf.append(quote);
View
1  plugins/org.python.pydev/pysrc/pydev_pysrc.py
@@ -0,0 +1 @@
+'''An empty file in pysrc that can be imported (from sitecustomize) to find the location of pysrc'''
View
153 plugins/org.python.pydev/pysrc/pydev_sitecustomize/sitecustomize.py
@@ -260,3 +260,156 @@ def pydev_getpass(msg='Password: '):
if DEBUG:
import traceback;traceback.print_exc() #@Reimport
+"""
+The UserModuleDeleter and runfile methods are copied from
+Spyder and carry their own license agreement.
+http://code.google.com/p/spyderlib/source/browse/spyderlib/widgets/externalshell/sitecustomize.py
+
+Spyder License Agreement (MIT License)
+--------------------------------------
+
+Copyright (c) 2009-2012 Pierre Raybaut
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+"""
+
+
+# The following classes and functions are mainly intended to be used from
+# an interactive Python session
+class UserModuleDeleter(object):
+ """
+ User Module Deleter (UMD) aims at deleting user modules
+ to force Python to deeply reload them during import
+
+ pathlist [list]: blacklist in terms of module path
+ namelist [list]: blacklist in terms of module name
+ """
+ def __init__(self, namelist=None, pathlist=None):
+ if namelist is None:
+ namelist = []
+ self.namelist = namelist+['sitecustomize']
+ if pathlist is None:
+ pathlist = []
+ self.pathlist = pathlist
+ try:
+ # blacklist all files in org.python.pydev/pysrc
+ import pydev_pysrc, inspect
+ self.pathlist.append(os.path.dirname(pydev_pysrc.__file__))
+ except:
+ pass
+ self.previous_modules = sys.modules.keys()
+
+ def is_module_blacklisted(self, modname, modpath):
+ for path in [sys.prefix]+self.pathlist:
+ if modpath.startswith(path):
+ return True
+ else:
+ return set(modname.split('.')) & set(self.namelist)
+
+ def run(self, verbose=False):
+ """
+ Del user modules to force Python to deeply reload them
+
+ Do not del modules which are considered as system modules, i.e.
+ modules installed in subdirectories of Python interpreter's binary
+ Do not del C modules
+ """
+ log = []
+ for modname, module in sys.modules.items():
+ if modname not in self.previous_modules:
+ modpath = getattr(module, '__file__', None)
+ if modpath is None:
+ # *module* is a C module that is statically linked into the
+ # interpreter. There is no way to know its path, so we
+ # choose to ignore it.
+ continue
+ if not self.is_module_blacklisted(modname, modpath):
+ log.append(modname)
+ del sys.modules[modname]
+ if verbose and log:
+ print("\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted",
+ ": "+", ".join(log)))
+
+__umd__ = None
+
+
+def _get_globals():
+ """Return current Python interpreter globals namespace"""
+ from __main__ import __dict__ as namespace
+ shell = namespace.get('__ipythonshell__')
+ if shell is not None and hasattr(shell, 'user_ns'):
+ # IPython 0.12+ kernel
+ return shell.user_ns
+ else:
+ # Python interpreter
+ return namespace
+ return namespace
+
+
+def runfile(filename, args=None, wdir=None, namespace=None):
+ """
+ Run filename
+ args: command line arguments (string)
+ wdir: working directory
+ """
+ try:
+ if hasattr(filename, 'decode'):
+ filename = filename.decode('utf-8')
+ except (UnicodeError, TypeError):
+ pass
+ global __umd__
+ import os
+ if os.environ.get("PYDEV_UMD_ENABLED", "").lower() == "true":
+ if __umd__ is None:
+ namelist = os.environ.get("PYDEV_UMD_NAMELIST", None)
+ if namelist is not None:
+ namelist = namelist.split(',')
+ __umd__ = UserModuleDeleter(namelist=namelist)
+ else:
+ verbose = os.environ.get("PYDEV_UMD_VERBOSE", "").lower() == "true"
+ __umd__.run(verbose=verbose)
+ if args is not None and not isinstance(args, basestring):
+ raise TypeError("expected a character buffer object")
+ if namespace is None:
+ namespace = _get_globals()
+ namespace['__file__'] = filename
+ sys.argv = [filename]
+ if args is not None:
+ for arg in args.split():
+ sys.argv.append(arg)
+ if wdir is not None:
+ try:
+ if hasattr(wdir, 'decode'):
+ wdir = wdir.decode('utf-8')
+ except (UnicodeError, TypeError):
+ pass
+ os.chdir(wdir)
+ execfile(filename, namespace)
+ sys.argv = ['']
+ namespace.pop('__file__')
+
+if IS_PYTHON_3K:
+ import builtins
+ builtins.runfile = runfile
+else:
+ import __builtin__
+ __builtin__.runfile = runfile
Something went wrong with that request. Please try again.