~ubuntu-branches/debian/experimental/spyder/experimental

« back to all changes in this revision

Viewing changes to spyderlib/widgets/externalshell/sitecustomize.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2013-01-20 12:19:54 UTC
  • mfrom: (1.1.16)
  • Revision ID: package-import@ubuntu.com-20130120121954-1jt1xa924bshhvh0
Tags: 2.2.0~beta1+dfsg-2
fix typo ipython-qtconsol -> ipython-qtconsole

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# Spyder's ExternalPythonShell sitecustomize
3
 
 
4
 
import sys
5
 
import os
6
 
import os.path as osp
7
 
import pdb
8
 
import bdb
9
 
import __builtin__
10
 
 
11
 
 
12
 
# Colorization of sys.stderr (standard Python interpreter)
13
 
if os.environ.get("COLORIZE_SYS_STDERR", "").lower() == "true"\
14
 
   and not os.environ.get('IPYTHON', False):
15
 
    class StderrProxy(object):
16
 
        """Proxy to sys.stderr file object overriding only the `write` method 
17
 
        to provide red colorization for the whole stream, and blue-underlined 
18
 
        for traceback file links""" 
19
 
        def __init__(self):
20
 
            self.old_stderr = sys.stderr
21
 
            self.__buffer = ''
22
 
            sys.stderr = self
23
 
        
24
 
        def __getattr__(self, name):
25
 
            return getattr(self.old_stderr, name)
26
 
            
27
 
        def write(self, text):
28
 
            if os.name == 'nt' and '\n' not in text:
29
 
                self.__buffer += text
30
 
                return
31
 
            for text in (self.__buffer+text).splitlines(True):
32
 
                if text.startswith('  File') \
33
 
                and not text.startswith('  File "<'):
34
 
                    # Show error links in blue underlined text
35
 
                    colored_text = '  '+'\x1b[4;34m'+text[2:]+'\x1b[0m'
36
 
                else:
37
 
                    # Show error messages in red
38
 
                    colored_text = '\x1b[31m'+text+'\x1b[0m'
39
 
                self.old_stderr.write(colored_text)
40
 
            self.__buffer = ''
41
 
    
42
 
    stderrproxy = StderrProxy()
43
 
 
44
 
 
45
 
# Prepending this spyderlib package's path to sys.path to be sure 
46
 
# that another version of spyderlib won't be imported instead:
47
 
spyderlib_path = osp.dirname(__file__)
48
 
while not osp.isdir(osp.join(spyderlib_path, 'spyderlib')):
49
 
    spyderlib_path = osp.abspath(osp.join(spyderlib_path, os.pardir))
50
 
if not spyderlib_path.startswith(sys.prefix):
51
 
    # Spyder is not installed: moving its parent directory to the top of 
52
 
    # sys.path to be sure that this spyderlib package will be imported in 
53
 
    # the remote process (instead of another installed version of Spyder)
54
 
    while spyderlib_path in sys.path:
55
 
        sys.path.remove(spyderlib_path)
56
 
    sys.path.insert(0, spyderlib_path)
57
 
os.environ['SPYDER_PARENT_DIR'] = spyderlib_path
58
 
 
59
 
 
60
 
# Set PyQt4 API to #1 or #2
61
 
pyqt_api = int(os.environ.get("PYQT_API", "0"))
62
 
if pyqt_api:
63
 
    try:
64
 
        import sip
65
 
        try:
66
 
            for qtype in ('QString', 'QVariant'):
67
 
                sip.setapi(qtype, pyqt_api)
68
 
        except AttributeError:
69
 
            # Old version of sip
70
 
            pass
71
 
    except ImportError:
72
 
        pass
73
 
 
74
 
 
75
 
mpl_backend = os.environ.get("MATPLOTLIB_BACKEND")
76
 
if mpl_backend:
77
 
    try:
78
 
        import matplotlib
79
 
        matplotlib.use(mpl_backend)
80
 
    except ImportError:
81
 
        pass
82
 
 
83
 
 
84
 
if os.environ.get("MATPLOTLIB_PATCH", "").lower() == "true":
85
 
    try:
86
 
        from spyderlib import mpl_patch
87
 
        mpl_patch.apply()
88
 
    except ImportError:
89
 
        pass
90
 
 
91
 
 
92
 
if os.name == 'nt': # Windows platforms
93
 
            
94
 
    # Setting console encoding (otherwise Python does not recognize encoding)
95
 
    try:
96
 
        import locale, ctypes
97
 
        _t, _cp = locale.getdefaultlocale('LANG')
98
 
        try:
99
 
            _cp = int(_cp[2:])
100
 
            ctypes.windll.kernel32.SetConsoleCP(_cp)
101
 
            ctypes.windll.kernel32.SetConsoleOutputCP(_cp)
102
 
        except (ValueError, TypeError):
103
 
            # Code page number in locale is not valid
104
 
            pass
105
 
    except ImportError:
106
 
        pass
107
 
        
108
 
    # Workaround for IPython thread issues with win32 comdlg32
109
 
    if os.environ.get('IPYTHON', False):
110
 
        try:
111
 
            import win32gui, win32api
112
 
            try:
113
 
                win32gui.GetOpenFileNameW(File=win32api.GetSystemDirectory()[:2])
114
 
            except win32gui.error:
115
 
                # This error is triggered intentionally
116
 
                pass
117
 
        except ImportError:
118
 
            # Unfortunately, pywin32 is not installed...
119
 
            pass
120
 
 
121
 
 
122
 
# Set standard outputs encoding:
123
 
# (otherwise, for example, print u"é" will fail)
124
 
encoding = None
125
 
try:
126
 
    import locale
127
 
except ImportError:
128
 
    pass
129
 
else:
130
 
    loc = locale.getdefaultlocale()
131
 
    if loc[1]:
132
 
        encoding = loc[1]
133
 
 
134
 
if encoding is None:
135
 
    encoding = "UTF-8"
136
 
 
137
 
sys.setdefaultencoding(encoding)
138
 
os.environ['SPYDER_ENCODING'] = encoding
139
 
    
140
 
try:
141
 
    import sitecustomize  #analysis:ignore
142
 
except ImportError:
143
 
    pass
144
 
 
145
 
 
146
 
# Communication between Spyder and the remote process
147
 
if os.environ.get('SPYDER_SHELL_ID') is None:
148
 
    monitor = None
149
 
else:
150
 
    from spyderlib.widgets.externalshell.monitor import Monitor
151
 
    monitor = Monitor("127.0.0.1",
152
 
                      int(os.environ['SPYDER_I_PORT']),
153
 
                      int(os.environ['SPYDER_N_PORT']),
154
 
                      os.environ['SPYDER_SHELL_ID'],
155
 
                      float(os.environ['SPYDER_AR_TIMEOUT']),
156
 
                      os.environ["SPYDER_AR_STATE"].lower() == "true")
157
 
    monitor.start()
158
 
    
159
 
    def open_in_spyder(source, lineno=1):
160
 
        """Open in Spyder's editor the source file
161
 
(may be a filename or a Python module/package)"""
162
 
        if not isinstance(source, basestring):
163
 
            try:
164
 
                source = source.__file__
165
 
            except AttributeError:
166
 
                raise ValueError("source argument must be either "
167
 
                                 "a string or a module object")
168
 
        if source.endswith('.pyc'):
169
 
            source = source[:-1]
170
 
        monitor.notify_open_file(source, lineno=lineno)
171
 
    __builtin__.open_in_spyder = open_in_spyder
172
 
    
173
 
    # * PyQt4:
174
 
    #   * Removing PyQt4 input hook which is not working well on Windows since 
175
 
    #     opening a subprocess do not attach a real console to it
176
 
    #     (with keyboard events...)
177
 
    #   * Replacing it with our own input hook
178
 
    # * PySide:
179
 
    #   * Installing an input hook: this feature is not yet supported 
180
 
    #     natively by PySide
181
 
    if os.environ.get("INSTALL_QT_INPUTHOOK", "").lower() == "true"\
182
 
       and not os.environ.get('IPYTHON', False):
183
 
        # For now, the Spyder's input hook does not work with IPython:
184
 
        # with IPython v0.10 or non-Windows platforms, this is not a
185
 
        # problem. However, with IPython v0.11 on Windows, this will be
186
 
        # fixed by patching IPython to force it to use our inputhook.
187
 
 
188
 
        if os.environ["QT_API"] == 'pyqt':
189
 
            from PyQt4 import QtCore
190
 
            # Removing PyQt's PyOS_InputHook implementation:
191
 
            QtCore.pyqtRemoveInputHook()
192
 
        elif os.environ["QT_API"] == 'pyside':
193
 
            from PySide import QtCore
194
 
            # XXX: when PySide will implement an input hook, we will have to 
195
 
            # remove it here
196
 
        else:
197
 
            assert False
198
 
 
199
 
        def qt_inputhook():
200
 
            """Qt input hook for Spyder's console
201
 
            
202
 
            This input hook wait for available stdin data (notified by
203
 
            ExternalPythonShell through the monitor's inputhook_flag
204
 
            attribute), and in the meantime it processes Qt events."""
205
 
            # Refreshing variable explorer, except on first input hook call:
206
 
            # (otherwise, on slow machines, this may freeze Spyder)
207
 
            monitor.refresh_from_inputhook()
208
 
            if os.name == 'nt':
209
 
                try:
210
 
                    # This call fails for Python without readline support
211
 
                    # (or on Windows platforms) when PyOS_InputHook is called
212
 
                    # for the second consecutive time, because the 100-bytes
213
 
                    # stdin buffer is full.
214
 
                    # For more details, see the `PyOS_StdioReadline` function
215
 
                    # in Python source code (Parser/myreadline.c)
216
 
                    sys.stdin.tell()
217
 
                except IOError:
218
 
                    return 0
219
 
            app = QtCore.QCoreApplication.instance()
220
 
            if app and app.thread() is QtCore.QThread.currentThread():
221
 
                timer = QtCore.QTimer()
222
 
                QtCore.QObject.connect(timer, QtCore.SIGNAL('timeout()'),
223
 
                                       app, QtCore.SLOT('quit()'))
224
 
                monitor.toggle_inputhook_flag(False)
225
 
                while not monitor.inputhook_flag:
226
 
                    timer.start(50)
227
 
                    QtCore.QCoreApplication.exec_()
228
 
                    timer.stop()
229
 
#                # Socket-based alternative:
230
 
#                socket = QtNetwork.QLocalSocket()
231
 
#                socket.connectToServer(os.environ['SPYDER_SHELL_ID'])
232
 
#                socket.waitForConnected(-1)
233
 
#                while not socket.waitForReadyRead(10):
234
 
#                    timer.start(50)
235
 
#                    QtCore.QCoreApplication.exec_()
236
 
#                    timer.stop()
237
 
#                socket.read(3)
238
 
#                socket.disconnectFromServer()
239
 
            return 0
240
 
 
241
 
        # Installing Spyder's PyOS_InputHook implementation:
242
 
        import ctypes
243
 
        cb_pyfunctype = ctypes.PYFUNCTYPE(ctypes.c_int)(qt_inputhook)
244
 
        pyos_ih = ctypes.c_void_p.in_dll(ctypes.pythonapi, "PyOS_InputHook")
245
 
        pyos_ih.value = ctypes.cast(cb_pyfunctype, ctypes.c_void_p).value
246
 
    else:
247
 
        # Quite limited feature: notify only when a result is displayed in
248
 
        # console (does not notify at every prompt)
249
 
        def displayhook(obj):
250
 
            sys.__displayhook__(obj)
251
 
            monitor.refresh()
252
 
    
253
 
        sys.displayhook = displayhook
254
 
 
255
 
 
256
 
#===============================================================================
257
 
# Monkey-patching pdb
258
 
#===============================================================================
259
 
class SpyderPdb(pdb.Pdb):
260
 
    def set_spyder_breakpoints(self):
261
 
        self.clear_all_breaks()
262
 
        #------Really deleting all breakpoints:
263
 
        for bp in bdb.Breakpoint.bpbynumber:
264
 
            if bp:
265
 
                bp.deleteMe()
266
 
        bdb.Breakpoint.next = 1
267
 
        bdb.Breakpoint.bplist = {}
268
 
        bdb.Breakpoint.bpbynumber = [None]
269
 
        #------
270
 
        from spyderlib.config import CONF
271
 
        CONF.load_from_ini()
272
 
        if CONF.get('run', 'breakpoints/enabled', True):
273
 
            breakpoints = CONF.get('run', 'breakpoints', {})
274
 
            i = 0
275
 
            for fname, data in breakpoints.iteritems():
276
 
                for linenumber, condition in data:
277
 
                    i += 1
278
 
                    self.set_break(self.canonic(fname), linenumber,
279
 
                                   cond=condition)
280
 
                    
281
 
    def notify_spyder(self, frame):
282
 
        if not frame:
283
 
            return
284
 
        fname = self.canonic(frame.f_code.co_filename)
285
 
        lineno = frame.f_lineno
286
 
        if isinstance(fname, basestring) and isinstance(lineno, int):
287
 
            if osp.isfile(fname) and monitor is not None:
288
 
                monitor.notify_pdb_step(fname, lineno)
289
 
 
290
 
pdb.Pdb = SpyderPdb
291
 
 
292
 
def monkeypatch_method(cls, patch_name):
293
 
    # This function's code was inspired from the following thread:
294
 
    # "[Python-Dev] Monkeypatching idioms -- elegant or ugly?"
295
 
    # by Robert Brewer <fumanchu at aminus.org>
296
 
    # (Tue Jan 15 19:13:25 CET 2008)
297
 
    """
298
 
    Add the decorated method to the given class; replace as needed.
299
 
    
300
 
    If the named method already exists on the given class, it will
301
 
    be replaced, and a reference to the old method is created as 
302
 
    cls._old<patch_name><name>. If the "_old_<patch_name>_<name>" attribute 
303
 
    already exists, KeyError is raised.
304
 
    """
305
 
    def decorator(func):
306
 
        fname = func.__name__
307
 
        old_func = getattr(cls, fname, None)
308
 
        if old_func is not None:
309
 
            # Add the old func to a list of old funcs.
310
 
            old_ref = "_old_%s_%s" % (patch_name, fname)
311
 
            #print old_ref, old_func
312
 
            old_attr = getattr(cls, old_ref, None)
313
 
            if old_attr is None:
314
 
                setattr(cls, old_ref, old_func)
315
 
            else:
316
 
                raise KeyError("%s.%s already exists."
317
 
                               % (cls.__name__, old_ref))
318
 
        setattr(cls, fname, func)
319
 
        return func
320
 
    return decorator
321
 
 
322
 
@monkeypatch_method(pdb.Pdb, 'Pdb')
323
 
def user_return(self, frame, return_value):
324
 
    """This function is called when a return trap is set here."""
325
 
    # This is useful when debugging in an active interpreter (otherwise,
326
 
    # the debugger will stop before reaching the target file)
327
 
    if self._wait_for_mainpyfile:
328
 
        if (self.mainpyfile != self.canonic(frame.f_code.co_filename)
329
 
            or frame.f_lineno<= 0):
330
 
            return
331
 
        self._wait_for_mainpyfile = 0
332
 
    self._old_Pdb_user_return(frame, return_value)
333
 
        
334
 
@monkeypatch_method(pdb.Pdb, 'Pdb')
335
 
def interaction(self, frame, traceback):
336
 
    self.setup(frame, traceback)
337
 
    self.notify_spyder(frame) #-----Spyder-specific-------------------------
338
 
    self.print_stack_entry(self.stack[self.curindex])
339
 
    self.cmdloop()
340
 
    self.forget()
341
 
 
342
 
@monkeypatch_method(pdb.Pdb, 'Pdb')
343
 
def reset(self):
344
 
    self._old_Pdb_reset()
345
 
    if monitor is not None:
346
 
        monitor.register_pdb_session(self)
347
 
    self.set_spyder_breakpoints()
348
 
 
349
 
#XXX: notify spyder on any pdb command (is that good or too lazy? i.e. is more 
350
 
#     specific behaviour desired?)
351
 
@monkeypatch_method(pdb.Pdb, 'Pdb')
352
 
def postcmd(self, stop, line):
353
 
    self.notify_spyder(self.curframe)
354
 
    return self._old_Pdb_postcmd(stop, line)
355
 
 
356
 
 
357
 
# Restoring (almost) original sys.path:
358
 
# (Note: do not remove spyderlib_path from sys.path because if Spyder has been
359
 
#  installed using python setup.py install, then this could remove the 
360
 
#  'site-packages' directory from sys.path!)
361
 
try:
362
 
    sys.path.remove(osp.join(spyderlib_path,
363
 
                             "spyderlib", "widgets", "externalshell"))
364
 
except ValueError:
365
 
    pass
366
 
 
367
 
# Ignore PyQt4's sip API changes (this should be used wisely -e.g. for
368
 
# debugging- as dynamic API change is not supported by PyQt)
369
 
if os.environ.get("IGNORE_SIP_SETAPI_ERRORS", "").lower() == "true":
370
 
    try:
371
 
        import sip
372
 
        from sip import setapi as original_setapi
373
 
        def patched_setapi(name, no):
374
 
            try:
375
 
                original_setapi(name, no)
376
 
            except ValueError, msg:
377
 
                print >>sys.stderr, "Warning/PyQt4-Spyder (%s)" % str(msg)
378
 
        sip.setapi = patched_setapi
379
 
    except ImportError:
380
 
        pass
381
 
 
382
 
 
383
 
# The following classes and functions are mainly intended to be used from 
384
 
# an interactive Python/IPython session
385
 
class UserModuleDeleter(object):
386
 
    """
387
 
    User Module Deleter (UMD) aims at deleting user modules 
388
 
    to force Python to deeply reload them during import
389
 
    
390
 
    pathlist [list]: blacklist in terms of module path
391
 
    namelist [list]: blacklist in terms of module name
392
 
    """
393
 
    def __init__(self, namelist=None, pathlist=None):
394
 
        if namelist is None:
395
 
            namelist = []
396
 
        self.namelist = namelist+['sitecustomize', 'spyderlib', 'spyderplugins']
397
 
        if pathlist is None:
398
 
            pathlist = []
399
 
        self.pathlist = pathlist
400
 
        self.previous_modules = sys.modules.keys()
401
 
 
402
 
    def is_module_blacklisted(self, modname, modpath):
403
 
        for path in [sys.prefix]+self.pathlist:
404
 
            if modpath.startswith(path):
405
 
                return True
406
 
        else:
407
 
            return set(modname.split('.')) & set(self.namelist)
408
 
        
409
 
    def run(self, verbose=False):
410
 
        """
411
 
        Del user modules to force Python to deeply reload them
412
 
        
413
 
        Do not del modules which are considered as system modules, i.e. 
414
 
        modules installed in subdirectories of Python interpreter's binary
415
 
        Do not del C modules
416
 
        """
417
 
        log = []
418
 
        for modname, module in sys.modules.items():
419
 
            if modname not in self.previous_modules:
420
 
                modpath = getattr(module, '__file__', None)
421
 
                if modpath is None:
422
 
                    # *module* is a C module that is statically linked into the 
423
 
                    # interpreter. There is no way to know its path, so we 
424
 
                    # choose to ignore it.
425
 
                    continue
426
 
                if not self.is_module_blacklisted(modname, modpath):
427
 
                    log.append(modname)
428
 
                    del sys.modules[modname]
429
 
        if verbose and log:
430
 
            print "\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted",
431
 
                                                     ": "+", ".join(log))
432
 
 
433
 
__umd__ = None
434
 
 
435
 
 
436
 
def _get_globals():
437
 
    """Return current Python/IPython interpreter globals namespace"""
438
 
    from __main__ import __dict__ as namespace
439
 
    if hasattr(__builtin__, '__IPYTHON__'):
440
 
        # IPython 0.10
441
 
        shell = __builtin__.__IPYTHON__
442
 
    else:
443
 
        # IPython 0.11+
444
 
        shell = namespace.get('__ipythonshell__')
445
 
    if shell is not None and hasattr(shell, 'user_ns'):
446
 
        # IPython
447
 
        return shell.user_ns
448
 
    else:
449
 
        return namespace
450
 
 
451
 
 
452
 
def runfile(filename, args=None, wdir=None, namespace=None):
453
 
    """
454
 
    Run filename
455
 
    args: command line arguments (string)
456
 
    wdir: working directory
457
 
    """
458
 
    try:
459
 
        filename = filename.decode('utf-8')
460
 
    except (UnicodeError, TypeError):
461
 
        pass
462
 
    global __umd__
463
 
    if os.environ.get("UMD_ENABLED", "").lower() == "true":
464
 
        if __umd__ is None:
465
 
            namelist = os.environ.get("UMD_NAMELIST", None)
466
 
            if namelist is not None:
467
 
                namelist = namelist.split(',')
468
 
            __umd__ = UserModuleDeleter(namelist=namelist)
469
 
        else:
470
 
            verbose = os.environ.get("UMD_VERBOSE", "").lower() == "true"
471
 
            __umd__.run(verbose=verbose)
472
 
    if args is not None and not isinstance(args, basestring):
473
 
        raise TypeError("expected a character buffer object")
474
 
    if namespace is None:
475
 
        namespace = _get_globals()
476
 
    namespace['__file__'] = filename
477
 
    sys.argv = [filename]
478
 
    if args is not None:
479
 
        for arg in args.split():
480
 
            sys.argv.append(arg)
481
 
    if wdir is not None:
482
 
        try:
483
 
            wdir = wdir.decode('utf-8')
484
 
        except (UnicodeError, TypeError):
485
 
            pass
486
 
        os.chdir(wdir)
487
 
    execfile(filename, namespace)
488
 
    sys.argv = ['']
489
 
    namespace.pop('__file__')
490
 
    
491
 
__builtin__.runfile = runfile
492
 
 
493
 
 
494
 
def debugfile(filename, args=None, wdir=None):
495
 
    """
496
 
    Debug filename
497
 
    args: command line arguments (string)
498
 
    wdir: working directory
499
 
    """
500
 
    debugger = pdb.Pdb()
501
 
    filename = debugger.canonic(filename)
502
 
    debugger._wait_for_mainpyfile = 1
503
 
    debugger.mainpyfile = filename
504
 
    debugger._user_requested_quit = 0
505
 
    debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir))
506
 
 
507
 
__builtin__.debugfile = debugfile
508
 
 
509
 
 
510
 
def evalsc(command):
511
 
    """Evaluate special commands
512
 
    (analog to IPython's magic commands but far less powerful/complete)"""
513
 
    assert command.startswith(('%', '!'))
514
 
    system_command = command.startswith('!')
515
 
    command = command[1:].strip()
516
 
    if system_command:
517
 
        # System command
518
 
        if command.startswith('cd '):
519
 
            evalsc('%'+command)
520
 
        else:
521
 
            from subprocess import Popen, PIPE
522
 
            Popen(command, shell=True, stdin=PIPE)
523
 
            print '\n'
524
 
    else:
525
 
        # General command
526
 
        namespace = _get_globals()
527
 
        import re
528
 
        clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", command)
529
 
        cd_match = re.match(r"^cd \"?\'?([a-zA-Z0-9_\ \:\\\/\.]+)", command)
530
 
        if cd_match:
531
 
            os.chdir(eval('r"%s"' % cd_match.groups()[0].strip()))
532
 
        elif clear_match:
533
 
            varnames = clear_match.groups()[0].replace(' ', '').split(',')
534
 
            for varname in varnames:
535
 
                try:
536
 
                    namespace.pop(varname)
537
 
                except KeyError:
538
 
                    pass
539
 
        elif command in ('cd', 'pwd'):
540
 
            print os.getcwdu()
541
 
        elif command == 'ls':
542
 
            if os.name == 'nt':
543
 
                evalsc('!dir')
544
 
            else:
545
 
                evalsc('!ls')
546
 
        elif command == 'scientific':
547
 
            from spyderlib import baseconfig
548
 
            execfile(baseconfig.SCIENTIFIC_STARTUP, namespace)
549
 
        else:
550
 
            raise NotImplementedError, "Unsupported command: '%s'" % command
551
 
 
552
 
__builtin__.evalsc = evalsc
553
 
 
554
 
 
555
 
# Restoring original PYTHONPATH
556
 
try:
557
 
    os.environ['PYTHONPATH'] = os.environ['OLD_PYTHONPATH']
558
 
    del os.environ['OLD_PYTHONPATH']
559
 
except KeyError:
560
 
    if os.environ.get('PYTHONPATH') is not None:
561
 
        del os.environ['PYTHONPATH']
 
1
# -*- coding: utf-8 -*-
 
2
# Spyder's ExternalPythonShell sitecustomize
 
3
 
 
4
import sys
 
5
import os
 
6
import os.path as osp
 
7
import pdb
 
8
import bdb
 
9
import __builtin__
 
10
 
 
11
 
 
12
# Colorization of sys.stderr (standard Python interpreter)
 
13
if os.environ.get("COLORIZE_SYS_STDERR", "").lower() == "true":
 
14
    class StderrProxy(object):
 
15
        """Proxy to sys.stderr file object overriding only the `write` method 
 
16
        to provide red colorization for the whole stream, and blue-underlined 
 
17
        for traceback file links""" 
 
18
        def __init__(self):
 
19
            self.old_stderr = sys.stderr
 
20
            self.__buffer = ''
 
21
            sys.stderr = self
 
22
        
 
23
        def __getattr__(self, name):
 
24
            return getattr(self.old_stderr, name)
 
25
            
 
26
        def write(self, text):
 
27
            if os.name == 'nt' and '\n' not in text:
 
28
                self.__buffer += text
 
29
                return
 
30
            for text in (self.__buffer+text).splitlines(True):
 
31
                if text.startswith('  File') \
 
32
                and not text.startswith('  File "<'):
 
33
                    # Show error links in blue underlined text
 
34
                    colored_text = '  '+'\x1b[4;34m'+text[2:]+'\x1b[0m'
 
35
                else:
 
36
                    # Show error messages in red
 
37
                    colored_text = '\x1b[31m'+text+'\x1b[0m'
 
38
                self.old_stderr.write(colored_text)
 
39
            self.__buffer = ''
 
40
    
 
41
    stderrproxy = StderrProxy()
 
42
 
 
43
 
 
44
# Prepending this spyderlib package's path to sys.path to be sure 
 
45
# that another version of spyderlib won't be imported instead:
 
46
spyderlib_path = osp.dirname(__file__)
 
47
while not osp.isdir(osp.join(spyderlib_path, 'spyderlib')):
 
48
    spyderlib_path = osp.abspath(osp.join(spyderlib_path, os.pardir))
 
49
if not spyderlib_path.startswith(sys.prefix):
 
50
    # Spyder is not installed: moving its parent directory to the top of 
 
51
    # sys.path to be sure that this spyderlib package will be imported in 
 
52
    # the remote process (instead of another installed version of Spyder)
 
53
    while spyderlib_path in sys.path:
 
54
        sys.path.remove(spyderlib_path)
 
55
    sys.path.insert(0, spyderlib_path)
 
56
os.environ['SPYDER_PARENT_DIR'] = spyderlib_path
 
57
 
 
58
 
 
59
# Set PyQt4 API to #1 or #2
 
60
pyqt_api = int(os.environ.get("PYQT_API", "0"))
 
61
if pyqt_api:
 
62
    try:
 
63
        import sip
 
64
        try:
 
65
            for qtype in ('QString', 'QVariant'):
 
66
                sip.setapi(qtype, pyqt_api)
 
67
        except AttributeError:
 
68
            # Old version of sip
 
69
            pass
 
70
    except ImportError:
 
71
        pass
 
72
 
 
73
 
 
74
mpl_backend = os.environ.get("MATPLOTLIB_BACKEND")
 
75
if mpl_backend:
 
76
    try:
 
77
        import matplotlib
 
78
        matplotlib.use(mpl_backend)
 
79
    except ImportError:
 
80
        pass
 
81
 
 
82
 
 
83
if os.environ.get("MATPLOTLIB_PATCH", "").lower() == "true":
 
84
    try:
 
85
        from spyderlib import mpl_patch
 
86
        mpl_patch.apply()
 
87
    except ImportError:
 
88
        pass
 
89
 
 
90
 
 
91
if os.name == 'nt': # Windows platforms
 
92
            
 
93
    # Setting console encoding (otherwise Python does not recognize encoding)
 
94
    try:
 
95
        import locale, ctypes
 
96
        _t, _cp = locale.getdefaultlocale('LANG')
 
97
        try:
 
98
            _cp = int(_cp[2:])
 
99
            ctypes.windll.kernel32.SetConsoleCP(_cp)
 
100
            ctypes.windll.kernel32.SetConsoleOutputCP(_cp)
 
101
        except (ValueError, TypeError):
 
102
            # Code page number in locale is not valid
 
103
            pass
 
104
    except ImportError:
 
105
        pass
 
106
 
 
107
 
 
108
# Set standard outputs encoding:
 
109
# (otherwise, for example, print u"é" will fail)
 
110
encoding = None
 
111
try:
 
112
    import locale
 
113
except ImportError:
 
114
    pass
 
115
else:
 
116
    loc = locale.getdefaultlocale()
 
117
    if loc[1]:
 
118
        encoding = loc[1]
 
119
 
 
120
if encoding is None:
 
121
    encoding = "UTF-8"
 
122
 
 
123
sys.setdefaultencoding(encoding)
 
124
os.environ['SPYDER_ENCODING'] = encoding
 
125
    
 
126
try:
 
127
    import sitecustomize  #analysis:ignore
 
128
except ImportError:
 
129
    pass
 
130
 
 
131
 
 
132
# Communication between Spyder and the remote process
 
133
if os.environ.get('SPYDER_SHELL_ID') is None:
 
134
    monitor = None
 
135
else:
 
136
    from spyderlib.widgets.externalshell.monitor import Monitor
 
137
    monitor = Monitor("127.0.0.1",
 
138
                      int(os.environ['SPYDER_I_PORT']),
 
139
                      int(os.environ['SPYDER_N_PORT']),
 
140
                      os.environ['SPYDER_SHELL_ID'],
 
141
                      float(os.environ['SPYDER_AR_TIMEOUT']),
 
142
                      os.environ["SPYDER_AR_STATE"].lower() == "true")
 
143
    monitor.start()
 
144
    
 
145
    def open_in_spyder(source, lineno=1):
 
146
        """
 
147
        Open a source file in Spyder's editor (it could be a filename or a 
 
148
        Python module/package).
 
149
        
 
150
        If you want to use IPython's %edit use %ed instead
 
151
        """
 
152
        try:
 
153
            source = sys.modules[source]
 
154
        except KeyError:
 
155
            source = source
 
156
        if not isinstance(source, basestring):
 
157
            try:
 
158
                source = source.__file__
 
159
            except AttributeError:
 
160
                print "The argument must be either a string or a module object"
 
161
        if source.endswith('.pyc'):
 
162
            source = source[:-1]
 
163
        source = osp.abspath(source)
 
164
        if osp.exists(source):
 
165
            monitor.notify_open_file(source, lineno=lineno)
 
166
        else:
 
167
            print "Can't open file %s" % source
 
168
    __builtin__.open_in_spyder = open_in_spyder
 
169
    
 
170
    # * PyQt4:
 
171
    #   * Removing PyQt4 input hook which is not working well on Windows since 
 
172
    #     opening a subprocess do not attach a real console to it
 
173
    #     (with keyboard events...)
 
174
    #   * Replacing it with our own input hook
 
175
    # * PySide:
 
176
    #   * Installing an input hook: this feature is not yet supported 
 
177
    #     natively by PySide
 
178
    if os.environ.get("INSTALL_QT_INPUTHOOK", "").lower() == "true":
 
179
        if os.environ["QT_API"] == 'pyqt':
 
180
            from PyQt4 import QtCore
 
181
            # Removing PyQt's PyOS_InputHook implementation:
 
182
            QtCore.pyqtRemoveInputHook()
 
183
        elif os.environ["QT_API"] == 'pyside':
 
184
            from PySide import QtCore
 
185
            # XXX: when PySide will implement an input hook, we will have to 
 
186
            # remove it here
 
187
        else:
 
188
            assert False
 
189
 
 
190
        def qt_inputhook():
 
191
            """Qt input hook for Spyder's console
 
192
            
 
193
            This input hook wait for available stdin data (notified by
 
194
            ExternalPythonShell through the monitor's inputhook_flag
 
195
            attribute), and in the meantime it processes Qt events."""
 
196
            # Refreshing variable explorer, except on first input hook call:
 
197
            # (otherwise, on slow machines, this may freeze Spyder)
 
198
            monitor.refresh_from_inputhook()
 
199
            if os.name == 'nt':
 
200
                try:
 
201
                    # This call fails for Python without readline support
 
202
                    # (or on Windows platforms) when PyOS_InputHook is called
 
203
                    # for the second consecutive time, because the 100-bytes
 
204
                    # stdin buffer is full.
 
205
                    # For more details, see the `PyOS_StdioReadline` function
 
206
                    # in Python source code (Parser/myreadline.c)
 
207
                    sys.stdin.tell()
 
208
                except IOError:
 
209
                    return 0
 
210
            app = QtCore.QCoreApplication.instance()
 
211
            if app and app.thread() is QtCore.QThread.currentThread():
 
212
                timer = QtCore.QTimer()
 
213
                QtCore.QObject.connect(timer, QtCore.SIGNAL('timeout()'),
 
214
                                       app, QtCore.SLOT('quit()'))
 
215
                monitor.toggle_inputhook_flag(False)
 
216
                while not monitor.inputhook_flag:
 
217
                    timer.start(50)
 
218
                    QtCore.QCoreApplication.exec_()
 
219
                    timer.stop()
 
220
#                # Socket-based alternative:
 
221
#                socket = QtNetwork.QLocalSocket()
 
222
#                socket.connectToServer(os.environ['SPYDER_SHELL_ID'])
 
223
#                socket.waitForConnected(-1)
 
224
#                while not socket.waitForReadyRead(10):
 
225
#                    timer.start(50)
 
226
#                    QtCore.QCoreApplication.exec_()
 
227
#                    timer.stop()
 
228
#                socket.read(3)
 
229
#                socket.disconnectFromServer()
 
230
            return 0
 
231
 
 
232
        # Installing Spyder's PyOS_InputHook implementation:
 
233
        import ctypes
 
234
        cb_pyfunctype = ctypes.PYFUNCTYPE(ctypes.c_int)(qt_inputhook)
 
235
        pyos_ih = ctypes.c_void_p.in_dll(ctypes.pythonapi, "PyOS_InputHook")
 
236
        pyos_ih.value = ctypes.cast(cb_pyfunctype, ctypes.c_void_p).value
 
237
    else:
 
238
        # Quite limited feature: notify only when a result is displayed in
 
239
        # console (does not notify at every prompt)
 
240
        def displayhook(obj):
 
241
            sys.__displayhook__(obj)
 
242
            monitor.refresh()
 
243
    
 
244
        sys.displayhook = displayhook
 
245
 
 
246
 
 
247
#===============================================================================
 
248
# Monkey-patching pdb
 
249
#===============================================================================
 
250
class SpyderPdb(pdb.Pdb):
 
251
    def set_spyder_breakpoints(self):
 
252
        self.clear_all_breaks()
 
253
        #------Really deleting all breakpoints:
 
254
        for bp in bdb.Breakpoint.bpbynumber:
 
255
            if bp:
 
256
                bp.deleteMe()
 
257
        bdb.Breakpoint.next = 1
 
258
        bdb.Breakpoint.bplist = {}
 
259
        bdb.Breakpoint.bpbynumber = [None]
 
260
        #------
 
261
        from spyderlib.config import CONF
 
262
        CONF.load_from_ini()
 
263
        if CONF.get('run', 'breakpoints/enabled', True):
 
264
            breakpoints = CONF.get('run', 'breakpoints', {})
 
265
            i = 0
 
266
            for fname, data in breakpoints.iteritems():
 
267
                for linenumber, condition in data:
 
268
                    i += 1
 
269
                    self.set_break(self.canonic(fname), linenumber,
 
270
                                   cond=condition)
 
271
                    
 
272
    def notify_spyder(self, frame):
 
273
        if not frame:
 
274
            return
 
275
        fname = self.canonic(frame.f_code.co_filename)
 
276
        lineno = frame.f_lineno
 
277
        if isinstance(fname, basestring) and isinstance(lineno, int):
 
278
            if osp.isfile(fname) and monitor is not None:
 
279
                monitor.notify_pdb_step(fname, lineno)
 
280
 
 
281
pdb.Pdb = SpyderPdb
 
282
 
 
283
#XXX: I know, this function is now also implemented as is in utils/misc.py but
 
284
#     I'm kind of reluctant to import spyderlib in sitecustomize, even if this
 
285
#     import is very clean.
 
286
def monkeypatch_method(cls, patch_name):
 
287
    # This function's code was inspired from the following thread:
 
288
    # "[Python-Dev] Monkeypatching idioms -- elegant or ugly?"
 
289
    # by Robert Brewer <fumanchu at aminus.org>
 
290
    # (Tue Jan 15 19:13:25 CET 2008)
 
291
    """
 
292
    Add the decorated method to the given class; replace as needed.
 
293
    
 
294
    If the named method already exists on the given class, it will
 
295
    be replaced, and a reference to the old method is created as 
 
296
    cls._old<patch_name><name>. If the "_old_<patch_name>_<name>" attribute 
 
297
    already exists, KeyError is raised.
 
298
    """
 
299
    def decorator(func):
 
300
        fname = func.__name__
 
301
        old_func = getattr(cls, fname, None)
 
302
        if old_func is not None:
 
303
            # Add the old func to a list of old funcs.
 
304
            old_ref = "_old_%s_%s" % (patch_name, fname)
 
305
            #print old_ref, old_func
 
306
            old_attr = getattr(cls, old_ref, None)
 
307
            if old_attr is None:
 
308
                setattr(cls, old_ref, old_func)
 
309
            else:
 
310
                raise KeyError("%s.%s already exists."
 
311
                               % (cls.__name__, old_ref))
 
312
        setattr(cls, fname, func)
 
313
        return func
 
314
    return decorator
 
315
 
 
316
@monkeypatch_method(pdb.Pdb, 'Pdb')
 
317
def user_return(self, frame, return_value):
 
318
    """This function is called when a return trap is set here."""
 
319
    # This is useful when debugging in an active interpreter (otherwise,
 
320
    # the debugger will stop before reaching the target file)
 
321
    if self._wait_for_mainpyfile:
 
322
        if (self.mainpyfile != self.canonic(frame.f_code.co_filename)
 
323
            or frame.f_lineno<= 0):
 
324
            return
 
325
        self._wait_for_mainpyfile = 0
 
326
    self._old_Pdb_user_return(frame, return_value)
 
327
        
 
328
@monkeypatch_method(pdb.Pdb, 'Pdb')
 
329
def interaction(self, frame, traceback):
 
330
    self.setup(frame, traceback)
 
331
    self.notify_spyder(frame) #-----Spyder-specific-------------------------
 
332
    self.print_stack_entry(self.stack[self.curindex])
 
333
    self.cmdloop()
 
334
    self.forget()
 
335
 
 
336
@monkeypatch_method(pdb.Pdb, 'Pdb')
 
337
def reset(self):
 
338
    self._old_Pdb_reset()
 
339
    if monitor is not None:
 
340
        monitor.register_pdb_session(self)
 
341
    self.set_spyder_breakpoints()
 
342
 
 
343
#XXX: notify spyder on any pdb command (is that good or too lazy? i.e. is more 
 
344
#     specific behaviour desired?)
 
345
@monkeypatch_method(pdb.Pdb, 'Pdb')
 
346
def postcmd(self, stop, line):
 
347
    self.notify_spyder(self.curframe)
 
348
    return self._old_Pdb_postcmd(stop, line)
 
349
 
 
350
 
 
351
# Restoring (almost) original sys.path:
 
352
# (Note: do not remove spyderlib_path from sys.path because if Spyder has been
 
353
#  installed using python setup.py install, then this could remove the 
 
354
#  'site-packages' directory from sys.path!)
 
355
try:
 
356
    sys.path.remove(osp.join(spyderlib_path,
 
357
                             "spyderlib", "widgets", "externalshell"))
 
358
except ValueError:
 
359
    pass
 
360
 
 
361
# Ignore PyQt4's sip API changes (this should be used wisely -e.g. for
 
362
# debugging- as dynamic API change is not supported by PyQt)
 
363
if os.environ.get("IGNORE_SIP_SETAPI_ERRORS", "").lower() == "true":
 
364
    try:
 
365
        import sip
 
366
        from sip import setapi as original_setapi
 
367
        def patched_setapi(name, no):
 
368
            try:
 
369
                original_setapi(name, no)
 
370
            except ValueError, msg:
 
371
                print >>sys.stderr, "Warning/PyQt4-Spyder (%s)" % str(msg)
 
372
        sip.setapi = patched_setapi
 
373
    except ImportError:
 
374
        pass
 
375
 
 
376
 
 
377
# The following classes and functions are mainly intended to be used from 
 
378
# an interactive Python session
 
379
class UserModuleDeleter(object):
 
380
    """
 
381
    User Module Deleter (UMD) aims at deleting user modules 
 
382
    to force Python to deeply reload them during import
 
383
    
 
384
    pathlist [list]: blacklist in terms of module path
 
385
    namelist [list]: blacklist in terms of module name
 
386
    """
 
387
    def __init__(self, namelist=None, pathlist=None):
 
388
        if namelist is None:
 
389
            namelist = []
 
390
        self.namelist = namelist+['sitecustomize', 'spyderlib', 'spyderplugins']
 
391
        if pathlist is None:
 
392
            pathlist = []
 
393
        self.pathlist = pathlist
 
394
        self.previous_modules = sys.modules.keys()
 
395
 
 
396
    def is_module_blacklisted(self, modname, modpath):
 
397
        for path in [sys.prefix]+self.pathlist:
 
398
            if modpath.startswith(path):
 
399
                return True
 
400
        else:
 
401
            return set(modname.split('.')) & set(self.namelist)
 
402
        
 
403
    def run(self, verbose=False):
 
404
        """
 
405
        Del user modules to force Python to deeply reload them
 
406
        
 
407
        Do not del modules which are considered as system modules, i.e. 
 
408
        modules installed in subdirectories of Python interpreter's binary
 
409
        Do not del C modules
 
410
        """
 
411
        log = []
 
412
        for modname, module in sys.modules.items():
 
413
            if modname not in self.previous_modules:
 
414
                modpath = getattr(module, '__file__', None)
 
415
                if modpath is None:
 
416
                    # *module* is a C module that is statically linked into the 
 
417
                    # interpreter. There is no way to know its path, so we 
 
418
                    # choose to ignore it.
 
419
                    continue
 
420
                if not self.is_module_blacklisted(modname, modpath):
 
421
                    log.append(modname)
 
422
                    del sys.modules[modname]
 
423
        if verbose and log:
 
424
            print "\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted",
 
425
                                                     ": "+", ".join(log))
 
426
 
 
427
__umd__ = None
 
428
 
 
429
 
 
430
def _get_globals():
 
431
    """Return current Python interpreter globals namespace"""
 
432
    from __main__ import __dict__ as namespace
 
433
    shell = namespace.get('__ipythonshell__')
 
434
    if shell is not None and hasattr(shell, 'user_ns'):
 
435
        # IPython 0.13+ kernel
 
436
        return shell.user_ns
 
437
    else:
 
438
        # Python interpreter
 
439
        return namespace
 
440
    return namespace
 
441
 
 
442
 
 
443
def runfile(filename, args=None, wdir=None, namespace=None):
 
444
    """
 
445
    Run filename
 
446
    args: command line arguments (string)
 
447
    wdir: working directory
 
448
    """
 
449
    try:
 
450
        filename = filename.decode('utf-8')
 
451
    except (UnicodeError, TypeError):
 
452
        pass
 
453
    global __umd__
 
454
    if os.environ.get("UMD_ENABLED", "").lower() == "true":
 
455
        if __umd__ is None:
 
456
            namelist = os.environ.get("UMD_NAMELIST", None)
 
457
            if namelist is not None:
 
458
                namelist = namelist.split(',')
 
459
            __umd__ = UserModuleDeleter(namelist=namelist)
 
460
        else:
 
461
            verbose = os.environ.get("UMD_VERBOSE", "").lower() == "true"
 
462
            __umd__.run(verbose=verbose)
 
463
    if args is not None and not isinstance(args, basestring):
 
464
        raise TypeError("expected a character buffer object")
 
465
    if namespace is None:
 
466
        namespace = _get_globals()
 
467
    namespace['__file__'] = filename
 
468
    sys.argv = [filename]
 
469
    if args is not None:
 
470
        for arg in args.split():
 
471
            sys.argv.append(arg)
 
472
    if wdir is not None:
 
473
        try:
 
474
            wdir = wdir.decode('utf-8')
 
475
        except (UnicodeError, TypeError):
 
476
            pass
 
477
        os.chdir(wdir)
 
478
    execfile(filename, namespace)
 
479
    sys.argv = ['']
 
480
    namespace.pop('__file__')
 
481
    
 
482
__builtin__.runfile = runfile
 
483
 
 
484
 
 
485
def debugfile(filename, args=None, wdir=None):
 
486
    """
 
487
    Debug filename
 
488
    args: command line arguments (string)
 
489
    wdir: working directory
 
490
    """
 
491
    debugger = pdb.Pdb()
 
492
    filename = debugger.canonic(filename)
 
493
    debugger._wait_for_mainpyfile = 1
 
494
    debugger.mainpyfile = filename
 
495
    debugger._user_requested_quit = 0
 
496
    debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir))
 
497
 
 
498
__builtin__.debugfile = debugfile
 
499
 
 
500
 
 
501
def evalsc(command):
 
502
    """Evaluate special commands
 
503
    (analog to IPython's magic commands but far less powerful/complete)"""
 
504
    assert command.startswith(('%', '!'))
 
505
    system_command = command.startswith('!')
 
506
    command = command[1:].strip()
 
507
    if system_command:
 
508
        # System command
 
509
        if command.startswith('cd '):
 
510
            evalsc('%'+command)
 
511
        else:
 
512
            from subprocess import Popen, PIPE
 
513
            Popen(command, shell=True, stdin=PIPE)
 
514
            print '\n'
 
515
    else:
 
516
        # General command
 
517
        namespace = _get_globals()
 
518
        import re
 
519
        clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", command)
 
520
        cd_match = re.match(r"^cd \"?\'?([a-zA-Z0-9_\ \:\\\/\.]+)", command)
 
521
        if cd_match:
 
522
            os.chdir(eval('r"%s"' % cd_match.groups()[0].strip()))
 
523
        elif clear_match:
 
524
            varnames = clear_match.groups()[0].replace(' ', '').split(',')
 
525
            for varname in varnames:
 
526
                try:
 
527
                    namespace.pop(varname)
 
528
                except KeyError:
 
529
                    pass
 
530
        elif command in ('cd', 'pwd'):
 
531
            print os.getcwdu()
 
532
        elif command == 'ls':
 
533
            if os.name == 'nt':
 
534
                evalsc('!dir')
 
535
            else:
 
536
                evalsc('!ls')
 
537
        elif command == 'scientific':
 
538
            from spyderlib import baseconfig
 
539
            execfile(baseconfig.SCIENTIFIC_STARTUP, namespace)
 
540
        else:
 
541
            raise NotImplementedError, "Unsupported command: '%s'" % command
 
542
 
 
543
__builtin__.evalsc = evalsc
 
544
 
 
545
 
 
546
# Restoring original PYTHONPATH
 
547
try:
 
548
    os.environ['PYTHONPATH'] = os.environ['OLD_PYTHONPATH']
 
549
    del os.environ['OLD_PYTHONPATH']
 
550
except KeyError:
 
551
    if os.environ.get('PYTHONPATH') is not None:
 
552
        del os.environ['PYTHONPATH']