1
# -*- coding: utf-8 -*-
3
# Copyright © 2011 Pierre Raybaut
4
# Licensed under the terms of the MIT License
5
# (see spyderlib/__init__.py for details)
7
"""External shell's introspection and notification servers"""
9
from spyderlib.qt.QtCore import QThread, SIGNAL, Signal
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
21
LOG_FILENAME = get_conf_path('introspection.log')
25
logging.basicConfig(filename=get_conf_path('introspection_debug.log'),
30
class IntrospectionServer(threading.Thread):
31
"""Introspection server"""
33
threading.Thread.__init__(self)
37
self.port = SPYDER_PORT = select_port(default_port=SPYDER_PORT)
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
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)
51
logging.debug('Introspection server: shell [%r] port [%r]'
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) )
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)
67
class NotificationServer(IntrospectionServer):
68
"""Notification server"""
70
IntrospectionServer.__init__(self)
71
self.notification_threads = {}
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()
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)
87
logging.debug('Notification server: shell [%r] port [%r]'
88
% (self.shells[shell_id], self.port))
90
INTROSPECTION_SERVER = None
92
def start_introspection_server():
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
98
global INTROSPECTION_SERVER
99
if INTROSPECTION_SERVER is None:
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
110
NOTIFICATION_SERVER = None
112
def start_notification_server():
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)
119
global NOTIFICATION_SERVER
120
if NOTIFICATION_SERVER is None:
121
NOTIFICATION_SERVER = NotificationServer()
122
NOTIFICATION_SERVER.start()
123
return NOTIFICATION_SERVER
126
class NotificationThread(QThread):
127
"""Notification thread"""
128
sig_process_remote_view = Signal(object)
130
QThread.__init__(self)
131
self.notify_socket = None
133
def set_notify_socket(self, notify_socket):
134
"""Set the notification socket"""
135
self.notify_socket = notify_socket
138
"""Start notification thread"""
140
if self.notify_socket is None:
145
cdict = read_packet(self.notify_socket)
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)
154
# Another notification thread has just terminated and
155
# then wrote 'None' in the notification socket
156
# (see the 'finally' statement below)
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':
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':
174
self.emit(SIGNAL('open_file(QString,int)'), fname, lineno)
176
raise RuntimeError('Unsupported command: %r' % command)
178
logging.debug("received command: %r" % command)
180
log_last_error(LOG_FILENAME, "notification thread")
183
write_packet(self.notify_socket, output)
185
# The only reason why it should fail is that Spyder is
186
# closing while this thread is still alive
1
# -*- coding: utf-8 -*-
3
# Copyright © 2011 Pierre Raybaut
4
# Licensed under the terms of the MIT License
5
# (see spyderlib/__init__.py for details)
7
"""External shell's introspection and notification servers"""
9
from spyderlib.qt.QtCore import QThread, SIGNAL, Signal
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
22
LOG_FILENAME = get_conf_path('introspection.log')
26
logging.basicConfig(filename=get_conf_path('introspection_debug.log'),
31
class IntrospectionServer(threading.Thread):
32
"""Introspection server"""
34
threading.Thread.__init__(self)
38
self.port = SPYDER_PORT = select_port(default_port=SPYDER_PORT)
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
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)
52
logging.debug('Introspection server: shell [%r] port [%r]'
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) )
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:
71
shell_id = read_packet(conn)
72
if shell_id is not None:
73
self.send_socket(shell_id, conn)
75
class NotificationServer(IntrospectionServer):
76
"""Notification server"""
78
IntrospectionServer.__init__(self)
79
self.notification_threads = {}
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()
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)
95
logging.debug('Notification server: shell [%r] port [%r]'
96
% (self.shells[shell_id], self.port))
98
INTROSPECTION_SERVER = None
100
def start_introspection_server():
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
106
global INTROSPECTION_SERVER
107
if INTROSPECTION_SERVER is None:
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
118
NOTIFICATION_SERVER = None
120
def start_notification_server():
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)
127
global NOTIFICATION_SERVER
128
if NOTIFICATION_SERVER is None:
129
NOTIFICATION_SERVER = NotificationServer()
130
NOTIFICATION_SERVER.start()
131
return NOTIFICATION_SERVER
134
class NotificationThread(QThread):
135
"""Notification thread"""
136
sig_process_remote_view = Signal(object)
138
QThread.__init__(self)
139
self.notify_socket = None
141
def set_notify_socket(self, notify_socket):
142
"""Set the notification socket"""
143
self.notify_socket = notify_socket
146
"""Start notification thread"""
148
if self.notify_socket is None:
153
cdict = read_packet(self.notify_socket)
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)
162
# Another notification thread has just terminated and
163
# then wrote 'None' in the notification socket
164
# (see the 'finally' statement below)
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':
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':
182
self.emit(SIGNAL('open_file(QString,int)'), fname, lineno)
184
raise RuntimeError('Unsupported command: %r' % command)
186
logging.debug("received command: %r" % command)
188
log_last_error(LOG_FILENAME, "notification thread")
191
write_packet(self.notify_socket, output)
193
# The only reason why it should fail is that Spyder is
194
# closing while this thread is still alive