Snapshot idea/138.1696 from git://git.jetbrains.org/idea/community.git

Change-Id: I50c97b83a815ce635e49a38380ba5b8765e4b16a
diff --git a/python/helpers/pydev/pydev_umd.py b/python/helpers/pydev/pydev_umd.py
new file mode 100644
index 0000000..0bfeda7
--- /dev/null
+++ b/python/helpers/pydev/pydev_umd.py
@@ -0,0 +1,172 @@
+"""
+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.
+"""
+
+import sys
+import os
+
+# The following classes and functions are mainly intended to be used from
+# an interactive Python session
+class UserModuleDeleter:
+    """
+    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
+        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 = list(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 = []
+        modules_copy = dict(sys.modules)
+        for modname, module in modules_copy.items():
+            if modname == 'aaaaa':
+                print(modname, module)
+                print(self.previous_modules)
+            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
+
+_get_globals_callback = None
+def _set_globals_function(get_globals):
+    global _get_globals_callback
+    _get_globals_callback = get_globals
+def _get_globals():
+    """Return current Python interpreter globals namespace"""
+    if _get_globals_callback is not None:
+        return _get_globals_callback()
+    else:
+        try:
+            from __main__ import __dict__ as namespace
+        except ImportError:
+            try:
+                # The import fails on IronPython
+                import __main__
+                namespace = __main__.__dict__
+            except:
+                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__
+    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()
+    if '__file__' in namespace:
+        old_file = namespace['__file__']
+    else:
+        old_file = None
+    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 = ['']
+    if old_file is None:
+        del namespace['__file__']
+    else:
+        namespace['__file__'] = old_file