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

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2013-02-27 09:51:28 UTC
  • mfrom: (1.1.18)
  • Revision ID: package-import@ubuntu.com-20130227095128-wtx1irpvf4vl79lj
Tags: 2.2.0~beta3+dfsg-1
* Imported Upstream version 2.2.0~beta3+dfsg
* debian /patches
  - 0002-feature-forwarded-add-icon-to-desktop-file.patch (deleted)
    this patch was integrated by the upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
#
3
 
# Copyright © 2011 Pierre Raybaut
4
 
# Licensed under the terms of the MIT License
5
 
# (see spyderlib/__init__.py for details)
6
 
 
7
 
"""External shell's introspection and notification servers"""
8
 
 
9
 
from spyderlib.qt.QtCore import QThread, SIGNAL, Signal
10
 
 
11
 
import threading
12
 
import socket
13
 
 
14
 
# Local imports
15
 
from spyderlib.baseconfig import get_conf_path, DEBUG
16
 
from spyderlib.utils.misc import select_port
17
 
from spyderlib.utils.debug import log_last_error
18
 
from spyderlib.utils.bsdsocket import read_packet, write_packet
19
 
 
20
 
 
21
 
LOG_FILENAME = get_conf_path('introspection.log')
22
 
 
23
 
if DEBUG:
24
 
    import logging
25
 
    logging.basicConfig(filename=get_conf_path('introspection_debug.log'),
26
 
                        level=logging.DEBUG)
27
 
 
28
 
SPYDER_PORT = 20128
29
 
 
30
 
class IntrospectionServer(threading.Thread):
31
 
    """Introspection server"""
32
 
    def __init__(self):
33
 
        threading.Thread.__init__(self)
34
 
        self.shells = {}
35
 
        self.setDaemon(True)
36
 
        global SPYDER_PORT
37
 
        self.port = SPYDER_PORT = select_port(default_port=SPYDER_PORT)
38
 
        SPYDER_PORT += 1
39
 
        
40
 
    def register(self, shell):
41
 
        """Register introspection server
42
 
        See notification server below"""
43
 
        shell_id = str(id(shell))
44
 
        self.shells[shell_id] = shell
45
 
    
46
 
    def send_socket(self, shell_id, sock):
47
 
        """Send socket to the appropriate object for later communication"""
48
 
        shell = self.shells[shell_id]
49
 
        shell.set_introspection_socket(sock)
50
 
        if DEBUG:
51
 
            logging.debug('Introspection server: shell [%r] port [%r]'
52
 
                          % (shell, self.port))
53
 
        
54
 
    def run(self):
55
 
        """Start server"""
56
 
        sock = socket.socket(socket.AF_INET)
57
 
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
58
 
        sock.bind( ("127.0.0.1", self.port) )
59
 
        
60
 
        while True:
61
 
            sock.listen(2)
62
 
            conn, _addr = sock.accept()
63
 
            shell_id = read_packet(conn)
64
 
            if shell_id is not None:
65
 
                self.send_socket(shell_id, conn)
66
 
 
67
 
class NotificationServer(IntrospectionServer):
68
 
    """Notification server"""
69
 
    def __init__(self):
70
 
        IntrospectionServer.__init__(self)
71
 
        self.notification_threads = {}
72
 
        
73
 
    def register(self, shell):
74
 
        """Register notification server
75
 
        See pythonshell.ExternalPythonShell.create_process"""
76
 
        IntrospectionServer.register(self, shell)
77
 
        shell_id = str(id(shell))
78
 
        n_thread = self.notification_threads[shell_id] = NotificationThread()
79
 
        return n_thread
80
 
    
81
 
    def send_socket(self, shell_id, sock):
82
 
        """Send socket to the appropriate object for later communication"""
83
 
        n_thread = self.notification_threads[shell_id]
84
 
        n_thread.set_notify_socket(sock)
85
 
        n_thread.start()
86
 
        if DEBUG:
87
 
            logging.debug('Notification server: shell [%r] port [%r]'
88
 
                          % (self.shells[shell_id], self.port))
89
 
 
90
 
INTROSPECTION_SERVER = None
91
 
 
92
 
def start_introspection_server():
93
 
    """
94
 
    Start introspection server (only one time)
95
 
    This server is dedicated to introspection features, i.e. Spyder is calling 
96
 
    it to retrieve informations on remote objects
97
 
    """
98
 
    global INTROSPECTION_SERVER
99
 
    if INTROSPECTION_SERVER is None:
100
 
        if DEBUG:
101
 
            import time
102
 
            time_str = "Logging time: %s" % time.ctime(time.time())
103
 
            logging.debug("="*len(time_str))
104
 
            logging.debug(time_str)
105
 
            logging.debug("="*len(time_str))
106
 
        INTROSPECTION_SERVER = IntrospectionServer()
107
 
        INTROSPECTION_SERVER.start()
108
 
    return INTROSPECTION_SERVER
109
 
 
110
 
NOTIFICATION_SERVER = None
111
 
 
112
 
def start_notification_server():
113
 
    """
114
 
    Start notify server (only one time)
115
 
    This server is dedicated to notification features, i.e. remote objects 
116
 
    are notifying Spyder about anything relevant like debugging data (pdb) 
117
 
    or "this is the right moment to refresh variable explorer" (syshook)
118
 
    """
119
 
    global NOTIFICATION_SERVER
120
 
    if NOTIFICATION_SERVER is None:
121
 
        NOTIFICATION_SERVER = NotificationServer()
122
 
        NOTIFICATION_SERVER.start()
123
 
    return NOTIFICATION_SERVER
124
 
 
125
 
 
126
 
class NotificationThread(QThread):
127
 
    """Notification thread"""
128
 
    sig_process_remote_view = Signal(object)
129
 
    def __init__(self):
130
 
        QThread.__init__(self)
131
 
        self.notify_socket = None
132
 
        
133
 
    def set_notify_socket(self, notify_socket):
134
 
        """Set the notification socket"""
135
 
        self.notify_socket = notify_socket
136
 
        
137
 
    def run(self):
138
 
        """Start notification thread"""
139
 
        while True:
140
 
            if self.notify_socket is None:
141
 
                continue
142
 
            output = None
143
 
            try:
144
 
                try:
145
 
                    cdict = read_packet(self.notify_socket)
146
 
                except:
147
 
                    # This except statement is intended to handle a struct.error
148
 
                    # (but when writing 'except struct.error', it doesn't work)
149
 
                    # Note: struct.error is raised when the communication has 
150
 
                    # been interrupted and the received data is not a string 
151
 
                    # of length 8 as required by struct.unpack (see read_packet)
152
 
                    break
153
 
                if cdict is None:
154
 
                    # Another notification thread has just terminated and 
155
 
                    # then wrote 'None' in the notification socket
156
 
                    # (see the 'finally' statement below)
157
 
                    continue
158
 
                if not isinstance(cdict, dict):
159
 
                    raise TypeError("Invalid data type: %r" % cdict)
160
 
                command = cdict['command']
161
 
                data = cdict.get('data')
162
 
                if command == 'pdb_step':
163
 
                    fname, lineno = data
164
 
                    self.emit(SIGNAL('pdb(QString,int)'), fname, lineno)
165
 
                    self.emit(SIGNAL('refresh_namespace_browser()'))
166
 
                elif command == 'refresh':
167
 
                    self.emit(SIGNAL('refresh_namespace_browser()'))
168
 
                elif command == 'remote_view':
169
 
                    self.sig_process_remote_view.emit(data)
170
 
                elif command == 'ipython_kernel':
171
 
                    self.emit(SIGNAL('new_ipython_kernel(QString)'), data)
172
 
                elif command == 'open_file':
173
 
                    fname, lineno = data
174
 
                    self.emit(SIGNAL('open_file(QString,int)'), fname, lineno)
175
 
                else:
176
 
                    raise RuntimeError('Unsupported command: %r' % command)
177
 
                if DEBUG:
178
 
                    logging.debug("received command: %r" % command)
179
 
            except:
180
 
                log_last_error(LOG_FILENAME, "notification thread")
181
 
            finally:
182
 
                try:
183
 
                    write_packet(self.notify_socket, output)
184
 
                except:
185
 
                    # The only reason why it should fail is that Spyder is 
186
 
                    # closing while this thread is still alive
187
 
                    break
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Copyright © 2011 Pierre Raybaut
 
4
# Licensed under the terms of the MIT License
 
5
# (see spyderlib/__init__.py for details)
 
6
 
 
7
"""External shell's introspection and notification servers"""
 
8
 
 
9
from spyderlib.qt.QtCore import QThread, SIGNAL, Signal
 
10
 
 
11
import threading
 
12
import socket
 
13
import errno
 
14
 
 
15
# Local imports
 
16
from spyderlib.baseconfig import get_conf_path, DEBUG
 
17
from spyderlib.utils.misc import select_port
 
18
from spyderlib.utils.debug import log_last_error
 
19
from spyderlib.utils.bsdsocket import read_packet, write_packet
 
20
 
 
21
 
 
22
LOG_FILENAME = get_conf_path('introspection.log')
 
23
 
 
24
if DEBUG:
 
25
    import logging
 
26
    logging.basicConfig(filename=get_conf_path('introspection_debug.log'),
 
27
                        level=logging.DEBUG)
 
28
 
 
29
SPYDER_PORT = 20128
 
30
 
 
31
class IntrospectionServer(threading.Thread):
 
32
    """Introspection server"""
 
33
    def __init__(self):
 
34
        threading.Thread.__init__(self)
 
35
        self.shells = {}
 
36
        self.setDaemon(True)
 
37
        global SPYDER_PORT
 
38
        self.port = SPYDER_PORT = select_port(default_port=SPYDER_PORT)
 
39
        SPYDER_PORT += 1
 
40
        
 
41
    def register(self, shell):
 
42
        """Register introspection server
 
43
        See notification server below"""
 
44
        shell_id = str(id(shell))
 
45
        self.shells[shell_id] = shell
 
46
    
 
47
    def send_socket(self, shell_id, sock):
 
48
        """Send socket to the appropriate object for later communication"""
 
49
        shell = self.shells[shell_id]
 
50
        shell.set_introspection_socket(sock)
 
51
        if DEBUG:
 
52
            logging.debug('Introspection server: shell [%r] port [%r]'
 
53
                          % (shell, self.port))
 
54
        
 
55
    def run(self):
 
56
        """Start server"""
 
57
        sock = socket.socket(socket.AF_INET)
 
58
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
59
        sock.bind( ("127.0.0.1", self.port) )
 
60
        
 
61
        while True:
 
62
            sock.listen(2)
 
63
            try:
 
64
                conn, _addr = sock.accept()
 
65
            except socket.error as e:
 
66
                # See Issue 1275 for details on why errno EINTR is
 
67
                # silently ignored here.
 
68
                if e.args[0] == errno.EINTR:
 
69
                    continue
 
70
                raise
 
71
            shell_id = read_packet(conn)
 
72
            if shell_id is not None:
 
73
                self.send_socket(shell_id, conn)
 
74
 
 
75
class NotificationServer(IntrospectionServer):
 
76
    """Notification server"""
 
77
    def __init__(self):
 
78
        IntrospectionServer.__init__(self)
 
79
        self.notification_threads = {}
 
80
        
 
81
    def register(self, shell):
 
82
        """Register notification server
 
83
        See pythonshell.ExternalPythonShell.create_process"""
 
84
        IntrospectionServer.register(self, shell)
 
85
        shell_id = str(id(shell))
 
86
        n_thread = self.notification_threads[shell_id] = NotificationThread()
 
87
        return n_thread
 
88
    
 
89
    def send_socket(self, shell_id, sock):
 
90
        """Send socket to the appropriate object for later communication"""
 
91
        n_thread = self.notification_threads[shell_id]
 
92
        n_thread.set_notify_socket(sock)
 
93
        n_thread.start()
 
94
        if DEBUG:
 
95
            logging.debug('Notification server: shell [%r] port [%r]'
 
96
                          % (self.shells[shell_id], self.port))
 
97
 
 
98
INTROSPECTION_SERVER = None
 
99
 
 
100
def start_introspection_server():
 
101
    """
 
102
    Start introspection server (only one time)
 
103
    This server is dedicated to introspection features, i.e. Spyder is calling 
 
104
    it to retrieve informations on remote objects
 
105
    """
 
106
    global INTROSPECTION_SERVER
 
107
    if INTROSPECTION_SERVER is None:
 
108
        if DEBUG:
 
109
            import time
 
110
            time_str = "Logging time: %s" % time.ctime(time.time())
 
111
            logging.debug("="*len(time_str))
 
112
            logging.debug(time_str)
 
113
            logging.debug("="*len(time_str))
 
114
        INTROSPECTION_SERVER = IntrospectionServer()
 
115
        INTROSPECTION_SERVER.start()
 
116
    return INTROSPECTION_SERVER
 
117
 
 
118
NOTIFICATION_SERVER = None
 
119
 
 
120
def start_notification_server():
 
121
    """
 
122
    Start notify server (only one time)
 
123
    This server is dedicated to notification features, i.e. remote objects 
 
124
    are notifying Spyder about anything relevant like debugging data (pdb) 
 
125
    or "this is the right moment to refresh variable explorer" (syshook)
 
126
    """
 
127
    global NOTIFICATION_SERVER
 
128
    if NOTIFICATION_SERVER is None:
 
129
        NOTIFICATION_SERVER = NotificationServer()
 
130
        NOTIFICATION_SERVER.start()
 
131
    return NOTIFICATION_SERVER
 
132
 
 
133
 
 
134
class NotificationThread(QThread):
 
135
    """Notification thread"""
 
136
    sig_process_remote_view = Signal(object)
 
137
    def __init__(self):
 
138
        QThread.__init__(self)
 
139
        self.notify_socket = None
 
140
        
 
141
    def set_notify_socket(self, notify_socket):
 
142
        """Set the notification socket"""
 
143
        self.notify_socket = notify_socket
 
144
        
 
145
    def run(self):
 
146
        """Start notification thread"""
 
147
        while True:
 
148
            if self.notify_socket is None:
 
149
                continue
 
150
            output = None
 
151
            try:
 
152
                try:
 
153
                    cdict = read_packet(self.notify_socket)
 
154
                except:
 
155
                    # This except statement is intended to handle a struct.error
 
156
                    # (but when writing 'except struct.error', it doesn't work)
 
157
                    # Note: struct.error is raised when the communication has 
 
158
                    # been interrupted and the received data is not a string 
 
159
                    # of length 8 as required by struct.unpack (see read_packet)
 
160
                    break
 
161
                if cdict is None:
 
162
                    # Another notification thread has just terminated and 
 
163
                    # then wrote 'None' in the notification socket
 
164
                    # (see the 'finally' statement below)
 
165
                    continue
 
166
                if not isinstance(cdict, dict):
 
167
                    raise TypeError("Invalid data type: %r" % cdict)
 
168
                command = cdict['command']
 
169
                data = cdict.get('data')
 
170
                if command == 'pdb_step':
 
171
                    fname, lineno = data
 
172
                    self.emit(SIGNAL('pdb(QString,int)'), fname, lineno)
 
173
                    self.emit(SIGNAL('refresh_namespace_browser()'))
 
174
                elif command == 'refresh':
 
175
                    self.emit(SIGNAL('refresh_namespace_browser()'))
 
176
                elif command == 'remote_view':
 
177
                    self.sig_process_remote_view.emit(data)
 
178
                elif command == 'ipykernel':
 
179
                    self.emit(SIGNAL('new_ipython_kernel(QString)'), data)
 
180
                elif command == 'open_file':
 
181
                    fname, lineno = data
 
182
                    self.emit(SIGNAL('open_file(QString,int)'), fname, lineno)
 
183
                else:
 
184
                    raise RuntimeError('Unsupported command: %r' % command)
 
185
                if DEBUG:
 
186
                    logging.debug("received command: %r" % command)
 
187
            except:
 
188
                log_last_error(LOG_FILENAME, "notification thread")
 
189
            finally:
 
190
                try:
 
191
                    write_packet(self.notify_socket, output)
 
192
                except:
 
193
                    # The only reason why it should fail is that Spyder is 
 
194
                    # closing while this thread is still alive
 
195
                    break