1"""
2The UserModuleDeleter and runfile methods are copied from
3Spyder and carry their own license agreement.
4http://code.google.com/p/spyderlib/source/browse/spyderlib/widgets/externalshell/sitecustomize.py
5
6Spyder License Agreement (MIT License)
7--------------------------------------
8
9Copyright (c) 2009-2012 Pierre Raybaut
10
11Permission is hereby granted, free of charge, to any person
12obtaining a copy of this software and associated documentation
13files (the "Software"), to deal in the Software without
14restriction, including without limitation the rights to use,
15copy, modify, merge, publish, distribute, sublicense, and/or sell
16copies of the Software, and to permit persons to whom the
17Software is furnished to do so, subject to the following
18conditions:
19
20The above copyright notice and this permission notice shall be
21included in all copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
25OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
27HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
28WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30OTHER DEALINGS IN THE SOFTWARE.
31"""
32
33import sys
34import os
35
36# The following classes and functions are mainly intended to be used from
37# an interactive Python session
38class UserModuleDeleter:
39    """
40    User Module Deleter (UMD) aims at deleting user modules
41    to force Python to deeply reload them during import
42
43    pathlist [list]: ignore list in terms of module path
44    namelist [list]: ignore list in terms of module name
45    """
46    def __init__(self, namelist=None, pathlist=None):
47        if namelist is None:
48            namelist = []
49        self.namelist = namelist
50        if pathlist is None:
51            pathlist = []
52        self.pathlist = pathlist
53        try:
54            # ignore all files in org.python.pydev/pysrc
55            import pydev_pysrc, inspect
56            self.pathlist.append(os.path.dirname(pydev_pysrc.__file__))
57        except:
58            pass
59        self.previous_modules = list(sys.modules.keys())
60
61    def is_module_ignored(self, modname, modpath):
62        for path in [sys.prefix] + self.pathlist:
63            if modpath.startswith(path):
64                return True
65        else:
66            return set(modname.split('.')) & set(self.namelist)
67
68    def run(self, verbose=False):
69        """
70        Del user modules to force Python to deeply reload them
71
72        Do not del modules which are considered as system modules, i.e.
73        modules installed in subdirectories of Python interpreter's binary
74        Do not del C modules
75        """
76        log = []
77        modules_copy = dict(sys.modules)
78        for modname, module in modules_copy.items():
79            if modname == 'aaaaa':
80                print(modname, module)
81                print(self.previous_modules)
82            if modname not in self.previous_modules:
83                modpath = getattr(module, '__file__', None)
84                if modpath is None:
85                    # *module* is a C module that is statically linked into the
86                    # interpreter. There is no way to know its path, so we
87                    # choose to ignore it.
88                    continue
89                if not self.is_module_ignored(modname, modpath):
90                    log.append(modname)
91                    del sys.modules[modname]
92        if verbose and log:
93            print("\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted",
94                                                     ": " + ", ".join(log)))
95
96__umd__ = None
97
98_get_globals_callback = None
99def _set_globals_function(get_globals):
100    global _get_globals_callback
101    _get_globals_callback = get_globals
102def _get_globals():
103    """Return current Python interpreter globals namespace"""
104    if _get_globals_callback is not None:
105        return _get_globals_callback()
106    else:
107        try:
108            from __main__ import __dict__ as namespace
109        except ImportError:
110            try:
111                # The import fails on IronPython
112                import __main__
113                namespace = __main__.__dict__
114            except:
115                namespace
116        shell = namespace.get('__ipythonshell__')
117        if shell is not None and hasattr(shell, 'user_ns'):
118            # IPython 0.12+ kernel
119            return shell.user_ns
120        else:
121            # Python interpreter
122            return namespace
123        return namespace
124
125
126def runfile(filename, args=None, wdir=None, namespace=None):
127    """
128    Run filename
129    args: command line arguments (string)
130    wdir: working directory
131    """
132    try:
133        if hasattr(filename, 'decode'):
134            filename = filename.decode('utf-8')
135    except (UnicodeError, TypeError):
136        pass
137    global __umd__
138    if os.environ.get("PYDEV_UMD_ENABLED", "").lower() == "true":
139        if __umd__ is None:
140            namelist = os.environ.get("PYDEV_UMD_NAMELIST", None)
141            if namelist is not None:
142                namelist = namelist.split(',')
143            __umd__ = UserModuleDeleter(namelist=namelist)
144        else:
145            verbose = os.environ.get("PYDEV_UMD_VERBOSE", "").lower() == "true"
146            __umd__.run(verbose=verbose)
147    if args is not None and not isinstance(args, basestring):
148        raise TypeError("expected a character buffer object")
149    if namespace is None:
150        namespace = _get_globals()
151    if '__file__' in namespace:
152        old_file = namespace['__file__']
153    else:
154        old_file = None
155    namespace['__file__'] = filename
156    sys.argv = [filename]
157    if args is not None:
158        for arg in args.split():
159            sys.argv.append(arg)
160    if wdir is not None:
161        try:
162            if hasattr(wdir, 'decode'):
163                wdir = wdir.decode('utf-8')
164        except (UnicodeError, TypeError):
165            pass
166        os.chdir(wdir)
167    execfile(filename, namespace)
168    sys.argv = ['']
169    if old_file is None:
170        del namespace['__file__']
171    else:
172        namespace['__file__'] = old_file
173