##// END OF EJS Templates
Documentation for the project added
muzgash -
r2542:78287a08 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
@@ -5,7 +5,6 b' IPython.'
5
5
6 IPython is a set of tools for interactive and exploratory computing in Python.
6 IPython is a set of tools for interactive and exploratory computing in Python.
7 """
7 """
8
9 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2009 The IPython Development Team
9 # Copyright (C) 2008-2009 The IPython Development Team
11 #
10 #
@@ -112,7 +112,7 b' c = get_config()'
112 # ]
112 # ]
113 # c.InteractiveShell.readline_remove_delims = '-/~'
113 # c.InteractiveShell.readline_remove_delims = '-/~'
114 # c.InteractiveShell.readline_merge_completions = True
114 # c.InteractiveShell.readline_merge_completions = True
115 # c.InteractiveShell.readline_omit_names = 0
115 # c.InteractiveShell.readline_omit__names = 0
116
116
117 # c.InteractiveShell.screen_length = 0
117 # c.InteractiveShell.screen_length = 0
118
118
@@ -145,4 +145,4 b' c = get_config()'
145
145
146 # c.AliasManager.user_aliases = [
146 # c.AliasManager.user_aliases = [
147 # ('foo', 'echo Hi')
147 # ('foo', 'echo Hi')
148 # ] No newline at end of file
148 # ]
@@ -224,7 +224,7 b' class Application(object):'
224
224
225 For the most part, we try to set default in the class attributes
225 For the most part, we try to set default in the class attributes
226 of Components. But, defaults the top-level Application (which is
226 of Components. But, defaults the top-level Application (which is
227 not a HasTraitlets or Component) are not set in this way. Instead
227 not a HasTraits or Component) are not set in this way. Instead
228 we set them here. The Global section is for variables like this that
228 we set them here. The Global section is for variables like this that
229 don't belong to a particular component.
229 don't belong to a particular component.
230 """
230 """
@@ -27,7 +27,7 b' from weakref import WeakValueDictionary'
27 from IPython.utils.importstring import import_item
27 from IPython.utils.importstring import import_item
28 from IPython.config.loader import Config
28 from IPython.config.loader import Config
29 from IPython.utils.traitlets import (
29 from IPython.utils.traitlets import (
30 HasTraitlets, MetaHasTraitlets, Instance, This
30 HasTraits, MetaHasTraits, Instance, This
31 )
31 )
32
32
33
33
@@ -173,7 +173,7 b' class __ComponentNameGenerator(object):'
173 ComponentNameGenerator = __ComponentNameGenerator('ipython.component')
173 ComponentNameGenerator = __ComponentNameGenerator('ipython.component')
174
174
175
175
176 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
176 class MetaComponent(MetaHasTraits, MetaComponentTracker):
177 pass
177 pass
178
178
179
179
@@ -182,11 +182,11 b' class MetaComponent(MetaHasTraitlets, MetaComponentTracker):'
182 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
183
183
184
184
185 class Component(HasTraitlets):
185 class Component(HasTraits):
186
186
187 __metaclass__ = MetaComponent
187 __metaclass__ = MetaComponent
188
188
189 # Traitlets are fun!
189 # Traits are fun!
190 config = Instance(Config,(),{})
190 config = Instance(Config,(),{})
191 parent = This()
191 parent = This()
192 root = This()
192 root = This()
@@ -256,7 +256,7 b' class Component(HasTraitlets):'
256 self.created = datetime.datetime.now()
256 self.created = datetime.datetime.now()
257
257
258 #-------------------------------------------------------------------------
258 #-------------------------------------------------------------------------
259 # Static traitlet notifiations
259 # Static trait notifiations
260 #-------------------------------------------------------------------------
260 #-------------------------------------------------------------------------
261
261
262 def _parent_changed(self, name, old, new):
262 def _parent_changed(self, name, old, new):
@@ -282,12 +282,12 b' class Component(HasTraitlets):'
282 def _config_changed(self, name, old, new):
282 def _config_changed(self, name, old, new):
283 """Update all the class traits having ``config=True`` as metadata.
283 """Update all the class traits having ``config=True`` as metadata.
284
284
285 For any class traitlet with a ``config`` metadata attribute that is
285 For any class trait with a ``config`` metadata attribute that is
286 ``True``, we update the traitlet with the value of the corresponding
286 ``True``, we update the trait with the value of the corresponding
287 config entry.
287 config entry.
288 """
288 """
289 # Get all traitlets with a config metadata entry that is True
289 # Get all traits with a config metadata entry that is True
290 traitlets = self.traitlets(config=True)
290 traits = self.traits(config=True)
291
291
292 # We auto-load config section for this class as well as any parent
292 # We auto-load config section for this class as well as any parent
293 # classes that are Component subclasses. This starts with Component
293 # classes that are Component subclasses. This starts with Component
@@ -301,7 +301,7 b' class Component(HasTraitlets):'
301 # dynamically create the section with name self.__class__.__name__.
301 # dynamically create the section with name self.__class__.__name__.
302 if new._has_section(sname):
302 if new._has_section(sname):
303 my_config = new[sname]
303 my_config = new[sname]
304 for k, v in traitlets.items():
304 for k, v in traits.items():
305 # Don't allow traitlets with config=True to start with
305 # Don't allow traitlets with config=True to start with
306 # uppercase. Otherwise, they are confused with Config
306 # uppercase. Otherwise, they are confused with Config
307 # subsections. But, developers shouldn't have uppercase
307 # subsections. But, developers shouldn't have uppercase
@@ -182,7 +182,7 b' def get_default_colors():'
182 class SeparateStr(Str):
182 class SeparateStr(Str):
183 """A Str subclass to validate separate_in, separate_out, etc.
183 """A Str subclass to validate separate_in, separate_out, etc.
184
184
185 This is a Str based traitlet that converts '0'->'' and '\\n'->'\n'.
185 This is a Str based trait that converts '0'->'' and '\\n'->'\n'.
186 """
186 """
187
187
188 def validate(self, obj, value):
188 def validate(self, obj, value):
@@ -263,7 +263,7 b' class InteractiveShell(Component, Magic):'
263
263
264 screen_length = Int(0, config=True)
264 screen_length = Int(0, config=True)
265
265
266 # Use custom TraitletTypes that convert '0'->'' and '\\n'->'\n'
266 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
267 separate_in = SeparateStr('\n', config=True)
267 separate_in = SeparateStr('\n', config=True)
268 separate_out = SeparateStr('', config=True)
268 separate_out = SeparateStr('', config=True)
269 separate_out2 = SeparateStr('', config=True)
269 separate_out2 = SeparateStr('', config=True)
@@ -286,7 +286,7 b' class InteractiveShell(Component, Magic):'
286 banner1=None, banner2=None, display_banner=None,
286 banner1=None, banner2=None, display_banner=None,
287 custom_exceptions=((),None)):
287 custom_exceptions=((),None)):
288
288
289 # This is where traitlets with a config_key argument are updated
289 # This is where traits with a config_key argument are updated
290 # from the values on config.
290 # from the values on config.
291 super(InteractiveShell, self).__init__(parent, config=config)
291 super(InteractiveShell, self).__init__(parent, config=config)
292
292
@@ -341,7 +341,7 b' class InteractiveShell(Component, Magic):'
341 return self
341 return self
342
342
343 #-------------------------------------------------------------------------
343 #-------------------------------------------------------------------------
344 # Traitlet changed handlers
344 # Trait changed handlers
345 #-------------------------------------------------------------------------
345 #-------------------------------------------------------------------------
346
346
347 def _banner1_changed(self):
347 def _banner1_changed(self):
@@ -24,7 +24,7 b' from unittest import TestCase'
24
24
25 from IPython.core.component import Component, ComponentError
25 from IPython.core.component import Component, ComponentError
26 from IPython.utils.traitlets import (
26 from IPython.utils.traitlets import (
27 TraitletError, Int, Float, Str
27 TraitError, Int, Float, Str
28 )
28 )
29 from IPython.config.loader import Config
29 from IPython.config.loader import Config
30
30
@@ -109,7 +109,7 b' class TestComponent(TestCase):'
109
109
110 def test_subclass_parent(self):
110 def test_subclass_parent(self):
111 c1 = Component(None)
111 c1 = Component(None)
112 self.assertRaises(TraitletError, setattr, c1, 'parent', 10)
112 self.assertRaises(TraitError, setattr, c1, 'parent', 10)
113
113
114 class MyComponent(Component):
114 class MyComponent(Component):
115 pass
115 pass
@@ -28,7 +28,10 b' from zope.interface import Interface, implements'
28 from twisted.internet.base import DelayedCall
28 from twisted.internet.base import DelayedCall
29 DelayedCall.debug = True
29 DelayedCall.debug = True
30
30
31 from foolscap import Referenceable, DeadReferenceError
31 try:
32 from foolscap.api import Referenceable, DeadReferenceError
33 except ImportError:
34 from foolscap import Referenceable, DeadReferenceError
32 from foolscap.referenceable import RemoteReference
35 from foolscap.referenceable import RemoteReference
33
36
34 from IPython.kernel.pbutil import packageFailure, unpackageFailure
37 from IPython.kernel.pbutil import packageFailure, unpackageFailure
@@ -24,7 +24,10 b' from twisted.internet import reactor, defer'
24 from twisted.python import log
24 from twisted.python import log
25
25
26 import foolscap
26 import foolscap
27 from foolscap import Tub, UnauthenticatedTub
27 try:
28 from foolscap.api import Tub, UnauthenticatedTub
29 except ImportError:
30 from foolscap import Tub, UnauthenticatedTub
28
31
29 from IPython.config.loader import Config
32 from IPython.config.loader import Config
30 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
33 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
@@ -22,7 +22,11 b' import warnings'
22 from twisted.python import components
22 from twisted.python import components
23 from twisted.python.failure import Failure
23 from twisted.python.failure import Failure
24 from zope.interface import Interface, implements, Attribute
24 from zope.interface import Interface, implements, Attribute
25 from foolscap import DeadReferenceError
25
26 try:
27 from foolscap.api import DeadReferenceError
28 except ImportError:
29 from foolscap import DeadReferenceError
26
30
27 from IPython.utils.coloransi import TermColors
31 from IPython.utils.coloransi import TermColors
28
32
@@ -24,7 +24,10 b' from zope.interface import Interface, implements'
24 from twisted.internet import defer
24 from twisted.internet import defer
25 from twisted.python import components, failure
25 from twisted.python import components, failure
26
26
27 from foolscap import Referenceable
27 try:
28 from foolscap.api import Referenceable
29 except ImportError:
30 from foolscap import Referenceable
28
31
29 from IPython.kernel import error
32 from IPython.kernel import error
30 from IPython.kernel import map as Map
33 from IPython.kernel import map as Map
@@ -20,7 +20,11 b' __docformat__ = "restructuredtext en"'
20
20
21 from zope.interface import Interface, implements
21 from zope.interface import Interface, implements
22 from twisted.python import components
22 from twisted.python import components
23 from foolscap import DeadReferenceError
23
24 try:
25 from foolscap.api import DeadReferenceError
26 except ImportError:
27 from foolscap import DeadReferenceError
24
28
25 from IPython.kernel.twistedutil import blockingCallFromThread
29 from IPython.kernel.twistedutil import blockingCallFromThread
26 from IPython.kernel import task, error
30 from IPython.kernel import task, error
@@ -24,7 +24,10 b' from zope.interface import Interface, implements'
24 from twisted.internet import defer
24 from twisted.internet import defer
25 from twisted.python import components
25 from twisted.python import components
26
26
27 from foolscap import Referenceable
27 try:
28 from foolscap.api import Referenceable
29 except ImportError:
30 from foolscap import Referenceable
28
31
29 from IPython.kernel import task as taskmodule
32 from IPython.kernel import task as taskmodule
30 from IPython.kernel.clientinterfaces import (
33 from IPython.kernel.clientinterfaces import (
@@ -25,8 +25,8 b' Authors:'
25 from unittest import TestCase
25 from unittest import TestCase
26
26
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
28 HasTraits, MetaHasTraits, TraitType, Any,
29 Int, Long, Float, Complex, Str, Unicode, TraitletError,
29 Int, Long, Float, Complex, Str, Unicode, TraitError,
30 Undefined, Type, This, Instance
30 Undefined, Type, This, Instance
31 )
31 )
32
32
@@ -36,9 +36,9 b' from IPython.utils.traitlets import ('
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38
38
39 class HasTraitletsStub(HasTraitlets):
39 class HasTraitsStub(HasTraits):
40
40
41 def _notify_traitlet(self, name, old, new):
41 def _notify_trait(self, name, old, new):
42 self._notify_name = name
42 self._notify_name = name
43 self._notify_old = old
43 self._notify_old = old
44 self._notify_new = new
44 self._notify_new = new
@@ -49,17 +49,17 b' class HasTraitletsStub(HasTraitlets):'
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 class TestTraitletType(TestCase):
52 class TestTraitType(TestCase):
53
53
54 def test_get_undefined(self):
54 def test_get_undefined(self):
55 class A(HasTraitlets):
55 class A(HasTraits):
56 a = TraitletType
56 a = TraitType
57 a = A()
57 a = A()
58 self.assertEquals(a.a, Undefined)
58 self.assertEquals(a.a, Undefined)
59
59
60 def test_set(self):
60 def test_set(self):
61 class A(HasTraitletsStub):
61 class A(HasTraitsStub):
62 a = TraitletType
62 a = TraitType
63
63
64 a = A()
64 a = A()
65 a.a = 10
65 a.a = 10
@@ -69,10 +69,10 b' class TestTraitletType(TestCase):'
69 self.assertEquals(a._notify_new, 10)
69 self.assertEquals(a._notify_new, 10)
70
70
71 def test_validate(self):
71 def test_validate(self):
72 class MyTT(TraitletType):
72 class MyTT(TraitType):
73 def validate(self, inst, value):
73 def validate(self, inst, value):
74 return -1
74 return -1
75 class A(HasTraitletsStub):
75 class A(HasTraitsStub):
76 tt = MyTT
76 tt = MyTT
77
77
78 a = A()
78 a = A()
@@ -80,26 +80,26 b' class TestTraitletType(TestCase):'
80 self.assertEquals(a.tt, -1)
80 self.assertEquals(a.tt, -1)
81
81
82 def test_default_validate(self):
82 def test_default_validate(self):
83 class MyIntTT(TraitletType):
83 class MyIntTT(TraitType):
84 def validate(self, obj, value):
84 def validate(self, obj, value):
85 if isinstance(value, int):
85 if isinstance(value, int):
86 return value
86 return value
87 self.error(obj, value)
87 self.error(obj, value)
88 class A(HasTraitlets):
88 class A(HasTraits):
89 tt = MyIntTT(10)
89 tt = MyIntTT(10)
90 a = A()
90 a = A()
91 self.assertEquals(a.tt, 10)
91 self.assertEquals(a.tt, 10)
92
92
93 # Defaults are validated when the HasTraitlets is instantiated
93 # Defaults are validated when the HasTraits is instantiated
94 class B(HasTraitlets):
94 class B(HasTraits):
95 tt = MyIntTT('bad default')
95 tt = MyIntTT('bad default')
96 self.assertRaises(TraitletError, B)
96 self.assertRaises(TraitError, B)
97
97
98 def test_is_valid_for(self):
98 def test_is_valid_for(self):
99 class MyTT(TraitletType):
99 class MyTT(TraitType):
100 def is_valid_for(self, value):
100 def is_valid_for(self, value):
101 return True
101 return True
102 class A(HasTraitlets):
102 class A(HasTraits):
103 tt = MyTT
103 tt = MyTT
104
104
105 a = A()
105 a = A()
@@ -107,10 +107,10 b' class TestTraitletType(TestCase):'
107 self.assertEquals(a.tt, 10)
107 self.assertEquals(a.tt, 10)
108
108
109 def test_value_for(self):
109 def test_value_for(self):
110 class MyTT(TraitletType):
110 class MyTT(TraitType):
111 def value_for(self, value):
111 def value_for(self, value):
112 return 20
112 return 20
113 class A(HasTraitlets):
113 class A(HasTraits):
114 tt = MyTT
114 tt = MyTT
115
115
116 a = A()
116 a = A()
@@ -118,33 +118,33 b' class TestTraitletType(TestCase):'
118 self.assertEquals(a.tt, 20)
118 self.assertEquals(a.tt, 20)
119
119
120 def test_info(self):
120 def test_info(self):
121 class A(HasTraitlets):
121 class A(HasTraits):
122 tt = TraitletType
122 tt = TraitType
123 a = A()
123 a = A()
124 self.assertEquals(A.tt.info(), 'any value')
124 self.assertEquals(A.tt.info(), 'any value')
125
125
126 def test_error(self):
126 def test_error(self):
127 class A(HasTraitlets):
127 class A(HasTraits):
128 tt = TraitletType
128 tt = TraitType
129 a = A()
129 a = A()
130 self.assertRaises(TraitletError, A.tt.error, a, 10)
130 self.assertRaises(TraitError, A.tt.error, a, 10)
131
131
132
132
133 class TestHasTraitletsMeta(TestCase):
133 class TestHasTraitsMeta(TestCase):
134
134
135 def test_metaclass(self):
135 def test_metaclass(self):
136 self.assertEquals(type(HasTraitlets), MetaHasTraitlets)
136 self.assertEquals(type(HasTraits), MetaHasTraits)
137
137
138 class A(HasTraitlets):
138 class A(HasTraits):
139 a = Int
139 a = Int
140
140
141 a = A()
141 a = A()
142 self.assertEquals(type(a.__class__), MetaHasTraitlets)
142 self.assertEquals(type(a.__class__), MetaHasTraits)
143 self.assertEquals(a.a,0)
143 self.assertEquals(a.a,0)
144 a.a = 10
144 a.a = 10
145 self.assertEquals(a.a,10)
145 self.assertEquals(a.a,10)
146
146
147 class B(HasTraitlets):
147 class B(HasTraits):
148 b = Int()
148 b = Int()
149
149
150 b = B()
150 b = B()
@@ -152,7 +152,7 b' class TestHasTraitletsMeta(TestCase):'
152 b.b = 10
152 b.b = 10
153 self.assertEquals(b.b,10)
153 self.assertEquals(b.b,10)
154
154
155 class C(HasTraitlets):
155 class C(HasTraits):
156 c = Int(30)
156 c = Int(30)
157
157
158 c = C()
158 c = C()
@@ -161,7 +161,7 b' class TestHasTraitletsMeta(TestCase):'
161 self.assertEquals(c.c,10)
161 self.assertEquals(c.c,10)
162
162
163 def test_this_class(self):
163 def test_this_class(self):
164 class A(HasTraitlets):
164 class A(HasTraits):
165 t = This()
165 t = This()
166 tt = This()
166 tt = This()
167 class B(A):
167 class B(A):
@@ -172,7 +172,7 b' class TestHasTraitletsMeta(TestCase):'
172 self.assertEquals(B.tt.this_class, B)
172 self.assertEquals(B.tt.this_class, B)
173 self.assertEquals(B.ttt.this_class, B)
173 self.assertEquals(B.ttt.this_class, B)
174
174
175 class TestHasTraitletsNotify(TestCase):
175 class TestHasTraitsNotify(TestCase):
176
176
177 def setUp(self):
177 def setUp(self):
178 self._notify1 = []
178 self._notify1 = []
@@ -186,12 +186,12 b' class TestHasTraitletsNotify(TestCase):'
186
186
187 def test_notify_all(self):
187 def test_notify_all(self):
188
188
189 class A(HasTraitlets):
189 class A(HasTraits):
190 a = Int
190 a = Int
191 b = Float
191 b = Float
192
192
193 a = A()
193 a = A()
194 a.on_traitlet_change(self.notify1)
194 a.on_trait_change(self.notify1)
195 a.a = 0
195 a.a = 0
196 self.assertEquals(len(self._notify1),0)
196 self.assertEquals(len(self._notify1),0)
197 a.b = 0.0
197 a.b = 0.0
@@ -200,31 +200,31 b' class TestHasTraitletsNotify(TestCase):'
200 self.assert_(('a',0,10) in self._notify1)
200 self.assert_(('a',0,10) in self._notify1)
201 a.b = 10.0
201 a.b = 10.0
202 self.assert_(('b',0.0,10.0) in self._notify1)
202 self.assert_(('b',0.0,10.0) in self._notify1)
203 self.assertRaises(TraitletError,setattr,a,'a','bad string')
203 self.assertRaises(TraitError,setattr,a,'a','bad string')
204 self.assertRaises(TraitletError,setattr,a,'b','bad string')
204 self.assertRaises(TraitError,setattr,a,'b','bad string')
205 self._notify1 = []
205 self._notify1 = []
206 a.on_traitlet_change(self.notify1,remove=True)
206 a.on_trait_change(self.notify1,remove=True)
207 a.a = 20
207 a.a = 20
208 a.b = 20.0
208 a.b = 20.0
209 self.assertEquals(len(self._notify1),0)
209 self.assertEquals(len(self._notify1),0)
210
210
211 def test_notify_one(self):
211 def test_notify_one(self):
212
212
213 class A(HasTraitlets):
213 class A(HasTraits):
214 a = Int
214 a = Int
215 b = Float
215 b = Float
216
216
217 a = A()
217 a = A()
218 a.on_traitlet_change(self.notify1, 'a')
218 a.on_trait_change(self.notify1, 'a')
219 a.a = 0
219 a.a = 0
220 self.assertEquals(len(self._notify1),0)
220 self.assertEquals(len(self._notify1),0)
221 a.a = 10
221 a.a = 10
222 self.assert_(('a',0,10) in self._notify1)
222 self.assert_(('a',0,10) in self._notify1)
223 self.assertRaises(TraitletError,setattr,a,'a','bad string')
223 self.assertRaises(TraitError,setattr,a,'a','bad string')
224
224
225 def test_subclass(self):
225 def test_subclass(self):
226
226
227 class A(HasTraitlets):
227 class A(HasTraits):
228 a = Int
228 a = Int
229
229
230 class B(A):
230 class B(A):
@@ -240,15 +240,15 b' class TestHasTraitletsNotify(TestCase):'
240
240
241 def test_notify_subclass(self):
241 def test_notify_subclass(self):
242
242
243 class A(HasTraitlets):
243 class A(HasTraits):
244 a = Int
244 a = Int
245
245
246 class B(A):
246 class B(A):
247 b = Float
247 b = Float
248
248
249 b = B()
249 b = B()
250 b.on_traitlet_change(self.notify1, 'a')
250 b.on_trait_change(self.notify1, 'a')
251 b.on_traitlet_change(self.notify2, 'b')
251 b.on_trait_change(self.notify2, 'b')
252 b.a = 0
252 b.a = 0
253 b.b = 0.0
253 b.b = 0.0
254 self.assertEquals(len(self._notify1),0)
254 self.assertEquals(len(self._notify1),0)
@@ -260,7 +260,7 b' class TestHasTraitletsNotify(TestCase):'
260
260
261 def test_static_notify(self):
261 def test_static_notify(self):
262
262
263 class A(HasTraitlets):
263 class A(HasTraits):
264 a = Int
264 a = Int
265 _notify1 = []
265 _notify1 = []
266 def _a_changed(self, name, old, new):
266 def _a_changed(self, name, old, new):
@@ -296,73 +296,73 b' class TestHasTraitletsNotify(TestCase):'
296 def callback3(name, old, new):
296 def callback3(name, old, new):
297 self.cb = (name, old, new)
297 self.cb = (name, old, new)
298
298
299 class A(HasTraitlets):
299 class A(HasTraits):
300 a = Int
300 a = Int
301
301
302 a = A()
302 a = A()
303 a.on_traitlet_change(callback0, 'a')
303 a.on_trait_change(callback0, 'a')
304 a.a = 10
304 a.a = 10
305 self.assertEquals(self.cb,())
305 self.assertEquals(self.cb,())
306 a.on_traitlet_change(callback0, 'a', remove=True)
306 a.on_trait_change(callback0, 'a', remove=True)
307
307
308 a.on_traitlet_change(callback1, 'a')
308 a.on_trait_change(callback1, 'a')
309 a.a = 100
309 a.a = 100
310 self.assertEquals(self.cb,('a',))
310 self.assertEquals(self.cb,('a',))
311 a.on_traitlet_change(callback1, 'a', remove=True)
311 a.on_trait_change(callback1, 'a', remove=True)
312
312
313 a.on_traitlet_change(callback2, 'a')
313 a.on_trait_change(callback2, 'a')
314 a.a = 1000
314 a.a = 1000
315 self.assertEquals(self.cb,('a',1000))
315 self.assertEquals(self.cb,('a',1000))
316 a.on_traitlet_change(callback2, 'a', remove=True)
316 a.on_trait_change(callback2, 'a', remove=True)
317
317
318 a.on_traitlet_change(callback3, 'a')
318 a.on_trait_change(callback3, 'a')
319 a.a = 10000
319 a.a = 10000
320 self.assertEquals(self.cb,('a',1000,10000))
320 self.assertEquals(self.cb,('a',1000,10000))
321 a.on_traitlet_change(callback3, 'a', remove=True)
321 a.on_trait_change(callback3, 'a', remove=True)
322
322
323 self.assertEquals(len(a._traitlet_notifiers['a']),0)
323 self.assertEquals(len(a._trait_notifiers['a']),0)
324
324
325
325
326 class TestHasTraitlets(TestCase):
326 class TestHasTraits(TestCase):
327
327
328 def test_traitlet_names(self):
328 def test_trait_names(self):
329 class A(HasTraitlets):
329 class A(HasTraits):
330 i = Int
330 i = Int
331 f = Float
331 f = Float
332 a = A()
332 a = A()
333 self.assertEquals(a.traitlet_names(),['i','f'])
333 self.assertEquals(a.trait_names(),['i','f'])
334
334
335 def test_traitlet_metadata(self):
335 def test_trait_metadata(self):
336 class A(HasTraitlets):
336 class A(HasTraits):
337 i = Int(config_key='MY_VALUE')
337 i = Int(config_key='MY_VALUE')
338 a = A()
338 a = A()
339 self.assertEquals(a.traitlet_metadata('i','config_key'), 'MY_VALUE')
339 self.assertEquals(a.trait_metadata('i','config_key'), 'MY_VALUE')
340
340
341 def test_traitlets(self):
341 def test_traits(self):
342 class A(HasTraitlets):
342 class A(HasTraits):
343 i = Int
343 i = Int
344 f = Float
344 f = Float
345 a = A()
345 a = A()
346 self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f))
346 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
347
347
348 def test_traitlets_metadata(self):
348 def test_traits_metadata(self):
349 class A(HasTraitlets):
349 class A(HasTraits):
350 i = Int(config_key='VALUE1', other_thing='VALUE2')
350 i = Int(config_key='VALUE1', other_thing='VALUE2')
351 f = Float(config_key='VALUE3', other_thing='VALUE2')
351 f = Float(config_key='VALUE3', other_thing='VALUE2')
352 j = Int(0)
352 j = Int(0)
353 a = A()
353 a = A()
354 self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f, j=A.j))
354 self.assertEquals(a.traits(), dict(i=A.i, f=A.f, j=A.j))
355 traitlets = a.traitlets(config_key='VALUE1', other_thing='VALUE2')
355 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
356 self.assertEquals(traitlets, dict(i=A.i))
356 self.assertEquals(traits, dict(i=A.i))
357
357
358 # This passes, but it shouldn't because I am replicating a bug in
358 # This passes, but it shouldn't because I am replicating a bug in
359 # traits.
359 # traits.
360 traitlets = a.traitlets(config_key=lambda v: True)
360 traits = a.traits(config_key=lambda v: True)
361 self.assertEquals(traitlets, dict(i=A.i, f=A.f, j=A.j))
361 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
362
362
363
363
364 #-----------------------------------------------------------------------------
364 #-----------------------------------------------------------------------------
365 # Tests for specific traitlet types
365 # Tests for specific trait types
366 #-----------------------------------------------------------------------------
366 #-----------------------------------------------------------------------------
367
367
368
368
@@ -371,7 +371,7 b' class TestType(TestCase):'
371 def test_default(self):
371 def test_default(self):
372
372
373 class B(object): pass
373 class B(object): pass
374 class A(HasTraitlets):
374 class A(HasTraits):
375 klass = Type
375 klass = Type
376
376
377 a = A()
377 a = A()
@@ -379,42 +379,42 b' class TestType(TestCase):'
379
379
380 a.klass = B
380 a.klass = B
381 self.assertEquals(a.klass, B)
381 self.assertEquals(a.klass, B)
382 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
382 self.assertRaises(TraitError, setattr, a, 'klass', 10)
383
383
384 def test_value(self):
384 def test_value(self):
385
385
386 class B(object): pass
386 class B(object): pass
387 class C(object): pass
387 class C(object): pass
388 class A(HasTraitlets):
388 class A(HasTraits):
389 klass = Type(B)
389 klass = Type(B)
390
390
391 a = A()
391 a = A()
392 self.assertEquals(a.klass, B)
392 self.assertEquals(a.klass, B)
393 self.assertRaises(TraitletError, setattr, a, 'klass', C)
393 self.assertRaises(TraitError, setattr, a, 'klass', C)
394 self.assertRaises(TraitletError, setattr, a, 'klass', object)
394 self.assertRaises(TraitError, setattr, a, 'klass', object)
395 a.klass = B
395 a.klass = B
396
396
397 def test_allow_none(self):
397 def test_allow_none(self):
398
398
399 class B(object): pass
399 class B(object): pass
400 class C(B): pass
400 class C(B): pass
401 class A(HasTraitlets):
401 class A(HasTraits):
402 klass = Type(B, allow_none=False)
402 klass = Type(B, allow_none=False)
403
403
404 a = A()
404 a = A()
405 self.assertEquals(a.klass, B)
405 self.assertEquals(a.klass, B)
406 self.assertRaises(TraitletError, setattr, a, 'klass', None)
406 self.assertRaises(TraitError, setattr, a, 'klass', None)
407 a.klass = C
407 a.klass = C
408 self.assertEquals(a.klass, C)
408 self.assertEquals(a.klass, C)
409
409
410 def test_validate_klass(self):
410 def test_validate_klass(self):
411
411
412 class A(HasTraitlets):
412 class A(HasTraits):
413 klass = Type('no strings allowed')
413 klass = Type('no strings allowed')
414
414
415 self.assertRaises(ImportError, A)
415 self.assertRaises(ImportError, A)
416
416
417 class A(HasTraitlets):
417 class A(HasTraits):
418 klass = Type('rub.adub.Duck')
418 klass = Type('rub.adub.Duck')
419
419
420 self.assertRaises(ImportError, A)
420 self.assertRaises(ImportError, A)
@@ -422,19 +422,19 b' class TestType(TestCase):'
422 def test_validate_default(self):
422 def test_validate_default(self):
423
423
424 class B(object): pass
424 class B(object): pass
425 class A(HasTraitlets):
425 class A(HasTraits):
426 klass = Type('bad default', B)
426 klass = Type('bad default', B)
427
427
428 self.assertRaises(ImportError, A)
428 self.assertRaises(ImportError, A)
429
429
430 class C(HasTraitlets):
430 class C(HasTraits):
431 klass = Type(None, B, allow_none=False)
431 klass = Type(None, B, allow_none=False)
432
432
433 self.assertRaises(TraitletError, C)
433 self.assertRaises(TraitError, C)
434
434
435 def test_str_klass(self):
435 def test_str_klass(self):
436
436
437 class A(HasTraitlets):
437 class A(HasTraits):
438 klass = Type('IPython.utils.ipstruct.Struct')
438 klass = Type('IPython.utils.ipstruct.Struct')
439
439
440 from IPython.utils.ipstruct import Struct
440 from IPython.utils.ipstruct import Struct
@@ -442,7 +442,7 b' class TestType(TestCase):'
442 a.klass = Struct
442 a.klass = Struct
443 self.assertEquals(a.klass, Struct)
443 self.assertEquals(a.klass, Struct)
444
444
445 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
445 self.assertRaises(TraitError, setattr, a, 'klass', 10)
446
446
447 class TestInstance(TestCase):
447 class TestInstance(TestCase):
448
448
@@ -451,7 +451,7 b' class TestInstance(TestCase):'
451 class Bar(Foo): pass
451 class Bar(Foo): pass
452 class Bah(object): pass
452 class Bah(object): pass
453
453
454 class A(HasTraitlets):
454 class A(HasTraits):
455 inst = Instance(Foo)
455 inst = Instance(Foo)
456
456
457 a = A()
457 a = A()
@@ -460,13 +460,13 b' class TestInstance(TestCase):'
460 self.assert_(isinstance(a.inst, Foo))
460 self.assert_(isinstance(a.inst, Foo))
461 a.inst = Bar()
461 a.inst = Bar()
462 self.assert_(isinstance(a.inst, Foo))
462 self.assert_(isinstance(a.inst, Foo))
463 self.assertRaises(TraitletError, setattr, a, 'inst', Foo)
463 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
464 self.assertRaises(TraitletError, setattr, a, 'inst', Bar)
464 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
465 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
465 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
466
466
467 def test_unique_default_value(self):
467 def test_unique_default_value(self):
468 class Foo(object): pass
468 class Foo(object): pass
469 class A(HasTraitlets):
469 class A(HasTraits):
470 inst = Instance(Foo,(),{})
470 inst = Instance(Foo,(),{})
471
471
472 a = A()
472 a = A()
@@ -481,18 +481,18 b' class TestInstance(TestCase):'
481 def __init__(self, c, d):
481 def __init__(self, c, d):
482 self.c = c; self.d = d
482 self.c = c; self.d = d
483
483
484 class A(HasTraitlets):
484 class A(HasTraits):
485 inst = Instance(Foo, (10,))
485 inst = Instance(Foo, (10,))
486 a = A()
486 a = A()
487 self.assertEquals(a.inst.c, 10)
487 self.assertEquals(a.inst.c, 10)
488
488
489 class B(HasTraitlets):
489 class B(HasTraits):
490 inst = Instance(Bah, args=(10,), kw=dict(d=20))
490 inst = Instance(Bah, args=(10,), kw=dict(d=20))
491 b = B()
491 b = B()
492 self.assertEquals(b.inst.c, 10)
492 self.assertEquals(b.inst.c, 10)
493 self.assertEquals(b.inst.d, 20)
493 self.assertEquals(b.inst.d, 20)
494
494
495 class C(HasTraitlets):
495 class C(HasTraits):
496 inst = Instance(Foo)
496 inst = Instance(Foo)
497 c = C()
497 c = C()
498 self.assert_(c.inst is None)
498 self.assert_(c.inst is None)
@@ -500,25 +500,25 b' class TestInstance(TestCase):'
500 def test_bad_default(self):
500 def test_bad_default(self):
501 class Foo(object): pass
501 class Foo(object): pass
502
502
503 class A(HasTraitlets):
503 class A(HasTraits):
504 inst = Instance(Foo, allow_none=False)
504 inst = Instance(Foo, allow_none=False)
505
505
506 self.assertRaises(TraitletError, A)
506 self.assertRaises(TraitError, A)
507
507
508 def test_instance(self):
508 def test_instance(self):
509 class Foo(object): pass
509 class Foo(object): pass
510
510
511 def inner():
511 def inner():
512 class A(HasTraitlets):
512 class A(HasTraits):
513 inst = Instance(Foo())
513 inst = Instance(Foo())
514
514
515 self.assertRaises(TraitletError, inner)
515 self.assertRaises(TraitError, inner)
516
516
517
517
518 class TestThis(TestCase):
518 class TestThis(TestCase):
519
519
520 def test_this_class(self):
520 def test_this_class(self):
521 class Foo(HasTraitlets):
521 class Foo(HasTraits):
522 this = This
522 this = This
523
523
524 f = Foo()
524 f = Foo()
@@ -526,10 +526,10 b' class TestThis(TestCase):'
526 g = Foo()
526 g = Foo()
527 f.this = g
527 f.this = g
528 self.assertEquals(f.this, g)
528 self.assertEquals(f.this, g)
529 self.assertRaises(TraitletError, setattr, f, 'this', 10)
529 self.assertRaises(TraitError, setattr, f, 'this', 10)
530
530
531 def test_this_inst(self):
531 def test_this_inst(self):
532 class Foo(HasTraitlets):
532 class Foo(HasTraits):
533 this = This()
533 this = This()
534
534
535 f = Foo()
535 f = Foo()
@@ -537,7 +537,7 b' class TestThis(TestCase):'
537 self.assert_(isinstance(f.this, Foo))
537 self.assert_(isinstance(f.this, Foo))
538
538
539 def test_subclass(self):
539 def test_subclass(self):
540 class Foo(HasTraitlets):
540 class Foo(HasTraits):
541 t = This()
541 t = This()
542 class Bar(Foo):
542 class Bar(Foo):
543 pass
543 pass
@@ -549,7 +549,7 b' class TestThis(TestCase):'
549 self.assertEquals(b.t, f)
549 self.assertEquals(b.t, f)
550
550
551 def test_subclass_override(self):
551 def test_subclass_override(self):
552 class Foo(HasTraitlets):
552 class Foo(HasTraits):
553 t = This()
553 t = This()
554 class Bar(Foo):
554 class Bar(Foo):
555 t = This()
555 t = This()
@@ -557,10 +557,10 b' class TestThis(TestCase):'
557 b = Bar()
557 b = Bar()
558 f.t = b
558 f.t = b
559 self.assertEquals(f.t, b)
559 self.assertEquals(f.t, b)
560 self.assertRaises(TraitletError, setattr, b, 't', f)
560 self.assertRaises(TraitError, setattr, b, 't', f)
561
561
562 class TraitletTestBase(TestCase):
562 class TraitTestBase(TestCase):
563 """A best testing class for basic traitlet types."""
563 """A best testing class for basic trait types."""
564
564
565 def assign(self, value):
565 def assign(self, value):
566 self.obj.value = value
566 self.obj.value = value
@@ -577,33 +577,33 b' class TraitletTestBase(TestCase):'
577 def test_bad_values(self):
577 def test_bad_values(self):
578 if hasattr(self, '_bad_values'):
578 if hasattr(self, '_bad_values'):
579 for value in self._bad_values:
579 for value in self._bad_values:
580 self.assertRaises(TraitletError, self.assign, value)
580 self.assertRaises(TraitError, self.assign, value)
581
581
582 def test_default_value(self):
582 def test_default_value(self):
583 if hasattr(self, '_default_value'):
583 if hasattr(self, '_default_value'):
584 self.assertEquals(self._default_value, self.obj.value)
584 self.assertEquals(self._default_value, self.obj.value)
585
585
586
586
587 class AnyTraitlet(HasTraitlets):
587 class AnyTrait(HasTraits):
588
588
589 value = Any
589 value = Any
590
590
591 class AnyTraitTest(TraitletTestBase):
591 class AnyTraitTest(TraitTestBase):
592
592
593 obj = AnyTraitlet()
593 obj = AnyTrait()
594
594
595 _default_value = None
595 _default_value = None
596 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
596 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
597 _bad_values = []
597 _bad_values = []
598
598
599
599
600 class IntTraitlet(HasTraitlets):
600 class IntTrait(HasTraits):
601
601
602 value = Int(99)
602 value = Int(99)
603
603
604 class TestInt(TraitletTestBase):
604 class TestInt(TraitTestBase):
605
605
606 obj = IntTraitlet()
606 obj = IntTrait()
607 _default_value = 99
607 _default_value = 99
608 _good_values = [10, -10]
608 _good_values = [10, -10]
609 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
609 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
@@ -611,13 +611,13 b' class TestInt(TraitletTestBase):'
611 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
611 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
612
612
613
613
614 class LongTraitlet(HasTraitlets):
614 class LongTrait(HasTraits):
615
615
616 value = Long(99L)
616 value = Long(99L)
617
617
618 class TestLong(TraitletTestBase):
618 class TestLong(TraitTestBase):
619
619
620 obj = LongTraitlet()
620 obj = LongTrait()
621
621
622 _default_value = 99L
622 _default_value = 99L
623 _good_values = [10, -10, 10L, -10L]
623 _good_values = [10, -10, 10L, -10L]
@@ -627,13 +627,13 b' class TestLong(TraitletTestBase):'
627 u'-10.1']
627 u'-10.1']
628
628
629
629
630 class FloatTraitlet(HasTraitlets):
630 class FloatTrait(HasTraits):
631
631
632 value = Float(99.0)
632 value = Float(99.0)
633
633
634 class TestFloat(TraitletTestBase):
634 class TestFloat(TraitTestBase):
635
635
636 obj = FloatTraitlet()
636 obj = FloatTrait()
637
637
638 _default_value = 99.0
638 _default_value = 99.0
639 _good_values = [10, -10, 10.1, -10.1]
639 _good_values = [10, -10, 10.1, -10.1]
@@ -642,13 +642,13 b' class TestFloat(TraitletTestBase):'
642 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
642 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
643
643
644
644
645 class ComplexTraitlet(HasTraitlets):
645 class ComplexTrait(HasTraits):
646
646
647 value = Complex(99.0-99.0j)
647 value = Complex(99.0-99.0j)
648
648
649 class TestComplex(TraitletTestBase):
649 class TestComplex(TraitTestBase):
650
650
651 obj = ComplexTraitlet()
651 obj = ComplexTrait()
652
652
653 _default_value = 99.0-99.0j
653 _default_value = 99.0-99.0j
654 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
654 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
@@ -656,13 +656,13 b' class TestComplex(TraitletTestBase):'
656 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
656 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
657
657
658
658
659 class StringTraitlet(HasTraitlets):
659 class StringTrait(HasTraits):
660
660
661 value = Str('string')
661 value = Str('string')
662
662
663 class TestString(TraitletTestBase):
663 class TestString(TraitTestBase):
664
664
665 obj = StringTraitlet()
665 obj = StringTrait()
666
666
667 _default_value = 'string'
667 _default_value = 'string'
668 _good_values = ['10', '-10', '10L',
668 _good_values = ['10', '-10', '10L',
@@ -671,13 +671,13 b' class TestString(TraitletTestBase):'
671 ['ten'],{'ten': 10},(10,), None, u'string']
671 ['ten'],{'ten': 10},(10,), None, u'string']
672
672
673
673
674 class UnicodeTraitlet(HasTraitlets):
674 class UnicodeTrait(HasTraits):
675
675
676 value = Unicode(u'unicode')
676 value = Unicode(u'unicode')
677
677
678 class TestUnicode(TraitletTestBase):
678 class TestUnicode(TraitTestBase):
679
679
680 obj = UnicodeTraitlet()
680 obj = UnicodeTrait()
681
681
682 _default_value = u'unicode'
682 _default_value = u'unicode'
683 _good_values = ['10', '-10', '10L', '-10L', '10.1',
683 _good_values = ['10', '-10', '10L', '-10L', '10.1',
@@ -17,7 +17,7 b" We don't support:"
17 * Delegation
17 * Delegation
18 * Automatic GUI generation
18 * Automatic GUI generation
19 * A full set of trait types. Most importantly, we don't provide container
19 * A full set of trait types. Most importantly, we don't provide container
20 traitlets (list, dict, tuple) that can trigger notifications if their
20 traits (list, dict, tuple) that can trigger notifications if their
21 contents change.
21 contents change.
22 * API compatibility with enthought.traits
22 * API compatibility with enthought.traits
23
23
@@ -57,7 +57,18 b' from types import ('
57 ListType, TupleType
57 ListType, TupleType
58 )
58 )
59
59
60 from IPython.utils.importstring import import_item
60 def import_item(name):
61 """Import and return bar given the string foo.bar."""
62 package = '.'.join(name.split('.')[0:-1])
63 obj = name.split('.')[-1]
64 execString = 'from %s import %s' % (package, obj)
65 try:
66 exec execString
67 except SyntaxError:
68 raise ImportError("Invalid class specification: %s" % name)
69 exec 'temp = %s' % obj
70 return temp
71
61
72
62 ClassTypes = (ClassType, type)
73 ClassTypes = (ClassType, type)
63
74
@@ -75,11 +86,9 b' NoDefaultSpecified = NoDefaultSpecified()'
75 class Undefined ( object ): pass
86 class Undefined ( object ): pass
76 Undefined = Undefined()
87 Undefined = Undefined()
77
88
78
89 class TraitError(Exception):
79 class TraitletError(Exception):
80 pass
90 pass
81
91
82
83 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
84 # Utilities
93 # Utilities
85 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
@@ -129,12 +138,12 b' def parse_notifier_name(name):'
129 >>> parse_notifier_name(['a','b'])
138 >>> parse_notifier_name(['a','b'])
130 ['a', 'b']
139 ['a', 'b']
131 >>> parse_notifier_name(None)
140 >>> parse_notifier_name(None)
132 ['anytraitlet']
141 ['anytrait']
133 """
142 """
134 if isinstance(name, str):
143 if isinstance(name, str):
135 return [name]
144 return [name]
136 elif name is None:
145 elif name is None:
137 return ['anytraitlet']
146 return ['anytrait']
138 elif isinstance(name, (list, tuple)):
147 elif isinstance(name, (list, tuple)):
139 for n in name:
148 for n in name:
140 assert isinstance(n, str), "names must be strings"
149 assert isinstance(n, str), "names must be strings"
@@ -172,25 +181,25 b' def getmembers(object, predicate=None):'
172
181
173
182
174 #-----------------------------------------------------------------------------
183 #-----------------------------------------------------------------------------
175 # Base TraitletType for all traitlets
184 # Base TraitType for all traits
176 #-----------------------------------------------------------------------------
185 #-----------------------------------------------------------------------------
177
186
178
187
179 class TraitletType(object):
188 class TraitType(object):
180 """A base class for all traitlet descriptors.
189 """A base class for all trait descriptors.
181
190
182 Notes
191 Notes
183 -----
192 -----
184 Our implementation of traitlets is based on Python's descriptor
193 Our implementation of traits is based on Python's descriptor
185 prototol. This class is the base class for all such descriptors. The
194 prototol. This class is the base class for all such descriptors. The
186 only magic we use is a custom metaclass for the main :class:`HasTraitlets`
195 only magic we use is a custom metaclass for the main :class:`HasTraits`
187 class that does the following:
196 class that does the following:
188
197
189 1. Sets the :attr:`name` attribute of every :class:`TraitletType`
198 1. Sets the :attr:`name` attribute of every :class:`TraitType`
190 instance in the class dict to the name of the attribute.
199 instance in the class dict to the name of the attribute.
191 2. Sets the :attr:`this_class` attribute of every :class:`TraitletType`
200 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
192 instance in the class dict to the *class* that declared the traitlet.
201 instance in the class dict to the *class* that declared the trait.
193 This is used by the :class:`This` traitlet to allow subclasses to
202 This is used by the :class:`This` trait to allow subclasses to
194 accept superclasses for :class:`This` values.
203 accept superclasses for :class:`This` values.
195 """
204 """
196
205
@@ -200,7 +209,7 b' class TraitletType(object):'
200 info_text = 'any value'
209 info_text = 'any value'
201
210
202 def __init__(self, default_value=NoDefaultSpecified, **metadata):
211 def __init__(self, default_value=NoDefaultSpecified, **metadata):
203 """Create a TraitletType.
212 """Create a TraitType.
204 """
213 """
205 if default_value is not NoDefaultSpecified:
214 if default_value is not NoDefaultSpecified:
206 self.default_value = default_value
215 self.default_value = default_value
@@ -225,11 +234,11 b' class TraitletType(object):'
225 return dv
234 return dv
226
235
227 def instance_init(self, obj):
236 def instance_init(self, obj):
228 """This is called by :meth:`HasTraitlets.__new__` to finish init'ing.
237 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
229
238
230 Some stages of initialization must be delayed until the parent
239 Some stages of initialization must be delayed until the parent
231 :class:`HasTraitlets` instance has been created. This method is
240 :class:`HasTraits` instance has been created. This method is
232 called in :meth:`HasTraitlets.__new__` after the instance has been
241 called in :meth:`HasTraits.__new__` after the instance has been
233 created.
242 created.
234
243
235 This method trigger the creation and validation of default values
244 This method trigger the creation and validation of default values
@@ -238,8 +247,8 b' class TraitletType(object):'
238
247
239 Parameters
248 Parameters
240 ----------
249 ----------
241 obj : :class:`HasTraitlets` instance
250 obj : :class:`HasTraits` instance
242 The parent :class:`HasTraitlets` instance that has just been
251 The parent :class:`HasTraits` instance that has just been
243 created.
252 created.
244 """
253 """
245 self.set_default_value(obj)
254 self.set_default_value(obj)
@@ -249,30 +258,30 b' class TraitletType(object):'
249
258
250 This method is called by :meth:`instance_init` to create and
259 This method is called by :meth:`instance_init` to create and
251 validate the default value. The creation and validation of
260 validate the default value. The creation and validation of
252 default values must be delayed until the parent :class:`HasTraitlets`
261 default values must be delayed until the parent :class:`HasTraits`
253 class has been instantiated.
262 class has been instantiated.
254 """
263 """
255 dv = self.get_default_value()
264 dv = self.get_default_value()
256 newdv = self._validate(obj, dv)
265 newdv = self._validate(obj, dv)
257 obj._traitlet_values[self.name] = newdv
266 obj._trait_values[self.name] = newdv
258
267
259 def __get__(self, obj, cls=None):
268 def __get__(self, obj, cls=None):
260 """Get the value of the traitlet by self.name for the instance.
269 """Get the value of the trait by self.name for the instance.
261
270
262 Default values are instantiated when :meth:`HasTraitlets.__new__`
271 Default values are instantiated when :meth:`HasTraits.__new__`
263 is called. Thus by the time this method gets called either the
272 is called. Thus by the time this method gets called either the
264 default value or a user defined value (they called :meth:`__set__`)
273 default value or a user defined value (they called :meth:`__set__`)
265 is in the :class:`HasTraitlets` instance.
274 is in the :class:`HasTraits` instance.
266 """
275 """
267 if obj is None:
276 if obj is None:
268 return self
277 return self
269 else:
278 else:
270 try:
279 try:
271 value = obj._traitlet_values[self.name]
280 value = obj._trait_values[self.name]
272 except:
281 except:
273 # HasTraitlets should call set_default_value to populate
282 # HasTraits should call set_default_value to populate
274 # this. So this should never be reached.
283 # this. So this should never be reached.
275 raise TraitletError('Unexpected error in TraitletType: '
284 raise TraitError('Unexpected error in TraitType: '
276 'default value not set properly')
285 'default value not set properly')
277 else:
286 else:
278 return value
287 return value
@@ -281,8 +290,8 b' class TraitletType(object):'
281 new_value = self._validate(obj, value)
290 new_value = self._validate(obj, value)
282 old_value = self.__get__(obj)
291 old_value = self.__get__(obj)
283 if old_value != new_value:
292 if old_value != new_value:
284 obj._traitlet_values[self.name] = new_value
293 obj._trait_values[self.name] = new_value
285 obj._notify_traitlet(self.name, old_value, new_value)
294 obj._notify_trait(self.name, old_value, new_value)
286
295
287 def _validate(self, obj, value):
296 def _validate(self, obj, value):
288 if hasattr(self, 'validate'):
297 if hasattr(self, 'validate'):
@@ -292,7 +301,7 b' class TraitletType(object):'
292 if valid:
301 if valid:
293 return value
302 return value
294 else:
303 else:
295 raise TraitletError('invalid value for type: %r' % value)
304 raise TraitError('invalid value for type: %r' % value)
296 elif hasattr(self, 'value_for'):
305 elif hasattr(self, 'value_for'):
297 return self.value_for(value)
306 return self.value_for(value)
298 else:
307 else:
@@ -303,13 +312,13 b' class TraitletType(object):'
303
312
304 def error(self, obj, value):
313 def error(self, obj, value):
305 if obj is not None:
314 if obj is not None:
306 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
315 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
307 % (self.name, class_of(obj),
316 % (self.name, class_of(obj),
308 self.info(), repr_type(value))
317 self.info(), repr_type(value))
309 else:
318 else:
310 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
319 e = "The '%s' trait must be %s, but a value of %r was specified." \
311 % (self.name, self.info(), repr_type(value))
320 % (self.name, self.info(), repr_type(value))
312 raise TraitletError(e)
321 raise TraitError(e)
313
322
314 def get_metadata(self, key):
323 def get_metadata(self, key):
315 return getattr(self, '_metadata', {}).get(key, None)
324 return getattr(self, '_metadata', {}).get(key, None)
@@ -319,62 +328,62 b' class TraitletType(object):'
319
328
320
329
321 #-----------------------------------------------------------------------------
330 #-----------------------------------------------------------------------------
322 # The HasTraitlets implementation
331 # The HasTraits implementation
323 #-----------------------------------------------------------------------------
332 #-----------------------------------------------------------------------------
324
333
325
334
326 class MetaHasTraitlets(type):
335 class MetaHasTraits(type):
327 """A metaclass for HasTraitlets.
336 """A metaclass for HasTraits.
328
337
329 This metaclass makes sure that any TraitletType class attributes are
338 This metaclass makes sure that any TraitType class attributes are
330 instantiated and sets their name attribute.
339 instantiated and sets their name attribute.
331 """
340 """
332
341
333 def __new__(mcls, name, bases, classdict):
342 def __new__(mcls, name, bases, classdict):
334 """Create the HasTraitlets class.
343 """Create the HasTraits class.
335
344
336 This instantiates all TraitletTypes in the class dict and sets their
345 This instantiates all TraitTypes in the class dict and sets their
337 :attr:`name` attribute.
346 :attr:`name` attribute.
338 """
347 """
339 # print "MetaHasTraitlets (mcls, name): ", mcls, name
348 # print "MetaHasTraitlets (mcls, name): ", mcls, name
340 # print "MetaHasTraitlets (bases): ", bases
349 # print "MetaHasTraitlets (bases): ", bases
341 # print "MetaHasTraitlets (classdict): ", classdict
350 # print "MetaHasTraitlets (classdict): ", classdict
342 for k,v in classdict.iteritems():
351 for k,v in classdict.iteritems():
343 if isinstance(v, TraitletType):
352 if isinstance(v, TraitType):
344 v.name = k
353 v.name = k
345 elif inspect.isclass(v):
354 elif inspect.isclass(v):
346 if issubclass(v, TraitletType):
355 if issubclass(v, TraitType):
347 vinst = v()
356 vinst = v()
348 vinst.name = k
357 vinst.name = k
349 classdict[k] = vinst
358 classdict[k] = vinst
350 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
359 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
351
360
352 def __init__(cls, name, bases, classdict):
361 def __init__(cls, name, bases, classdict):
353 """Finish initializing the HasTraitlets class.
362 """Finish initializing the HasTraits class.
354
363
355 This sets the :attr:`this_class` attribute of each TraitletType in the
364 This sets the :attr:`this_class` attribute of each TraitType in the
356 class dict to the newly created class ``cls``.
365 class dict to the newly created class ``cls``.
357 """
366 """
358 for k, v in classdict.iteritems():
367 for k, v in classdict.iteritems():
359 if isinstance(v, TraitletType):
368 if isinstance(v, TraitType):
360 v.this_class = cls
369 v.this_class = cls
361 super(MetaHasTraitlets, cls).__init__(name, bases, classdict)
370 super(MetaHasTraits, cls).__init__(name, bases, classdict)
362
371
363 class HasTraitlets(object):
372 class HasTraits(object):
364
373
365 __metaclass__ = MetaHasTraitlets
374 __metaclass__ = MetaHasTraits
366
375
367 def __new__(cls, *args, **kw):
376 def __new__(cls, *args, **kw):
368 # This is needed because in Python 2.6 object.__new__ only accepts
377 # This is needed because in Python 2.6 object.__new__ only accepts
369 # the cls argument.
378 # the cls argument.
370 new_meth = super(HasTraitlets, cls).__new__
379 new_meth = super(HasTraits, cls).__new__
371 if new_meth is object.__new__:
380 if new_meth is object.__new__:
372 inst = new_meth(cls)
381 inst = new_meth(cls)
373 else:
382 else:
374 inst = new_meth(cls, *args, **kw)
383 inst = new_meth(cls, *args, **kw)
375 inst._traitlet_values = {}
384 inst._trait_values = {}
376 inst._traitlet_notifiers = {}
385 inst._trait_notifiers = {}
377 # Here we tell all the TraitletType instances to set their default
386 # Here we tell all the TraitType instances to set their default
378 # values on the instance.
387 # values on the instance.
379 for key in dir(cls):
388 for key in dir(cls):
380 # Some descriptors raise AttributeError like zope.interface's
389 # Some descriptors raise AttributeError like zope.interface's
@@ -385,19 +394,20 b' class HasTraitlets(object):'
385 except AttributeError:
394 except AttributeError:
386 pass
395 pass
387 else:
396 else:
388 if isinstance(value, TraitletType):
397 if isinstance(value, TraitType):
389 value.instance_init(inst)
398 value.instance_init(inst)
399
390 return inst
400 return inst
391
401
392 # def __init__(self):
402 # def __init__(self):
393 # self._traitlet_values = {}
403 # self._trait_values = {}
394 # self._traitlet_notifiers = {}
404 # self._trait_notifiers = {}
395
405
396 def _notify_traitlet(self, name, old_value, new_value):
406 def _notify_trait(self, name, old_value, new_value):
397
407
398 # First dynamic ones
408 # First dynamic ones
399 callables = self._traitlet_notifiers.get(name,[])
409 callables = self._trait_notifiers.get(name,[])
400 more_callables = self._traitlet_notifiers.get('anytraitlet',[])
410 more_callables = self._trait_notifiers.get('anytrait',[])
401 callables.extend(more_callables)
411 callables.extend(more_callables)
402
412
403 # Now static ones
413 # Now static ones
@@ -430,25 +440,25 b' class HasTraitlets(object):'
430 elif nargs + offset == 3:
440 elif nargs + offset == 3:
431 c(name, old_value, new_value)
441 c(name, old_value, new_value)
432 else:
442 else:
433 raise TraitletError('a traitlet changed callback '
443 raise TraitError('a trait changed callback '
434 'must have 0-3 arguments.')
444 'must have 0-3 arguments.')
435 else:
445 else:
436 raise TraitletError('a traitlet changed callback '
446 raise TraitError('a trait changed callback '
437 'must be callable.')
447 'must be callable.')
438
448
439
449
440 def _add_notifiers(self, handler, name):
450 def _add_notifiers(self, handler, name):
441 if not self._traitlet_notifiers.has_key(name):
451 if not self._trait_notifiers.has_key(name):
442 nlist = []
452 nlist = []
443 self._traitlet_notifiers[name] = nlist
453 self._trait_notifiers[name] = nlist
444 else:
454 else:
445 nlist = self._traitlet_notifiers[name]
455 nlist = self._trait_notifiers[name]
446 if handler not in nlist:
456 if handler not in nlist:
447 nlist.append(handler)
457 nlist.append(handler)
448
458
449 def _remove_notifiers(self, handler, name):
459 def _remove_notifiers(self, handler, name):
450 if self._traitlet_notifiers.has_key(name):
460 if self._trait_notifiers.has_key(name):
451 nlist = self._traitlet_notifiers[name]
461 nlist = self._trait_notifiers[name]
452 try:
462 try:
453 index = nlist.index(handler)
463 index = nlist.index(handler)
454 except ValueError:
464 except ValueError:
@@ -456,25 +466,25 b' class HasTraitlets(object):'
456 else:
466 else:
457 del nlist[index]
467 del nlist[index]
458
468
459 def on_traitlet_change(self, handler, name=None, remove=False):
469 def on_trait_change(self, handler, name=None, remove=False):
460 """Setup a handler to be called when a traitlet changes.
470 """Setup a handler to be called when a trait changes.
461
471
462 This is used to setup dynamic notifications of traitlet changes.
472 This is used to setup dynamic notifications of trait changes.
463
473
464 Static handlers can be created by creating methods on a HasTraitlets
474 Static handlers can be created by creating methods on a HasTraits
465 subclass with the naming convention '_[traitletname]_changed'. Thus,
475 subclass with the naming convention '_[traitname]_changed'. Thus,
466 to create static handler for the traitlet 'a', create the method
476 to create static handler for the trait 'a', create the method
467 _a_changed(self, name, old, new) (fewer arguments can be used, see
477 _a_changed(self, name, old, new) (fewer arguments can be used, see
468 below).
478 below).
469
479
470 Parameters
480 Parameters
471 ----------
481 ----------
472 handler : callable
482 handler : callable
473 A callable that is called when a traitlet changes. Its
483 A callable that is called when a trait changes. Its
474 signature can be handler(), handler(name), handler(name, new)
484 signature can be handler(), handler(name), handler(name, new)
475 or handler(name, old, new).
485 or handler(name, old, new).
476 name : list, str, None
486 name : list, str, None
477 If None, the handler will apply to all traitlets. If a list
487 If None, the handler will apply to all traits. If a list
478 of str, handler will apply to all names in the list. If a
488 of str, handler will apply to all names in the list. If a
479 str, the handler will apply just to that name.
489 str, the handler will apply just to that name.
480 remove : bool
490 remove : bool
@@ -490,62 +500,62 b' class HasTraitlets(object):'
490 for n in names:
500 for n in names:
491 self._add_notifiers(handler, n)
501 self._add_notifiers(handler, n)
492
502
493 def traitlet_names(self, **metadata):
503 def trait_names(self, **metadata):
494 """Get a list of all the names of this classes traitlets."""
504 """Get a list of all the names of this classes traits."""
495 return self.traitlets(**metadata).keys()
505 return self.traits(**metadata).keys()
496
506
497 def traitlets(self, **metadata):
507 def traits(self, **metadata):
498 """Get a list of all the traitlets of this class.
508 """Get a list of all the traits of this class.
499
509
500 The TraitletTypes returned don't know anything about the values
510 The TraitTypes returned don't know anything about the values
501 that the various HasTraitlet's instances are holding.
511 that the various HasTrait's instances are holding.
502
512
503 This follows the same algorithm as traits does and does not allow
513 This follows the same algorithm as traits does and does not allow
504 for any simple way of specifying merely that a metadata name
514 for any simple way of specifying merely that a metadata name
505 exists, but has any value. This is because get_metadata returns
515 exists, but has any value. This is because get_metadata returns
506 None if a metadata key doesn't exist.
516 None if a metadata key doesn't exist.
507 """
517 """
508 traitlets = dict([memb for memb in getmembers(self.__class__) if \
518 traits = dict([memb for memb in getmembers(self.__class__) if \
509 isinstance(memb[1], TraitletType)])
519 isinstance(memb[1], TraitType)])
510
520
511 if len(metadata) == 0:
521 if len(metadata) == 0:
512 return traitlets
522 return traits
513
523
514 for meta_name, meta_eval in metadata.items():
524 for meta_name, meta_eval in metadata.items():
515 if type(meta_eval) is not FunctionType:
525 if type(meta_eval) is not FunctionType:
516 metadata[meta_name] = _SimpleTest(meta_eval)
526 metadata[meta_name] = _SimpleTest(meta_eval)
517
527
518 result = {}
528 result = {}
519 for name, traitlet in traitlets.items():
529 for name, trait in traits.items():
520 for meta_name, meta_eval in metadata.items():
530 for meta_name, meta_eval in metadata.items():
521 if not meta_eval(traitlet.get_metadata(meta_name)):
531 if not meta_eval(trait.get_metadata(meta_name)):
522 break
532 break
523 else:
533 else:
524 result[name] = traitlet
534 result[name] = trait
525
535
526 return result
536 return result
527
537
528 def traitlet_metadata(self, traitletname, key):
538 def trait_metadata(self, traitname, key):
529 """Get metadata values for traitlet by key."""
539 """Get metadata values for trait by key."""
530 try:
540 try:
531 traitlet = getattr(self.__class__, traitletname)
541 trait = getattr(self.__class__, traitname)
532 except AttributeError:
542 except AttributeError:
533 raise TraitletError("Class %s does not have a traitlet named %s" %
543 raise TraitError("Class %s does not have a trait named %s" %
534 (self.__class__.__name__, traitletname))
544 (self.__class__.__name__, traitname))
535 else:
545 else:
536 return traitlet.get_metadata(key)
546 return trait.get_metadata(key)
537
547
538 #-----------------------------------------------------------------------------
548 #-----------------------------------------------------------------------------
539 # Actual TraitletTypes implementations/subclasses
549 # Actual TraitTypes implementations/subclasses
540 #-----------------------------------------------------------------------------
550 #-----------------------------------------------------------------------------
541
551
542 #-----------------------------------------------------------------------------
552 #-----------------------------------------------------------------------------
543 # TraitletTypes subclasses for handling classes and instances of classes
553 # TraitTypes subclasses for handling classes and instances of classes
544 #-----------------------------------------------------------------------------
554 #-----------------------------------------------------------------------------
545
555
546
556
547 class ClassBasedTraitletType(TraitletType):
557 class ClassBasedTraitType(TraitType):
548 """A traitlet with error reporting for Type, Instance and This."""
558 """A trait with error reporting for Type, Instance and This."""
549
559
550 def error(self, obj, value):
560 def error(self, obj, value):
551 kind = type(value)
561 kind = type(value)
@@ -554,16 +564,16 b' class ClassBasedTraitletType(TraitletType):'
554 else:
564 else:
555 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
565 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
556
566
557 super(ClassBasedTraitletType, self).error(obj, msg)
567 super(ClassBasedTraitType, self).error(obj, msg)
558
568
559
569
560 class Type(ClassBasedTraitletType):
570 class Type(ClassBasedTraitType):
561 """A traitlet whose value must be a subclass of a specified class."""
571 """A trait whose value must be a subclass of a specified class."""
562
572
563 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
573 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
564 """Construct a Type traitlet
574 """Construct a Type trait
565
575
566 A Type traitlet specifies that its values must be subclasses of
576 A Type trait specifies that its values must be subclasses of
567 a particular class.
577 a particular class.
568
578
569 If only ``default_value`` is given, it is used for the ``klass`` as
579 If only ``default_value`` is given, it is used for the ``klass`` as
@@ -575,12 +585,12 b' class Type(ClassBasedTraitletType):'
575 The default value must be a subclass of klass. If an str,
585 The default value must be a subclass of klass. If an str,
576 the str must be a fully specified class name, like 'foo.bar.Bah'.
586 the str must be a fully specified class name, like 'foo.bar.Bah'.
577 The string is resolved into real class, when the parent
587 The string is resolved into real class, when the parent
578 :class:`HasTraitlets` class is instantiated.
588 :class:`HasTraits` class is instantiated.
579 klass : class, str, None
589 klass : class, str, None
580 Values of this traitlet must be a subclass of klass. The klass
590 Values of this trait must be a subclass of klass. The klass
581 may be specified in a string like: 'foo.bar.MyClass'.
591 may be specified in a string like: 'foo.bar.MyClass'.
582 The string is resolved into real class, when the parent
592 The string is resolved into real class, when the parent
583 :class:`HasTraitlets` class is instantiated.
593 :class:`HasTraits` class is instantiated.
584 allow_none : boolean
594 allow_none : boolean
585 Indicates whether None is allowed as an assignable value. Even if
595 Indicates whether None is allowed as an assignable value. Even if
586 ``False``, the default value may be ``None``.
596 ``False``, the default value may be ``None``.
@@ -592,7 +602,7 b' class Type(ClassBasedTraitletType):'
592 klass = default_value
602 klass = default_value
593
603
594 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
604 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
595 raise TraitletError("A Type traitlet must specify a class.")
605 raise TraitError("A Type trait must specify a class.")
596
606
597 self.klass = klass
607 self.klass = klass
598 self._allow_none = allow_none
608 self._allow_none = allow_none
@@ -646,7 +656,7 b' class DefaultValueGenerator(object):'
646 return klass(*self.args, **self.kw)
656 return klass(*self.args, **self.kw)
647
657
648
658
649 class Instance(ClassBasedTraitletType):
659 class Instance(ClassBasedTraitType):
650 """A trait whose value must be an instance of a specified class.
660 """A trait whose value must be an instance of a specified class.
651
661
652 The value can also be an instance of a subclass of the specified class.
662 The value can also be an instance of a subclass of the specified class.
@@ -654,9 +664,9 b' class Instance(ClassBasedTraitletType):'
654
664
655 def __init__(self, klass=None, args=None, kw=None,
665 def __init__(self, klass=None, args=None, kw=None,
656 allow_none=True, **metadata ):
666 allow_none=True, **metadata ):
657 """Construct an Instance traitlet.
667 """Construct an Instance trait.
658
668
659 This traitlet allows values that are instances of a particular
669 This trait allows values that are instances of a particular
660 class or its sublclasses. Our implementation is quite different
670 class or its sublclasses. Our implementation is quite different
661 from that of enthough.traits as we don't allow instances to be used
671 from that of enthough.traits as we don't allow instances to be used
662 for klass and we handle the ``args`` and ``kw`` arguments differently.
672 for klass and we handle the ``args`` and ``kw`` arguments differently.
@@ -664,7 +674,7 b' class Instance(ClassBasedTraitletType):'
664 Parameters
674 Parameters
665 ----------
675 ----------
666 klass : class, str
676 klass : class, str
667 The class that forms the basis for the traitlet. Class names
677 The class that forms the basis for the trait. Class names
668 can also be specified as strings, like 'foo.bar.Bar'.
678 can also be specified as strings, like 'foo.bar.Bar'.
669 args : tuple
679 args : tuple
670 Positional arguments for generating the default value.
680 Positional arguments for generating the default value.
@@ -684,7 +694,7 b' class Instance(ClassBasedTraitletType):'
684 self._allow_none = allow_none
694 self._allow_none = allow_none
685
695
686 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
696 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
687 raise TraitletError('The klass argument must be a class'
697 raise TraitError('The klass argument must be a class'
688 ' you gave: %r' % klass)
698 ' you gave: %r' % klass)
689 self.klass = klass
699 self.klass = klass
690
700
@@ -700,9 +710,9 b' class Instance(ClassBasedTraitletType):'
700 kw = {}
710 kw = {}
701
711
702 if not isinstance(kw, dict):
712 if not isinstance(kw, dict):
703 raise TraitletError("The 'kw' argument must be a dict or None.")
713 raise TraitError("The 'kw' argument must be a dict or None.")
704 if not isinstance(args, tuple):
714 if not isinstance(args, tuple):
705 raise TraitletError("The 'args' argument must be a tuple or None.")
715 raise TraitError("The 'args' argument must be a tuple or None.")
706
716
707 default_value = DefaultValueGenerator(*args, **kw)
717 default_value = DefaultValueGenerator(*args, **kw)
708
718
@@ -741,9 +751,9 b' class Instance(ClassBasedTraitletType):'
741 def get_default_value(self):
751 def get_default_value(self):
742 """Instantiate a default value instance.
752 """Instantiate a default value instance.
743
753
744 This is called when the containing HasTraitlets classes'
754 This is called when the containing HasTraits classes'
745 :meth:`__new__` method is called to ensure that a unique instance
755 :meth:`__new__` method is called to ensure that a unique instance
746 is created for each HasTraitlets instance.
756 is created for each HasTraits instance.
747 """
757 """
748 dv = self.default_value
758 dv = self.default_value
749 if isinstance(dv, DefaultValueGenerator):
759 if isinstance(dv, DefaultValueGenerator):
@@ -752,11 +762,11 b' class Instance(ClassBasedTraitletType):'
752 return dv
762 return dv
753
763
754
764
755 class This(ClassBasedTraitletType):
765 class This(ClassBasedTraitType):
756 """A traitlet for instances of the class containing this trait.
766 """A trait for instances of the class containing this trait.
757
767
758 Because how how and when class bodies are executed, the ``This``
768 Because how how and when class bodies are executed, the ``This``
759 traitlet can only have a default value of None. This, and because we
769 trait can only have a default value of None. This, and because we
760 always validate default values, ``allow_none`` is *always* true.
770 always validate default values, ``allow_none`` is *always* true.
761 """
771 """
762
772
@@ -768,7 +778,7 b' class This(ClassBasedTraitletType):'
768 def validate(self, obj, value):
778 def validate(self, obj, value):
769 # What if value is a superclass of obj.__class__? This is
779 # What if value is a superclass of obj.__class__? This is
770 # complicated if it was the superclass that defined the This
780 # complicated if it was the superclass that defined the This
771 # traitlet.
781 # trait.
772 if isinstance(value, self.this_class) or (value is None):
782 if isinstance(value, self.this_class) or (value is None):
773 return value
783 return value
774 else:
784 else:
@@ -776,17 +786,17 b' class This(ClassBasedTraitletType):'
776
786
777
787
778 #-----------------------------------------------------------------------------
788 #-----------------------------------------------------------------------------
779 # Basic TraitletTypes implementations/subclasses
789 # Basic TraitTypes implementations/subclasses
780 #-----------------------------------------------------------------------------
790 #-----------------------------------------------------------------------------
781
791
782
792
783 class Any(TraitletType):
793 class Any(TraitType):
784 default_value = None
794 default_value = None
785 info_text = 'any value'
795 info_text = 'any value'
786
796
787
797
788 class Int(TraitletType):
798 class Int(TraitType):
789 """A integer traitlet."""
799 """A integer trait."""
790
800
791 evaluate = int
801 evaluate = int
792 default_value = 0
802 default_value = 0
@@ -798,7 +808,7 b' class Int(TraitletType):'
798 self.error(obj, value)
808 self.error(obj, value)
799
809
800 class CInt(Int):
810 class CInt(Int):
801 """A casting version of the int traitlet."""
811 """A casting version of the int trait."""
802
812
803 def validate(self, obj, value):
813 def validate(self, obj, value):
804 try:
814 try:
@@ -807,8 +817,8 b' class CInt(Int):'
807 self.error(obj, value)
817 self.error(obj, value)
808
818
809
819
810 class Long(TraitletType):
820 class Long(TraitType):
811 """A long integer traitlet."""
821 """A long integer trait."""
812
822
813 evaluate = long
823 evaluate = long
814 default_value = 0L
824 default_value = 0L
@@ -823,7 +833,7 b' class Long(TraitletType):'
823
833
824
834
825 class CLong(Long):
835 class CLong(Long):
826 """A casting version of the long integer traitlet."""
836 """A casting version of the long integer trait."""
827
837
828 def validate(self, obj, value):
838 def validate(self, obj, value):
829 try:
839 try:
@@ -832,8 +842,8 b' class CLong(Long):'
832 self.error(obj, value)
842 self.error(obj, value)
833
843
834
844
835 class Float(TraitletType):
845 class Float(TraitType):
836 """A float traitlet."""
846 """A float trait."""
837
847
838 evaluate = float
848 evaluate = float
839 default_value = 0.0
849 default_value = 0.0
@@ -848,7 +858,7 b' class Float(TraitletType):'
848
858
849
859
850 class CFloat(Float):
860 class CFloat(Float):
851 """A casting version of the float traitlet."""
861 """A casting version of the float trait."""
852
862
853 def validate(self, obj, value):
863 def validate(self, obj, value):
854 try:
864 try:
@@ -856,8 +866,8 b' class CFloat(Float):'
856 except:
866 except:
857 self.error(obj, value)
867 self.error(obj, value)
858
868
859 class Complex(TraitletType):
869 class Complex(TraitType):
860 """A traitlet for complex numbers."""
870 """A trait for complex numbers."""
861
871
862 evaluate = complex
872 evaluate = complex
863 default_value = 0.0 + 0.0j
873 default_value = 0.0 + 0.0j
@@ -872,7 +882,7 b' class Complex(TraitletType):'
872
882
873
883
874 class CComplex(Complex):
884 class CComplex(Complex):
875 """A casting version of the complex number traitlet."""
885 """A casting version of the complex number trait."""
876
886
877 def validate (self, obj, value):
887 def validate (self, obj, value):
878 try:
888 try:
@@ -881,8 +891,8 b' class CComplex(Complex):'
881 self.error(obj, value)
891 self.error(obj, value)
882
892
883
893
884 class Str(TraitletType):
894 class Str(TraitType):
885 """A traitlet for strings."""
895 """A trait for strings."""
886
896
887 evaluate = lambda x: x
897 evaluate = lambda x: x
888 default_value = ''
898 default_value = ''
@@ -895,7 +905,7 b' class Str(TraitletType):'
895
905
896
906
897 class CStr(Str):
907 class CStr(Str):
898 """A casting version of the string traitlet."""
908 """A casting version of the string trait."""
899
909
900 def validate(self, obj, value):
910 def validate(self, obj, value):
901 try:
911 try:
@@ -907,8 +917,8 b' class CStr(Str):'
907 self.error(obj, value)
917 self.error(obj, value)
908
918
909
919
910 class Unicode(TraitletType):
920 class Unicode(TraitType):
911 """A traitlet for unicode strings."""
921 """A trait for unicode strings."""
912
922
913 evaluate = unicode
923 evaluate = unicode
914 default_value = u''
924 default_value = u''
@@ -923,7 +933,7 b' class Unicode(TraitletType):'
923
933
924
934
925 class CUnicode(Unicode):
935 class CUnicode(Unicode):
926 """A casting version of the unicode traitlet."""
936 """A casting version of the unicode trait."""
927
937
928 def validate(self, obj, value):
938 def validate(self, obj, value):
929 try:
939 try:
@@ -932,8 +942,8 b' class CUnicode(Unicode):'
932 self.error(obj, value)
942 self.error(obj, value)
933
943
934
944
935 class Bool(TraitletType):
945 class Bool(TraitType):
936 """A boolean (True, False) traitlet."""
946 """A boolean (True, False) trait."""
937 evaluate = bool
947 evaluate = bool
938 default_value = False
948 default_value = False
939 info_text = 'a boolean'
949 info_text = 'a boolean'
@@ -945,7 +955,7 b' class Bool(TraitletType):'
945
955
946
956
947 class CBool(Bool):
957 class CBool(Bool):
948 """A casting version of the boolean traitlet."""
958 """A casting version of the boolean trait."""
949
959
950 def validate(self, obj, value):
960 def validate(self, obj, value):
951 try:
961 try:
@@ -954,7 +964,7 b' class CBool(Bool):'
954 self.error(obj, value)
964 self.error(obj, value)
955
965
956
966
957 class Enum(TraitletType):
967 class Enum(TraitType):
958 """An enum that whose value must be in a given sequence."""
968 """An enum that whose value must be in a given sequence."""
959
969
960 def __init__(self, values, default_value=None, allow_none=True, **metadata):
970 def __init__(self, values, default_value=None, allow_none=True, **metadata):
@@ -999,7 +1009,7 b' class List(Instance):'
999 """An instance of a Python list."""
1009 """An instance of a Python list."""
1000
1010
1001 def __init__(self, default_value=None, allow_none=True, **metadata):
1011 def __init__(self, default_value=None, allow_none=True, **metadata):
1002 """Create a list traitlet type from a list or tuple.
1012 """Create a list trait type from a list or tuple.
1003
1013
1004 The default value is created by doing ``list(default_value)``,
1014 The default value is created by doing ``list(default_value)``,
1005 which creates a copy of the ``default_value``.
1015 which creates a copy of the ``default_value``.
@@ -1008,6 +1018,8 b' class List(Instance):'
1008 args = ((),)
1018 args = ((),)
1009 elif isinstance(default_value, SequenceTypes):
1019 elif isinstance(default_value, SequenceTypes):
1010 args = (default_value,)
1020 args = (default_value,)
1021 else:
1022 raise TypeError('default value of List was %s' % default_value)
1011
1023
1012 super(List,self).__init__(klass=list, args=args,
1024 super(List,self).__init__(klass=list, args=args,
1013 allow_none=allow_none, **metadata)
1025 allow_none=allow_none, **metadata)
@@ -11,8 +11,8 b' subdirectory in ``.html`` and ``.pdf`` formats, also available online at our'
11 the plaintext version of these manuals.
11 the plaintext version of these manuals.
12
12
13
13
14 Instant running and testing
14 Instant running
15 ===========================
15 ===============
16
16
17 You can run IPython from this directory without even installing it system-wide
17 You can run IPython from this directory without even installing it system-wide
18 by typing at the terminal:
18 by typing at the terminal:
@@ -20,10 +20,4 b' by typing at the terminal:'
20 .. code-block:: bash
20 .. code-block:: bash
21
21
22 python ipython.py
22 python ipython.py
23
24 and similarly, you can execute the built-in test suite with:
25
23
26 .. code-block:: bash
27
28 python iptest.py
29 No newline at end of file
@@ -50,7 +50,9 b' html: api'
50 @echo
50 @echo
51 @echo "Build finished. The HTML pages are in build/html."
51 @echo "Build finished. The HTML pages are in build/html."
52
52
53 api:
53 api: source/api/generated/gen.txt
54
55 source/api/generated/gen.txt:
54 python autogen_api.py
56 python autogen_api.py
55 @echo "Build API docs finished."
57 @echo "Build API docs finished."
56
58
@@ -71,7 +73,7 b' htmlhelp:'
71 @echo "Build finished; now you can run HTML Help Workshop with the" \
73 @echo "Build finished; now you can run HTML Help Workshop with the" \
72 ".hhp project file in build/htmlhelp."
74 ".hhp project file in build/htmlhelp."
73
75
74 latex:
76 latex: api
75 mkdir -p build/latex build/doctrees
77 mkdir -p build/latex build/doctrees
76 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
78 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
77 @echo
79 @echo
@@ -1,71 +1,148 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Run a Monte-Carlo options pricer in parallel."""
2 """Run a Monte-Carlo options pricer in parallel."""
3
3
4 #-----------------------------------------------------------------------------
5 # Imports
6 #-----------------------------------------------------------------------------
7
8 import sys
9 import time
4 from IPython.kernel import client
10 from IPython.kernel import client
5 import numpy as np
11 import numpy as np
6 from mcpricer import price_options
12 from mcpricer import price_options
13 from matplotlib import pyplot as plt
14
15 #-----------------------------------------------------------------------------
16 # Setup parameters for the run
17 #-----------------------------------------------------------------------------
18
19 def ask_question(text, the_type, default):
20 s = '%s [%r]: ' % (text, the_type(default))
21 result = raw_input(s)
22 if result:
23 return the_type(result)
24 else:
25 return the_type(default)
26
27 cluster_profile = ask_question("Cluster profile", str, "default")
28 price = ask_question("Initial price", float, 100.0)
29 rate = ask_question("Interest rate", float, 0.05)
30 days = ask_question("Days to expiration", int, 260)
31 paths = ask_question("Number of MC paths", int, 10000)
32 n_strikes = ask_question("Number of strike values", int, 5)
33 min_strike = ask_question("Min strike price", float, 90.0)
34 max_strike = ask_question("Max strike price", float, 110.0)
35 n_sigmas = ask_question("Number of volatility values", int, 5)
36 min_sigma = ask_question("Min volatility", float, 0.1)
37 max_sigma = ask_question("Max volatility", float, 0.4)
38
39 strike_vals = np.linspace(min_strike, max_strike, n_strikes)
40 sigma_vals = np.linspace(min_sigma, max_sigma, n_sigmas)
41
42 #-----------------------------------------------------------------------------
43 # Setup for parallel calculation
44 #-----------------------------------------------------------------------------
7
45
8 # The MultiEngineClient is used to setup the calculation and works with all
46 # The MultiEngineClient is used to setup the calculation and works with all
9 # engine.
47 # engine.
10 mec = client.MultiEngineClient(profile='mycluster')
48 mec = client.MultiEngineClient(profile=cluster_profile)
11
49
12 # The TaskClient is an interface to the engines that provides dynamic load
50 # The TaskClient is an interface to the engines that provides dynamic load
13 # balancing at the expense of not knowing which engine will execute the code.
51 # balancing at the expense of not knowing which engine will execute the code.
14 tc = client.TaskClient(profile='mycluster')
52 tc = client.TaskClient(profile=cluster_profile)
15
53
16 # Initialize the common code on the engines. This Python module has the
54 # Initialize the common code on the engines. This Python module has the
17 # price_options function that prices the options.
55 # price_options function that prices the options.
18 mec.run('mcpricer.py')
56 mec.run('mcpricer.py')
19
57
20 # Define the function that will make up our tasks. We basically want to
58 #-----------------------------------------------------------------------------
21 # call the price_options function with all but two arguments (K, sigma)
59 # Perform parallel calculation
22 # fixed.
60 #-----------------------------------------------------------------------------
23 def my_prices(K, sigma):
61
24 S = 100.0
62 print "Running parallel calculation over strike prices and volatilities..."
25 r = 0.05
63 print "Strike prices: ", strike_vals
26 days = 260
64 print "Volatilities: ", sigma_vals
27 paths = 100000
65 sys.stdout.flush()
28 return price_options(S, K, sigma, r, days, paths)
66
29
67 # Submit tasks to the TaskClient for each (strike, sigma) pair as a MapTask.
30 # Create arrays of strike prices and volatilities
68 t1 = time.time()
31 nK = 10
32 nsigma = 10
33 K_vals = np.linspace(90.0, 100.0, nK)
34 sigma_vals = np.linspace(0.1, 0.4, nsigma)
35
36 # Submit tasks to the TaskClient for each (K, sigma) pair as a MapTask.
37 # The MapTask simply applies a function (my_prices) to the arguments:
38 # my_prices(K, sigma) and returns the result.
39 taskids = []
69 taskids = []
40 for K in K_vals:
70 for strike in strike_vals:
41 for sigma in sigma_vals:
71 for sigma in sigma_vals:
42 t = client.MapTask(my_prices, args=(K, sigma))
72 t = client.MapTask(
73 price_options,
74 args=(price, strike, sigma, rate, days, paths)
75 )
43 taskids.append(tc.run(t))
76 taskids.append(tc.run(t))
44
77
45 print "Submitted tasks: ", len(taskids)
78 print "Submitted tasks: ", len(taskids)
79 sys.stdout.flush()
46
80
47 # Block until all tasks are completed.
81 # Block until all tasks are completed.
48 tc.barrier(taskids)
82 tc.barrier(taskids)
83 t2 = time.time()
84 t = t2-t1
85
86 print "Parallel calculation completed, time = %s s" % t
87 print "Collecting results..."
49
88
50 # Get the results using TaskClient.get_task_result.
89 # Get the results using TaskClient.get_task_result.
51 results = [tc.get_task_result(tid) for tid in taskids]
90 results = [tc.get_task_result(tid) for tid in taskids]
52
91
53 # Assemble the result into a structured NumPy array.
92 # Assemble the result into a structured NumPy array.
54 prices = np.empty(nK*nsigma,
93 prices = np.empty(n_strikes*n_sigmas,
55 dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)]
94 dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)]
56 )
95 )
96
57 for i, price_tuple in enumerate(results):
97 for i, price_tuple in enumerate(results):
58 prices[i] = price_tuple
98 prices[i] = price_tuple
59 prices.shape = (nK, nsigma)
99
60 K_vals, sigma_vals = np.meshgrid(K_vals, sigma_vals)
100 prices.shape = (n_strikes, n_sigmas)
101 strike_mesh, sigma_mesh = np.meshgrid(strike_vals, sigma_vals)
61
102
62 def plot_options(sigma_vals, K_vals, prices):
103 print "Results are available: strike_mesh, sigma_mesh, prices"
104 print "To plot results type 'plot_options(sigma_mesh, strike_mesh, prices)'"
105
106 #-----------------------------------------------------------------------------
107 # Utilities
108 #-----------------------------------------------------------------------------
109
110 def plot_options(sigma_mesh, strike_mesh, prices):
63 """
111 """
64 Make a contour plot of the option price in (sigma, K) space.
112 Make a contour plot of the option price in (sigma, strike) space.
65 """
113 """
66 from matplotlib import pyplot as plt
114 plt.figure(1)
67 plt.contourf(sigma_vals, K_vals, prices)
115
116 plt.subplot(221)
117 plt.contourf(sigma_mesh, strike_mesh, prices['ecall'])
118 plt.axis('tight')
68 plt.colorbar()
119 plt.colorbar()
69 plt.title("Option Price")
120 plt.title('European Call')
121 plt.ylabel("Strike Price")
122
123 plt.subplot(222)
124 plt.contourf(sigma_mesh, strike_mesh, prices['acall'])
125 plt.axis('tight')
126 plt.colorbar()
127 plt.title("Asian Call")
128
129 plt.subplot(223)
130 plt.contourf(sigma_mesh, strike_mesh, prices['eput'])
131 plt.axis('tight')
132 plt.colorbar()
133 plt.title("European Put")
70 plt.xlabel("Volatility")
134 plt.xlabel("Volatility")
71 plt.ylabel("Strike Price")
135 plt.ylabel("Strike Price")
136
137 plt.subplot(224)
138 plt.contourf(sigma_mesh, strike_mesh, prices['aput'])
139 plt.axis('tight')
140 plt.colorbar()
141 plt.title("Asian Put")
142 plt.xlabel("Volatility")
143
144
145
146
147
148
@@ -134,7 +134,7 b' traitlets for a number of other types.'
134 .. note::
134 .. note::
135
135
136 Underneath the hood, the :class:`Component` base class is a subclass of
136 Underneath the hood, the :class:`Component` base class is a subclass of
137 :class:`IPython.utils.traitlets.HasTraitlets`. The
137 :class:`IPython.utils.traitlets.HasTraits`. The
138 :mod:`IPython.utils.traitlets` module is a lightweight version of
138 :mod:`IPython.utils.traitlets` module is a lightweight version of
139 :mod:`enthought.traits`. Our implementation is a pure Python subset
139 :mod:`enthought.traits`. Our implementation is a pure Python subset
140 (mostly API compatible) of :mod:`enthought.traits` that does not have any
140 (mostly API compatible) of :mod:`enthought.traits` that does not have any
@@ -17,4 +17,4 b" IPython developer's guide"
17 magic_blueprint.txt
17 magic_blueprint.txt
18 notification_blueprint.txt
18 notification_blueprint.txt
19 ipgraph.txt
19 ipgraph.txt
20
20 ipython_qt.txt
@@ -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
@@ -68,7 +68,8 b' def mangle_docstrings(app, what, name, obj, options, lines,'
68 def mangle_signature(app, what, name, obj, options, sig, retann):
68 def mangle_signature(app, what, name, obj, options, sig, retann):
69 # Do not try to inspect classes that don't define `__init__`
69 # Do not try to inspect classes that don't define `__init__`
70 if (inspect.isclass(obj) and
70 if (inspect.isclass(obj) and
71 'initializes x; see ' in pydoc.getdoc(obj.__init__)):
71 (not hasattr(obj, '__init__') or
72 'initializes x; see ' in pydoc.getdoc(obj.__init__))):
72 return '', ''
73 return '', ''
73
74
74 if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
75 if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now