##// END OF EJS Templates
Merging upstream.
Brian Granger -
r2529:43350bb3 merge
parent child Browse files
Show More
@@ -0,0 +1,286 b''
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,0 +1,55 b''
1 =========================
2 IPython GUI Support Notes
3 =========================
4
5 IPython allows GUI event loops to be run in an interactive IPython session.
6 This is done using Python's PyOS_InputHook hook which Python calls
7 when the :func:`raw_input` function is called and waiting for user input.
8 IPython has versions of this hook for wx, pyqt4 and pygtk.
9
10 When a GUI program is used interactively within IPython, the event loop of
11 the GUI should *not* be started. This is because, the PyOS_Inputhook itself
12 is responsible for iterating the GUI event loop.
13
14 IPython has facilities for installing the needed input hook for each GUI
15 toolkit and for creating the needed main GUI application object. Usually,
16 these main application objects should be created only once and for some
17 GUI toolkits, special options have to be passed to the application object
18 to enable it to function properly in IPython.
19
20 We need to answer the following questions:
21
22 * Who is responsible for creating the main GUI application object, IPython
23 or third parties (matplotlib, enthought.traits, etc.)?
24
25 * What is the proper way for third party code to detect if a GUI application
26 object has already been created? If one has been created, how should
27 the existing instance be retrieved?
28
29 * In a GUI application object has been created, how should third party code
30 detect if the GUI event loop is running. It is not sufficient to call the
31 relevant function methods in the GUI toolkits (like ``IsMainLoopRunning``)
32 because those don't know if the GUI event loop is running through the
33 input hook.
34
35 * We might need a way for third party code to determine if it is running
36 in IPython or not. Currently, the only way of running GUI code in IPython
37 is by using the input hook, but eventually, GUI based versions of IPython
38 will allow the GUI event loop in the more traditional manner. We will need
39 a way for third party code to distinguish between these two cases.
40
41 Here is some sample code I have been using to debug this issue::
42
43 from matplotlib import pyplot as plt
44
45 from enthought.traits import api as traits
46
47 class Foo(traits.HasTraits):
48 a = traits.Float()
49
50 f = Foo()
51 f.configure_traits()
52
53 plt.plot(range(10))
54
55
@@ -0,0 +1,111 b''
1 ===============================
2 IPython session storage notes
3 ===============================
4
5 This document serves as a sample/template for ideas on how to store session
6 data on disk. This stems from discussions we had on various mailing lists, and
7 should be considered a pure work in progress. We haven't settled these ideas
8 completely yet, and there's a lot to discuss; this document should just serve
9 as a reference of the distilled points from various conversations on multiple
10 mailing lists, and will congeal over time on a specific design we implement.
11
12 The frontend would store, for now, 5 types of data:
13
14 #. Input: this is python/ipython code to be executed.
15
16 #. Output (python): result of executing Inputs.
17
18 #. Standard output: from subprocesses.
19
20 #. Standard error: from subprocesses.
21
22 #. Text: arbitrary text. For now, we'll just store plain text and will defer
23 to the user on how to format it, though it should be valid reST if it is
24 later to be converted into html/pdf.
25
26 The non-text cells would be stored on-disk as follows::
27
28 .. input-cell::
29 :id: 1
30
31 3+3
32
33 .. output-cell::
34 :id: 1
35
36 6
37
38 .. input-cell::
39 :id: 2
40
41 ls
42
43 .. stdout-cell::
44 :id: 2
45
46 a.py b.py
47
48 .. input-cell::
49 :id: 3
50
51 !askdfj
52
53 .. stderr-cell::
54 :id: 3
55
56 sh: askdfj: command not found
57
58 Brian made some interesting points on the mailing list inspired by the
59 Mathematica format, reproduced here for reference:
60
61 The Mathematica notebook format is a plain text file that itself is *valid
62 Mathematica code*. This id documented here:
63
64 http://reference.wolfram.com/mathematica/guide/LowLevelNotebookProgramming.html
65
66 For examples a simple notebook with one text cell is just::
67
68 Notebook[{Cell['Here is my text', 'Text']}]
69
70 Everything - input cells, output cells, static images and all are represented
71 in this way and embedded in the plain text notebook file. The Python
72 generalization of this would be the following:
73
74 * A Python notebook is plain text, importable Python code.
75
76 * That code is simply a tree of objects that declare the relevant parts of the
77 notebook.
78
79 This has a number of advantages:
80
81 * A notebook can be imported, manipulated and run by anyone who has the support
82 code (the notebook module that defines the relevant classes).
83
84 * A notebook doesn't need to be parsed. It is valid Python and can be imported
85 or exec'd. Once that is done, you have the full notebook in memory. You can
86 immediately do anything you want with it.
87
88 * The various Notebook, Cell, Image, etc. classes can know about how to output
89 to various formats, latex, html, reST, XML, etc::
90
91 import mynotebook
92 mynotebook.notebook.export('rest')
93
94 * Each individual format (HTML, reST, latex) has weaknesses. If you pick any
95 one to be *the* notebook format, you are building those weaknesses into your
96 design. A pure python based notebook format won't suffer from that syndrome.
97
98 * It is a clean separation of the model (Notebook, Cell, Image, etc.) and the
99 view (HTML, reST, etc.). Picking HTML or reST for the notebook format
100 confuses (at some level) the model and view...
101
102 * Third party code can define new Notebook elements that specify how they can
103 be rendered in different contexts. For example, matplotlib could ship a
104 Figure element that knows how to render itself as a native PyQt GUI, a static
105 image, a web page, etc.
106
107 * A notebook remains a single plain text file that anyone can edit - even if it
108 has embedded images. Neither HTML nor reST have the ability to inline
109 graphics in plain text files. While I love reST, it is a pain that I need an
110 entire directory of files to render a single Sphinx doc.
111 No newline at end of file
@@ -21,9 +21,9 b" name = 'ipython'"
21 # bdist_deb does not accept underscores (a Debian convention).
21 # bdist_deb does not accept underscores (a Debian convention).
22
22
23 development = True # change this to False to do a release
23 development = True # change this to False to do a release
24 version_base = '0.11'
24 version_base = '0.11.alpha1'
25 branch = 'ipython'
25 branch = 'ipython'
26 revision = '1363'
26 revision = '1223'
27
27
28 if development:
28 if development:
29 if branch == 'ipython':
29 if branch == 'ipython':
@@ -24,15 +24,25 b' The main classes in this module are:'
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Warnings control
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 import sys
31 import warnings
30 import warnings
32
31
33 # from IPython.utils import growl
32 # Twisted generates annoying warnings with Python 2.6, as will do other code
34 # growl.start("IPython1 Client")
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 #-----------------------------------------------------------------------------
35
44
45 import sys
36
46
37 from twisted.internet import reactor
47 from twisted.internet import reactor
38 from twisted.internet.error import PotentialZombieWarning
48 from twisted.internet.error import PotentialZombieWarning
@@ -73,8 +83,6 b' rit.setDaemon(True)'
73 rit.start()
83 rit.start()
74
84
75
85
76
77
78 __all__ = [
86 __all__ = [
79 'MapTask',
87 'MapTask',
80 'StringTask',
88 'StringTask',
@@ -23,7 +23,7 b' from IPython.core.component import Component'
23 from IPython.external import Itpl
23 from IPython.external import Itpl
24 from IPython.utils.traitlets import Str, Int, List, Unicode
24 from IPython.utils.traitlets import Str, Int, List, Unicode
25 from IPython.utils.path import get_ipython_module_path
25 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
27 from IPython.kernel.twistedutil import (
27 from IPython.kernel.twistedutil import (
28 gatherBoth,
28 gatherBoth,
29 make_deferred,
29 make_deferred,
@@ -538,7 +538,10 b' class SSHEngineSetLauncher(BaseLauncher):'
538 # This is only used on Windows.
538 # This is only used on Windows.
539 def find_job_cmd():
539 def find_job_cmd():
540 if os.name=='nt':
540 if os.name=='nt':
541 return find_cmd('job')
541 try:
542 return find_cmd('job')
543 except FindCmdError:
544 return 'job'
542 else:
545 else:
543 return 'job'
546 return 'job'
544
547
@@ -57,3 +57,5 b' methods in :class:`InteractiveShell` that manage code execution::'
57 nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r',
57 nx.draw_spectral(g, node_size=100, alpha=0.6, node_color='r',
58 font_size=10, node_shape='o')
58 font_size=10, node_shape='o')
59 plt.show()
59 plt.show()
60
61
General Comments 0
You need to be logged in to leave comments. Login now