Skip to content

Commit 43350bb

Browse files
committed
Merging upstream.
2 parents ec6b47e + 5234bee commit 43350bb

7 files changed

Lines changed: 475 additions & 10 deletions

File tree

IPython/core/release.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
# bdist_deb does not accept underscores (a Debian convention).
2222

2323
development = True # change this to False to do a release
24-
version_base = '0.11'
24+
version_base = '0.11.alpha1'
2525
branch = 'ipython'
26-
revision = '1363'
26+
revision = '1223'
2727

2828
if development:
2929
if branch == 'ipython':

IPython/kernel/client.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,25 @@
2424
#-----------------------------------------------------------------------------
2525

2626
#-----------------------------------------------------------------------------
27-
# Imports
27+
# Warnings control
2828
#-----------------------------------------------------------------------------
2929

30-
import sys
3130
import warnings
3231

33-
# from IPython.utils import growl
34-
# growl.start("IPython1 Client")
32+
# Twisted generates annoying warnings with Python 2.6, as will do other code
33+
# that imports 'sets' as of today
34+
warnings.filterwarnings('ignore', 'the sets module is deprecated',
35+
DeprecationWarning )
36+
37+
# This one also comes from Twisted
38+
warnings.filterwarnings('ignore', 'the sha module is deprecated',
39+
DeprecationWarning)
40+
41+
#-----------------------------------------------------------------------------
42+
# Imports
43+
#-----------------------------------------------------------------------------
3544

45+
import sys
3646

3747
from twisted.internet import reactor
3848
from twisted.internet.error import PotentialZombieWarning
@@ -73,8 +83,6 @@
7383
rit.start()
7484

7585

76-
77-
7886
__all__ = [
7987
'MapTask',
8088
'StringTask',

IPython/kernel/launcher.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from IPython.external import Itpl
2424
from IPython.utils.traitlets import Str, Int, List, Unicode
2525
from IPython.utils.path import get_ipython_module_path
26-
from IPython.utils.process import find_cmd, pycmd2argv
26+
from IPython.utils.process import find_cmd, pycmd2argv, FindCmdError
2727
from IPython.kernel.twistedutil import (
2828
gatherBoth,
2929
make_deferred,
@@ -538,7 +538,10 @@ class SSHEngineSetLauncher(BaseLauncher):
538538
# This is only used on Windows.
539539
def find_job_cmd():
540540
if os.name=='nt':
541-
return find_cmd('job')
541+
try:
542+
return find_cmd('job')
543+
except FindCmdError:
544+
return 'job'
542545
else:
543546
return 'job'
544547

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
========================================
2+
Design proposal for mod:`IPython.core`
3+
========================================
4+
5+
Currently mod:`IPython.core` is not well suited for use in GUI applications.
6+
The purpose of this document is to describe a design that will resolve this
7+
limitation.
8+
9+
Process and thread model
10+
========================
11+
12+
The design described here is based on a two process model. These two processes
13+
are:
14+
15+
1. The IPython engine/kernel. This process contains the user's namespace and
16+
is responsible for executing user code. If user code uses
17+
:mod:`enthought.traits` or uses a GUI toolkit to perform plotting, the GUI
18+
event loop will run in this process.
19+
20+
2. The GUI application. The user facing GUI application will run in a second
21+
process that communicates directly with the IPython engine using
22+
asynchronous messaging. The GUI application will not execute any user code.
23+
The canonical example of a GUI application that talks to the IPython
24+
engine, would be a GUI based IPython terminal. However, the GUI application
25+
could provide a more sophisticated interface such as a notebook.
26+
27+
We now describe the threading model of the IPython engine. Two threads will be
28+
used to implement the IPython engine: a main thread that executes user code
29+
and a networking thread that communicates with the outside world. This
30+
specific design is required by a number of different factors.
31+
32+
First, The IPython engine must run the GUI event loop if the user wants to
33+
perform interactive plotting. Because of the design of most GUIs, this means
34+
that the user code (which will make GUI calls) must live in the main thread.
35+
36+
Second, networking code in the engine (Twisted or otherwise) must be able to
37+
communicate with the outside world while user code runs. An example would be
38+
if user code does the following::
39+
40+
import time
41+
for i in range(10):
42+
print i
43+
time.sleep(2)
44+
45+
We would like to result of each ``print i`` to be seen by the GUI application
46+
before the entire code block completes. We call this asynchronous printing.
47+
For this to be possible, the networking code has to be able to be able to
48+
communicate the current value of ``sys.stdout`` to the GUI application while
49+
user code is run. Another example is using :mod:`IPython.kernel.client` in
50+
user code to perform a parallel computation by talking to an IPython
51+
controller and a set of engines (these engines are separate from the one we
52+
are discussing here). This module requires the Twisted event loop to be run in
53+
a different thread than user code.
54+
55+
For the GUI application, threads are optional. However, the GUI application
56+
does need to be able to perform network communications asynchronously (without
57+
blocking the GUI itself). With this in mind, there are two options:
58+
59+
* Use Twisted (or another non-blocking socket library) in the same thread as
60+
the GUI event loop.
61+
62+
* Don't use Twisted, but instead run networking code in the GUI application
63+
using blocking sockets in threads. This would require the usage of polling
64+
and queues to manage the networking in the GUI application.
65+
66+
Thus, for the GUI application, there is a choice between non-blocking sockets
67+
(Twisted) or threads.
68+
69+
Asynchronous messaging
70+
======================
71+
72+
The GUI application will use asynchronous message queues to communicate with
73+
the networking thread of the engine. Because this communication will typically
74+
happen over localhost, a simple, one way, network protocol like XML-RPC or
75+
JSON-RPC can be used to implement this messaging. These options will also make
76+
it easy to implement the required networking in the GUI application using the
77+
standard library. In applications where secure communications are required,
78+
Twisted and Foolscap will probably be the best way to go for now, but HTTP is
79+
also an option.
80+
81+
There is some flexibility as to where the message queues are located. One
82+
option is that we could create a third process (like the IPython controller)
83+
that only manages the message queues. This is attractive, but does require
84+
an additional process.
85+
86+
Using this communication channel, the GUI application and kernel/engine will
87+
be able to send messages back and forth. For the most part, these messages
88+
will have a request/reply form, but it will be possible for the kernel/engine
89+
to send multiple replies for a single request.
90+
91+
The GUI application will use these messages to control the engine/kernel.
92+
Examples of the types of things that will be possible are:
93+
94+
* Pass code (as a string) to be executed by the engine in the user's namespace
95+
as a string.
96+
97+
* Get the current value of stdout and stderr.
98+
99+
* Get the ``repr`` of an object returned (Out []:).
100+
101+
* Pass a string to the engine to be completed when the GUI application
102+
receives a tab completion event.
103+
104+
* Get a list of all variable names in the user's namespace.
105+
106+
The in memory format of a message should be a Python dictionary, as this
107+
will be easy to serialize using virtually any network protocol. The
108+
message dict should only contain basic types, such as strings, floats,
109+
ints, lists, tuples and other dicts.
110+
111+
Each message will have a unique id and will probably be determined by the
112+
messaging system and returned when something is queued in the message
113+
system. This unique id will be used to pair replies with requests.
114+
115+
Each message should have a header of key value pairs that can be introspected
116+
by the message system and a body, or payload, that is opaque. The queues
117+
themselves will be purpose agnostic, so the purpose of the message will have
118+
to be encoded in the message itself. While we are getting started, we
119+
probably don't need to distinguish between the header and body.
120+
121+
Here are some examples::
122+
123+
m1 = dict(
124+
method='execute',
125+
id=24, # added by the message system
126+
parent=None # not a reply,
127+
source_code='a=my_func()'
128+
)
129+
130+
This single message could generate a number of reply messages::
131+
132+
m2 = dict(
133+
method='stdout'
134+
id=25, # my id, added by the message system
135+
parent_id=24, # The message id of the request
136+
value='This was printed by my_func()'
137+
)
138+
139+
m3 = dict(
140+
method='stdout'
141+
id=26, # my id, added by the message system
142+
parent_id=24, # The message id of the request
143+
value='This too was printed by my_func() at a later time.'
144+
)
145+
146+
m4 = dict(
147+
method='execute_finished',
148+
id=27,
149+
parent_id=24
150+
# not sure what else should come back with this message,
151+
# but we will need a way for the GUI app to tell that an execute
152+
# is done.
153+
)
154+
155+
We should probably use flags for the method and other purposes:
156+
157+
EXECUTE='0'
158+
EXECUTE_REPLY='1'
159+
160+
This will keep out network traffic down and enable us to easily change the
161+
actual value that is sent.
162+
163+
Engine details
164+
==============
165+
166+
As discussed above, the engine will consist of two threads: a main thread and
167+
a networking thread. These two threads will communicate using a pair of
168+
queues: one for data and requests passing to the main thread (the main
169+
thread's "input queue") and another for data and requests passing out of the
170+
main thread (the main thread's "output queue"). Both threads will have an
171+
event loop that will enqueue elements on one queue and dequeue elements on the
172+
other queue.
173+
174+
The event loop of the main thread will be of a different nature depending on
175+
if the user wants to perform interactive plotting. If they do want to perform
176+
interactive plotting, the main threads event loop will simply be the GUI event
177+
loop. In that case, GUI timers will be used to monitor the main threads input
178+
queue. When elements appear on that queue, the main thread will respond
179+
appropriately. For example, if the queue contains an element that consists of
180+
user code to execute, the main thread will call the appropriate method of its
181+
IPython instance. If the user does not want to perform interactive plotting,
182+
the main thread will have a simpler event loop that will simply block on the
183+
input queue. When something appears on that queue, the main thread will awake
184+
and handle the request.
185+
186+
The event loop of the networking thread will typically be the Twisted event
187+
loop. While it is possible to implement the engine's networking without using
188+
Twisted, at this point, Twisted provides the best solution. Note that the GUI
189+
application does not need to use Twisted in this case. The Twisted event loop
190+
will contain an XML-RPC or JSON-RPC server that takes requests over the
191+
network and handles those requests by enqueing elements on the main thread's
192+
input queue or dequeing elements on the main thread's output queue.
193+
194+
Because of the asynchronous nature of the network communication, a single
195+
input and output queue will be used to handle the interaction with the main
196+
thread. It is also possible to use multiple queues to isolate the different
197+
types of requests, but our feeling is that this is more complicated than it
198+
needs to be.
199+
200+
One of the main issues is how stdout/stderr will be handled. Our idea is to
201+
replace sys.stdout/sys.stderr by custom classes that will immediately write
202+
data to the main thread's output queue when user code writes to these streams
203+
(by doing print). Once on the main thread's output queue, the networking
204+
thread will make the data available to the GUI application over the network.
205+
206+
One unavoidable limitation in this design is that if user code does a print
207+
and then enters non-GIL-releasing extension code, the networking thread will
208+
go silent until the GIL is again released. During this time, the networking
209+
thread will not be able to process the GUI application's requests of the
210+
engine. Thus, the values of stdout/stderr will be unavailable during this
211+
time. This goes beyond stdout/stderr, however. Anytime the main thread is
212+
holding the GIL, the networking thread will go silent and be unable to handle
213+
requests.
214+
215+
GUI Application details
216+
=======================
217+
218+
The GUI application will also have two threads. While this is not a strict
219+
requirement, it probably makes sense and is a good place to start. The main
220+
thread will be the GUI tread. The other thread will be a networking thread and
221+
will handle the messages that are sent to and from the engine process.
222+
223+
Like the engine, we will use two queues to control the flow of messages
224+
between the main thread and networking thread. One of these queues will be
225+
used for messages sent from the GUI application to the engine. When the GUI
226+
application needs to send a message to the engine, it will simply enque the
227+
appropriate message on this queue. The networking thread will watch this queue
228+
and forward messages to the engine using an appropriate network protocol.
229+
230+
The other queue will be used for incoming messages from the engine. The
231+
networking thread will poll for incoming messages from the engine. When it
232+
receives any message, it will simply put that message on this other queue. The
233+
GUI application will periodically see if there are any messages on this queue
234+
and if there are it will handle them.
235+
236+
The GUI application must be prepared to handle any incoming message at any
237+
time. Due to a variety of reasons, the one or more reply messages associated
238+
with a request, may appear at any time in the future and possible in different
239+
orders. It is also possible that a reply might not appear. An example of this
240+
would be a request for a tab completion event. If the engine is busy, it won't
241+
be possible to fulfill the request for a while. While the tab completion
242+
request will eventually be handled, the GUI application has to be prepared to
243+
abandon waiting for the reply if the user moves on or a certain timeout
244+
expires.
245+
246+
Prototype details
247+
=================
248+
249+
With this design, it should be possible to develop a relatively complete GUI
250+
application, while using a mock engine. This prototype should use the two
251+
process design described above, but instead of making actual network calls,
252+
the network thread of the GUI application should have an object that fakes the
253+
network traffic. This mock object will consume messages off of one queue,
254+
pause for a short while (to model network and other latencies) and then place
255+
reply messages on the other queue.
256+
257+
This simple design will allow us to determine exactly what the message types
258+
and formats should be as well as how the GUI application should interact with
259+
the two message queues. Note, it is not required that the mock object actually
260+
be able to execute Python code or actually complete strings in the users
261+
namespace. All of these things can simply be faked. This will also help us to
262+
understand what the interface needs to look like that handles the network
263+
traffic. This will also help us to understand the design of the engine better.
264+
265+
The GUI application should be developed using IPython's component, application
266+
and configuration system. It may take some work to see what the best way of
267+
integrating these things with PyQt are.
268+
269+
After this stage is done, we can move onto creating a real IPython engine for
270+
the GUI application to communicate with. This will likely be more work that
271+
the GUI application itself, but having a working GUI application will make it
272+
*much* easier to design and implement the engine.
273+
274+
We also might want to introduce a third process into the mix. Basically, this
275+
would be a central messaging hub that both the engine and GUI application
276+
would use to send and retrieve messages. This is not required, but it might be
277+
a really good idea.
278+
279+
Also, I have some ideas on the best way to handle notebook saving and
280+
persistence.
281+
282+
Refactoring of IPython.core
283+
===========================
284+
285+
We need to go through IPython.core and describe what specifically needs to be
286+
done.

0 commit comments

Comments
 (0)